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' }}"