Repeat for_each loop syntax with template

Is this not a valid way to call the repeat for_each loop?

repeat:
  for_each: "{{ states.person | map(attribute='object_id') | list }}"

The template works fine in the editor, but I get this error when I try to save my automation:

Message malformed: template value does not contain a dynamic template for dictionary value @ data['actions'][2]['repeat']['for_each']

I’m not sure exactly what it’s telling me.

Post the rest of the action… that part looks fine.

Yes, it wa something else in the following section of the loop. The error didn’t make it that obvious. Instead of looping through all the person entities, I instead looped through the members of a group that has some person entities in it. (Why can’t we make groups with person entities in the GUI?)

This made me think. I’m conflating person enities with users. Is there a way to deal with users of the system in an automation?

Anyway, here’s what I was trying to accomplish. I’ve taken my blink camera’s motion-detection videos and provided a more useful notification, including the video and giving the recipient the ability to snooze the notifications (because they can get to be quite a lot) while still showing a low importance notification of how many motion events they missed that includes an option to stop snoozing.

The blink integration has a hard-coded video file naming standard that I parse to get to the camera entity. for the snooze and the number of missed notifications, I used some helpers that are named with the person’s name so that I can reconstruct the entity ids of those helpers.

I’m checking for triggers being defined in several places because I was testing by manually running the actions, in which case there is no trigger.

alias: Security Camera Motion Detected
description: >-
  Sends a notification with the latest ideo when motion is detected from a
  camera.
triggers:
  - id: new_motion
    trigger: state
    entity_id:
      - event.security_camera_folder_watcher
    attribute: event_type
    to: closed
  - id: callback
    alias: Handle notification callback event
    trigger: event
    event_type: notification_callback
conditions:
  - condition: template
    value_template: >-
      {{ (trigger is undefined) or (trigger.platform != 'event') or
      trigger.event.data.id.startswith('snooze_camera_notifications_') }}
    alias: If it was from the callback, only handle it if it was our event
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ trigger is defined and trigger.platform == 'event' }}"
            alias: If we were triggered by an event
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ trigger.event.data.id.split('_')[3] == 'on' }}"
                    alias: Turning snooze on
                sequence:
                  - action: input_boolean.turn_on
                    target:
                      entity_id: >-
                        {%- set p = trigger.event.data.id.split('_')[-1] -%}
                        input_boolean.{{ p }}_snooze_camera_notifications
              - conditions:
                  - alias: Turning snooze off
                    condition: template
                    value_template: "{{ trigger.event.data.id.split('_')[3] == 'off' }}"
                sequence:
                  - action: counter.reset
                    target:
                      entity_id: >-
                        {%- set p = trigger.event.data.id.split('_')[-1] -%}
                        counter.{{ p }}_missed_camera_notifications
                  - action: input_boolean.turn_off
                    target:
                      entity_id: >-
                        {%- set p = trigger.event.data.id.split('_')[-1] -%}
                        input_boolean.{{ p }}_snooze_camera_notifications
    default:
      - if:
          - condition: template
            value_template: "{{ camera_entity_id | length > 0}}"
        then:
          - action: shell_command.copy_{{ camera_entity_id.replace('.', '_') }}_video
            metadata: {}
            data: {}
            response_variable: video_copy_response
          - action: homeassistant.update_entity
            data:
              entity_id:
                - "{{ camera_entity_id }}_video"
      - alias: Repeat an action for every 'person' entity
        repeat:
          for_each: |-
            {{
              expand('group.camera_notifications')
              | map(attribute='object_id')
              | list 
            }}
          sequence:
            - alias: Determine if the user has notifications snoozed
              if:
                - alias: If user has notifications snoozed
                  condition: template
                  value_template: >-
                    {{ is_state('input_boolean.' + repeat.item +
                    '_snooze_camera_notifications', 'on') }}
              then:
                - action: counter.increment
                  target:
                    entity_id: counter.{{ repeat.item }}_missed_camera_notifications
                  alias: Increment the number of notifications missed
                - action: notify.{{ repeat.item }}
                  alias: Update snooze notification
                  data:
                    title: Motion detection snoozed
                    message: >-
                      {%- set c = states('counter.' + repeat.item +
                      '_missed_camera_notifications') -%} Since snoozing, there
                      have been {{ c | int }} motion events.
                    data:
                      priority: high
                      ttl: 0
                      group: motion-detection-snoozed
                      tag: motion-detection-snoozed
                      channel: Motion detection snoozed
                      importance: low
                      actions:
                        - action: snooze_camera_notifications_off_{{ repeat.item }}
                          title: Turn off Snooze
              else:
                - alias: Send the regular motion notification
                  action: notify.{{ repeat.item }}
                  data:
                    title: "{{ camera_name }} camera"
                    message: "{{ time_string }} on {{ date_string }}"
                    data:
                      priority: high
                      ttl: 0
                      group: motion-detected
                      channel: Motion detection
                      video: "{{ video_file_path }}"
                      actions:
                        - action: URI
                          title: Watch
                          uri: >-
                            {{ state_attr(camera_entity_id + '_video',
                            'entity_picture') }}
                        - action: snooze_camera_notifications_on_{{ repeat.item }}
                          title: Snooze
mode: queued
variables:
  video_file_path: |-
    {{
      state_attr('event.security_camera_folder_watcher', 'path')
      if ((trigger is undefined) or (trigger.to_state is undefined)) else
      trigger.to_state.attributes.path
    }}
  video_file_name: "{{ video_file_path.split('/') | last }}"
  serial: "{{ video_file_name[16:-4] | upper }}"
  camera_by_serial: |-
    {{
      states.camera
        | selectattr('attributes.brand', '==', 'Blink')
        | selectattr('attributes.serial', '==', serial)
        | map(attribute='entity_id')
        | first
    }}
  camera_entity_id: |-
    {{
      'camera.front_door'
      if (camera_by_serial | length == 0) else
      camera_by_serial
    }}
  camera_name: "{{ state_attr(camera_entity_id, 'friendly_name') }}"
  year: "{{ video_file_name[:4] }}"
  month: "{{ strptime(video_file_name[4:6], '%m').strftime('%b') }}"
  day: "{{ video_file_name[6:8] }}"
  hour: "{{ video_file_name[9:11] }}"
  minute: "{{ video_file_name[11:13] }}"
  second: "{{ video_file_name[13:15] }}"
  date_string: "{{ month }} {{ day }}"
  time_string: "{{ hour }}:{{ minute }}:{{ second }}"
max: 25

There is always a trigger variable when running an automation, even when you run the automation manually. For such a run it will only have a single key platform whose value is null / none. So the proper way to check for a manual run in a template would be trigger.platform is none.

1 Like