Automation: multiple mqtt messages

Hi,

I’m trying to make automated zigbee2mqtt OTA updater.

Here is my automation, but this publishes multiple device ids to same topic. I would like to add topic and payload to same for sentence so every match would make new message.

- id: update_zigbeedevices1
  alias: OTA update Zigbee devices
  trigger:
  - event_data:
      action: update_zigbeedevices
    event_type: home_assistant_notifications_action
    platform: event
  action:
    - service: mqtt.publish
      data:
        topic: zigbee2mqtt/bridge/ota_update/update
        payload: >
          {%- for entity_id in states.group.zigbee_updates.attributes.entity_id -%}            
            {%- set parts = entity_id.split('.') -%}
            {% if is_state(entity_id, "on") -%}
              {{ states[parts[0]][parts[1]].name | replace("_update_available","") }}
            {%- endif -%}
          {%- endfor -%}

Not 100% sure what you want. You want to be able to send a bunch of these out, one for each entity?

You can use the new repeat mechanics to give it a shot.

action:
  - variables: 
      # Store the list of entities here for easier lookup
      entities: "{{ state_attr('group.zigbee_updates', 'entity_id') }}"
  - repeat:
      # Repeat for every entity in the list
      count: "{{ entities | count }}"
      sequence:
        # Only do something if the entity is 'on'. Else, do nothing. 
        - choose:
          conditions:
            condition: state
            # The value 'repeat.index' is the current loop index. Use that
            # to grab the next entity_id from the list we created in variables. 
            entity_id: "{{ entities[repeat.index] }}"
            state: 'on'
          sequence:
            - service: mqtt.publish
              data:
                topic: zigbee2mqtt/bridge/ota_update/update
              payload: >
                {% set entity_id = entities[repeat.index] %}
                {{ states[parts[0]][parts[1]].name | replace("_update_available","") }}

1 Like

Yes, one message for each entity.

Your code seems just that what I’m trying to do. I will check it out tomorrow!

@jocnnor Thanks for guiding me to right direction. Code didn’t work out of the box, but it almost works now.

- id: update_zigbeedevices1
  alias: OTA update Zigbee devices
  trigger:
  - event_data:
      action: update_zigbeedevices
    event_type: home_assistant_notifications_action
    platform: event
  action:
    - variables: 
        # Store the list of entities here for easier lookup
        entities: "{{ state_attr('group.zigbee_updates', 'entity_id') }}"
    - repeat:
        # Repeat for every entity in the list
        count: "{{ entities | count }}"
        sequence:
          - choose:
              conditions:
                - condition: state
                  entity_id: "{{ entities[repeat.index] }}"
                  state: "on"
              sequence:
                - service: mqtt.publish
                  data:
                    topic: zigbee2mqtt/bridge/ota_update/check
                    payload: >
                      {% set entity_id = state_attr('group.zigbee_updates', 'entity_id')[repeat.index] %}
                      {% set parts = entity_id.split('.') -%}
                      {{ states[parts[0]][parts[1]].name | replace("_update_available","") }}
                - delay: 5

This gives me error:

Invalid config for [automation]: Entity ID {{ entities[repeat.index] }} is an invalid entity id for dictionary value @ data['action'][1]['repeat']['sequence'][0]['choose'][0]['conditions'][0]['entity_id']. Got None.

But this automation works, so it just can’t read entities for some reason, while repeat count and entities variable is set correctly.

- id: update_zigbeedevices1
  alias: OTA update Zigbee devices
  trigger:
  - event_data:
      action: update_zigbeedevices
    event_type: home_assistant_notifications_action
    platform: event
  action:
    - variables: 
        # Store the list of entities here for easier lookup
        entities: "{{ state_attr('group.zigbee_updates', 'entity_id') }}"
    - repeat:
        # Repeat for every entity in the list
        count: "{{ entities | count }}"
        sequence:
          - choose:
              conditions:
                - condition: time
                  after: '08:00:00'
                  before: '21:00:00'
              sequence:
                - service: mqtt.publish
                  data:
                    topic: zigbee2mqtt/bridge/ota_update/check
                    payload: >
                      {% set entity_id = state_attr('group.zigbee_updates', 'entity_id')[repeat.index] %}
                      {% set parts = entity_id.split('.') -%}
                      {{ states[parts[0]][parts[1]].name | replace("_update_available","") }}
                - delay: 5

I tried to set ‘entities’ variable so that it would be a list only for entities, with state on, but I cannot get entity_id out of it:

 {{ states|selectattr('entity_id','in',state_attr('group.zigbee_updates','entity_id'))|selectattr('state','eq','on')|list }}

This returns correctly right one, but how to make a list of entity_id’s with on state?

return:

[<template TemplateState(<state binary_sensor.0x000b57fffef2c3e6_update_available=on; linkquality=78, state=OFF, update_available=True, friendly_name=0x000b57fffef2c3e6_update_available @ 2020-11-05T19:39:52.426161+02:00>)>]

Oh, you know what…I don’t think the built in “variables” can return anything other than a string. I’m assuming it can return an array, the same way it would if you did

  {% set entities = state_attr('group.zigbee_updates', 'entity_id') %}

I was testing this in the template section in dev tools and set the variable with the set command which is why I got it wrong.

You can either just remove the variables section, or treat it as a string and add some extra stuff to it when you use it. Since it’s a string literal, we need to do a .split(’,’) to create the array. It would look like this:

# Strip the leading ( and ), then split by comma to generate the array. 
count: "{{ entities[1;-1].split(",") | count }}"

Since that’s not that great, I just removed the entire variables section and use the state_attr call each time we need it.

You should also move the ‘on’ check to the “choose” conditions section. The reason this was created was because you can’t have an empty service call. Before, I made a “script.noopt” that literally did nothing, and in my service_template, I would either call the service I wanted, or call this script…because I wasn’t allowed to call nothing. What we want is the service “choose” to be called which itself will do nothing if it doesn’t pass the conditions.

- id: update_zigbeedevices1
  alias: OTA update Zigbee devices
  trigger:
  - event_data:
      action: update_zigbeedevices
    event_type: home_assistant_notifications_action
    platform: event
  action:
    - repeat:
        # Repeat for every entity in the list
        count: "{{ state_attr('group.zigbee_updates', 'entity_id') | count }}"
        sequence:
          - choose:
              conditions:
                - condition: time
                  after: '08:00:00'
                  before: '21:00:00'
                - condition: state
                  entity_id: "{{ state_attr('group.zigbee_updates', 'entity_id')[repeat.index] }}"
                  state: "on"
              sequence:
                - service: mqtt.publish
                  data:
                    topic: zigbee2mqtt/bridge/ota_update/check
                    payload: >
                      {% set entity_id = state_attr('group.zigbee_updates', 'entity_id')[repeat.index] %}
                      {% set parts = entity_id.split('.') -%}
                      {{ states[parts[0]][parts[1]].name | replace("_update_available","") }}
                - delay: 5

2 Likes

Thanks!

I actually called state_attr in payload section, because it wasn’t working there either. I don’t know why I didn’t realize to do that also in entity_id section. :sweat_smile:

I’ll try again :smiley:

If you employ the expand function, you can reduce it to this:

- id: update_zigbeedevices1
  alias: OTA update Zigbee devices
  trigger:
    - platform: event
      event_type: home_assistant_notifications_action
      event_data:
        action: update_zigbeedevices
  condition:
    - conditions: "{{ 8 < now().hour < 21 }}"
  action:
    - repeat:
        count: "{{ expand('group.zigbee_updates') | count }}"
        sequence:
          - choose:
              conditions: "{{ expand('group.zigbee_updates')[repeat.index].state == 'on' }}"
              sequence:
                - service: mqtt.publish
                  data:
                    topic: zigbee2mqtt/bridge/ota_update/check
                    payload: "{{ expand('group.zigbee_updates')[repeat.index].name.replace('_update_available', '') }}"
                - delay: 5
2 Likes

@123 @jocnnor Thanks! It works now.

1 Like

You’re welcome.

Out of curiosity, which one of the two suggested examples are you using?

Yours, because it was shorter way. Both should be fine.

I shared this to everyone, because I think that many could benefit from it.