Chat with Claude from Home Assistant Assist, and call it from your automations — powered by the companion Claude Code add-on running on your own hardware. Requires Home Assistant OS or Supervised.
claude_ha exposes Claude to Home Assistant itself. It talks to Claude through
the Claude Code add-on over Home
Assistant's internal network — the add-on holds the login and runs Claude, while
this integration is a thin, secure client. Nothing here calls the Anthropic
cloud directly.
| 💬 Conversation agent | Talk to Claude from HA Assist — replies stream in live, it acts on benign requests immediately and confirms important ones (judged per action). |
| 👁️ Camera vision | Optionally, a visual question ("who's at the door?") sends one snapshot of an Assist-exposed camera to Claude. Off by default. |
| 🗨️ Dashboard chat card | A bundled Lovelace card to chat with Claude and Apply/Dismiss its suggestions. |
⚙️ claude_ha.ask action |
Send a prompt to Claude from automations, optionally confirming changes via a phone notification. |
| 📟 Sensors | Add-on readiness, active model, and Claude token usage + prompt-API cost. |
| 🩺 Health checks | Detects the "chat can't see your home" gaps and raises a repair with the exact fix. |
| 🎙️ Local voice one-click | claude_ha.setup_voice installs Whisper + Piper and builds an Assist pipeline for your language. |
| 🔒 Secure by design | Read-only by default, bearer-token auth, scoped writes, no cloud calls. |
| 🚀 Zero-touch setup | Discovered, installed and started for you via the Supervisor. |
- How it works
- Requirements
- Installation
- Configuration
- Usage
- Security model
- How data is updated
- Known limitations
- Troubleshooting
- Removal
- Development
- License
HA Assist ─┐
├─► claude_ha (this integration) ──HTTP + bearer──► Claude Code add-on ──► Claude
Automation ┘ conversation entity (own agentic loop,
claude_ha.ask action scoped HA access)
The integration and the add-on are separate projects that connect through a small, versioned HTTP contract (a bearer-authenticated prompt server on the add-on's internal port). The connection details — host, port and a shared token — are handed to the integration automatically through Supervisor discovery, so there is nothing to type in.
- Home Assistant OS or Supervised (the integration manages a Supervisor add-on).
- The Claude Code add-on. Home Assistant
doesn't ship it, so add its repository URL —
https://github.com/LayerTM/ClaudeInHA— to your add-on store once, then install the add-on from there.
This integration is listed in the HACS default store. A freshly listed repository takes a few hours to reach every HACS install, so the button below is the reliable route in the meantime — if your HACS doesn't know the repository yet, it offers to add it.
- Click Open in HACS above and install Claude. (Accept the prompt if
HACS asks to add the repository first.) You can also search HACS for
claude-ha— search for Claude alone and you'll get several similarly-named integrations, whileclaude-hamatches only this one. - Restart Home Assistant.
- Install the Claude Code add-on. Once it starts, Home Assistant offers to set up the Claude integration automatically — accept it. No configuration needed.
Neither route working? Add LayerTM/claude-ha as a custom repository
(category Integration). If HACS replies that the repository already exists in
the store, then it is already listed — search for claude-ha instead.
Copy custom_components/claude_ha into your Home Assistant
config/custom_components/ directory and restart.
Setup is zero-touch: when the Claude Code add-on starts it advertises its host, port and a freshly generated token through Supervisor discovery, and Home Assistant surfaces a one-click setup. You can also add it from Settings → Devices & services → Add integration → Claude; it will find, install and start the add-on for you. There are no options to fill in.
Select Claude as a conversation agent under Settings → Voice assistants, or target it directly. It answers in any language, and it can act on your home. Answers stream in live, token by token (with the Claude Code add-on ≥ 1.17.0; older add-ons return the whole answer at once).
Every request is read first. If it would change state, Claude proposes the exact actions, and the integration decides — per action, from live entity metadata — whether to carry it out immediately or confirm:
- Benign, low-risk actions run right away (e.g. turning a light on) and reply
Done: …. - Important actions are held and confirmed with a plain
yes/no— anything the deterministic classifier flags (locks, alarms, garage/door/gate covers, firmware updates, router/AP and other config entities, and opaque-effect wrappers like scenes, scripts and automations), anything Claude itself marks non-low-risk, or anything you pin as critical. The classifier only auto-runs an action it can positively prove benign — anything it can't resolve or bound falls back to confirmation. The confirmation replays the exact validated actions, so it never depends on the model remembering them.
Criticality is judged per action, not per domain — a shade and a garage door are
both cover, but only the garage door is confirmed. Requires the Claude Code
add-on ≥ 1.8.0 (older add-ons simply confirm everything). Tune it under the
integration's Configure options: turn off auto-execute to confirm every
change, or list entities that must always be confirmed.
Claude can only ever touch entities you have exposed to Assist — that exposure list is the outer ceiling; the per-action classifier is the inner gate.
Camera vision (opt-in, off by default; add-on ≥ 1.17.0). Turn on Let Claude look at cameras in the options and a clearly visual question — "who's at the door?", "look at the backyard camera" — sends one snapshot to Claude. Only a camera you have exposed to Assist is ever sent, only when the message is visual and exactly one camera resolves (by name, area or floor); it never guesses between cameras. The add-on fetches and downscales the snapshot itself; the integration passes only the entity id.
A chat card ships with the integration — no separate install. Add a Manual card (or pick Claude Chat in the card picker) with:
type: custom:claude-chat-card
title: ClaudeType a message and the card shows Claude's reply. If Claude proposes a change, an inline Apply / Dismiss appears; Apply runs the confirmed write.
action: claude_ha.ask
data:
prompt: Summarise today's calendar and suggest what to wear.
response_variable: claudeclaude.text holds Claude's answer; the response also includes proposal (a
described state change, or null), tools_used, and truncated.
To act on something, use the two-phase flow: a read call returns a proposal
with intents; after your own confirmation, echo those exact intents back in a
write call. Writes are scoped by the Claude Code add-on to just those confirmed
intents — only use write from automations you control, never on untrusted input.
# 1. Ask (read) — get the proposed intents.
- action: claude_ha.ask
data:
prompt: Turn off everything in the garage.
response_variable: claude
# 2. Confirm, then act (write) — echo the confirmed intents back.
- action: claude_ha.ask
data:
prompt: Turn off everything in the garage.
mode: write
intents: "{{ claude.proposal.intents }}"Or let Home Assistant ask you to confirm on your phone: pass a notify target,
and if Claude proposes a change you get an actionable Approve / Dismiss
notification — Approve runs the confirmed write for you.
action: claude_ha.ask
data:
prompt: Turn off everything in the garage.
notify: mobile_app_my_phone- Status (
sensor.claude_code_status) —ready/initializing, with the add-on version, Claude version, active model, whether a scoped HA MCP is configured and reachable, ahealthsummary and the count of entities exposed to Assist as attributes. - Chat health (
sensor.claude_code_chat_health) — a rolling summary of recent chat outcomes (ok/degraded). It asks whether failures are still happening, not whether any ever did. Three failed chats in a row raises it straight away — that's an outage, whatever the longer-run numbers say — and so does one chat in ten or more failing. It clears again once the clean chats since the last failure outnumber the failures behind them, or that failure is over 6 hours old. So a single old blip no longer nags for days, while a problem that is happening right now shows up before it has had time to move an average, and one chat getting through mid-outage isn't enough to call it recovered. The attributes show the working: recent/ok/degraded/recovered counts, thefailure_ratethe state turns on,consecutive_okandconsecutive_failed(chats since the last failure, and since the last success), the last degrade reason (a token, never prompt content) and the window's timestamps. Needs add-on ≥ 1.20.0; unavailable otherwise. The run counters and the timestamps need add-on ≥ 1.49.0 and are null without it — an add-on that reports none of them is judged on the rate alone, so a missing field neither quiets a warning nor invents one. - Daily budget spend (
sensor.claude_code_daily_budget_spend) — today's spend in USD against the add-on's daily budget, withlimit/remaining/fraction_used/ a softnear_capflag as attributes (a limit of 0 = unlimited leaves those null). Diagnostic; never a repair. Needs add-on ≥ 1.21.0. - Token usage today (
sensor.claude_code_token_usage_today) — today's input + output tokens (unittokens), with the full usage report (per-period and per-model token totals, message counts) as attributes. Polled slowly (~5 min; the add-on caches it). - Prompt API cost (
sensor.claude_code_prompt_api_cost) — the total prompt-API cost in USD (interactive-console use is measured in tokens, not dollars). The usage/cost sensors need the Claude Code add-on ≥ 1.7.0 and stay unavailable otherwise.
Entity ids above are the defaults (device Claude Code + entity name); adjust to your own if you've renamed them.
A ready-made overview using only built-in cards — paste it into a dashboard (Edit
dashboard → Add card → Manual) to see cost, budget, chat health and MCP state at a
glance. No extra install; adjust the entity ids if yours differ, and set the gauge
max to your add-on's daily budget cap.
type: vertical-stack
cards:
- type: entities
title: Claude
entities:
- entity: sensor.claude_code_status
name: Status
- entity: sensor.claude_code_chat_health
name: Chat health
- entity: sensor.claude_code_daily_budget_spend
name: Budget spent today
- entity: sensor.claude_code_token_usage_today
name: Tokens today
- entity: sensor.claude_code_prompt_api_cost
name: Total API cost
- type: gauge
entity: sensor.claude_code_daily_budget_spend
name: Daily budget
needle: true
max: 5 # set to your add-on's daily budget cap
severity:
green: 0
yellow: 3.5
red: 4.5
- type: history-graph
title: API cost (24 h)
hours_to_show: 24
entities:
- sensor.claude_code_prompt_api_cost
- type: history-graph
title: Tokens today (24 h)
hours_to_show: 24
entities:
- sensor.claude_code_token_usage_todayCost (USD) and tokens are on separate graphs on purpose — a single graph shares one y-axis, so the small USD range would be flattened against the much larger token count. The budget gauge is only meaningful with a daily budget set on the add-on; with no budget (unlimited) the spend still shows but the gauge scale is arbitrary.
The most common "dead on arrival" failure is a chat that reaches Claude but can't actually see your home. The integration checks for this on every status poll and raises a repair with the exact fix when it finds:
- Claude isn't logged in (the add-on is up but unauthenticated);
- Claude has no Home Assistant token to read your home;
- the Model Context Protocol Server integration is missing or unreachable;
- nothing is exposed to Assist (so there's nothing to see or control).
These checks cost nothing (they read the status poll, not Claude). Press the Check Claude health button to run a deeper probe — a tiny read that confirms Claude can actually reach the MCP server right now. Needs the add-on ≥ 1.14.0 for the reachability signal.
claude_ha.setup_voice turns local voice chat with Claude into one action. Give
it a language and it installs and starts the official Whisper (speech-to-text)
and Piper (text-to-speech) add-ons, then creates an Assist pipeline whose
conversation agent is Claude:
action: claude_ha.setup_voice
data:
language: uk # uk, pl, en, de, …
# tts_voice: uk_UA-ukrainian_tts-medium # override the default voice
# stt_model: small-int8 # override the Whisper modelThen pick the new pipeline on your phone. Honest caveats: Whisper on CPU takes a
few seconds per phrase, and some voices (Ukrainian especially) are weaker than
others — pass tts_voice or switch to Cloud TTS if the default doesn't fit. The
service returns the engines it wired and the pipeline id.
The security-critical work lives in the add-on; this integration deliberately does the least it can.
- Bearer token, not network trust. Every request carries a token issued via Supervisor discovery. It never appears in the UI and is redacted from diagnostics.
- Read-only by default. Chat is treated as untrusted input and runs deny-by-default; state changes require explicit, scoped, confirmed intents.
- No cloud calls from HA. The integration only talks to the local add-on.
See SECURITY.md for the reporting policy, and the add-on for the full picture (env-scrubbed child processes, per-call statelessness, rate limiting, output redaction and audit logging).
The status sensor is refreshed by a DataUpdateCoordinator that polls the
add-on's /api/status endpoint every 60 seconds. Prompts (chat turns and the
ask action) are sent on demand.
- Requires Home Assistant OS / Supervised — the add-on is a Supervisor add-on and is not available on Home Assistant Container or Core installs.
- One Claude instance per Home Assistant (one add-on → one config entry).
- The conversation agent surfaces proposed state changes but does not run a full
confirm-and-act handshake from chat; use
claude_ha.askwithmode: writefrom a trusted automation for that.
- "The Claude Code add-on is not running". A repair issue offers to start it; or start it from the add-on page. Home Assistant retries setup automatically.
- Setup keeps retrying. Check that the add-on is installed, started and healthy; the status endpoint must be reachable on the internal network.
- Diagnostics. Download diagnostics from the integration's device page (the token is redacted) to inspect the last known status.
Delete the Claude integration from Settings → Devices & services. If Home Assistant installed the Claude Code add-on for you, remove it separately from the add-on store.
python3.14 -m venv .venv && source .venv/bin/activate
pip install -r requirements_test.txt
pre-commit install
ruff check custom_components tests scripts
ruff format --check custom_components tests scripts
mypy custom_components/claude_ha
pytest --cov=custom_components.claude_ha --cov-report=term-missing
python scripts/secret_scan.py .CI runs hassfest, HACS validation, ruff, mypy, the test suite (100% coverage) and a secret scan on every push and pull request. See CONTRIBUTING.md.
Brand images live under custom_components/claude_ha/brand/ and are served by
Home Assistant's Brands Proxy API (2026.3+) — no separate brands submission
needed.
MIT © LayerTM