String Lights — Priority Renderer¶
Date: 2026-07-09 Status: Approved, ready for implementation
Problem¶
The string lights (switch.string_lights) are driven by two dumb, unconditional automations (automations.yaml §10):
- String Lights — On at Sunset +30m →
switch.turn_onat sunset + 30 minutes - String Lights — Off at 11pm →
switch.turn_offat 23:00
Neither knows about the house's sleep state. Good Night reaches the switch only indirectly, by turning off group.lights (which switch.string_lights is a member of). So the "off" behavior lives in two unrelated places — a time-of-day automation and a group membership — and neither can see the other.
Goals¶
- Refactor to the house's established priority-renderer pattern (
script.night_lights_render,script.master_bedroom_sitting_lamp_render): one script owns the switch, one automation calls it on every relevant trigger. - Turn the lights off when the house goes to sleep (
input_boolean.sleeping→on). - Turn the lights off after midnight — replacing the 23:00 hard-off.
Non-goals¶
- No presence awareness. The renderer does not check
zone.home. Exterior string lights on a schedule read as occupancy whether or not anyone is home; adding an away branch buys nothing. - No periodic re-render. If the switch is flipped manually at 2am, the renderer will not "correct" it until the next trigger fires. This matches
script.master_bedroom_sitting_lamp_render, which has the same property. Accepted.
Design¶
script.string_lights_render¶
mode: restart, a choose: priority stack. First matching branch wins.
| Priority | Condition | Result |
|---|---|---|
| 1 | input_boolean.sleeping is on | off |
| 2 | condition: sun, after: sunset, after_offset: "00:30:00" | on |
| default | — | off |
The midnight cutoff is a property of the sun condition, not a separate branch. HA's sun condition compares against today's sunset. At 00:01 today's sunset is still in the future, so after: sunset evaluates false and the default branch turns the lights off. This is a documented HA gotcha ("the sun condition is not valid after midnight") that happens to be exactly the semantics we want, so we lean on it deliberately — and comment it, because a future reader will otherwise assume it's a bug.
The same property gives us a free morning guard: when Good Morning clears input_boolean.sleeping around 6am, the sun is below the horizon but after: sunset is false, so the renderer resolves to off rather than relighting the lights at dawn. (This is the failure the sitting-lamp renderer had to solve with an explicit 16:00–02:00 time gate; the sun condition subsumes it here.)
automation.string_lights_render_triggers¶
Replaces both existing §10 automations. Sole action: script.string_lights_render.
Triggers:
sun/sunset,offset: "00:30:00"— the on edgetimeat00:00:00— the off edgestateoninput_boolean.sleeping— both directions (sleep-off, and re-render at Good Morning)homeassistantstart— re-render to the correct state after a restart
group.lights membership¶
switch.string_lights is removed from group.lights (configuration.yaml), following the switch.master_bedroom_sitting_lamp precedent: the renderer is the single writer of the switch.
Consumer audit — group.lights is referenced in exactly one place, script.good_night (scripts.yaml:89, homeassistant.turn_off). script.all_lights_off calls only light.turn_off: all and therefore never reached switch.string_lights to begin with, so dropping the membership is not a regression there.
Good Night still turns the string lights off — one step later than before, via input_boolean.sleeping → the render trigger → the renderer's priority-1 branch.
Behavior change¶
On a night where nobody runs Good Night, the string lights now burn until midnight instead of 23:00. This is intended and was chosen explicitly over keeping 23:00 or splitting weeknight/weekend.
Testing¶
The existing suite covers this without new tests:
- Tier 0/2 — yamllint; unique automation
id/alias(the newstring_lights_render_triggersid; the two retired ids disappear). - Tier 1 —
check_configvalidates the new script and thesuncondition offset syntax. - Tier 3 — entity-ref test confirms
switch.string_lights,input_boolean.sleeping, andscript.string_lights_renderall resolve againsttests/fixtures/entities.txt.
script.string_lights_render is a new entity, so make snapshot must be re-run against the live instance after deploy and tests/fixtures/entities.txt committed — otherwise the Tier 3 test fails on the dashboard/automation reference to it. (Same sequencing as the sitting-lamp renderer: the automation references the script, which only exists post-deploy.)
Files touched¶
| File | Change |
|---|---|
scripts.yaml | add string_lights_render |
automations.yaml | replace the two §10 automations with string_lights_render_triggers |
configuration.yaml | drop switch.string_lights from group.lights; update the group's comment |
CLAUDE.md | §9 Architecture entry; the group.lights line under Key Entities |
docs/automations.md | rewrite Section 10 |
docs/lighting.md | add a String Lights section describing the sunset→midnight/sleep behavior |