| name | home-assistant-best-practices |
| description | Best practices for HA automations, helpers, scripts, and dashboards.
TRIGGER THIS SKILL WHEN: - Creating or editing automations, scripts, scenes, dashboards, blueprints - Choosing template sensors, helpers, or Jinja macros - Restructuring triggers, conditions, or modes; button, remote, or event-entity automations - Renaming entities or migrating device_id to entity_id - Looking up card types or domain docs; writing AppDaemon apps - Deleting or restoring a backup, or upgrading Core or the OS
SYMPTOMS: - Jinja2 templates where native options exist - device_id used instead of entity_id - Entity IDs changed without checking consumers - Wrong automation mode chosen - Raw sensor or hard-coded value used where a helper belongs - Direct .storage edits, or generated YAML snippets - User told to edit configuration.yaml for UI integrations - Hardcoded Blueprint entities or skipped selectors - Existing state changed with no recovery path - Jinja copy-pasted between templates
|
| metadata | {"version":"26"} |
Home Assistant Best Practices
Core principle: Use native Home Assistant constructs wherever possible. Templates bypass validation, fail silently at runtime, and make debugging opaque.
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 safe-refactoring 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 a purpose-specific, then generic native, trigger/condition
Since 2026.7 the default building blocks are purpose-specific triggers/conditions — <domain>.<name> keys (motion detected, battery low, door opened) with area/floor/label targets. Check for one that matches the intent first, then a generic native trigger/condition, and only then a template. See automation-patterns #purpose-specific-triggers--conditions-default-since-20267.
Common substitutions:
- List of individual sensor entities in a trigger → one purpose-specific trigger with an area/floor/label
target:
{{ 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 safe-refactoring #trigger-restructuring)
2. Check for built-in helper or Template Helper
Before creating a template sensor, check helper-selection.
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
If no built-in helper fits, use a Template Helper — not YAML.
Create it via the HA config flow (programmatically or in the UI:
Settings → Devices & Services → Helpers → Create Helper → Template). A flow-created helper
is UI-editable; a template: YAML entry needs a template.reload and is not.
Write template: YAML when the user asks for it, when neither path is available, or when
the config needs a key the flow has no field for — trigger-based templates and attributes:
are the common ones. Then use managed YAML editing (yaml-only-integrations), not a hand-edit.
3. Select correct automation mode
Default single mode is often wrong. See automation-patterns #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 device-control.
Exception: Zigbee2MQTT autodiscovered device triggers are acceptable.
5. For buttons and remotes
- Any integration exposing an
event.* entity: Use event.received targeting that entity — a normal entity, so it can be renamed and survives a re-add when the integration keeps a stable unique ID
- ZHA: No event entities — use an
event trigger with device_ieee (persistent)
- Z2M: Event entities are experimental and off by default — use a
device trigger (autodiscovered) or mqtt trigger
See device-control #buttonremote-patterns.
Critical Anti-Patterns
Reference Files
Read these when you need detailed information:
| File | When to read |
|---|
| safe-refactoring | Renaming entities, replacing helpers, restructuring automations, or any modification to existing config |
| automation-patterns | Writing triggers, conditions, waits, variables, or choosing automation modes; capturing action responses; documenting/annotating steps; disabling automations; continue_on_error, stopping a sequence, repeat, if/then vs choose, parallel, trigger IDs |
| helper-selection | Deciding whether to use a built-in helper vs template sensor — aggregation, rate of change, thresholds, time-in-state, counting/timing, scheduling, grouping, probabilistic inference, smoothing, climate, domain conversion, decision matrix |
| template-guidelines | Confirming templates ARE appropriate for a use case; sharing Jinja logic between templates with custom_templates macros |
| yaml-only-integrations | Creating or editing YAML-only integrations that have no config flow (e.g. command_line, platform-based mqtt, rest) |
| device-control | Writing actions, button/remote automations, or using target: |
| scenes | Authoring or activating scenes; snapshot/restore patterns; snapshot-vs-script distinction |
| dashboard-guide | Designing or modifying Lovelace dashboards — layout, view types, strategies, sections, cards, badges, CSS styling, HACS |
| dashboard-cards |