From 1d74397f2602a948003d0351efa8d0b65651ba3e Mon Sep 17 00:00:00 2001 From: kingpanther13 Date: Sun, 7 Jun 2026 15:45:03 -0400 Subject: [PATCH] docs: cover pre-2026.6 automation, helper, dashboard & scene gaps Fills coverage gaps that predate 2026.6 (issue #50): automation-patterns.md - Trigger Types: zone, template, calendar, webhook, homeassistant, conversation, tag, geo_location, persistent_notification triggers; fire-an-event action note - Native Conditions: trigger condition; device-condition caveat note - New sections: Parallel Actions (vs mode: parallel), Stopping a Sequence (stop:), Variables (+ trigger_variables caveat), Capturing Action Responses (response_variable); delay: action; per-element enabled: helper-selection.md - trend, bayesian (new Probabilistic Inference section), mold_indicator - 'How Helpers Are Created': storage-collection vs config-entry-flow classification (HA mechanisms, no tool names) - TOC + decision-matrix rows dashboard-guide.md - Dashboard Strategies (+ 'Take control'); heading card; markdown card; grid_options card sizing; expanded visibility conditions (screen, etc.); typed entity/object badge form; drop stale (2024+) heading qualifier references/scenes.md (new) - Scene config shape, turn_on/create/apply/reload, snapshot-vs-script - SKILL.md Reference Files + README Skill Contents updated Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 1 + skills/home-assistant-best-practices/SKILL.md | 7 +- .../references/automation-patterns.md | 253 +++++++++++++++++- .../references/dashboard-guide.md | 116 +++++++- .../references/helper-selection.md | 116 +++++++- .../references/scenes.md | 92 +++++++ 6 files changed, 567 insertions(+), 18 deletions(-) create mode 100644 skills/home-assistant-best-practices/references/scenes.md diff --git a/README.md b/README.md index b6807cf..5a73f29 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ The `home-assistant-best-practices` skill includes: | `references/template-guidelines.md` | When to use templates, when to avoid them, and sensor best practices | | `references/yaml-only-integrations.md` | YAML-only integration types, post-edit actions (reload vs restart) | | `references/device-control.md` | Service calls, entity_id vs device_id, Zigbee buttons | +| `references/scenes.md` | Scene authoring: config shape, snapshot/restore, snapshot-vs-script distinction | | `references/dashboard-guide.md` | Dashboard layout, view types, sections, custom cards, CSS styling | | `references/dashboard-cards.md` | Card type lookup and card-specific documentation | | `references/domain-docs.md` | Integration and domain documentation for service calls, entity attributes | diff --git a/skills/home-assistant-best-practices/SKILL.md b/skills/home-assistant-best-practices/SKILL.md index efee982..50e7929 100644 --- a/skills/home-assistant-best-practices/SKILL.md +++ b/skills/home-assistant-best-practices/SKILL.md @@ -123,12 +123,13 @@ 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`, `#config-entry-data--blind-spots-for-entity-registry-renames`, `#storage-mode-dashboards-storagelovelace` | -| `references/automation-patterns.md` | Writing triggers, conditions, waits, or choosing automation modes; disabling automations | `#native-conditions`, `#trigger-types`, `#wait-actions`, `#automation-modes`, `#continue-on-error`, `#repeat-actions`, `#ifthen-vs-choose`, `#trigger-ids`, `#disabling-automations` | -| `references/helper-selection.md` | Deciding whether to use a built-in helper vs template sensor | `#menu-based-helpers`, `#numeric-aggregation`, `#rate-and-change`, `#time-based-tracking`, `#counting-and-timing`, `#scheduling`, `#entity-grouping`, `#data-smoothing`, `#random-values`, `#climate-control`, `#domain-conversion`, `#template-helpers`, `#decision-matrix` | +| `references/automation-patterns.md` | Writing triggers, conditions, waits, variables, or choosing automation modes; capturing action responses; disabling automations | `#native-conditions`, `#trigger-types`, `#wait-actions`, `#automation-modes`, `#continue-on-error`, `#stopping-a-sequence`, `#variables`, `#capturing-action-responses`, `#repeat-actions`, `#ifthen-vs-choose`, `#parallel-actions`, `#trigger-ids`, `#disabling-automations` | +| `references/helper-selection.md` | Deciding whether to use a built-in helper vs template sensor | `#how-helpers-are-created`, `#menu-based-helpers`, `#numeric-aggregation`, `#rate-and-change`, `#time-based-tracking`, `#counting-and-timing`, `#scheduling`, `#entity-grouping`, `#probabilistic-inference`, `#data-smoothing`, `#random-values`, `#climate-control`, `#domain-conversion`, `#template-helpers`, `#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/yaml-only-integrations.md` | Creating or editing YAML-only integrations that have no config flow (e.g. `command_line`, platform-based `mqtt`, `rest`) | `#yaml-only-integration-types`, `#post-edit-actions` | | `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/dashboard-guide.md` | Designing or modifying Lovelace dashboards — layout, view types, sections, custom cards, CSS styling, HACS | `#dashboard-structure`, `#view-types`, `#built-in-cards`, `#features`, `#custom-cards`, `#css-styling`, `#common-pitfalls` | +| `references/scenes.md` | Authoring or activating scenes; snapshot/restore patterns; snapshot-vs-script distinction | `#scene-config-shape`, `#activating-a-scene`, `#snapshot--restore-scenecreate`, `#apply-states-without-storing-sceneapply` | +| `references/dashboard-guide.md` | Designing or modifying Lovelace dashboards — layout, view types, strategies, sections, cards, badges, CSS styling, HACS | `#dashboard-structure`, `#view-types`, `#dashboard-strategies`, `#built-in-cards`, `#features`, `#badges`, `#custom-cards`, `#css-styling`, `#common-pitfalls` | | `references/dashboard-cards.md` | Looking up available card types or fetching card-specific documentation | — | | `references/domain-docs.md` | Looking up integration or domain documentation for service calls, entity attributes, or configuration | — | | `references/examples.yaml` | Need compound examples combining multiple best practices | — | diff --git a/skills/home-assistant-best-practices/references/automation-patterns.md b/skills/home-assistant-best-practices/references/automation-patterns.md index 3efbf2c..e4d039e 100644 --- a/skills/home-assistant-best-practices/references/automation-patterns.md +++ b/skills/home-assistant-best-practices/references/automation-patterns.md @@ -8,10 +8,14 @@ This document covers native Home Assistant automation constructs that should be 3. [Wait Actions](#wait-actions) 4. [Automation Modes](#automation-modes) 5. [Continue on Error](#continue-on-error) -6. [Repeat Actions](#repeat-actions) -7. [if/then vs choose](#ifthen-vs-choose) -8. [Trigger IDs](#trigger-ids) -9. [Disabling Automations](#disabling-automations) +6. [Stopping a Sequence](#stopping-a-sequence) +7. [Variables](#variables) +8. [Capturing Action Responses](#capturing-action-responses) +9. [Repeat Actions](#repeat-actions) +10. [if/then vs choose](#ifthen-vs-choose) +11. [Parallel Actions](#parallel-actions) +12. [Trigger IDs](#trigger-ids) +13. [Disabling Automations](#disabling-automations) --- @@ -201,6 +205,18 @@ conditions: value_template: "{{ trigger.to_state.attributes.brightness > 100 }}" ``` +### Trigger Condition + +Matches *which* trigger started the run, by trigger `id` — the clean way to branch on the trigger (see [Trigger IDs](#trigger-ids)) instead of templating `trigger.id`. Only true when the run was started by a trigger (false in scripts and manual runs). + +```yaml +condition: trigger +id: motion_on # a single id, or a list (OR semantics): +# id: +# - motion_on +# - motion_off +``` + --- ## Trigger Types @@ -342,6 +358,16 @@ conditions: - "{{ trigger.platform == 'event' and 'light.kitchen' in trigger.event.data.entity_id }}" ``` +**Firing an event** (the action counterpart of this trigger) — useful for decoupling automations (one fires `my_event`, several others trigger on it): + +```yaml +actions: + - event: my_custom_event + event_data: + source: kitchen + value: "{{ states('sensor.power') }}" # templates work directly; event_data_template is no longer needed +``` + ### MQTT Trigger Fires on MQTT messages. @@ -367,6 +393,121 @@ triggers: subtype: single ``` +A matching **`condition: device`** variant exists (`device_id`, `domain`, `entity_id`, `type`) with the same caveats — `device_id` is not persistent; prefer a `state`/`numeric_state` condition. + +### Zone Trigger + +Fires when a person or device tracker enters or leaves a zone. + +```yaml +triggers: + - trigger: zone + entity_id: person.john + zone: zone.home + event: enter # "enter" or "leave" +``` + +To *check* zone membership in a condition rather than trigger on the transition, see [Zone Condition](#zone-condition). + +### Template Trigger + +Fires when a Jinja template renders truthy. Prefer `state`/`numeric_state` whenever the change can be expressed natively — reach for the template trigger only for cross-entity or derived conditions. + +```yaml +triggers: + - trigger: template + value_template: "{{ states('sensor.temp') | float > 25 and is_state('binary_sensor.window', 'on') }}" + for: "00:05:00" # optional: template must stay true this long before firing +``` + +### Calendar Trigger + +Fires at the start or end of a calendar event, with an optional offset. Prefer this over a `state` trigger on the calendar entity, which only tracks one event at a time. + +```yaml +triggers: + - trigger: calendar + entity_id: calendar.work + event: start # "start" or "end" + offset: "-00:15:00" # optional: fire 15 min before the event +``` + +Exposes `trigger.calendar_event` with `.summary`, `.start`, `.end`, `.description`, `.location` for filtering by event details. + +### Webhook Trigger + +Fires when an HTTP request hits `/api/webhook/`. The canonical way to trigger HA from external systems (scripts, IFTTT, other servers). + +```yaml +triggers: + - trigger: webhook + webhook_id: "my-secret-hook-id" + allowed_methods: [POST, PUT] # optional; default POST + PUT + local_only: true # optional; default true — set false for external callers +``` + +Read the payload via `trigger.json`, `trigger.data` (form-encoded), `trigger.query`, or `trigger.headers`. + +### Home Assistant Trigger + +Fires when HA finishes starting or begins shutting down. + +```yaml +triggers: + - trigger: homeassistant + event: start # "start" or "shutdown" +``` + +Use `start` for boot-time setup (restore state, resync devices). `shutdown` handlers get only ~20 seconds before HA stops — keep them short. + +### Conversation Trigger + +Fires when a voice/Assist sentence matches. The match syntax supports `[optional]` words, `(a|b)` alternatives, and `{slot}` wildcards. + +```yaml +triggers: + - trigger: conversation + command: + - "party time" + - "play {album} by {artist}" +``` + +Captured wildcards are available as `trigger.slots.`; the full text is `trigger.sentence`. To make the assistant speak a dynamic reply, use the `set_conversation_response` action (`- set_conversation_response: "Done"`; `~` clears a previously set response). + +### Tag Trigger + +Fires when an NFC/QR tag is scanned. + +```yaml +triggers: + - trigger: tag + tag_id: "A7-6B-90-5F" + device_id: 0e19cd3c... # optional: limit to one scanner (device_id is not persistent — omit unless scoping) +``` + +### Geolocation Trigger + +Fires when a transient entity from a geolocation feed (NWS alerts, GDACS, fire-service feeds, USGS quakes) enters or leaves a zone. Keyed by the feed `source`, not a fixed `entity_id`. + +```yaml +triggers: + - trigger: geo_location + source: nsw_rural_fire_service_feed + zone: zone.fire_alert + event: enter # "enter" or "leave" +``` + +### Persistent Notification Trigger + +Fires on persistent-notification lifecycle changes (e.g. react to an integration posting an error notice). + +```yaml +triggers: + - trigger: persistent_notification + update_type: [added, removed] # any of: added, removed, updated, current; omit for all + notification_id: invalid_config # optional: filter to one notification +``` + ### Presence and Person Triggers and Conditions (Removed in 2026.5) The `entered_home`/`left_home` device trigger types and `is_home`/`is_not_home` device condition types for `person` and `device_tracker` domains were **removed in 2026.5**. Use state triggers and conditions instead. @@ -489,6 +630,16 @@ Both waits set `wait.completed` and `wait.remaining`: message: "Door still open after 5 minutes!" ``` +### delay + +Pauses the sequence for a fixed time. Accepts a time string, a units dict, or a template. Prefer `wait_for_trigger` when waiting for an *event* rather than a fixed duration. + +```yaml +- delay: "00:01:30" # HH:MM:SS +- delay: {minutes: 1, seconds: 30} # units dict (combinable) +- delay: "{{ states('input_number.delay_seconds') | int }}" # template → seconds +``` + --- ## Automation Modes @@ -608,6 +759,63 @@ actions: --- +## Stopping a Sequence + +`stop:` halts the rest of the sequence cleanly — clearer than nesting everything inside a `choose`/`if` guard. + +```yaml +- stop: "reason shown in the trace" + +# Mark the run as failed (red in the trace, propagates to callers): +- stop: "unexpected state" + error: true + +# Return a value from a script and halt: +- stop: "done" + response_variable: my_result +``` + +--- + +## Variables + +Compute a value once and reuse it — keeps sequences DRY and avoids repeating long templates. + +```yaml +# Mid-sequence (scoped to the remaining steps of this run): +- variables: + brightness: 100 + targets: + - light.kitchen + - light.living_room + +# Automation/script top level (full templates; usable in conditions and actions): +variables: + threshold: 25 +``` + +`trigger_variables:` is a separate top-level key evaluated **before** triggers fire — it supports **limited templates only** (no `states()`/`state_attr()`), mainly for passing a blueprint `!input` into trigger options. Don't put state-based templates there. + +--- + +## Capturing Action Responses + +`response_variable` captures the data a service returns (e.g. `weather.get_forecasts`, `calendar.get_events`, `todo.get_items`) into a variable for later steps — the only native mechanism for response-aware service calls. + +```yaml +- action: weather.get_forecasts + target: + entity_id: weather.home + data: + type: daily + response_variable: forecast +- action: notify.mobile_app + data: + message: "High today: {{ forecast['weather.home'].forecast[0].temperature }}°" +``` + +--- + ## Repeat Actions Four repeat variants are available: @@ -713,6 +921,26 @@ actions: --- +## Parallel Actions + +The `parallel:` action runs a group of actions **concurrently** within one sequence. This is distinct from `mode: parallel` (see [Automation Modes](#automation-modes)), which controls concurrency of whole automation *runs*. Steps inside a nested `sequence:` still run in order. + +```yaml +actions: + - parallel: + - action: notify.person1 + data: + message: "Sent at the same time" + - sequence: + - wait_for_trigger: + - trigger: state + entity_id: binary_sensor.motion + to: "on" + - action: notify.person2 +``` + +--- + ## Trigger IDs Assign IDs to triggers for use in conditions and choose: @@ -817,3 +1045,20 @@ Disabling an automation via *Settings → Automations → open automation → # UI: Settings → Automations → open automation → ⋮ → Settings → Enabled toggle # Or via WebSocket API: config/entity_registry/update (disabled_by: user) ``` + +### `enabled:` on individual triggers, conditions, and actions + +While `enabled:` is not valid as a *top-level* automation key (above), it **is** valid on any individual trigger, condition, or action — as a boolean or a blueprint `!input`. A disabled element is skipped without disabling the whole automation. It also accepts a **limited template** (variables / blueprint inputs only — no `states()`), evaluated **once when the automation loads**. + +```yaml +triggers: + - trigger: sun + event: sunset + enabled: false # statically disabled + - trigger: time + at: "15:30:00" + enabled: "{{ enable_afternoon }}" # limited template over a variable/!input; evaluated once at load +actions: + - action: notify.notify + enabled: false +``` diff --git a/skills/home-assistant-best-practices/references/dashboard-guide.md b/skills/home-assistant-best-practices/references/dashboard-guide.md index 7aac63a..731161e 100644 --- a/skills/home-assistant-best-practices/references/dashboard-guide.md +++ b/skills/home-assistant-best-practices/references/dashboard-guide.md @@ -6,15 +6,17 @@ Patterns and decisions for designing Home Assistant Lovelace dashboards. - [Dashboard Structure](#dashboard-structure) - [View Types](#view-types) +- [Dashboard Strategies](#dashboard-strategies) - [Built-in Cards](#built-in-cards) - [Features](#features) +- [Badges](#badges) - [Actions](#actions) - [Custom Cards](#custom-cards) - [CSS Styling](#css-styling) - [HACS Integration](#hacs-integration) - [Complete Example: Multi-View Dashboard](#complete-example-multi-view-dashboard) - [Common Pitfalls](#common-pitfalls) -- [Modern Best Practices (2024+)](#modern-best-practices-2024) +- [Modern Best Practices](#modern-best-practices) - [Visual Iteration Workflow](#visual-iteration-workflow) --- @@ -77,6 +79,29 @@ Patterns and decisions for designing Home Assistant Lovelace dashboards. --- +## Dashboard Strategies + +A **strategy** generates a dashboard (or a single view) from code instead of a static card list. The default Overview/Home dashboard an agent first encounters **is** a strategy dashboard — its raw config is just: + +```yaml +strategy: + type: original-states # built-in strategy; auto-generates views from current entities/areas +views: [] +``` + +A strategy can be set at dashboard level (`strategy:` at the top) or per view (`views: - strategy: {type: ...}`). Custom strategies use the `custom:` prefix; any extra keys are strategy-specific options. The only universal key is `type`. + +```yaml +strategy: + type: custom:my-area-dashboard + # extra keys here are passed to the custom strategy +views: [] +``` + +**"Take control":** to convert an auto-generated strategy dashboard into a static, hand-editable one, use the dashboard's three-dots menu → **Take control**. This is **one-way** — once taken over, the dashboard no longer auto-updates as new entities/areas appear. Editing its cards directly without taking control is a common failure mode — take control first, or edit the strategy options. + +--- + ## Built-in Cards | Category | Cards | @@ -120,6 +145,54 @@ Patterns and decisions for designing Home Assistant Lovelace dashboards. } ``` +### Heading Card + +The official way to label a section, replacing a bare section `title`. Supports a title/subtitle style, an icon, a tap action, and inline entity/button badges. + +```json +{ + "type": "heading", + "heading": "Kitchen", + "heading_style": "title", + "icon": "mdi:fridge", + "tap_action": {"action": "navigate", "navigation_path": "/lovelace/kitchen"}, + "badges": [ + {"type": "entity", "entity": "sensor.kitchen_temperature", "show_state": true}, + {"type": "button", "icon": "mdi:lightbulb-off", "tap_action": {"action": "perform-action", "perform_action": "light.turn_off"}} + ] +} +``` + +Use `"heading_style": "subtitle"` for sub-headers. + +### Markdown Card + +The only built-in card that renders Jinja2 templates — the go-to for computed/composite status text without a custom card. + +```json +{ + "type": "markdown", + "content": "Temp {{ states('sensor.living_room') }}°. Door {{ 'open' if is_state('binary_sensor.door','on') else 'closed' }}.", + "entity_id": ["sensor.living_room", "binary_sensor.door"] +} +``` + +The card auto-detects entities referenced in the template; `entity_id` (a list) is an optional fallback for when that analysis misses some, forcing a re-render on those. `text_only: true` strips the card chrome for inline labels. + +### Card Sizing in Sections (grid_options) + +In a `sections` view each section is a 12-column grid. Size or span any card with `grid_options` — this replaces nested `vertical-stack`/`horizontal-stack` hacks. + +```json +{ + "type": "tile", + "entity": "light.kitchen", + "grid_options": {"columns": 6, "rows": "auto"} +} +``` + +`columns`: 1–12, or `"full"` for full width. `rows`: an integer or `"auto"`. + --- ## Features @@ -147,6 +220,31 @@ Feature `style` options: `"dropdown"` or `"icons"` --- +## Badges + +Badges appear at the top of a view. The simple form is a list of entity IDs, but the **object form** unlocks more: + +```json +{ + "badges": [ + "person.john", + { + "type": "entity", + "entity": "sensor.kitchen_temperature", + "show_name": true, + "show_state": true, + "color": "amber", + "state_content": ["state", "last_changed"] + } + ] +} +``` + +- `type: entity` options: `show_name`, `show_state`, `show_icon`, `show_entity_picture`, `state_content` (`state`/`last_changed`/`last_updated`/an attribute), and per-badge `visibility`. +- **`color` accepts a color token or hex only — not a Jinja template.** + +--- + ## Actions ```json @@ -157,19 +255,27 @@ Feature `style` options: `"dropdown"` or `"icons"` } ``` -Action types: `toggle`, `call-service`, `more-info`, `navigate`, `url`, `none` +Action types: `more-info`, `toggle`, `perform-action` (the service-call action), `navigate`, `url`, `assist`, `none` ### Visibility Conditions +Any card or badge accepts a `visibility` list (all conditions must pass to show). Condition types: `state`, `numeric_state`, `screen` (responsive — a CSS media query), `user`, `time`, `location`, and the `and`/`or`/`not` wrappers. + ```json { "visibility": [ - {"condition": "user", "users": ["user_id_hex"]}, - {"condition": "state", "entity": "sun.sun", "state": "above_horizon"} + {"condition": "screen", "media_query": "(min-width: 1280px)"}, + {"condition": "numeric_state", "entity": "sensor.temperature", "above": 20}, + {"condition": "and", "conditions": [ + {"condition": "state", "entity": "sun.sun", "state": "above_horizon"}, + {"condition": "user", "users": ["user_id_hex"]} + ]} ] } ``` +`screen` is the canonical way to show/hide cards by viewport (desktop vs. mobile). + --- ## Custom Cards @@ -360,7 +466,7 @@ Search HACS for community cards by name/category, review repository details, the --- -## Modern Best Practices (2024+) +## Modern Best Practices - Use **sections** view type with grid-based layouts - Use **tile** cards as primary card type (replaces legacy entity/light/climate cards) diff --git a/skills/home-assistant-best-practices/references/helper-selection.md b/skills/home-assistant-best-practices/references/helper-selection.md index 070c90e..36ac8b8 100644 --- a/skills/home-assistant-best-practices/references/helper-selection.md +++ b/skills/home-assistant-best-practices/references/helper-selection.md @@ -4,17 +4,25 @@ This document covers Home Assistant's built-in helpers and integrations that sho ## Table of Contents 1. [Numeric Aggregation](#numeric-aggregation) - min_max, statistics -2. [Rate and Change](#rate-and-change) - derivative, threshold +2. [Rate and Change](#rate-and-change) - derivative, threshold, trend 3. [Time-Based Tracking](#time-based-tracking) - utility_meter, history_stats, integration (Riemann sum) 4. [State Storage](#state-storage) - input_boolean, input_number, input_select, input_text, input_datetime, input_button 5. [Counting and Timing](#counting-and-timing) - counter, timer 6. [Scheduling](#scheduling) - schedule, time of day (tod) 7. [Entity Grouping](#entity-grouping) - group, binary sensor groups -8. [Data Smoothing](#data-smoothing) - filter -9. [Random Values](#random-values) - random -10. [Climate Control](#climate-control) - generic_thermostat, generic_hygrostat -11. [Domain Conversion](#domain-conversion) - switch_as_x -12. [Template Helpers](#template-helpers) - template (escape hatch when no dedicated helper fits) +8. [Probabilistic Inference](#probabilistic-inference) - bayesian +9. [Data Smoothing](#data-smoothing) - filter +10. [Random Values](#random-values) - random +11. [Climate Control](#climate-control) - generic_thermostat, generic_hygrostat, mold_indicator +12. [Domain Conversion](#domain-conversion) - switch_as_x +13. [Template Helpers](#template-helpers) - template (escape hatch when no dedicated helper fits) + +## How Helpers Are Created + +Helpers reach Home Assistant through two different creation mechanisms — which one a helper uses determines whether you submit flat fields in a single step or step through a config flow: + +- **Storage-collection helpers** — created via a per-domain WebSocket collection command (`/create`, e.g. `input_boolean/create`, `counter/create`) with flat, structured fields: `input_boolean`, `input_number`, `input_select`, `input_text`, `input_datetime`, `input_button`, `counter`, `timer`, `schedule`, `zone`, `person`, `tag`. (`schedule` additionally offers an optional YAML mode.) +- **Config-entry-flow helpers** — created through the generic config-entry flow (`config_entries/flow`, handler = the helper domain), often a multi-step flow that begins with a sub-type menu (see [Menu-Based Helpers](#menu-based-helpers)): `template`, `group`, `utility_meter`, `derivative`, `min_max`, `threshold`, `integration`, `statistics`, `trend`, `random`, `filter`, `tod`, `generic_thermostat`, `generic_hygrostat`, `switch_as_x`, `bayesian`, `mold_indicator`. ## Menu-Based Helpers @@ -210,6 +218,45 @@ With upper: 25 and hysteresis: 1: --- +### trend + +**Use for:** A binary sensor that turns on when a numeric sensor is trending up (or down) over time — directly, without chaining `derivative` → `threshold`. + +**Instead of:** +```yaml +# WRONG - Template comparing against a stored previous value +template: + - binary_sensor: + - name: "Temperature Rising" + state: "{{ states('sensor.temp') | float > state_attr('sensor.temp', 'prev') | float(0) }}" +``` + +**Use this:** +```yaml +# RIGHT - Trend helper. NOTE: `sensors:` is a MAPPING keyed by a slug (not a list) +binary_sensor: + - platform: trend + sensors: + temp_rising: + entity_id: sensor.temperature + sample_duration: 1800 # seconds of history to consider + min_gradient: 0.001 # units per SECOND to count as a trend + max_samples: 120 # cap on samples kept (independent of sample_duration) + invert: false # true = detect a downward trend +``` + +**Key behaviors:** +- `min_gradient` is units **per second** (0.001 °/s ≈ 3.6 °/h). +- `invert: true` detects a *downward* trend. +- `sensors:` is a slug-keyed mapping (not a list), unlike most other `binary_sensor` platforms. + +**Common uses:** +- Temperature/pressure rising or falling +- Battery draining +- A value drifting before it crosses a hard threshold + +--- + ## Time-Based Tracking ### utility_meter @@ -692,6 +739,43 @@ template: --- +## Probabilistic Inference + +### bayesian + +**Use for:** Inferring an unmeasurable state (someone cooking, showering, room occupied) from several probabilistic signals — instead of hand-tuning a template with stacked `and`/`or`/threshold logic. + +**Use this:** +```yaml +binary_sensor: + - platform: bayesian + name: "Kitchen In Use" + prior: 0.3 # baseline probability before any observation + probability_threshold: 0.5 # turns on when posterior probability exceeds this + observations: + - entity_id: binary_sensor.kitchen_motion + platform: state # or numeric_state / template + to_state: "on" + prob_given_true: 0.95 + prob_given_false: 0.33 + - entity_id: sensor.kitchen_power + platform: numeric_state + above: 50 + prob_given_true: 0.8 + prob_given_false: 0.05 +``` + +**Key behaviors:** +- Each observation contributes `prob_given_true` / `prob_given_false`; the sensor turns on when the combined posterior probability exceeds `probability_threshold`. +- Observation `platform` is `state`, `numeric_state` (uses `above`/`below`), or `template` (uses `value_template`). +- **YAML uses probabilities `0..1`; the UI config flow uses percentages `0..100`** — a common mismatch. + +**Common uses:** +- "Someone is cooking" / "shower running" from motion + power + humidity +- Occupancy inference from several weak presence signals + +--- + ## Data Smoothing ### filter @@ -842,6 +926,23 @@ humidifier: --- +### mold_indicator + +**Use for:** Estimating mold/condensation risk from indoor temperature + humidity vs. a cold-surface (outdoor) temperature — instead of hand-rolling a dew-point template. + +```yaml +sensor: + - platform: mold_indicator + indoor_temp_sensor: sensor.indoor_temp + indoor_humidity_sensor: sensor.indoor_humidity + outdoor_temp_sensor: sensor.outdoor_temp + calibration_factor: 2.0 +``` + +Outputs an estimated humidity-at-cold-surface percentage; mold risk rises above ~70%. **`calibration_factor` must be physically calibrated** to a known condensation point — it is not a value to guess. + +--- + ## Domain Conversion ### switch_as_x @@ -927,6 +1028,7 @@ See the [Decision Matrix](#decision-matrix) for when the template helper is the | Average over time | `statistics` | Template tracking history | | Rate of change | `derivative` | Template calculating delta | | On/off at threshold | `threshold` | Template binary sensor | +| Sensor trending up/down | `trend` | Template with derivative + threshold | | Consumption per period | `utility_meter` | Counter with reset automation | | Time in state | `history_stats` | Template tracking timestamps | | Power to energy | `integration` | Template approximating | @@ -943,6 +1045,8 @@ See the [Decision Matrix](#decision-matrix) for when the template helper is the | Reject out-of-range values | `filter` (`range`) | Template with bounds check | | Thermostat from switch + temp sensor | `generic_thermostat` | Automation with hysteresis logic | | Humidifier from switch + humidity sensor | `generic_hygrostat` | Automation with hysteresis logic | +| Mold/condensation risk from temp + humidity | `mold_indicator` | Dew-point template | +| Infer an unmeasurable state from several signals | `bayesian` | Template with stacked and/or logic | | Switch presented as light/cover/lock | `switch_as_x` | Template light/cover/lock | | Random sensor value | `random` | Template with `range()` | | Custom logic no other helper covers | `template` helper (via UI flow) | YAML `template:` platform sensor | diff --git a/skills/home-assistant-best-practices/references/scenes.md b/skills/home-assistant-best-practices/references/scenes.md new file mode 100644 index 0000000..abc7815 --- /dev/null +++ b/skills/home-assistant-best-practices/references/scenes.md @@ -0,0 +1,92 @@ +# Scenes + +A scene is a **saved snapshot of target entity states, applied atomically** — not a sequential script. Activating it pushes every listed entity toward its stored state at once (optionally with a transition). Use a scene to *restore a set of states*; use a script's `sequence` when you need ordered, conditional, or timed steps. + +## Table of Contents +1. [Scene Config Shape](#scene-config-shape) +2. [Activating a Scene](#activating-a-scene) +3. [Snapshot + Restore (scene.create)](#snapshot--restore-scenecreate) +4. [Apply States Without Storing (scene.apply)](#apply-states-without-storing-sceneapply) +5. [Reloading Scenes](#reloading-scenes) + +--- + +## Scene Config Shape + +```yaml +scene: + - name: Romantic # required; `id:` optional but recommended (stable unique id) + icon: "mdi:flower-tulip" # optional + entities: + light.tv_back_light: "on" # simple form: entity_id → state string + light.ceiling: # object form: state + attributes + state: "on" + brightness: 200 # 0–255 + color_temp_kelvin: 2700 # Kelvin — NOT color_temp/mireds (removed 2026.3) + climate.living_room: + state: "heat" + temperature: 21 +``` + +- `entities` is a dict mapping `entity_id` → a state string, or an object with `state` plus the entity's attributes. +- Light color attributes use `color_temp_kelvin` / `rgb_color` / `xy_color` / `hs_color`. The mireds-based `color_temp` (and `kelvin`/`min_mireds`/`max_mireds`) were removed in 2026.3. +- A scene only sets the attributes you list; unlisted entities/attributes are untouched. +- UI-created scenes live in `scenes.yaml` and snapshot the *current* state of each added entity at save time. + +## Activating a Scene + +```yaml +actions: + - action: scene.turn_on + target: + entity_id: scene.romantic + data: + transition: 2.5 # optional, seconds +``` + +## Snapshot + Restore (scene.create) + +Capture live states into a transient scene, then restore them later — the canonical "save current state, do something, put it back" pattern. + +```yaml +# Capture current states (transient scene — discarded on a config/scene reload) +- action: scene.create + data: + scene_id: before_alert # required; lowercase + underscores + snapshot_entities: # capture the CURRENT state of these + - light.living_room + - light.kitchen + entities: # optionally also set explicit states + light.porch: + state: "on" + brightness: 255 +# ...later, restore exactly what was captured: +- action: scene.turn_on + target: + entity_id: scene.before_alert +``` + +`scene.create` requires at least one of `snapshot_entities` / `entities` (they combine). The created scene is temporary and lost on a config/scene reload. + +## Apply States Without Storing (scene.apply) + +Push a one-off set of states atomically, with no `scene.*` entity created. + +```yaml +- action: scene.apply + data: + entities: # same shape as a scene's `entities` + light.tv_back_light: + state: "on" + brightness: 100 + light.ceiling: "off" + transition: 2.5 # optional, seconds +``` + +## Reloading Scenes + +After editing scene YAML, apply it without restarting: + +```yaml +- action: scene.reload +```