# 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/`. 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.