Complex switching situation with MQTT lights

Hey guys,
I’m using HA for quite some time and was able to find all answers simply by searching here.
But I can’t find a solution for this problem:

I have 3 mqtt devices in a room. All of them have a light and a switch. All of them should work together.
E.g.:
push switch 1 → turn on light 1,2,3
push switch 3 → turn of light 1,2,3

Inital approach, two simple rules with multi trigger and multi aciton:

 - alias: dev2_4_14_on
    initial_state: True
    hide_entity: False
    trigger:
      - platform: state
        entity_id: light.dev2
        to: 'on'
      - platform: state
        entity_id: light.dev4
        to: 'on'
      - platform: state
        entity_id: light.dev14
        to: 'on'
    action:
      - service: light.turn_on
        entity_id: 
          - light.dev2
          - light.dev4
          - light.dev14

Same for off … that sort of works … but its important to notice what happens under the hood:
(1) Push switch 1 → send mqtt status “dev1/status: ON”
(2) Automation trigger → send mqtt: “dev1/switch: ON” “dev2/switch: ON” “dev3/switch: ON”
(3) dev1 will ignore the message as the light was already on
(4) dev2 will send mqtt status “dev2/status: ON”
lets assume that dev3 is a bit slower or that the messages are in some kind of queue
(5) Automation trigger based on dev2 change → send mqtt: “dev1/switch: ON” “dev2/switch: ON” “dev3/switch: ON”
(6) dev1,2 will ignore the message as the light was already on
(7) dev3 will send mqtt status “dev3/status: ON”
(8) Automation trigger based on dev3 change → send mqtt: “dev1/switch: ON” “dev2/switch: ON” “dev3/switch: ON”
(9) dev1,2,3 will ignore the message as the light was already on
So there is quite some traffic.

Now image that there are 100 units in a room … each unit will trigger this automation so 100 x 100 messages will be send out. That will take some time, but thats alright, it will all happen under the hood and I don’t have 100 lights anyway.

But what happens if another switch changes to “off” during this processing time? This will send out an “off” and trigger the 2nd automaition, which will start a wave of messages in parallel. Ultimatly some lights will received a ON followed by an OFF by an ON by an OFF … and retrigger the automation. It’ll start to blink and eventually crash the system at some point.

Again, I don’t have 100 lights but the same problem happened to me with a bouncing switch. ON + OFF was triggered for the same switch within 200ms and the first automation wasn’t finished within the first 200ms. Result was a blinking room. Caught in a infinity-loop …

TLDR:
How do I connect many lights with many switches to act as group?
Put them in a group an turn-on/off the group will have the same effect, each light will trigger the group.

This must be a typical problem, right?
Thanks for your input
JKW

One possible way:

  • Publish only a separate message on a button press e.g. “toggle lights”
  • automation_on would be triggered by the toggle-message, with a condition lights are off
  • turn on all lights and just use the state as feedback in homeassistant but not as an automation_on

But that would effectivly lead to an hardware/automation only solution. I couldn’t usethe HA interface anymore. switching on one of the lights on the interface would just turn on that very light, but not the others (toggle message would never be send -> no trigger on state change … no automation).

Yes I could in addtion add a mqtt switch to send the “toggle lights” message, but thats a really ugly solution, having the three lights and the “combined switch” … there must be a better solution

JKW

You should only change state to on when the current state is off, so use conditions. And use templates (for trigger, condition and action) to simplify things and have HA take care of the logic. If the automation is done properly there is no problem to solve as far as I can see.

alright, that’s what I’ve done already:

action:
  - service: script.turn_on
    entity_id: 
      - script.light_on_dev20
      - script.light_on_dev3
      - script.light_on_dev16

and those scripts will only turn on the device if they have been off before

light_on_dev3:
sequence:
- condition: state
entity_id: light.dev3
state: ‘off’
- service: light.turn_on
entity_id: light.dev3

but that will only change about 50% of the problem … I can still happen that the first message from the first state change (due to button press) will trigger 100 messages and it’ll take a while until all of those states come in. there is a good chance that one of those devices will change the status in the meantime.

can you explain your suggestion with the “templates” a little more in detail?
JKW

Why did you create a script? Switching a light on/off is a very basic automation using a trigger, condition (optional) and action.

The way you’ve setup your automation is actually “a disaster waiting to happen” :wink: If you have a state change of light 1 trigger light 2 and at the same time a state change of light 2 trigger light 1 you’re simply lucky not being caught in an endless loop. From that point of view you are right 100 lights will definitely fail but not due to processing speed or number of messages. Actually your faulty switch problem shows this isn’t an issue as your lights where simply processing the messages.

The easiest way to trigger all lights in parallel is to put the lights in a group (or use the default HA group group.all_lights) to switch them all on/off at once.

You can also add a template condition to check if the “light off” automation was triggered for example within 1 minute of the “light_on” automation (and the other way around) to limit the amount of messages due to a faulty switch. This will also solve your worries about what happens if a switch is used again within the “processing time” of the first automation.

- condition: template
    value_template: '{% if states.automation.your_light_off_automation.last_triggered  %} {{(as_timestamp(now())-as_timestamp(states.automation.your_light_on_automation.last_triggered)) > 60 }}  {% else %}  true {% endif %}'
1 Like

The way I think I would do it

  1. Each light subscribes to dev1/switch and all/switch
  2. Each switch sends dev1/switch on or dev1/switch off.
  3. When HA receives a dev1/switch message, it sends the equivalent all/switch message to switch the other light. Each light will return it own status message.
    You can do something like #/switch as the subscription topic, to avoid listing all the individual light topics, but you will have to check the mqtt syntax, as I don’t remember exactly.
  4. The light related to the original switch will receive and respond to the original dev1/switch message, so it would work without HA working. You could also have a physical connection from the switch to the light if you want it to work when the broker isn’t available.

Hi and thanks for your feedback

Exactly! And as you’ve pointed out it can and will end in a forever loop. And it did before.
But the group won’t help at this point … it’ll only reproduce the problem.
The 1 minute off template would be a solution but that’ll make the system slow and you have to hope that everything is done within a minute.

@gpbenton i guess your solution would work.
I push the button on light 1 → send dev1/switch. HA will see and react with all/switch. That would break the loop as “all/switch” won’t retrigger. The only problem: I’d have to reflash every unit or precompile different binarys to make only a few listen to “all” … because I only want to turn on one room and not all rooms, right?

This is what I came up with yesterday:
https://github.com/KoljaWindeler/ESP8266_mqtt_pwm_pir_temp/blob/master/HA/automation.yaml

I’ve created three “joiner” lights. E.g. Dev{1,2,3}/button will toggle the joiner1 state and the state change of joiner1 will forward the state to dev1, dev2, dev3. I’ve also replaced the interface entries of dev1,2,3 with the joiner entry.
This seams to work fine. The only issue: it’s a bit optimistic as I can never get the state of the joiner from the physical device (1 might be on, 2 off: what is my state?) But I’ll keep experimenting

Jkw

I am with @sjee - use a group for your lights

You say you have 3 switches and 3 lights.

  1. put the lights in a group
  2. if a switch is pressed, toggle the lights group.

As you point out, latency issues might cause weird outcomes if you try and use the light states at a point in time as a test for state - let the state machine worry about that for the group.

One small point about toggle though is that if one light is on and another is off (due perhaps to switching them on or off manually) then the toggle will toggle each light from current status to the alternate. Which is great for some use cases … but not for this one!

True :wink: … Found out the same thing yesterday.
Automation based on state changes of unit that are switched via automation is a really bad idea :wink:
Jkw

However, you could change the state of the group to an alternate state - just requires a logic branch, or 2 rules.

Test the state of the group, then change the group to the alternate state yourself. But base it upon a switch as the trigger, ot change in state of a single light.

eg:
trigger is switch pressed
if state of group is on, then homeassistnt.turn_off group
if state of group os off, then homeassistant.turn_on group