Critical Notifications: Escalation, SMS, Repeats, Acknowledgment, and Fallback
Critical Notifications: Escalation, SMS, Repeats, Acknowledgment, and Fallback
In Lesson 2 you built the push channel and planned a second SMS channel. Now you'll assemble an actual alarm system out of them: an urgent push, a repeat, human acknowledgment, a local household response, and optional SMS for when the internet is down and you're not home.
This is a lesson about escalation. You're still not installing flood or smoke sensors. You're building a response template that Lessons 4 through 9 will hang specific events off of. That way, every security scenario calls the same escalation scripts instead of duplicating repeat-and-fallback logic everywhere.
Plan on about 90 to 105 minutes. By the end you'll have a working alarm test from the dashboard, an acknowledgment button, a local siren or lights, and, if you have the hardware, a working SMS pattern inside the escalation.
The rule for this lesson
One notification isn't enough for flooding, smoke, or a break-in.
Notify, repeat, escalate, give the house a local signal, and consider a second SMS channel.
The module's rule for critical events
For routine events, push is enough. For critical ones: push plus escalation plus a local response, plus optionally SMS. None of these channels is a guarantee on its own; together they cut the risk of an important alarm getting lost.
A real-life problem: the push went out, nobody responded
Picture flooding under the washing machine at 10:30 PM. You're away from home. The home internet went down after a storm. Home Assistant is running locally and detected the water, but the push to your phone never went out. The automation "did its job": the notify action had no way to reach you outside the house. You don't know about the failure. The water is still running. This isn't a sensor failure. It's a missing escalation, a missing second channel, and a missing local response.
The same scenario looks different when you have a siren in the house, local SMS through an LTE router or a GSM gateway, and, from Lesson 4, an automatically closing water shutoff valve. The system doesn't have to wait until you read a push. It can limit the damage and reach you through another route.
In Lesson 2 you broke a notification down into five stages. This lesson closes stages 4 and 5: a household member has to notice the alert and confirm they're responding. If they don't, the system doesn't go quiet after a single push.
What's actually the risk
One push and nothing more: the household member is asleep, out of range, or ignoring notifications.
No acknowledgment: you don't know whether someone's already checking the situation, or whether the alarm is just hanging open.
Phone only, zero local signal: with no internet, push doesn't arrive, but HA can still trigger a siren in the house.
Mindless repeat spam: dozens of identical alerts every minute kill trust in the whole system.
Push only, with no internet: you're away, the router died, the alert never goes out, and the sensor has already detected flooding.
Treating SMS as a guarantee: no GSM signal, an expired SIM, or a modem with no UPS behave exactly like unreliable push.
Confusing app delivery with a human response: confirmation: true tells you the phone got the push, not that someone drove home.
What you need from Lesson 2
This lesson assumes you already have working notify_household and notify_household_urgent scripts, a notification test helper, and a test button on the dashboard. If the test from Lesson 2 doesn't reach your phone, go back and fix the channel before building escalation on top of it.
For a local response, at least one of the following will come in handy. You don't need all of them. A siren, or even one light in the hallway, is enough.
| Device | Example entity | Role |
|---|---|---|
| Zigbee / Wi-Fi siren | switch.security_siren |
An audible alarm in the house with no push needed |
| A light | light.hallway |
A visual signal when someone's home |
| Speaker / media player | media_player.living_room |
A spoken text-to-speech announcement |
Don't have a siren yet?
To start, one light set to 100% brightness and red works fine. In the YAML examples, swap the entities for your own from Developer Tools. If you don't have text-to-speech, remove that action from the local script.
Which entities we're watching
All the escalation logic rests on two helpers. One says whether an alarm is active. The other says whether someone has acknowledged it. Everything else just reacts to those states.
| Entity | Meaning |
|---|---|
input_boolean.security_alarm_active |
The alarm is ongoing, escalation can keep going |
input_boolean.security_alarm_acknowledged |
Someone has consciously confirmed they're responding |
input_boolean.security_alarm_test |
The escalation-test button on the dashboard |
input_boolean.alarm_sms_enabled |
Turns on the second SMS channel during escalation |
binary_sensor.internet_available |
Whether the house has internet access (a ping test) |
The escalation pattern
The pattern below is the target flow. We'll build it step by step in YAML. Important: the repeats happen every few minutes, not every few seconds.
1. Alarm starts: an urgent push (if the internet is up) plus a local siren / lights / TTS.
2. No internet: SMS goes out immediately through an LTE router or a local GSM gateway, if the SMS channel is enabled.
3. Wait 5 minutes: if nobody has acknowledged, send a repeat over whatever channel is available.
4. Repeat up to 3 times: push if the internet is working, or SMS if it isn't and the SMS channel is on.
5. Final escalation: an ESCALATION push plus SMS, if the alarm is still unacknowledged.
6. Acknowledgment: a household member taps I'M RESPONDING (or a helper): the repeats stop; closing the event is a separate decision.
Alarm helpers: separate event types
A single shared acknowledgment helper would mix flooding with smoke. That's why we split out at least three tracks: life, flooding, and intrusion. This is still a course-level simplification, not a professional alarm queue, but it doesn't hide concurrent threats from each other.
input_boolean:
life_alarm_active:
name: "Life alarm active"
icon: mdi:fire-alert
life_alarm_acknowledged:
name: "Life alarm: I'm responding"
icon: mdi:alarm-check
flood_alarm_active:
name: "Flood alarm active"
icon: mdi:water-alert
flood_alarm_acknowledged:
name: "Flood alarm: I'm responding"
icon: mdi:alarm-check
intrusion_alarm_active:
name: "Intrusion alarm active"
icon: mdi:shield-alert
intrusion_alarm_acknowledged:
name: "Intrusion alarm: I'm responding"
icon: mdi:alarm-check
alarm_event_closed:
name: "Close the event (cause checked)"
icon: mdi:check-circle
security_alarm_test:
name: "Security alarm test"
icon: mdi:alarm-bell
alarm_sms_enabled:
name: "Alarm SMS enabled"
icon: mdi:message-alert
initial: false
alarm_sms_test:
name: "Alarm SMS test"
icon: mdi:message-alert
…_acknowledged means: "I saw it, I'm responding, stop the repeats." It does not mean the cause is gone. Clearing the alarm status is a separate decision, made through alarm_event_closed or a stop script once the cause has been checked.
The native alert integration has active / acknowledged / resolved states and repeats built in: it's worth considering later as an alternative to your own loops. To start, we're building transparent scripts.
The internet-availability sensor
A ping is a hint, not a gatekeeper for push. Cloudflare's 1.1.1.1 may not answer ICMP even with a working connection; Local Push at home can work without "internet on"; a brief unknown right after a restart shouldn't be allowed to stop the local response. Add the current Ping configuration through the UI (Settings → Devices & Services → Add Integration → Ping). The old-style YAML block binary_sensor: - platform: ping below is an illustration of the entity, not the only recommended method.
# Entity illustration only (prefer configuring Ping through the UI)
# binary_sensor.internet_available
# Default refresh is roughly 30s; also watch for packet loss.
The rule: always try push (continue_on_error: true). When the internet isn't a confident on, also try SMS. Once nobody has acknowledged, use every channel you have.
Script: the local household response
The local response starts before and independently of push. A notify failure must never stop the siren. For TTS: the target is a TTS entity, and you supply the player through media_player_entity_id. Limit siren runtime with a separate timer (say, 10 minutes) so it doesn't run forever.
script:
security_alarm_local:
alias: "Local alarm response"
mode: parallel
fields:
tts_message:
description: "A voice message matching the type of threat"
default: "Attention. Security alarm at home."
sequence:
- action: switch.turn_on
target:
entity_id: switch.security_siren
- action: light.turn_on
target:
entity_id: light.hallway
data:
brightness_pct: 100
rgb_color: [255, 0, 0]
- action: tts.speak
continue_on_error: true
target:
entity_id: tts.piper
data:
media_player_entity_id: media_player.living_room
message: "{{ tts_message }}"
- delay:
minutes: 10
- action: switch.turn_off
target:
entity_id: switch.security_siren
TTS
Swap tts.piper for your own TTS entity. If you don't have TTS: remove the action. The siren and the light are enough on their own. The TTS message for smoke, CO, or gas has to be different from "check your phone": see Lesson 5.
Three independent escalation scripts
Instead of one mode: single script that rejects a second critical alarm, you have three scripts in mode: queued. Flooding doesn't block smoke. Smoke doesn't block an intrusion. The order inside each script: activate → local response → push (with continue_on_error) → optional SMS → repeats → final escalation.
script:
life_alarm:
alias: "Life alarm (smoke / CO / gas)"
mode: queued
max: 5
fields:
title:
description: "Alarm title"
message:
description: "Alarm body"
sequence:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.life_alarm_active
- action: input_boolean.turn_off
target:
entity_id: input_boolean.life_alarm_acknowledged
- action: script.turn_on
target:
entity_id: script.security_alarm_local
data:
variables:
tts_message: "Attention. Fire, carbon monoxide, or gas alarm. Leave the house following the plan."
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "{{ title }}"
message: "{{ message }}"
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "HOME ALARM: {{ title }}"
- repeat:
count: 3
sequence:
- wait_template: "{{ is_state('input_boolean.life_alarm_acknowledged', 'on') }}"
timeout: "00:05:00"
continue_on_timeout: true
- choose:
- conditions:
- condition: state
entity_id: input_boolean.life_alarm_acknowledged
state: "on"
sequence:
- stop: "Alarm acknowledged"
- conditions:
- condition: state
entity_id: input_boolean.life_alarm_active
state: "off"
sequence:
- stop: "Alarm canceled"
default:
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "REPEAT: {{ title }}"
message: "{{ message }}: alarm not yet acknowledged."
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "REPEAT ALARM: {{ title }}"
- condition: state
entity_id: input_boolean.life_alarm_acknowledged
state: "off"
- condition: state
entity_id: input_boolean.life_alarm_active
state: "on"
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "ESCALATION: {{ title }}"
message: "{{ message }}: nobody has acknowledged."
- choose:
- conditions:
- condition: state
entity_id: input_boolean.alarm_sms_enabled
state: "on"
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "ESCALATION ALARM: {{ title }}"
flood_alarm:
alias: "Flood alarm"
mode: queued
max: 5
fields:
title:
description: "Alarm title"
message:
description: "Alarm body"
sequence:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.flood_alarm_active
- action: input_boolean.turn_off
target:
entity_id: input_boolean.flood_alarm_acknowledged
- action: script.turn_on
target:
entity_id: script.security_alarm_local
data:
variables:
tts_message: "Attention. Flooding detected. Check the water and the shutoff valve."
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "{{ title }}"
message: "{{ message }}"
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "HOME ALARM: {{ title }}"
- repeat:
count: 3
sequence:
- wait_template: "{{ is_state('input_boolean.flood_alarm_acknowledged', 'on') }}"
timeout: "00:05:00"
continue_on_timeout: true
- choose:
- conditions:
- condition: state
entity_id: input_boolean.flood_alarm_acknowledged
state: "on"
sequence:
- stop: "Alarm acknowledged"
- conditions:
- condition: state
entity_id: input_boolean.flood_alarm_active
state: "off"
sequence:
- stop: "Alarm canceled"
default:
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "REPEAT: {{ title }}"
message: "{{ message }}: alarm not yet acknowledged."
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "REPEAT ALARM: {{ title }}"
- condition: state
entity_id: input_boolean.flood_alarm_acknowledged
state: "off"
- condition: state
entity_id: input_boolean.flood_alarm_active
state: "on"
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "ESCALATION: {{ title }}"
message: "{{ message }}: nobody has acknowledged."
- choose:
- conditions:
- condition: state
entity_id: input_boolean.alarm_sms_enabled
state: "on"
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "ESCALATION ALARM: {{ title }}"
intrusion_alarm:
alias: "Intrusion alarm"
mode: queued
max: 5
fields:
title:
description: "Alarm title"
message:
description: "Alarm body"
sequence:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.intrusion_alarm_active
- action: input_boolean.turn_off
target:
entity_id: input_boolean.intrusion_alarm_acknowledged
- action: script.turn_on
target:
entity_id: script.security_alarm_local
data:
variables:
tts_message: "Attention. Intrusion alarm. Check the entry points per the plan."
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "{{ title }}"
message: "{{ message }}"
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "HOME ALARM: {{ title }}"
- repeat:
count: 3
sequence:
- wait_template: "{{ is_state('input_boolean.intrusion_alarm_acknowledged', 'on') }}"
timeout: "00:05:00"
continue_on_timeout: true
- choose:
- conditions:
- condition: state
entity_id: input_boolean.intrusion_alarm_acknowledged
state: "on"
sequence:
- stop: "Alarm acknowledged"
- conditions:
- condition: state
entity_id: input_boolean.intrusion_alarm_active
state: "off"
sequence:
- stop: "Alarm canceled"
default:
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "REPEAT: {{ title }}"
message: "{{ message }}: alarm not yet acknowledged."
- choose:
- conditions:
- condition: template
value_template: >
{{ not is_state('binary_sensor.internet_available', 'on')
and is_state('input_boolean.alarm_sms_enabled', 'on') }}
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "REPEAT ALARM: {{ title }}"
- condition: state
entity_id: input_boolean.intrusion_alarm_acknowledged
state: "off"
- condition: state
entity_id: input_boolean.intrusion_alarm_active
state: "on"
- action: script.notify_household_urgent
continue_on_error: true
data:
title: "ESCALATION: {{ title }}"
message: "{{ message }}: nobody has acknowledged."
- choose:
- conditions:
- condition: state
entity_id: input_boolean.alarm_sms_enabled
state: "on"
sequence:
- action: script.alarm_sms
continue_on_error: true
data:
number: "+15550123456"
message: "ESCALATION ALARM: {{ title }}"
In Lessons 4 through 6 and 9 you'll call the right script directly. The old name script.security_alarm is no longer the only one: treat it as legacy. For tests, use script.turn_on, so the automation doesn't sit and wait 15 minutes for the escalation to finish.
script.turn_on versus calling it directlyaction: script.flood_alarm waits for the whole sequence to finish (including three × 5-minute waits). That's why, for tests and whenever you want the helper reset right away, use action: script.turn_on with variables.
Buttons in the notification (recommended)
A household member shouldn't have to go hunting for a helper on the dashboard. The Companion App supports actionable notifications with buttons. The helper on the panel stays as your Plan B.
# Example of a critical push with buttons (Android + iOS)
# Use the device-specific action or notify data per the Companion documentation
action: notify.mobile_app_marek_phone
continue_on_error: true
data:
title: "{{ title }}"
message: "{{ message }}"
data:
actions:
- action: "ALARM_ACK"
title: "I'M RESPONDING"
- action: "URI"
title: "OPEN DASHBOARD"
uri: "/lovelace/security"
# Android: channel / importance; iOS: push.sound critical: per Companion docs
automation:
- alias: "Security: I'M RESPONDING button in a notification"
mode: queued
triggers:
- trigger: event
event_type: mobile_app_notification_action
event_data:
action: "ALARM_ACK"
actions:
- choose:
- conditions:
- condition: state
entity_id: input_boolean.life_alarm_active
state: "on"
sequence:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.life_alarm_acknowledged
- conditions:
- condition: state
entity_id: input_boolean.flood_alarm_active
state: "on"
sequence:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.flood_alarm_acknowledged
default:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.intrusion_alarm_acknowledged
A second alarm channel: SMS
Push from the Home Assistant app is fast and convenient, but it shouldn't be the only channel for critical alarms. SMS can go out through an LTE router with an HA integration, a local GSM gateway, or an external API. For real emergency use, a local setup makes the most sense: an LTE router or GSM gateway with its own SIM card, independent of the home internet.
SMS can fail too
No GSM signal, no prepaid balance, an expired SIM, a modem stuck after months without a test, no power to HA or the router, a message that's too long, or an internet-based SMS gateway during a connectivity outage. SMS reduces the risk; it doesn't eliminate it.
Option A: an LTE router with an HA integration
If you have a compatible Huawei LTE or Netgear LTE router, the integration can create a notify.* service. Check the exact name in Developer Tools → Actions. Keep alarm SMS short: no emoji, no long sentences, and ideally plain ASCII. Netgear LTE has message-length limits.
action: notify.huawei_lte
data:
message: "HOME ALARM: flooding detected under the washer."
Option B: a local SMS gateway over REST (recommended pattern)
The GSM modem and Gammu live outside the main Home Assistant instance, on a separate device, add-on, or container running an HTTP gateway (for example sms-gammu-gateway). HA only calls the local REST API through rest_command. That way, swapping the modem doesn't require rewriting your security automations.
The URL, method, and payload depend on your own gateway. The example below assumes a local API that accepts JSON with number and message fields. Adapt it to your device's documentation.
rest_command:
sms_gateway_send:
url: "http://192.168.1.80:5000/api/sms"
method: post
content_type: "application/json"
username: !secret sms_gateway_user
password: !secret sms_gateway_password
authentication: basic
payload: >
{
"number": "{{ number }}",
"message": "{{ message }}"
}
secrets.yaml
sms_gateway_user: admin
sms_gateway_password: a_very_strong_password
script:
alarm_sms:
alias: "Alarm SMS"
mode: queued
fields:
number:
description: "Recipient's phone number, in +1... format"
message:
description: "Short SMS body"
sequence:
- action: rest_command.sms_gateway_send
data:
number: "{{ number }}"
message: "{{ message }}"
Watch out for HiLink-style USB modems
Many 4G dongles act as a network adapter or USB router rather than a classic AT modem exposed as a serial port. Gammu generally needs a modem in AT mode. Check your modem's documentation before buying.
An SMS test from the dashboard
automation:
- alias: "Security: alarm SMS test"
description: "Sends a test SMS through the GSM gateway"
mode: single
triggers:
- trigger: state
entity_id: input_boolean.alarm_sms_test
to: "on"
actions:
- action: script.alarm_sms
data:
number: "+15550123456"
message: "HA TEST: the SMS channel is working."
- delay:
seconds: 2
- action: input_boolean.turn_off
target:
entity_id: input_boolean.alarm_sms_test
If you're using an LTE router instead of a REST gateway, swap rest_command in the alarm_sms script for a notify.huawei_lte or notify.netgear_lte action. What matters is that the whole escalation calls one single script, not three different places holding phone numbers.
Stop / acknowledge / close the event
script:
security_alarm_stop:
alias: "Stop the local response (siren / lights)"
mode: single
sequence:
- action: switch.turn_off
target:
entity_id: switch.security_siren
- action: light.turn_off
target:
entity_id: light.hallway
automation:
- alias: "Security: acknowledging life alarm stops the repeats"
mode: queued
triggers:
- trigger: state
entity_id: input_boolean.life_alarm_acknowledged
to: "on"
actions:
- action: script.security_alarm_stop
# life_alarm_active does NOT have to clear here: closing the event is a separate, deliberate decision
- alias: "Security: flood alarm acknowledged"
mode: queued
triggers:
- trigger: state
entity_id: input_boolean.flood_alarm_acknowledged
to: "on"
actions:
- action: script.security_alarm_stop
- alias: "Security: intrusion alarm acknowledged"
mode: queued
triggers:
- trigger: state
entity_id: input_boolean.intrusion_alarm_acknowledged
to: "on"
actions:
- action: script.security_alarm_stop
- alias: "Security: closing the event"
mode: single
triggers:
- trigger: state
entity_id: input_boolean.alarm_event_closed
to: "on"
actions:
- action: input_boolean.turn_off
target:
entity_id:
- input_boolean.life_alarm_active
- input_boolean.flood_alarm_active
- input_boolean.intrusion_alarm_active
- input_boolean.alarm_event_closed
- action: script.security_alarm_stop
Acknowledging delivery is not the same as closing the event. Don't mix up these two decisions.
Automation: an alarm test from the dashboard
automation:
- alias: "Security: alarm test from the dashboard"
description: "Runs a full escalation test without blocking the helper reset"
mode: single
triggers:
- trigger: state
entity_id: input_boolean.security_alarm_test
to: "on"
actions:
- action: script.turn_on
target:
entity_id: script.intrusion_alarm
data:
variables:
title: "TEST: security alarm"
message: "This is a test of the full alarm escalation."
- delay:
seconds: 2
- action: input_boolean.turn_off
target:
entity_id: input_boolean.security_alarm_test
The test is a real escalation
Push, SMS (if enabled), and the siren all fire. Test during the day, and give the household a heads-up. The helper turns itself off after 2 seconds thanks to script.turn_on.
Critical scenario: flooding with no internet
You're away, the internet is down, and a sensor has detected water
If you only had push, you might never find out. That's why, for flooding, we don't rely solely on a notification. The system should trigger a siren and lights locally, and send SMS as a second channel through an LTE router or a local GSM gateway. In Lesson 4 you'll add automatically closing a water shutoff valve: that's the first response that actually limits damage, and it matters more than the push itself.
The target architecture for flooding looks like this: sensor → HA locally → water shutoff valve (Lesson 4) → siren and lights → push (if the internet is up) → SMS (if the internet is down or there's no acknowledgment). Without the shutoff valve, the system only informs you. With it, it can limit the damage even without you at home.
A silenced phone, no internet, and a UPS
Push depends on your phone, your carrier, and Apple's or Google's services. SMS depends on GSM signal and the modem. A local siren, light, and TTS work on the home network even when the internet is down, as long as Home Assistant and the devices have power.
A UPS matters
If you expect a response during an internet or power outage, put Home Assistant, whatever router or switch you need for communication, the Zigbee coordinator, and the LTE router or GSM gateway all on backup power. Otherwise, a sensor might detect water, but the system will have no way to close the valve or send an SMS.
| Scenario | Push | SMS | Local response |
|---|---|---|---|
| You're home, internet's up | Should arrive | Optional, during escalation | Siren and lights as a bonus |
| You're away, internet's down | Probably won't arrive | May get through via GSM/LTE | Siren alerts anyone home |
| You're away, phone's on silent | May go unnoticed | SMS may be noticed later | Won't help you while you're away |
| Power's out at home | HA doesn't run without a UPS | The modem doesn't either, without a UPS | Doesn't work without power either |
This setup doesn't guarantee you'll always get the message. It cuts the risk, because it responds locally, doesn't rely solely on push, has a second SMS channel, and requires acknowledgment. For threats to life, a certified detector with its own siren still matters most, not just Home Assistant.
A dashboard card: alarm and acknowledgment
Add an alarm section alongside the notifications card from Lesson 2. The acknowledgment button should be visible whenever an alarm is active.
type: entities
title: Security alarm
entities:
- input_boolean.security_alarm_test
- input_boolean.security_alarm_active
- input_boolean.security_alarm_acknowledged
- input_boolean.alarm_sms_enabled
- input_boolean.alarm_sms_test
- binary_sensor.internet_available
- script.security_alarm
- script.security_alarm_stop
- script.alarm_sms
In the mobile app, you can also add a shortcut to the acknowledgment helper, or use a view with one large button. What matters is that acknowledging is faster than hunting for an automation in settings.
How to test, step by step
1. Let the household know you're about to test the alarm.
2. Turn on security_alarm_test on the dashboard.
3. Check the urgent push, the siren, and the lights. intrusion_alarm_active should be on.
4. Within 5 minutes, turn on intrusion_alarm_acknowledged. The alarm should stop.
5. Repeat the test, but this time don't acknowledge right away. Wait for the push with the REPEAT prefix.
6. If you have an SMS gateway, turn on alarm_sms_test and check that the SMS reaches your phone.
7. Safely simulate losing the internet: unplug the WAN cable on the router or turn off Wi-Fi on a test phone, don't power off the entire router or the switch you use to reach Home Assistant. Run the alarm test and check that the escalation falls back to SMS instead of push.
8. Finally, run script.security_alarm_stop manually if anything got left running.
Common mistakes
Escalating every minute: the household will disable HA notifications permanently.
No single mode on the alarm script: two events launch two parallel escalations.
Acknowledgment doesn't silence the siren: the test ends, but the house keeps shouting.
Confusing push delivery with acknowledgment: an app event isn't the same as the acknowledgment helper.
Push only, no local response: with the router offline, you have no signal in the house.
Testing the alarm at night with no warning: you wake the house with a false production alarm.
SMS never tested: the SIM card expired, and you find out during a real flood.
An internet-based SMS gateway as your only backup channel: it won't help during a connectivity outage.
What not to do
Don't build escalation on top of a notify setup from Lesson 2 that isn't already working.
Don't use the alarm test as your weekly reminder: it's a full escalation, not a routine ping.
Don't duplicate the repeat logic in every sensor automation: keep it inside the alarm scripts.
Don't promise yourself that acknowledgment in HA replaces a physical response: the helper only means someone tapped "I'm responding."
Don't promise yourself SMS will always arrive: it's a second channel, not a guarantee.
Assignment: the alarm escalation system
To do
☐ Add the security_alarm_active, security_alarm_acknowledged, and security_alarm_test helpers.
☐ Create the security_alarm_local script with your own siren, light, or TTS entities.
☐ Create the escalation scripts with repeats every 5 minutes for life, flood, and intrusion alarms.
☐ Create the security_alarm_stop script and the acknowledgment automations.
☐ Add the alarm-test automation triggered from the dashboard.
☐ Place the alarm card on the security dashboard next to the notifications section.
☐ Test the scenario where you acknowledge within 5 minutes.
☐ Test the scenario with no acknowledgment and check for the push with the REPEAT prefix.
☐ If you have an LTE router or a GSM gateway: configure script.alarm_sms and test SMS from the dashboard.
☐ Safely simulate losing the internet (for example, unplug the WAN, not the whole router) and check that escalation falls back to SMS.
☐ Note down: who gets the push, who gets the SMS, and who acknowledges the alarm.
☐ Write down whether your GSM modem / LTE router is on a UPS.
Key takeaways
One notification isn't enough for flooding, smoke, or a break-in.
The escalation scripts centralize this pattern for the whole module.
Acknowledgment is a human decision, not just the phone receiving a push.
A local siren and lights still work when push fails inside the house.
SMS is a second channel, not a replacement for push or a delivery guarantee.
With no internet, the local response and SMS matter more than push on its own.
Run the alarm test and the SMS test deliberately, every so often, not every day.
What's next
In Lesson 4 you'll wire up the first real scenario: flood sensors in the kitchen, bathroom, near the washing machine, and near the dishwasher. The flooding automation will call the flood alarm script and, if you have a valve, close the water supply locally before push or SMS ever reach you away from home.
In Lesson 10 we'll come back to SMS and push during internet outages, power failures, and backup-channel tests. If your alarm test ends with an acknowledgment and the siren going quiet, you have the module's response engine in place.