Two switches with one MQTT topic

I asked for some help on the forums with this, and it was suggested I post in the forums as it may be a little complex. Hoping someone can help get me started!

In short, I currently have a device configured like this (topic, etc has been simplified for clarity):

- platform: mqtt
  name: "My Device"
  state_topic: "things/my-device/mode/read"
  state_on: "3"
  state_off: "4"
  command_topic: "things/my-device/mode/write"
  payload_on: "{\"value\":\"3\"}"
  payload_off: "{\"value\":\"4\"}"
  value_template: "{{ value_json.value }}"

This works great. Toggling the switch writes either {"value":"3"} or {"value":"4"} to the command topic as it should, and the state is shown correctly.

However, the device actually has 2 switches (we’ll call them A and B), not one, and they are both reported by same MQTT topic. The values are:

Value |  A  |  B
------|-----|---
1     | on  | off
2     | off | on
3     | on  | on
4     | off | off

Until now, I’ve only been interested in toggling both switches together, so switching the mode value from 3 to 4 worked fine. But now I’d like to toggle them independently.

I think what I need is a template switch, but I’m unclear how to combine that with MQTT. Again, I think the way it should work is that I make two switches - A and B.

A would be configured such that it is on if value is 1 or 3, else off. B would be configured to be on if value is 2 or 4. Then the payload_on and payload_off fields for each switch would need to check the value of the other switch and set the appropriate value. Am I on the right lines?

Any help would be appreciated!

Based on the table you provided, perhaps something like this:

- platform: mqtt
  name: "My Device A"
  state_topic: "things/my-device/mode/read"
  value_template: "{{ 'ON' if value_json.value in ['1', '3'] else 'OFF' }}"
  command_topic: "things/my-device/mode/write"
  payload_on: "{\"value\":\"3\"}"
  payload_off: "{\"value\":\"4\"}"

- platform: mqtt
  name: "My Device B"
  state_topic: "things/my-device/mode/read"
  value_template: "{{ 'ON' if value_json.value in ['2', '3'] else 'OFF' }}"
  command_topic: "things/my-device/mode/write"
  payload_on: "{\"value\":\"3\"}"
  payload_off: "{\"value\":\"4\"}"

That would work for reading the switch status, but not for writing it. Turning on either switch would actually turn on both switches, and similarly turning either switch off would turn them both off.

Sorry, missed the part that you now what to operate them independently.

The problem is that they share the same commands. If a 3 is received it is understood by both switches to mean they should turn on (or off if a 4 is received).

So let’s say our solution for operating them independently is to cease using 3 and 4 and only use 1 and 2. However, that’s still not truly independent because, according to your table, the two switches maintain opposite states (send a 1 and it turns on A and turns off B; send a 2 and A is turned off and B is turned on).

Yes, that wouldn’t work either. In pseudo code, I imagine something like the following, but not sure how to make that work in reality:

- platform: mqtt
  name: "My Device A"
  entity_id: my_device_a
  state_topic: "things/my-device/mode/read"
  value_template: "{{ 'ON' if value_json.value in ['1', '3'] else 'OFF' }}"
  command_topic: "things/my-device/mode/write"
  payload_on: "'{\"value\":\"3\"}' if is_state('switch.my_device_b', 'on') else '{\"value\":\"1\"}'"
  payload_off: "'{\"value\":\"2\"}' if is_state('switch.my_device_b', 'on') else '{\"value\":\"4\"}'"

- platform: mqtt
  name: "My Device B"
  entity_id: my_device_b
  state_topic: "things/my-device/mode/read"
  value_template: "{{ 'ON' if value_json.value in ['2', '3'] else 'OFF' }}"
  command_topic: "things/my-device/mode/write"
  payload_on: "'{\"value\":\"3\"}' if is_state('switch.my_device_a', 'on') else '{\"value\":\"2\"}'"
  payload_off: "'{\"value\":\"1\"}' if is_state('switch.my_device_a', 'on') else '{\"value\":\"4\"}'"

As you already know, there’s no command_template option so we are unable to use a template to reformat the published payload in an MQTT Switch. However, what we can do is use a ‘middleman’ automation to achieve the same thing.

  • Each MQTT Switch publishes to a ‘middleman’ topic (for example, temp/my_device_a).
  • An automation is subscribed to the ‘middleman’ topic.
  • It converts a received payload and publishes it to things/my-device/mode/write.

MQTT Switches

- platform: mqtt
  name: "My Device A"
  state_topic: things/my-device/mode/read
  value_template: "{{ 'ON' if value_json.value in ['1', '3'] else 'OFF' }}"
  command_topic: temp/my_device_a


- platform: mqtt
  name: "My Device B"
  state_topic: "things/my-device/mode/read"
  value_template: "{{ 'ON' if value_json.value in ['2', '3'] else 'OFF' }}"
  command_topic: temp/my_device_b

Automation

- alias: 'my device command converter'
  mode: queued
  trigger:
    - platform: mqtt
      topic: temp/my_device_a
    - platform: mqtt
      topic: temp/my_device_b
  action:
    - service: mqtt.publish
      data_template:
        topic: things/my-device/mode/write
        payload: >
          {% set one = '{\"value\":\"1\"}' %}
          {% set two = '{\"value\":\"2\"}' %}
          {% set three = '{\"value\":\"3\"}' %}
          {% set four = '{\"value\":\"4\"}' %}
          {% set other_on = is_state('switch.my_device_' ~ 'b' if trigger.topic[-1:] == 'a' else 'a', 'on') %}

          {% if trigger.topic[-1:] == 'a' %}
            {% if trigger.payload == 'ON' %}
              {{ three if other_on else one }}
            {% else %}
              {{ two if other_on else four }}
            {% endif %}
          {% else %}
            {% if trigger.payload == 'ON' %}
              {{ three if other_on else two }}
            {% else %}
              {{ one if other_on else four }}
            {% endif %}
          {% endif %}

EDIT

Shortened a variable name.

That’s not a bad idea!

I do have NodeRED running so had considered doing the normalization / denormalization there, but at the moment NodeRED isn’t being used for any critical operations (it’s mostly data gathering) so I was loathe to introduce it into the mix.

I just stumbled upon the mqtt_template_switch custom component and have managed to get it working with that. Full implementation is below in case anyone stumbles across this.

Thanks @123 for your help in working this one out!

- platform: mqtt_template
  name: "My Device A"
  unique_id: "my_device_a"
  state_topic: "things/my_device/mode/read"
  state_on: "ON"
  state_off: "OFF"
  command_topic: "things/my_device/mode/write"
  value_template: "{{ 'ON' if value_json.value in [2, 3] else 'OFF' }}"
  payload_template_on: >-
    {% if is_state('switch.my_device_b', 'on') %}
      {"value": 3}
    {% else %}
      {"value": 2}
    {% endif %}
  payload_template_off: >-
    {% if is_state('switch.my_device_b', 'on') %}
      {"value": 1}
    {% else %}
      {"value": 4}
    {% endif %}

- platform: mqtt_template
  name: "My Device B"
  state_topic: "things/my_device/mode/read"
  state_on: "ON"
  state_off: "OFF"
  command_topic: "things/my_device/mode/write"
  value_template: "{{ 'ON' if value_json.value in [1, 3] else 'OFF' }}"
  unique_id: "my_device_b"
    {% if is_state('switch.my_device_a', 'on') %}
      {"value": 3}
    {% else %}
      {"value": 1}
    {% endif %}
  payload_template_off: >-
    {% if is_state('switch.my_device_a', 'on') %}
      {"value": 2}
    {% else %}
      {"value": 4}
    {% endif %}
1 Like

Glad to hear you got MQTT Template Switch to solve the problem.

FWIW, you don’t need to specify state_on and state_off because you are using their default values.

Should you wish to shorten the templates, you can use an inline if. For example, replace this:

  payload_template_on: >-
    {% if is_state('switch.my_device_b', 'on') %}
      {"value": 3}
    {% else %}
      {"value": 2}
    {% endif %}

with this:

  payload_template_on: >-
    {"value": {{ 3 if is_state('switch.my_device_b', 'on') else 2}}}

1 Like

Perfect! I wondered if there was a more concise form than I had used - I’ll update my config now! Thanks again!