Skip to content

Voice Assistant

Pick brAIn as a conversation agent under Settings → Voice Assistants and every Assist pipeline — voice satellites, the chat dialog, the mobile app — routes through Claude with full knowledge of your home.

Why it’s fast

Typical commands answer in ~3–5 seconds, and replies stream, so TTS starts speaking at the first sentence on streaming-capable pipelines. Two mechanisms:

  • Fast mode (assist_fast_mode, default on): a pool of pre-warmed Claude worker processes — one per active conversation plus a hot spare — so voice turns skip CLI boot and MCP handshake entirely. Budget ~150–300 MB RAM per warm worker (max 3). If a worker ever errors, the add-on falls back to a one-shot spawn automatically.
  • The area map: a cached area → entity map (controllable domains plus Weather and People) is spliced into the system prompt, so “turn off the kitchen lights” acts directly — no lookup turns.

binary_sensor.brain_system_assist_healthy reports pool health (worker count, spare, last-request latency), and brain doctor probes the whole path end-to-end.

Personalities

Each conversation agent you create gets its own name, model, and system prompt — a butler, a snarky roommate, a kid-friendly narrator. Prompts are layered deliberately:

  1. Your personality leads, and explicitly owns identity, tone, and verbosity.
  2. An operational block follows — tools, the area map, timezone, routing rules — deliberately identity-free so it can’t fight your persona.

A voice assistant in Home Assistant with the conversation-agent picker open, showing custom brAIn personalities — Opus, Picard, Data, HAL, Jarvis, GLADOS, Haiku and more

Without a personality, a built-in default applies (“helpful, efficient, 1–2 short sentences”). With one, that default is dropped entirely — so if your persona should stay brief for TTS, say so in the persona: “…no matter how excited you get, keep spoken replies under two sentences.”

New agents default to Claude Haiku for snappy responses; pick any model per agent in the integration options.

You can run several at once, each with its own name, model, personality and deny-list:

Two agents, one house
"Jarvis" You are Jarvis, a dry British butler. Address me as "sir".
However excited you get, keep spoken replies under two sentences.
Blocked: nothing.
"Kids' speaker" You are a cheerful helper for a 7-year-old. Short, simple words.
Never mention alarms or locks; if asked, say you'll fetch a grown-up.
Blocked: lock.unlock, lock.open, alarm_control_panel.*, script.*

The deny-list is enforced where every control tool funnels through, and it’s checked against what a call does, not what it’s called: Home Assistant’s meta-services forward to the target’s own domain, so homeassistant.turn_on aimed at cover.garage_door is a cover call, and the chokepoint resolves meta-calls to their targets and refuses any that touch a restricted domain — or whose targets it can’t resolve at all, like a whole area. fire_event, which can trigger an automation that does exactly what the deny-list forbids, is refused entirely on restricted channels. (Before 1.26.0 the check matched only the spelled service name, so a meta-call could sidestep it.)

Because voice reads the same memory as everything else, an agent already knows the house it’s talking about:

“Turn on the beacon.” → memory says the beacon is the office lamp.

“It’s too cold in here.” → memory says the heating is a heat pump on weather compensation, not a boiler, so it raises the flow temperature instead of asking for a target temperature that would do nothing.

Conversation memory

While the same Assist chat or voice session stays open, Home Assistant keeps the same conversation_id — and the add-on resumes the same Claude session for each turn, so follow-ups genuinely remember the conversation. A new session starts a clean slate; brain.clear_conversation resets one (or all) manually. If session resume isn’t possible, the integration replays recent turns instead, so follow-ups still work with shorter memory. (Before 1.26.0 a follow-up turn could be handed the pre-warmed spare worker — which knows nothing — and lose the conversation’s context for good; the spare now only serves conversations that genuinely start from nothing.)

Voice is also how the house learns: say “remember that we call the office lamp ‘the beacon’” and it’s stored instantly.

Safe by default: tool scoping

Voice can control everything but can’t run shell commands or edit files — assist_tool_access: mcp_only is the default. Two more dials:

  • assist_max_turns caps agentic loops per request (default 8) so a misunderstood command can’t spiral. It stays deliberately modest where the other channels are generous: latency is the product for voice, and a twenty-turn voice command is a failed interaction whatever it answers. Worth knowing on older versions: until 1.26.0, neither this nor assist_tool_access reached fast mode — the default — at all; the worker pool never saw them, so voice always ran at 5 turns whatever the option said.
  • Per-agent service deny-lists (e.g. forbid lock.unlock for the kids’ satellite): every control tool funnels through one chokepoint, so the deny-list covers all of them — homeassistant.* meta-calls included, resolved against their targets as above. brain.* is a one-click deny option too: it keeps a restricted agent away from all 65 registry-admin Power Tools, including brain.create_user, which can mint an admin login. See the Reference for the option syntax.

With Power Tools installed, voice requests can also reorganize — “create a zone for the gym”, “hide the child-lock switches” — with the same validation and admin gating as everywhere else.