Skip to content

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 +30mswitch.turn_on at sunset + 30 minutes
  • String Lights — Off at 11pmswitch.turn_off at 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

  1. 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.
  2. Turn the lights off when the house goes to sleep (input_boolean.sleepingon).
  3. 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 edge
  • time at 00:00:00 — the off edge
  • state on input_boolean.sleeping — both directions (sleep-off, and re-render at Good Morning)
  • homeassistant start — 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 new string_lights_render_triggers id; the two retired ids disappear).
  • Tier 1check_config validates the new script and the sun condition offset syntax.
  • Tier 3 — entity-ref test confirms switch.string_lights, input_boolean.sleeping, and script.string_lights_render all resolve against tests/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