Skip to content

Instantly share code, notes, and snippets.

@haberda
Last active May 9, 2024 12:17
Show Gist options
  • Save haberda/a43a40d8b2e734036b1575e1e6df7a59 to your computer and use it in GitHub Desktop.
Save haberda/a43a40d8b2e734036b1575e1e6df7a59 to your computer and use it in GitHub Desktop.
Periodic Lights XY
blueprint:
name: Periodic lights RGB
description: "Dim and adjust color of lights progressively throughout the evening (RGB bulbs only). This is indexed to the midpoint between sunset and sunrise and fit to a sin function, so the midpoint will be minimum brightness and midpoint +12hr will be maximum brightness. The midpoint can be offset by a fixed amount if lights are too dim or bright at the desired time."
domain: automation
source_url: https://gist.github.com/haberda/a43a40d8b2e734036b1575e1e6df7a59
input:
light:
name: Light(s)
description: The light(s) to control
selector:
entity:
multiple: true
domain: light
min_brightness:
name: Minimum Brightness
description: Minimum brightness of the light(s)
default: 1
selector:
number:
min: 0.0
max: 100.0
mode: slider
step: 1.0
unit_of_measurement: '%'
max_brightness:
name: Maximum Brightness
description: Maximum brightness of the light(s)
default: 100
selector:
number:
min: 0.0
max: 100.0
mode: slider
step: 1.0
unit_of_measurement: '%'
max_x:
name: x1
description: x1 in the xy color space.
default: 0.35
selector:
number:
min: 0.0
max: 0.8
mode: slider
step: 0.01
min_x:
name: x2
description: x2 in the xy color space.
default: 0.5
selector:
number:
min: 0.0
max: 0.8
mode: slider
step: 0.01
max_y:
name: y1
description: y1 in the xy color space.
default: 0.35
selector:
number:
min: 0.0
max: 0.8
mode: slider
step: 0.01
min_y:
name: y2
description: y2 in the xy color space.
default: 0.4
selector:
number:
min: 0.0
max: 0.8
mode: slider
step: 0.01
threshold_brightness:
name: Brightness change threshold
description: Threshold value, above which the lights will not be adjusted (+ or -). This is the residual value between the current brightness level and the level it would adjust to.
default: 10
selector:
number:
min: 0.0
max: 100.0
mode: slider
step: 1.0
unit_of_measurement: '%'
transition:
name: Transition
description: Transition time (s)
default: 0
selector:
number:
min: 0.0
max: 30.0
mode: slider
step: 1.0
unit_of_measurement: 's'
offset:
name: Offset
description: Offset from middle of the night (e.g. dimmest lights). Positive for brighter evening and dimmer morning. Negative for dimmer evening and brighter morning.
default: 0
selector:
number:
min: -120.0
max: 120.0
step: 1.0
unit_of_measurement: min
mode: slider
light_on_adjust:
name: Adjust lights when they are turned on
description: 'If true adjusts a light immediately when it is turned on.'
default: true
selector:
boolean:
entity_off_adjust:
name: Adjust lights when disable entity is turned off
description: 'If true adjusts a light immediately when the disable entity is turned off.'
default: true
selector:
boolean:
disable_entity:
name: Disable entity
description: Binary sensor or input boolean that when on disables the updating of lights.
selector:
entity:
multiple: true
event_fired:
name: Event
description: The name of the event that will force an update.
default: 'periodic_lights'
mode: parallel
max_exceeded: silent
variables:
light_list: !input 'light'
threshold_brightness: !input 'threshold_brightness'
min_brightness: !input 'min_brightness'
max_brightness: !input 'max_brightness'
max_x: !input 'max_x'
min_x: !input 'min_x'
max_y: !input 'max_y'
min_y: !input 'min_y'
offset: !input 'offset'
light_on_adjust: !input light_on_adjust
entity_off_adjust: !input entity_off_adjust
pct: >
{% if (states('sun.sun') == 'below_horizon') %}
{% set sunset = as_timestamp(states.sun.sun.attributes.next_setting)|float - 86400 %}
{% set sunrise = as_timestamp(states.sun.sun.attributes.next_rising)|float %}
{% else %}
{% set sunset = as_timestamp(states.sun.sun.attributes.next_setting)|float %}
{% set sunrise = as_timestamp(states.sun.sun.attributes.next_rising)|float %}
{% endif %}
{% set midpoint = (sunset |float + sunrise |float) / 2 + (offset|float * 60) | float %}
{{ sin(((as_timestamp(now())|float - midpoint)/86400)*pi) |abs }}
brightness_percent: '{{ (min_brightness|int+(max_brightness|int-min_brightness|int) * pct) | int }}'
desired_xy_color: >
{% set slope = (max_y|float-min_y|float)/(max_x|float-min_x) %}
{% set b = max_y|float - slope|float * max_x|float %}
{% set desired_x_color = (min_x|float + (max_x|float - min_x|float) * pct|float) %}
{% set desired_y_color = slope * desired_x_color + b %}
{{ [desired_x_color, desired_y_color] }}
on_lights: >
{{ expand(light_list) | selectattr('state', 'eq', 'on')
| map(attribute='entity_id') | list }}
# compliant_lights: "{% if on_lights %}{% for light_entity in on_lights if (state_attr(light_entity, 'brightness') | int - (brightness_percent|int*2.55)) | abs < (threshold_brightness|int*2.55) %}{% if loop.last %}{{ light_entity }}{% else %}{{ light_entity }},{% endif %}{% endfor %}{% endif %}"
compliant_lights: >
{{ expand(light_list) | selectattr('state', 'eq', 'on')
| selectattr('attributes.brightness','ge',((brightness_percent-threshold_brightness)*2.55))
| selectattr('attributes.brightness','le',((brightness_percent+threshold_brightness)*2.55))
| map(attribute='entity_id') | list }}
trigger:
- platform: time_pattern
minutes: '/3'
id: 'time_change'
- platform: state
entity_id: !input 'light'
to: 'on'
id: 'light_on'
- platform: state
entity_id: !input 'disable_entity'
to: 'off'
id: 'disable_off'
- platform: event
event_type: !input event_fired
id: "event_fired"
condition:
- condition: state
entity_id: !input disable_entity
state: 'off'
- condition: template
value_template: "{{ on_lights|length > 0 }}"
action:
- delay:
hours: 0
minutes: 0
seconds: 0
milliseconds: 500
- choose:
- conditions:
- or:
- condition: trigger
id: "event_fired"
- and:
- condition: trigger
id: "disable_off"
- condition: template
value_template: "{{ entity_off_adjust }}"
- condition: template
value_template: "{{ on_lights[0] in light_list }}"
sequence:
- service: light.turn_on
data_template:
entity_id: '{{ on_lights }}'
brightness_pct: "{{ brightness_percent|int }}"
xy_color: '{{ desired_xy_color }}'
transition: !input transition
- conditions:
- condition: trigger
id: "light_on"
- condition: template
value_template: "{{ light_on_adjust }}"
sequence:
- service: light.turn_on
data_template:
entity_id: '{{ on_lights }}'
brightness_pct: "{{ brightness_percent|int }}"
xy_color: '{{ desired_xy_color }}'
default:
- variables:
compliant_lights: >
{{ expand(light_list) | selectattr('state', 'eq', 'on')
| selectattr('attributes.brightness','ge',((brightness_percent-threshold_brightness)*2.55))
| selectattr('attributes.brightness','le',((brightness_percent+threshold_brightness)*2.55))
| map(attribute='entity_id') | list }}
- condition: template
value_template: "{{ compliant_lights|length > 0 }}"
- service: light.turn_on
data:
entity_id: "{{ compliant_lights }}"
brightness_pct: "{{ brightness_percent|int }}"
xy_color: '{{ desired_xy_color }}'
transition: !input transition
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment