Hue bulbs and groups

Hi,

Thanks for all the pointers…valuable lesson learnt, never code whilst tired…

Fresh eyes this morning and it’s sorted:

import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.concurrent.ConcurrentLinkedQueue
val logName = "GateKeeper"
val deviceQueue = new ConcurrentLinkedQueue()
var Timer timer = null
val delay = 1000  // Delay in milliseconds between commands

// GK for Living Room Lights Brightness

rule "GK Living Room Lights Brightness"
    when
        Item vLightsGatekeeper_B received command
    then
    // Add the commands to the Queue
    val GKcommand = vLightsGatekeeper_B.state.toString
    logInfo(logName, "GK_B GKCommand - " + GKcommand)
    val groupName = GKcommand.split("#").get(0)
    logInfo(logName, "GK_B groupName - " + groupName)
    val cmd = GKcommand.split("#").get(1)
    logInfo(logName, "GK_B cmd - " + cmd)
    val group = ScriptServiceUtil.getItemRegistry.getItem(groupName) as GroupItem
    logInfo(logName, "GK_B group - " + group)
    
    // Get group members
    group.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,cmd)
                logInfo(logName, "deviceQueue - " + light + cmd + " - SENT")
                timer.reschedule(now.plusMillis(delay))
            }
            else
            {
                timer = null
            }
        ])
    }
end

Trimmed it down a little and added a delay variable so I can control the delay rather than hard coding it in via a time calculation.

All polls correctly, I had to declare the group as a GroupItem for it to pass the members and I will probably comment out all the logs at some point when it’s all working and just leave the final one in but that’s for a little later on.

Next question, I have 2 commands to pass each member, brightness and ColorTemp, would it make sense to create a duplicate rule for ColorTemp to do the same process or am I at risk of undoing the whole point of the timer here, if I have 2 timers running at the same time I’ll have twice the commands thrown at the hue api and so back to square 1.

Should I just append the ColorTemp part to the bottom of this rule so that the system sorts out the brightness and then sorts out the color temp, or is there a better way?

That’s what I’d do - encode both your brightness and colour in one GK “instruction”. You can code the rule smart enough to parse out a color directive and do it, or ignore it if not supplied (so the rule will work for both dimmers or color).

I like that idea, I do have an actual color bulb coming in the post so I’ll see if I can get this rule to work with whatever is passed to it…

Just when you think you’ve got it solved…

Been testing it all for a while and sometime the command don’t match to the physical light state. I’ve been looking through the logs and I can see it happen:

Log relating to the lights.rules

2020-04-30 12:33:42.058 [INFO ] [clipse.smarthome.model.script.lights] - caseItem - All
2020-04-30 12:33:42.067 [INFO ] [clipse.smarthome.model.script.lights] - caseValue - OFF
2020-04-30 12:33:42.089 [INFO ] [clipse.smarthome.model.script.lights] - OFF - caseBrightness set to 0 / caseColorTemp to 0
2020-04-30 12:33:42.102 [INFO ] [clipse.smarthome.model.script.lights] - SpotGroupB - gSpots_LR_B_All#0
2020-04-30 12:33:42.115 [INFO ] [clipse.smarthome.model.script.lights] - SpotGroupTC - gSpots_LR_TC_All#0

Log relating to the GateKeeper.rules

2020-04-30 12:33:42.137 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B GKCommand - gSpots_LR_B_All#100
2020-04-30 12:33:42.154 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B groupName - gSpots_LR_B_All
2020-04-30 12:33:42.166 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B cmd - 100
2020-04-30 12:33:42.178 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B group - gSpots_LR_B_All (Type=GroupItem, BaseType=DimmerItem, Members=4, State=100, Label=Living Room Spots All ON, Category=null)
2020-04-30 12:33:43.227 [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_B_All, gSpots_LR_B_These])100 - SENT
2020-04-30 12:33:44.322 [INFO ] [se.smarthome.model.script.GateKeeper] - deviceQueue - Spot_GF_LR_2_Brightness (Type=DimmerItem, State=100, Label=GF LR Spot 2 Brightness, Category=null, Groups=[gSpots_LR_B_All, gSpots_LR_B_These, gSpots_LR_B_Sofa])100 - SENT
2020-04-30 12:33:45.374 [INFO ] [se.smarthome.model.script.GateKeeper] - deviceQueue - Spot_GF_LR_3_Brightness (Type=DimmerItem, State=100, Label=GF LR Spot 3 Brightness, Category=null, Groups=[gSpots_LR_B_All, gSpots_LR_B_These, gSpots_LR_B_Sofa])100 - SENT
2020-04-30 12:33:46.418 [INFO ] [se.smarthome.model.script.GateKeeper] - deviceQueue - Spot_GF_LR_4_Brightness (Type=DimmerItem, State=100, Label=GF LR Spot 4 Brightness, Category=null, Groups=[gSpots_LR_B_All, gSpots_LR_B_These])100 - SENT

Sending an OFF command, runs through the lights.rules correctly, The final val the rule creates is SpotGroupB which logs correctly as ‘gSpots_LR_B_All#0’ -

The final command does a sendCommand of SpotGroupB to a vLightsGatekeeper_B dummy string item.

vLightsGatekeeper_B.sendCommand(SpotGroupB)

The GateKeeper rule triggers on the vLightsGatekeeper_B received command

The first thing it does is:

val GKcommand = vLightsGatekeeper_B.state.toString
logInfo(logName, "GK_B GKCommand - " + GKcommand)

Which I would fully expect to be the same as SpotGroupB as that is the command it was sent.

However the log shows that it seems to have the previous state still stored ‘gSpots_LR_B_All#100’…

Have I confused it all and a reboot / clear cache will bring it back into line or do I have something else going on here?

openHAB is asynchronous. Meaning never, ever expect the effect of a command to be instantaneous.
The Item model is designed around a command whirling away across the ether to some remote device. Some time later, a response comes back telling us a new state for the Item. Not instantaneous.
This still holds true even when there is no real device, and you are relying on autoupdate to predict a command into an Item state. Not instantaneous.

You can see this in your events.log

So, the effect for rule writers;

someItem.sendCommand(XX)
variable = someItem.state

It’s doomed to failure.
You almost certainly get the old state from before the command takes effect. The rule does not stop and wait.
Of course, that is stupid code … you already know what XX was so you can predict the state for yourself.

Effect on interlinked rules or events

rule "some rule or UI click"
...
myItem.sendCommand(XX)

rule "my real rule"
when
   Item myItem receivedCommand
then
   variable = myItem.state

It’s doomed.
This is just an extended version of the earlier example.
The events bus does not stop and wait, the command event will trigger the second rule before anyone else has got around to responding to the command to update the Item.
It’s indeterminate, but you will again almost certainly get the old state before command has had any effect.

Here, you want to know what the command actually was. The state is useless.
This is what the implicit variable receivedCommand is for.

or

If you really need to act after the command has taken effect, then don’t trigger from command. Trigger from changed or updated as appropriate, which fire after any state update has been carried out.

As always, makes total sense when you point it out and explain it. You really have the patience of a saint for us coders trying to work our way through the learning process!

EDIT - For completion the GateKeeper rule now runs on changed and it gets the value via triggeringItem:

rule "GK Living Room Lights Brightness"
    when
        Item vLightsGatekeeper_B changed
    then
    // Add the commands to the Queue
    val GKcommand = triggeringItem.state.toString

This seems to work correctly, I suppose as expected now…

Is that the best way? The problem I can see for this is that I can’t then change the rule to be

Item vLightsGatekeeper_B changed or
Item vLightsGatekeeper_CT changed

To then run the rule to catch both the brightness and color temp changes in the same rule as it will only pick up the triggering item…hum, some thinking to do…

No, it doesn’t. The Object itself knows it’s a GroupItem because an Object always knows what it is. When you log out the Item’s toString you call the toString function implemented by that Object. But that has no bearing on what the Rule thinks. The error is clear, the Rule thinks it’s an Item.

‘members’ is not a member of ‘org.eclipse.smarthome.core.items.Item’

When you cast or define the type of an Object, you are not actually changing anything about the Object itself. You are just telling the code that is using the Object (in this case the Rule) what data and methods are available on that Object.

I believe Group is an Interface that get’s implemented by GroupItem. But I think members is only implemented by GroupItem (actually another interface that GroupItem inherits from) so it wouldn’t show up when look at the Object as a Group.

You will want to handle the ColorTemp in this Rule. You only want the one GK Rule talking to the Hue or else you can’t guarantee the timing of the commands.

But what needs to be different to handle the ColorTemp?

This works OK as long as you never need to send the same command to the same Group in a row. In that case the vLightsGatekeeper_B won’t change on the second command and GK won’t run.

First, why do you need a second Item? Just send the color commands to the same vLightsGatekeeper_B Item and somehow put into the String that is part of the command something you can parse out to see if it’s a dimmer command or a color command.

But again I ask, what needs to be done differently? As written, you pass the name of a Group and the command to send to all members of that Group. Both are defined as Strings. So the same code can handle Groups of Items of any type, so long as the command you pass with it is supported.

What is special about the color temp that makes this not work as is?

Well, I’m going to take this as a win…I haven’t updated this thread for a few hours now as I’ve just been re-jigging the rules to do just this :slight_smile:

The lights.rules now sends all the commands in on line to the dummy item

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

        vLightsGatekeeper_Set.sendCommand(SpotGroupSet)

LOG

2020-04-30 16:13:36.504 [INFO ] [clipse.smarthome.model.script.lights] - SpotGroupSet - gSpots_LR_B_All#ON#100#14

The GK rule then grabs this and splits it down by the #

rule "GK Living Room Lights Brightness"
    when
        Item vLightsGatekeeper_Set changed
    then
    // Add the commands to the Queue
    val GKcommand = triggeringItem.state.toString
    //val GKcommand = vLightsGatekeeper_B.state.toString
    logInfo(logName, "GK_B GKCommand - " + GKcommand)
    val groupName = GKcommand.split("#").get(0)
    logInfo(logName, "GK_B groupName - " + groupName)
    val onoff = GKcommand.split("#").get(1)
    logInfo(logName, "GK_B onoff - " + onoff)
    val brightness = GKcommand.split("#").get(2)
    logInfo(logName, "GK_B brightness - " + brightness)    
    val colortemp = GKcommand.split("#").get(3)
    logInfo(logName, "GK_B colortemp - " + colortemp)
    val group = ScriptServiceUtil.getItemRegistry.getItem(groupName) as GroupItem
    logInfo(logName, "GK_B group - " + group)

LOG

2020-04-30 16:13:38.200 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B GKCommand - gSpots_LR_B_All#ON#100#14
2020-04-30 16:13:38.207 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B groupName - gSpots_LR_B_All
2020-04-30 16:13:38.212 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B onoff - ON
2020-04-30 16:13:38.218 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B brightness - 100
2020-04-30 16:13:38.224 [INFO ] [se.smarthome.model.script.GateKeeper] - GK_B colortemp - 14

It then uses these to send the relevant commands in the timer

// 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))

The color temp part isn’t quite right as I need to tell it to go to the ColorTemp dimmer item but that shouldn’t take too long to sort.

One question, I’m still firing 3 commands to the light in one go, is this the best way to do it or am I again going against the ideal of the GK?

That really all depends on the hue technology and if it can manage it. The underlying problem here is that it doesn’t queue nice. Maybe it does queue different commands nice, maybe it doesn’t.

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.