home-assistant/AUTOMATIONS.md
jm 7a0d75d9db Add TOU winter peak-shaving; parameterize by season (v3)
Pepco MD TOU-P has different on-peak windows by season: summer (Jun-Sep)
14:00-19:00, winter (Oct-May) 06:00-09:00 and 17:00-21:00. Heat is gas, so
winter electric peak logic is cooling-only.

* pre-cool: bounded to summer (Jun-Sep); winter has no pre-cool.
* peak-setback (hold) and peak-ends (return): unified across seasons via a
  season self-select (month -> season; triggers carry id: summer/winter and act
  only when trigger.id == season) plus a params map for setpoints. Summer forces
  cool + 82 / returns 74 daily; winter sets 82 / returns 74 ONLY when already in
  cool mode, so it never disturbs the gas furnace.
* new ac_winter_morning_kill: 06:00 winter work days, if in cool mode set
  hvac_mode off to dodge the 6-9am winter peak. No auto-resume yet (planned).

Decision logic verified as a truth table via the template engine across
summer/winter x cooling x workday before commit. Deployed in place (existing
ids unchanged); new id ac_winter_morning_kill added.

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

109 lines
5.4 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.
## Utility rate context (Pepco MD, TOU-P, eff. 2026-02-01)
| Season (rate) | Months | On-peak window(s), weekdays only | On-peak | Off-peak |
|---|---|---|---|---|
| **Summer** | JunSep | **14:0019:00** (27 PM) | $0.42 | $0.17 |
| **Winter** | OctMay | **06:0009:00** and **17:0021:00** | $0.32 | $0.13 |
Weekends and **federal** holidays are always off-peak — handled by
`binary_sensor.workday_sensor` (with a MonFri fallback). Heating is **gas**
(not on this electric peak logic), so the winter rules are **cooling-only**.
## 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 |
`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 # deploy every automations/*.yaml (in place, by id)
python3 deploy.py ac_peak_ends # deploy just one by id
```
## Parameterization (the "params map" model)
Rather than duplicate summer vs winter rules, each phase is **one self-selecting
automation**:
- A `variables:` block computes `season` from the month
(`summer` = {6,7,8,9}, else `winter`).
- Time triggers carry `id: summer` / `id: winter`; the body only acts when
`trigger.id == season`, so the off-season time is a harmless no-op.
- Setpoints come from a `params` map
(`{'summer': {'hold':82,'ret':74}, 'winter': {'hold':82,'ret':74}}`) so the
two seasons can diverge later by editing one line. (Both are equal today.)
The map is repeated in `ac_peak_setback` and `ac_peak_ends`; keep them in sync.
## The rules
All act on `climate.t6_pro_z_wave_programmable_thermostat_with_smartstart`.
### 1. `ac_precool_before_peak` — Pre-cool before peak (summer only)
- **12:00**, summer work days. Fetches today's forecast high and pre-cools:
≥90→68, ≥85→70, ≥80→71, <80no pre-cool.
- Winter has no pre-cool (marginal for an evening peak).
- **Fallbacks:** workday sensor broken MonFri; forecast broken assume 88°F
70°F (`continue_on_error` + `fc is defined and is mapping` guard).
### 2. `ac_peak_setback` — Peak setback to 82 (both seasons)
- **14:00** (summer) / **17:00** (winter), work days.
- Summer: forces `cool` and sets **82**.
- Winter: **cooling-only** sets 82 *only if already in `cool` mode* (never
forces mode, never touches gas heat).
### 3. `ac_peak_ends` — Peak ends, return to 74 (both seasons)
- **19:00** (summer) / **21:00** (winter). Sets **74**.
- Summer returns daily; winter returns only if in `cool` mode.
### 4. `ac_winter_morning_kill` — Winter morning peak kill (new in v3)
- **06:00**, winter work days. If in `cool` mode, sets `hvac_mode: off` to dodge
the 69 AM winter peak. **No auto-resume yet** (add later). Cooling-only.
## Failure-mode design
| 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) |
| Wrong-season trigger fires | `trigger.id != season` | `applies = trigger.id == season` guard | no-op |
| Winter, not cooling (e.g. gas heat on) | `is_state(climate,'cool')` false | cooling-only guard | no-op (furnace safe) |
Fallback and season/cooling logic verified via HA's template engine and
execution traces before each deploy.
## 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;
removed throwaway debug automations (three `TEST` spoofs + `ac_flap_1758_1820`).
- **2026-07-09 v3**: TOU winter support. Pre-cool bounded to summer (JunSep).
`ac_peak_setback` and `ac_peak_ends` unified across seasons via a season
self-select + `params` map (summer 14:00/19:00, winter 17:00/21:00), winter
cooling-only. Added `ac_winter_morning_kill` (6 AM winter, coolingoff, no
resume yet).