Raw state and selectors
The entity hooks cover the kinds Dashhaus models. For anything else, read the store directly. The store holds
Home Assistant’s raw state, every entity as { entity_id, state, attributes, last_changed, … }, and keeps an
unchanged entity’s object the same across updates. The hooks below lean on that: each re-renders only when what it
read has changed.
One entity, raw
Section titled “One entity, raw”useHaEntity returns one entity as Home Assistant sent it, or undefined until it arrives. An update has no
model here, so read its attributes yourself:
import { useHaEntity } from "@dashhaus/core";
/** Any entity, raw: Home Assistant's state and attributes as they arrive, for a domain with no model yet. */export function FirmwareNotice() { const update = useHaEntity("update.living_room_tv_firmware"); if (!update || update.state !== "on") return null; const version = update.attributes.latest_version; return <p>New firmware: {typeof version === "string" ? version : "available"}</p>;}Attributes are unknown: check their type before you use them. useHaEntities does the same for a list of ids, and
keeps the array the same until one of those entities changes, so a card made of several entities re-renders for its
own and not for the rest of the house.
entity_idstringstatestringattributesHassAttributeslast_changed?stringlast_updated?stringlast_reported?stringcontext?unknown
One value from the whole house
Section titled “One value from the whole house”useHaSelector runs a function over the whole snapshot and re-renders only when its result changes (compared with
Object.is). Return a number, a string, a boolean, or an object the store already holds; a new array or object every
time would re-render on every change in the house.
import { useHaSelector } from "@dashhaus/core";import type { HaSnapshot } from "@dashhaus/core";
/** A number re-renders only when it changes, so this badge ignores every other change in the house. */const lightsOn = (snapshot: HaSnapshot) => Object.values(snapshot.entities).filter((entity) => entity.entity_id.startsWith("light.") && entity.state === "on").length;
export function LightsOnBadge() { const count = useHaSelector(lightsOn); return <span>{count === 1 ? "1 light on" : `${count} lights on`}</span>;}The entity pages’ “every entity” examples use the same rule: they select the ids as one string, which only changes when an entity comes or goes.
The snapshot
Section titled “The snapshot”statusHaConnectionStatusentitiesHassEntitieserror?unknownlastActionError?HaActionErrorgenerationnumber- Increments whenever the connection is lost, recovers, fails, or the store connects or disconnects.
useHaSnapshot returns all of it and re-renders on every change in the house: for a diagnostics page, not for a
dashboard.
The store
Section titled “The store”useHaStore returns the store. Calling a service through it is what the entity hooks’ actions do:
getSnapshot() => HaSnapshotsubscribe(listener: () => void) => () => voidcallService(domain: string, service: string, serviceData?: Record<string, unknown> | undefined, options?: HaServiceCallOptions | undefined) => Promise<unknown>query(domain: string, service: string, serviceData?: Record<string, unknown> | undefined, options?: HaServiceCallOptions | undefined) => Promise<unknown>- Background read-only request that stays out of action ordering and `lastActionError`.
callApi(type: string, payload?: Record<string, unknown> | undefined) => Promise<unknown>- Generic websocket command (e.g. recorder history reads) that stays out of action ordering and `lastActionError`.
callService is an action: it is ordered with the others, and a failure becomes the store’s lastActionError.
query and callApi are background reads that never do either. In a component, a read belongs in
useHaRequest, which shares it and drops a stale answer.