Alarm
One alarm_control_panel.* entity: its state, the modes it arms to, whether it wants a code, and its exit and entry delays.
Quick example
Section titled “Quick example”import { useState } from "react";import { useHaAlarm } from "@dashhaus/core";
/** * Arm away, with the code when the panel asks for one. The handler answers false for a wrong code and rejects when * the call didn't go through, so each outcome gets its own words. */export function ArmAway({ entityId }: { entityId: string }) { const { entity, actions } = useHaAlarm(entityId, { loadHistory: false }); const [code, setCode] = useState(""); const [answer, setAnswer] = useState(""); const needsCode = entity.codeFormat != null && entity.codeArmRequired !== false;
async function arm() { try { const accepted = await actions.onArm("away", needsCode ? code : undefined); setAnswer(accepted ? "Arming…" : "Wrong code."); } catch { setAnswer("Home Assistant didn't take it. Try again."); } setCode(""); }
if (!entity.modes.includes("away")) return null; return ( <form onSubmit={(event) => { event.preventDefault(); void arm(); }} > {needsCode && ( <label> Code <input type="password" inputMode={entity.codeFormat === "number" ? "numeric" : "text"} autoComplete="off" value={code} onChange={(event) => setCode(event.currentTarget.value)} /> </label> )} <button type="submit" disabled={entity.state !== "disarmed"}> Arm away </button> <p role="status">{answer}</p> </form> );}Arming and disarming answer, unlike the other actions: true when Home Assistant took the call, false for a wrong code, and a rejection when the call didn’t go through. The store never keeps a call with a code in it, so a failed disarm is never offered again with the code.
Component
Section titled “Component”<HaAlarm> is useHaAlarm as a component that draws nothing of its own. It takes every option of the hook as a
prop, and hands the hook’s result to its child function. Use it where hooks can’t go, such as one per item of a list.
entityIdstring- The Home Assistant entity id.
children(live: HaAlarmResult) => ReactNode- Draws the hook's result. The component itself draws nothing.
useHaAlarm(entityId: string, options?: HaAlarmOptions): HaAlarmResultOne live alarm panel for any UI: the mapped entity, today's history and its arm and disarm actions.
It reads the panel, its area and today’s history. Home Assistant’s alarm entity has no delay length, so exitDelay and entryDelay give it (Alarmo’s delay attribute wins when it reports one). openSensors and triggeredBy are yours to compose from your door and window sensors.
Options
Section titled “Options”name?string- Overrides Home Assistant's friendly name.
area?string- Overrides the area Home Assistant's registries resolve for the entity.
lastSeen?string- Overrides when the entity was last reachable, shown while it is unavailable.
exitDelay?numberentryDelay?numberopenSensors?string[]triggeredBy?string- What set off a pending/triggered state; Home Assistant has no such attribute, so the host composes it.
history?AlarmHistory- Host-owned history override; when omitted, today's history is read from Home Assistant.
loadHistory?boolean- Set false to skip the Home Assistant history read.
onActionError?(error: HaActionError) => void- Called with an HaActionError when an action is refused or fails.
Returns
Section titled “Returns”entityAlarmEntityhistoryAlarmHistoryactionsAlarmHandlers
entity: AlarmEntity
Section titled “entity: AlarmEntity”entityIdstring- The Home Assistant entity id.
namestring- Its name: the `name` option, else Home Assistant's friendly name, else its entity id.
area?string- Its area: the `area` option, else the one Home Assistant's registries resolve; none when neither has one.
stateAlarmStatemodesAlarmMode[]- The arm modes this panel supports, in the order to show them.
codeFormat?"number" | "text" | null- Whether a code is asked for, and what kind (HA code_format). Null or omitted: no code.
codeArmRequired?boolean- HA code_arm_required: the code is needed to arm too, not only to disarm. Home Assistant's default is true.
armingTo?AlarmMode- While arming: the mode it is arming to (HA's manual alarm and Alarmo expose it as next_state).
delay?number- Length of the current exit or entry delay, in seconds. Home Assistant's alarm entity doesn't expose it: Alarmo has it as the `delay` attribute; for the manual alarm it's your arming_time / delay_time. With `changedAt`, the card counts down. Without it, the slot shows the delay with no timer.
changedAt?number- When the current state started, in ms since the epoch (HA last_changed).
changedBy?string- HA changed_by.
lastChanged?stringtriggeredBy?string- Pending or triggered: what set it off.
openSensors?string[]- Doors and windows open right now (from their binary sensors). Arming would bypass them.
lastSeen?string- While it is unavailable, when it was last reachable: the `lastSeen` option, else what Home Assistant reports.
history: AlarmHistory
Section titled “history: AlarmHistory”Today, from Home Assistant’s recorder, kept up to date by every change since. Pass history to supply your own, or
loadHistory={false} to skip the read.
samplesHourSample[]- 1 for hours it was armed, 0 disarmed, "unavailable", null for hours ahead.
currentHournumberarmedMinutesnumber- Minutes armed today.
actions: AlarmHandlers
Section titled “actions: AlarmHandlers”onArm(mode: AlarmMode, code?: string | undefined) => Promise<boolean>- Calls `alarm_control_panel.alarm_arm_<mode>` (`home`, `away`, `night`, `vacation`, `custom_bypass`) with the code when given. A mode the panel doesn't support rejects with `unsupported`.
onDisarm(code?: string | undefined) => Promise<boolean>- Calls `alarm_control_panel.alarm_disarm` with the code when given.
A failure other than a wrong code also goes to onActionError before the promise rejects.
Every alarm panel
Section titled “Every alarm panel”Hooks can’t be called in a loop, so a list renders one headless component per entity. The selector returns a string: useHaSelector re-renders only when its value changes, and a string of ids changes only when an entity comes or goes. Lists skip each item’s history read with loadHistory={false}.
import { HaAlarm, useHaSelector } from "@dashhaus/core";import type { HaSnapshot } from "@dashhaus/core";
const alarmIds = (snapshot: HaSnapshot) => Object.keys(snapshot.entities).filter((id) => id.startsWith("alarm_control_panel.")).sort().join(" ");
export function AllAlarms() { const ids = useHaSelector(alarmIds).split(" ").filter(Boolean); return ( <ul> {ids.map((id) => ( <li key={id}> <HaAlarm entityId={id} loadHistory={false}> {({ entity }) => ( <> {entity.name}: {entity.state} {entity.openSensors?.length ? ` (open: ${entity.openSensors.join(", ")})` : ""} </> )} </HaAlarm> </li> ))} </ul> );}Show the countdown
Section titled “Show the countdown”import { useHaAlarm } from "@dashhaus/core";import { useDelayLeft } from "@dashhaus/core/hooks";
const WORDS: Record<string, string> = { disarmed: "Disarmed", arming: "Arming", pending: "Entry delay", triggered: "Triggered", unavailable: "Unavailable",};
/** The panel's state, with the exit or entry delay counting down when the host knows its length. */export function AlarmState({ entityId }: { entityId: string }) { const { entity } = useHaAlarm(entityId, { exitDelay: 30, entryDelay: 30 }); const counting = entity.state === "arming" || entity.state === "pending"; const left = useDelayLeft(entity.delay, entity.changedAt, counting); return ( <p role="status"> {entity.name}: {WORDS[entity.state] ?? entity.state.replace("armed_", "Armed ")} {left != null && `, ${left} s`} {entity.triggeredBy && ` by ${entity.triggeredBy}`} </p> );}useDelayLeft from @dashhaus/core/hooks counts the delay down from changedAt, the moment the state began, and catches up at once when a sleeping tablet wakes.
In a skin
Section titled “In a skin”Orb’s HaAlarmTile and Field’s HaAlarmModule are useHaAlarm above each skin’s card. They take the hook’s
options as props, plus their own:
import { HaAlarmTile } from "@dashhaus/orb";import { HaAlarmModule } from "@dashhaus/field";
export function OrbAlarm() { return <HaAlarmTile entityId="alarm_control_panel.house" exitDelay={30} entryDelay={30} />;}
/** A panel Home Assistant can read but not control: the state and today, with nothing to arm. */export function FieldAlarm() { return <HaAlarmModule entityId="alarm_control_panel.ring" readOnly />;}entityIdstring- The Home Assistant entity id.
showControl?boolean- Compact only. false removes the inline control, shows the value in its place, and makes the row open the detail sheet. Default true.
readOnly?boolean- View only: show the alarm's state, countdowns and history, with nothing to arm, disarm or type a code into. For panels Home Assistant can read but not control (a Ring Alarm without a subscription, say). Default: on when neither onArm nor onDisarm is given.
footprint?CardFootprint- Card footprint. The skin's stylesheet sets the heights; the three names are the shared vocabulary.
defaultOpen?booleandefaultAction?NonNullable<{ kind: "arm"; mode: AlarmMode; } | { kind: "disarm"; }>
entityIdstring- The Home Assistant entity id.
readOnly?boolean- View only: the alarm as a reading, its state, delays and today, with nothing to arm, disarm or type a code into. For panels Home Assistant can read but not control (a Ring Alarm without a subscription). Default: on when neither onArm nor onDisarm is given.
footprint?FieldFootprintdefaultOpen?boolean- Open the detail panel on first render (stories).