Files
haos-blueprints/Common/Alarm Notification/blueprint.yaml
T
alexei.dolgolyov 3e98e14882 Add debug mode and harden notify flow in Alarm Notification
- Add `debug_enabled` input that surfaces per-stage persistent
  notifications ([1/4] trigger, [1.5/4] conditions, [2/4] message,
  [2.25/4]/[2.75/4] around the service call, [3/4] result,
  [3.5/4] post-branch checkpoint, [4/4] alarm control).
- Include a `blueprint_version` stamp in [1/4] so it's obvious
  when a stale cached copy is still running.
- Gate the notify branch on `trigger.id == 'sensor_on'` so clearing
  a sensor no longer fires a stray notification when another sensor
  is still active.
- Replace `binary_sensors.index()` (sandbox-restricted) with a
  namespace loop and drop the `sensor_index.__class__` reference
  that broke the [2/4] render.
- Drop `response_variable` from the notify call — it could abort
  the script past `continue_on_error` when the service didn't
  declare response support.
- Add `continue_on_error` on notify and alarm service calls so one
  misconfigured step doesn't cancel the whole automation.
- Tighten `has_notify_target`/`has_alarm_switch` to also reject
  whitespace, '[]', and 'None' string forms.

Version bumped 2.5.2 → 2.6.4.
2026-04-23 20:34:13 +03:00

498 lines
19 KiB
YAML

# Multi-Sensor Alarm & Notification Blueprint
# Monitors sensors and triggers notifications and alarm actions when activated.
# See README.md for detailed documentation.
blueprint:
name: "Custom: Multi-Sensor Alarm & Notification"
description: >
Triggers notifications and alarm actions when any of the configured binary sensors
change to "on". Supports per-sensor notification texts, optional alarm switch,
melody and volume selectors.
domain: automation
input:
# -------------------------------------------------------------------------
# Sensor Configuration
# -------------------------------------------------------------------------
devices:
name: "Devices"
collapsed: false
input:
binary_sensors:
name: Binary Sensors
description: List of binary sensors or input booleans to monitor for alarm triggers
selector:
entity:
domain:
- binary_sensor
- input_boolean
multiple: true
binary_sensors_debounce_duration:
name: Sensor Debounce Duration
description: >
Minimum time a sensor must stay ON before triggering an alarm.
Helps prevent false alarms from brief or spurious sensor activations.
default: 5
selector:
number:
min: 0
max: 30
unit_of_measurement: seconds
# -------------------------------------------------------------------------
# Notification Configuration
# -------------------------------------------------------------------------
notification:
name: "Notification"
collapsed: false
input:
notify_target:
name: Notification Target (optional)
description: >
Notify entity to send notifications (e.g., notify.mobile_app_phone).
Leave empty to disable notifications.
default:
selector:
entity:
domain: notify
notify_texts:
name: Notification Messages
description: >
Custom message for each sensor (one per line, in same order as sensor list).
If a sensor doesn't have a corresponding message, a default will be used.
default: []
selector:
text:
multiple: true
# -------------------------------------------------------------------------
# Alarm Device Configuration
# -------------------------------------------------------------------------
alarm_group:
name: "Alarm"
collapsed: false
input:
alarm_switch:
name: Alarm Switch (optional)
description: Switch entity to control the alarm device. Leave empty to disable alarm control.
default: []
selector:
entity:
domain: switch
multiple: false
melody_select:
name: Melody Selector (optional)
description: Select entity for choosing alarm melody/ringtone
default: []
selector:
entity:
domain:
- input_select
- select
multiple: false
melody_id:
name: Melody Value
description: The melody/ringtone option to select when alarm triggers
default: ""
selector:
text:
volume_select:
name: Volume Selector (optional)
description: Select entity for choosing alarm volume level
default: []
selector:
entity:
domain:
- input_select
- select
multiple: false
volume_id:
name: Volume Value
description: The volume level option to select when alarm triggers
default: ""
selector:
text:
# -------------------------------------------------------------------------
# Debug Configuration
# -------------------------------------------------------------------------
debug_group:
name: "Debug"
collapsed: true
input:
debug_enabled:
name: Enable Debug Logging
description: >
When enabled, creates persistent notifications at each stage of execution:
trigger received, notification attempt, notification result, alarm control.
Use to troubleshoot why notifications aren't reaching the target.
default: false
selector:
boolean:
# Restart mode ensures rapid sensor changes are handled cleanly
mode: restart
# =============================================================================
# Triggers
# =============================================================================
trigger:
# Sensor turned ON and stayed on for the debounce duration
- platform: state
entity_id: !input binary_sensors
to: "on"
for:
seconds: !input binary_sensors_debounce_duration
id: "sensor_on"
# Sensor turned OFF (for turning off alarm when all clear)
- platform: state
entity_id: !input binary_sensors
to: "off"
id: "sensor_off"
# =============================================================================
# Variables
# =============================================================================
variables:
# Input references
binary_sensors: !input binary_sensors
notify_target: !input notify_target
notify_texts: !input notify_texts
alarm_switch: !input alarm_switch
melody_select: !input melody_select
melody_id: !input melody_id
volume_select: !input volume_select
volume_id: !input volume_id
is_debug: !input debug_enabled
# Computed state values
enabled_sensors: "{{ binary_sensors | select('is_state', 'on') | list }}"
is_any_sensor_on: "{{ (binary_sensors | select('is_state', 'on') | list | length) > 0 }}"
# Whether a usable notify target is configured
has_notify_target: "{{ notify_target is not none and (notify_target | string | trim) not in ['', '[]', 'None'] }}"
# Whether alarm control is configured
has_alarm_switch: "{{ alarm_switch is not none and (alarm_switch | string | trim) not in ['', '[]', 'None'] }}"
# Small delay between setting melody/volume to ensure device processes each command
delay_between_commands_ms: 100
# Version stamp so debug output can confirm which blueprint revision is live.
# Bump this whenever debug/flow logic changes.
blueprint_version: "2.6.4 (notify.send_message + entity_id)"
# =============================================================================
# Actions
# =============================================================================
action:
# ---------------------------------------------------------------------------
# Debug Stage 1: Trigger Received
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_trigger"
title: "Alarm Debug [1/4]: Trigger Received"
message: >
Blueprint version: {{ blueprint_version }}
Trigger ID: {{ trigger.id }}
Entity: {{ trigger.entity_id }}
From: {{ trigger.from_state.state if trigger.from_state is not none else 'n/a' }}
→ To: {{ trigger.to_state.state if trigger.to_state is not none else 'n/a' }}
All monitored sensors: {{ binary_sensors }}
Currently active (on): {{ enabled_sensors }}
Any sensor on: {{ is_any_sensor_on }}
Notify target: {{ notify_target }}
Has notify target: {{ has_notify_target }}
Alarm switch: {{ alarm_switch }}
Has alarm switch: {{ has_alarm_switch }}
- service: system_log.write
data:
level: info
logger: blueprint.alarm_notification
message: >-
Trigger '{{ trigger.id }}' from {{ trigger.entity_id }};
active={{ enabled_sensors }};
notify_target={{ notify_target }};
has_notify_target={{ has_notify_target }}
# ---------------------------------------------------------------------------
# Debug Stage 1.5: What the notification-branch conditions evaluate to
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_pre_notify"
title: "Alarm Debug [1.5/4]: Evaluating notify conditions"
message: >
trigger.id = {{ trigger.id }}
trigger.id == 'sensor_on': {{ trigger.id == 'sensor_on' }}
has_notify_target: {{ has_notify_target }}
Combined (should be True to enter notify branch):
{{ trigger.id == 'sensor_on' and has_notify_target }}
# ---------------------------------------------------------------------------
# Send Notification (only on sensor_on, when target configured)
# ---------------------------------------------------------------------------
- choose:
- conditions:
# Single template condition avoids any quirks with condition: trigger
# inside a choose block.
- condition: template
value_template: "{{ trigger.id == 'sensor_on' and has_notify_target }}"
sequence:
- variables:
# The sensor that fired this trigger
sensor: "{{ trigger.entity_id }}"
# Namespace-loop avoids Jinja sandbox restrictions on list.index()
sensor_index: >-
{%- set ns = namespace(idx=-1) -%}
{%- for s in binary_sensors -%}
{%- if s == sensor -%}{%- set ns.idx = loop.index0 -%}{%- endif -%}
{%- endfor -%}
{{ ns.idx }}
# Resolve message (strip whitespace to keep notifications clean)
message: >-
{%- set messages = notify_texts | list -%}
{%- set idx = sensor_index | int(-1) -%}
{%- if idx >= 0 and idx < messages | length -%}
{{ messages[idx] }}
{%- else -%}
Alarm: {{ state_attr(sensor, 'friendly_name') or sensor }} triggered
{%- endif -%}
# Debug Stage 2: Variables computed, about to send
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_notify_attempt"
title: "Alarm Debug [2/4]: Sending Notification"
message: >
Target: {{ notify_target }}
Sensor: {{ sensor }}
Sensor index (raw): "{{ sensor_index }}"
Messages defined: {{ notify_texts | length }}
Resolved message: "{{ message }}"
# Debug Stage 2.25: About to invoke service
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_pre_service"
title: "Alarm Debug [2.25/4]: Before notify.send_message"
message: >
Blueprint version: {{ blueprint_version }}
About to call: action notify.send_message
with target.entity_id: {{ notify_target }}
and data.message: "{{ message }}"
# Send via notify.send_message targeting the notify entity.
# This matches the canonical HA pattern used by working automations.
# continue_on_error ensures the alarm-control stage still runs
# if the notify integration is misconfigured.
- action: notify.send_message
metadata: {}
target:
entity_id: "{{ notify_target }}"
data:
message: "{{ message }}"
continue_on_error: true
# Debug Stage 2.75: Service call returned (success or caught error)
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_post_service"
title: "Alarm Debug [2.75/4]: After notify.send_message"
message: >
notify.send_message to {{ notify_target }} returned
(either success or continue_on_error caught it).
If this notification is missing but [2.25/4] is
present, the call raised an error that
continue_on_error did NOT catch. Check the HA log
and verify {{ notify_target }} is a notify entity
(Developer Tools → States should list it).
# Debug Stage 3: Notification result
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_notify_result"
title: "Alarm Debug [3/4]: Notification Sent"
message: >
notify.send_message dispatched to {{ notify_target }}
with message: "{{ message }}".
If nothing arrived on the device, check:
1) HA log for errors from the notify integration
2) That the underlying integration (Telegram bot,
mobile app, etc.) is online
3) Test from Developer Tools → Actions:
notify.send_message with target {{ notify_target }}
- service: system_log.write
data:
level: info
logger: blueprint.alarm_notification
message: >-
Notification attempted to {{ notify_target }};
sensor={{ sensor }}; message='{{ message }}'
# ---------------------------------------------------------------------------
# Debug Stage 3.5: Unconditional checkpoint so we can tell whether the
# notification branch silently aborted the script above. If this appears
# but [2/4] or [3/4] don't, the variables block or notify.send_message
# errored silently.
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_after_notify"
title: "Alarm Debug [3.5/4]: After notify branch"
message: >
Execution reached this point. trigger.id = {{ trigger.id }}.
If [2/4] and [3/4] did NOT appear above, the notify branch
either did not enter (check [1.5/4]), or errored silently
in the variables / notify.send_message step.
Check Home Assistant log for Jinja or service errors.
# ---------------------------------------------------------------------------
# Alarm Device Control
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ has_alarm_switch }}"
sequence:
# Set melody (if configured)
- choose:
- conditions:
- condition: template
value_template: "{{ melody_select is not none and (melody_select | string | trim) not in ['', '[]', 'None'] and (melody_id | string | length) > 0 }}"
sequence:
- service: select.select_option
target:
entity_id: "{{ melody_select }}"
data:
option: "{{ melody_id }}"
continue_on_error: true
- delay:
milliseconds: "{{ delay_between_commands_ms }}"
# Set volume (if configured)
- choose:
- conditions:
- condition: template
value_template: "{{ volume_select is not none and (volume_select | string | trim) not in ['', '[]', 'None'] and (volume_id | string | length) > 0 }}"
sequence:
- service: select.select_option
target:
entity_id: "{{ volume_select }}"
data:
option: "{{ volume_id }}"
continue_on_error: true
- delay:
milliseconds: "{{ delay_between_commands_ms }}"
# Turn alarm ON or OFF based on sensor state
- choose:
# Any sensor active -> turn alarm ON
- conditions:
- condition: template
value_template: "{{ is_any_sensor_on }}"
sequence:
- service: switch.turn_on
target:
entity_id: "{{ alarm_switch }}"
continue_on_error: true
# All sensors clear -> turn alarm OFF
default:
- service: switch.turn_off
target:
entity_id: "{{ alarm_switch }}"
continue_on_error: true
# Debug Stage 4: Alarm control done
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
notification_id: "alarm_debug_alarm_control"
title: "Alarm Debug [4/4]: Alarm Control Done"
message: >
Alarm switch: {{ alarm_switch }}
Action: {{ 'turn_on' if is_any_sensor_on else 'turn_off' }}
Melody: {{ melody_id }} → {{ melody_select }}
Volume: {{ volume_id }} → {{ volume_select }}