Files
homelab-configs/frigate/frigate_notifications.yaml
2026-03-12 21:07:53 -05:00

2141 lines
111 KiB
YAML
Executable File

blueprint:
name: Frigate Notifications (0.14.0.2w)
author: SgtBatten
homeassistant:
min_version: 2024.11.0
description: |
## Frigate Notifications
A highly customisable Notification template for Frigate Camera Notifications.
### Software Version Requirements
- Minimum Home Assistant Version: 2024.10
- Minimum Frigate Version: 0.14.0
- Minimum Frigate Integration Version: 5.7.0
- **Note:** "Enable the unauthenticated notification event proxy" must be ticked during setup
- An MQTT broker connected to Home Assistant and Frigate.
- Minimum iOS Version: 15.0
### Required Configuration Options:
- Frigate Cameras
- Mobile Device (Running HA Companion App)
### Features:
- Easily select the camera entities or mobile device using a drop-down menu.
- Configure multiple cameras in one automation.
For the full features list visit the [GitHub Repository][1]
[1]: https://github.com/SgtBatten/HA_blueprints/blob/main/Frigate_Camera_Notifications
## Support
Go to https://github.com/SgtBatten/HA_blueprints to report bugs, request new features, or get support with your configuration.
domain: automation
source_url: https://github.com/SgtBatten/HA_blueprints/blob/main/Frigate_Camera_Notifications/Beta.yaml
input:
camera:
name: Frigate Cameras
description: |
Select the cameras that will trigger notifications.
If you do not see cameras listed in the drop-down, check you have the Frigate integration installed.
To more effectively customise the notifications you may need to create one automation per camera.
selector:
entity:
filter:
integration: frigate
domain: camera
multiple: true
notify_device:
name: Mobile Device
description: Select a device that runs the official Home Assistant app to receive notifications. If you wish to notify a group of devices and/or Android/Fire TV use the field below to override this selection. This will be ignored in that case but is still required.
default: false
selector:
device:
filter:
integration: mobile_app
notify_group:
name: Notification Group or Android/Fire TV (Optional)
description: |
The name of the group or individual TV to send notifications to.
If set, this will override the individual devices above.
Note: If the group contains both mobile devices and TVs, the TV will not display the snapshot unless "TV notifications" is set to true. However, this will stop Android phones from receiving thumbnails.
default: ""
base_url:
name: Base URL (Optional)
description: |
The external URL for your Home Assistant instance.
Recommended for iOS and required for Android/Fire TV.
Must include the scheme i.e http:// or https://
Examples: http://192.168.1.25:8123 https://homeassistant.mydomain.com
default: ""
mqtt_topic:
name: MQTT Topic (Advanced)
description: The MQTT topic Frigate sends review messages in.
default: frigate/reviews
client_id:
name: Client ID (Optional-Advanced)
description: Used to support multiple instances of Frigate. Leave blank if you don't know what to do.
default: ""
telegram:
name: |
# Telegram Configuration
description: These are Telegram specific Options.
icon: mdi:phone-outline
collapsed: true
input:
telegram_base_url:
name: Telegram Base URL (Optional)
description: |
The internal URL for your Home Assistant instance.
Used only for telegram integration (For downloading media). Useful when your base URL points to a proxy instance
which could be unreachable from your home assistant instance. If unset telegram will use Base URL.
Must include the scheme i.e http:// or https://
Examples: http://192.168.1.25:8123
default: ""
notify_telegram_chat_id:
name: Telegram Chat ID (Optional)
description: |
The chat ID to send telegram notifications to.
If set, this will override individual devices and groups above. Note: TV (far below) must be set to false.
Base URL must start with https since telegram URL buttons only work with https.
default: ""
notification_customisations:
name: |
# Notification Customisations
description: These are Notification Customisation Options
icon: mdi:bell
collapsed: true
input:
title:
name: Notification Title (Optional)
description: The title of the notification.
default: ""
message:
name: Notification Message
description: |
The message of the notification.
You can use variables such as {{camera_name}} and {{label}}
e.g., A {{ label }} {{ ' was' if type == 'end' else '' }} detected on the {{ camera_name }} camera.
default: "A {{ label }} {{ 'was detected' }} on the {{ camera_name }} camera."
selector:
select:
options:
- label: "Default: e.g., A Person was detected on the Side camera."
value: "A {{ label }} {{ 'was detected' }} on the {{ camera_name }} camera."
- label: "Short: e.g., Person detected - Side"
value: "{{ label }} detected - {{ camera_name }}"
- label: "Short with a timestamp HH:MM"
value: "{{ label }} detected - {{ camera_name }} at {{event['after']['start_time']|timestamp_custom('%H:%M')}}"
- label: "Long: e.g., A Person was detected on the Side camera in the driveway."
value: "{{ label }}{{ ' was' if type == 'end' else '' }} detected on the {{ camera_name }} camera{{' in the ' if after_zones|length}}{{ after_zones | join(', ') | replace('_', ' ' ) | title }}."
- label: "Long with a timestamp HH:MM"
value: "{{ label }}{{ ' was' if type == 'end' else '' }} detected on the {{ camera_name }} camera{{' in the ' if after_zones|length}}{{ after_zones | join(', ') | replace('_', ' ' ) | title }} at {{event['after']['start_time']|timestamp_custom('%H:%M')}}."
custom_value: true
update_sub_label:
name: Update Sub Labels (Advanced)
description: |
Update the title and/or message with the matched face name and trigger a notification update.
Requires face detection to be configured via Double Take (Advanced). Requires Title and/or Message to contain {{ label }} variable
default: true
selector:
boolean:
subtitle:
name: Subtitle
description: A secondary heading you can use in your notifications.
default: ""
critical:
name: Critical Notification (Optional)
description: |
Send as a critical notification to the mobile device. This will ignore silent/vibrate modes.
You can choose to limit critical notifications to certain times using a template (some examples are provided but you can enter your own as long as it outputs true or false)
default: "false"
selector:
select:
options:
- "false"
- "true"
- "{{'false' if now().hour in [8,9,10,11,12,13,14,15,16,17,18] else 'true'}}"
- "{{'true' if is_state('sun.sun', 'above_horizon') else 'false' }}"
- "{{ event['after']['top_score'] |float(0) > 0.8 }}"
custom_value: true
tts:
name: Use TTS to override silent/dnd modes on specific phones.
description: |
Some phones (the Samsung galaxy line, in particular,) disable the ability to use `alarm_stream` as a means to bypass the current sound settings. This option allows TTS to announce the alert instead, which does bypass the sound settings.
default: false
selector:
boolean:
tts_helper:
name: Entity to store ids already alerted by TTS
description: |
In order for TTS to honor alert_once settings, create a text helper (/config/helpers), which will be used to store the ids for any tts alerts that have gone out. Set the maximum character count to 250 to store the latest 25 alert ids. Then select it here
default: ""
selector:
entity:
filter:
integration: input_text
alert_once:
name: Alert Once (Optional)
description: Only the first notification for each event will play a sound. Updates, including new thumbnails, will be silent. iOS users who use Critical Notifications above will still hear default critical sounds for updates.
default: false
selector:
boolean:
attachment:
name: Initial Attachment
description: |
Choose which image or GIF to attach to the initial notification.
Note: TVs will always get sent the snapshot.
default: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg"
selector:
select:
options:
# format=android converts it to a 2:1 ratio for mobile display.
- label: Thumbnail
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg"
- label: Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: Snapshot with bounding box
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1"
- label: Snapshot cropped
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?crop=1"
- label: Snapshot cropped with bounding box
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&crop=1"
- label: Review GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{review_id}}/review_preview.gif"
# this will show the GIF associated with the first object detected by frigate
- label: Object 1 Event GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/event_preview.gif"
- label: Thumbnail (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg?format=android"
- label: Snapshot (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?format=android"
- label: Snapshot with bounding box (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&format=android"
- label: Snapshot cropped (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?crop=1&format=android"
- label: Snapshot cropped with bounding box (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&crop=1&format=android"
mode: dropdown
attachment_2:
name: Subsequent Attachment (Optional)
description: |
Choose which image or GIF to attach to the notification for any updates. It is recommended to enable Final Update
Set to None to continue using Initial Attachment for updates.
Note: TVs will get sent the snapshot if GIF is selected.
default: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg"
selector:
select:
options:
- label: None
value: ""
# format=android converts it to a 2:1 ratio for mobile display.
- label: Thumbnail
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg"
- label: Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: Snapshot with bounding box
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1"
- label: Snapshot cropped
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?crop=1"
- label: Snapshot cropped with bounding box
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&crop=1"
- label: Review GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{review_id}}/review_preview.gif"
# this will show the GIF associated with the first object detected by frigate
- label: Object 1 Event GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/event_preview.gif"
- label: Thumbnail (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/thumbnail.jpg?format=android"
- label: Snapshot (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?format=android"
- label: Snapshot with bounding box (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&format=android"
- label: Snapshot cropped (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?crop=1&format=android"
- label: Snapshot cropped with bounding box (2:1)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg?bbox=1&crop=1&format=android"
mode: dropdown
video:
name: Video (Optional)
description: You can optionally attach the clip to the notification which will replace the attachment above, if available.
default: ""
selector:
select:
options:
- label: None
value: ""
- label: Review GIF
# compared to attachments above, we add format=ts to allow HA to interpret the GIFs as videos, otherwise it fails.
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{review_id}}/review_preview.gif?format=ts"
- label: Object 1 Event GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/event_preview.gif?format=ts"
- label: Clip mp4
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
- label: Clip m3u8 (IOS)
# requires frigate integration 5.7.0+
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/master.m3u8"
mode: dropdown
final_update:
name: Final update (Optional)
description: Update the notification at the end of the event. Used to ensure the full video of GIF is sent. Configure the delay if necessary in the timers section (default - 5 seconds).
default: false
selector:
boolean:
color:
name: Notification Color - Android and TV only (Optional)
description: |
Set the color of the notification on your Android mobile device or TV.
TV supports `grey`, `black`, `indigo`, `green`, `red`, `cyan`, `teal`, `amber` or `pink` only.
default: "#03a9f4"
selector:
select:
options:
- label: Primary (Steelblue)
value: "#03a9f4"
- label: Red
value: "#f44336"
- label: Pink
value: "#e91e63"
- label: Purple
value: "#926bc7"
- label: Deep Purple
value: "#6e41ab"
- label: Indigo
value: "#3f51b5"
- label: Blue
value: "#2196f3"
- label: Light Blue
value: "#03a9f4"
- label: Cyan
value: "#00bcd4"
- label: Teal
value: "#009688"
- label: Green
value: "#4caf50"
- label: Light Green
value: "#8bc34a"
- label: Lime
value: "#cddc39"
- label: Yellow
value: "#ffeb3b"
- label: Amber
value: "#ffc107"
- label: Orange
value: "#ff9800"
- label: Deep Orange
value: "#ff5722"
- label: Brown
value: "#795548"
- label: Light Grey
value: "#bdbdbd"
- label: Grey
value: "#9e9e9e"
- label: Dark Grey
value: "#606060"
- label: Blue Grey
value: "#607d8b"
- label: Black
value: "#000000"
- label: White
value: "#ffffff"
icon:
name: Notification Icon (Optional)
description: Change the icon that displays on the notification. You can enter a single icon or create a template like the example given in the dropdown. You must include 'mdi:' in the icon name.
default: mdi:homeassistant
selector:
select:
options:
- mdi:homeassistant
- mdi:cctv
- "mdi:{{'account-outline' if label == 'Person' else 'dog' if label == 'Dog' else 'cat' if label == 'Cat' else 'car' if label == 'Car' else 'home-assistant'}}"
custom_value: true
group:
name: Group
description: The group name that will determine if notifications are grouped on your mobile device. If you want notifications grouped by camera, ensure it contains {{camera}}
default: "{{camera}}-frigate-notification"
sound:
name: Notification Sound - iOS only (Optional)
description: You can specify a sound file on your device that will play for the notifications. You will need to import the sound file into Home Assistant.
default: default
selector:
select:
options:
- default
- none
custom_value: true
volume:
name: Volume Sound - iOS only (Optional)
description: You can specify a sound level between 0 and 100
default: 100
selector:
number:
max: 100
min: 0
unit_of_measurement: "%"
step: 1
mode: slider
ios_live_view:
name: Live View Entity - iOS only (Optional)
description: Attach a live view from the selected entity to the notification for iOS devices.
default: ""
selector:
entity:
filter:
domain: camera
android_auto:
name: Android Auto
description: Show the notification on Android Auto if the receiving device is connected.
default: false
selector:
boolean:
sticky:
name: Sticky - Android only (Optional)
description: |
When enabled, the notification will stay active on the device after tapping it and remain there until cleared.
default: false
selector:
boolean:
channel:
name: Notification Channel - Android only (Optional)
description: Create a new channel for notifications to allow custom notification sounds, vibration patterns, and override of Do Not Disturb mode. Configured directly on the device.
default: ""
filters:
name: |
# Filters
description: These help limit when or what you are notified about.
icon: mdi:filter
collapsed: true
input:
review_severity:
name: Event Type
description: |
Specify the classification of Frigate events to be notified about.
default:
- alert
- detection
selector:
select:
options:
- label: Alerts
value: alert
- label: Detections
value: detection
multiple: true
master_condition:
name: Master Condition (Optional)
description: Set conditions that will stop the automation from running if the result is false. No other tests will be conducted later. This is a kill switch on initiation.
default: []
selector:
condition: {}
zone_filter:
name: Zone Filter on/off (Optional)
description: Enable to only notify if an object has entered a zone listed below.
default: false
selector:
boolean:
zones:
name: Required Zones (Optional - Enabled Above)
description: |
Enter the name of one zone at a time. It must be lowercase and include underscores as per your Frigate config.
By default any zone is acceptable. If you desire ALL listed zones to be entered before getting a notification, enable the multi toggle below.
default: []
selector:
select:
options:
- examples
- porch
- front_door
- side
- garden
multiple: true
custom_value: true
zone_multi:
name: Multi-Zone (Optional)
description: Require all zones specified above to be entered, instead of any listed zone. Zone Filter must be enabled also.
default: false
selector:
boolean:
zone_order_enforced:
name: Zone Order Enforced (Optional)
description: |
Combined with Multi-Zone, requires zones to be entered in the same order as their requirement.
Useful for alerting on arriving vehicles, while ignoring departing vehicles, for instance
default: false
selector:
boolean:
labels:
name: Object Filter (Optional)
description: Specify objects you wish to be notified about. Enter or select one object at a time.
default: ""
selector:
select:
options:
- person
- dog
- cat
- car
- package
- bird
multiple: true
custom_value: true
presence_filter:
name: Presence Filter (Optional)
description: Only notify if ALL of the selected presence entities are not "home".
default: ""
selector:
entity:
filter:
domain:
- device_tracker
- person
- group
multiple: true
state_filter:
name: State Filter on/off (Optional)
description: Enable the two State Filter settings below. Only notify if the selected entity is in the specified states.
default: false
selector:
boolean:
state_entity:
name: State Filter Entity (Optional)
description: Only notify if the selected entity is in the below state. You must enable "State Filter" above to use this.
default: ""
selector:
entity:
state_filter_states:
name: State Filter States (Optional)
description: Enter the states that the above entity must be in, one at a time.
default: []
selector:
select:
options: []
multiple: true
custom_value: true
disable_times:
name: Time Filter (Optional)
description: Prevent notifications from being sent during the specified hours
default: []
selector:
select:
multiple: true
options:
- label: 00:00 - 00:59
value: "0"
- label: 01:00 - 01:59
value: "1"
- label: 02:00 - 02:59
value: "2"
- label: 03:00 - 03:59
value: "3"
- label: 04:00 - 04:59
value: "4"
- label: 05:00 - 05:59
value: "5"
- label: 06:00 - 06:59
value: "6"
- label: 07:00 - 07:59
value: "7"
- label: 08:00 - 08:59
value: "8"
- label: 09:00 - 09:59
value: "9"
- label: 10:00 - 10:59
value: "10"
- label: 11:00 - 11:59
value: "11"
- label: 12:00 - 12:59
value: "12"
- label: 13:00 - 13:59
value: "13"
- label: 14:00 - 14:59
value: "14"
- label: 15:00 - 15:59
value: "15"
- label: 16:00 - 16:59
value: "16"
- label: 17:00 - 17:59
value: "17"
- label: 18:00 - 18:59
value: "18"
- label: 19:00 - 19:59
value: "19"
- label: 20:00 - 20:59
value: "20"
- label: 21:00 - 21:59
value: "21"
- label: 22:00 - 22:59
value: "22"
- label: 23:00 - 23:59
value: "23"
custom_filter:
name: Custom Filter (Optional - Advanced)
description: A filter that must result in true or false but can be templated as necessary. You will need to ensure it is enclosed with appropriate \"quotes\" and \{\{brackets\}\}.
default: true
timers:
name: |
# Timers
description: These are Timers you can customise
icon: mdi:gesture-double-tap
collapsed: true
input:
cooldown:
name: Cooldown (Optional)
description: Delay before sending another notification for this camera after the last event.
default: 0
selector:
number:
max: 86400
min: 0
unit_of_measurement: seconds
timeout:
name: Timeout
description: Length of time the automation will wait for MQTT updates.
default: 2
selector:
number:
max: 90
min: 0
unit_of_measurement: minutes
silence_timer:
name: Silence New Notifications (Optional)
description: |
How long to pause notifications for this camera when requested as part of the actionable notification.
Note: Restarting Home Assistant during the period will break the timer and may disable the automation
default: 30
selector:
number:
max: 3600
min: 0
unit_of_measurement: minutes
loiter_timer:
name: Loitering Notifications (Optional)
description: >
Sends new loitering notification if a stationary object is detected for longer
than the specified time. 0 is off and will not send notifications.
default: 0
selector:
number:
max: 3600
min: 0
unit_of_measurement: seconds
initial_delay:
name: Delay initial notification (Optional)
description: |
How long to delay the first notification for each event. Only applies if all conditions are met when the first event is defined by frigate
Use this if you DO NOT use "update image" and are experiencing notifications without attached images. Start with small numbers.
default: 0
selector:
number:
max: 15
min: 0
unit_of_measurement: seconds
final_delay:
name: Delay Final notification (Optional)
description: |
How long to delay the final notification for each event. Only applies if Final Update is enabled.
default: 5
selector:
number:
max: 20
min: 0
unit_of_measurement: seconds
actions:
name: |
# Action Buttons and URLS
description: These are Action Buttons and URL Options
icon: mdi:gesture-double-tap
collapsed: true
input:
tap_action:
name: Tap Action URL
description:
The URL to open when tapping on the notification. Some presets are provided, you can also set your own by typing in the box.
These options define the text and URLs associated with the three action buttons at the bottom of the notification.
default: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
selector:
select:
options:
- label: View Clip
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
- label: View Clip (IOS)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/master.m3u8"
- label: View GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{review_id}}/review_preview.gif"
- label: View Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: View Stream
value: "{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json['after']['camera'] | lower | replace('-','_')}}?token={{state_attr( 'camera.' ~ camera, 'access_token')}}"
- label: Open Home Assistant (web)
value: "{{base_url}}/lovelace"
- label: Open Home Assistant (app)
value: /lovelace
- label: Open Frigate
value: /ccab4aaf_frigate/dashboard
- label: Open Frigate (Full Access)
value: /ccab4aaf_frigate-fa/dashboard
- label: Open Frigate (proxy)
value: /ccab4aaf_frigate-proxy/dashboard
- label: Open Reolink App (Android)
value: app://com.mcu.reolink
- label: None (Android)
value: noAction
- label: Notification History (Android)
value: settings://notification_history
custom_value: true
button_1:
name: Action Button 1 Text
description: 'The text used on the first Action button at the bottom of the notification. Set the URL below. (Default is "View Clip")'
default: "View Clip"
url_1:
name: Action Button 1 URL
description: Customise what happens when you press the first Action Button. Select from one of the preconfigured options or enter your own custom URL.
default: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
selector:
select:
options:
- label: View Clip
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
- label: View Clip (IOS)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/master.m3u8"
- label: View GIF
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{review_id}}/review_preview.gif"
- label: View Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: View Stream
value: "{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json['after']['camera'] | lower | replace('-','_')}}?token={{state_attr( 'camera.' ~ camera, 'access_token')}}"
- label: Open Home Assistant (web)
value: "{{base_url}}/lovelace"
- label: Open Home Assistant (app)
value: /lovelace
- label: Open Frigate
value: /ccab4aaf_frigate/dashboard
- label: Open Frigate (Full Access)
value: /ccab4aaf_frigate-fa/dashboard
- label: Open Frigate (proxy)
value: /ccab4aaf_frigate-proxy/dashboard
- label: Open Reolink App (Android)
value: app://com.mcu.reolink
custom_value: true
icon_1:
name: Action Button 1 icon - iOS Only
description: Customise the Icon on the first Action Button. Only the iOS SFSymbols library is supported, not mdi:icons. e.g., sfsymbols:bell
default: ""
button_2:
name: Action Button 2 Text
description: "The text used on the second Action button at the bottom of the notification. Set the URL below."
default: "View Snapshot"
url_2:
name: Action Button 2 URL
description: Customise what happens when you press the second Action Button. Select from one of the preconfigured options or enter your own custom URL.
default: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
selector:
select:
options:
- label: View Clip
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
- label: View Clip (IOS)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/master.m3u8"
- label: View Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: View Stream
value: "{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json['after']['camera'] | lower | replace('-','_')}}?token={{state_attr( 'camera.' ~ camera, 'access_token')}}"
- label: Open Home Assistant (web)
value: "{{base_url}}/lovelace"
- label: Open Home Assistant (app)
value: /lovelace
- label: Open Frigate
value: /ccab4aaf_frigate/dashboard
- label: Open Frigate (Full Access)
value: /ccab4aaf_frigate-fa/dashboard
- label: Open Frigate (proxy)
value: /ccab4aaf_frigate-proxy/dashboard
- label: Open Reolink App (Android)
value: app://com.mcu.reolink
- label: Custom Action (Manual Trigger)
value: custom-{{ this.entity_id }}
custom_value: true
icon_2:
name: Action Button 2 icon - iOS Only
description: Customise the Icon on the second Action Button. Only the iOS SFSymbols library is supported, not mdi:icons. e.g., sfsymbols:bell
default: ""
button_3:
name: Action Button 3 Text
description: "The text used on the third Action button at the bottom of the notification. Set the URL below."
default: "Silence New Notifications"
url_3:
name: Action Button 3 URL
description: Customise what happens when you press the third Action Button. Select from one of the preconfigured options or enter your own custom URL.
default: silence-{{ this.entity_id }}
selector:
select:
options:
- label: Silence New Notifications
value: silence-{{ this.entity_id }}
- label: View Clip
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/clip.mp4"
- label: View Clip (IOS)
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/{{camera}}/master.m3u8"
- label: View Snapshot
value: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
- label: View Stream
value: "{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json['after']['camera'] | lower | replace('-','_')}}?token={{state_attr( 'camera.' ~ camera, 'access_token')}}"
- label: Open Home Assistant (web)
value: "{{base_url}}/lovelace"
- label: Open Home Assistant (app)
value: /lovelace
- label: Open Frigate
value: /ccab4aaf_frigate/dashboard
- label: Open Frigate (Full Access)
value: /ccab4aaf_frigate-fa/dashboard
- label: Open Frigate (proxy)
value: /ccab4aaf_frigate-proxy/dashboard
- label: Open Reolink App (Android)
value: app://com.mcu.reolink
- label: Custom Action (Manual Trigger)
value: custom-{{ this.entity_id }}
custom_value: true
icon_3:
name: Action Button 3 icon - iOS Only
description: Customise the Icon on the third Action Button. Only the iOS SFSymbols library is supported, not mdi:icons. e.g., sfsymbols:bell
default: ""
custom_action_manual:
name: Custom Action (Manual Trigger)
description: Customisable action that you can trigger with the Custom Action buttons in the notification. Select Custom Action on any Action Button above.
selector:
action: {}
default: []
custom_action_auto:
name: Custom Action (Auto Trigger)
description: Customisable action that will trigger on the initial notification (subject to all other notification conditions).
selector:
action: {}
default: []
custom_action_auto_multi:
name: Custom Action (Auto Trigger in Loop)
description: Customisable action that will trigger on all subsequent notifications (subject to all other notification conditions). If you want it to also trigger on the initial notification, you must enter it in both input fields.
selector:
action: {}
default: []
tv_notifications:
name: |
# TV Notifications
description: These are TV Notification Options
icon: mdi:television
collapsed: true
input:
tv:
name: TV Notification (Optional)
description: |
Set to true if you are notifying an Android/Fire TV
Can also be used to prioritise snapshots on the TV over Android mobile apps when notifying a mixed device group.
Base URL must be set
The below settings are for TV notifications only
default: false
selector:
boolean:
tv_position:
name: TV Notification Position (Optional)
description: Set the position of the notification on your TV
default: center
selector:
select:
mode: dropdown
options:
- bottom-right
- bottom-left
- top-right
- top-left
- center
tv_size:
name: TV Notification Size (Optional)
description: Set the size of the notification on your TV.
default: large
selector:
select:
mode: dropdown
options:
- small
- medium
- large
- max
tv_duration:
name: TV Notification Duration (Optional)
description: The duration (in seconds) that the notification will display on your TV.
default: 10
selector:
number:
max: 300
min: 0
unit_of_measurement: seconds
tv_transparency:
name: TV notification Transparency (Optional)
description: Set the transparency of the notification on your TV.
default: 0%
selector:
select:
mode: dropdown
options:
- 0%
- 25%
- 50%
- 75%
- 100%
tv_interrupt:
name: TV Notification Interrupt (Optional)
description: "If set to true the notification is interactive and can be dismissed or selected to display more details. Depending on the running app (e.g., Netflix), this may stop playback."
default: false
selector:
boolean:
debug:
name: |
# DEBUG
description: These are DEBUG Options
icon: mdi:bug
collapsed: true
input:
debug:
name: Debug
description: Enable to send debug messages to the home assistant logbook.
default: false
selector:
boolean:
redacted:
name: Redact Base URL
description: Hides the Base URL in the debug output for easier sharing. Does not hide it from other parts of the trace.
default: true
selector:
boolean:
mode: parallel
trigger_variables:
# severity: !input review_severity
mqtt_topic: !input mqtt_topic
triggers:
- trigger: mqtt
topic: "{{mqtt_topic}}"
payload: "new"
value_template: "{{value_json['type']}}"
id: frigate-event
- trigger: event
event_type: mobile_app_notification_action
event_data:
action: "silence-{{ this.entity_id }}"
id: silence
- trigger: event
event_type: mobile_app_notification_action
event_data:
action: "custom-{{ this.entity_id}}"
id: custom
variables:
input_camera: !input camera
input_camera_name: "{{input_camera|expand|map(attribute='attributes.camera_name', default='none')|list}}"
camera: "{{trigger.payload_json['after']['camera'] if trigger.id == 'frigate-event'}}"
camera_name: "{{ camera | replace('_', ' ') | title }}"
input_severity: !input review_severity
severity: "{{trigger.payload_json['after']['severity'] if trigger.id == 'frigate-event'}}"
type: "{{trigger.payload_json['type'] if trigger.id == 'frigate-event'}}"
input_base_url: !input base_url
base_url: "{{ input_base_url.rstrip('/')}}"
input_telegram_base_url: !input telegram_base_url
telegram_base_url: "{{ input_telegram_base_url.rstrip('/') if input_telegram_base_url else base_url }}"
update_sub_label: !input update_sub_label
input_client_id: !input client_id
client_id: "{{input_client_id if not input_client_id else '/' + input_client_id if '/' not in input_client_id else input_client_id }}"
alert_once: !input alert_once
final_update: !input final_update
ios_live_view: !input ios_live_view
android_auto: !input android_auto
notify_group: !input notify_group
notify_group_target: "{{ notify_group | lower | regex_replace('^notify\\.', '') | replace(' ','_') }}"
telegram_chat_id: !input notify_telegram_chat_id
telegram: "{{true if (telegram_chat_id | length > 0) else false}}"
zone_only: !input zone_filter
input_zones: !input zones
zones: "{{ input_zones }}"
zone_multi: !input zone_multi
zone_order_enforced: !input zone_order_enforced
input_labels: !input labels
labels: "{{ input_labels | list | lower }}"
presence_entity: !input presence_filter
disable_times: !input disable_times
cooldown: !input cooldown
timeout: !input timeout
loitering: false
loiter_timer: !input loiter_timer
initial_delay: !input initial_delay
final_delay: !input final_delay
fps: "{{ states('sensor.' + camera + '_camera_fps')|int(5) }}"
state_only: !input state_filter
input_entity: !input state_entity
input_states: !input state_filter_states
states_filter: "{{ input_states | list | lower }}"
custom_filter: !input custom_filter
color: !input color
sound: !input sound
input_volume: !input volume
volume: "{{ (1 * input_volume|int(100))/100 }}"
sticky: !input sticky
tv: !input tv
tv_position: !input tv_position
tv_size: !input tv_size
tv_duration: !input tv_duration
tv_transparency: !input tv_transparency
tv_interrupt: !input tv_interrupt
debug: !input debug
redacted: !input redacted
master_condition: !input master_condition
# Dynamic Variables per event
severity_satisfied: "{{input_severity|select('in', severity)|list|length > 0 if trigger.id == 'frigate-event'}}"
objects: "{{ trigger.payload_json['after']['data']['objects'] if trigger.id == 'frigate-event' }}"
objects_satisfied: "{{ not labels|length or labels|select('in', objects)|list|length > 0 or ('person-verified' in objects and 'person' in labels) }}"
initial_home: "{{ presence_entity != '' and presence_entity|expand|selectattr('state','eq','home')|list|length != 0 }}"
state_satisfied: "{{ not state_only or states(input_entity)|lower in states_filter }}"
before_zones: "{{ trigger.payload_json['before']['data']['zones'] if trigger.id == 'frigate-event' }}"
after_zones: "{{ trigger.payload_json['after']['data']['zones'] if trigger.id == 'frigate-event' }}"
zone_multi_filter: "{{zone_only and zone_multi and after_zones|length and zones and zones |reject('in', after_zones) |list |length == 0 }}"
conditions:
condition: or
conditions:
- condition: trigger
id: silence
- condition: trigger
id: custom
- condition: and
conditions:
- condition: trigger
id: frigate-event
- alias: Camera Match
condition: template
value_template: "{{ input_camera_name|select('equalto', camera)|list|length>0 }}"
- alias: Master Condition
condition: !input master_condition
- alias: Cooldown
condition: template
value_template: "{{ not this.attributes.last_triggered or (now() - this.attributes.last_triggered).seconds > cooldown }}"
- alias: Disable Times
condition: template
value_template: "{{ not disable_times|length or not now().hour in disable_times|map('int')|list }}"
actions:
- choose:
- alias: "Silence New Object Notifications"
conditions:
- condition: trigger
id: silence
sequence:
- action: automation.turn_off
target:
entity_id: "{{ this.entity_id }}"
data:
stop_actions: false
- delay:
minutes: !input silence_timer
- action: automation.turn_on
target:
entity_id: "{{ this.entity_id }}"
- alias: "Custom Action Manual"
conditions:
- condition: trigger
id: custom
sequence: !input "custom_action_manual"
- alias: "GenAI Summary"
conditions:
- condition: trigger
id: "frigate-event"
- condition: template
value_template: "{{ trigger.payload_json['type'] == 'genai' }}"
sequence:
- variables:
event: "{{ trigger.payload_json }}"
metadata: "{{ event['after']['data']['metadata'] | default({}) }}"
review_id: "{{ event['after']['id'] }}"
genai_title: "{{ metadata.title | default('') }}"
genai_shortSummary: "{{ metadata.shortSummary | default('') }}"
genai_threat_level: "{{ metadata.potential_threat_level | int(0) }}"
title: >-
{% if genai_threat_level == 2 %}
Security Concern: {{ genai_title }}
{% elif genai_threat_level == 1 %}
Needs Review: {{ genai_title }}
{% else %}
{{ genai_title }}
{% endif %}
message: "{{ genai_shortSummary }}"
subtitle: !input subtitle
critical_input: !input critical
critical: "{{ true if critical_input == 'true' else true if critical_input == True else false }}"
icon: !input icon
group: !input group
tag: "{{ review_id }}"
channel: !input channel
color: !input color
sticky: !input sticky
sound: !input sound
tap_action: !input tap_action
button_1: !input button_1
button_2: !input button_2
button_3: !input button_3
url_1: !input url_1
url_2: !input url_2
url_3: !input url_3
icon_1: !input icon_1
icon_2: !input icon_2
icon_3: !input icon_3
ios_live_view: !input ios_live_view
android_auto: !input android_auto
tv: !input tv
tv_position: !input tv_position
tv_size: !input tv_size
tv_duration: !input tv_duration
tv_transparency: !input tv_transparency
tv_interrupt: !input tv_interrupt
attachment: !input attachment
video: !input video
- alias: "Send GenAI Notification"
sequence:
- choose:
- conditions: "{{ telegram }}"
sequence:
- action: telegram_bot.send_photo
data:
target: "{{telegram_chat_id}}"
caption: "{{message}}"
url: "{{attachment|replace( base_url, telegram_base_url )}}"
- conditions: "{{ not notify_group_target }}"
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ tag }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- conditions: "{{ tv }}"
sequence:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ tag }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
image:
#only send still images to TV
url: "{{attachment | replace('review_preview.gif','snapshot.jpg') | replace('event_preview.gif','snapshot.jpg')}}"
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
timeout: 30
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
default:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ tag }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
subtitle: "{{subtitle}}"
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
# iOS Specific
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- alias: "Frigate Event"
conditions:
- condition: trigger
id: "frigate-event"
sequence:
- variables:
event: "{{ trigger.payload_json }}"
detections: "{{ event['after']['data']['detections'] }}"
review_id: "{{event['after']['id']}}"
id: "{{ detections[0] }}"
objects: "{{ event['after']['data']['objects'] }}"
sub_labels: "{{ event['after']['data']['sub_labels'] }}"
label: >-
{% if update_sub_label %}
{% set data = namespace(labels=[]) %}
{% if labels|length %}
{% for obj in objects|select('in', labels) %}
{% if "-verified" in obj %}
{% else %}
{% set data.labels = data.labels + [obj] %}
{% endif %}
{% endfor %} {% set data.labels = data.labels + sub_labels %}
{{ data.labels | unique | list | sort | join(", ") | title }}
{% else %}
{% for obj in objects %}
{% if "-verified" in obj %}
{% else %}
{% set data.labels = data.labels + [obj] %}
{% endif %}
{% endfor %} {% set data.labels = data.labels + sub_labels %}
{{ data.labels | unique | list | sort | join(", ") | title }}
{% endif %}
{% else %}
{{ objects | list | join(", ") | title }}
{% endif %}
# Dynamic Variables per event
# after_zones: "{{ event['after']['data']['zones'] }}"
# zone_multi_filter: "{{zone_only and zone_multi and after_zones|length and zones and zones |reject('in', after_zones) |list |length == 0 }}"
# Zones Variables
# If no zones defined, or any zones have been entered
any_zones_entered: "{{ zones | length == 0 or ((zones | select('in', after_zones) | list | length) > 0) }}"
zone_single_satisfied: "{{ any_zones_entered if zone_only else true}}"
# If no zones defined, or all zones have been entered
all_zones_entered: "{{ zones | length == 0 or ((zones | reject('in', after_zones) | list | length) == 0) }}"
zone_multi_satisfied: "{{ all_zones_entered if zone_only and zone_multi else true}}"
# Compare the joined strings for equality is the simplest solution, due to both variables being defined in band.
ordered_zones_match: >
{% set ns = namespace(intersection=[]) %}
{% for item in after_zones %}
{% if item in zones %}
{% set ns.intersection = ns.intersection + [item] %}
{% endif %}
{% endfor %}
{{ ns.intersection == zones }}
# Fails fast if prerequisite of zone or zone_multi is false, as they are pre-requisites
zone_order_satisfied: "{{ (zone_only and zone_multi and ordered_zones_match) if zone_order_enforced else true }}"
zones_satisfied: "{{zone_single_satisfied and zone_multi_satisfied and zone_order_satisfied}}"
# Customisation of text
title: !input title
message: !input message
subtitle: !input subtitle
tap_action: !input tap_action
button_1: !input button_1
button_2: !input button_2
button_3: !input button_3
url_1: !input url_1
url_2: !input url_2
url_3: !input url_3
icon_1: !input icon_1
icon_2: !input icon_2
icon_3: !input icon_3
# other things that can be templated and might need info from the event
critical_input: !input critical
critical: "{{ true if critical_input == 'true' else true if critical_input == True else false }}"
tts: !input tts
tts_helper: !input tts_helper
custom_filter: !input custom_filter
icon: !input icon
group: !input group
channel: !input channel
attachment: !input attachment
video: !input video
custom_action_auto: !input custom_action_auto
- alias: "Debug: Initial Output"
choose:
- conditions:
- "{{debug}}"
sequence:
- action: logbook.log
data_template:
name: Frigate Notification
message: |
DEBUG:
Info:
FPS: {{fps}},
Frigate Review id: {{review_id}}{{', Frigate client ID: ' + client_id if client_id else ''}},
Frigate Detections: {{detections}}
Objects: {{label}},
Sub Labels: {{sub_labels | join(', ')}}
Config:
Camera(formatted): {{camera}}({{camera_name}}),
Base URL: {{'REDACTED' if base_url and redacted else base_url}},
Critical: {{critical}},
TTS: {{tts}}
TTS Helper: {{tts_helper if tts else 'N/A'}},
Alert Once: {{alert_once}},
Final_Update (delay): {{final_update}} ({{final_delay}})s,
Attachment: {{attachment if not redacted or not base_url else attachment |replace(base_url, 'REDACTED')}}
Video: {{video if not redacted or not base_url else video |replace(base_url, 'REDACTED')}},
iOS URL: {{(video if not redacted or not base_url else video |replace(base_url, 'REDACTED')) if video|length >0 else attachment if not redacted or not base_url else attachment |replace(base_url, 'REDACTED')}}
Target: {{'group (input/formatted): ' + notify_group + '/' + notify_group_target + ', ' if notify_group else 'Mobile Device'}},
Cooldown: {{cooldown}}s,
Initial Delay: {{initial_delay}}s,
Color: {{color}},
Sound: {{sound}},
Volume: {{ iif(sound == 'none', 0, volume) }}
IOS Live entity: "{{ios_live_view}}"
Android Auto: {{android_auto}},
Tag: {{ id }},
Group: {{group}},
Channel: {{channel}} {{' - overridden by alarm_stream' if critical}},
Sticky: {{sticky}},
Title: {{title}},
Message: {{message}},
Subtitle: {{subtitle}},
Tap Action: {{tap_action if not redacted or not base_url else tap_action |replace(base_url, 'REDACTED')}},
Action Button 1 Text/URL/Icon: {{iif(button_1, button_1, 'unset')}} ({{url_1 if not redacted or not base_url else url_1 |replace(base_url, 'REDACTED')}}) {{icon_1}},
Action Button 2 Text/URL/Icon: {{iif(button_2, button_2, 'unset')}} ({{url_2 if not redacted or not base_url else url_2 |replace(base_url, 'REDACTED')}}) {{icon_2}},
Action Button 3 Text/URL/Icon: {{iif(button_3, button_3, 'unset')}} ({{url_3 if not redacted or not base_url else url_3 |replace(base_url, 'REDACTED')}}) {{icon_3}},
Icon: {{icon}}
TV: {{ tv }},
Position: {{tv_position}},
Size: {{tv_size}},
Duration: {{tv_duration}},
Transparency: {{tv_transparency}},
TV Interrupt: {{tv_interrupt}},
Telegram: {{telegram}},
Telegram_chat_id: {{telegram_chat_id}},
Filters:
Severity:
Required Severity: {{input_severity}},
TEST: {{'PASS' if severity_satisfied else 'FAIL'}} - {{severity}}
Zones:
Zone Filter toggle on: {{zone_only}},
Multi-Zone toggle on: {{zone_multi}},
Required zones: {{input_zones}},
Zone Order toggle on: {{zone_order_enforced}}
Entered Zones: {{after_zones}},
TEST: {{'PASS' if zones_satisfied else 'FAIL' }} {{'(Multi)' if zone_only and zone_multi}} {{'(Order-Enforced)' if zone_only and zone_multi and zone_order_enforced}},
Required objects:
Input: {{input_labels}},
TEST: {{'PASS' if objects_satisfied else 'FAIL'}} - {{objects}}
presence entity (not home):
Entity: {{presence_entity}}
TEST: {{'PASS' if not initial_home else 'FAIL'}},
disabled times: {{disable_times}},
State Filter:
State Filter toggle on: {{state_only}},
State Filter Entity: {{input_entity}},
Required States: {{input_states}},
TEST: {{'PASS' if state_satisfied else 'FAIL' }},
Custom Filter: {{custom_filter}}
- choose:
- conditions:
- alias: Severity Filter
condition: template
value_template: "{{ severity_satisfied }}"
- alias: "Object Filter"
condition: template
value_template: "{{ objects_satisfied }}"
- alias: Zone Filter
condition: template
value_template: "{{ zones_satisfied }}"
- alias: Presence Filter
condition: template
value_template: "{{ not initial_home }}"
- alias: State Filter
condition: template
value_template: "{{ state_satisfied }}"
- alias: Custom Filter
condition: template
value_template: "{{ custom_filter }}"
sequence:
- alias: "Delay for image"
choose:
- conditions: "{{initial_delay > 0}}"
sequence:
- delay:
seconds: "{{initial_delay}}"
- alias: "Custom Action Auto"
choose:
- conditions: "{{ custom_action_auto |length > 0 }}"
sequence: !input "custom_action_auto"
- alias: "Send Notification"
sequence:
- choose:
- conditions: "{{ telegram }}"
sequence:
- action: telegram_bot.send_photo
data:
target: "{{telegram_chat_id}}"
caption: "{{message}}"
url: "{{attachment|replace( base_url, telegram_base_url )}}"
- conditions: "{{ not notify_group_target }}"
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- conditions: "{{ tv }}"
sequence:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
image:
#only send still images to TV
url: "{{attachment | replace('review_preview.gif','snapshot.jpg') | replace('event_preview.gif','snapshot.jpg')}}"
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
timeout: 30
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
default:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
subtitle: "{{subtitle}}"
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
# iOS Specific
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{sound}}"
volume: "{{ iif(sound == 'none', 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- if: "{{tts and (not tts_helper or not alert_once or (tts_helper and id not in states(tts_helper)))}}"
then:
sequence:
- choose:
- conditions: "{{ not notify_group_target }}"
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
message: "{{'TTS'}}"
data:
tag: "{{ id }}{{'-tts'}}"
channel: "{{'alarm_stream' if critical else channel}}"
alert_once: "{{ alert_once }}"
tts_text: "{{message}}"
default:
- action: "notify.{{ notify_group_target }}"
data:
message: "{{'TTS'}}"
data:
tag: "{{ id }}{{'-tts'}}"
channel: "{{'alarm_stream' if critical else channel}}"
alert_once: "{{ alert_once }}"
tts_text: "{{message}}"
- action: input_text.set_value
data:
value: |
{% set newIds = id + '|' + states(tts_helper) %}
{{ newIds[:250] }}
target:
entity_id: "{{tts_helper}}"
########################################################
#################### LOOP for updates ##################
########################################################
- repeat:
sequence:
- wait_for_trigger:
- trigger: mqtt
topic: "{{mqtt_topic}}"
payload: "{{ review_id }}"
value_template: "{{ value_json['after']['id'] }}"
timeout:
minutes: "{{timeout}}"
continue_on_timeout: false
- variables:
# use the subsequent attchment variable if set
attachment_2: !input attachment_2
attachment: "{{iif(attachment_2, attachment_2, attachment)}}"
event: "{{ wait.trigger.payload_json }}"
type: "{{event['type']}}"
initial_severity: "{{severity}}"
old_objects: "{{objects}}"
last_zones: "{{after_zones}}"
# Sometimes mqtt messages are missed so we keep the previous iteration severity, objects and zones to compare against the new ones,
# rather than comparing the new before and after which may match despite a change between mqtt messages
severity: "{{event['after']['severity']}}"
severity_updated: "{{initial_severity != severity}}"
severity_satisfied: "{{((input_severity | select('equalto', severity) | list | length) > 0) }}"
objects: "{{ event['after']['data']['objects'] }}"
objects_satisfied: "{{ not labels|length or labels|select('in', objects)|list|length > 0 or ('person-verified' in objects and 'person' in labels) }}"
#loitering: "{{ loiter_timer and event['before']['motionless_count']/fps/60 < loiter_timer and event['after']['motionless_count']/fps/60 >= loiter_timer }}"
home: "{{presence_entity |reject('in', '') |select('is_state', 'home') |list |length != 0 }}"
#new_snapshot: "{{ update_thumbnail and event['before']['snapshot']['frame_time'] != event['after']['snapshot']['frame_time'] }}"
presence_changed: "{{ presence_entity |reject('in', '') |expand |map(attribute='last_changed') |list |select('gt', as_datetime(event['before']['start_time'])) |list |length != 0 }}"
# Zones Variables
before_zones: "{{ event['before']['data']['zones'] | default([]) }}"
after_zones: "{{ event['after']['data']['zones'] | default([]) }}"
# If no zones defined, or any zones have been entered
any_zones_entered: "{{ zones | length == 0 or ((zones | select('in', after_zones) | list | length) > 0) }}"
zone_single_satisfied: "{{ any_zones_entered if zone_only else true}}"
# If no zones defined, or all zones have been entered
all_zones_entered: "{{ zones | length == 0 or ((zones | reject('in', after_zones) | list | length) == 0) }}"
zone_multi_satisfied: "{{ all_zones_entered if zone_only and zone_multi else true}}"
# Compare the joined strings for equality is the simplest solution, due to both variables being defined in band.
ordered_zones_match: >
{% set ns = namespace(intersection=[]) %}
{% for item in after_zones %}
{% if item in zones %}
{% set ns.intersection = ns.intersection + [item] %}
{% endif %}
{% endfor %}
{{ ns.intersection == zones }}
# Fails fast if prerequisite of zone or zone_multi is false, as they are pre-requisites
zone_order_satisfied: "{{ (zone_only and zone_multi and ordered_zones_match) if zone_order_enforced else true }}"
zones_satisfied: "{{zone_single_satisfied and zone_multi_satisfied and zone_order_satisfied}}"
new_entered_zones: "{{ zones | select('in', after_zones) | list }}"
last_entered_zones: "{{ zones | select('in', last_zones) | list }}"
entered_new_zones: "{{ not zone_only and after_zones | length > last_zones | length }}"
entered_new_filter_zones: "{{ zone_only and zones | length > 0 and (new_entered_zones | length > last_entered_zones | length) }}"
# stationary_moved: "{{ event['after']['position_changes'] > event['before']['position_changes'] }}"
state_satisfied: "{{ not state_only or states(input_entity)|lower in states_filter }}"
before_sub_labels: "{{ sub_labels }}"
sub_labels: "{{ event['after']['data']['sub_labels'] | default([]) }}"
# assess the sub labels from the mqtt message.
# If the user has configured specific objects we eliminate objects not in that list
# Otherwise we loop through all objects and replace any verified objects with the sub label
label: >-
{% if update_sub_label %}
{% set data = namespace(labels=[]) %}
{% if labels|length %}
{% for obj in objects|select('in', labels) %}
{% if "-verified" in obj %}
{% else %}
{% set data.labels = data.labels + [obj] %}
{% endif %}
{% endfor %} {% set data.labels = data.labels + sub_labels %}
{{ data.labels | unique | list | sort | join(", ") | title }}
{% else %}
{% for obj in objects %}
{% if "-verified" in obj %}
{% else %}
{% set data.labels = data.labels + [obj] %}
{% endif %}
{% endfor %} {% set data.labels = data.labels + sub_labels %}
{{ data.labels | unique | list | sort | join(", ") | title }}
{% endif %}
{% else %}
{{objects | list | join(', ') | title}}
{% endif %}
# import the title and message again so any sublabel, object or zone changes are captured.
title: !input title
message: !input message
subtitle: !input subtitle
critical_input: !input critical
critical: "{{ true if critical_input == 'true' else true if critical_input == True else false }}"
tts: !input tts
sub_label_updated: "{{ update_sub_label and sub_labels != before_sub_labels }}"
# If we are filtering based on zones, and zone conditions are met and there is a new zone added.
zone_updated: "{{ (entered_new_filter_zones and zones_satisfied) or entered_new_zones }}"
object_updated: "{{ old_objects | select('in', labels) | list | length != objects | select('in', labels) | list | length }}"
# update is used to determine if we should play a sound. if it returns true, we should be silent.
silent_update: "{{ alert_once or (not presence_changed and not zone_updated and not object_updated and not sub_label_updated) }}"
custom_action_auto_multi: !input custom_action_auto_multi
- alias: "Debug: Loop Output"
choose:
- conditions:
- "{{debug}}"
sequence:
- action: logbook.log
data_template:
name: Frigate Notification
message: |
DEBUG (in loop):
Send Notification: {{custom_filter and not home and zones_satisfied and state_satisfied and objects_satisfied and severity_satisfied and ((final_update and type == 'end') or (severity_updated or presence_changed or zone_updated or object_updated or sub_label_updated)) }}
Info:
Sub Label: {{sub_labels}},
Image: "{{attachment if not redacted or not base_url else attachment |replace(base_url, 'REDACTED')}}"
Title: {{title}}
Message: {{message}}
iOS sound: {{'Critical' if critical else 'disabled by alert once' if alert_once else 'Silent' if silent_update else sound}},
Android Sound: {{'disabled by alert once' if alert_once else 'enabled'}},
iOS url: "{{(video if not redacted or not base_url else video |replace(base_url, 'REDACTED')) if video else attachment if not redacted or not base_url else attachment |replace(base_url, 'REDACTED')}}",
Video: "{{video if not redacted or not base_url else video |replace(base_url, 'REDACTED')}}",
Critical: {{critical}},
TTS: {{tts}}
Triggers:
New Snapshot: False (not functional with reviews)
Severity Changed: {{severity_updated}},
Presence Changed: {{presence_changed}},
Object changed: {{object_updated}},
Zones Changed: {{'True' if entered_new_filter_zones and zones_satisfied else 'True - zone filter disabled' if entered_new_zones and not zone_only else 'True - Filter Criteria not met' if entered_new_zones else 'False'}},
Sublabel changed: {{ sub_label_updated }}{{' - Disabled' if not update_sub_label}},
Final Update: {{final_update and type == 'end'}}
Conditions:
Filters:
Severity:
Required Severity: {{input_severity}}
TEST: {{'PASS' if severity_satisfied else 'FAIL'}} - {{severity}},
Zones:
Zone Filter toggle on: {{zone_only}},
Multi-Zone toggle on: {{zone_multi}},
Required zones: {{input_zones}},
Zone Order toggle on: {{zone_order_enforced}}
Last Zones: {{ last_zones | list | length }} - {{last_zones}},
New Zones: {{ after_zones | list | length }} - {{after_zones}},
TEST: {{'PASS' if zones_satisfied else 'FAIL' }} {{'(Multi)' if zone_only and zone_multi}} {{'(Order-Enforced)' if zone_only and zone_multi and zone_order_enforced}},
Object Filter:
Input: {{input_labels}},
TEST: {{'PASS' if objects_satisfied else 'FAIL'}} - {{objects}},
Presence entity (not home):
Entity: {{presence_entity}},
TEST: {{'PASS' if not home else 'FAIL'}},
Time Filter:
Disabled times: {{disable_times}},
TEST: {{'PASS' if now().hour not in disable_times else 'FAIL'}}
State Filter:
State Filter toggle on: {{state_only}},
State Filter Entity: {{input_entity}},
Required States: {{input_states}},
TEST: {{'PASS' if state_satisfied else 'FAIL'}},
Custom Filter: {{'PASS' if custom_filter else 'FAIL'}},
- choose:
- conditions:
- and:
- alias: Severity Filter
condition: template
value_template: "{{severity_satisfied}}"
- alias: Object Filter
condition: template
value_template: "{{ objects_satisfied }}"
- alias: Zone Filter
condition: template
value_template: "{{zones_satisfied}}"
- alias: State Filter
condition: template
value_template: "{{state_satisfied}}"
- alias: Custom Filter
condition: template
value_template: "{{custom_filter}}"
- alias: Presence Filter
condition: template
value_template: "{{not home}}"
- or:
- alias: Presence Changed
condition: template
value_template: "{{presence_changed}}"
- alias: Zone Changed
condition: template
value_template: "{{zone_updated}}"
- alias: Sub Label Changed
condition: template
value_template: "{{sub_label_updated}}"
- alias: Object Changed
condition: template
value_template: "{{object_updated}}"
- alias: Severity Changed
condition: template
value_template: "{{severity_updated}}"
- and:
- alias: Final Update
condition: template
value_template: "{{final_update}}"
- alias: End of Event
condition: template
value_template: "{{event['type'] == 'end'}}"
sequence:
- alias: "Custom Action Auto Multi"
choose:
- conditions:
- "{{ custom_action_auto_multi | length > 0 }}"
sequence: !input "custom_action_auto_multi"
- alias: "Delay for Final Update"
choose:
- conditions:
- "{{ event['type'] == 'end' }}"
- "{{ final_update }}"
sequence:
- delay:
seconds: "{{final_delay}}"
- alias: "Update Notification"
sequence:
- choose:
- conditions: "{{ telegram }}"
sequence:
- choose:
- conditions:
- "{{ event['type'] == 'end' }}"
- "{{ video | length > 0 }}"
sequence:
- action: telegram_bot.send_video
data:
target: "{{telegram_chat_id}}"
caption: "{{message}}"
url: "{{ video | replace(base_url, telegram_base_url) }}"
inline_keyboard:
- [
[
"{{button_1}}",
"{{url_1}}",
],
[
"{{button_2}}",
"{{url_2}}",
],
]
- [
[
"{{button_3}}",
"{{url_3}}",
],
]
default:
- action: telegram_bot.send_photo
data:
target: "{{telegram_chat_id}}"
caption: "{{message}}"
url: "{{attachment | replace(base_url,telegram_base_url)}}"
inline_keyboard:
- [
["{{button_1}}", "{{url_1}}"],
["{{button_2}}", "{{url_2}}"],
]
- [["{{button_3}}", "{{url_3}}"]]
- conditions: "{{ not notify_group_target }}"
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
alert_once: "{{ alert_once }}"
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{ iif(silent_update, 'none', sound) }}"
volume: "{{ iif((silent_update or sound == 'none'), 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- conditions: "{{ tv }}"
sequence:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
alert_once: "{{ alert_once }}"
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
image:
url: "{{base_url}}/api/frigate{{client_id}}/notifications/{{id}}/snapshot.jpg"
video: "{{video}}"
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
timeout: 30
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{ iif(silent_update, 'none', sound) }}"
volume: "{{ iif((silent_update or sound == 'none'), 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
default:
- action: "notify.{{ notify_group_target }}"
data:
title: "{{title}}"
message: "{{message}}"
data:
tag: "{{ id }}"
group: "{{ group }}"
color: "{{color}}"
# Android Specific
subject: "{{subtitle}}"
image: "{{attachment}}"
video: "{{video}}"
clickAction: "{{tap_action}}"
ttl: 0
priority: high
alert_once: "{{ alert_once }}"
notification_icon: "{{icon}}"
sticky: "{{sticky}}"
channel: "{{'alarm_stream' if critical else channel}}"
car_ui: "{{android_auto}}"
# Android/Fire TV
fontsize: "{{tv_size}}"
position: "{{tv_position}}"
duration: "{{tv_duration}}"
transparency: "{{tv_transparency}}"
interrupt: "{{tv_interrupt}}"
# iOS Specific
subtitle: "{{subtitle}}"
url: "{{tap_action}}"
attachment:
url: "{{video if video else attachment }}"
content-type: "{{ 'application/vnd.apple.mpegurl' if 'm3u8' in video else 'mp4' if 'mp4' in video else 'jpeg' if 'jpg' in attachment else 'gif' }}"
push:
sound:
name: "{{ iif(silent_update, 'none', sound) }}"
volume: "{{ iif((silent_update or sound == 'none'), 0, volume) }}"
critical: "{{ iif(critical, 1, 0) }}"
entity_id: "{{ios_live_view}}"
# Actions
actions:
- action: URI
title: "{{button_1}}"
uri: "{{url_1}}"
icon: "{{icon_1}}"
- action: URI
title: "{{button_2}}"
uri: "{{url_2}}"
icon: "{{icon_2}}"
- action: "{{ 'URI' if '/' in url_3 else url_3 }}"
title: "{{button_3}}"
uri: "{{url_3}}"
icon: "{{icon_3}}"
destructive: true
- if: "{{tts and (not tts_helper or not alert_once or (tts_helper and id not in states(tts_helper)))}}"
then:
sequence:
- choose:
- conditions: "{{ not notify_group_target }}"
sequence:
- device_id: !input notify_device
domain: mobile_app
type: notify
message: "{{'TTS'}}"
data:
tag: "{{ id }}{{'-loitering-tts' if loitering}}"
channel: "{{'alarm_stream' if critical else channel}}"
alert_once: "{{ alert_once }}"
tts_text: "{{message}}"
default:
- action: "notify.{{ notify_group_target }}"
data:
message: "{{'TTS'}}"
data:
tag: "{{ id }}{{'-loitering-tts' if loitering}}"
channel: "{{'alarm_stream' if critical else channel}}"
alert_once: "{{ alert_once }}"
tts_text: "{{message}}"
- action: input_text.set_value
data:
value: |
{% set newIds = id + '|' + states(tts_helper) %}
{{ newIds[:250] }}
target:
entity_id: "{{tts_helper}}"
until: "{{ not wait.trigger or wait.trigger.payload_json['type'] == 'end' }}"