Skip to content

Ring Alarm — Whole-House Audio Feedback

Date: 2026-07-05 Status: Design — approved, pending implementation plan Scope: automations.yaml (§22 Ring Alarm), scripts.yaml, configuration.yaml (helpers + dashboard registration), a new YAML dashboard, docs manual

Problem

Today the only audio feedback for the Ring alarm is a single one-shot Alexa announce on the kitchen Echo Show when alarm_control_panel.blairmont_alarm goes to triggered (automation #50). Everything else — arming, disarming, the entry/exit grace period, a failed arm — is silent from HA's side.

The Ring base station siren and the laundry-room keypad chirp natively during exit/entry delay and blare on trigger, but that sound only reaches the rooms near those two devices. The house has 7 Echoes (kitchen, family room, front room, garage, master bathroom, master bedroom, office) that can carry spoken alerts into every room the keypad can't.

Goal: give the alarm whole-house spoken feedback for its full lifecycle — louder/wider on trigger, arm/disarm confirmation, entry/exit boundary announcements, and a "failed to arm" heads-up — with every knob (enable, volume, repeat interval) live-tunable from a dedicated dashboard so it can be dialed in over days/weeks, and test buttons to preview each announcement without setting off the real alarm.

What the panel exposes (verified live)

alarm_control_panel.blairmont_alarm (ring-mqtt) reports:

  • States: disarmed, armed_home, armed_away, arming (exit delay), pending (entry delay), triggered.
  • Attributes: exitSecondsLeft, entrySecondsLeft (live countdown), lastArmedBy, lastDisarmedBy, targetState.

Non-goals (deliberate)

  • No literal per-second countdown ("30, 29, 28…"). Alexa announce has ~1–2s latency and cannot keep pace; the Ring keypad/base already chirps every second natively. HA does boundary announcements only (start of delay + one ~10s warning).
  • Sonos deferred. The 7 Echoes already cover the whole house. Sonos uses a separate cloud tts.speak + music-ducking path — added failure surface for redundancy we mostly already have. Ship Echoes first.
  • HA does not arm, disarm, or dispatch. Ring remains the brain (Ring Protect Pro dispatches). HA only reacts to state via ring-mqtt.
  • Speaker assignment per event is fixed in config, not dashboard-tunable. The dashboard tunes enable + volume + interval; changing which room speaks is a code edit (YAGNI for a settings panel).

Architecture

Three layers, so both the real automations and the dashboard test buttons share one code path (mirrors how script.speak_briefing / script.night_lights_render centralize a capability):

1. script.alarm_speak(message, speakers, volume) — low-level fan-out

  • message (string) — text to speak.
  • speakers (list) — target Echo media_player entities.
  • volume (float 0.0–1.0) — level to set before announcing.

Behavior: media_player.volume_set on the targets → Alexa announce fan-out. Owns how the house talks. Nothing else.

Fan-out mechanism (validate at implementation): prefer the generic notify.alexa_media service with a target: list + data.type: announce (one call, all speakers). If the generic service isn't registered in this alexa_media build, fall back to per-device notify.alexa_media_<slug> calls (the pattern automation #50 uses today). data.type: announce = chime prefix + spoken, matching the #61 alexa_media migration.

2. script.alarm_audio_event(event, message, force) — mid-level policy

Given an event name and a ready-built message: - Stop unless input_boolean.alarm_audio_enabled (master) is on — unless force: true (test buttons bypass the master + per-event enable so a disabled event can still be previewed). - Stop unless that event's input_boolean.alarm_audio_<event>_enabled is on (again bypassed by force). - Resolve speakers from the event→speaker map (below) and volume from input_number.alarm_audio_<event>_volume. - Call script.alarm_speak.

Callers build the message because only they have the live vars (zones, target, seconds); this script owns enable-policy + speaker/volume resolution.

3. Automations — one per state transition

Each triggers on a state transition, builds the message, and calls script.alarm_audio_event. The trigger event additionally wraps the call in a repeat loop.

Events → behavior

ALL_ECHOES = the 7 Echo media_players (kitchen, family_room, front_room, garage, master_bathroom, master_bedroom, office).

# Trigger (state transition) Speakers Default volume Spoken message
Trigger to: triggered ALL_ECHOES 0.8 "Security alert. The Ring alarm has been triggered. {zones}." — repeats every interval s while triggered
Exit delay to: arming front_room_echo 0.6 "Arming to {target}. You have {exitSecondsLeft} seconds to exit." + one "Ten seconds." near the end
Entry delay to: pending ALL_ECHOES 0.8 "Entry delay. Disarm now."
Armed armingarmed_home/armed_away master_bedroom_echo, office_echo 0.3 "Alarm armed to {home/away}."
Disarmed to: disarmed (from armed_*/pending) master_bedroom_echo, office_echo (announce) + ALL_ECHOES (volume reset only) 0.3 "Alarm disarmed." — also resets all 7 Echoes to the reset-volume default
Failed to arm armingdisarmed master_bedroom_echo, office_echo 0.6 "The alarm could not arm. Check doors and windows."

Notes: - {zones} reuses the existing template in #50 (names any open Ring contact/motion sensor: binary_sensor.personnel_door, binary_sensor.foyer_motion_sensor — extend as Retrofit zones are added). - {target} = armed_home → "home", armed_away → "away" (from targetState). - Repeating trigger loop mirrors §51 (Night Lights Red Beacon): mode: restart, while: state == triggered → call alarm_audio_eventdelay: {input_number.alarm_audio_trigger_interval} s. Loop ends when the alarm clears. - Disarmed vs Failed-to-arm both end in disarmed; disambiguated by from: (armed_*/pending → disarmed; arming → disarmed = failed/aborted). Distinct from: values prevent double-firing.

Volume strategy

Alexa announce plays at each Echo's current volume, so it must be forced. alexa_media can't cleanly snapshot-and-restore per-device volume, so we set a per-event level (from the helper) before announcing and reset at the natural all-clear:

  • Per-event default volumes are the "Default volume" column above; each is a live input_number (see helpers).
  • Reset point: on transition to disarmed, reset all 7 Echoes to input_number.alarm_audio_reset_volume (default 0.4). Disarming is the "all clear," so any loud event is followed by a clean reset — no fragile mid-announce restore.

Tunable settings (helpers)

All defined in configuration.yaml. No initial: on the numbers/booleans so dashboard tweaks persist across restarts (same convention as input_number.precool_high_threshold); set them once after first deploy.

Helper Type Range / default Purpose
input_boolean.alarm_audio_enabled bool Master on/off for all alarm audio
input_boolean.alarm_audio_trigger_enabled bool Enable trigger announcement
input_boolean.alarm_audio_exit_enabled bool Enable exit-delay announcement
input_boolean.alarm_audio_entry_enabled bool Enable entry-delay announcement
input_boolean.alarm_audio_armed_enabled bool Enable armed confirmation
input_boolean.alarm_audio_disarmed_enabled bool Enable disarmed confirmation
input_boolean.alarm_audio_failed_enabled bool Enable failed-to-arm announcement
input_number.alarm_audio_trigger_volume number 0–1, step 0.05 (0.8) Trigger volume
input_number.alarm_audio_exit_volume number 0–1 (0.6) Exit-delay volume
input_number.alarm_audio_entry_volume number 0–1 (0.8) Entry-delay volume
input_number.alarm_audio_armed_volume number 0–1 (0.3) Armed volume
input_number.alarm_audio_disarmed_volume number 0–1 (0.3) Disarmed volume
input_number.alarm_audio_failed_volume number 0–1 (0.6) Failed-to-arm volume
input_number.alarm_audio_reset_volume number 0–1 (0.4) Echo volume restored on disarm
input_number.alarm_audio_trigger_interval number 5–60 s, step 1 (12) Seconds between trigger repeats
input_button.alarm_audio_test_trigger button Preview trigger announcement
input_button.alarm_audio_test_exit button Preview exit-delay announcement
input_button.alarm_audio_test_entry button Preview entry-delay announcement
input_button.alarm_audio_test_armed button Preview armed announcement
input_button.alarm_audio_test_disarmed button Preview disarmed announcement
input_button.alarm_audio_test_failed button Preview failed-to-arm announcement

Test buttons: each input_button press fires an automation that calls script.alarm_audio_event(event, <canned sample message>, force=true) — plays that event's announcement on its assigned speakers at its current volume, regardless of enable state, with sample values for dynamic vars (e.g. "…60 seconds…"). This is the dial-in path: press, listen, adjust the slider, repeat — no real alarm needed.

Dashboard — separate YAML dashboard (NOT Command)

A new YAML-mode dashboard, distinct from the Command dashboard.

  • File: alarm_audio_dashboard.yaml (repo root, edited directly like the other YAML dashboards).
  • Registration: add under lovelace.dashboards in configuration.yaml — sidebar title "Alarm Audio", url_path: alarm-audio (must contain a hyphen or check_config fails and blocks deploy).
  • CI: add alarm_audio_dashboard.yaml to the deployable HA files list / ha_config changed-files matcher (deploy-ha job, full ha core restart), and to the dashboard entity-ref test in tests/conftest.py (now 4 YAML dashboards).

Layout — one entities card per event (its built-in title = the event name; these are standard card headers, not standalone title cards), each grouping that event's enable toggle + volume slider + test button, plus a top card for the master toggle, reset volume, and trigger interval. Plain entities cards (no custom components) keep it robust:

┌ Master ───────────────────────┐
│ ⏻ Alarm Audio (master)        │
│ 🔊 Reset volume        [0.40] │
│ ⏱ Trigger interval      [12s] │
└───────────────────────────────┘
┌ Trigger ──────────────────────┐   ┌ Exit delay ──────────────────┐
│ ⏻ Enabled                     │   │ ⏻ Enabled                    │
│ 🔊 Volume              [0.80] │   │ 🔊 Volume             [0.60] │
│ ▶ Test                        │   │ ▶ Test                       │
└───────────────────────────────┘   └──────────────────────────────┘
  … Entry delay / Armed / Disarmed / Failed to arm, same shape …

Integration with existing automations

  • Replace the single kitchen announce in automation #50 with the new repeating whole-house trigger loop. Keep #50's critical push to both phones + snapshot + persistent notification unchanged — only the audio action changes. No two competing trigger announcements.
  • §51 Night Lights Red Beacon (visual) untouched, runs in parallel.
  • Good Night / Good Morning routines unchanged — audio keys off the resulting state transition, so it also covers arming/disarming from the Ring app/keypad.

Failed-to-arm detection — risk & validation

Baseline signal: the armingdisarmed transition (exit-delay countdown aborted instead of reaching armed_*). Best-effort — a manual cancel during exit delay would also announce "could not arm." At implementation, verify ring-mqtt's behavior when arming with an open zone (stays disarmed / bypasses / exposes a fault attribute) and prefer a fault attribute if reliable. Otherwise the transition heuristic ships; a false positive on a rare manual cancel is low-cost.

Testing (pre-deploy suite)

  • Tier 2 (structural): unique id + alias on every new automation (including the 6 test-button automations).
  • Tier 3 (entity refs): the ~21 new input_* helpers don't exist in tests/fixtures/entities.txt until the instance restarts with the new config. Chicken-and-egg → add them to tests/fixtures/entities_allow.txt with a justifying comment; after first deploy, make snapshot and drop the allowlist entries. All 7 media_player.*_echo + alarm_control_panel.blairmont_alarm already exist. notify.alexa_media_* are services, not entity_ids (not checked).
  • New dashboard is entity-ref tested via conftest.py — its referenced helpers must resolve (covered by the allowlist above).
  • Tier 1 (check_config): helpers, both scripts, the automations, and the dashboard registration (hyphenated url_path) must pass container config check.

Docs (update in the same change — CLAUDE.md manual rule)

  • docs/notifications.md — the alarm audio alerts (what each says, when).
  • docs/security.md — whole-house alarm audio behavior + the Alarm Audio tuning dashboard.
  • docs/automations.md — §22 technical reference: the new automations, both scripts, the helpers.
  • CLAUDE.md — add the "Alarm Audio" dashboard to the dashboards table + YAML-dashboard deploy list; note the audio primitive/helpers under §22.

Deploy

Edit automations.yaml, scripts.yaml, configuration.yaml, alarm_audio_dashboard.yaml; make test; commit; push. CI restarts HA (configuration.yaml/scripts.yaml changed). Docs deploy via docs.yml.

Open questions for implementation (not blocking the spec)

  1. Is generic notify.alexa_media (with target:) registered here, or must we fan out per-device?
  2. ring-mqtt's real "arm with open zone" behavior — confirm the failed-to-arm signal.
  3. Does announce reliably respect a volume_set issued milliseconds prior, or is a short delay needed between them?