niklas‑stephan.de

Home Assistant on iris: update, Zigbee, voice stack, LLM conversation agent

Last updated 05/11/2026 · migrated-from-outline, llm, metis, homeassistant

id: home-assistant-metis title: Home Assistant (on iris since 2026-10-21; voice stack and LLM on metis): update, Wyoming… importance: medium tags: migrated-from-outline, llm, metis, homeassistant updated: 2026-10-31 content: HA moved to iris 2026-10-21 (voice/llama entries still broken); update procedure, Whisper/Piper stack, conversation agent, LLM tests. outline: /doc/home-assistant-on-iris-since-2026-10-21-voice-stack-and-llm-on-metis-update-wyoming-CoIcbqrl7b

.

CURRENT STATE: HA runs on iris since 2026-10-21

  • HA moved from metis to iris (compose /data/docker/HomeAssistant/compose.yml on iris, config ./config, image 2026.10.2, docker exec homeassistant ... on iris). LAN http://192.168.68.63:8123, tailnet http://100.64.0.3:8123. Skill: home-assistant.
  • The ConBee II is plugged into iris; compose maps its /dev/serial/by-id/usb-dresden_elektronik_ingenieurtechnik_GmbH_ConBee_II_DE2153523-if00 to /dev/ttyACM0 so the ZHA config is unchanged. After the move ZHA recovered (unavailable 197 to 40 = battery devices).
  • Public access: home.handtrixxx.com forwarder forward-homeassistant (in /data/docker/homelab.vpn/compose.yml on handtrixxx.com) points at TCP:100.64.0.3:8123. internal_url in .storage/core.config is http://192.168.68.63:8123.
  • Rollback copy on metis: gone. As of 2026-10-25 there is no homeassistant container and no /data/docker/HomeAssistant/ directory on metis (removed 2026-10-23); only the orphan ghcr.io/home-assistant/home-assistant:stable image (1.6 GB) is still cached, referenced by nothing. Never start HA on metis - the live instance is iris.
  • Voice moved to iris 2026-10-23: whisper/piper/openwakeword now run on iris in /data/docker/voice/ (127.0.0.1, HA host network), so the three Wyoming entries work again. The metis copies of those three images were docker rmi’d on 2026-10-25, so voice tasks must run on iris; there is no local fallback left on metis. Still broken (backlog): the llama conversation entry points at 127.0.0.1; repoint to http://192.168.68.50:8000/v1.
  • Hallway lights (2026-10-21): two new innr RCL 231 T (light.flur_eingang, light.flur_kleiderschrank, ZHA) replaced the two IKEA E14 bulbs, which now sit in the area Arbeitszimmer and were renamed the same day to light.arbeitszimmer_telefon / light.arbeitszimmer_schrank (device names Arbeitszimmer Telefon / Arbeitszimmer Schrank; their HomeKit accessory entries Arbeitszimmer Leuchte Telefon|Schrank follow). Flur Bewegung now switches only light.flur_kleiderschrank at 15 % / 2000 K. Groups Lichter Flur / Lichter Arbeitszimmer, ZHA group 0x000C, the remote and motion automations (Lichter an/aus/Heller/Dunkler, Flur Bewegung) and the mobile dashboard were updated; backups on iris: config-lights-pre-20261021.tar.gz, config-rename-pre-20261021.tar.gz, automations.yaml.bak-lights-20261021, automations.yaml.bak-motion-20261021. Procedure in skill home-assistant (Zigbee section).
  • Bluetooth/iBeacon is intentionally dropped (adapter was on metis; the 95 iBeacon entities were passing cars).
  • Everything below is history up to 2026-10-15 and describes metis; paths and 127.0.0.1 URLs there are outdated for HA itself and for the voice stack (voice is on iris now); only the llama server at 192.168.68.50:8000 is still on metis.

Status as of 2026-10-09 (history). Compose projects lived on Metis under /data/docker/.

HA container

  • /data/docker/HomeAssistant/compose.yml: image ghcr.io/home-assistant/home-assistant:stable, host networking, privileged, config ./config.
  • Updated 2026.9.2 to 2026.10.0 on 2026-10-09. Procedure used: docker tag <old image id> ha-rollback:2026.9.2 then docker compose pull, docker compose stop, tar the config, docker compose up -d. (HA rewrites state on shutdown, so always stop it before copying/editing config.)
  • Rollback: point the compose image to ha-rollback:2026.9.2 and restore config-backup-20261009-pre2026.10.tar.gz (in /data/docker/HomeAssistant/, ~150 MB, owned by root).
  • 2026.10 breaking changes reviewed: none affect our integrations (legacy auth-provider mode removed, mqtt.publish/mqtt.dump need admin, several dead integrations removed). Custom components: frigate 5.15.6, hacs 2.0.5, mbapi2020 v0.40.0, nodered, philips_airpurifier_coap, speedport, tplink_deco, llama_conversation (home-llm) v0.4.12.
  • 2026.10 has a new Settings > System > AI page with a one-click MCP server card (HA as MCP server; URLs listed there). Plan: use it for Hermes (see backlog).
  • HA language de, country DE, timezone Europe/Berlin.

Voice stack: /data/docker/voice/ (compose project on Metis)

Container Image Port Notes
whisper rhasspy/wyoming-whisper 127.0.0.1:10300 model large-v3-turbo, de, **WYO_WHISPER_COMPUTE_TYPE=int8**, 8 threads, beam 1
piper rhasspy/wyoming-piper 127.0.0.1:10200 voice de_DE-thorsten-medium
openwakeword rhasspy/wyoming-openwakeword 127.0.0.1:10400 wake word

(ha-llm-proxy was removed 2026-10-15, see “Current LLM setup” below.)

  • Ports are bound to localhost only; HA is on host networking so it connects to 127.0.0.1. Nothing is exposed on the LAN.
  • Gotcha: default compute type on CPU is float32 (3.5 GiB RAM); int8 brings it to ~0.9 GiB and is fast enough. CPU has 32 cores, no GPU needed.
  • Measured (Piper TTS to Whisper STT, synthetic German commands): transcript correct, STT 1.3 s for 2 s of audio, TTS ~0.07 s warm.
  • A Wyoming service from another container can’t reach 127.0.0.1:port of the host; test inside the compose network by service name (piper:10200).

Current LLM setup (since 2026-10-15)

  • HA talks directly to the production llama on metis — since 2026-10-18 that is Qwen3.8-Flash-Next (llama-flashnext, http://192.168.68.50:8000/v1; formerly Ornith-1.5-35B-A3B). Generic OpenAI backend, port 8000, path v1; llama-server ignores the model name. Thinking stays on (server default). No proxy. Because HA runs on iris, the entry cannot use 127.0.0.1 — it must point at metis’s LAN IP (backlog item, still open 2026-11-01).
  • Why the proxy was dropped: measured on prod with 3 HA-style prompts and 2 tools, thinking on = 0.9 to 3.7 s vs no-think 0.5 to 0.9 s, identical tool calls. Ornith is fast enough that the extra 0.4 to 3 s does not matter. Verified live: connections go only to :8000, light on/off via Assist works.
  • Gotcha (HA UI): host/port are stored in the config entry’s options (Reconfigure on the parent entry), the model name in the conversation sub-entry. The entry title (...:8002/v1) is only a label created at setup and does not update.
  • Open latency item: every turn sends a ~7.8k-token prompt (entity list + in-context examples) that is processed from scratch (~5.9 s cold, ~6.4 s total), because refresh_prompt_per_turn rebuilds it and the prefix cache misses. Ideas: expose fewer entities, fewer in-context examples, check whether the time sits at the top of the prompt.
  • Rebuild recipe if a no-think proxy is ever needed again: ~60-line Python stdlib ThreadingHTTPServer (python:3.12-alpine, host network, 127.0.0.1:8002) forwarding to 127.0.0.1:8000; for POST */chat/completions it does chat_template_kwargs.setdefault("enable_thinking", False) and temperature.setdefault(0.4), streams the upstream body back unchanged (HTTP/1.0 close-delimited), passes Authorization. home-llm cannot send chat_template_kwargs itself, which is why the proxy was needed. Backup of the old compose: /data/docker/voice/compose.yml.bak-20261015-noproxy.

Choosing the conversation model (tested 2026-10-09, German, tool calling; HISTORICAL, superseded by “Current LLM setup”)

Model Latency Result
Qwen3.5-2B (llama-small, :8001) 0.3 to 0.9 s Unsafe: “light off” became HassLightSet brightness=100, invented entity names
Qwen3.8-27B no-think (via ha-llm-proxy :8002) 2 to 3 s non-stream; ~1.2 s to first token streaming Correct tools/areas (HassTurnOff area=Wohnzimmer domain=[light])

Decision then (obsolete, 2026-10-09): 27B no-think via the proxy for the HA conversation agent. Superseded: prod is llama-flashnext, proxy removed. HA’s built-in intent matching still handles simple commands instantly (prefer local handling first); the LLM is the fallback.

  • Why there was a proxy (ha-llm-proxy on 8002, now removed): home-llm (acon96) cannot send chat_template_kwargs, and the old Qwen3.8-27B thought by default (effort xhigh). Obsolete - prod is llama-flashnext and the proxy is gone.

home-llm (acon96/home-llm v0.4.12)

  • Installed by copying custom_components/llama_conversation from the git tag into /data/docker/HomeAssistant/config/custom_components/ (HACS is installed too, but update it via HACS later if wanted). HA restarted, loads fine. Needs HA >= 2026.5.
  • Backends: Generic OpenAI API (Chat Completions), llama.cpp server, Ollama, etc. Correct values today: Generic OpenAI, host 192.168.68.50 (metis LAN - HA runs on iris, so 127.0.0.1 never works), port 8000, API path v1, model qwen3.8-flashnext (the old ornith-1.5-35b-a3b and the removed ha-llm-proxy on 8002 are obsolete). “Use server defaults for sampling” can stay on.

Remaining manual UI steps (need a logged-in HA session)

  1. Settings > Devices & services > Add integration > Wyoming Protocol: host 127.0.0.1 port 10300 (Whisper), again with port 10200 (Piper).
  2. Add integration > Local LLMs, pick Generic OpenAI API with the values above, then add a conversation agent (API: Assist).
  3. Settings > Voice assistants: create/edit a pipeline: language German, conversation agent = the new one, STT = faster-whisper, TTS = piper (de_DE-thorsten-medium). Enable “Prefer handling commands locally”.
  4. Expose only the entities you want Assist to control (Settings > Voice assistants > Expose).

Update 2026-10-09 (late): Hermes–HA, 2B model, female voice, wake word

Hermes ↔ Home Assistant is already working

  • data/.env has HASS_URL + HASS_TOKEN; platforms.homeassistant.enabled: true.
  • Verified from the Hermes container: REST returns 635 entities; a read-only hermes chat -Q --oneshot -t homeassistant query answered “26 lights, 4 on”.
  • Log warnings are harmless: “Reconnection failed 502” = HA restarts; “No watch_domains/entities configured” = event-watch filter unset.
  • Optional: use the tailnet URL http://100.64.0.1:8123 instead of the public Traefik URL.

Qwen3.5-2B (llama-small, :8001)

  • Only used for Hermes title generation (~7 requests/24 h). Unusable for HA control (wrong tool arguments). Marginal value; can be replaced by the 27B no-think at the cost of queueing behind -np 1.

Female German voice

  • Piper’s female German voices are all low quality: ramona-low, kerstin-low, eva_k-x_low.
  • Alternative: Wyoming-Piper OmniVoice backend (image rhasspy/wyoming-piper:omnivoice, ~1.5 GiB RAM, CPU). Voices live in omnivoice_voices/de_DE/<name>/instruct.txt (voice design) or ref.wav + ref.txt (cloning).
  • instruct.txt accepts only: gender, age (child/teenager/young adult/middle-aged/elderly), pitch (very low..very high), whisper, accents. Words like “warm”/“calm” produce an error. Restart the container after editing voices.
  • Timing: first audio ~1.6 s, 8 s sentence in ~6 s (Piper: ~0.07 s). Run with --omnivoice-steps 16.
  • Trial container piper-omni-trial (127.0.0.1:10201, data in /data/docker/voice/omnivoice-data).

Wake word / satellite

  • Hands-free needs a mic/speaker satellite plus wake word. Without it Assist works only typed or push-to-talk (Companion app / browser).
  • openWakeWord (Wyoming, port 10400) supports English wake words only (okay nabu, hey jarvis, alexa, hey mycroft). Porcupine has German wake words. On-device microWakeWord (ESP32-S3-BOX-3, Voice PE, Android Companion App) uses the same English set.
  • Decide the satellite hardware first; the wake word container is cheap but useless without one.

Decisions and changes 2026-10-09 (final state of this session)

  • TTS stays Piper **de_DE-thorsten-medium** (male is fine). OmniVoice trial container, omnivoice-data (3.7 GB) and samples removed. OmniVoice remains an option if a female voice is wanted later: image rhasspy/wyoming-piper:omnivoice, --backend omnivoice --omnivoice-ref-dir ... --omnivoice-steps 16.
  • openWakeWord added to /data/docker/voice/compose.yml: rhasspy/wyoming-openwakeword, 127.0.0.1:10400, 512 MB limit (uses ~35 MiB), English wake words only. Not yet added in HA (no satellite yet).
  • Qwen3.5-2B / **llama-small** removed (freed ~1.8 GiB VRAM + 1.3 GB disk). Hermes auxiliary.title_generation now points at the 27B (base_url http://100.64.0.1:8000/v1, model alias qwen3.8-27b-nothink, extra_body.chat_template_kwargs.enable_thinking=false); tested: title in ~6 s. Backup config.yaml.bak3-20261009. Note: calling generate_title() outside the gateway without an explicit base_url fails (“main provider unavailable”), so keep the explicit base_url.
  • Hermes -> HA now uses **HASS_URL=http://100.64.0.1:8123** (tailnet, no Traefik). Latency is the same as the public URL (~0.1 s); the benefit is independence from Traefik/TLS/public DNS. Backup .env.bak-20261009. Verified with hermes chat -Q --oneshot -t homeassistant -q "..." (use -q, the query is not positional).

Source: Wiki.js