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>
88 lines
4.1 KiB
Markdown
88 lines
4.1 KiB
Markdown
# 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:00–19:00 (2–7 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 → Mon–Fri; 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 → Mon–Fri.
|
||
|
||
### 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` | Mon–Fri |
|
||
| 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 (88→70) |
|
||
|
||
Verified empirically on 2026-07-09 by forcing each failure against the live
|
||
automations and reading their execution traces (workday → `unavailable` still
|
||
ran the Mon–Fri 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.
|