Light
One light.* entity: whether it’s on, how bright, and its warmth or colour when it has them.
Quick example
Section titled “Quick example”import { useHaLight } from "@dashhaus/core";
export function LightButton({ entityId }: { entityId: string }) { const { entity, actions } = useHaLight(entityId); return ( <button type="button" aria-pressed={entity.state === "on"} onClick={actions.onToggle}> {entity.name} {entity.state === "on" && entity.brightness != null ? `${entity.brightness}%` : entity.state} </button> );}The hook maps the raw light into a LightEntity and builds its actions. A tap calls light.toggle; the button changes when Home Assistant reports the new state.
Component
Section titled “Component”<HaLight> is useHaLight 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: HaLightResult) => ReactNode- Draws the hook's result. The component itself draws nothing.
useHaLight(entityId: string, options?: HaLightOptions): HaLightResultOne live light for any UI: the mapped entity, today's history and its service actions.
It reads the light, its area from Home Assistant’s registries, and today’s history from the recorder.
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.
history?LightHistory- 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”entityLightEntityhistoryLightHistoryactionsLightHandlers
entity: LightEntity
Section titled “entity: LightEntity”entityIdstring- The Home Assistant entity id.
namestring- Its name: the `name` option, else Home Assistant's friendly name, else its entity id.
area?string- Area name, shown under the light's name.
stateLightStatesupportsBrightnessboolean- True when the light supports brightness (HA color mode other than `onoff`).
brightness?number- Brightness in percent, 1-100 (HA `brightness` / 2.55). Absent when HA has not reported a reading.
supportsColorTemp?booleankelvin?number- Color temperature in Kelvin (HA `color_temp_kelvin`).
minKelvin?numbermaxKelvin?numbersupportsColor?boolean- True when the light takes a colour (an HA `hs`, `xy`, `rgb`, `rgbw` or `rgbww` colour mode).
hs?[number, number]- Its colour as HA `hs_color`: hue 0-360, saturation 0-100. Absent while off or showing a colour temperature.
lastChanged?string- Local time of the last state change, e.g. "10:31".
lastSeen?string- Local time the light was last reachable, shown while unavailable.
history: LightHistory
Section titled “history: LightHistory”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[]currentHournumber- Index of the current hour in `samples`.
onMinutesnumber- Minutes on today, including the current hour.
actions: LightHandlers
Section titled “actions: LightHandlers”onToggle() => void- Calls `light.toggle`.
onBrightnessChange(percent: number) => void- Calls `light.turn_on` with `brightness_pct`, rounded and held between 1 and 100. A light without brightness reports `unsupported`.
onKelvinChange?(kelvin: number) => void- Only for a light that takes a colour temperature. Calls `light.turn_on` with `color_temp_kelvin`; a value outside `minKelvin` to `maxKelvin` reports `invalid_input`.
onColorChange?(hs: [number, number]) => void- Only for a colour light. Calls `light.turn_on` with `hs_color`: hue 0 to 360, saturation 0 to 100.
Every action on an unavailable entity is reported to onActionError as unsupported and never sent. A light Home Assistant reports as unknown is not unavailable: its state is "unknown", and every action is sent.
Every light
Section titled “Every light”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 { HaLight, useHaSelector } from "@dashhaus/core";import type { HaSnapshot } from "@dashhaus/core";
/** Every light in the house, as one string, so the selector's value only changes when a light comes or goes. */const lightIds = (snapshot: HaSnapshot) => Object.keys(snapshot.entities).filter((id) => id.startsWith("light.")).sort().join(" ");
export function AllLights() { const ids = useHaSelector(lightIds).split(" ").filter(Boolean); return ( <ul> {ids.map((id) => ( <li key={id}> <HaLight entityId={id} loadHistory={false}> {({ entity, actions }) => ( <button type="button" aria-pressed={entity.state === "on"} onClick={actions.onToggle}> {entity.name} </button> )} </HaLight> </li> ))} </ul> );}Dim it and set its warmth
Section titled “Dim it and set its warmth”import { useHaLight } from "@dashhaus/core";
/** A brightness slider, and a warmth slider when the light takes a colour temperature. */export function Dimmer({ entityId }: { entityId: string }) { const { entity, actions } = useHaLight(entityId, { loadHistory: false }); const unavailable = entity.state === "unavailable";
return ( <fieldset disabled={unavailable}> <legend>{entity.name}</legend> {entity.supportsBrightness && ( <label> Brightness <input type="range" min={1} max={100} value={entity.brightness ?? 1} onChange={(event) => actions.onBrightnessChange(event.currentTarget.valueAsNumber)} /> </label> )} {actions.onKelvinChange && entity.minKelvin != null && entity.maxKelvin != null && ( <label> Warmth <input type="range" min={entity.minKelvin} max={entity.maxKelvin} step={100} value={entity.kelvin ?? entity.minKelvin} onChange={(event) => actions.onKelvinChange?.(event.currentTarget.valueAsNumber)} /> </label> )} </fieldset> );}onKelvinChange and onColorChange are only there when the light can use them, so test the handler, not the light’s name or model.
How long it’s been on today
Section titled “How long it’s been on today”import { useHaLight } from "@dashhaus/core";
/** How long a light has been on today, from the history the hook reads. */export function OnToday({ entityId }: { entityId: string }) { const { entity, history } = useHaLight(entityId); const hours = Math.floor(history.onMinutes / 60); const minutes = history.onMinutes % 60; return ( <p> {entity.name} has been on for {hours} h {minutes} min today. </p> );}history.onMinutes counts today’s minutes on, the current hour included. history.samples has one value per hour for a strip of your own.
In a skin
Section titled “In a skin”Orb’s HaLightTile and Field’s HaLightModule are useHaLight above each skin’s card. They take the hook’s
options as props, plus their own:
import { HaLightTile } from "@dashhaus/orb";import { HaLightModule } from "@dashhaus/field";
export function OrbLight() { return <HaLightTile entityId="light.living_room" footprint="featured" />;}
export function FieldLight() { return <HaLightModule entityId="light.living_room" footprint="compact" />;}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. The orb keeps its tap and hold actions. 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 Home Assistant entity id.
footprint?FieldFootprintdefaultOpen?boolean- Open the detail panel on first render (stories).