Design Pattern: Proxy Item

Hello, I have been struggling with correct settings of my new setup and this design pattern (Proxy / Virtual item) is exactly what I needed. It took me a while until I fully understood it to apply to my case and finally have it working. I have improved it with what I have learnt from @rlkoshak in another topic about PIR alarm, by using a group of items and triggeringItem and would like to share it for anyone else who would be searching for such solution.

My setup:
I have a relay (actually 2 x 8 relays modules = 16 relays) which are connected to Arduino MEGA and those relays are connected to my room(s) lights. I have also wall switches, also connected to MEGA.

The MEGA is then communicatig via serial with ESP8266 which is then publishing and subscribing to MQTT topics managed by a mosquitto broker on my RPI3 where also Openhabian is running.

My approach was to keep things running even if Openhab would be offline for any reason so Wall switches are toggling states of relays directly on MEGA. So the MEGA is the only point of failure which could be easily replaced by another one which (will be :slight_smile: ) ready for that.

To be able to add Openhab in, I had to connect MEGA to the wifi. I decided to use ESP8266 which is forwarding commands to relays (from Openhab etc.) and states of relays (to Openhab). I am exchanging these between MEGA and ESP via EasyTransfer library for Arduino which is basically serial communication where I am exchanging only Pin ID and Pin Value (e.g. Pin ID = 22 and Pin Value = 1 means relay connected to pin D22 will switch OFF (this would actually switch the relay ON due to inverted logic on relays but that is not important here).

So whenever my wall switch toggles the relay (living room light) state, I am sending this information from MEGA to ESP and publishing this change of relay’s state to a relevant topic (e.g. house/livingroom/lightState)

The rule then updates my virtual switch item in openhab to the new state.

And vice versa, when my virtual item switch (or a rule or a mobile app) wants to toggle the relay/light, the rule (which could make some additional checks) forwards this command via MQTT topic (e.g. house/livingroom/lightCommand) to ESP which then forwards it to MEGA which finally toggles the relay to desired state and replies back with the new state to confirm command received.

I decided to use two separated MQTT topics to avoid loops which was my issue when I tried to use a simpler way of having only one switch item subscribed and publishing it’s state to one same topic, not even using a virtual item.

So you could easily use a one rule for a one set of relay/switch, but in my case this would be about 12+ sets so 12+ x rules. Very inefficient, hard to maintain etc. So I used the group way that I learnt from Rich (thanks again) and improved that for me.

So my items (simplified and generalized) are:

//Lights
Group:Switch:OR(ON, OFF) gCommands "All relays [(%d)]"
Group:Switch:OR(ON, OFF) gVirtualSwitches "All virtual switches [(%d)]"

Switch LRLights_State		    "Living room lights state"        <switch> 	                {mqtt="<[mosquitto:house/livingroom/lightState:state:default]"}
Switch LRLights_Command		    "Living room lights command"      <switch>    (gCommands)     {mqtt=">[mosquitto:house/livingroom/lightCommand:command:*:default]"}
Switch LRLights_VirtualSwitch    "Living room lights virtual switch"      <light>     (gVirtualSwitches)

And my rules are:

rule "VirtualSwitch received command" //so I want to toggle relay via openhab/mobile app, rule etc.
when
    Item LRLights_VirtualSwitch received command// or
    //Item LRLights2_VirtualSwitch received command 
    //...
    
then
        //logInfo("light.rules switch received command", "STARTED")
     // getting here the command Item object related to the same lights virtual switch which triggered this rule    
     val relCommand = gCommands.members.findFirst[c| c.name == triggeringItem.name.split("_").get(0) + "_Command"]
        //logInfo("light.rules switch received command", "triggeringItem.name=[{}]",triggeringItem.name)
        //logInfo("light.rules switch received command", "virtSwitch.name=[{}]",triggeringItem.name)
    
    switch(triggeringItem.name){
        case "LRLights_VirtualSwitch": {
                //logInfo("light.rules switch received command", "case: LRLights_VirtualSwitch")
            if(receivedCommand == ON) {
                // additional ON logic, e.g. check presence, time of day, motion detectors, set/unset a flag, etc.
                relCommand.sendCommand(ON) // forward the command
                    //logInfo("light.rules switch received command", "send command: ON to:[{}]",relCommand.name)
            }
            else {
                // additional OFF logic, e.g. check presence, time of day, motion detectors, set/unset a flag, etc.
                relCommand.sendCommand(OFF) // forward the command
                    //logInfo("light.rules switch received command", "send command: OFF to:[{}]",relCommand.name)
            }
        }
        //case "LRLights2_VirtualSwitch": {}
        //....   
 }
end

rule "State item received update"// so the relay state has changed, via virtual switch above or via wall switch, so ESP is informing (confirming) openhab about it's new state
when
    Item LRLights_State received update //or
    //Item LRLights2_State received update or
    //...
then
    //getting here the virtual switch item object which is related to the relay/light that changed it's state so I can update the new state to the virtual switch    
    val virtSwitch = gVirtualSwitches.members.findFirst[c| c.name == triggeringItem.name.split("_").get(0) + "_VirtualSwitch"]
        logInfo("light.rules state received update", "triggeringItem.name=[{}]",triggeringItem.name)
    
    switch(triggeringItem.name){
        case "LRLights_State": {
            if(triggeringItem.state == ON) {
                // additional ON logic, e.g. check presence, time of day, motion detectors, set/unset a flag, etc.
                virtSwitch.postUpdate(ON) // forward the command
            }
            else {
                // additional OFF logic, e.g. check presence, time of day, motion detectors, set/unset a flag, etc.
                virtSwitch.postUpdate(OFF) // forward the command
            }
        }
        //case "LRLights2_State": {}
        //...    
}
end
3 Likes