Hue bulbs and groups

Yes, I feared you’d say that :wink:

Hi,

To keep the thread going in case it’s useful to anyone…

I’ve updated the rules so it is more generic in that the lights.rule now passes one string with all the required commands on and the GateKeeper rule acts accordingly:

Part of Lights.rule

        val SpotGroupSet = ("gSpots_LR#" + caseItem + "#" + caseONOFF + "#" + caseBrightness + "#" + caseColorTemp)
        logInfo(logName, "SpotGroupSet - " + SpotGroupSet)

        vLightsGatekeeper_Set.sendCommand(SpotGroupSet)

Which results in the LOG

2020-04-30 19:43:46.900 [INFO ] [clipse.smarthome.model.script.lights] - SpotGroupSet - gSpots_LR#All#ON#100#14

So you can see it passes everything separated by #

The GK rule then takes the string and splits it back down:

rule "GK Living Room Lights Brightness"
    when
        Item vLightsGatekeeper_Set changed
    then
    // Split the command down gSpots_LR#All#OFF#0#0
    val GKcommand = triggeringItem.state.toString
    logInfo(logName, "GK GKCommand - " + GKcommand)
    val groupName = GKcommand.split("#").get(0)
    logInfo(logName, "GK groupName - " + groupName)
    val groupVal = GKcommand.split("#").get(1)

    logInfo(logName, "GK groupVal - " + groupVal)
    val onoff = GKcommand.split("#").get(2)
    logInfo(logName, "GK onoff - " + onoff)
    val brightness = GKcommand.split("#").get(3)
    logInfo(logName, "GK brightness - " + brightness)    
    val colortemp = GKcommand.split("#").get(4)
    logInfo(logName, "GK colortemp - " + colortemp)

It then builds the appropriate string to pass to the group members part

    val SpotGroupBuild_B = (groupName + "_B_" + groupVal)
    logInfo(logName, "SpotGroupBuild B - " + SpotGroupBuild_B)

    val SpotGroupBuild_CT = (groupName + "_CT_" + groupVal)
    logInfo(logName, "SpotGroupBuild CT - " + SpotGroupBuild_CT)

    val groupB = ScriptServiceUtil.getItemRegistry.getItem(SpotGroupBuild_B) as GroupItem
    logInfo(logName, "GK group B - " + groupB)

    val groupCT = ScriptServiceUtil.getItemRegistry.getItem(SpotGroupBuild_CT) as GroupItem
    logInfo(logName, "GK group CT - " + groupCT)

LOG

2020-04-30 19:50:34.065 [INFO ] [se.smarthome.model.script.GateKeeper] - GK GKCommand - gSpots_LR#All#ON#100#14
2020-04-30 19:50:34.070 [INFO ] [se.smarthome.model.script.GateKeeper] - GK groupName - gSpots_LR
2020-04-30 19:50:34.077 [INFO ] [se.smarthome.model.script.GateKeeper] - GK groupVal - All
2020-04-30 19:50:34.082 [INFO ] [se.smarthome.model.script.GateKeeper] - GK onoff - ON
2020-04-30 19:50:34.088 [INFO ] [se.smarthome.model.script.GateKeeper] - GK brightness - 100
2020-04-30 19:50:34.093 [INFO ] [se.smarthome.model.script.GateKeeper] - GK colortemp - 14
2020-04-30 19:50:34.099 [INFO ] [se.smarthome.model.script.GateKeeper] - SpotGroupBuild B - gSpots_LR_B_All
2020-04-30 19:50:34.106 [INFO ] [se.smarthome.model.script.GateKeeper] - SpotGroupBuild CT - gSpots_LR_CT_All

My groups are the same name as SpotGroupBuild B for Brightness Group and SpotGroupBuild CT for the Color Temp group

Here’s where I’m stuck as I have 2 different groups to send the appropriate info to, brightness and color temp. At the moment only one group is used in the timer / queue…how do I add the second one in, do I need to let groupB run through and then add a second block of timer code for groupCT or is there some clever group.members command to combine two groups together and still know which is which???

    // Get group members
    groupB.members.forEach[ light | deviceQueue.add(light) ]

    // Timer
    if(timer === null) 
    {
        timer = createTimer(now.plusMillis(delay), [ |
            if(deviceQueue.peek !== null) 
            {
                
                val light = deviceQueue.poll
                sendCommand(light,onoff)
                logInfo(logName, "deviceQueue - " + light + onoff + " - SENT")
                sendCommand(light,brightness)
                logInfo(logName, "deviceQueue - " + light + brightness + " - SENT")
                //sendCommand(light,colortemp)
                //logInfo(logName, "deviceQueue - " + light + colortemp + " - SENT")
                timer.reschedule(now.plusMillis(delay))
            }
            else
            {
                timer = null
            }
        ])
    }
end

@MadFrankie are you using the OH2 Zigbee binding? What Zigbee HW?
Are your group actions now 100% synchronised within the groups? (as far as the eye can see … :slight_smile: )

The reason I’m asking is that I started out with Ikea Zigbee bulbs and the Ikea Gateway a few years back, and when doing group operations there could be several seconds action delay within the group.

That was the reason I moved to ConBee/Deconz, which has great support for Zigbee groups.
I have 10 GU10 bulbs in a group, and dimming and on/off is in perfect unison.
Another added bonus, is that you can set the ‘alarm’ short or long property of a group and all lights will dim up and down and settle in their original state.

Is this functionality now also available in the native OH2 Zigbee binding?

Hi,

No, mine are hue bulbs so I’m using the hue binding. I’ve had some issues with it not accepting all the commands and bulbs going outside of sequence’, (hence this and a few other threads), So I have thought of using a ZigBee binding but I’ve got other hue items so I determined to work out how to make them work how is like / expect.

Ah, OK, so you are actually using the HUE gateway?
I have one also (controlling 16 facade E27 bulbs in a group), since I like checking the ‘pulse’ of different vendors.

I found that the REST API for a HUE gateway and the Deconz are exactly the same. (Hence they can both use the same OH2 binding I guess :slight_smile: )
Tell me if you want me to share how to control these groups from OH2 rules.

A trick here is assigning Item names according to a predictable convention e.g.

SomeLamp_XX_brightness
SomeLamp_XX_colour

Given one Item name, you can work out its partner(s) with a bit of string manipulation

You’re already doing this?

Yes, my items are

GF_LR_HueSpot1
GF_LR_HueSpot2

Each item has 2 channels

GF_LR_HueSpot1_Brightness,
GF_LR_HueSpot1_ColorTemp,

GF_LR_HueSpot2_Brightness,
GF_LR_HueSpot2_ColorTemp,
etc

and then the groups are

gSpots_LR_B_All
gSpots_LR_CT_All

The issue is that I’m using groups to run this rule and as brightness and color temp are different channels in the items I’ve created different groups to control them…

There is only and should only be one queue (commands). There should only be one Timer that reads the commands off of that Queue and sends them to the given Items. The Queue consists of a String with Item name and command to send to the Item. So what you do is you add the color temp Items and commands to the same commands queue. Neither the queue nor the Timer cares that one Item is a color temp and the other is for brightness.

If you have two Groups of stuff to send commands to, either send command to vLightsGatekeeper.sendCommand(“Group1#CMD1”) and then vLightsGatekeeper.sendCommand(“Group2#CMD2”).

Or, if you want to handle it all in the Gatekeeper Rule than just pass the first part of the group name and construct the rest of the group name for brightness and color temp and use the Item registry to pull them both and loop through both.

You have full control over your Groups. If they are not serving you, change them or add new Groups that do serve your purpose.

But eventually you end up dealing with an Item like
GF_LR_HueSpot2_Brightness
and you know what the partner color Item is called.

Hi,

Thanks for all the thoughts, I’ve reshaped the rule so that both Brightness and ColorTemp channels are in the same group now.

The GateKeep rule grabs all the values needed and splits them as before, the group.members grabs all group items and adds them to the queue.

Now, I’ve modified the timer section:

// Timer
    if(timer === null) 
    {
        timer = createTimer(now.plusMillis(delay), [ |
            if(deviceQueue.peek !== null) 
            {
                val light = deviceQueue.poll
                val light2 = light.toString
                val lightSplit = light2.split("_").get(4)
                logInfo(logName, "lightSplit - " + lightSplit)
                if(lightSplit == "Brightness")
                {
                    sendCommand(light,onoff)
                    logInfo(logName, "deviceQueue - " + light + onoff + " - SENT")
                    sendCommand(light,brightness)
                    logInfo(logName, "deviceQueue - " + light + brightness + " - SENT")
                }
                if(lightSplit == "ColorTemp")
                {
                    sendCommand(light,colortemp)
                    logInfo(logName, "deviceQueue - " + light + colortemp + " - SENT")
                }
                timer.reschedule(now.plusMillis(delay))
            }
            else
            {
                timer = null
            }
        ])

My items are named:

GF_LR_HueSpot1_Brightness
GF_LR_HueSpot1_ColorTemp
GF_LR_HueSpot2_Brightness
GF_LR_HueSpot2_ColorTemp
etc

So the new part gets the item in question from the queue and splits it to find out if it is Brightness or ColorTemp and then I’ve dropped in a couple of if statements to determine which sendCommand to send…

However, everything runs ok, all the logs are correct, but neither of the if statements run so I’m now missing something here…

:confused: Why does it need to care? All the Timer needs is the name of the Item and the value to send to it and how long before it can send the next command. Don’t put anything more complicated than that in it. When you are adding the stuff to the command queue is where you do this.

commands.add("LightSwitch#ON")
commands.add("LightBrightness#50")
commands.add("LightColorTemp#75")

You need to put a delay between all command to the Hue or else some will get lost.

Again, the only thing the Timer does is send the command and sleep a bit before sending the next command on the queue.

Thanks and sorry, this is probably really frustrating for you…this is the bit I just can’t get my head around and I don’t know why. As a learning experience it’s been great for me, it’s introduced me to so many new topics and methods that I’m pretty pleased so far, but it’s just a different way of thinking that I need that ‘eureka’ moment for it to fully sink in.

So, in my head…

  1. I’ve passed the GK rule all the info I need (groupname#onoff#brightness#colortemp):
GK GKCommand - gSpots_LR_All#ON#100#14
  1. I have added all items into the same group:
Group:Dimmer  gSpots_LR_All
  1. So that when I call
group.members.forEach[ light | deviceQueue.add(light) ]
  1. This is how I’m adding all members of that group into the deviceQueue:
Spot_GF_LR_1_Brightness
Spot_GF_LR_2_Brightness
Spot_GF_LR_3_Brightness
Spot_GF_LR_4_Brightness
Spot_GF_LR_1_ColorTemp
Spot_GF_LR_2_ColorTemp
Spot_GF_LR_3_ColorTemp
Spot_GF_LR_4_ColorTemp
  1. Where I am struggling is that I send the x_Brightness and x_ColorTemp items different values, in this case Brightness = 100 and ColorTemp = 14 but because I’m automatically filling the deviceQueue this way how do I differentiate between item to give it the correct command?

Hi,

Been reading through the thread and some relevant group based posts and I think it might be sinking in.

I get that I must pass everything / all the info to the deviceQueue outside of the timer, and the timer just polls each item in the queue and does something to it.

So, I need to append the command, (ie ON / brightness to 100 / colortemp to 14), to each group member. In the timer loop can then split the deviceQueue item and then send the appropriate command to the sendCommand(name,command) part.

So, unless someone can suggest I’m going down the wrong route…I need to work out how to filter the group into _brightness items and _colortemp items, append the appropriate command to them and then add them to the deviceQueue.

As I’m new to groups I’m reading the DP for it and docs and think that I might need to

group.members.filter [where the last part of the name = brightness ]  .forEach [ each one | 'append member and #command into one string' and 'deviceQueue.add(appended string)']

group.members.filter [where the last part of the name = colortemp ]  .forEach [ eachone | 'append member and #command into one string' and 'deviceQueue.add(appended string)']

Does that make sense?
Can you filter for a command like where name contains ‘brightness’? or do I need to do more work outside the groups.filter command?

EDIT

I’m currently here in my thinking

group.members.filter[ b | b.name.toString.split("_".get(4)) == "Brightness"].forEach[ bYES | bYES.deviceQueue.add(bYES + "#" + brightness)]

But the split part isn’t working, LOG

Rule 'GK Living Room Lights Brightness': 'get' is not a member of 'java.lang.String';

Right, getting somewhere slowly…

Group filter now works:

group.members.filter[ light | light.name.split("_").get(4) == "Brightness"].forEach[ bYES | deviceQueue.add(bYES + "#" + brightness)]

Timer routine:

    // Timer
    if(timer === null) 
    {
        timer = createTimer(now.plusMillis(delay), [ |
            if(deviceQueue.peek !== null) 
            {
                val light = deviceQueue.poll
                val lights = light.toString
                val lightSplit = lights.split("#").get(0)
                val lightSplit2 = lights.split("#").get(1)
                sendCommand(lightSplit,lightSplit2)
                logInfo(logName, "deviceQueue - " + lightSplit + lightSplit2 + " - SENT")
                timer.reschedule(now.plusMillis(delay))
            }
            else
            {
                timer = null
            }
        ])
    }

This also works…however, (there always was going to be a however :smile:)

The LOG shows that the item does not exist.

2020-05-01 13:02:15.388 [WARN ] [rthome.model.script.actions.BusEvent] - Item 'Spot_GF_LR_1_Brightness (Type=DimmerItem, State=100, Label=GF LR Spot 1 Brightness, Category=null, Groups=[gSpots_LR_All, gSpots_LR_B_These])' does not exist.
2020-05-01 13:02:15.397 [INFO ] [se.smarthome.model.script.GateKeeper] - deviceQueue - Spot_GF_LR_1_Brightness (Type=DimmerItem, State=100, Label=GF LR Spot 1 Brightness, Category=null, Groups=[gSpots_LR_All, gSpots_LR_B_These])0 - SENT
202

I assume that by splitting it into a val it needs to be converted back to something???

Doh, worked it out, I needed to add .name to the end of my forEach item…

Seems to be working now…going to have some real life testing and then I will post the whole rule if anyone is interested.

Also, just for future reference, I found this in the hue api text:

How many commands you can send per second?

You can send commands to the lights too fast. If you stay roughly around 10 commands per second to the /lights resource as maximum you should be fine.

For /groups commands you should keep to a maximum of 1 per second.

I’ve found that 1 per second is still a little unpredictable, so have set my delay to 1.5 seconds, which seems fine…I may work to trim this down and test but for now I’ll leave it where it is!

2 Likes

That’s one approach. There are others of course.

  • Just put the brightness Items into the Group and generate the name of the corresponding Color Temp Item
....forEach[ light | 
      deviceQueue.add(light.name+"#"+brightness)
      deviceQueue.add(light.name.replace("Brightness", "ColorTemp")+"#"+color)
    ]
  • Instead of passing everything as one String to the GK Item, put each type of Item in it’s own Group and call the GK Rule once for each Group
    // In the Rule that calls GK
    vLightsGatekeeper_Set.sendCommand("gSpots_LR_Switch_All#ON")
    vLightsGatekeeper_Set.sendCommand("gSpots_LR_B_All#100")
    vLightsGatekeeper_Set.sendCommand("gSpots_LR_CT#14")

This is the approach I would use as it keeps the GK Rule very generic.

You have a misplaced closing paren.

b.name.toString.split("_").get(4) == "Brightness"

That’s very helpful information. You should be able to get it down to 100 msec between commands according to this as long as you are not commanding Hue Grouped lights (not to be confused with openHAB Groups).

Hi,

It’s all working well at the moment, I like your alternative forEach addition. I may try that out a bit later.

Found some more info on the Hue REST API

‘Depending on the size of your Zigbee network commands to the API. Larger networks may require up to a second between commands.’

1 Like

Hi,

I’ve completely re-written my rules again :slight_smile: This time I think the penny has dropped, I’ve changed it so that all the work is done in the lights.rules and then the GateKeeper.rules is kept generic so I can use it elsewhere. I now get it!

The process now simply send the GK dummy variable that stats the rule an (itemname#command), which the GK rule splits and then processes accordingly, (sorry it’s taken this long to get to where you started @rlkoshak!)

I’m just tidying up the rules and then I’ll post them all for history as a learning experience.

One thing that I can’t seem to get to work is that I have 2 colour bulbs that accept HSBType commands, ie (hue, saturation, brightness), or (100,50,100) or whatever.

I’ve tried various things to set it into a HSBType in both lights.rules and in GK.rules. AS I have to send the GK the command 100,50,100 as a string I assume it needs to be split and then converted in the GK rule, (which isn’t perfect as it makes it less generic but so be it if it has a couple of if statements in there over time)

I’ve tried various including:

if(GKItemCommand.toString.split(",").get(2) !== null)
                {
                var DecimalType hue = new DecimalType(GKItemCommand.split(",").get(0))
                var PercentTypeType sat = new PercentType(GKItemCommand.split(",").get(1))
                var PercentType bright = new PercentType(GKItemCommand.split(",").get(2))
                var HSBType GKItemCommand = new HSBType(hue,sat,bright)
                }

ie all commands I send it are one number, if I send it one with 100,50,100 if the 3rd split 100 isn’t null then create a new HSBType item to pass to the sendCommand…

However it doesn’t work…This particular code threw the log error:

2020-05-03 13:04:30.233 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 61 2020-05-03T13:04:30.205+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@daf97f (conditionalExpression: false)

But there have been others, including:

2020-05-03 12:27:39.559 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '-1' to a command type which item 'Spot_GF_LR_7_ColorTemp' accepts: [PercentType, OnOffType, IncreaseDecreaseType, RefreshType].

Which suggests to me that I’m just not getting the HSBType right for the item to accept…I get the feeling I’m just making it far more complicated that I need to.

Any ideas?

I like the idea, but think you have to implement slightly differently.
get(2) will blow up when there is no (2), rather than gracefully return null
You could see if the length of the array was >2 instead.

Details, details. Are you meant to be making three variables here, not defining the same one ‘hue’ three times? And that type?