Connecting and auth
The packages never log in. They have no login form, store no token and refresh none: whoever opens the connection owns it, and hands the store a transport.
In Home Assistant
Section titled “In Home Assistant”A skin’s Lovelace card uses the frontend’s own authenticated connection: the bundle carries no credentials, the card never closes the frontend’s connection, and there is nothing to set up.
In an app of your own
Section titled “In an app of your own”Get an Auth from home-assistant-js-websocket, through Home Assistant’s OAuth flow (getAuth) or a long-lived token
your app manages, and open the connection with connectHomeAssistant:
import { createRoot } from "react-dom/client";import { getAuth } from "home-assistant-js-websocket";import { HaProvider, createHaStore } from "@dashhaus/core";import { connectHomeAssistant } from "@dashhaus/core/websocket";import { App } from "./App";
// Your app owns the login: here, Home Assistant's own OAuth flow through home-assistant-js-websocket.const auth = await getAuth({ hassUrl: "http://homeassistant.local:8123" });const transport = await connectHomeAssistant(auth);const store = createHaStore(transport);
createRoot(document.getElementById("root")!).render( <HaProvider store={store}> <App /> </HaProvider>,);connectHomeAssistant(auth: Auth): Promise<HomeAssistantTransport>Open an official Home Assistant websocket connection from host-owned auth.
createHomeAssistantTransport(connection: Connection): HomeAssistantTransportAdapt an already-authenticated official Home Assistant connection to the store's transport contract. The library reconnects with backoff and resubscribes `subscribe_entities` on its own; this adapter only forwards its `ready` / `disconnected` / `reconnect-error` events.
createHomeAssistantTransport wraps a Connection you already have. The transport is yours: call
transport.close() when you’re done. HaProvider connects and disconnects the store, but never closes the transport.
@dashhaus/core/websocket is the only module that imports home-assistant-js-websocket.
On a kiosk or a wall panel
Section titled “On a kiosk or a wall panel”Keep Home Assistant’s credentials off the browser: run a small gateway on a server you trust, and give the page a transport that talks to it. The gateway should allowlist the entities, services and service fields the panel may use, refuse anything else, strip credentials and alarm codes from what it passes on, cap the size and rate of requests, and never log tokens.
Your own transport
Section titled “Your own transport”A transport is four functions. Anything that implements them can feed the store: a gateway, a recording, a test.
subscribeEntities(listener: EntityListener) => Unsubscribe- Deliver the full entity map now and after every change, keeping unchanged entities' object identity. The transport keeps this subscription alive across its own reconnects.
subscribeConnection?(listener: ConnectionStatusListener) => Unsubscribe- Report loss ("disconnected"), recovery ("connected") and a terminal failure ("error").
callService(domain: string, service: string, serviceData?: Record<string, unknown> | undefined, options?: HaServiceCallOptions | undefined) => Promise<unknown>sendMessage?(type: string, payload?: Record<string, unknown> | undefined) => Promise<unknown>- Generic websocket command (e.g. history reads). Optional so injected demo transports stay valid.
createHaStore(transport: HaTransport, initialEntities?: HassEntities): HaStoreCreate one shared raw-entity store for one Home Assistant transport. The transport owns the socket: it keeps the entity subscription alive across its own reconnects and reports loss and recovery through `subscribeConnection`. The store fans snapshots out to React, clears stale entities while the connection is down, and advances `generation` on every loss and recovery so reads and actions started under an older connection are reported as having an unknown outcome. `initialEntities` is a complete snapshot the host already has, such as Home Assistant's `hass.states`. With it, the store is connected from the first frame instead of waiting for its own subscription's snapshot; the subscription still replaces it with the live snapshot when that arrives. A reconnect has no seed and waits for the next snapshot, as before.
initialEntities is a complete set of states you already hold, such as the Lovelace frontend’s hass.states: the
store starts connected from it, and the live subscription replaces it when it arrives.