Multiway switch

Currently I’m using some PLC-like on RaspberryPI for control our house and now evaluating OpenHab to change into it.

In most rooms I have lights controlled by multiple switches. For simplicity imagine wall-switches (not push buttons) all connected as inputs to GPIO. And light controlled by relay connected as output to GPIO.

E.g. for light at stairway I have one switch upstairs, second switch downstairs and third switch in web-page interface.
Then I have something like:

StairwayLight = SwitchUpstairs xor SwitchDownstairs xor SwitchWeb 

What is recommended way to do this in OpenHab?
I’m curious that I didn’t find any sample nor question with such topics, so probably I search wrong terms?

Is possible (and good idea) to write it as?:

rule "xyz"
when
    Item SwitchUpstairs changed
    or Item SwitchDownstairs changed
    or Item SwitchWeb changed        //should be handled differently in OpenHab?
then
    if(SwitchUpstairs.state == ON ^^ SwitchDownstairs.state == ON ^^ SwitchWeb.state == ON)
	sendCommand(StairwayLight, ON)
    else 
        sendCommand(StairwayLight, OFF)
end

because I didn’t see way to write it as group:

    Group:Switch:XOR(ON,OFF) ...

Thanks

You are correct, there is no XOR operator for Groups so you do have to write the logic as a Rule.

You probably haven’t seen this particularly problem addressed because almost no (perhaps none) commercial multi-switches work like this. This sort of logic is either built into the switches themselves or, more typically, built into how they are wired to the light. So the problem hasn’t come up.

Also, there is no logical XOR operator in Java and I couldn’t find anything about one being in Xtend either. In other words, I don’t think ^^ is a thing (I could be wrong). But I’m not really sure you need that nor am I certain XOR is the correct operator.

For the most part aren’t you just looking to turn the light on is any of the other switches are ON? This is typically how multi-switches work.

If that is the case you can do something like the following:

Put the Items into a Group. You can use Group:Switch:OR(ON,OFF) gStairwayLight to show the Group’s state as ON if any of the three are ON and OFF only if all of the three are OFF. However, you still need the Rule but can write it as

NOTE: I’m just typing this in, it may not work as written.

rule "StairwayLight"
when
    Item gStairwayLight received update
then
    gStairwayLight.members.forEach[SwitchItem sw |
        if(sw.state == ON) {
            if(StairwayLight.state != ON) StairwayLight.sendCommand(ON)
            return
        }
    ]

    // If we got this far, all the switches are OFF
    if(StiarwayLight.state != OFF) StairwayLight.sendCommand(OFF)
end

However, if to only turn the light on if all the switches are ON. In that case you can just swap the logic around a bit.

Change the Group to Group:AND(ON,OFF) gStairwayLight which will set the Group’s state of ON if all the switches are ON and OFF if any of the Switches are OFF.

Then the Rule becomes:

rule "StairwayLight"
when
    Item gStairwayLight received update
then
    gStairwayLight.members.forEach[SwitchItem sw |
        if(sw.state == OFF) {
            if(StairwayLight.state != OFF) StairwayLight.sendCommand(OFF)
            return
        }
    ]

    // If we got this far, all the switches are ON
    if(StairwayLight.state != ON) StairwayLight.sendCommand(ON)
end

However, if you really do mean XOR (i.e. if 0 < number of switches that are ON < total number of switches, turn on the light. The Group won’t really work in this case but a Rule would be:

rule "StairwayLight"
when
    Item gStairwayLight received update
then
    val numON = gStairwayLight.members.filter[SwitchItem sw|sw.state == ON].size
    val newState = if(numOn != 0 && numON != gStairwayLight.members.size) ON else OFF

    if(StairwayLight.state != newState) StairwayLight.sendCommand(newState)
end

EDIT: I did some more reading and the bitwise XOR can be used as a logic XOR as well so the command would be:

if(SwitchUpstairs.state == ON ^ SwitchDownstairs.state == ON ^ SwitchWeb.state == ON)

Which, if I have my order of operations right is equivalent to

if((SwitchUpstairs.state == ON ^ SwitchDownstairs.state) ^ SwitchWeb.state == ON)

Thanks for response. Really mean and need xor - change state of light if any of switches are switched.
I understand that typically it is wired like in https://en.wikipedia.org/wiki/Multiway_switching but expected that for “inteligent house” isn’t needed to interconnect switches.

So something like?:

rule "StairwayLight"
when
    Item gStairwayLight received update
then
    bool result = false
    gStairwayLight.members.forEach[SwitchItem sw |
        if(sw.state == ON) {
           result = !result
        }
    ]

    if(result)
	sendCommand(StairwayLight, ON)
    else 
        sendCommand(StairwayLight, OFF)
end

@dangif,

I think your original approach was close to working. I’m no java coder, but java does have a bitwise exclusive-or operator, ^, which should behave as expected when applied to instances of boolean.

You might give the following a go:

rule "xyz"
when
    Item SwitchUpstairs changed
    or Item SwitchDownstairs changed
    or Item SwitchWeb changed        //should be handled differently in OpenHab?
then
    if((SwitchUpstairs.state == ON) ^ (SwitchDownstairs.state == ON) ^ (SwitchWeb.state == ON))
	sendCommand(StairwayLight, ON)
    else 
        sendCommand(StairwayLight, OFF)
end

Note that I added parens to ensure the bitwise xor was applied to the results of your equivalence tests.

Cheers!

@rlkoshak You are faster with edit than I with response. :slight_smile:

Probably solution by group can be better that I can freely add/remove switches to group, without altering rule.
Is there difference in terms of speed? As I want raspberry underclocked to stay cold.

Now I must look how to solve “SwitchWeb” in e.g. HABDroid, if I want to see on one line current light state and control to toggle it.
So, what if I put into the group only SwitchUpstairs, SwitchDownstairs. In site map I will have only StairwayLight, and write rule only to toggle?:

rule "StairwayLight"
when
    Item gStairwayLight received update
then
    if(StairwayLight.state == ON)
	sendCommand(StairwayLight, OFF)
    else 
        sendCommand(StairwayLight, ON)
end

Or is this totally wrong idea?

I don’t follow why the state of the switches needs examining? Isn’t the idea that if any switch changes state, the light should change state? i.e. the switches have no particular on or off position from a user point of view, you just move the lever and the light changes.

rule "xyz"
when
    Item SwitchUpstairs changed
    or Item SwitchDownstairs changed
    or Item SwitchWeb changed
then
    if(StairwayLight.state == OFF) {
	sendCommand(StairwayLight, ON)
    } else {
        sendCommand(StairwayLight, OFF)
    }
end

You might choose to do something different with the web / UI switch though.

@dangif and @rossko57, I think you nailed it. I didn’t see that forest for the trees :wink:

I don’t think XOR will quite do that for you. Keep in mind that XOR works on only two operands at a time and works from left to right. So if you write the truth table for a three item XOR in sequence like that you get.

‘’'
A B. C. Result
Off off off off
On off off on
Off on off on
Off off on on
On on off off
On off on off
Off on on off
On off on off
On on on on

I’m pretty sure that is not the behavior you are looking for.

To explain one of the word cases.

On on on:

On xor on = off. Off xor on = on

The problem is because it evaluates the first two operands and then uses the result of that operation with the last operand.

From your latest description it sounds like you don’t care what state the switches are in. If any of them change state your want to toggle the light.

That is pretty easy too but using the group update to trigger the rule is s problem because the group receives multiple updates for each event. So the rule becomes:

‘’‘
rule "light"
when
Item Switch1 received command or
Item Switch2 received command or
Item Switch3 received command
then
Light.sendCommand(if(Light.state ==ON) OFF else ON)
end
’’’

there difference in terms of speed?

If there is it is not enough to matter.

Or is this totally wrong idea?

Unfortunately this is one case where you can’t use groups because one change in a group’s Item generates multiple updates. And since you don’t care about the switch’s absolute state, only the fact that one of them changed, there is no clear way to filter out the extra events. Consequently your lights would flicker and may or may not end up where you want them.

However, with two extra lines of code my rule from above will work. Assuming Light3 is your WebSwitch:

‘’‘rule "light"
when
Item Switch1 received command or
Item Switch2 received command or
Item Switch3 received command
then
val newState = if(Light.state == ON) OFF else ON
Light.sendCommand(newState)
Switch3.postUpdate(newState)
end
’’’

By using postUpdate we can change the state of Switch3 to always match the relay without retriggering the rule. So if you put Switch3 on your sitemap you can use this one Item to know the state of the light and to turn the light off and on. No need for separate status and control lines in the sitemap.

Note, typed all of the above on my phone. Pardon the errors.

When rule react on “changed” only, it turns light during startup, which is unacceptable, so on my test-bed I ended with:

rule "light"
when
    Item sw1 changed from OPEN to CLOSED
    or Item sw1 changed from CLOSED to OPEN
    or Item sw2 changed from OPEN to CLOSED
    or Item sw2 changed from CLOSED to OPEN
then
    light.sendCommand(if(light.state == ON) OFF else ON)
end

and items are:

Contact sw1 "sw1" { gpio="pin:19" }
Contact sw2 "sw2" { gpio="pin:26" }
Switch light "light" { gpio="pin:18" }

This works as wanted, light is toggled on any change of contacts (wall-switches), and from mobile can control directly the light.

There stay minor problem:
When change both contacts at same time, the rules react in parallel, so light is toggle only once, as “both rules” read same old state and set same new state.
I tried solve this by lock/unlock:

rule "light"
when
    Item sw1 changed from OPEN to CLOSED
    or Item sw1 changed from CLOSED to OPEN
    or Item sw2 changed from OPEN to CLOSED
    or Item sw2 changed from CLOSED to OPEN
then
    logWarn("gt", "begin")
    lock.lock()        
    logWarn("gt", "begin2")
    try {
        light.sendCommand(if(light.state == ON) OFF else ON)
    } finally{
        logWarn("gt", "end")
        lock.unlock()
    }
  end

But probably even if i guard the sendCommand, it looks like it didn’t guard the real “ItemStateChangedEvent”.
So sometimes the “second rule” still can read state before first rule applied.

Worse problem is, that I can achieve such problem when physical contact bounce:
Will examine this more, with this I crete new thread Contact bounce - lost events?

Hi rikoshak, I know this is an old thread but I am trying a two way switch with a third switch in the Habpanel.
The two Sonoff T1 switches are in a group called “Lounge_Light”. Below is the rule I created from your rule above but the log get hammered with “2018-07-02 10:26:52.497 [ome.event.ItemCommandEvent] - Item ‘SW2_02_1’ received command ON”

Here is the Rule
"
rule “Lounge_light”
when
Item SW3_01_1 received command or
Item SW2_02_1 received command
then
val newState = if(SW3_01_1.state == ON) OFF else ON
SW2_02_1.sendCommand(newState)
Lounge_Light.postUpdate(newState)
end
"

Any help appreciated

Note the endless loop - trigger on command, send command, trigger, send, etc.

This was avoided in earlier rule example where the switch Items representing user controls are different from the switch Item actually controlling the light. Depending on how you bind to the Sonoffs, can you arrange that? i.e. an input-only user-button reporting Item and a separate power control and status Item.

Without knowing what your Items actually are, do you need the rule to always act on the buttons, are they controlling the lamp directly without needing OpenHAB action?

Sorry, I’m totally new to this and don’t really understand what you mean. I can see the loop for sure but the rest went over my head. They will go in a stairway type configuration.
I have 2 Sonoff T1 swiches. They are items SW3_01_1 and SW2_02_1. At the moment they are just on the bench but when installed, SW3_01 will supply voltage to turn the light on/off and SW2_2_01 will only work via a rule (not actually connected to light).

The T1’s have a nice light when the relay is activated on, so I would like to mirror the switch activity.
When SW3_01_1 goes on then SW2_2_01 goes on
When SW3_01_1 goes off then SW2_2_01 goes off and the same from the other switch.

Does that make sense?

For further clarification here is my items file for the sonoff items.
"
//Sonoff T1- 3 Gang #1
Switch SW3_01_1 “Lounge Light SW1” (Lounge_Light){ mqtt=">[broker:cmnd/SW3_01/power1:command::default],<[broker:stat/SW3_01/POWER1:state:default]" }
Switch SW3_01_2 “Dinning Light SW1” (Living_Lights){ mqtt=">[broker:cmnd/SW3_01/power2:command:
:default],<[broker:stat/SW3_01/POWER2:state:default]" }
Switch SW3_01_3 “Kitchen Light SW1” (Living_Lights){ mqtt=">[broker:cmnd/SW3_01/power3:command:*:default],<[broker:stat/SW3_01/POWER3:state:default]" }

//Sonoff T1- 2 Gang #1
Switch SW2_01_1 “Dinning Light SW2” (Living_Lights){ mqtt=">[broker:cmnd/SW2_01/power1:command::default],<[broker:stat/SW2_01/POWER1:state:default]" }
Switch SW2_01_2 “Kitchen Light SW2” (Living_Lights){ mqtt=">[broker:cmnd/SW2_01/power2:command:
:default],<[broker:stat/SW2_01/POWER2:state:default]" }

//Sonoff T1- 2 Gang #2
Switch SW2_02_1 “Lounge Light SW2” (Lounge_Light){ mqtt=">[broker:cmnd/SW2_02/power1:command::default],<[broker:stat/SW2_02/POWER1:state:default]" }
Switch SW2_02_2 “Front Light Right” (Living_Lights){ mqtt=">[broker:cmnd/SW2_02/power2:command:
:default],<[broker:stat/SW2_02/POWER2:state:default]" }

I think so. You have two power control relays, A & B. Only one controls a real lamp, but you want both to work together because there is handy LED.

Each of the relays can be manually controlled, and you want that manual action to be “copied” to the other one.

Set up a pair of rules that trigger on changes in your Item state (rather than commands or simple updates).
The rule should then sendCommand the new state to the opposite switch.

rule “update lamp B”
when
   Item lamp_A changed
then
   if ( lamp_A.state == ON ) {
      lamp_B.sendCommand(ON)
   } else {
      lamp_B.sendCommand(OFF)
   }
end

There’s many ways to do that, but that one should behave sensibly in case of errors.
You’d have another mirror-image rule to copy changes the other way B -> A

Because it works on changes and not just events it shouldn’t get in a loop… A good test is starting off with the two relays in different states, they should synch up at first manual action.

For neatness, you could even write a system started rule that looked at a ‘master’ relay current state, and forced the slave relay to that state as well initially.

Thanks for the advice, with this. The ON syncs for both switches but not the OFF. Ie Sw A turns on Sw B and Sw B turns on Sw A but each only turn themselves off. Any help apreciated

The rule trigger syntax includes e.g. changed to ON or changed to ON , if you need different behaviours

Edit - changed to OFF I meant, but I think I misunderstood your complaint as a request.
Note that my previous suggestion was for a pair of rules, and I gave one example, leaving you to figure out its partner.

Sorry, I dont understand what you mean. I added the rule and made an opposite rule for the other light and I have tried the rule and it only works as I described. On is in sync, Off is not

rule “update lamp B”
when
   Item lamp_A changed
then
    lamp_B.sendCommand(lamp_A.state.toString)
end

rule “update lamp A”
when
   Item lamp_B changed
then
    lamp_A.sendCommand(lamp_B.state.toString)
end
1 Like

Cheers Vincent, Ill try that tonight and let you know

Thank you Vincent, that worked perfectly.
I really appreciate it,
JD