Skip to content

Instantly share code, notes, and snippets.

@yougotborked
Last active September 20, 2025 03:48
Show Gist options
  • Select an option

  • Save yougotborked/2a61daa1015615dca4357794dc6355bf to your computer and use it in GitHub Desktop.

Select an option

Save yougotborked/2a61daa1015615dca4357794dc6355bf to your computer and use it in GitHub Desktop.
blueprint:
name: Universal Actionable Notification (fixed, entity selectors)
description: >
Send highly customizable notifications to iOS and Android devices, supporting
critical notifications, dynamic actions, and optional camera snapshot/stream.
domain: automation
input:
iphone_services:
name: iPhone Notification Services
description: Select one or more iPhone notify services.
selector:
entity:
domain: notify
multiple: true
android_services:
name: Android Notification Services
description: Select one or more Android notify services.
selector:
entity:
domain: notify
multiple: true
trigger_entity:
name: Trigger Entity
description: Entity to monitor for state changes.
selector:
entity:
notification_title:
name: Notification Title
description: Title of the notification.
default: "Alert"
selector:
text:
notification_message:
name: Notification Message
description: Body of the notification.
default: "Action required."
selector:
text:
critical_ios:
name: Enable Critical Notifications (iOS)
description: Enable iOS critical notifications (uses `push.critical` and `interruption-level`).
default: false
selector:
boolean:
critical_android:
name: Enable Critical Notifications (Android)
description: Enable Android high-priority + alarm stream.
default: false
selector:
boolean:
tts_message:
name: TTS Message (Android, Optional)
description: Text-to-Speech message for Android critical notifications.
default: ""
selector:
text:
tts_volume_max:
name: TTS at Max Volume (Android)
description: Enable Text-To-Speech at maximum volume.
default: false
selector:
boolean:
dynamic_camera:
name: Include Camera (Optional)
description: Attach camera snapshot / stream in the notification (device support varies).
default: false
selector:
boolean:
camera_entity:
name: Camera Entity
description: The camera to attach (snapshot/stream depending on platform/app support).
selector:
entity:
domain: camera
action_1_title:
name: First Action Name (Optional)
default: ""
selector:
text:
first_action:
name: First Action Sequence (Optional)
default: []
selector:
action:
action_2_title:
name: Second Action Name (Optional)
default: ""
selector:
text:
second_action:
name: Second Action Sequence (Optional)
default: []
selector:
action:
action_3_title:
name: Third Action Name (Optional)
default: ""
selector:
text:
third_action:
name: Third Action Sequence (Optional)
default: []
selector:
action:
action_4_title:
name: Fourth Action Name (Optional)
default: ""
selector:
text:
fourth_action:
name: Fourth Action Sequence (Optional)
default: []
selector:
action:
mode: restart
triggers:
- id: send
trigger: state
entity_id: !input trigger_entity
- id: action
trigger: event
event_type: mobile_app_notification_action
variables:
title: !input notification_title
body: !input notification_message
is_critical_ios: !input critical_ios
is_critical_android: !input critical_android
tts_text: !input tts_message
tts_max: !input tts_volume_max
include_camera: !input dynamic_camera
cam: !input camera_entity
a1_title: !input action_1_title
a2_title: !input action_2_title
a3_title: !input action_3_title
a4_title: !input action_4_title
iphone_services_list: !input iphone_services
android_services_list: !input android_services
actions_list: >-
{% set acts = [] %}
{% if a1_title %}{% set acts = acts + [{ 'action': 'ACTION_1', 'title': a1_title }] %}{% endif %}
{% if a2_title %}{% set acts = acts + [{ 'action': 'ACTION_2', 'title': a2_title }] %}{% endif %}
{% if a3_title %}{% set acts = acts + [{ 'action': 'ACTION_3', 'title': a3_title }] %}{% endif %}
{% if a4_title %}{% set acts = acts + [{ 'action': 'ACTION_4', 'title': a4_title }] %}{% endif %}
{{ acts }}
ios_data: >-
{% set push = {'interruption-level': ('critical' if is_critical_ios else 'active')} %}
{% if is_critical_ios %}
{% set push = dict(push, **{'critical': 1}) %}
{% endif %}
{% set d = {
'push': push,
'hide-thumbnail': false,
'content-type': 'image/jpeg',
'actions': actions_list
} %}
{% if include_camera and cam %}
{% set d = dict(d, **{'entity_id': cam}) %}
{% endif %}
{{ d }}
android_data: >-
{% set ch = 'alarm_stream_max' if tts_max else 'alarm_stream' %}
{% set d = {
'ttl': 0,
'priority': ('high' if is_critical_android else 'normal'),
'channel': ch,
'media_stream': 'alarm_stream',
'tts_text': (tts_text or ''),
'hide-thumbnail': false,
'content-type': 'image/jpeg',
'actions': actions_list
} %}
{% if include_camera and cam %}
{% set d = dict(d, **{'entity_id': cam}) %}
{% endif %}
{{ d }}
tapped_action: >-
{{ (trigger.event.data.action if trigger.id == 'action' else '') | default('', true) }}
condition: []
action:
- choose:
- conditions: "{{ trigger.id == 'action' and tapped_action != '' }}"
sequence:
- choose:
- conditions: "{{ tapped_action == 'ACTION_1' }}"
sequence: !input first_action
- conditions: "{{ tapped_action == 'ACTION_2' }}"
sequence: !input second_action
- conditions: "{{ tapped_action == 'ACTION_3' }}"
sequence: !input third_action
- conditions: "{{ tapped_action == 'ACTION_4' }}"
sequence: !input fourth_action
- conditions: "{{ trigger.id == 'send' }}"
sequence:
- choose:
- conditions: "{{ iphone_services_list | length > 0 }}"
sequence:
- repeat:
for_each: "{{ iphone_services_list }}"
sequence:
- service: "{{ repeat.item }}"
data:
title: "{{ title }}"
message: "{{ body }}"
data: "{{ ios_data }}"
- choose:
- conditions: "{{ android_services_list | length > 0 }}"
sequence:
- repeat:
for_each: "{{ android_services_list }}"
sequence:
- service: "{{ repeat.item }}"
data:
title: "{{ title }}"
message: >-
{{ tts_text if (is_critical_android and tts_text) else body }}
data: "{{ android_data }}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment