Skip to content

Camera

One camera.* entity: whether it is idle, recording or streaming, and its still, the picture Home Assistant serves for it. Read-only, with no history.

import { useHaCamera } from "@dashhaus/core";
import { usePicture } from "@dashhaus/core/hooks";
import { cameraWord, stillNote } from "@dashhaus/core/entities/camera/words";
/** The front door's still, refreshed every 10 seconds while it is on screen, with what the camera is doing. */
export function FrontDoor() {
const { entity } = useHaCamera("camera.front_door");
const still = usePicture<HTMLDivElement>(entity.picture, { refresh: 10_000 });
return (
<figure>
<div ref={still.ref} style={{ aspectRatio: "16 / 9", background: "#ddd" }}>
{still.src && <img src={still.src} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />}
</div>
<figcaption>{entity.name}: {cameraWord(entity)} {stillNote(entity, still) ?? ""}</figcaption>
</figure>
);
}

The still is Home Assistant’s own entity_picture, a signed address whose token Home Assistant changes every few minutes; the hook follows it, and gives none while the camera is unavailable, so a past frame never reads as now. usePicture loads it only once its box is on screen and the page is visible, refreshes it only while both stay true, and keeps the last frame up while the next one loads. Nothing is fetched in the background.

<HaCamera> is useHaCamera 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: HaCameraResult) => ReactNode
Draws the hook's result. The component itself draws nothing.
useHaCamera(entityId: string, options?: HaCameraOptions): HaCameraResult

One live camera for any UI: its state, and its still from Home Assistant's own signed `entity_picture`, resolved by the connection (`useHaPicture`) and dropped while it is unavailable. Draw the still with `usePicture` (`@dashhaus/core/hooks`), which loads it only while it is seen; open the live stream with `useHaMoreInfo`. It sends no services and reads no history.

It reads the camera, its area and its still. Pass still: false to have no still at all: nothing is ever requested.

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.
still?boolean
False: no still at all, for privacy or cost (the YAML's `still: false`, or a dashboard's `stills: false`). The model then carries no picture, so nothing is ever requested, rather than a loaded one being hidden.
entityCameraEntity
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: "Porch".
stateCameraState
stateText?string
An `other` state in words, as Home Assistant reports it: `motion_alert` → "Motion alert".
picture?string
Its still, a URL the page can load: Home Assistant's signed `entity_picture`, resolved by the connection. None while it is unavailable, so a past frame never reads as now.
brand?string
Its maker, where the integration says (HA `brand`): "Reolink".
model?string
Its model, where the integration says (HA `model_name`): "Video Doorbell".
motionDetection?boolean
Whether it is watching for motion (HA `motion_detection`), where it says.
lastChanged?string
When its state last changed, formatted for the card.
lastSeen?string
While unavailable, when it was last reachable.

Dashhaus has no stream player of its own. useHaMoreInfo opens Home Assistant’s own more-info dialog for the camera, which plays its live stream; outside Home Assistant there is none, and it gives nothing, so open your own details instead.

import { useHaMoreInfo } from "@dashhaus/core";
/** Inside Home Assistant, Home Assistant's own live stream; elsewhere, your own details. */
export function WatchNursery({ openDetails }: { openDetails: () => void }) {
const openLive = useHaMoreInfo("camera.nursery");
return <button type="button" onClick={openLive ?? openDetails}>Watch the nursery</button>;
}

Orb’s HaCameraTile and Field’s HaCameraModule are useHaCamera above each skin’s card. Each says the state in words, draws the still beside them (Orb in a quiet well, Field as a window in its screen), says when a still is from when it stops refreshing, opens its details from the name, and opens the live stream from the still and from a Live view in the details, which is there even with stills off. A compact row never shows a still. They take the hook’s options as props, plus their own: refresh, in seconds, and paused, which holds the still until the card comes back on screen or the panel wakes.

import { HaCameraTile } from "@dashhaus/orb";
import { HaCameraModule } from "@dashhaus/field";
/** The front door, its still refreshed every 5 seconds while it is on screen. */
export function OrbDoorbell() {
return <HaCameraTile entityId="camera.front_door" refresh={5} />;
}
/** The nursery on a panel at night: the still holds until the panel wakes, and says when it is from. */
export function FieldNursery() {
return <HaCameraModule entityId="camera.nursery" paused />;
}
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 details. Default true.
footprint?CardFootprint
Card footprint. The skin's stylesheet sets the heights; the three names are the shared vocabulary.
defaultOpen?boolean
defaultAction?undefined
refresh?number
How often the still refreshes while it is seen, in seconds. Default 10.
paused?boolean
Hold the still instead of refreshing it (a `quiet` dashboard): it loads when it comes on screen and when the panel wakes, and the card says when it is from.

Every state of both is in Storybook: Orb and Field.