Synchronize 2 dependent items

Hi,

I have a standard CCT LED controller connected via a Phoscon gateway.
Using the deconz binding I can without any issues control the LED via openHAB using the following 2 items:

Number          Farbtemperatur_Kueche
                "Farbtemperatur LED Küche"                             
                { channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:color_temperature" }

Dimmer          Helligkeit_LED_Kueche
                "Helligkeit LED Küche"
                (gKuechenLED)                
                {   channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:brightness" }

The color temperature is stored and expected in Kelvin by the deconz binding
With the brightness channel I am also able to switch on/off the lights.

However, I want to control the LED only via homekit using the respective binding and now there are some problems. I was able to make it work somehow but I am not sure whether I am doing this correctly.

  • Homekit expects color temperatures in Mired instead of Kelvin
  • Homekit expects a dedicated “On/Off state” variable of Switch type to switch lights on/off (it does not accept Dimmer types)

The items now look as follows:

Group           gKuechenLED
                "LED Küchenschrank"    
                {homekit="Lighting"}    

Switch          Kueche_LED_SyncTimer                
                {expire="5s,command=OFF"}

Number          Farbtemperatur_Kueche_homekit
                "Farbtemperatur LED Küche für Homekit"
                (gKuechenLED)              
                { homekit="Lighting.ColorTemperature" [minValue=147, maxValue=455] }

Number          Farbtemperatur_Kueche
                "Farbtemperatur LED Küche"                             
                { channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:color_temperature" }

Dimmer          Helligkeit_LED_Kueche
                "Helligkeit LED Küche"
                (gKuechenLED)                
                {   channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:brightness",
                    homekit="Lighting.Brightness"
                }

Switch          Status_LED_Kueche
                "Status LED Küche"
                (gKuechenLED)
                { homekit="Lighting.OnState" }

I need a mechanism which switches off/on the LEDs based on the “OnState” variable.

Apparently I need to synchronize the dependent items but I have no idea how this is safely done.
What I am doing right now looks as follows. Could you please tell me whether this makes sense or is complete rubbish? I use a sync timer and some sleep commands to make sure that items only get updated when the referenced item was changed by the user. By this I want to avoid an infinite loop of items changing each other. Does this make sense?

rule "Lichter: Status LED Küche in Homekit aktualisieren"
when Item Helligkeit_LED_Kueche changed
then
        logInfo("Lichter: Status LED Küche in Homekit aktualisieren", "Helligkeit_LED_Kueche changed to " + Helligkeit_LED_Kueche.state)
        if(Kueche_LED_SyncTimer.state == ON)
                return;

        if ((Helligkeit_LED_Kueche.state as Number).intValue > 0) {
                Status_LED_Kueche.sendCommand(ON)
        } else {
                Status_LED_Kueche.sendCommand(OFF)
        }
        Kueche_LED_SyncTimer.sendCommand(ON)
        Thread::sleep(2000)
end

rule "Lichter: LED Küche in deconz einschalten"
when Item Status_LED_Kueche changed
then
        logInfo("Lichter: LED Küche in deconz einschalten", "Status_LED_Kueche changed to " + Status_LED_Kueche.state)
        if(Kueche_LED_SyncTimer.state == ON)
                return;
        
        if(Status_LED_Kueche.state == ON) {
                Helligkeit_LED_Kueche.sendCommand(100)                             
        } else {
                Helligkeit_LED_Kueche.sendCommand(0)
        }
        Kueche_LED_SyncTimer.sendCommand(ON)
        Thread::sleep(2000)        
end

The sleeps give me some concerns.

Typically the biggest problem we have is an infinite loop, which you correctly identify. However, you’ve done a lot already to avoid that but only triggering the rules based on a change. The other standard thing to do is to not command an Item to a state it’s already in. So check to see if the Item is ON before commanding it ON.

Beyond that, the sleep and SyncTimer shouldn’t be required. They certainly are not stopping any infinite loops. Something like this would be the minimum required to avoid infinite loops.

rule "Lichter: Status LED Küche in Homekit aktualisieren"
when Item Helligkeit_LED_Kueche changed
then
        logInfo("Lichter: Status LED Küche in Homekit aktualisieren", "Helligkeit_LED_Kueche changed to " + Helligkeit_LED_Kueche.state)

        val command = if(Helligkeit_LED_Kueche.state as Number) > 0) ON else OFF // on liner if/else
        if(Status_LED_Kueche.state != command) Status_LED_Kueche.sendCommand(command)
end

rule "Lichter: LED Küche in deconz einschalten"
when Item Status_LED_Kueche changed
then
        logInfo("Lichter: LED Küche in deconz einschalten", "Status_LED_Kueche changed to " + Status_LED_Kueche.state)

        val command = if(Status_LED_Kueche.state == ON) 100 else 0
        if(Helligkeit_LED_Kueche.state != command) Helligkeit_LED_Kueche.sendCommand(command)
end

That will be sufficient to avoid inifinite loops and limit the number of commands sent particularly in the case where the Helligkeit_LED_Kueche Item changes gradually.

All the sleep will do is delay the time between rule runs since only one instance of a given rule can run at a time and subsequent triggers get queued and run off in turn. And in some ways, that sleep is working against the SyncTimer.

You don’t really need either, but you definitely don’t need both.

1 Like

Rich, as always, thank you for the time you invested in my question. :blush:
This was never clear to me so far.

I will give it a try tomorrow but I‘m pretty sure it will work as expected.

So, I just tested it and Rich’s solution works. It is very elegant!

I have two other items which needed synchronization and I think I finally managed it. They were not so easy to synchronize like the brightness. I will share the example below in case one day someone stumbles upon the same issue.

It is about syncing the color temperature. openHAB expects it in Kelvin and Homekit in Mired. For sure I want both systems always be synchronized. I use two proxy variables defined as global and use them to find out which of the two items (openHAB-Kelvin or Homekit-Miret) were changed by the user. This avoids an infinite loop:

The items

Group:Number    gFarbtemperaturKueche
                "Farbtemperatur Küche"

Number          Farbtemperatur_Kueche_homekit
                "Farbtemperatur LED Küche für Homekit"
                (gKuechenLED, gFarbtemperaturKueche)              
                { homekit="Lighting.ColorTemperature" [minValue=158, maxValue=495] }

Number          Farbtemperatur_Kueche
                "Farbtemperatur LED Küche"      
                (gFarbtemperaturKueche)                       
                { channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:color_temperature" }

The rule

var Integer farbTempKueche = 2200
var Integer farbTempKuecheHomekit = 455

rule "Lichter: Farbtemperatur LED Küche synchronisieren"
when Member of gFarbtemperaturKueche changed
then               
        var String msg = "Farbtemperatur geändert. " + triggeringItem.name + ": " + triggeringItem.state + " "
        if(triggeringItem.name == "Farbtemperatur_Kueche_homekit") {
                farbTempKuecheHomekit = (Farbtemperatur_Kueche_homekit.state as Number).intValue
                farbTempKueche = 1000000 / (Farbtemperatur_Kueche_homekit.state as Number).intValue
        } else {
                farbTempKueche = (Farbtemperatur_Kueche.state as Number).intValue
                farbTempKuecheHomekit = 1000000 / (Farbtemperatur_Kueche.state as Number).intValue                
        }

        if(triggeringItem.name == "Farbtemperatur_Kueche_homekit" && (Farbtemperatur_Kueche.state as Number).intValue != farbTempKueche) {                
                Farbtemperatur_Kueche.sendCommand(farbTempKueche)
                msg += "Mired. Farbtemperatur_Kueche: " + farbTempKueche + " K"                
                logInfo("Lichter: Farbtemperatur LED Küche synchronisieren", msg)   
        } else if(triggeringItem.name == "Farbtemperatur_Kueche" && (Farbtemperatur_Kueche_homekit.state as Number).intValue != farbTempKuecheHomekit) {                
                Farbtemperatur_Kueche_homekit.sendCommand(farbTempKuecheHomekit)
                msg += "K. Farbtemperatur_Kueche_homekit: " + farbTempKuecheHomekit + " Mired"   
                logInfo("Lichter: Farbtemperatur LED Küche synchronisieren", msg)                
        }        
        
end