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
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.
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
@JimT if you find the time, please read through the initial post of this topic. Can JRuby help me out here as well to get rid of synchronisation rules? Would profiles help? The idea is to have one item only receiving values in Mired and the profile converts it to Kelvin before sending it out to the thing. Does this make sense?
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],
channel="deconz:colortemperaturelight:phoscon-gw:Kuechenschrank:color_temperature" [ profile="ruby:mired_to_kelvin" ] }
mired_to_kelvin_or_any_name_you_like.rb
profile(:mired_to_kelvin) do |event, callback:, state:, command:|
case event
when :command_from_item then callback.handle_command((command | "mired" | "K").to_f)
when :state_from_handler then callback.send_update((state | "K" | "mired").to_f)
else next true
end
false
end
In this version, you don’t even need a ruby file, but I’d probably prefer the previous version using a Ruby profile if you have to do this on many items.
I will need to do in on several items, yes.
I tried to understand your code. Where does the actual conversion take place? 1,000,000 / MiretValue = KelvinValue
Well the input (state or command) is a number (DecimalType)
so state | "mired" converts that into a QuantityType with the unit “mired” because that’s the implicit unit of the command coming from Homekit.
Then (state | "mired") | "K" converts that mired QuantityType into its equivalent value in Kelvin unit. Then .to_f converts it to float, stripping the unit.
I’m not 100% sure that callback.handle_command / send_update will accept a float, or whether it needs a DecimalType or String but I think it’ll work as is. The JRuby library is very flexible whenever possible.
Ah, there you are! I did not know the library can handle these types and can handle the conversion directly. This will shrink my code extensively!
As usual thank you very much!
The lamp however shows normal behaviour. It switched to 2200 K temperature when having send 454 Mired to the item. That is nice.
But when restarting openHAB or commenting out the color temperature item from the item file and adding it again I find the value “2200” stored in the item and not “454” anymore. This confuses the homekit addon and it refuses to work. Can this be avoided?
I shall also read more into the documentation the next days.
Yesterday evening I realized that switching from RuleDSL to JRuby won’t be that easy. The concept on which my current rule set is based on is quite different as I would do it now since it has grown over time. Instead of switching “rule by rule” I come to the conclusion that I need to build up my over the time grown system probably from scratch which will need a little bit more effort than expected.
Once I am over the part here of this thread I will deliver an update.
Take a look at the Semantic model. It is very handy in associating related items without using the “naming pattern” trick. For example, you can send a command to all lights in the room where your motion sensor triggered.
Furthermore, jruby library makes working with semantics easier.
Yeah, I think that makes sense. So far I could not really make sense of it as I am using Homekit to actively interact with my system. But for the rules and examples I found (mostly in the docs and examples in the forum by you) it looks very handy.