Home Assistant REST API
Docs: https://developers.home-assistant.io/docs/api/rest/
NixOS extraComponents bundles integration code, but config-flow-only integrations (Spotify, Matter, HomeKit Controller, Cast, etc.) require the REST API or UI to complete setup.
hass-cli (preferred for inspection/simple calls)
home-assistant-cli is installed on the NUC. Prefer it over raw curl for entity listing, service calls, device/area management, and event watching.
On NUC (after getting a token):
export HASS_SERVER=http://localhost:8123 export HASS_TOKEN=<token>
hass-cli state list 'light.' # list entities by glob hass-cli state get light.office # get single entity (yaml) hass-cli service call homeassistant.toggle --arguments entity_id=light.office hass-cli device list # all devices with area hass-cli area list # all areas hass-cli device assign Kitchen --match "Kitchen Light" # bulk assign area hass-cli event watch # watch all events hass-cli event watch deconz_event # watch specific event type hass-cli -o yaml state list # yaml output hass-cli -o json state list 'light.' | jq '[.[] | {entity: .entity_id, name: .attributes.friendly_name, state: .state}]' hass-cli -o json state list 'light.*' | python3 -c "import json,sys; d=json.load(sys.stdin); print([x['entity_id'] for x in d if x['state']=='on'])"
Note: hass-cli info is broken on current HA (deprecated endpoint). All other commands work.
Use raw curl (below) for config flows, app credentials, and anything hass-cli doesn't cover.
Querying the API (inline SSH)
Scripts in scripts/ exist but are local — they can't be referenced by path on the NUC. Use inline SSH commands instead.
Get a token
TOKEN=$(ssh nuc "sudo python3 -c ' import hashlib, hmac, base64, time, json auth = json.load(open("/var/lib/hass/.storage/auth")) for t in auth["data"]["refresh_tokens"]: if t.get("client_name") == "agent-automation": header = base64.urlsafe_b64encode(json.dumps({"alg":"HS256","typ":"JWT"}).encode()).rstrip(b"=") now = int(time.time()) payload = base64.urlsafe_b64encode(json.dumps({"iss":t["id"],"iat":now,"exp":now+86400*365}).encode()).rstrip(b"=") sig_input = header + b"." + payload sig = base64.urlsafe_b64encode(hmac.new(t["jwt_key"].encode(), sig_input, hashlib.sha256).digest()).rstrip(b"=") print((sig_input + b"." + sig).decode()) break '" 2>/dev/null)
List entities (by domain)
ssh nuc "curl -s -H 'Authorization: Bearer $TOKEN' http://localhost:8123/api/states" | python3 -c " import json, sys states = json.load(sys.stdin) for s in sorted(states, key=lambda x: x['entity_id']): eid = s['entity_id'] name = s['attributes'].get('friendly_name', '') domain = eid.split('.')[0] if domain in ('light', 'switch', 'cover', 'media_player', 'fan', 'binary_sensor', 'scene', 'humidifier'): print(f'{eid:55s} {name}') "
Change the domain in (...) filter as needed, or remove it for all entities.
Call a service
ssh nuc "curl -s -X POST -H 'Authorization: Bearer $TOKEN'
-H 'Content-Type: application/json'
-d '{"entity_id": "media_player.tv"}'
http://localhost:8123/api/services/media_player/turn_off"
Start a config flow
ssh nuc "curl -s -X POST -H 'Authorization: Bearer $TOKEN'
-H 'Content-Type: application/json'
-d '{"handler": "spotify"}'
http://localhost:8123/api/config/config_entries/flow"
Helper scripts (reference)
Scripts in scripts/ are useful as reference for the API patterns but must be piped via SSH or inlined — they aren't deployed to the NUC.
Script Purpose
ha-token.sh
Generate JWT from auth storage
ha-api.sh
General-purpose API wrapper
ha-entities.sh
List entities by domain
ha-integrations.sh
List configured integrations
ha-call.sh
Call a service on an entity
ha-flow.sh
Manage config flows (start/submit)
References
Read these for detailed information:
File Contents
references/integration-flows.md
Per-integration config flow behavior, abort reasons, mDNS discovery commands
references/default-integrations.md
NixOS defaultIntegrations list — what's auto-loaded, Nix config examples
references/device-protocols.md
Identify device protocol (Matter/Zigbee/Thread/HomeKit) from device registry; vendor model-name conventions; decision tree for ZHA migration
Token generation
HA long-lived tokens are HS256 JWTs signed with a per-token key in /var/lib/hass/.storage/auth . Use scripts/ha-token.sh or inline:
ssh nuc "sudo bash ha-token.sh" # uses "agent-automation" token ssh nuc "sudo bash ha-token.sh my-token" # use a different token name
If no token exists yet, create via HA UI: Profile → Security → Long-Lived Access Tokens → Create Token
Verify: curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8123/api/
→ {"message":"API running."}
API quick reference
All requests to http://127.0.0.1:8123 with Authorization: Bearer $TOKEN .
Action Method Endpoint Body
Health check GET /api/
—
HA config GET /api/config
—
List states GET /api/states
—
Get entity state GET /api/states/{entity_id}
—
Set entity state POST /api/states/{entity_id}
{"state": "...", "attributes": {...}}
Fire event POST /api/events/{event_type}
{...event_data}
Call service POST /api/services/{domain}/{service}
{"entity_id": "..."}
- service data
List services GET /api/services
—
List config entries GET /api/config/config_entries/entry
—
Start config flow POST /api/config/config_entries/flow
{"handler": "domain"}
Submit flow step POST /api/config/config_entries/flow/{flow_id}
step-specific data
Abort flow DELETE /api/config/config_entries/flow/{flow_id}
—
Delete config entry DELETE /api/config/config_entries/entry/{entry_id}
—
Add app credentials POST /api/config/application_credentials
{"domain":"...","client_id":"...","client_secret":"..."}
Render template POST /api/template
{"template": "{{ states('...') }}"}
Check config POST /api/config/core/check_config
—
Key workflows
Config flow (non-OAuth)
ha-flow.sh start cast # auto-discovery, usually creates entry immediately ha-flow.sh start matter # returns form → submit with URL ha-flow.sh submit <flow_id> '{"url":"ws://localhost:5580/ws"}'
OAuth integrations (Spotify, Google, etc.)
-
Register app credentials first (abort reason: missing_credentials )
-
Start config flow — returns auth URL for user
ha-api.sh POST /api/config/application_credentials
'{"domain":"spotify","client_id":"ID","client_secret":"SECRET"}'
ha-flow.sh start spotify
See references/integration-flows.md for per-integration details.
NixOS context
-
Auth storage: /var/lib/hass/.storage/auth
-
API: http://127.0.0.1:8123 (localhost only), HTTPS via Tailscale serve
-
Public URL: https://homeassistant.cinnamon-rooster.ts.net/
-
defaultIntegrations auto-loads input helpers, automation, scene, script, etc. — see references/default-integrations.md