Home Assistant Manager
Operate a remote Home Assistant instance precisely: make a change, get it live, prove it
worked. Optimize for the fewest safe round-trips.
Assumptions
- The repo you're editing is the HA
/config dir, git-connected to the instance.
Edits aren't live until pulled on the instance.
root@homeassistant.local in examples is a placeholder. Resolve the real user/host once
(project CLAUDE.md, ~/.ssh/config, or ask) and if it isn't recorded in the project
CLAUDE.md yet, add it so future sessions skip this step.
- Access via one or more of:
hass-cli (REST), SSH ha, or an MCP server (see below).
- Only edit
.yaml/.yml/.md. Never read/write .env or secrets.yaml; use !secret.
Remote access — pick the right tool
- SSH
ha — always works, needs no local env. Use for ha core check|restart|logs|info.
hass-cli (REST) — state/service calls, but needs HASS_SERVER/HASS_TOKEN in the
shell before the session starts. If they're unset, hass-cli falls back to the wrong
host (localhost) and errors — don't retry, check [ -n "$HASS_TOKEN" ] once, then use
SSH or MCP instead.
- MCP (preferred when available) — first-class tools for live state/control, no env
juggling. Official
mcp_server integration (HA core ≥2025.2) or community ha-mcp
(richer, 80+ tools). Use it instead of shelling out when present.
The deploy pipeline (the one canonical flow)
Changes are not live until step 4.
- Edit YAML locally.
- Validate:
ssh root@homeassistant.local "ha core check" (slow, ~30-60s — see "when to
skip" below).
- Commit + push:
git add … && git commit -m "…" && git push.
- Make it live:
ssh root@homeassistant.local "cd /config && git pull".
- Apply: reload if possible, else restart (table below).
- Verify (next section).
Rapid iteration: skip git and scp straight to the instance, then reload — good for
dashboards and tight test loops. Commit to git only once stable.
scp automations.yaml root@homeassistant.local:/config/ → reload.
When to skip ha core check: it parses the whole config and is slow. For an isolated
YAML edit you're confident in, a domain reload surfaces errors faster and the logs tell
you immediately. Always run it before a restart or for configuration.yaml changes.
Reload vs restart
| Change |
Action |
| automations, scripts, scenes, groups, template entities, themes |
reload the domain (hass-cli service call automation.reload, etc.) |
configuration.yaml core, new integrations, platform sensors (min/max), MQTT sensor/binary_sensor platforms, dashboard registry (lovelace_dashboards) |
restart (ssh … "ha core restart", ~30s) |
Prefer reload. Never restart without a passing ha core check. Before risky changes
(core configuration.yaml surgery, removing an integration), snapshot first — it's cheap:
ssh root@homeassistant.local "ha backups new --name pre-<change>".
Verify — don't assume it worked
- Reload/restart the right domain.
- For automations, trigger manually for instant feedback:
hass-cli service call automation.trigger --arguments entity_id=automation.<id>
(or call the service via MCP). This bypasses conditions by default — it proves the
actions, not the gate. To test conditions too, pass skip_condition: false or exercise
the real trigger, then read the automation's trace in the UI.
- Read the logs filtered to your change:
ssh root@homeassistant.local "ha core logs | grep -iE '<name>|error' | tail -20".
Good: Running automation actions, Executing step …. Bad: Invalid data for call_service, TypeError, Template variable warning, Error executing script.
- Confirm the real outcome: device/sensor state (
hass-cli state get <entity>), or ask
the user for notification-type actions.
- On error: fix → re-pull/scp → reload → re-check. Loop until clean.
Automations — write modern syntax
HA 2024.10 renamed the keys; legacy syntax still works but don't emit it in new code:
top-level triggers:/conditions:/actions: (plural), trigger: not platform: inside a
trigger, action: not service: for calls. Every automation gets a stable id: (traces
and UI editing need it) plus an alias.
Full automation reference (syntax table, mode: behavior, blueprints, trace debugging,
pitfalls) → read reference/automations.md when writing or
debugging automations.
Templates — the precision rules
- Always coerce types before comparing:
states('sensor.x') | int(0) < 7. Bare states are
strings; '5' < 7 raises TypeError. Provide a default (int(0)) so startup None
doesn't error.
- Test in Developer Tools → Template before committing.
state_attr(...) returns None if the entity/attr is missing — guard it.
Conventions
- Surgical edits; preserve comments; 2-space indent.
- Validate before restart; prefer reload; verify from logs.
- Use context7 MCP for current HA docs before non-trivial or unfamiliar config.
Dashboards
Lovelace dashboards live in .storage/lovelace.* (JSON). UI edits show on a browser refresh;
direct file edits (scp/git) may not appear until a ha core restart — HA caches the
lovelace store in memory. Adding a new dashboard to .storage/lovelace_dashboards also needs
a restart. scp + refresh is the fast loop; validate JSON first:
python3 -m json.tool .storage/lovelace.x > /dev/null. After deploying, validate the UI
visually in the browser (see the reference) — logs/state won't catch a broken card or a
mis-sorted popup.
Full dashboard reference (view types, card catalog, template cards, tablet layout,
pitfalls, debugging) → read reference/dashboards.md when doing
UI work. Modern HA: native sections view (drag-drop grid, badges, heading cards) and
feature-rich tile cards now cover most needs without custom cards; reach for Mushroom
only when you want its specific look.
Quick reference
# Validate / apply
ssh root@homeassistant.local "ha core check"
ssh root@homeassistant.local "ha core restart"
ssh root@homeassistant.local "cd /config && git pull" # make pushed changes live
# Logs
ssh root@homeassistant.local "ha core logs | grep -iE 'error|<name>' | tail -20"
# State / services (needs env loaded, or use MCP)
hass-cli state get <entity>
hass-cli service call <domain>.reload
hass-cli service call automation.trigger --arguments entity_id=automation.<id>
# Rapid deploy
scp <file>.yaml root@homeassistant.local:/config/ && hass-cli service call automation.reload
1---2name: home-assistant-manager3description: Manage Home Assistant configuration safely and fast — edit and deploy YAML (automations, blueprints, scripts, scenes, templates, MQTT), validate with ha core check, deploy via git or rapid scp, reload-vs-restart correctly, verify changes from logs, traces and entity state, and build Lovelace dashboards. Use for any Home Assistant config, automation, template, or dashboard work over SSH/hass-cli/MCP.4---56# Home Assistant Manager78Operate a remote Home Assistant instance precisely: make a change, get it live, prove it9worked. Optimize for the fewest safe round-trips.1011## Assumptions12- The repo you're editing **is** the HA `/config` dir, git-connected to the instance.13 Edits aren't live until pulled on the instance.14- `root@homeassistant.local` in examples is a placeholder. Resolve the real user/host once15 (project CLAUDE.md, `~/.ssh/config`, or ask) and if it isn't recorded in the project16 CLAUDE.md yet, add it so future sessions skip this step.17- Access via one or more of: `hass-cli` (REST), SSH `ha`, or an MCP server (see below).18- Only edit `.yaml`/`.yml`/`.md`. Never read/write `.env` or `secrets.yaml`; use `!secret`.1920## Remote access — pick the right tool21- **SSH `ha`** — always works, needs no local env. Use for `ha core check|restart|logs|info`.22- **`hass-cli`** (REST) — state/service calls, but needs `HASS_SERVER`/`HASS_TOKEN` in the23 shell *before* the session starts. If they're unset, hass-cli falls back to the wrong24 host (localhost) and errors — don't retry, check `[ -n "$HASS_TOKEN" ]` once, then use25 SSH or MCP instead.26- **MCP** (preferred when available) — first-class tools for live state/control, no env27 juggling. Official `mcp_server` integration (HA core ≥2025.2) or community `ha-mcp`28 (richer, 80+ tools). Use it instead of shelling out when present.2930## The deploy pipeline (the one canonical flow)31Changes are not live until step 4.321. Edit YAML locally.332. Validate: `ssh root@homeassistant.local "ha core check"` (slow, ~30-60s — see "when to34 skip" below).353. Commit + push: `git add … && git commit -m "…" && git push`.364. **Make it live:** `ssh root@homeassistant.local "cd /config && git pull"`.375. Apply: **reload** if possible, else **restart** (table below).386. Verify (next section).3940**Rapid iteration:** skip git and `scp` straight to the instance, then reload — good for41dashboards and tight test loops. Commit to git only once stable.42`scp automations.yaml root@homeassistant.local:/config/` → reload.4344**When to skip `ha core check`:** it parses the whole config and is slow. For an isolated45YAML edit you're confident in, a domain reload surfaces errors faster and the logs tell46you immediately. Always run it before a *restart* or for `configuration.yaml` changes.4748## Reload vs restart49| Change | Action |50|--------|--------|51| automations, scripts, scenes, groups, template entities, themes | **reload** the domain (`hass-cli service call automation.reload`, etc.) |52| `configuration.yaml` core, new integrations, platform sensors (min/max), MQTT sensor/binary_sensor platforms, dashboard registry (`lovelace_dashboards`) | **restart** (`ssh … "ha core restart"`, ~30s) |5354Prefer reload. Never restart without a passing `ha core check`. Before risky changes55(core `configuration.yaml` surgery, removing an integration), snapshot first — it's cheap:56`ssh root@homeassistant.local "ha backups new --name pre-<change>"`.5758## Verify — don't assume it worked591. Reload/restart the right domain.602. For automations, **trigger manually** for instant feedback:61 `hass-cli service call automation.trigger --arguments entity_id=automation.<id>`62 (or call the service via MCP). This **bypasses `conditions` by default** — it proves the63 actions, not the gate. To test conditions too, pass `skip_condition: false` or exercise64 the real trigger, then read the automation's trace in the UI.653. Read the logs filtered to your change:66 `ssh root@homeassistant.local "ha core logs | grep -iE '<name>|error' | tail -20"`.67 Good: `Running automation actions`, `Executing step …`. Bad: `Invalid data for68 call_service`, `TypeError`, `Template variable warning`, `Error executing script`.694. Confirm the real outcome: device/sensor state (`hass-cli state get <entity>`), or ask70 the user for notification-type actions.715. On error: fix → re-pull/scp → reload → re-check. Loop until clean.7273## Automations — write modern syntax74HA 2024.10 renamed the keys; legacy syntax still works but don't emit it in new code:75top-level `triggers:/conditions:/actions:` (plural), `trigger:` not `platform:` inside a76trigger, `action:` not `service:` for calls. Every automation gets a stable `id:` (traces77and UI editing need it) plus an `alias`.7879**Full automation reference** (syntax table, `mode:` behavior, blueprints, trace debugging,80pitfalls) → read [`reference/automations.md`](reference/automations.md) when writing or81debugging automations.8283## Templates — the precision rules84- Always coerce types before comparing: `states('sensor.x') | int(0) < 7`. Bare states are85 strings; `'5' < 7` raises `TypeError`. Provide a default (`int(0)`) so startup `None`86 doesn't error.87- Test in **Developer Tools → Template** before committing.88- `state_attr(...)` returns `None` if the entity/attr is missing — guard it.8990## Conventions91- Surgical edits; preserve comments; 2-space indent.92- Validate before restart; prefer reload; verify from logs.93- Use context7 MCP for current HA docs before non-trivial or unfamiliar config.9495## Dashboards96Lovelace dashboards live in `.storage/lovelace.*` (JSON). UI edits show on a browser refresh;97**direct file edits (scp/git) may not appear until a `ha core restart`** — HA caches the98lovelace store in memory. Adding a *new* dashboard to `.storage/lovelace_dashboards` also needs99a restart. `scp` + refresh is the fast loop; validate JSON first:100`python3 -m json.tool .storage/lovelace.x > /dev/null`. After deploying, **validate the UI101visually in the browser** (see the reference) — logs/state won't catch a broken card or a102mis-sorted popup.103104**Full dashboard reference** (view types, card catalog, template cards, tablet layout,105pitfalls, debugging) → read [`reference/dashboards.md`](reference/dashboards.md) when doing106UI work. Modern HA: native **sections** view (drag-drop grid, badges, `heading` cards) and107feature-rich **tile** cards now cover most needs without custom cards; reach for Mushroom108only when you want its specific look.109110## Quick reference111```bash112# Validate / apply113ssh root@homeassistant.local "ha core check"114ssh root@homeassistant.local "ha core restart"115ssh root@homeassistant.local "cd /config && git pull" # make pushed changes live116117# Logs118ssh root@homeassistant.local "ha core logs | grep -iE 'error|<name>' | tail -20"119120# State / services (needs env loaded, or use MCP)121hass-cli state get <entity>122hass-cli service call <domain>.reload123hass-cli service call automation.trigger --arguments entity_id=automation.<id>124125# Rapid deploy126scp <file>.yaml root@homeassistant.local:/config/ && hass-cli service call automation.reload127```