openHAB proxy items and groups

Posted by

Lights in my home automation setup may be controlled multiple ways: by a physical switch hardwired to the light, by a button on a UI screen, or by a rule in response to some other events. To keep it simple, I combine the design patterns for Proxy Items,for Groups and for Associated Items. I define rules for the desired behavior at the level of a group, and then assign the lights to that group.

With this setup, the proxy item will always correctly reflect the status of the light, independent of what caused that status (command from a rule, gesture on a physical control, gesture on a UI element).

Naming conventions

I always follow this naming convention:

  • the status item is named Basename_Stat and belongs to group gSwS
  • the control item is named Basename_Cmnd and belongs to group gSwC
  • the proxy switch is named Basename_Proxy and belongs to group gSwP
  • optionally, there is an Expire item named Basename_Expire, and if the status item is in group gSwA, then the light will automatically turn off after the set time

So a typical items definition for a light might look like this (this is a custom-made light with an ESP8266 running Tasmota firmware):

Switch PotteryCabinet_Proxy   <light>  (gSwP,gIniOff)
Switch PotteryCabinet_Cmnd             (gSwC)
       {mqtt=">[mosquitto:cmnd/esp8266-B/power:command:*:default]"}
Switch PotteryCabinet_Stat             (gSwS)         
       {mqtt="<[mosquitto:stat/esp8266-B/POWER:state:default]"}
Switch PotteryCabinet_Expire           (gSwX) {expire="10m,command=OFF" }

Rules

Now let’s define how commands sent to the proxy switch affect the physical light, and how the proxy item is affected when the status of the physical light changes, maybe because someone flipped the physical light switch or because of a UI action.

When the proxy item changes, then set the physical light accordingly:

rule "GENERIC: proxy switch received command"
when
    Member of gSwP received command
then 
    val theProxy = triggeringItem
    val theCmndName = theProxy.name.replace("_Proxy","_Cmnd")
    val theCmnd = gSwC.members.findFirst[ t | t.name == theCmndName]
    if (theCmnd !== null) {
        theCmnd.sendCommand(receivedCommand)
    }
end  

When the physical light status changes, then update the proxy item accordingly: If someone turned it OFF, then also cancel the auto-off timer.

rule "GENERIC: real switch received update"
when 
    Member of gSwS received update 
then 
    val theStat = triggeringItem 
    val theProxyName = theStat.name.replace("_Stat","_Proxy")
    val theExpireName = theStat.name.replace("_Stat","_Expire")
    val theProxy = gSwP.members.findFirst[ t | t.name == theProxyName]
    val theExpire = gSwX.members.findFirst[ t | t.name == theExpireName]
    if (theProxy !== null) {
        if (theProxy.state != theStat.state) theProxy.postUpdate(theStat.state)
    }
    if (theExpire !== null) {
        if (theExpire.state==ON && theStat.state==OFF) theExpire.postUpdate(OFF)
    }
end 

When the auto-off timer expires, then turn off the light, via its proxy item.

rule "GENERIC: switch expire"
when 
    Member of gSwX received command
then 
    val theExpire = triggeringItem 
    val theProxyName = theExpire.name.replace("_Expire","_Proxy")
    val theProxy = gSwP.members.findFirst[ t | t.name == theProxyName]
    if (theProxy !== null) {
        if (theProxy.state != receivedCommand) theProxy.sendCommand(receivedCommand)
    }
end

If someone turned ON the light, and it is configured for auto-off, i.e. if Basename_Stat is in group gSwA, then start the expiration timer:

rule "GENERIC: start auto-expire"
when 
    Member of gSwA changed to ON
then 
    val theStat = triggeringItem 
    val theExpireName = theStat.name.replace("_Stat","_Expire")
    val theExpire = gSwX.members.findFirst[ t | t.name == theExpireName]
    if (theExpire !== null) {
        theExpire.sendCommand(ON)
    }
end 

Leave a Reply