ADR 0052 — Daemon-level alarm MQTT topics (extends ADR 0011)¶
- Status: Accepted
- Date: 2026-07-15
- Related: ADR 0011 — MQTT topic & payload architecture, ADR 0002 — multi-CCU first class
Context¶
The alarm engine's areas (notes/concepts/alarm-concept.md §14) are daemon-level objects: an area's sensors and outputs reference (central_name, DataPointKey) pairs, and an area routinely spans more than one configured CCU — the common case for anything beyond a single-CCU household. ADR 0011 fixed every state and command topic to carry a <central> segment immediately below <base>, so several CCUs can share one broker namespace without collision. An alarm area has no single owning CCU to put in that segment; forcing it onto one central's namespace, or publishing it once per central, would misrepresent the domain model and break as soon as an area's sensors span two CCUs.
The only existing precedent for a topic without <central> is the read-only <base>/bridge/status / <base>/bridge/health pair (ADR 0011, topic hierarchy) — genuinely daemon-level facts that predate any CCU connection. The alarm subtree (notes/concepts/alarm-concept.md §13.3) is the first writable, domain-carrying use of that same exception.
Decision¶
Alarm topics live directly under <base>/alarm/<area>/, omitting the <central> segment, mirroring the bridge/* precedent: state (retained), availability (retained), event (JSON, not retained), and set (command). Wire shapes, the HA/JSON command vocabulary, and the loom-specific SILENCE extension are pinned in docs/mqtt-topic-schema.md, not restated here.
<area> is either a configured area id or the reserved pseudo-area master, published whenever 2 or more areas are configured. Master topics aggregate every area: any triggered wins, else any pending, else any arming, else all-disarmed, else the shared mode token if every armed area agrees, or armed_away when mixed. Master arm is best-effort — each area arms independently and a failure surfaces as a per-area FAILED_TO_ARM detail rather than failing the whole request, per the lean recorded in notes/concepts/alarm-concept.md §18 item 5 ("matches G5"). Master disarm disarms every area unconditionally.
HA MQTT Discovery still advertises one alarm_control_panel entity per area (plus master) the normal way — this ADR fixes the topic shape only, not the discovery mechanism, which stays exactly as ADR 0011 describes it for every other component.
Consequences¶
-
Interim security posture: until the alarm-codes feature ships,
code_arm_required/code_disarm_requiredarefalseand the JSONcodefield is accepted but not validated — broker authentication and topic ACLs are the only gate onARM_*/DISARM/SILENCEfor MQTT publishers. Operators who treat the broker as untrusted should restrict write access to<base>/alarm/#until codes land (the concept's §16 "cannot disarm a code-protected area" reassurance applies only from that point on). -
docs/mqtt-topic-schema.mddocuments<base>/alarm/<area>/*as a second explicit exception to the "every topic carries<central>" rule, alongsidebridge/*. - Subscribers that blanket-wildcard
<base>/+/...expecting the second segment to always be a central name must special-casealarm— no different from the existingbridge/*exception they already had to handle. - No change to the per-CCU topic tree (
<base>/<central>/...); this ADR only adds the daemon-levelalarmsubtree next to the existing daemon-levelbridgesubtree. - The
masterpseudo-area is a read/write convenience, not a new domain object — the alarm engine still applies commands per real area; the master command handler fans a request out and never becomes an area of its own in the store schema.