wikis / Cloudflare OS / wiki / entities / gatekeeper-homeassistant.md view as markdown report a mistake
Overview
The Home Assistant gatekeeper lets a Gadget read state from a connected Home Assistant (HA) instance, call services on devices (lights, thermostats, locks, etc.), edit Lovelace dashboards, render templates, and read history — all mediated through the standard Gadgets approval queue.
Characteristics
- Auth: Home Assistant long-lived access token (LLAT) + instance URL, no OAuth flow — chosen because each HA instance has a different URL (no central directory), LLATs don't expire (10-year default lifetime), it's the pattern users already know from other HA tools, and it works for both Nabu Casa Cloud and self-hosted/LAN users. The user pastes URL + LLAT into a form; the gatekeeper validates via
GET /api/and stores both in a per-user Durable Object. - Reachability: on Cloudflare-hosted deployments, HA must be publicly reachable (Nabu Casa, Cloudflare Tunnel, port-forwarding). On self-hosted (workerd) deployments, LAN addresses like
http://homeassistant.local:8123orhttp://192.168.x.x:8123work directly — the intended setup for same-network deployments. - Five resource granularities: Whole instance (every area/device/entity/dashboard/service), Area (one room's devices/entities), Label (all entities with a given HA label), Device (one physical device and its entities), Entity (one light/sensor/switch/etc.) — each with its own configurator picker UI.
- TypeScript API: bindings expose
HomeAssistantSession(whole-instance),Area,Label,Device, orEntitydepending on granularity. Example:const areas = await session.listAreas(); const light = await session.getEntity("light.kitchen"); await light.turnOn({ brightness: 200 }); const state = await light.getState(); // reflects simulated post-write state const livingRoom = await session.getArea("living_room"); await livingRoom.callService("light", "turn_off"); const temp = await session.renderTemplate("{{ states('sensor.outside_temp') | float }}"); const dashboard = await session.getDashboard("lovelace"); await dashboard.saveConfig(dashboardConfig); - Approval & simulation: every read calls
authorizeObservation, every write goes throughsubmitAction; writes do not execute against HA until approved. Until approval, reads reflect a simulated post-action world (e.g.getState()shows"on"right afterturnOn()) — but simulation predicts final states only, with no transition timing, and leaves state untouched for unrecognized service calls (custom integrations, scenes, scripts, templates). - Not yet implemented (Phase 2): caching (every read is a fresh registry fetch) and hooks/push events (
setHookis a no-op; WebSocketsubscribe_events/subscribe_entitieswould enable aHomeAssistantHook). - Implementation notes: service calls go through HA's WebSocket API (
call_service), not the REST endpoint, for full target-shape support across area/label/floor targets; every action gets an integeridfrom a per-DO counter, stored underpending:<id>for read-time simulation; malformed action bodies fail synchronously with a corrected-call suggestion.
How to Use
- In a Gadget's Connections UI, connect a Home Assistant account by pasting the instance URL and a long-lived access token (generate one in HA's own UI).
- Choose a resource granularity to grant: whole instance, an area, a label, a device, or a single entity, using the matching configurator picker.
- Use the TypeScript Session API (see
src/types.d.tsfor full method list and@exampleblocks) to read state, call services, render templates, or edit dashboards; writes queue for approval before taking effect on the real HA instance.
Related Entities
- gatekeepers
- writing a gatekeeper
- gatekeeper spotify — comparable approval/simulation model for a consumer device-control API
- gatekeepers catalog
