Repeat same action on multiple person - how to optimise this automation?

I’m using the following automation to send information to a Pi Zero using a Pimoroni Blinkt LED bar as a visual indicator for who’s at home in my house. Communication is via json MQTT.

On start-up the Python3 code on the Pi sends an “Initialise” payload to blinktpi/response, and the automation uses that trigger to send the current status back.

The automation is working, but can maybe be optimised a bit into one action rather than repeating the same one for all 4 people (each person here has an HA user). Is there any way to loop through all person.x accounts and do the same MQTT publish action on each, based on their individual status?

alias: MQTT Presence Initialise
description: Respond with data when the BlinktPi Initialises
trigger:
  - platform: mqtt
    topic: blinktpi/response
    payload: Initialise
condition: []
action:
  - service: mqtt.publish
    data:
      topic: blinktpi/presence
      payload: '{ "person": "darren","status": "{{ states.person.darren.state }}" }'
  - service: mqtt.publish
    data:
      topic: blinktpi/presence
      payload: '{ "person": "h","status": "{{ states.person.h.state }}" }'
  - service: mqtt.publish
    data:
      topic: blinktpi/presence
      payload: '{ "person": "a","status": "{{ states.person.a.state }}" }'
  - service: mqtt.publish
    data:
      topic: blinktpi/presence
      payload: '{ "person": "k","status": "{{ states.person.k.state }}" }'
mode: single

Yes, you can use Counted Repeat. Create a list containing each person’s name and use the repeat.index variable to iterate through each name in the list.

This would be in a script, and call that script from the automation?

I’m not sure how the script would be set up (especially making the list) - can you fill in some gaps or maybe sketch up an example I can work from?

Do the four persons mentioned in your automation represent all of the persons in your system or a subset of them?

In other words, do you want the automation to publish a payload for each person entity that exists in your system or just these four?

I you only want the automation to process four specific persons, here’s how it can be done with Counted Repeat.

alias: MQTT Presence Initialise
description: Respond with data when the BlinktPi Initialises
trigger:
  - platform: mqtt
    topic: blinktpi/response
    payload: Initialise
condition: []
action:
  - variables:
      persons: [ 'darren', 'h', 'a', 'k' ]
  - repeat:
      count: '{{ persons | count }}'
      sequence:
      - variables:
          person: '{{ persons[repeat.index - 1] }}'
      - service: mqtt.publish
        data:
          topic: blinktpi/presence
          payload_template: >
            { "person": "{{ person }}","status": "{{ states('person.' ~ person) }}" }
mode: single
4 Likes

@123 thanks for that - works perfectly :smiley:

To answer your question, those 4 person entities are the only 4 on my set-up. So it covers what I need nicely.

Out of interest, is there any simple way to step through all person entities on a given set-up? Is it stored/accessible anywhere, at least without some manual workaround like forming a group or something?

If you replace the first variables declaration with the following, the automation should continue to work properly (and will reference all of the existing person entities).

  - variables:
      persons: "{{ states.person | map(attribute='object_id') | list }}"

@123 - OK, thanks again.

Looks very much like advanced player code to me (both the main code and the new variable), but I can follow most of it. The only thing that isn’t so obvious in meaning is the ~ person in the payload template.

Still gives me something to look into and learn, so hopefully next time I can prepare something like this myself… :smiley:

Oh and can confirm the new variables code works perfectly (as ever for your examples :wink: ).

The tilde character represents a concatenation operator in Jinja (the templating language). In other words it means to append one string to another.

You may see the plus symbol used the same way but it can also be used to add two numbers. This can lead to an ambiguous situation where you want to use it to concatenate strings but the Jinja interpreter sees two numbers and computes their sum.

To avoid potential ambiguity, use the tilde character to concatenate strings.

In this particular case, it appends the value of the variable person (like “darren”) to the string “person.” to create the entity person.darren. It supplies the result of the concatenation to the states() function to get the state value of person.darren.

{{ states('person.' ~ person) }}

Gotcha - only knew about + for concat, not ~ . Learned something new there :slight_smile:

Can follow and understand the whole automation now, so life is good. Need to do some more reading on Jinja I think as the next step, and getting my head around using variables in templates…

@123 can I trouble you for a little more of your guru wisdom please?

I’m extending this to a device tracker as a separate automation, which I can get to work with the variable devices: '{{ states.device_tracker | map(attribute=''object_id'') | list }}'

However what I’d like to do, rather than using all of my tracked devices, is to use a sub-set of them which I have set up as sensor.track_<device> .

How could I adjust the variable to compile a list of those? If I use states.sensor I get all my sensors, when I just want the subset sensor.track_* Or would I need to set them up as a group to do this and then use expand somewhere to get the list back?

Can such wildcards be used in the variable set-up template to narrow to the sensor sub-set? I had a look at the jinja docs, but I can’t see anything there to help.

The goal is to select a subset of entities and there are several ways to do it.

  1. Group
    Simplest way is to create a group containing the desired entities. As you’ve mentioned, use expand in the template to get the group’s members.

  2. Custom attribute
    Another way is to assign a custom attribute to each device tracker with the same value. In other words, instead of using the entity’s name to hold a key word (the leading “track_” in its object_id) you create a new attribute, perhaps called “type”, and assign it the value “track” or “tracker”. The template can then use this to get all sensors whose “type” is “tracker”

states.sensor | selectattr('attributes.type', 'eq', 'tracker')` | map(attribute=''object_id'') | list }}

For more information about this powerful technique:

  1. match test
    Another way is to use the recently introduced match test.
{{ states.sensor | selectattr('object_id', 'match', 'track_*') | map(attribute='object_id') | list }}
1 Like

Good day, thank you for sharing this knowledge.
in a similar scenario if I have the following service

    - service: image_processing.deepstack_teach_face
        data:
          name: a
          file_path: /config/www/cctv/known_faces/a/a_1.jpg

Can I just add another count definition, ie:

 count: 
 - '{{ persons | count }}'
 - '{{jpeg|count}}'

so it iterares thrug persons and jpegs ?

Thank you

No. You have to supply count with an integer value. The example you posted produces a list (containing two integer values).

Thank you very much for your response.

@123 - many thanks for such a detailed guidance.

Went with option 2 (custom attributes) as it also then gives me the full flexibility of exactly what I want to send to the other display devices via MQTT. Now have it working perfectly, sending status and IP address via a json payload.

Now just need to update the Python code on the displays to work with it (currently they just do pings on the network), and to look at the trigger for the automation. At the moment for testing it’s just running every 2 minutes, but I’d like to use a template trigger to have it trigger when any of the tracked device status changes. That’s a job for later though…

Thanks again :smiley:

@123 Would I be correct to assume that such templating cannot be done for the trigger of the automation?

Looking through the docs, it doesn’t look like setting up variables etc can be done in the trigger, and the only way to do it would be to have an explicit list of entities?

Version 2021.3.0 introduced the concept of trigger_variables. However, at this early stage, they don’t support everything that can normally be done with a Jinja template. The documentation refers to this reduced templating functionality as “limited templates”. It explains what it doesn’t support and it’s quite extensive. Basically, “limited templates” let you define constants and not much more than that.

FWIW, I had submitted a Feature Request months earlier and subsequently reviewed trigger_variables when it was first released

Thanks - that page (the docs one) I had found but wasn’t sure how it all hung together and where the gaps were.

I’ll look over your feature request link and examples and see what I can learn there. But as you say, the gaps in what’s supported are probably be a showstopper at the moment for that last step. I see already that states/is_state/state_attr etc aren’t supported, which would be what I need.

Still it’s now triggering once a minute and I have the mqtt client at the other end now just about coded up and working. Just got to add a web-page to it from the older ping version, but that can be done later. Too damn hot today for a coding session :wink: