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