Sound
A sound machine’s sound: the switch.* that plays it, and the volume number.* beside it.
Quick example
Section titled “Quick example”import { useHaSound } from "@dashhaus/core";
/** A sound machine: its sound's switch, and its volume number found beside it. */export function SoundMachine({ entityId }: { entityId: string }) { const { entity, actions } = useHaSound(entityId); const { onVolumeChange } = actions; return ( <fieldset disabled={entity.state === "unavailable"}> <legend>{entity.name}</legend> <button type="button" aria-pressed={entity.state === "on"} onClick={actions.onToggle}> {entity.state === "on" ? "Playing" : "Off"} </button> {onVolumeChange && ( <label> Volume <input type="range" min={0} max={100} value={entity.volume ?? 0} onChange={(event) => onVolumeChange(event.currentTarget.valueAsNumber)} /> </label> )} </fieldset> );}Without volumeEntityId, the hook looks beside the switch: for switch.nursery_music it tries number.nursery_music_volume, then number.nursery_volume. The volume is a percent of that number’s own range.
Component
Section titled “Component”<HaSound> is useHaSound 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: HaSoundResult) => ReactNode- Draws the hook's result. The component itself draws nothing.
useHaSound(entityId: string, options?: HaSoundOptions): HaSoundResultOne live sound machine for any UI: the switch and its volume mapped together, today's history and its actions.
It reads the switch, the volume number, its area and today’s history.
Options
Section titled “Options”volumeEntityId?string- The volume `number.*`. Default: `number.<device>_music_volume` or `number.<device>_volume` beside the switch.
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.
history?SoundHistory- 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”entitySoundEntityhistorySoundHistoryactionsSoundHandlers
entity: SoundEntity
Section titled “entity: SoundEntity”entityIdstring- The Home Assistant entity id.
namestring- Its name: the `name` option, else Home Assistant's friendly name, else its entity id.
area?string- Where it is: "Bedroom".
state"unavailable" | "off" | "on"volume?number- Volume as a percent of the device's range, 0-100. Absent without a volume entity or a reading.
lastChanged?stringlastSeen?string- While it is unavailable, when it was last reachable: the `lastSeen` option, else what Home Assistant reports.
history: SoundHistory
Section titled “history: SoundHistory”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 on, 0 off, "unavailable", null for hours ahead. Fractions for part of an hour.
currentHournumberonMinutesnumber- Minutes on today, including the current hour.
actions: SoundHandlers
Section titled “actions: SoundHandlers”onToggle() => void- Calls `switch.toggle`.
onVolumeChange?(percent: number) => void- Only when a volume number with a usable range is found. Calls `number.set_value`, turning the percent back into the number's range and step. Setting the volume never switches the sound on or off.
Every action on an unavailable entity is reported to onActionError as unsupported and never sent.
Every sound
Section titled “Every sound”A switch doesn’t say it plays a sound, so there’s nothing in the house to find them by: list them yourself. The Lovelace cards ask for the same thing, with card: sound on the switch.
import { HaSound } from "@dashhaus/core";
/** * A sound is a switch, and nothing in a switch says it plays a sound, so the list is yours to name. The volume * entity is found beside each one unless you give it. */const SOUNDS = [ { entityId: "switch.nursery_music" }, { entityId: "switch.bedroom_noise", volumeEntityId: "number.bedroom_noise_level" },];
export function AllSounds() { return ( <ul> {SOUNDS.map((sound) => ( <li key={sound.entityId}> <HaSound {...sound} loadHistory={false}> {({ entity, actions }) => ( <button type="button" aria-pressed={entity.state === "on"} onClick={actions.onToggle}> {entity.name} {entity.volume == null ? "" : `, volume ${entity.volume}%`} </button> )} </HaSound> </li> ))} </ul> );}In a skin
Section titled “In a skin”Orb’s HaSoundTile and Field’s HaSoundModule are useHaSound above each skin’s card. They take the hook’s
options as props, plus their own:
import { HaSoundTile } from "@dashhaus/orb";import { HaSoundModule } from "@dashhaus/field";
export function OrbSound() { return <HaSoundTile entityId="switch.nursery_music" footprint="compact" />;}
export function FieldSound() { return <HaSoundModule entityId="switch.nursery_music" volumeEntityId="number.nursery_volume" />;}entityIdstring- The sound's `switch.*` (the Tuya integration's `switch.<device>_music`).
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 sound's `switch.*` (the Tuya integration's `switch.<device>_music`).
footprint?FieldFootprintdefaultOpen?boolean- Open the detail panel on first render (stories).