A Home Assistant add-on that runs Anthropic's Claude Code CLI in a browser terminal — opened from your HA sidebar, starting in /config so Claude can read and edit your configuration in place. Persistent package installs and image paste included.
Community add-on — not affiliated with, endorsed by, or supported by Anthropic or the Home Assistant project / Open Home Foundation. "Claude" and "Claude Code" are trademarks of Anthropic, PBC; "Home Assistant" is a trademark of the Open Home Foundation. Claude Code itself is subject to Anthropic's terms.
This is a maintained community fork of ESJavadex/claude-code-ha. Why a fork? It fixes the statx launch crash (Alpine 3.21 / musl 1.2.5), repairs package persistence, and auto-wires Claude to the Home Assistant MCP server out of the box — full rationale in About this fork.
A browser-terminal Claude Code CLI for Home Assistant, opened from the sidebar over the authenticated ingress panel (no host port by default). The terminal starts in /config, so Claude can:
- Write and edit automations, scripts, and YAML config in place
- Debug your setup and run
git - Install system/Python packages that persist across restarts
- Analyze pasted images (Ctrl+V / drag-drop / upload)
The Claude binary is pinned to a known-good version and updated by rebuilding the add-on — not from inside the container (see Updating Claude Code).
Claude Code runs inside Home Assistant with your /config open — and, when paired with the Home Assistant MCP server, with live access to your entity states, history, and automation traces. That lets you drive real work in plain language. For example:
- Build dashboards — "Build a Lovelace dashboard for the downstairs floor with a climate card, a light group, and the front-door camera." Claude writes the YAML into
/config; you reload. "Convert this dashboard to sections view and hide the garage row when the door is shut." - Author automations & scripts — "When the last person leaves after 9pm, turn everything off and arm the alarm." Paired with ha-mcp, Claude schema-validates the automation before saving. "Turn this 200-line automation into a reusable blueprint with inputs for the sensor and the delay."
- Troubleshoot & investigate — "Why did the living-room lights turn on at 3am?" With ha-mcp, Claude reads the logbook, history, and the automation trace to find the cause. "This automation isn't firing — check its last trace and tell me which condition failed."
- Maintain & refactor — "Find every automation still using the deprecated
service:syntax and update it toaction:." "Which entities have beenunavailablefor over a week?" "Split my monolithicconfiguration.yamlinto packages."
Two modes, one deliberate split. Reading and editing the files under
/configworks with just this add-on. Operating and observing your live system — calling services, reading state/history/traces, schema-validating changes — comes from pairing with the Home Assistant MCP server (one-paste auto-wiring, below). Claude will tell you when a task needs it.
- Settings → Add-ons → Add-on Store, open the ⋮ menu, choose Repositories.
- Add
https://github.com/unsnow-iac/claude-code-haand click Add. - Install Claude Code for Home Assistant, then Start it.
- Open it from the Claude Code sidebar panel (ingress — there's no host-port web UI by default).
- On first launch, follow the OAuth prompt to log in to your Anthropic account.
The add-on works out of the box; every option below is optional.
| Option | Default | What it does |
|---|---|---|
auto_launch_claude |
true |
Auto-start Claude on open, vs. showing the session picker. |
dangerously_skip_permissions |
false |
Run Claude with --dangerously-skip-permissions (unrestricted file access). |
enable_home_assistant_mcp |
true |
Auto-wire the ha-mcp server on boot (see below). |
home_assistant_mcp_url |
"" |
ha-mcp server URL from its add-on log. Empty = no-op (nothing is wired). |
enable_onboarding_hint |
true |
Seed a short orientation note into the add-on's own ~/.claude/CLAUDE.md (never your /config/CLAUDE.md). |
enable_ssh |
false |
Run an SSH server that drops you into the running Claude session (see below). |
ssh_authorized_keys |
[] |
Public keys allowed to log in. The only credential — passwords are disabled. |
persistent_apk_packages |
[] |
System (apk) packages to auto-install on boot. |
persistent_pip_packages |
[] |
Python (pip) packages to auto-install on boot. |
Ingress-only by design. ttyd runs unauthenticated, so there is no host-port option: the
7680/7681ports cannot be mapped to the host from the Network panel. Access is always through the authenticated ingress panel (docker execstill works for in-container access). The optional SSH port below is a separate, key-authenticated door — it does not change this.
The terminal runs inside a tmux session that outlives the thing you opened it
with. Close the browser tab, lose Wi-Fi, or switch from your phone to your
laptop, and you reattach to the same conversation, still running. Ctrl-b d
detaches without killing anything.
All three ways in land in that one session:
# the Claude Code sidebar panel (ingress)
ssh -p <host-port> root@<home-assistant-ip> # if enable_ssh is on
docker exec -it addon_<slug> claude-tmux # from a host shellIf you live in a terminal, you can skip the browser entirely. SSH is off by default and opening it takes two deliberate steps plus a key you hold:
- Set
enable_ssh: trueand paste your public key (the contents of~/.ssh/id_ed25519.pub, not the private key) intossh_authorized_keys. - In the add-on's Network panel, map a host port to
2222/tcp. Until you do, nothing is reachable — the port is declared but unmapped.
Then ssh -p <host-port> root@<home-assistant-ip> puts you straight into the
running Claude session. ssh <host> '<command>' still works for one-off commands.
What it will and won't do:
- Public keys only. Password, empty-password and keyboard-interactive auth are disabled. A missing or malformed key list makes sshd refuse to start — a misconfiguration can only mean "no SSH", never "open shell".
- No forwarding, tunnelling, or SFTP subsystem. TCP/agent/stream forwarding
and X11 are refused, so the port cannot be turned into a tunnel into your
network, and
sftp/modernscpwill not connect. Running a command (ssh <host> '<cmd>', and so legacyscp -Oorrsync) still works by design — that passthrough is the point. It is not a privilege boundary either way: you authenticated as root. - Login is
root, because the container is a root shell by design and a second user would be a cosmetic boundary rather than a real one. Treat opening this port as equivalent to handing out shell access to your Home Assistant configuration, and only expose it on a network you trust. - The host key is generated once and kept in
/data, so it survives restarts and rebuilds — a changed-host-key warning from your client means something is actually wrong.
Treat this add-on as a shell + config editor, and pair it with the Home Assistant MCP server (ha-mcp) add-on for operating Home Assistant:
- Operate HA via the MCP — call services, query state, manage entities/areas/other add-ons, the host, and backups through an audited, structured channel.
- Author config in this terminal — edit the YAML under
/config, rungit, install packages, and have Claude write changes directly into your configuration.
By design this add-on carries only a homeassistant-level Supervisor token (not manager): ha core check/restart/info keep working, but shell-level control of other add-ons, the host, Docker, and backups is intentionally dropped — route those through the MCP. (The manager privilege lives in the separate ha-mcp add-on's own token, not this one.) Power users who need shell manager access must run a local copy with hassio_role: manager (a fixed manifest field, not raisable from the HA UI).
You don't have to wire the MCP server by hand. Install the Home Assistant MCP Server (homeassistant-ai/ha-mcp) add-on, open its Log tab, copy the server URL it prints (http://<host>:9583/private_<secret>), and paste it into this add-on's home_assistant_mcp_url option. On the next start the terminal opens already connected — Claude can call the ha_* tools with no claude mcp add.
- The secret path is the credential; no token is required. ha-mcp's own
managertoken (not this add-on's) does the work, so nothing about this add-on's reduced privilege changes. - Leaving
home_assistant_mcp_urlempty disables auto-wiring and touches no Claude config — if you already wired ha-mcp yourself, it's left as-is. - If ha-mcp is reinstalled its secret path rotates; if the
ha_*tools stop working, re-copy the new URL from its log into the option. To paste only once, pin the path with ha-mcp's advancedsecret_pathoption (persisted to its own/data/secret_path.txt) so the URL stays stable across reinstalls. - With
dangerously_skip_permissions: true, MCP tool calls aren't prompted on the Claude side — for unattended use, consider ha-mcp's ownread_only_modeorenable_tool_security_policiesas a server-side guard. The latter adds a Tool Security Policies tab in ha-mcp's web UI (its Open Web UI) where you approve held tool calls and set per-tool rules.
- Persistent package management —
persist-install <pkg>copies binaries and theirldd-resolved libraries into/data, surviving restarts and container recreation (plainapk add/pip installdon't). Auto-install on boot viapersistent_apk_packages/persistent_pip_packages; isolated Python venv included. - Image paste — paste (Ctrl+V), drag-drop, or upload images for Claude (JPEG/PNG/GIF/WebP/SVG, 10 MB limit); lightweight service (~10 MB RAM, ARM-friendly); stored in
/data/images/. - Pinned, baked toolchain — Claude,
ttyd, andtmuxare baked into the image, so the terminal starts even when Alpine repos are unreachable. - Persistent auth & config — OAuth credentials and settings live under
/data, preserved across restarts and rebuilds. - Ingress-only by default — served through the authenticated HA panel; no open host port.
Maintained by unsnow-iac on the main branch of unsnow-iac/claude-code-ha. It is a maintenance fork of ESJavadex/claude-code-ha by Javier Santos, itself a fork of heytcass/home-assistant-addons by Tom Cassady. It exists to fix issues that broke the add-on in practice:
| Fixed | Why it mattered |
|---|---|
| Base image → Alpine 3.21 (was 3.19) | Alpine 3.19 ships musl 1.2.4, which lacks the statx symbol current Claude Code native builds require — newer binaries crashed at launch with Error relocating ...: statx: symbol not found. 3.21 ships musl 1.2.5. |
Claude pinned + baked; ttyd/tmux baked |
Reproducible builds (ARG CLAUDE_VERSION); the terminal no longer depends on apk reaching the network at every boot. |
persist-install rewritten |
apk info -L lists paths without a leading slash, so the old == /usr/bin/* test never matched — the script reported success but copied nothing, so packages vanished on container recreation. Now normalises paths and resolves real deps via ldd. |
Removed the persistent_claude layer |
It chased an obsolete cli.js path and npm install-ed @latest into /data/npm, fighting the baked-binary model. The launcher is now force-linked to the baked binary each boot, so a stray claude update self-heals on restart. |
In-container self-update is disabled by design. To ship a new Claude version:
- Bump
ARG CLAUDE_VERSIONinclaude-terminal/Dockerfile. - Bump
version:inclaude-terminal/config.yamland the label inclaude-terminal/build.yaml. - Commit, push to
main, then Update/Rebuild the add-on in Home Assistant.
The add-on builds on-device (no prebuilt image), so the rebuild picks up the new base + pinned Claude. /data (auth, config, packages) is preserved across rebuilds.
- ha-ws-client-go by @schoolboyqueue — a lightweight Go CLI for the Home Assistant WebSocket API: entity states, service calls, automation traces, and real-time monitoring. Single binary, no dependencies.
Questions or issues? Please open an issue in this repository. For more detail, see the add-on documentation.
MIT — see LICENSE. Original Claude Terminal add-on by Tom Cassady (@heytcass); persistent-package management and enhancements by Javier Santos (@esjavadex); this fork (Alpine 3.21/statx fix, persist-install repair, least-privilege + ha-mcp wiring, public release) by unsnow-iac.
