Home Assistant Integration¶
Overview¶
Home Assistant Core lives at http://192.168.86.180:8123 (VM CT 100 on Finn). Long-lived API token in ~/.forge-secrets/ (NOT in repo). Forge consumes HA via two paths:
- Direct REST API calls from
scripts/integrations/{eight-sleep,garmin}/poll.py, pollers push wellness sensors into HA. See reference_wellness_pollers memory entry. - Context API HA poller at
infra/context-api/scripts/ha_poller.py, every 15 min ingests wellness + presence + device state into the SQLite Context Graph atdata/context.db. Surfaced via/contextskill.
Capabilities verified (2026-03)¶
- 42 lights detected and controllable via REST
- RGB color control, scenes (Gaming Scene, Work Scene), power on/off/toggle
- State queries (individual + bulk)
- Wellness dashboard at
/wellness-dash(HA Lovelace)
Remote access / Cloudflare Access (changed 2026-05-30)¶
homeassistant.justinsforge.com is served via Cloudflare tunnel. Cloudflare Access was REMOVED 2026-05-30 so the iOS HA companion app works (iOS app can't carry the Access cookie on its WebSocket and has no custom-header support, so Access broke the app — showed a broken-image page; Safari worked). Tradeoff accepted by Justin: HA's own login is now the only public gate. Full Access app+policy config backed up at data/ha-backups/cf-access-homeassistant-*.json — one-step restore. A CF Access service token "Home Assistant App" still exists (works for the Android app's custom headers; unused by iOS). Two legacy "HA Alexa bypass" Access apps remain (now redundant).
RECOMMENDED hardening (not yet applied): HA http: should set use_x_forwarded_for: true + trusted_proxies: [<cloudflared connector IP>] and ip_ban_enabled: true, login_attempts_threshold: 5. Without correct trusted_proxies, ip_ban would ban the tunnel connector (lock everyone out), so the connector IP must be identified first.
Lighting / scenes stack (added 2026-05-30)¶
No Philips Hue bridge; bulbs are WiZ, ZHA (Zigbee: Hue/Tradfri/Inovelli), Tuya, Matter. To get Hue-level scenes across all brands, three HACS components are installed (downloaded manually into /config, HACS can adopt for updates):
- hue-like-light-card v1.11.0 (frontend) — Hue-app-style room/scene UI. JS at
/config/www/community/hue-like-light-card/, Lovelace resource/local/community/hue-like-light-card/hue-like-light-card.js. - Adaptive Lighting v1.30.1 — circadian color-temp+brightness. Config
/config/packages/adaptive_lighting.yaml, scoped to 6 living-area lights only (studio/office/outdoor/video/gaming EXCLUDED so they never auto-adjust). Master switchswitch.living_areas_adaptive_lighting_living_areas. - Animated Scenes v2.1.1 — dynamic cycling-color scenes; service
animated_scenes.start_animation. Activity Sensor config entry created. Scenes are scripts in/config/packages/lighting_animated_scenes.yaml(tropical/fireplace/ocean/party +animated_scenes_stop).
Static scene gallery: scene.relax|bright|evening|movie (+ existing studio_mood_lighting, work_scene, gaming_scene) in scenes.yaml. Dedicated Lighting dashboard at url_path lighting-control (Home dashboard deliberately untouched). Source copies of all package/config files in forge at data/ha-backups/.
Studio Inovelli switch (added 2026-06-10)¶
light.inovelli_studio_recessed_switch is a VZM31-SN Blue Series 2-in-1 (ZHA, smart bulb mode ON) driving all 16 studio lights via light.studio_lights (= studio_spotlights + studio_recessed_lights). Logic lives in /config/packages/studio_switch_scenes.yaml (source copy data/ha-backups/studio_switch_scenes.yaml) wired into automation "Inovelli Studio Recessed Lights" (fxlt ZHA blueprint, id 1777835542777):
- Up/down tap: on (3100K 80%) / off, targeting ZHA group entity
light.krystal_s_office_sonoff_dongle_plus_mg24_studio_all_zigbee(one multicast to all 16). Up/down HOLD: real-time dim via Zigbee binding, switch LevelControl cluster bound to ZHA group 3studio_all_zigbee(device-to-device, no HA round-trip; bound via websocketzha/groups/bind). - 3x tap up/down: cycle forward/back through
input_select.studio_scene_cycle(Warm 2700K / Middle 3100K / Cool 5000K / Mood / Purple / Sunset Split / Cyberwave), applied byscript.studio_scene_apply. - Config 2x/3x/4x/5x:
script.studio_party_mode/studio_cowboy_mode/studio_chill_mode/studio_armadillos_mode(Studio Echo alexa_devices devicefccdd3937151b89fb4c1d7d752a4ab58+ light looks). Armadillos mode plays a YouTube-only parody mp3 throughscript.echo_play_audio_url.
Playing arbitrary mp3s on Echo devices (verified 2026-06-11)¶
script.echo_play_audio_url (in packages/studio_switch_scenes.yaml) plays any hosted mp3 on any Echo: fields echo_player (alexa_media media_player entity, NOT the alexa_devices one), url, volume (default 0.6). Hard-won rules, all three verified by test:
- Use alexa_media notify with
data.type: tts.announcetype strips<audio>tags (silent failure); onlyttsplays them. - Message MUST start with
<speak>or alexa_media sends it as plain text (also silent failure). - File must be public https (valid cert, no CF Access), MP3 48kbps, max 240s. Transcode:
ffmpeg -ac 2 -ar 24000 -b:a 48k. Host in HA/config/www/audio/= auth-free athttps://homeassistant.justinsforge.com/local/audio/<file>(source copiesdata/ha-backups/www-audio/).
Note: three more VZM31-SNs in the kitchen (Counter, Island, Recessed) use the same blueprint; same pattern applies if those get scene cycles.
Two-switch split (added 2026-06-29)¶
A second studio VZM31-SN, "Inovelli Studio Spotlight Switch" (ieee f0:44:d3:ff:fe:6d:a6:9e, entity prefix spotlight_switch, smart-bulb-mode ON), now drives ONLY the 8 Hue track spots (light.studio_spotlights). All ~42 device settings (ramp rates, dim up/down speeds, on-level, power-on=Off, transition=0, dimming mode LeadingEdge, output Dimmer, double-tap levels, LED bar color/intensity) were copied from the Recessed Switch so behavior matches.
CONTROL MODEL (important, both studio switches): tap on/off + multi-tap scenes = the HA blueprint AUTOMATION (fxlt ZHA blueprint, reacts to ZHA button events); hold-to-dim = Zigbee group binding (LevelControl out, device-to-device, no HA round-trip). They are two independent layers. Changing the binding does NOT change tap behavior, and vice-versa. (First attempt only changed bindings and tap did nothing on the new switch, this was the fix.)
Split as of 2026-06-29:
- Recessed Switch automation automation.inovelli_studio_recessed_lights_switch (id 1777835542777): tap → light.sonoff_dongle_plus_mg24_studio_recessed_zigbee (cans, grp 16); hold-dim bound to grp 16; 3x-tap 7-scene cycle + config 2x-5x party/cowboy/chill/armadillos still target all 16 (scripts hit studio_all_zigbee directly).
- Spotlight Switch automation automation.inovelli_studio_spotlight_lights (id 1751240000001): tap → light.krystal_s_office_sonoff_dongle_plus_mg24_studio_spotlights_zigbee (8 spots, grp 4); hold-dim bound to grp 4; 3x-tap up/down cycles input_select.studio_spotlight_temp (Warm 2700/Neutral 3100/Cool 5000) via scripts studio_spotlight_temp_{apply,next,prev} (spots only).
FIRMWARE GOTCHA (root cause of a long 2026-06-29 debug): the VZM31-SN will NOT send dim/level commands over a Zigbee binding on older firmware. A fresh switch shipped on 0x01020303 (hold-dim dead despite correct smart-bulb-mode + bind + air-gap + reconfigure); the working recessed switch was on 0x01020304. Fix = OTA-update the new switch via its update.<name>_firmware entity (update.install, slow 30-60min, do NOT air-gap mid-flash) to match. Tap on/off masks the problem because that path is the AUTOMATION, not the binding. ALWAYS check update.*_firmware installed_version parity between a working and non-working Inovelli before chasing the binding.
Group binding via websocket zha/groups/bind/zha/groups/unbind {source_ieee, group_id, bindings:[{name,type:"out",id:6|8,endpoint_id:1}]}. Do NOT bind one switch's Level cluster to two groups at once, it breaks that switch's hold-dim entirely (observed on recessed during cross-test). NOTE: bulbs must be POWERED ON when creating the ZHA group / adding members, else the device-level Groups-cluster write fails silently and group-cast does nothing (hit this; re-ran zha/group/members/add once spots were on). Friendly-name rename only (entity_ids unchanged). These spotlight automation/scripts/input_select were created via REST/WS API, NOT yet in the studio_switch_scenes.yaml package file (divergence from the package source-of-truth; fold them in next time the package is edited on the HA host).
Reolink doorbell (native integration, added 2026-06-12)¶
The Front Door Reolink doorbell (192.168.86.197, admin creds = Frigate RTSP env) now has the native Reolink HA integration in addition to its Frigate feed. Frigate gives object detection (binary_sensor.front_door_*_occupancy, counts, snapshots); the native Reolink integration gives device control + the button-press entity. Key entity: binary_sensor.front_door_visitor fires on doorbell button press. Automation automation.reolink_doorbell_kitchen_echo_chime triggers on it: sets media_player.kitchen_echo to vol 0.7, then notify.alexa_media_kitchen_echo plays SSML <audio src="soundbank://soundlibrary/doorbells/amzn_sfx_doorbell_chime_01"/> with data.type: tts (announce strips audio tags). Other Echos: notify.alexa_media_<name> for each.
Token¶
Long-lived access token: HA → Profile → Long-Lived Access Tokens. Lives at ~/.forge-secrets/wellness.env (chmod 600) and is mirrored into n8n encrypted credential store (cred IDs in reference_wellness_pollers).
History¶
Earlier (Greg / OpenClaw era) there was a custom skills/home-assistant/ skill with its own FastAPI wrapper on :7357, superseded by the Context API on :7358 and removed 2026-04-28. If you need direct HA control again, call the REST API directly with the long-lived token rather than reviving the wrapper.