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.