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. A media_player 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 MediaTitle() { const player = useHaEntity("media_player.living_room"); if (!player || player.state === "unavailable") return null; const title = player.attributes.media_title; return <p>Now playing: {typeof title === "string" ? title : player.state}</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.