Automation trigger: Using a template to generate a list of entity_ids

Hi,

I’ve written this automation to try and generate a list off entity_id’s in the trigger. Needless to say it doesn’t work.

automation:
  - alias: 'Lights: Movement in hallway'
    id: "lights_hallway_movement"
    mode: queued
    trigger:
      - platform: state
        entity_id: binary_sensor.hallway_motion_occupancy
        to: "on"
        id: "motion"
      - platform: state
        entity_id: >
          {% set trigger = 'hallway_motion_occupancy' %}
          - {{ states.binary_sensor
            | selectattr('entity_id','match','.*_motion_occupancy')
            | rejectattr('entity_id','match','binary_sensor.'~trigger)
            | rejectattr('entity_id','search','patio')
            | rejectattr('entity_id','search','front')
            | map(attribute='entity_id')
            | list
            | join('\n- ')
            }}
        to: "on"
        id: "cancel"
    action:
    [...]

It certainly looks like a list of entity_ids when I do this in settings ➜ template:
Screenshot 2024-08-07 at 14.14.04

I realise result type: string isn’t helping, but removing both join() & the preceding - to create a list doesn’t work either.

My logs doesn’t really tell me why it is failing only that it:

failed to setup triggers and has been disabled: Entity {% set trigger = ‘study_motion_occupancy’ %} {{ states.binary_sensor | selectattr(‘entity_id’ is neither a valid entity ID nor a valid UUID for dictionary value @ data[‘entity_id’]. Got None

Is this possible and, if so, what am I doing wrong?

The State Trigger doesn’t support templates. Consider using a Template Trigger.

1 Like

thanks - good to know that I wasn’t going completely mad!

1 Like

No worries, it’s a common assumption. It would be handy if a State Trigger’s entity_id option supported templates but it currently doesn’t.

NOTE

Even if it did support templates, what you created still wouldn’t work.

Your template produces a list in YAML format. Don’t use templates to generate YAML code in triggers, Template Sensors, etc.

This would be the acceptable way to generate a list:

          {% set trigger = 'hallway_motion_occupancy' %}
          {{ states.binary_sensor
            | selectattr('entity_id','match','.*_motion_occupancy')
            | rejectattr('entity_id','match','binary_sensor.'~trigger)
            | rejectattr('entity_id','search','patio')
            | rejectattr('entity_id','search','front')
            | map(attribute='entity_id')
            | list }}
1 Like

This reminded me a related question:
Consider these different triggers:

- platform: state
  entity_id:
    - binary_sensor.xxx_1
    - binary_sensor.xxx_2
    - binary_sensor.xxx_3
  to: on
- platform: template
  value_template: >-
    {{ states.binary_sensor |
       selectattr('entity_id','search','binary_sensor.xxx_') |
       selectattr('state','==','on') |
       list | count > 0 }}

Which trigger consumes more CPU time?

thanks!

I was trying the following as you posted :slight_smile:

          {% set trigger = 'study_motion_occupancy' %}
          {{ states.binary_sensor
            | selectattr('entity_id','match','.*_motion_occupancy')
            | rejectattr('entity_id','match','binary_sensor.'~trigger)
            | rejectattr('entity_id','search','patio')
            | rejectattr('entity_id','search','front')
            | rejectattr('state','eq','off')
            | list
            | count
            }}

It’s a good question - I imagine the template one consumes more, but I don’t know for sure.

My rational for using the template is that it’s more ‘future proof’ (for when if I add more motion sensors and forget to update this particular automation). Also, I have several like this and was thinking of turning it into a blueprint.

Here’s how a Template Trigger is triggered:

So your template will trigger when the count changes from 0 to a positive integer (like 1). Any additional changes involving positive numbers will not serve to trigger it again. The count must first return to 0 and then change to a positive integer in order to trigger it. Any positive number is considered to be true and it must first change back to false (0) before it can be triggered again.

Example:

  • Assume all motion sensors are off.
  • Now one motion sensor turns on. It will trigger the automation because the count changes from 0 to 1.
  • While the motion sensor is still reporting on, a second motion sensor turns on.
  • The count changes from 1 to 2 but it won’t serve to trigger the Template Trigger again.

FWIW, petro has submitted a PR that would provide the Template Trigger with an option to trigger whenever its template’s result changes.


All this to say that the Template Trigger you have created doesn’t replicate the behavior of a State Trigger that will trigger whenever any of its monitored entities changes state (and there’s no way to do it currently).

2 Likes

Means - this template will work ONCE when some entity changes OFF-> ON

- platform: template
  value_template: >-
    {{ states.binary_sensor |
       selectattr('entity_id','search','binary_sensor.xxx_') |
       selectattr('state','==','on') |
       list | count > 0 }}

and will not be triggered again when ANOTHER entity changes OFF-> ON.
I.e. - not same functionality as

- platform: state
  entity_id:
    - binary_sensor.xxx_1
    - binary_sensor.xxx_2
    - binary_sensor.xxx_3
  to: on
1 Like

Exactly.

Yes, that was what I wanted (in this instance).

Basically, this is “switch on light if you are in that room and switch it off if you are anywhere else”.

The Template Trigger will be adequate provided this scenario is acceptable for your needs:

  • Person walks into bedroom, the bedroom motion sensor is turned on, automation is triggered.

  • While bedroom motion sensor is still reporting on, a second person walks into kitchen, the kitchen motion sensor is turned on, automation is not triggered.

1 Like

Yes, that is correct. You haven’t seen the whole automation - there is a condition that input_bookean.single_occupancy is on. If off, the automation will not respond to that trigger but, instead use timers and the continued motion detection in that room. I’ll post the full automation in a minute for info.

1 Like

Here is the full package (but it uses some entities not defined here (eg: input_select.house_mode).

Package
input_boolean: #----------------------------------------------------------------

  hallway_light_awake:
    name: Allow Hallway motion (awake)

  hallway_light_asleep:
    name: Allow Hallway motion (asleep)

  hallway_light_triggered_by_motion:
    name: Hallway light triggered by motion


input_number: #-----------------------------------------------------------------

  hallway_light_awake_level:
    name: Brightness (awake)
    unit_of_measurement: "%"
    min: 50
    max: 100
    step: 10
    icon: mdi:brightness-percent

  hallway_light_awake_time:
    name: Timer (awake)
    unit_of_measurement: "min"
    min: 0.5
    max: 5
    step: 0.5
    icon: mdi:timer

  hallway_light_awake_warning_level:
    name: Warning brightness (awake)
    unit_of_measurement: "%"
    min: 5
    max: 30
    step: 5
    icon: mdi:brightness-percent

  hallway_light_awake_warning_time:
    name: Warning timer (awake)
    unit_of_measurement: "min"
    min: 0.5
    max: 3
    step: 0.5
    icon: mdi:timer

  hallway_light_asleep_time:
    name: Timer (asleep)
    unit_of_measurement: "min"
    min: 0.5
    max: 5
    step: 0.5
    icon: mdi:timer


input_select: #-----------------------------------------------------------------

  hallway_light_level:
    name: "Allow Hallway motion when light is"
    icon: mdi:sunglasses
    options:
      - "Any"
      - "Grey or less"
      - "Dark or less"
      - "Night"


timer: #------------------------------------------------------------------------

  hallway_light:
    name: Timer
    icon: mdi:timer
    duration: "00:02:30"
    restore: true

  hallway_light_warning:
    name: Warning timer
    icon: mdi:timer
    duration: "00:02:30"
    restore: true


automation: #-------------------------------------------------------------------

  - alias: 'Lights: Movement in hallway'
    id: "lights_hallway_movement"
    mode: queued
    trigger:
      # activation sensor(s)
      - platform: state
        entity_id: binary_sensor.hallway_motion_occupancy
        to: "on"
        id: "motion_detected"
      # cancelation sensor(s) - single_occupancy_mode
      - platform: template
        value_template: >
          {% set trigger = 'hallway_motion_occupancy' %}
          {{ states.binary_sensor
            | selectattr('entity_id','match','.*_motion_occupancy')
            | selectattr('state','eq','on')
            | rejectattr('entity_id','match','binary_sensor.'~trigger)
            | rejectattr('entity_id','search','patio')
            | rejectattr('entity_id','search','front')
            | list
            | count > 0
            }}
        id: "single_occupancy_cancel"
      # timers
      - platform: event
        event_type: timer.finished
        event_data:
          entity_id: timer.hallway_light
        id: "timer_finished"
      - platform: event
        event_type: timer.finished
        event_data:
          entity_id: timer.hallway_light_warning
        id: "warning_finished"
      # house_mode
      - platform: state
        entity_id: input_select.house_mode
        to: "Asleep"
        id: "asleep_cancel"
    condition: "{{ states('input_boolean.lux_scenes_locked') == 'off' }}"
    action:
      - choose:
          # start ------------------------------
          - conditions:
              - "{{ trigger.id == 'motion_detected'}}"
              - "{{ states('light.hallway') == 'off' }}"
              - or: # house_mode
                  - and:
                      - "{{ states('input_select.house_mode') == 'Awake' }}"
                      - "{{ states('input_boolean.hallway_light_awake') == 'on' }}"
                  - and:
                      - "{{ states('input_select.house_mode') == 'Asleep' }}"
                      - "{{ states('input_boolean.hallway_light_asleep') == 'on' }}"
              - or: # light_level
                  - "{{ states('input_select.hallway_light_level') == 'Any' }}"
                  - and:
                      - "{{ states('input_select.hallway_light_level') == 'Grey or less' }}"
                      - "{{ states('sensor.light_level') in ['Grey','Dark','Night'] }}"
                  - and:
                      - "{{ states('input_select.hallway_light_level') == 'Dark or less' }}"
                      - "{{ states('sensor.light_level') in ['Dark','Night'] }}"
                  - "{{ states('sensor.light_level') == 'Night' }}"
            sequence:
              - service: light.turn_on
                target:
                  entity_id: light.hallway
                data:
                  transition: 1
                  brightness_pct: "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_level'),1) }}"
              - service: input_boolean.turn_on
                target:
                  entity_id: input_boolean.hallway_light_triggered_by_motion
              # start timer if not using cancelation sensors (single-user mode)
              - if: "{{ states('input_boolean.single_occupancy_mode') == 'off' }}"
                then:
                  - service: timer.start
                    target:
                      entity_id: timer.hallway_light
                    data:
                      duration:  "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_time')|float*60,states('input_number.hallway_light_asleep_time')|float*60) }}"
                  # cancel warning timer (just in case - prob not needed)
                  - service: timer.cancel
                    target:
                      entity_id: timer.hallway_light_warning

          # extend -----------------------------
          - conditions:
              - "{{ trigger.id == 'motion_detected'}}"
              - or:
                  - "{{ states('timer.hallway_light') == 'active' }}"
                  - "{{ states('timer.hallway_light_warning') == 'active' }}"
            sequence:
              # light (back) to non-warning level
              - service: light.turn_on
                target:
                  entity_id: light.hallway
                data:
                  transition: 1
                  brightness_pct: "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_level'),1) }}"
              # restart timer
              - service: timer.start
                target:
                  entity_id: timer.hallway_light
                data:
                  duration:  "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_time')|float*60,states('input_number.hallway_light_asleep_time')|float*60) }}"
              # cancel warning timer
              - service: timer.cancel
                target:
                  entity_id: timer.hallway_light_warning

          # warning if no motion detected ----------------------------
          - conditions: "{{ trigger.id == 'timer_finished'}}"
            sequence:
              - if: "{{ states('binary_sensor.hallway_motion_occupancy') == 'on' }}"   # motion detected
                then:
                  # restart timer
                  - service: timer.start
                    target:
                      entity_id: timer.hallway_light
                    data:
                      #duration:  "{{ iif(is_state('input_select.house_mode','Awake'),150,30) }}"
                      duration:  "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_time')|float*60,states('input_number.hallway_light_asleep_time')|float*60) }}"
                else:
                  # dim light
                  - service: light.turn_on
                    target:
                      entity_id: light.hallway
                    data:
                      transition: 1
                      brightness_pct: "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_warning_level'),1) }}"
                  # start warning timer
                  - service: timer.start
                    target:
                      entity_id: timer.hallway_light_warning
                    data:
                      duration:  "{{ iif(is_state('input_select.house_mode','Awake'),states('input_number.hallway_light_awake_warning_time')|float*60,1) }}"

          # cancel / finish -----------------------------
          - conditions:
              - or:
                  - "{{ trigger.id == 'warning_finished'}}"
                  - "{{ trigger.id == 'asleep_cancel'}}"
                  - and:
                      - "{{ trigger.id == 'single_occupancy_cancel'}}"
                      - "{{ states('input_boolean.single_occupancy_mode') == 'on' }}"
                      - "{{ states('input_boolean.hallway_light_triggered_by_motion') == 'on' }}"
            sequence:
              - service: timer.cancel
                target:
                  entity_id:
                    - timer.hallway_light
                    - timer.hallway_light_warning
              - service: light.turn_off
                target:
                  entity_id: light.hallway
                data:
                  transition: 1
              - service: input_boolean.turn_off
                target:
                  entity_id: input_boolean.hallway_light_triggered_by_motion