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>
60 lines
3.1 KiB
YAML
60 lines
3.1 KiB
YAML
# ac_precool_before_peak — pre-cool the house before the summer on-peak window
|
|
#
|
|
# id / entity : ac_precool_before_peak -> automation.ac_pre_cool_before_peak
|
|
# trigger : 12:00 local (America/New_York)
|
|
# purpose : On summer work days, pre-cool before the 14:00-19:00 peak,
|
|
# harder when the forecast high is hotter (>=90 -> 68, >=85 -> 70,
|
|
# >=80 -> 71, below 80 -> no pre-cool).
|
|
#
|
|
# Summer only: pre-cooling for the WINTER evening peak (17:00-21:00) is marginal
|
|
# (the daily high has already passed by then), so winter is hold+return only and
|
|
# has no pre-cool. This automation self-gates on season == 'summer'.
|
|
#
|
|
# Graceful degradation (a broken dependency must NOT skip pre-cooling):
|
|
# * workday sensor unavailable/unknown -> fall back to a plain Mon-Fri check.
|
|
# * weather forecast fails or returns nothing -> assume a regular hot day
|
|
# (88F -> 70F). continue_on_error covers a hard service error; the
|
|
# `fc is defined and is mapping` guard covers an empty/None response.
|
|
#
|
|
# NOTE: templates are single-line on purpose (YAML folding can corrupt Jinja).
|
|
#
|
|
# v3 (2026-07-09): bounded to summer (Jun-Sep). v2 added the fallbacks.
|
|
id: ac_precool_before_peak
|
|
alias: AC - Pre-cool before peak
|
|
description: >-
|
|
v3 (2026-07-09). Summer-only. Pre-cools before the 2-7pm peak on work days.
|
|
Falls back to Mon-Fri if the workday sensor fails, and to a regular pre-cool
|
|
(assume 88F high -> 70F) if the weather forecast fails. Managed in git; see
|
|
AUTOMATIONS.md.
|
|
mode: single
|
|
triggers:
|
|
- trigger: time
|
|
at: "12:00:00"
|
|
conditions: []
|
|
actions:
|
|
- action: weather.get_forecasts
|
|
continue_on_error: true
|
|
target:
|
|
entity_id: weather.forecast_4315aspenhill
|
|
data:
|
|
type: daily
|
|
response_variable: fc
|
|
- variables:
|
|
season: "{{ 'summer' if now().month in [6,7,8,9] else 'winter' }}"
|
|
is_workday: "{% set wd = states('binary_sensor.workday_sensor') %}{% if wd in ['on','off'] %}{{ wd == 'on' }}{% else %}{{ now().weekday() < 5 }}{% endif %}"
|
|
today_high: "{% if fc is defined and fc is mapping and 'weather.forecast_4315aspenhill' in fc and fc['weather.forecast_4315aspenhill'].forecast | default([]) | length > 0 %}{% set today = now().date() %}{% set entries = fc['weather.forecast_4315aspenhill'].forecast | selectattr('datetime') | list %}{% set match = entries | selectattr('datetime','search', today | string) | list %}{{ (match[0].temperature if match else entries[0].temperature) | float(88) }}{% else %}88{% endif %}"
|
|
setpoint: "{% set h = today_high | float(88) %}{% if h >= 90 %}68{% elif h >= 85 %}70{% elif h >= 80 %}71{% else %}none{% endif %}"
|
|
- if:
|
|
- condition: template
|
|
value_template: "{{ season == 'summer' and is_workday and setpoint != 'none' }}"
|
|
then:
|
|
- action: climate.set_hvac_mode
|
|
target:
|
|
entity_id: climate.t6_pro_z_wave_programmable_thermostat_with_smartstart
|
|
data:
|
|
hvac_mode: cool
|
|
- action: climate.set_temperature
|
|
target:
|
|
entity_id: climate.t6_pro_z_wave_programmable_thermostat_with_smartstart
|
|
data:
|
|
temperature: "{{ setpoint | int }}"
|