Washer Delay-to-Super-Off-Peak Button — Implementation Plan¶
For agentic workers: REQUIRED SUB-SKILL: superpowers:executing-plans or subagent-driven-development. Steps use checkbox syntax.
Goal: A one-tap script + dashboard tile (Alexa-exposed) that schedules the LG WashTower washer to start at ~midnight so it runs in super-off-peak (00:00–05:00 ET).
Architecture: One script.washer_delay_super_off_peak (guard → compute delay → set delay + start → confirm), a mushroom-template-card tile in the Overview quick-actions grid that calls it, docs, and a manual Alexa .storage exposure step.
Global Constraints¶
- Entities (verified live):
number.washer_delayed_start(int 0–19 h),select.washer_operation(start),binary_sensor.washer_remote_start(must beon),notify.mobile_app_louis_iphone+notify.mobile_app_lindsays_iphone. - Delay formula:
delay = 0 if now().hour < 5 else 24 - now().hour(always 0–19; lands start in 00:00–00:59, or starts now if already super-off-peak). - Notify both phones (guard-fail and confirm). No notify group (broken in 2026.6).
- Delay-start-vs-end is NOT settled — comment in the script + docs that it assumes delay-START; verify on first real cycle.
- Alexa exposure lives in
.storage/homeassistant.exposed_entities(not repo) — manual controller step; also bump the CLAUDE.md Alexa index 19→20. - Deploy:
scripts.yaml+command_dashboard.yaml→ CIdeploy-ha(full restart, since a non-automations config file changed).
Task 1: The script + dashboard tile + structural test¶
Files: Modify scripts.yaml, command_dashboard.yaml, tests/conftest.py (scripts fixture already added for #72), Create tests/test_washer_button.py.
- [ ] Step 1: Failing test — create
tests/test_washer_button.py:
"""Structural checks for the washer delay-to-super-off-peak button (#32)."""
def test_washer_delay_script_present_and_wired(scripts):
assert "washer_delay_super_off_peak" in scripts, "script missing"
body = str(scripts["washer_delay_super_off_peak"])
for ref in ("number.washer_delayed_start", "select.washer_operation",
"binary_sensor.washer_remote_start"):
assert ref in body, f"script doesn't reference {ref}"
Run: python3 -m pytest tests/test_washer_button.py -v → FAIL (script missing).
- [ ] Step 2: Add the script — append to
scripts.yaml:
# One-tap: schedule the LG WashTower WASHER to run overnight in super-off-peak (00:00–05:00 ET,
# cheapest tier) — sets number.washer_delayed_start so the cycle STARTS at ~midnight, then starts
# it. Requires Remote Start armed on the unit (load + pick a cycle + press Remote Start). #32.
# VERIFY on first real cycle: assumes washer_delayed_start delays the START (not the END); if LG
# treats it as delay-END, add the cycle time to the delay formula.
washer_delay_super_off_peak:
alias: "Delay Wash to Super-Off-Peak"
icon: mdi:washing-machine
mode: single
sequence:
# Guard: Remote Start must be armed on the unit or `start` does nothing.
- if:
- condition: state
entity_id: binary_sensor.washer_remote_start
state: "off"
then:
- action: notify.mobile_app_louis_iphone
data:
title: "🧺 Washer not ready"
message: "Load the washer, pick a cycle, and press Remote Start on the unit — then tap again."
- action: notify.mobile_app_lindsays_iphone
data:
title: "🧺 Washer not ready"
message: "Load the washer, pick a cycle, and press Remote Start on the unit — then tap again."
- stop: "washer remote-start not armed"
- variables:
delay: "{{ 0 if now().hour < 5 else 24 - now().hour }}"
- action: number.set_value
target:
entity_id: number.washer_delayed_start
data:
value: "{{ delay }}"
- action: select.select_option
target:
entity_id: select.washer_operation
data:
option: start
- variables:
msg: >-
{{ 'Wash starting now — super-off-peak.' if delay | int == 0
else 'Wash scheduled — starts ~midnight (super-off-peak), in ' ~ delay ~ 'h.' }}
- action: notify.mobile_app_louis_iphone
data:
title: "🧺 Wash → super-off-peak"
message: "{{ msg }}"
- action: notify.mobile_app_lindsays_iphone
data:
title: "🧺 Wash → super-off-peak"
message: "{{ msg }}"
Run: python3 -m pytest tests/test_washer_button.py -v → PASS.
- [ ] Step 3: Add the dashboard tile — in
command_dashboard.yaml, add thismushroom-template-cardinto the Overview quick-actions grid (thecards:list around line 270, alongside Wake House / Sleep House / All Off / Movie), matching their exact style:
- type: custom:mushroom-template-card
icon: mdi:washing-machine
icon_color: teal
primary: Wash → Cheap
layout: vertical
tap_action:
action: call-service
service: script.washer_delay_super_off_peak
card_mod:
style: |
ha-card { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.13); border-radius: 13px; box-shadow: none; height: 80px; --card-primary-font-size: 11px; }
-
[ ] Step 4: Verify —
make lint && make pytest(yamllint clean; suite green incl. the new test and Tier-3 entity-refs — the script's entities + notify services already exist). If Tier-3 flags any of the washer entities as not-in-snapshot, they're real → confirm inentities.txt; do NOT allowlist a typo. Confirm YAML parse:python3 -c "import yaml; d=yaml.safe_load(open('scripts.yaml')); assert 'washer_delay_super_off_peak' in d; print('ok')" -
[ ] Step 5: Commit
git add scripts.yaml command_dashboard.yaml tests/test_washer_button.py
git commit -m "feat(energy): one-tap washer delay-to-super-off-peak (script + Overview tile) (#32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"
Task 2: Docs¶
Files: CLAUDE.md, docs/energy.md, docs/notifications.md.
-
[ ] Step 1: CLAUDE.md — add
script.washer_delay_super_off_peakto the scripts/energy notes, and to the Alexa Voice Exposure index (19 → 20 scripts; add to the Utility group). Note the delay-start-vs-end caveat. -
[ ] Step 2: docs/energy.md — describe the button (overnight super-off-peak; Remote-Start-must-be-armed-first; dryer has no delay-start so it's washer-only). Match the file's style (read a neighbor first).
-
[ ] Step 3: docs/notifications.md — the two pushes (not-ready guidance; scheduled/starting-now confirmation).
-
[ ] Step 4: Gate + commit —
make lint && make pytestgreen.
git add CLAUDE.md docs/energy.md docs/notifications.md
git commit -m "docs(energy): document washer delay-to-super-off-peak button + Alexa exposure (#32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"
Controller finish (after tasks)¶
- Deploy: merge/push → CI
deploy-ha(full restart). - Alexa exposure (manual,
.storage): setscript.washer_delay_super_off_peak→assistants["cloud.alexa"].should_expose = truein/config/.storage/homeassistant.exposed_entities(HA UI Settings → Voice assistants → Alexa, or stop→jq-edit→start), thencloud/alexa/sync(WS) + re-discover in the Alexa app. Verify the live exposed list (thessh haos 'jq …'one-liner in CLAUDE.md) shows 20. - Verify live: call
script.washer_delay_super_off_peakwith Remote Start OFF → confirm the guidance push (no start). (Full start-a-real-wash test is the user's on first laundry load, incl. delay-start-vs-end.) - Close #32, board → Done.