home-assistant/AUTOMATIONS.md
jm 1e3c1001d0 Harden AC rules with workday+weather fallbacks (v2)
Replace the three live AC automations in place (same ids -> same entity_ids,
continuous history/traces):

* pre-cool & peak-setback: the workday check moves out of a blocking
  `condition` into template logic that falls back to a plain Mon-Fri test when
  binary_sensor.workday_sensor is unavailable/unknown. Previously a dead sensor
  silently skipped the run.
* pre-cool: weather.get_forecasts now uses continue_on_error, and a
  `fc is defined and is mapping` guard falls back to an assumed 88F high (->70F
  regular pre-cool) when the forecast hard-errors or returns nothing.
* peak-ends: unchanged behavior (runs daily), documented only.

Add AUTOMATIONS.md (identity model, per-rule spec, failure-mode table, deploy
steps) and deploy.py (pushes automations/*.yaml to the HA config API by id).

Fallbacks verified empirically against the live automations via execution
traces before this commit. Also removed throwaway server-side debug automations
(three TEST spoofs + the ac_flap_1758_1820 experiment).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 21:09:48 -04:00

88 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Home Assistant automations
Source of truth for the AC peak-shaving automations on the HA server
(`192.168.128.3:8123`). This repo is authoritative; HA is a **deploy target**
reached only through its REST config API — HA never sees these files directly.
## Identity model (how a file becomes a live automation)
```
file (git) Home Assistant
automations/ac_peak_setback.yaml ──deploy.py──▶ POST /api/config/automation/
id: ac_peak_setback (by id) config/ac_peak_setback
alias: "AC - Peak setback to 82" │
... ▼ registers entity
automation.ac_peak_setback_to_82
```
| Identifier | Example | Set by | Used by | Change it? |
|---|---|---|---|---|
| **file name** | `ac_peak_setback.yaml` | you | git only (HA never sees it) | freely |
| **`id`** | `ac_peak_setback` | you (in YAML) | config API URL, traces, `automation.trigger` | **never** (primary key) |
| **`alias`** | `AC - Peak setback to 82` | you (in YAML) | UI name; seeds `entity_id` at first create | rarely |
| **`entity_id`** | `automation.ac_peak_setback_to_82` | HA, from `slugify(alias)` | dashboards, other automations, scripts | sticky once registered |
Key point: **`entity_id` derives from `alias`, not `id`.** Reference automations
elsewhere in HA by their `entity_id`.
## Deploying
```bash
source ~/.bashrc # provides HA_API_KEY (long-lived token)
python3 deploy.py --check # parse only, no writes
python3 deploy.py # replace all three in place (same ids)
python3 deploy.py ac_peak_ends # deploy just one by id
```
`deploy.py` POSTs each file to `/api/config/automation/config/<id>`. Same `id`
= in-place replacement, so `entity_id`, run history, and traces stay continuous.
## The rules
All act on `climate.t6_pro_z_wave_programmable_thermostat_with_smartstart`.
The "peak" window is **14:0019:00 (27 PM) local (America/New_York)**.
### 1. `ac_precool_before_peak` — Pre-cool before peak
- **12:00**, work days. Fetches today's daily forecast high and pre-cools:
| forecast high | setpoint |
|---|---|
| ≥ 90°F | 68 |
| ≥ 85°F | 70 |
| ≥ 80°F | 71 |
| < 80°F | *(no pre-cool)* |
- **Fallbacks:** workday sensor broken MonFri; forecast broken assume 88°F
high 70°F (`continue_on_error` covers a hard error, the `fc is defined and is
mapping` guard covers an empty/None response).
### 2. `ac_peak_setback` — Peak setback to 82
- **14:00**, work days. Sets cool / **82°F** to coast through the peak.
- **Fallback:** workday sensor broken MonFri.
### 3. `ac_peak_ends` — Peak ends, return to 74
- **19:00**, **every day** (intentionally not workday-gated). Sets **74°F**.
- No external dependencies, so nothing to fall back on.
## Failure-mode design
Two independent dependencies, each handled so a failure keeps sensible defaults
instead of skipping:
| Failure | Symptom | Handled by | Default taken |
|---|---|---|---|
| Workday/holiday sensor unavailable | `states()` `on`/`off` | `{% if wd in ['on','off'] %}…{% else %} now().weekday()<5` | MonFri |
| Weather service raises | action would abort | `continue_on_error: true` | run continues |
| Weather returns empty/None | `fc` undefined / not a mapping | `{% if fc is defined and fc is mapping %}…{% else %} 88` | regular pre-cool (8870) |
Verified empirically on 2026-07-09 by forcing each failure against the live
automations and reading their execution traces (workday `unavailable` still
ran the MonFri branch; missing weather entity `today_high=88`, setpoint 70,
`script_execution: finished`).
## Changelog
- **2026-07-09 v1 baseline**: imported the three live automations as-is.
- **2026-07-09 v2**: added workday + weather fallbacks to pre-cool and
setback (a broken sensor/forecast no longer skips the run). Peak-ends
unchanged. Removed throwaway debug automations from the server: the three
`TEST - … (spoof, every 1 min)` rules and the `ac_flap_1758_1820`
(85/90 flap) experiment.