Automation trigger on any member of a group, is it possible?

Just an FYI template triggers and template sensors only update if the entity id’s spelled out in the templates update. So using any kind of group to search through it’s entities will only fire when the group’s state changes, not it’s underlying entities.

1 Like

well, it was worth the try, and i’ve learned a lot about lists and dictionaries. And again about the behavior and treatment of groups. not nearly enough.

luckily I have python to the rescue though to list the people at home within the blink of an eye and do exactly what we want here :wink:

just as an extra note:
this template on motion detection, tased on the same technique does respond rather swiftly. It only shows 1 sensor, admitted, but it does respond to changes when they trigger:

value_template: >
  {% if is_state('group.philips_motion_sensors', 'off') %}
    Clear
  {%else%}
    {{ dict((states|selectattr('entity_id', 'in', state_attr('group.philips_motion_sensors', 'entity_id'))
      |list)|groupby('state'))['on']|map(attribute='name')|list|join(', ') }}
  {%endif%}

not sure I understand why the difference in behavior compared to the people home scenario. Could the opposed logic of if > then > else be it? or would it be the fact that every few seconds the motion sensors are all off, the group is off, and on triggering the template kicks in?

If the latter would be the case, some sort of extra condition or time trigger could be added to the people home template, eg run every 10 sec or so?

Well, if you want to know how it works, I can explain:

Basically, the template sensor/trigger when created parses the template and finds all the entity_ids that will update the sensor or cause a trigger. It then listens to those entitys and updates based on their state. If you list the group, the parser only finds the group because the template has no reference to the groups entity list. That’s why template sensors has the entity_id list as an option. You can add a listener to those extra entities that way.

It’s not really spelled out that way in the documentation, I only found it out by reading the code and following the flow.

I’m not sure, it’s possible that the groups update differently when some devices are inside them. My guess is that would be the case for presence detection whereas lights wouldn’t need that. I’d have to look over the group code to know for sure…

1 Like

quite. thanks.

since you mention adding a listener: every once in a while i see errors in the logs not being able ‘to remove a listener’ or the likes of this.

Any ideas what those could refer to:

2018-06-13 16:54:01 WARNING (MainThread) [homeassistant.core] Unable to remove unknown listener <function async_track_point_in_utc_time.<locals>.point_in_time_listener at 0x6e373a08>

followed by:

Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 49, in uvloop.loop.Handle._run
  File "/usr/lib/python3.6/site-packages/homeassistant/helpers/event.py", line 211, in point_in_time_listener
    hass.async_run_job(action, now)
  File "/usr/lib/python3.6/site-packages/homeassistant/core.py", line 253, in async_run_job
    target(*args)
  File "/usr/lib/python3.6/site-packages/homeassistant/helpers/script.py", line 95, in async_script_delay
    self._async_listener.remove(unsub)
ValueError: list.remove(x): x not in list

Wouldn’t worry about it. Home assistant is trying to cancel a listener that doesn’t exist. Something else may have canceled it

So, to put it all together, here is what I have, which seems to work:

input_boolean:
  guests:
    name: We have guests
    icon: mdi:account-multiple

input_select:
  home_mode:
    name: Home Mode
    icon: mdi:home-account
    options:
      - Home
      - Away
      - Returning
      - Vacation

sensor:
  - platform: template
    sensors:
      n_people_home:
        friendly_name: Number of People Home
        entity_id:
          - device_tracker.name1
          - device_tracker.name2
          - device_tracker.name3
          - input_boolean.guests
        value_template: >
          {{ states|selectattr('entity_id','in',
                      state_attr('group.all_devices','entity_id'))
                   |selectattr('state','eq','home')|list|count +
             (1 if is_state('input_boolean.guests','on') else 0) }}

automation:
  - alias: Home Mode - Leaving
    trigger:
      platform: numeric_state
      entity_id: sensor.n_people_home
      below: 1
    condition:
      condition: state
      entity_id: input_select.home_mode
      state: Home
    action:
      service: input_select.select_option
      entity_id: input_select.home_mode
      data:
        option: Away

  - alias: Home Mode - Arriving
    trigger:
      platform: state
      entity_id: sensor.n_people_home
    condition:
      condition: template
      value_template: >
        {{ trigger.to_state.state|int > trigger.from_state.state|int }}
    action:
      service: input_select.select_option
      entity_id: input_select.home_mode
      data:
        option: Home
1 Like

I just found this thread as I was trying to trigger off any entity in a group changing. My biggest motivation is being able to add a new member to a group and have everything associated with that group seamlessly work/update.

It seems the way things currently are, I will have to manually find any automations that list the entity ids separately and add the new member manually to those lists. If you miss an automation in the manual update, it won’t respond to the new entity and it may take a long time to realize it was missed.

I was hoping something like this would work:

    trigger:
      - platform: template
        value_template: >
          {% set entities = expand('group.indoor_light_summary') %}
          {%- for x in entities -%}
          - {{ x.entity_id }}
          {% endfor %}

Is there still no way to use some kind of variable/template for the list of id’s so you don’t have to do a bunch of manual updating?

2 Likes

I guess one kind of nasty work around with limited value would be to use sensor.time as the trigger and then use conditions and/or templates as desired. I recently got help from some members here on creating a sensor for a list of any low batteries that home assistant is monitoring.

  ### Low Battery Sensor
  - platform: template
    sensors:
      low_batteries:
        entity_id: sensor.time
        value_template: >-
          {% set entities = expand('group.door_lock_batteries') +
                            expand('group.door_sensor_batteries') +
                            expand('group.motion_sensor_batteries') +
                            expand('group.temp_sensor_batteries') +
                            expand('group.other_batteries') +
                            expand('group.button_cube_batteries') %}
          {% for x in entities if x.state|int < 30 %}
            {%- if not loop.first %}, {% endif -%}
            {{- x.name -}}
          {% endfor %}

had the same idea: Use new template 'expand' functionality in automation trigger?

no solution just yet…it simply isnt accepted by the config checker. so not even a matter of updating or not

Move to appdaemon and you’ll have this dynamic functionality. Unfortunately, HA needs an entity ID to listen to when analyzing a template.

Listener listens to state changes -> state changes -> automation triggers.

without a state object to listen to, how would it know when to trigger?

In appdeamon, you walk through the groups entity_id’s and create the listeners.

A good enhancement/feature request would be some sort of group trigger that tracks state changes on sub entities with a paired value template.

1 Like

Hi @petro. Thanks for chiming in. I recognized your name right away, you have either directly helped me before or I have used threads where you have helped others a bunch of times. You have sufficiently motivated me to take the plunge and try out appdaemon. I’ve considered giving it a try tons of times, but I didn’t exactly know what advantages there would be and the whole thing is unknown/daunting to me (at least right now).

I agree about your “A good enhancement/feature request would be some sort of group trigger that tracks state changes on sub entities with a paired value template.” comment.

I recently ran into an issue where my wife would accidentally turn on all the lights when scrolling through the UI. There is a group called “indoor_light_summary” that has the toggle button that she would sometimes hit on accident. I wanted to be able to detect that event without having to update any lists manually when I add new members to group.indoor_light_summary. I did find a way, I would describe it as a form of “polling”. I’m still going to check out appdaemon to see if there is a cleaner way, but this is what I did:

Create an input_boolean that I can use as a listener for sensors

configuration.yaml
input_boolean:
  clock_5sec:
    name: Clock 5 Seconds
    icon: mdi:progress-clock

automation.yaml
  - id: clock_5sec
    alias: Five Second Clock
    trigger:
      - platform: time_pattern
        seconds: '/5'
    action:
      - service: homeassistant.toggle
        entity_id: input_boolean.clock_5sec

Now I can use that input_boolean to make the sensor below update without having to list out all the entity ids separately. When a new member is added to group.indoor_light_summary, the sensor will automatically include the new member without any manual modifications.

configuration.yaml
sensor:   
  - platform: template
    sensors:
      all_indoor_light_summary_on:
        entity_id: input_boolean.clock_5sec
        value_template: >-
          {% set num_lights = expand('group.indoor_light_summary') | 
                              map(attribute='name') | list | count %}
          {% set count_on = expand('group.indoor_light_summary') |
                            selectattr('state', 'equalto', 'on') | 
                            map(attribute='name') | list | count %}
          {% if count_on == num_lights %}
            All Lights On
          {% elif count_on == 0 %}
            All Lights Off
          {% elif count_on == 1 %}
            1 light is one
          {% else %}
            {{ count_on }} lights are on
          {% endif %}

Now I can use changes in that sensor to trigger an automation.

automation.yaml
  - id: all_indoor_light_summary_on
    alias: All Indoor Light Summary On
    trigger:
      - platform: state
        entity_id: sensor.all_indoor_light_summary_on
    condition:
      condition: state
      entity_id: sensor.all_indoor_light_summary_on
      state: 'All Lights On'
    action:
      - service: notify.text_jeremy_nichole
        data:
          message: "Did you mean to turn on all the lights?"

FYI, if you integrate custom:button-card in your UI for all switches, it has an option to lock buttons to avoid things like this. I added a bunch of switches with locks because me and my wife accidentally hit buttons in the UI when we don’t mean to.

fwiw, you can also use:

{% set num_lights = expand('group.indoor_light_summary')| count %}

that is, I’ve tested that with the automatic group

{% set num_lights = expand(‘group.all_lights’)| count %}

3 Likes

Hi, the topic is quite old, but I had the same problem and I have found a solution without using AppDaemon:

- alias: 'Device updated'
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data.entity_id in (expand('group.all_devices') | map(attribute='entity_id')) }}"
  action:
    - service: persistent_notification.create
      data_template:
        title: "State changed!"
        message: "{{ state_attr(trigger.event.data.entity_id, 'friendly_name')}}"
        notification_id: 1234
9 Likes

yes, you could do that, which has the same effect as:

value_template: >
  {{ trigger.event.data.entity_id in state_attr('group.all_devices','entity_id') }}

if you want the state in the notification:

        message: >
          {{ state_attr(trigger.event.data.entity_id, 'friendly_name')}} to {{states(trigger.event.data.entity_id)}}
5 Likes

And what node it Node-Red can be used for the same operation?

This is an old thread, but I liked to share a clean solution.
E.g. the last proposal to trigger on any state change could induce a lot of template processing:

With all the new template options I created a nice “group” template suitable to trigger automations ( template grouping was already mentioned earlier in this thread, but it was old style based).

For those looking how to triggering an automation by group members; my new style template example:

STEP 1: Create a group alike template. It is not exactly the same as official groups:

  • you can’t expand the group, but have enumerate the entity_id attribute instead.
    • expand( 'binary_sensor.myalarms') will fail!
    • use set entities = state_attr('binary_sensor.myalarms','entity_id') instead
    • hurrah: the auto-entities card does accept this as a group sensor!
  • The template is setup in such a way that is does minimal processing and “declare your entities once” moto:
    • YAML anchors and variables in the trigger section to achieve declare once
    • Templates evaluate triggers, then the state and finaly the attribute. So try to evaluate the group state in only once the trigger section and share the results via variables

This is the example of the templated group binary_sensor, that monitors alarm sensors (like water, smoke):

- trigger:
  - platform: state
    variables: &my_variables_alarms
      entities: &triggered_by_alarms
       - binary_sensor.keuken_smoke
       - binary_sensor.zolder_smoke
       - binary_sensor.water_leak_keuken
       - binary_sensor.water_leak_garden
      alarms: >
         {% set ns = namespace(alarms = []) %}
         {% for entity in entities %}
           {% if is_state(entity,'on') %}
             {% set ns.alarms = ns.alarms + [{ 
                'name':entity,
                'since':as_timestamp(states[entity].last_changed),
                }]
              %}
           {% endif %}
         {% endfor %}
         {{ ns.alarms | sort(reverse=true,attribute='since')  }}
    entity_id: *triggered_by_alarms
  - platform: homeassistant
    event: start
    variables: *my_variables_alarms
  binary_sensor:
  - unique_id: myalarms
    name: MyAlarms
    state: >
      {{ alarms | count > 0 }}
    attributes:
      entity_id: "{{ entities }}"
      alarms: "{{ alarms }}"
    device_class: problem

Some explanation on the template structure:

  • my_variables_alarms and triggered_by_alarms are yaml anchors.
    • Keep their names unique per sensor!
    • include the variable anchor my_variables_alarms in every extra trigger you add!
  • alarms in the trigger section is an rather advanced group evaluation example!
    • it creates a list of alarms triggers and when they occured (in seconds since). That is stored in the attributes for easy creation a notification text.
    • You can also simplify and just count them: {{ expand(entities) | rejectattr('state', 'eq', 'idle') | list | count }}, and change the state part into {{ alarms > 0 }}
    • the triggered alarms are reversed sorted on time stamp, so the last one is on the top of the list
  • The entities are only declared once in the template
  • The template group evaluation is only done for each trigger

The result will be this sensor:

entity_id:
  - binary_sensor.keuken_smoke
  - binary_sensor.zolder_smoke
  - binary_sensor.water_leak_keuken
  - binary_sensor.water_leak_garden
alarms:
  - name: binary_sensor.keuken_smoke
    since: 1697618037.804547
device_class: problem
icon: mdi:alarm-light
friendly_name: MyAlarms

note: only the first sensor was triggered in this example. So just one sensor listed in the attribute alarms.

STEP 2: the automation example

alias: Alarm group handling
description: Notify when e.g. water leakage or smoke alarms got activated
trigger:
  - platform: state
    entity_id:
      - binary_sensor.myalarms
condition:
  - condition: state
    entity_id: binary_sensor.myalarms
    state: "on"
action:
  - variables:
      msgtxt: >
        {% set alarms = state_attr('binary_sensor.myalarms','alarms') or [] %}
        {%- for alarm in alarms %} 
           {{ state_attr(alarm.name, 'friendly_name') }} ({{ relative_time(as_datetime(float(alarm.since))) }}) 
        {%- endfor %}
  - service: notify.mobile_app_iOS
    data:
      title: Alarm!
      message: "{{msgtxt}}"
      data:
        push:
          sound: default
          interruption-level: critical
          volume: 1
  - service: notify.mobile_app_Android
    data:
      title: Alarm!
      message: "{{msgtxt}}"
      data:
        ttl: 0
        priority: high
        importance: high
        channel: alarm_stream_max
    enabled: false
mode: single

Some explanation on the automation part:

  • Important that you monitor the group state without any other constrains (like to or from). That ensures that changes in the alarms attribute will also trigger this automation. So even if the state is already on, and an extra alarm emerges, the list will be extended.
  • Then the automation condition ensures the actions (e.g. notify) are only performed when the state is on
  • As a bonus, I left in two notification examples, that demonstrate how to prioritize them on the iOS and android platforms (as it is an alarm example)

I hope this example inspires :slight_smile:
Suggestions to further improve the concept are welcome :slight_smile:
Even better if it inspires home assistant contributors to natively support template groups as a helper :wink:

2 Likes

Your YAML anchor idea is great (for “static” groups)! Now I can:

group:
  my_group:
    entities: &my_group
      - sensor.aaa
      - sensor.bbb

automation:
  - trigger:
      - platform: state
        entity_id: *my_group
    action:
      - ...

# or where a list of entity ids are required:
    for_each: *my_group # for static groups this is the equivalent of '{{ expand("group.my_group") | map(attribute="entity_id") | list }}'

Hi,

didn’t need dynamic groups myself, but I’m sure you can create a dynamic group with these templates as well. Only restriction is that the template will trigger on a different scope than the entities you include. So be careful it is not triggered on all states

  • Create a domain or time_pattern trigger ( e.g device tracker) instead of the entities state trigger in my example
  • In that trigger create a variable entities.
  • Fill that variable with a template that defines the list of entries you want to have in the dynamic group.
  • use that entities variable to set the entity_id attribute

Success

1 Like