Two switches one light eg. staircase switch - rule

what’s your simpliest, cleanest and most elegant way how to do it over MQTT?

I came up with this

Thing mqtt:topic:SwitchA "A" (mqtt:broker:home) @ "Room" {
Channels:
    Type switch : switch        "A"  [ stateTopic="sonoffA/stat/POWER", commandTopic="sonoffA/cmnd/POWER", on="ON", off="OFF" ]
}

Thing mqtt:topic:SwitchB "B" (mqtt:broker:home) @ "Room" {
Channels:
    Type switch : switch        "Porch Lights"  [ stateTopic="sonoffB/stat/POWER", commandTopic="sonoffB/cmnd/POWER", on="ON", off="OFF" ]
}

Switch  Switch_A    "A"  <light>       { channel="mqtt:topic:SwitchA:switch" }
Switch  Switch_B    "B"  <light>       { channel="mqtt:topic:SwitchB:switch" }

rule "switches"
when
    Item Switch_A changed or
    Item Switch_B changed
then
    val stateA = Switch_A.state
    val stateB = Switch_B.state
    val newA = ""
    val newB = ""

    if(stateA == "OFF"){
        newB = "OFF"
    }
    if(stateB == "OFF"){
        newA = "OFF"
    }
    if(stateA == "ON"){
        newB = "ON"
    }
    if(stateB == "ON"){
        newA = "ON"
    }

    Switch_A.postupdate(newA)
    Switch_B.postupdate(newB)

end

This is not tested rule, it’s just now from my head as I havent put my switches on place yet.
Idea is that one switch is connected to actual light and one or n switches is not connected to anything, but receiving/sending signals so connected switch can change it’s state
I do like elegant solutions, and this is not the one :stuck_out_tongue:

That rule is most probably wrong, I would appreciate if somebody have better solution how to tell main or dummy switches to put them to on/off accordingly
Thanks

The follow Profile was made for this use case. It would look something like:

Switch MyLight "My Light" <light> { channel="mqtt:topic:SwitchA:switch", channel="mqtt:topic:SwitchB:switch"[profile="follow"] }

If done in a Rule, it can be made simpler in a number of ways.

  1. Go ahead and use more than one Rule
rule "Switch A"
when
    Item Switch_A changed
then
    if(Switch_B.state != Switch_A.state) Switch_B.postUpdate(Switch_A.state)
end

rule "Switch B"
when
    Item Switch_B changed
then
    if(Switch_B.state != Switch_A.state) Switch_A.postUpdate(Switch_B.state)
end

Python version:

from core.rules import rule
from core.triggers import when
from core.utils import sendCommandCheckFirst

@rule("Switch_A changed")
@when("Item Switch_A changed")
def switch_a(event):
    sendCommandCheckFirst("Switch_B", event.itemState)

@rule("Switch_B changed")
@when("Item Switch_B changed")
def switch_b(event):
    postUpdateCheckFirst("Switch_A", event.itemState)
  1. That’s already simpler. But it’s not DRY (Don’t Repeat Yourself)
import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "switches"
when
    Item Switch_A changed or
    Item Switch_B changed
then
    val other = if(triggeringItem.name == "Switch_A") "Switch_B" else "Switch_A"
    val otherItem = ScriptServiceUtil.getItemRegistry.getItem(other)
    if(otherItem.state != triggeringItem.state) otherItem.postUpdate(triggeringItem.state)
end

Python version

from core.rules import rule
from core.triggers import when
from core.utils import sendCommandCheckFirst

@rule("switches")
@when("Item Switch_A changed")
@when("Item Switch_B changed")
def switch_a(event):
    postUpdateCheckFirst("Switch_A" if event.itemName == "Switch_B" else "Switch_B", event.itemState) 

Not too bad. In the python version it’s just one line of logic code and three for Rules DSL.

Unfortunately that is correct, because it does in fact matter which Item changed so you update the other switch to that. As written if Switch_B is ON, Switch_A will always be set to ON even if the event that triggered the Rule was A turning OFF.

sweet thanks, didnt knew about follow profiles… can both items follow each other or just one is following master?

edit: ah … well it’s not good idea to follow each other as it will result in very ugly loop … so rules then

2019-07-31 23:31:14.516 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-07-31 23:31:14.562 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF

rule is working, but just for a status in OH, it’s not sending any cmnd topic to Switch_A when B is changed.
How to send command as well?

Well, you’d send a command - not a postUpdate

Bindings generally listen for commands to pass to devices, and make Item state updates in response to incoming data.

hmm when I do that, it’s complaining
eventho switch A and B are initialized, having OFF values, and when triggered from OH they works.

rule "Switch A"
when
    Item Switch_A changed
then
    if(Switch_B.state != Switch_A.state) Switch_B.sendCommand(Switch_A.state)
end
2019-08-01 00:13:16.068 [ome.event.ItemCommandEvent] - Item 'Switch_A' received command ON
2019-08-01 00:13:16.093 [nt.ItemStatePredictedEvent] - Switch_A predicted to become ON
2019-08-01 00:13:16.145 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 00:13:16.139 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Switch A': An error occurred during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.BusEvent.sendCommand(org.eclipse.smarthome.core.items.Item,java.lang.Number) on instance: null

Are you certain the items are not NULL or UNDEF? Log out the states of both items.

I think this is just one of those things where you can’t send OnOffType state as command

Switch_B.sendCommand(Switch_A.state.toString)

to make state ON look like command ON

thank you milliontimes indeed, .toString did the trick

@rossko57 Thanks for opening the feature request on GitHub. Some thoughts which I like to share here first.

Is it? Some other user tested it and told just the opposite:

What happens if you use the getStateAs method?

Switch_B.sendCommand(Switch_A.getStateAs(OnOffType))

I’ve done this:

Switch  Switch_A    "A"  <light>       { channel="mqtt:topic:SwitchA:switch", channel="mqtt:topic:SwitchB:switch"[profile="follow"]}
Switch  Switch_B    "B"  <light>       { channel="mqtt:topic:SwitchB:switch", channel="mqtt:topic:SwitchA:switch"[profile="follow"]}

which resulted both switches toggling on/off in like every half second or so. Not sure why.
Maybe I’ll give it a shot once again. Maybe it’s looping because of MQTT slight delay?

edit … I mean, yeah :smiley:

2019-08-01 14:31:23.916 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:24.023 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:24.027 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:24.288 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:24.351 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:24.353 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:24.413 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:24.584 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:24.615 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:24.667 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:24.717 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:24.839 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:24.922 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:25.004 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:25.068 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:25.247 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:25.263 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:25.267 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:25.339 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:25.481 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:25.617 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:25.664 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:25.801 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:25.913 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:25.978 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:25.984 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:26.035 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:26.237 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:26.338 [vent.ItemStateChangedEvent] - Switch_A changed from OFF to ON
2019-08-01 14:31:26.403 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF
2019-08-01 14:31:26.490 [vent.ItemStateChangedEvent] - Switch_B changed from OFF to ON
2019-08-01 14:31:26.615 [vent.ItemStateChangedEvent] - Switch_A changed from ON to OFF
2019-08-01 14:31:26.693 [vent.ItemStateChangedEvent] - Switch_B changed from ON to OFF

some observations:
It will crap itself after one of those switches is triggered, when they go from ON to OFF it turn OFF second of them, but OH keeps spamming MQTT with OFF
Mayhem starts when one switch is triggered to ON, which result to upper posted ON/OFF loop, mqtt spammed, switches are physically triggered.

Which I believe is because OH keep spamming OFF from first ON to OFF action.

Running 2.5.0-SNAPSHOT (#1650)

I believe with profiles you only define 1 item to control both channels, at least that is what I did with two lightbulbs and haven’t had any issues.

Switch SwitchA_B "A & B" <light> { channel="mqtt:topic:SwitchA:switch", channel="mqtt:topic:SwitchB:switch"[profile="follow"]} 

damn, you are kind of correct
it works… sort of

when it’s triggered from OH it’s obviously OK as it’s triggered by Swtich A

but when you press physically button on Switch B which is not defined, it won’t trigger Switch A
physical push of Switch A, triggers Swtich B just fine …
So yeah, it needs bit more love

Switch  Switch_A    "A"  <light>       { channel="mqtt:topic:SwitchA:switch", channel="mqtt:topic:SwitchB:switch"[profile="follow"]}

In my case I have this for my Lifx items:

Dimmer  lightdimV      "dimmer"  (gPersist)            { channel="lifx:whitelight:bedroom1:brightness", channel="lifx:whitelight:bedroom2:brightness" [profile="follow"] }
Switch  lightV         "Lifx bulbs"    (gPersist)      { channel="lifx:whitelight:bedroom1:brightness", channel="lifx:whitelight:bedroom2:brightness" [profile="follow"] }

yes because bulb is just passive item
but I need to have two or more wallswitches cooperate, which means there is no just one master and rest are following him, but every switch can act as master and rest have to follow it

@kriznik

I tested it with two Philips Hue bulbs and was successful. Maybe the MQTT is the problem?

here is my log:

2019-07-29 18:50:06.220 [ome.event.ItemCommandEvent] - Item 'Tint_Light3_Toggle' received command ON
2019-07-29 18:50:06.230 [vent.ItemStateChangedEvent] - Tint_Light3_Toggle changed from OFF to ON
2019-07-29 18:50:11.383 [vent.ItemStateChangedEvent] - Tint_Light3_Dimmer1 changed from 0 to 65
2019-07-29 18:50:11.386 [vent.ItemStateChangedEvent] - Tint_Light4_Toggle changed from OFF to ON
2019-07-29 18:50:11.387 [vent.ItemStateChangedEvent] - Tint_Light4_Dimmer1 changed from 0 to 66


2019-07-29 18:50:22.992 [ome.event.ItemCommandEvent] - Item 'Tint_Light3_Toggle' received command OFF
2019-07-29 18:50:22.996 [vent.ItemStateChangedEvent] - Tint_Light3_Toggle changed from ON to OFF
2019-07-29 18:50:31.426 [vent.ItemStateChangedEvent] - Tint_Light3_Dimmer1 changed from 65 to 0
2019-07-29 18:50:31.427 [vent.ItemStateChangedEvent] - Tint_Light4_Toggle changed from ON to OFF
2019-07-29 18:50:31.428 [vent.ItemStateChangedEvent] - Tint_Light4_Dimmer1 changed from 66 to 0


2019-07-29 18:50:42.790 [ome.event.ItemCommandEvent] - Item 'Tint_Light4_Toggle' received command ON
2019-07-29 18:50:42.793 [vent.ItemStateChangedEvent] - Tint_Light4_Toggle changed from OFF to ON
2019-07-29 18:50:51.476 [vent.ItemStateChangedEvent] - Tint_Light3_Dimmer1 changed from 0 to 65
2019-07-29 18:50:51.477 [vent.ItemStateChangedEvent] - Tint_Light3_Toggle changed from OFF to ON
2019-07-29 18:50:51.478 [vent.ItemStateChangedEvent] - Tint_Light4_Dimmer1 changed from 0 to 66


2019-07-29 18:51:04.372 [ome.event.ItemCommandEvent] - Item 'Tint_Light4_Toggle' received command OFF
2019-07-29 18:51:04.375 [vent.ItemStateChangedEvent] - Tint_Light4_Toggle changed from ON to OFF
2019-07-29 18:51:11.523 [vent.ItemStateChangedEvent] - Tint_Light3_Dimmer1 changed from 65 to 0
2019-07-29 18:51:11.525 [vent.ItemStateChangedEvent] - Tint_Light3_Toggle changed from ON to OFF
2019-07-29 18:51:11.529 [vent.ItemStateChangedEvent] - Tint_Light4_Dimmer1 changed from 66 to 0

as i have done more test runs during the day, OH simply keep sending mqtt cmnd even after state is already changed.
Which then is indeed causing troubles when you trigger opposite state as then you have two constant spamming threads one with OFF and second one with ON.

edit: I kind of believe it’s caused by combination of this follow rule and Tasmota stat/cmnd topics
is follow rule following stateTopic or commandTopic or both? it does look like both?
because in mqtt I can see this:

sonoff-touch-09/cmnd/POWER ON
sonoff-touch-09/stat/RESULT {"POWER":"ON"}
sonoff-touch-09/stat/POWER ON
sonoff-touch-08/cmnd/POWER ON
sonoff-touch-08/stat/RESULT {"POWER":"ON"}
sonoff-touch-08/stat/POWER ON
sonoff-touch-09/cmnd/POWER ON
sonoff-touch-09/stat/RESULT {"POWER":"ON"}
sonoff-touch-09/stat/POWER ON
sonoff-touch-08/cmnd/POWER ON
sonoff-touch-08/stat/RESULT {"POWER":"ON"}
sonoff-touch-08/stat/POWER ON
sonoff-touch-09/cmnd/POWER ON
....
....
....

we can see here that 09 was followed by 08 but then I think 09 reacted again to topic stat/POWER ON sent by 08 which is sent after receiving cmnd … and which caused another trigger and so on and on and on

@kriznik

As you can see in my logfile above, it works. My bulbs were switched ON and OFF both simultaneously (in real), but the eventbus was updated between 5 and 8.5 seconds later.

I totally believe you, no worries :wink:

1 Like

I find understanding exactly what follow profile does is headache provoking, but here’s what I got;

In the case where follow is used with no parameter…

Switch  mySwitch "blurb"  { channel="some:thing:channel" }

First, the ordinary OH linkage -
When Item mySwitch gets a state update, nothing (ordinarily) happens to some:thing channel (there are always exception bindings :crazy_face:)
When Item mySwitch gets a command, it gets passed to some:thing channel, to binding, and usually to device.
When some data arrives from device via binding to some:thing channel, it causes an Item state update.

It is possible to add a second channel to an Item

Switch  mySwitch "blurb"  { channel="some:thing:channel", channel="other:thing:channel" }

Here things work much the same as the ordinary way -
When Item mySwitch gets a state update, nothing (ordinarily) happens to either channel.
When Item mySwitch gets a command, it gets passed both to some:thing channel, to binding, to device, AND to other:thing channel, to other binding and device.
When some data arrives from device via binding to some:thing channel OR to other:thing channel, it causes an Item state update.

Okay, let’s apply follow profile to channel two

Switch  mySwitch "blurb"  { channel="some:thing:channel", channel="other:thing:channel" [profile="follow"] }

Here things work differently. The follow profile effectively reverses what the associated channel does, “follow” listens for state updates on the Item and converts them into commands on the channel, and at the same time blocks incoming updates from getting to the Item.
Let’s work it out -
When Item mySwitch gets a state update, nothing happens to some:thing channel. But other:thing channel gets the new state as a command, it’ll get sent to a real device likely. This is the “follow”.
When Item mySwitch gets a command, it gets passed to some:thing channel, to binding, to device. I’m not really sure what happens to other:thing channel, but I think nothing. But note that most likely the command will cause a state update to this Item one way or another, and then follow will kick into action.
When some data arrives from device via binding to some:thing channel it causes an Item state update. This update will invoke follow as already described, and we’ll send a command via other:thing channel
When some data arrives from device via binding to other:thing channel, nothing happens to this Item.

This explains a pair of cross-linked devices behaviour, where as in this case both accept commands and respond with update.
command -> device A (ignored by B)
or
someone presses the button on device A
A updates -> Item A, triggers ‘follow’ to make command -> device B
B updates -> Item B, triggers ‘follow’ to command -> device A
A updates -> Item A, triggers ‘follow’ to command -> device B

This happens because follow profile is too dim witted not to send a command if something is already in the desired state, and the device always responds with an update even if already in desired state.
I’m pretty sure follow kicks in for any update, and not just changes, but stand to be corrected on that.

The cure is a rule to add the needed intelligence.

Now, don’t ask me what follow profile does when you give it an Item as a parameter !!

yes so in other words, it’s better to use rules instead of following profile cos it’s following anything happening not comparing states, which in some cases can end up in infinite loop.

I’m pretty sure it will work just fine (I’ve tried) with dummy mqtt item which does not send confirmation message to mqtt to the topic monitored by OH.
Issue seems to be simply in fact that tasmota (and might be some others) basically doing:

--> received mqtt command
--> doing stuff
--> sending state to mqtt after command exectution

the last bit simply triggers following profile again, and whole process is repeated but on Swtich B, which then triggers Switch A and so on.

It’s nice feature tho, I’m not real fan of writing rules on everything, and tandem switches is something which is in many houses. Would be super great to use following profiles on this.