The sunset automation required TV mode off, so an afternoon of TV skipped the driveway string lights entirely (Sep 22 and 23) - not an outage. The driveway now has its own sunset -1h automation, with catch-up on restart or switch reconnect before 23:00. Evening brightness lives in one script (evening_lights_apply) that blends between the old step levels; a 5-minute ramp from 20:30 eases lights that are on and leaves alone any a person has changed by hand. The Dining Hall no longer bumps to 50% at 21:30. TV off after 23:30 now only turns off the living room glow instead of bringing the whole house back to full brightness, and TV on/off leave the lights alone in daylight. Lights-out moves to 23:30, and a 01:00 sweep catches anything switched back on at the wall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
3.3 KiB
YAML
77 lines
3.3 KiB
YAML
evening_lights_apply:
|
|
alias: Evening Lights - Apply Schedule
|
|
description: >-
|
|
Sets each evening light to its scheduled brightness for the current time,
|
|
blending linearly between the points in `schedule`. apply=force turns the
|
|
lights on (sunset, TV off); apply=ramp only eases lights that are already
|
|
on, and leaves alone any light someone has changed by hand. While TV mode
|
|
is on the living room and the lights that glare on the TV are left alone.
|
|
mode: queued
|
|
fields:
|
|
apply:
|
|
description: "force: turn lights on at the target. ramp: only adjust lights that are already on."
|
|
example: ramp
|
|
selector:
|
|
select:
|
|
options:
|
|
- force
|
|
- ramp
|
|
variables:
|
|
# [minute of day, brightness %] - 1230 = 20:30, 1260 = 21:00,
|
|
# 1290 = 21:30, 1350 = 22:30. Before the first point a light sits at the
|
|
# first value; after the last it holds the last value until lights-out.
|
|
schedule:
|
|
light.kitchen_lights: [[1230, 100], [1260, 50], [1290, 25], [1350, 1]]
|
|
light.kitchen_wall_light: [[1230, 100], [1260, 50], [1290, 25], [1350, 1]]
|
|
light.bathroom_hallway: [[1230, 75], [1260, 50], [1290, 25], [1350, 10]]
|
|
light.living_room: [[1230, 100], [1260, 50], [1290, 25], [1350, 10]]
|
|
light.dining_hall: [[1290, 25], [1350, 15]]
|
|
tv_mode_lights:
|
|
- light.living_room
|
|
- light.kitchen_wall_light
|
|
- light.dining_hall
|
|
- light.bathroom_hallway
|
|
apply_mode: "{{ apply | default('ramp') }}"
|
|
sequence:
|
|
- repeat:
|
|
for_each: "{{ schedule.keys() | list }}"
|
|
sequence:
|
|
- variables:
|
|
light: "{{ repeat.item }}"
|
|
# [target now, target 5 minutes ago - what the last ramp tick set]
|
|
levels: >-
|
|
{%- macro at(pts, t) -%}
|
|
{%- if t <= pts[0][0] -%}{{ pts[0][1] }}
|
|
{%- elif t >= pts[-1][0] -%}{{ pts[-1][1] }}
|
|
{%- else -%}
|
|
{%- for i in range(pts | length - 1) if pts[i][0] <= t < pts[i + 1][0] -%}
|
|
{{ (pts[i][1] + (pts[i + 1][1] - pts[i][1]) * (t - pts[i][0]) / (pts[i + 1][0] - pts[i][0])) | round(0) | int }}
|
|
{%- endfor -%}
|
|
{%- endif -%}
|
|
{%- endmacro -%}
|
|
{%- set t = now().hour * 60 + now().minute -%}
|
|
{{ [at(schedule[repeat.item], t) | int, at(schedule[repeat.item], t - 5) | int] }}
|
|
current: "{{ ((state_attr(repeat.item, 'brightness') or 0) / 2.55) | round(0) | int }}"
|
|
skip: "{{ is_state('input_boolean.tv_mode', 'on') and repeat.item in tv_mode_lights }}"
|
|
- choose:
|
|
- conditions: "{{ not skip and apply_mode == 'force' }}"
|
|
sequence:
|
|
- action: light.turn_on
|
|
target:
|
|
entity_id: "{{ light }}"
|
|
data:
|
|
brightness_pct: "{{ levels[0] }}"
|
|
transition: 2
|
|
# A light more than 10 points off the last tick was set by hand; the
|
|
# biggest scheduled change in 5 minutes is ~8
|
|
- conditions: >-
|
|
{{ not skip and apply_mode == 'ramp' and is_state(light, 'on')
|
|
and (current - levels[1]) | abs <= 10 and current != levels[0] }}
|
|
sequence:
|
|
- action: light.turn_on
|
|
target:
|
|
entity_id: "{{ light }}"
|
|
data:
|
|
brightness_pct: "{{ levels[0] }}"
|
|
transition: 60
|