Home Assistant Best Practices
Core principle: Use native Home Assistant constructs wherever possible. Templates bypass validation, fail silently at runtime, and make debugging opaque.
If task scope is unclear, route first with: homeassistant-router/SKILL.md.
For scripted bulk execution, use: homeassistant-ops/SKILL.md.
For interactive runtime CLI execution, use: home-assistant-cli-ops/SKILL.md.
Decision Workflow
Follow this sequence when creating any automation:
0. Gate: modifying existing config?
If your change affects entity IDs or cross-component references — renaming entities, replacing template sensors with helpers, converting device triggers, or restructuring automations — read references/safe-refactoring.md first. That reference covers impact analysis, device-sibling discovery, and post-change verification. Complete its workflow before proceeding.
Steps 1-5 below apply to new config or pattern evaluation.
1. Check for native condition/trigger
Before writing any template, check references/automation-patterns.md for native alternatives.
Common substitutions:
{{ states('x') | float > 25 }} → numeric_state condition with above: 25
{{ is_state('x', 'on') and is_state('y', 'on') }} → condition: and with state conditions
{{ now().hour >= 9 }} → condition: time with after: "09:00:00"
wait_template: "{{ is_state(...) }}" → wait_for_trigger with state trigger (caveat: different behavior when state is already true — see references/safe-refactoring.md#trigger-restructuring)
2. Check for built-in helper
Before creating a template sensor, check references/helper-selection.md.
Common substitutions:
- Sum/average multiple sensors →
min_max integration
- Binary any-on/all-on logic →
group helper
- Rate of change →
derivative integration
- Cross threshold detection →
threshold integration
- Consumption tracking →
utility_meter helper
3. Select correct automation mode
Default single mode is often wrong. See references/automation-patterns.md#automation-modes.
| Scenario |
Mode |
| Motion light with timeout |
restart |
| Sequential processing (door locks) |
queued |
| Independent per-entity actions |
parallel |
| One-shot notifications |
single |
4. Use entity_id over device_id
device_id breaks when devices are re-added. See references/device-control.md.
Exception: Zigbee2MQTT autodiscovered device triggers are acceptable.
5. For Zigbee buttons/remotes
- ZHA: Use
event trigger with device_ieee (persistent)
- Z2M: Use
device trigger (autodiscovered) or mqtt trigger
See references/device-control.md#zigbee-buttonremote-patterns.
Critical Anti-Patterns
| Anti-pattern |
Use instead |
Why |
Reference |
condition: template with float > 25 |
condition: numeric_state |
Validated at load, not runtime |
references/automation-patterns.md#native-conditions |
wait_template: "{{ is_state(...) }}" |
wait_for_trigger with state trigger |
Event-driven, not polling; waits for change (see references/safe-refactoring.md#trigger-restructuring for semantic differences) |
references/automation-patterns.md#wait-actions |
device_id in triggers |
entity_id (or device_ieee for ZHA) |
device_id breaks on re-add |
references/device-control.md#entity-id-vs-device-id |
mode: single for motion lights |
mode: restart |
Re-triggers must reset the timer |
references/automation-patterns.md#automation-modes |
| Template sensor for sum/mean |
min_max helper |
Declarative, handles unavailable states |
references/helper-selection.md#numeric-aggregation |
| Template binary sensor with threshold |
threshold helper |
Built-in hysteresis support |
references/helper-selection.md#threshold |
| Renaming entity IDs without impact analysis |
Follow references/safe-refactoring.md workflow |
Renames break dashboards, scripts, and scenes silently |
references/safe-refactoring.md#entity-renames |
Reference Files
Read these when you need detailed information:
| File |
When to read |
Key sections |
references/safe-refactoring.md |
Renaming entities, replacing helpers, restructuring automations, or any modification to existing config |
#universal-workflow, #entity-renames, #helper-replacements, #trigger-restructuring |
references/automation-patterns.md |
Writing triggers, conditions, waits, or choosing automation modes |
#native-conditions, #trigger-types, #wait-actions, #automation-modes, #ifthen-vs-choose, #trigger-ids |
references/helper-selection.md |
Deciding whether to use a built-in helper vs template sensor |
#numeric-aggregation, #rate-and-change, #time-based-tracking, #counting-and-timing, #scheduling, #entity-grouping, #decision-matrix |
references/template-guidelines.md |
Confirming templates ARE appropriate for a use case |
#when-templates-are-appropriate, #when-to-avoid-templates, #template-sensor-best-practices, #common-patterns, #error-handling |
references/device-control.md |
Writing service calls, Zigbee button automations, or using target: |
#entity-id-vs-device-id, #service-calls-best-practices, #zigbee-buttonremote-patterns, #domain-specific-patterns |
references/examples.yaml |
Need compound examples combining multiple best practices |
— |
1---2name: home-assistant-best-practices3description: Design and refactor Home Assistant automations safely, when choosing native triggers/conditions/helpers, automation modes, Zigbee button patterns, or entity_id vs device_id targeting, resulting in validated, maintainable configs.4---56# Home Assistant Best Practices78**Core principle:** Use native Home Assistant constructs wherever possible. Templates bypass validation, fail silently at runtime, and make debugging opaque.910If task scope is unclear, route first with: `homeassistant-router/SKILL.md`.11For scripted bulk execution, use: `homeassistant-ops/SKILL.md`.12For interactive runtime CLI execution, use: `home-assistant-cli-ops/SKILL.md`.1314## Decision Workflow1516Follow this sequence when creating any automation:1718### 0. Gate: modifying existing config?1920If your change affects entity IDs or cross-component references — renaming entities, replacing template sensors with helpers, converting device triggers, or restructuring automations — read `references/safe-refactoring.md` first. That reference covers impact analysis, device-sibling discovery, and post-change verification. Complete its workflow before proceeding.2122Steps 1-5 below apply to new config or pattern evaluation.2324### 1. Check for native condition/trigger25Before writing any template, check `references/automation-patterns.md` for native alternatives.2627**Common substitutions:**28- `{{ states('x') | float > 25 }}` → `numeric_state` condition with `above: 25`29- `{{ is_state('x', 'on') and is_state('y', 'on') }}` → `condition: and` with state conditions30- `{{ now().hour >= 9 }}` → `condition: time` with `after: "09:00:00"`31- `wait_template: "{{ is_state(...) }}"` → `wait_for_trigger` with state trigger (caveat: different behavior when state is already true — see `references/safe-refactoring.md#trigger-restructuring`)3233### 2. Check for built-in helper34Before creating a template sensor, check `references/helper-selection.md`.3536**Common substitutions:**37- Sum/average multiple sensors → `min_max` integration38- Binary any-on/all-on logic → `group` helper39- Rate of change → `derivative` integration40- Cross threshold detection → `threshold` integration41- Consumption tracking → `utility_meter` helper4243### 3. Select correct automation mode44Default `single` mode is often wrong. See `references/automation-patterns.md#automation-modes`.4546| Scenario | Mode |47|----------|------|48| Motion light with timeout | `restart` |49| Sequential processing (door locks) | `queued` |50| Independent per-entity actions | `parallel` |51| One-shot notifications | `single` |5253### 4. Use entity_id over device_id54`device_id` breaks when devices are re-added. See `references/device-control.md`.5556**Exception:** Zigbee2MQTT autodiscovered device triggers are acceptable.5758### 5. For Zigbee buttons/remotes59- **ZHA:** Use `event` trigger with `device_ieee` (persistent)60- **Z2M:** Use `device` trigger (autodiscovered) or `mqtt` trigger6162See `references/device-control.md#zigbee-buttonremote-patterns`.6364---6566## Critical Anti-Patterns6768| Anti-pattern | Use instead | Why | Reference |69|---|---|---|---|70| `condition: template` with `float > 25` | `condition: numeric_state` | Validated at load, not runtime | `references/automation-patterns.md#native-conditions` |71| `wait_template: "{{ is_state(...) }}"` | `wait_for_trigger` with state trigger | Event-driven, not polling; waits for *change* (see `references/safe-refactoring.md#trigger-restructuring` for semantic differences) | `references/automation-patterns.md#wait-actions` |72| `device_id` in triggers | `entity_id` (or `device_ieee` for ZHA) | device_id breaks on re-add | `references/device-control.md#entity-id-vs-device-id` |73| `mode: single` for motion lights | `mode: restart` | Re-triggers must reset the timer | `references/automation-patterns.md#automation-modes` |74| Template sensor for sum/mean | `min_max` helper | Declarative, handles unavailable states | `references/helper-selection.md#numeric-aggregation` |75| Template binary sensor with threshold | `threshold` helper | Built-in hysteresis support | `references/helper-selection.md#threshold` |76| Renaming entity IDs without impact analysis | Follow `references/safe-refactoring.md` workflow | Renames break dashboards, scripts, and scenes silently | `references/safe-refactoring.md#entity-renames` |7778---7980## Reference Files8182Read these when you need detailed information:8384| File | When to read | Key sections |85|------|--------------|--------------|86| `references/safe-refactoring.md` | Renaming entities, replacing helpers, restructuring automations, or any modification to existing config | `#universal-workflow`, `#entity-renames`, `#helper-replacements`, `#trigger-restructuring` |87| `references/automation-patterns.md` | Writing triggers, conditions, waits, or choosing automation modes | `#native-conditions`, `#trigger-types`, `#wait-actions`, `#automation-modes`, `#ifthen-vs-choose`, `#trigger-ids` |88| `references/helper-selection.md` | Deciding whether to use a built-in helper vs template sensor | `#numeric-aggregation`, `#rate-and-change`, `#time-based-tracking`, `#counting-and-timing`, `#scheduling`, `#entity-grouping`, `#decision-matrix` |89| `references/template-guidelines.md` | Confirming templates ARE appropriate for a use case | `#when-templates-are-appropriate`, `#when-to-avoid-templates`, `#template-sensor-best-practices`, `#common-patterns`, `#error-handling` |90| `references/device-control.md` | Writing service calls, Zigbee button automations, or using target: | `#entity-id-vs-device-id`, `#service-calls-best-practices`, `#zigbee-buttonremote-patterns`, `#domain-specific-patterns` |91| `references/examples.yaml` | Need compound examples combining multiple best practices | — |