From claude-code-homeassistant-hermit
Sets up Home Assistant access for this project, configuring HA connection and the official Home Assistant MCP Server integration.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-code-homeassistant-hermit:hatchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Set up the Home Assistant layer for this project. Idempotent — safe to re-run; will skip completed steps and offer re-verify only.
Set up the Home Assistant layer for this project. Idempotent — safe to re-run; will skip completed steps and offer re-verify only.
Read .claude-code-hermit/config.json.
_hermit_versions["claude-code-hermit"] is absent or less than 1.0.16:
AskUserQuestion: "Core hermit is not initialized. Run /claude-code-hermit:hatch now?"claude-code-hermit:hatch):
.claude-code-hermit/state/hatch-resume.json with { "skill": "claude-code-homeassistant-hermit:hatch" }./claude-code-homeassistant-hermit:hatch.)"/claude-code-hermit:hatch via the Skill tool — terminal action, stop after the call.Read _hermit_versions["claude-code-homeassistant-hermit"] from config.json. Read the version field from ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json.
AskUserQuestion: "Already set up. Re-verify HA access only (skip setup wizard)?". Yes → skip to §6. No → continue.Run ${CLAUDE_PLUGIN_ROOT}/bin/ha-agent-lab boot status and inspect the JSON output.
Important: do NOT use
grep,cat, orechoon.env— the deny-pattern hook blocks any Bash command whose arguments contain the literal stringTOKEN. Always use the CLI to check credential state.
token_configured: true and local_url non-null → proceed..env is missing or incomplete. Please create `.env` at the project root with:
HOMEASSISTANT_URL=http://homeassistant.local:8123 # or your remote URL
HOMEASSISTANT_TOKEN=<your long-lived access token>
Long-Lived Access Tokens: Home Assistant → Profile → Long-Lived Access Tokens.
AskUserQuestion: "When your .env is ready, type done to continue (or abort to stop)."
boot status and re-check. If still missing, repeat from step 1. If valid, proceed..env — it is the user's responsibility.Also check locale:
.claude-code-hermit/OPERATOR.md. If a ## HA hermit section has a - Language: entry, use it silently — do not re-ask.en, pt, es) Save it via ${CLAUDE_PLUGIN_ROOT}/bin/ha-agent-lab boot store --language <locale> (writes to OPERATOR.md under ## HA hermit).Do not collect or store the token — it stays in .env only.
The CLI runs on bun, which the core hermit requirement guarantees — no runtime deps to install.
Run ${CLAUDE_PLUGIN_ROOT}/bin/ha-agent-lab boot status (read-only, no --probe) to confirm the launcher resolves correctly. If it fails with "bun not found", stop and tell the user to install bun (https://bun.sh) — it is required by claude-code-hermit core.
Step A — Enable the integration in Home Assistant
Tell the user: go to Home Assistant → Settings → Devices & Services → Add Integration → search "Model Context Protocol Server". Enable it. This exposes the MCP endpoint at <your HA URL>/api/mcp.
Reference: https://www.home-assistant.io/integrations/mcp_server/
Step B — Write .mcp.json
Read the HA URL from the boot status JSON (active_url field, already fetched in §3). Read the token from .env using:
${CLAUDE_PLUGIN_ROOT}/bin/ha-agent-lab boot status
For the token value, use the Read tool on .env (not Bash — the deny-pattern hook blocks any Bash argument containing the literal string TOKEN, including via bun -e). Parse the HOMEASSISTANT_TOKEN=... line in-memory and use the value directly when writing .mcp.json. Do not echo the token to the conversation or log it.
Check the project root for .mcp.json:
homeassistant entry:
url or Authorization value contains ${ (old placeholder format) → rewrite that entry with literal values and tell the user the stale entry was replaced.{
"mcpServers": {
"homeassistant": {
"type": "http",
"url": "<HOMEASSISTANT_URL>/api/mcp",
"headers": { "Authorization": "Bearer <HOMEASSISTANT_TOKEN>" }
}
}
}
Replace <HOMEASSISTANT_URL> with the active_url from boot status (resolves to HOMEASSISTANT_URL, or HOMEASSISTANT_LOCAL_URL for existing installs) and <HOMEASSISTANT_TOKEN> with the literal values read above.
The name homeassistant is required — skills and the safety hook match on mcp__homeassistant__* tool IDs.
Note:
.mcp.jsonnow contains a live bearer token. Claude Code reads MCP env vars from the process environment, not from.env, so literal values are required here.
After writing .mcp.json, check the project's .gitignore:
.mcp.json is absent from it → append .mcp.json on a new line.Step C — Activate and verify
Tell the user: restart Claude Code in this project directory. On first use, Claude Code will prompt you to trust the homeassistant server — approve it. Then run /mcp to confirm homeassistant appears as connected. The next ha-boot will verify live HA connectivity.
Run ${CLAUDE_PLUGIN_ROOT}/bin/ha-agent-lab boot status --probe and present the result. If it fails:
HOMEASSISTANT_LOCAL_URL in .env.HOMEASSISTANT_TOKEN.Resolve target file: Read .claude-code-hermit/state/hatch-options.json. Use the "target" field:
"local" → target_file = CLAUDE.local.md
"committed" or absent → target_file = CLAUDE.md
If the file doesn't exist (no hatch-options.json yet — operator's core hermit predates 1.1.1): detect core_install_scope from claude plugin list --json using the same precedence rules as core hatch Step 1.5 item 2 (filter entries where plugin name is claude-code-hermit and enabled == true; precedence local > project (both require projectPath == project root) > user (any projectPath) > null; map project → committed, local/user/null → local). Ask with AskUserQuestion (header: "Visibility") — scope-derived default at position 0 with (recommended): .local files (gitignored — operator-personal) / Committed files (shared with teammates). Write the canonical 5-field schema to .claude-code-hermit/state/hatch-options.json:
{
"target": "<choice>",
"core_install_scope": "<project|local|user|null>",
"stamped_at": "<current ISO 8601 timestamp with timezone offset>",
"stamped_by": "claude-code-homeassistant-hermit:hatch",
"version": "<current ha-hermit plugin version from ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json>"
}
Read the plugin version from ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json and the stamped version from .claude-code-hermit/config.json at _hermit_versions["claude-code-homeassistant-hermit"] (treat absent as null). Step 8 of this skill stamps that field at the end of every run, so on re-runs it reflects the version that last wrote the block. Look for the marker <!-- claude-code-homeassistant-hermit: Home Assistant Workflow --> in target_file:
${CLAUDE_PLUGIN_ROOT}/state-templates/CLAUDE-APPEND.md and either append it to target_file (marker absent — the Edit tool creates target_file if missing) or replace the marked block (marker present) — everything from the opening <!-- claude-code-homeassistant-hermit: Home Assistant Workflow --> through the matching closing <!-- /claude-code-homeassistant-hermit: Home Assistant Workflow -->, inclusive. The template is the source of truth; no operator prompt is needed.Stray-block migration (block stranded in the non-target file after a target flip) is handled one-shot by the Upgrade Instructions in this version's CHANGELOG entry, executed by hermit-evolve Step 7. Hatch itself stays focused on target-aware setup and steady-state refresh.
Read ha_safety_mode from .claude-code-hermit/config.json.
AskUserQuestion: "Current safety mode is <value>. Change it?" Yes → re-prompt. No → skip this step.lock, alarm_control_panel, security-related cover/button/switch):
strict (recommended) — always block autonomous actuation; work goes through a proposal instead.ask — operator is prompted before any actuation of a sensitive entity. Build/validate normally; both YAML apply and direct MCP calls require an explicit operator confirmation before execution.Write the chosen value to config.json as ha_safety_mode. Default to strict if the operator skips or is unsure.
Read ha_assist_control_enabled from .claude-code-hermit/config.json.
HassTurnOn, HassLightSet, etc.) pass through the safety gate and HA's expose-to-Assist setting controls which devices the agent can reach. Requires the HA MCP Server's control tools to be enabled and each entity exposed in HA (Settings → Voice assistants → Expose)."
ha_assist_control_enabled: true to config.json.Read ha_update_auto_apply from .claude-code-hermit/config.json.
ha-update-check always proposes pending Home Assistant updates for your review. With this on, accepting an add-on or HACS update installs it immediately (HA backs it up first, and rolls back on failure). Core, OS, and Supervisor updates always wait for your explicit go-ahead in chat, even with this enabled — those can affect dashboard access and have no software undo."
ha_update_auto_apply: true to config.json.Read .claude-code-hermit/knowledge-schema.md.
Check if either - analysis: or - **analysis**: is present in the file. This string only appears as the last Raw Captures bullet written — so its presence means both blocks were fully written on a prior run.
If absent, append the following block under ## Work Products (create the section header if the base schema only has a template stub):
- brief: morning/evening house brief. location: compiled/brief-<morning|evening>-<date>.md
- context: foundational house/system profile. location: compiled/context-house-profile-<date>.md
- presence-report: presence history and tracker diagnostics. location: compiled/presence-report-<date>.md
And under ## Raw Captures (create if absent):
- audit: HA operational audit (safety, integration-health, context-refresh). location: raw/audit-ha-<type>-<date>.md
- simulation: HA automation simulation result. location: raw/audit-ha-simulation-<slug>-<date>.md
- apply: HA automation apply result. location: raw/audit-ha-apply-<slug>-<date>.md
- remove: HA automation/script delete audit. location: raw/audit-ha-remove-<slug>-<date>.md
- analysis: HA pattern analysis. location: raw/patterns-<date>.md
If already present: skip (idempotent).
Use Edit to make the changes.
Run bun ${CLAUDE_PLUGIN_ROOT}/scripts/automode-env.ts .claude/settings.local.json — always .claude/settings.local.json, regardless of hatch_target: Claude Code's auto-mode classifier reads autoMode config only from local/user scope, never a committed project .claude/settings.json. This names the operator's Home Assistant instance (read from .env's HOMEASSISTANT_URL/HOMEASSISTANT_LOCAL_URL/HOMEASSISTANT_REMOTE_URL — the same set curl-host-gate.ts already trusts) as a trusted internal domain, so the classifier stops treating the hermit's nightly unattended reads (briefs, audits, context refresh) as unrecognized outbound calls. If the script prints SKIP|... (no HA URL configured yet), note it and move on — Step 3 already required a working .env before reaching here, so this should only skip on an unusual re-run. Additive and idempotent; safe to re-run on every hatch.
Write _hermit_versions["claude-code-homeassistant-hermit"] into .claude-code-hermit/config.json with the current plugin version.
Compiled templates: Copy ${CLAUDE_PLUGIN_ROOT}/state-templates/compiled/acknowledged-violations.md to .claude-code-hermit/compiled/acknowledged-violations.md if that file does not already exist. Set created in the frontmatter to today's ISO date. This gives the operator a ready-to-use suppression list for the safety audit.
Boot skill registration: Read config.boot_skill from config.json.
The skill name format is /<plugin-id>:<skill-id>. Parse the plugin-id as the text between / and :.
null or absent → set it to /claude-code-homeassistant-hermit:ha-boot./claude-code-homeassistant-hermit: → no-op (report "already set").<value> from another plugin — skipping to avoid conflict. Run /claude-code-hermit:hermit-settings boot-skill to update it manually."HA routine registration: config.routines is an array of objects with {id, schedule, skill, enabled, run_during_waiting}. For each HA routine below, check whether an entry with that id already exists in the array. If it does, skip. If not, prompt and merge it in.
Context refresh — "Add daily HA context-refresh routine (08:30 every day)? Keeps entity snapshots fresh automatically."
{"id": "daily-ha-context", "schedule": "30 8 * * *", "skill": "claude-code-homeassistant-hermit:ha-refresh-context", "enabled": true, "run_during_waiting": false}
Morning brief — three paths based on the current config.routines state:
Fresh install (no entry with id: "morning-brief" exists): prompt — "Add morning house brief routine? Delivers a unified morning summary combining house state and hermit context."
morning routine)? Recommended for always-on setups."
{"id": "morning-brief", "schedule": "30 8 * * *", "skill": "claude-code-homeassistant-hermit:ha-morning-brief", "enabled": true, "run_during_waiting": true}. If config.routines contains an entry with id: "morning" and enabled: true, set its enabled to false and emit: "Disabled core morning routine — morning-brief subsumes it."{"id": "morning-brief", "schedule": "0 9 * * *", "skill": "claude-code-homeassistant-hermit:ha-morning-brief", "enabled": false, "run_during_waiting": false}.Re-hatch upgrade (entry with id: "morning-brief" exists but has schedule: "0 9 * * *" OR run_during_waiting: false): prompt — "Your morning-brief routine uses the old schedule (09:00, not firing during waiting). Upgrade to unified mode (08:30, always-on)?"
schedule: "30 8 * * *", enabled: true, run_during_waiting: true. Then disable core morning if present and enabled (same logic as fresh install unified path).Already current (entry exists with schedule: "30 8 * * *" and run_during_waiting: true): skip (no-op, report "config is current — check enabled flag if the routine isn't firing").
Non-standard config (entry exists but matches none of the above conditions — e.g. custom schedule): skip (no-op, report "non-standard morning-brief config detected — skipping upgrade prompt").
Evening brief — "Add evening house-check routine (22:30 every day)? Delivers a brief end-of-day security and device confirmation."
{"id": "evening-brief", "schedule": "30 22 * * *", "skill": "claude-code-homeassistant-hermit:ha-evening-brief", "enabled": true, "run_during_waiting": true}. If config.routines contains an entry with id: "evening" and enabled: true, set its enabled to false and emit: "Disabled core evening routine — evening-brief subsumes it."After adding or updating any entries, remind the operator: "Run /claude-code-hermit:hermit-routines load to activate routines in the current session."
Scheduled checks registration: config.scheduled_checks is an array of periodic skill entries that the scheduled-checks routine (via reflect --scheduled-checks) invokes on a cadence and funnels through the proposal pipeline. For each entry below, check whether an existing record has the same id. If not, append it — no prompt needed, all three are safe read-only analyses.
{"id": "ha-patterns", "plugin": "claude-code-homeassistant-hermit", "skill": "claude-code-homeassistant-hermit:ha-analyze-patterns", "enabled": true, "trigger": "interval", "interval_days": 7}
{"id": "ha-safety-audit", "plugin": "claude-code-homeassistant-hermit", "skill": "claude-code-homeassistant-hermit:ha-safety-audit", "enabled": true, "trigger": "interval", "interval_days": 7}
{"id": "ha-integration-health", "plugin": "claude-code-homeassistant-hermit", "skill": "claude-code-homeassistant-hermit:ha-integration-health", "enabled": true, "trigger": "interval", "interval_days": 1}
{"id": "ha-update-check", "plugin": "claude-code-homeassistant-hermit", "skill": "claude-code-homeassistant-hermit:ha-update-check", "enabled": true, "trigger": "interval", "interval_days": 1}
These replace any need for CronCreate routines around analysis/observability — the scheduled-checks routine picks up whichever check is due, runs it, and any findings surface as proposals automatically.
Summarize:
hatch complete
✓ .env verified (user-managed)
✓ CLI: bin/ha-agent-lab boot status --probe → OK / FAILED
✓ .mcp.json: homeassistant entry written / already present
✓ CLAUDE.md updated
✓ config.json stamped v<version>
✓ boot_skill: /claude-code-homeassistant-hermit:ha-boot (set | already set | operator override preserved)
✓ Routines registered: daily-ha-context, morning-brief (disabled by default), evening-brief
✓ Scheduled checks registered: ha-patterns, ha-safety-audit, ha-integration-health, ha-update-check
✓ knowledge-schema.md: HA types added (or already present)
Manual steps remaining:
- Enable 'Model Context Protocol Server' integration in Home Assistant (if not done)
Settings → Devices & Services → Add Integration → search "MCP"
- Restart Claude Code and approve the 'homeassistant' server on first use
- Run /mcp to confirm 'homeassistant' is connected
Go always-on (recommended):
- Docker: /claude-code-hermit:docker-setup
Builds the container and walks you through channel pairing in one go.
- Bare tmux: .claude-code-hermit/bin/hermit-start
For channels (Discord/Telegram) with tmux, run
/claude-code-hermit:channel-setup first.
Prefer to test interactively first?
1. /claude-code-homeassistant-hermit:ha-boot
— single entry point: starts the hermit session, probes HA,
and auto-refreshes the context snapshot if stale/missing.
2. /claude-code-hermit:hermit-routines load
— activates scheduled routines in the current Claude session.
The always-on runtime does both of these automatically — the interactive
steps are only for a test drive before handing over to the runtime.
Ready to organize your house?
/claude-code-homeassistant-hermit:ha-setup-house
— create areas, assign entities and devices, provision helpers,
and scaffold starter automations.
Read by /claude-code-hermit:docker-security when the operator enables LAN containment + DNS policy. Each entry is surfaced as a per-entry confirmation prompt; nothing here is auto-applied.
The nabu.casa entry covers Nabu Casa Cloud (<id>.ui.nabu.casa) since dnsmasq's server=/nabu.casa/... pattern matches subdomains. home-assistant.io covers integration docs (www.home-assistant.io) and the developer API reference (developers.home-assistant.io) that skills consult when verifying REST/WebSocket endpoints. READ_FROM_ENV:HOMEASSISTANT_URL resolves to the hostname of the operator's configured HA instance — covers custom remote domains (e.g. ha.mydomain.com) that are not under nabu.casa. Operators on a self-hosted local HA instance should accept ASK_OPERATOR_FOR_HA_IP and provide the LAN IP of their HA box. mDNS / homeassistant.local does not work through dnsmasq — use the IP directly.
npx claudepluginhub p/gtapps-claude-code-homeassistant-hermit-plugins-claude-code-homeassistant-hermitBuilds and manages Home Assistant configurations using the hab CLI, automating tasks like dashboard edits, automation creation, backups, and ESPHome operations.
Applies agentic design patterns (tool use, routing, planning, exception handling, memory) to Home Assistant for smart home automation and IoT orchestration.
Edit, validate, deploy, and verify Home Assistant YAML configs (automations, blueprints, scripts, scenes, templates, dashboards) over SSH, hass-cli, or MCP with a safe reload-vs-restart pipeline.