Owlet
One Owlet sock, from the custom owlet integration: its sleep state with its heart rate, oxygen, charging and alert sensors, and the base station’s switch.
Quick example
Section titled “Quick example”import { useHaOwlet } from "@dashhaus/core";
/** * A sock's readings and alerts. The heart rate, oxygen, charging and alert sensors are found beside the sleep state * sensor; `live` turns false once no reading has changed for a while, so old figures aren't read as current. */export function Vitals() { const { entity } = useHaOwlet("sensor.owlet_sock_ac000w0123_sleep_state", { name: "Nova" }); if (entity.charging) return <p>{entity.name}'s sock is charging.</p>; return ( <div> <p> {entity.name}: {entity.state.replace("_", " ")} {entity.heartRate != null && `, ${entity.heartRate} bpm`} {entity.oxygen != null && `, ${entity.oxygen}% O₂`} {entity.live === false && " (readings stopped)"} </p> {entity.alerts?.length ? <p role="alert">{entity.alerts.join(", ").replaceAll("_", " ")}</p> : null} </div> );}The entity id is the sleep state sensor. The heart rate, oxygen, charging and ten alert sensors and the base station switch are found beside it by the integration’s names; give their ids when yours are named otherwise.
Component
Section titled “Component”<HaOwlet> is useHaOwlet 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: HaOwletResult) => ReactNode- Draws the hook's result. The component itself draws nothing.
useHaOwlet(entityId: string, options?: HaOwletOptions): HaOwletResultOne live Owlet sock for any UI: composes the sleep state, readings, charging and alert sensors. Its one action is the base station's switch; the readings are read-only.
It reads the sleep state, every companion, its area and today’s history of sleep, heart rate and oxygen. staleAfterMinutes (default 3) is how long without a changed reading before live turns false.
Options
Section titled “Options”heartRateEntityId?string- Default: the integration's `…_heart_rate` beside the sleep state sensor.
oxygenEntityId?string- Default: the integration's `…_o2_saturation` beside the sleep state sensor.
chargingEntityId?string- Default: the integration's `binary_sensor.…_charging`.
baseEntityId?string- Default: the integration's `switch.…_base_station_on`, switched from the sheet.
alertEntityIds?Partial<Record<OwletAlert, string>>- Per alert, the binary sensor to read. Default: the integration's (`binary_sensor.…_low_oxygen_alert` …).
name?string- Usually the baby's name. Default: the sock's device 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.
history?OwletHistory- 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.
staleAfterMinutes?number- Minutes without a changed reading before the card warns that readings stopped. Default 3.
onActionError?(error: HaActionError) => void- Called with an HaActionError when an action is refused or fails.
Returns
Section titled “Returns”entityOwletEntityhistoryOwletHistoryactionsOwletHandlers
entity: OwletEntity
Section titled “entity: OwletEntity”entityIdstring- The sleep state sensor, `sensor.*_sleep_state`.
namestring- Usually the baby's name.
area?string- Its area: the `area` option, else the one Home Assistant's registries resolve; none when neither has one.
state"unavailable" | SleepState- "unavailable" while the sock charges, is off, or can't tell yet.
heartRate?number- Beats per minute; omitted while there is no reading.
oxygen?number- Oxygen saturation, 0-100 %; omitted while there is no reading.
live?boolean- Whether readings are still arriving: true while one has changed recently, false once none has for a while (the figures shown are then old). Omitted when the host can't tell.
alerts?OwletAlert[]- The alerts sounding now, in any order.
vitalsSince?number- When the vitals alerts sounding now started, in ms since the epoch: the first of them to go on. Omitted while none sounds, or when the host can't tell.
charging?boolean- On its charger: the reason it reports nothing.
baseOn?boolean- The base station's switch: false while it is off and sounds no alarm (the sock may still send readings). Omitted when the host has no such switch or can't tell.
lastUpdated?string- When the newest reading arrived ("11:54").
lastSeen?string- While it is unavailable, when it was last reachable: the `lastSeen` option, else what Home Assistant reports.
history: OwletHistory
Section titled “history: OwletHistory”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.
sleepHourSample[]- Per hour: 1 deep sleep, 0.5 light sleep, 0 awake, "unavailable", null for hours ahead.
heartRate?Reading[]oxygen?Reading[]currentHournumberasleepMinutesnumber- Minutes asleep today (light or deep), including the current hour.
actions: OwletHandlers
Section titled “actions: OwletHandlers”onBaseToggle?() => void- Only when a base station switch is found and reports on or off. Calls `switch.turn_on` or `switch.turn_off` for the state shown, never `switch.toggle`, so a repeated tap on a stale screen repeats the request instead of undoing it.
The readings are read-only; the base station is the one thing to switch.
Every sock
Section titled “Every sock”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 { HaOwlet, useHaSelector } from "@dashhaus/core";import type { HaSnapshot } from "@dashhaus/core";
/** Every sock, by its sleep state sensor, the way the Lovelace cards find them. */const sockIds = (snapshot: HaSnapshot) => Object.keys(snapshot.entities).filter((id) => /^sensor\.\w+_sleep_state$/.test(id)).sort().join(" ");
export function AllSocks() { const ids = useHaSelector(sockIds).split(" ").filter(Boolean); return ( <ul> {ids.map((id) => ( <li key={id}> <HaOwlet entityId={id} loadHistory={false}> {({ entity }) => ( <> {entity.name}: {entity.alerts?.length ? "alert" : entity.state.replace("_", " ")} </> )} </HaOwlet> </li> ))} </ul> );}Switch the base station
Section titled “Switch the base station”import { useHaOwlet } from "@dashhaus/core";
/** The one action: the base station's switch. It is absent when there is no base station switch to read. */export function BaseStation({ entityId }: { entityId: string }) { const { entity, actions } = useHaOwlet(entityId, { loadHistory: false }); if (!actions.onBaseToggle) return null; return ( <button type="button" role="switch" aria-checked={entity.baseOn === true} onClick={actions.onBaseToggle}> Base station {entity.baseOn ? "on" : "off: no alarm sounds"} </button> );}With the base station off, the sock may still send readings but no alarm sounds, so say that on the control.
In a skin
Section titled “In a skin”Orb’s HaOwletTile and Field’s HaOwletModule are useHaOwlet above each skin’s card. They take the hook’s
options as props, plus their own:
import { HaOwletTile } from "@dashhaus/orb";import { HaOwletModule } from "@dashhaus/field";
export function OrbSock() { return <HaOwletTile entityId="sensor.owlet_sock_ac000w0123_sleep_state" name="Nova" />;}
/** A sock whose sensors aren't named after the integration's: name its companions. */export function FieldSock() { return ( <HaOwletModule entityId="sensor.nova" heartRateEntityId="sensor.nova_bpm" oxygenEntityId="sensor.nova_spo2" chargingEntityId="binary_sensor.nova_dock" baseEntityId="switch.nova_base" alertEntityIds={{ low_oxygen: "binary_sensor.nova_spo2_low", sock_off: "binary_sensor.nova_off" }} /> );}entityIdstring- The sock's sleep state sensor, `sensor.owlet_sock_<serial>_sleep_state` (custom `owlet` integration).
showControl?boolean- Compact only. false removes the inline control, shows the value in its place, and makes the row open the details. Default true.
footprint?CardFootprint- Card footprint. The skin's stylesheet sets the heights; the three names are the shared vocabulary.
defaultOpen?booleandefaultAction?undefined
entityIdstring- The sock's sleep state sensor, `sensor.*_sleep_state`; its readings, alerts and base station are found beside it.
footprint?FieldFootprintdefaultOpen?boolean- Open the detail panel on first render (stories).