Skip to content

Instantly share code, notes, and snippets.

@fmartingr
Last active November 8, 2025 16:26
Show Gist options
  • Select an option

  • Save fmartingr/da951662beef27d08efd15abe02bc708 to your computer and use it in GitHub Desktop.

Select an option

Save fmartingr/da951662beef27d08efd15abe02bc708 to your computer and use it in GitHub Desktop.
blueprint:
name: "Repeating Alert Notifications"
description: >
Repeating Alert Notifications ideal for doors, windows, locks, etc
🚀 Version 2025.11.08.1
Author : fmartingr
Forked from: https://gist.github.com/Ltek/0d88ed720f635a7579849aac93b9ff93
📖 Features:
- Repeat notifications with several user confurible options : max count limit, time delay between alerts, etc
- Bypass entities to suppress notifications
- Customizable alerts with rich notifications
- Automatic notification clearing
📝 Changes:
- 2025.11.08.1:
- Changed: Selector for notification targets instead of a comma separated text input
- Fixed: Trimmed APNS notification tag to 64 characters
- Fixed: Multiple trigger entity handling
domain: automation
source_url: https://fmartingr.com/homeassistant/blueprints/persistent-notifications.yaml
input:
trigger_entity:
name: "\U0001F916 Triggering Entity"
description: "binary_sensor or input_boolean"
selector:
entity:
domain:
- binary_sensor
- input_boolean
multiple: true
issue_state:
name: "⚠️ Triggering State"
description: "Entity state to trigger the Notification"
default: "on"
selector:
select:
options:
- "on"
- "off"
duration_issue_state:
name: "⏰ Time Delay before Alert is Sent"
description: "How long (in minutes) entity must be in the triggering state before alerting starts. This delay can be used to only notify about sustained issues (e.g., a door left open for 10 minutes)."
default:
minutes: 0
selector:
duration:
enable_day: true
condition_send_notification:
name: "\U0001F50D Secondary Trigger Condition"
description: "Condition is checked after the Triggering Entity. Useful for custom triggers."
default: []
selector:
condition: {}
delete_notification:
name: "\U0001F5D1️ Clear Alert when Trigger ends?"
description: "Delete notification when state changes from issue state"
default: true
selector:
boolean: {}
duration_from_issue_state:
name: "⏳ Time Delay before Clearing Alert"
description: "How long the entity must be out of the trigger state before clearing / deleting the notification. This delay can add stability when the issue resolves (e.g., ignoring momentary door closings before clearing the alert)."
default:
seconds: 0
selector:
duration:
enable_day: true
friendly_name:
name: "\U0001F3F7️ Custom Friendly Name"
description: "Override the Friendly Name variable (Fridge, Snickerdoodle, etc)"
default: ""
selector:
text: {}
notify_services:
name: "📱 Notify devices (mobile_app)"
description: "Choose one or more phones/tablets that use the Companion App."
selector:
device:
filter:
- integration: mobile_app
multiple: true
notification_click_url:
name: "\U0001F517 Open URL with tap"
description: "URL opens when tapping the notification."
default: "/dashboard-ROOM"
selector:
text: {}
notification_title:
name: "\U0001F4E2 Notification Title"
description: "Allows variables. Default is {{ friendly_name }}"
default: "{{ friendly_name }} is Open"
selector:
text: {}
notification_message:
name: "\U0001F4EC Notification Message"
description: 'Allows variables like... {{ as_timestamp(states[trigger.entity_id].last_updated) | timestamp_custom("%I:%M:%S %p") }}'
default: 'Last Change was {{ as_timestamp(states[trigger.entity_id].last_updated) | timestamp_custom("%I:%M:%S %p") }}'
selector:
text: {}
repeat_notification:
name: "\U0001F501 Repeat Alerts"
description: "Repeat at specific time intervals"
default: false
selector:
boolean: {}
max_repeat_count:
name: "🕔 Maximum Repeat Count"
description: "Maximum number repeats for a single notification (0 for unlimited)"
default: 0
selector:
number:
min: 0
max: 100
step: 1
mode: box
time_between_repeat_notification:
name: "⏳ Delay Between Repeats"
description: "Time between repeat notifications."
default:
minutes: 1
selector:
duration:
enable_day: true
bypass_entities:
name: "🛡️ Bypass Entities"
description: 'When these Entities are "On" notifications will not be sent'
default: []
selector:
entity:
domain:
- binary_sensor
- input_boolean
multiple: true
notification_icon_warning:
name: "\U0001F6A8 Notification Icon (Android Only)"
description: "The icon that is shown when the issue is reported."
default: "alert"
selector:
select:
options:
- alert
- alert-circle
- door
- door-open
- motion-sensor
- fridge
- fridge-alert
- home
- home-alert
- home-assistant
- window-closed
- window-open
- window-open-variant
notification_color:
name: "\U0001F308 Notification Color (Android Only)"
description: "The color of the notification."
default: "red"
selector:
select:
options:
- red
- orange
- yellow
- green
- blue
- purple
notification_persistent:
name: "\U0001F4CC Persistent Notification (Android Only)"
description: "Notification cannot be closed manually, stays until the trigger clears. Can be used with Repeats Alerts."
default: false
selector:
boolean: {}
notification_interruption_level:
name: "\U0001F514 Interruption Level (iOS Only)"
description: "Intrusiveness of the notification received."
default: "active"
selector:
select:
options:
- passive
- active
- time-sensitive
- critical
custom_action_issue_state:
name: "⚙️ Custom Action - Entering Trigger State"
description: "Custom actions when trigger occurs"
default: []
selector:
action: {}
custom_action_from_issue_state:
name: "⚙️ Custom Action - Exiting Trigger State"
description: "Custom actions when trigger ends"
default: []
selector:
action: {}
mode: restart
max_exceeded: silent
variables:
custom_friendly_name: !input friendly_name
trigger_entity: !input trigger_entity
issue_state: !input issue_state
duration_issue_state: !input duration_issue_state
condition_send_notification: !input condition_send_notification
delete_notification: !input delete_notification
duration_from_issue_state: !input duration_from_issue_state
notify_services: !input notify_services
notification_click_url: !input notification_click_url
notification_title: !input notification_title
notification_message: !input notification_message
repeat_notification: !input repeat_notification
max_repeat_count: !input max_repeat_count
time_between_repeat_notification: !input time_between_repeat_notification
bypass_entities: !input bypass_entities
notification_icon_warning: !input notification_icon_warning
notification_color: !input notification_color
notification_persistent: !input notification_persistent
notification_interruption_level: !input notification_interruption_level
custom_action_issue_state: !input custom_action_issue_state
custom_action_from_issue_state: !input custom_action_from_issue_state
repeat_count: 0
trigger:
- platform: state
entity_id: !input trigger_entity
to: !input issue_state
for: !input duration_issue_state
id: send_notification
- platform: state
entity_id: !input trigger_entity
from: !input issue_state
for: !input duration_from_issue_state
id: delete_notification
action:
- variables:
# Derive notify.mobile_app_<slugified_device_name> for each selected device
notify_services: !input notify_services
notify_services_list: >
{% set ns = namespace(services=[]) %}
{% for dev in notify_services %}
{% set name = device_attr(dev, 'name') | slugify %}
{% set ns.services = ns.services + ['notify.mobile_app_' ~ name] %}
{% endfor %}
{{ ns.services }}
number_of_notify_services: "{{ notify_services_list | count }}"
base_tag: "{{ trigger.entity_id ~ ':' ~ issue_state }}"
# ASCII-only + <=64 bytes for APNs collapse-id
notification_tag: >
{{ base_tag | slugify | replace('-', '') | truncate(64, True, '') }}
initially_triggered_at: "{{ now() }}"
- choose:
- conditions:
- condition: trigger
id: send_notification
- condition: template
value_template: >-
{% if bypass_entities | length > 0 %}
{{ not (bypass_entities | selectattr('state', 'eq', 'on') | list | count > 0) }}
{% else %}
true
{% endif %}
- condition: !input condition_send_notification
sequence:
- variables:
friendly_name: >-
{% if custom_friendly_name != '' %}
{{ custom_friendly_name }}
{% else %}
{% set expanded_trigger_entity_list = expand(trigger_entity)
| selectattr('state', 'eq', issue_state)
| map(attribute='name') | join(', ') %}
{{ ' & '.join(expanded_trigger_entity_list.rsplit(', ', 1)) }}
{% endif %}
- parallel:
- repeat:
count: "{{ number_of_notify_services }}"
sequence:
- service: "{{ notify_services_list[repeat.index-1] }}"
data:
message: !input notification_message
title: !input notification_title
data:
clickAction: !input notification_click_url
url: !input notification_click_url
tag: "{{ notification_tag }}"
color: !input notification_color
notification_icon: mdi:{{ notification_icon_warning }}
push:
interruption-level: !input notification_interruption_level
persistent: !input notification_persistent
sticky: !input notification_persistent
- choose: []
default: !input custom_action_issue_state
- if:
- condition: template
value_template: "{{ repeat_notification and (max_repeat_count == 0 or repeat_count < max_repeat_count) and is_state(trigger.entity_id, issue_state) }}"
then:
- variables:
repeat_count: "{{ repeat_count + 1 }}"
- delay: !input time_between_repeat_notification
- repeat:
while:
- "{{ repeat_notification and (max_repeat_count == 0 or repeat_count < max_repeat_count) and is_state(trigger.entity_id, issue_state) }}"
sequence:
- parallel:
- repeat:
count: "{{ number_of_notify_services }}"
sequence:
- service: "{{ notify_services_list[repeat.index-1] }}"
data:
message: !input notification_message
title: !input notification_title
data:
clickAction: !input notification_click_url
url: !input notification_click_url
tag: "{{ notification_tag }}"
color: !input notification_color
notification_icon: mdi:{{ notification_icon_warning }}
push:
interruption-level: !input notification_interruption_level
persistent: !input notification_persistent
sticky: !input notification_persistent
- choose: []
default: !input custom_action_issue_state
- variables:
repeat_count: "{{ repeat_count + 1 }}"
- delay: !input time_between_repeat_notification
- conditions:
- condition: trigger
id: delete_notification
- condition: template
value_template: "{{ delete_notification }}"
sequence:
- variables:
repeat_count: 0
- parallel:
- repeat:
count: "{{ number_of_notify_services }}"
sequence:
- service: "{{ notify_services_list[repeat.index-1] }}"
data:
message: clear_notification
data:
tag: "{{ notification_tag }}"
- choose: []
default: !input custom_action_from_issue_state
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment