Optimize the MQTT code (Shelly 4 Pro)

Hello,
I set-up MQTT communication of a Shelly Pro 4PM between HA and Domoticz with success. The following code do the job for one of the four switches

- id: '1657428580050'
  alias: Send Shelly 4 switch_3 state to DZ
  description: ''
  trigger:
  - platform: state
    entity_id:
    - switch.shellypro4pm_083af27c0770_switch_3
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: domoticz/in
      payload_template: '{"command": "switchlight", "idx": 631 , "switchcmd": "{{states(''switch.shellypro4pm_083af27c0770_switch_3'')
        | title }}"}'
      qos: '0'
      retain: true
  mode: single
- id: 420588af-8871-4179-a041-5b94d5f79234
  alias: Send Shelly 4 switch_3 power to DZ
  description: ''
  trigger:
  - platform: state
    entity_id:
    - sensor.shellypro4pm_083af27c0770_switch_3_power
  condition: []
  action:
  - service: mqtt.publish
    data:
      topic: domoticz/in
      payload_template: '{"command": "udevice", "idx": 632 , "nvalue" : 0,  "svalue"
        : "{{states(''sensor.shellypro4pm_083af27c0770_switch_3_power'') }}"}'
      qos: '0'
      retain: true
  mode: single
- id: da320a33-5e6c-4a03-9e23-739a8974d51e
  alias: Read Shelly 4PM switch_3 state from DZ
  description: ''
  trigger:
  - platform: mqtt
    topic: domoticz/out/631
  condition: []
  action:
  - choose:
    - conditions:
      - condition: template
        value_template: '{{ trigger.payload_json.nvalue == 0 }}'
      sequence:
      - service: switch.turn_off
        data: {}
        target:
          entity_id: switch.shellypro4pm_083af27c0770_switch_3
    - conditions:
      - condition: template
        value_template: '{{ trigger.payload_json.nvalue == 1}}'
      sequence:
      - service: switch.turn_on
        data: {}
        target:
          entity_id: switch.shellypro4pm_083af27c0770_switch_3
    default: []
  mode: single

Now come the time either to duplicate the code 3 times in order to support the 4 switches and change some “variables”. The variables are entity id’s and idx values that depends on entity id’s. Is there a better way to do that, in order to avoid expansion of the code which turns into more maintenance? Any ideas?
Thank you

I can see how to consolidate the three automations into one that can handle all 4 switches (and sensors).

Post a list of all the entity_ids and their associated idx values.

This is very kind of you. Here are the “variables” in the code

switch.alimentation_electrovannes – idx :  644
sensor.alimentation_electrovannes_power – idx : 647
switch.fil_chauffant – idx : 645
sensor.fil_chauffant_power – idx : 648
switch.shellypro4pm_083af27c0770_switch_3 – idx: 631
sensor.shellypro4pm_083af27c0770_switch_3_power – idx: 632
switch.pressostat – idx: 646
sensor.pressostat_power – idx: 647

I’m wondering as well whether there is a better approach to code this:

action:
  - choose:
    - conditions:
      - condition: template
        value_template: '{{ trigger.payload_json.nvalue == 0 }}'
      sequence:
      - service: switch.turn_off
        data: {}
        target:
          entity_id: switch.shellypro4pm_083af27c0770_switch_3
    - conditions:
      - condition: template
        value_template: '{{ trigger.payload_json.nvalue == 1}}'
      sequence:
      - service: switch.turn_on
        data: {}
        target:
          entity_id: switch.shellypro4pm_083af27c0770_switch_3

Instead of calling a different service turn_off / turn_on based on a condition, set a variable that will be set to “on” or “off” depending on “trigger.payload_json.nvalue” and then call the service: switch.turn_{{ some_variable }}

I reduced it to two automations, one publishes to Domoticz and the other receives from it. It’s possible to combine them but not completely necessary.

The following two examples are not tested so they may require a few adjustments.

- id: 'publish_to_dz'
  alias: Publish to DZ
  description: ''
  variables:
    entities:
      switch.alimentation_electrovannes:  644
      sensor.alimentation_electrovannes_power: 647
      switch.fil_chauffant: 645
      sensor.fil_chauffant_power: 648
      switch.shellypro4pm_083af27c0770_switch_3: 631
      sensor.shellypro4pm_083af27c0770_switch_3_power: 632
      switch.pressostat: 646
      sensor.pressostat_power: 647
    index: "{{ entities.get(trigger.entity_id, 0) }}"
  trigger:
  - platform: state
    entity_id:
    - switch.alimentation_electrovannes
    - sensor.alimentation_electrovannes_power
    - switch.fil_chauffant
    - sensor.fil_chauffant_power
    - switch.shellypro4pm_083af27c0770_switch_3
    - sensor.shellypro4pm_083af27c0770_switch_3_power
    - switch.pressostat
    - sensor.pressostat_power
  condition: "{{ index != 0 }}"
  action:
  - service: mqtt.publish
    data:
      topic: domoticz/in
      payload_template: >
        {% if trigger.to_state.domain == 'switch' %}
          {"command": "switchlight", "idx": {{ index }}, "switchcmd": "{{ trigger.to_state.state | title }}"}
        {% else %}
          {"command": "udevice", "idx": {{ index }}, "nvalue": 0,  "svalue": "{{ trigger.to_state.state }}"}
        {% endif %}
      qos: '0'
      retain: true
  mode: queued



- id: 'receive_from_dz'
  alias: Receive from DZ
  description: ''
  variables:
    indexes:
      644: switch.alimentation_electrovannes
      647: sensor.alimentation_electrovannes_power
      645: switch.fil_chauffant
      648: sensor.fil_chauffant_power
      631: switch.shellypro4pm_083af27c0770_switch_3
      632: sensor.shellypro4pm_083af27c0770_switch_3_power
      646: switch.pressostat
      647: sensor.pressostat_power
    entity: "{{ indexes.get(trigger.topic.split('/') | last | int(0), 'unknown') }}"
  trigger:
  - platform: mqtt
    topic: domoticz/out/#
  condition: "{{ entity != 'unknown' }}"
  action:
  - service: "switch.turn_{{ iif(trigger.payload_json.nvalue == 0, 'on', 'off') }}"
    target:
      entity_id: "{{ entity }}"
  mode: queued
1 Like

Thanks very much I’ll will try that and report back

For publish to DZ I changed trigger.to_state.state by states(trigger.entity_id) and it works. The final code is

- id: 2a464810-8106-476b-a1ba-06dadba8994b
  alias: Publish to DZ
  description: ''
  variables:
    entities:
      switch.alimentation_electrovannes:  644
      sensor.alimentation_electrovannes_power: 647
      switch.fil_chauffant: 645
      sensor.fil_chauffant_power: 648
      switch.shellypro4pm_083af27c0770_switch_3: 631
      sensor.shellypro4pm_083af27c0770_switch_3_power: 632
      switch.pressostat: 646
      sensor.pressostat_power: 647
    index: "{{ entities.get(trigger.entity_id, 0) }}"
  trigger:
  - platform: state
    entity_id:
    - switch.alimentation_electrovannes
    - sensor.alimentation_electrovannes_power
    - switch.fil_chauffant
    - sensor.fil_chauffant_power
    - switch.shellypro4pm_083af27c0770_switch_3
    - sensor.shellypro4pm_083af27c0770_switch_3_power
    - switch.pressostat
    - sensor.pressostat_power
  condition: "{{ index != 0 }}"
  action:
  - service: mqtt.publish
    data:
      topic: domoticz/in
      payload_template: >
        {% if trigger.to_state.domain == 'switch' %}
          {"command": "switchlight", "idx": {{ index }}, "switchcmd": "{{ states(trigger.entity_id) | title }}"}
        {% else %}
          {"command": "udevice", "idx": {{ index }}, "nvalue": 0,  "svalue": "{{ states(trigger.entity_id) }}"}
        {% endif %}
      qos: '0'
      retain: true
  mode: queued

For receive from DZ, I removed the sensors since DZ do not publish anything (you could not guess), and I inverted the on/off conditions apart from that this is perfect

- id: 93404438-01a9-4f91-933f-1d3cfc823b20
  alias: Receive from DZ
  description: ''
  variables:
    indexes:
      644: switch.pressostat
      645: switch.alimentation_electrovannes
      646: switch.fil_chauffant
      631: switch.shellypro4pm_083af27c0770_switch_3
    entity: "{{ indexes.get(trigger.topic.split('/') | last | int(0), 'unknown') }}"
  trigger:
  - platform: mqtt
    topic: domoticz/out/#
  condition: "{{ entity != 'unknown' }}"
  action:
  - service: "switch.turn_{{ iif(trigger.payload_json.nvalue == 0, 'off', 'on') }}"
    target:
      entity_id: "{{ entity }}"
  mode: queued

Thank you very much again for your very valuable input, I made progress in mastering HA which is quite a challenge :wink: I did not know how to use variables, despite my search in the doc, I could not find it. I discovered the get function as well.

May I ask why?

trigger.to_state.state contains the current state value of the entity that triggered the State Trigger.

For example, if switch.pressostat changes from on to off it will trigger the State Trigger and the value of trigger.to_state.state will be off.

Your version takes an extra step. It queries the State Machine by using the states() function to get the current state value of switch.pressostat (using trigger.entity_id). That value is already available in the trigger object via trigger.to_state.state.

Sure, you are right I must have had something else preventing it to work correctly at the beginning of the debugging work. So the final code indeed includes:

trigger.to_state.state

instead of

states(trigger.entity_id)

Glad to hear the original code works.

Please consider marking my post above with the Solution tag. It will automatically place a check-mark next to the topic’s title which signals to other users that this topic has been resolved. This helps users find answers to similar questions.

For more information refer to guideline 21 in the FAQ.

I used this technique for many different entities but there is a downside. I noticed all automations listening to domoticz/out/# are triggered by any domoticz devices (including those that are not needed) which generates a lot of trafic. Since I decided to keep Domoticz and HA, I changed the Domoticz MQTT interface to Hierarchical. This allow to select Domoticz devices that are allowed to talk to MQTT reducing drastically trafic.

On HA side, two things changed in the code;

      # entity: '{{ indexes.get(trigger.topic.split(''/'') | last | int(0), ''unknown'')
replaced by
      entity: '{{ indexes.get(trigger.payload_json.idx, "unknown") }}'

and

      # topic: domoticz/out/#
replaced by
      topic: dz/mqttFloorPlan/mqttDevices

On Domoticz side, mqttDevices is a room plan in a mqttFloorPlan floor plan. Devices are added one by one to the room.