I was never satisfied with the Flux Light Adjustment component but I always loved the idea. To me it intuitively makes sense that as the night progresses you typically would want the lights to dim. Flux does a good job changing color through the night but there are a couple of problems with the Flux component that wanted to address with these automatons/scripts that include:
- Dimming to near zero at the end of the night
- Balancing of lights that have different inherent brightness
- The ability to change the brightness manually without the automation taking the control back 30 sec later
- When a light is initially turned on automatically adjust it to the current level
To achieve the third bullet I created input numbers for each light that has the same name as each light. I also create a dim_level input number to keep track of the current ‘master dim’ level The basic input number config looks like:
living_room_lamp:
name: Living Room Lamp
initial: 0
min: 0
max: 255
step: 1
dim_level:
name: Dim Level
initial: 12
min: 0
max: 255
step: 1
When any light is turned on it and it’s corresponding input number is immediately adjusted to the current level by this automation:
- id: '2'
alias: 'Autoadjust Light When Turned On' #When a light is turned on adjust to appropriate dim level
initial_state: true
trigger:
- platform: event
event_type: state_changed
condition:
- condition: template
value_template: "{{ trigger.event.data.entity_id.split('.')[0] == 'light' }}"
- condition: template
value_template: "{{ trigger.event.data.new_state.state == 'on' }}"
- condition: template
value_template: "{{ trigger.event.data.old_state.state == 'off' }}"
action:
- service: light.turn_on
data_template:
entity_id: '{{ trigger.event.data.entity_id }}'
brightness: '{{ states.input_number.dim_level.state|int }}'
- service: input_number.set_value
data_template:
entity_id: >
{% set slider_name = trigger.event.data.entity_id.split('.')[1] %}
{% set slider_name = slider_name.split('_lev')[0] %}
{{ "input_number." ~ slider_name }}
value: '{{ states.input_number.dim_level.state|int }}'
Similarly when a light is turned off:
- id: 'Autoadjust inputnumber When Turned off'
alias: 'Autoadjust inputnumber When Turned off'
initial_state: true
trigger:
- platform: event
event_type: state_changed
condition:
- condition: template
value_template: "{{ trigger.event.data.entity_id.split('.')[0] == 'light' }}"
- condition: template
value_template: "{{ trigger.event.data.new_state.state == 'off' }}"
- condition: template
value_template: "{{ trigger.event.data.old_state.state == 'on' }}"
action:
- service: input_number.set_value
data_template:
entity_id: >
{% set slider_name = trigger.event.data.entity_id.split('.')[1] %}
{% set slider_name = slider_name.split('_lev')[0] %}
{{ "input_number." ~ slider_name }}
value: 0
I also have a script that updates the state of zwave devices which tend to be slow to update.
I have to give the Flux component credit here as it provided the core dim level calculation by calculating the percent of time that has passed between sunset and the end time that I have set to 2130. The master dim level is changed by this automation:
- id: '14'
alias: Dim Level # Update Dim Level Every 5 Minutes
hide_entity: true
trigger:
- platform: time
minutes: '/5'
seconds: 00
- platform: state
entity_id: input_boolean.hold_lights
to: 'off'
- platform: homeassistant
event: start
condition:
- condition: sun
after: sunset
- condition: state
entity_id: input_boolean.hold_lights
state: 'off'
action:
- service: input_number.set_value
data_template:
entity_id: input_number.dim_level
value: >
{% if 255 - (255 * (as_timestamp(states.sensor.date.state ~ " " ~ states.sensor.time.state )-(as_timestamp(states.sun.sun.attributes.next_setting)-86400))/(as_timestamp(states.sensor.date.state ~ " 21:30:00") - (as_timestamp(states.sun.sun.attributes.next_setting)-86400)))|int > 12 %}
{{255 - (255 * (as_timestamp(states.sensor.date.state ~ " " ~ states.sensor.time.state )-(as_timestamp(states.sun.sun.attributes.next_setting)-86400))/(as_timestamp(states.sensor.date.state ~ " 21:30:00") - (as_timestamp(states.sun.sun.attributes.next_setting)-86400)))|int}}
{% else %}
12
{% endif %}
And the change is picked up by:
- id: '3'
alias: 'Change Lights When Dim Level Changes' #Changes dim level when a new one is calculated
initial_state: true
trigger:
- platform: state
entity_id: input_number.dim_level
condition:
- condition: time
after: '07:00'
action:
- service: script.turn_on
data:
entity_id:
- script.update_zwave
- delay:
seconds: 10
- service: script.turn_on
data:
entity_id:
- script.light_dimmer
- script.light_dimmer_low
- script.light_dimmer_high
I also have an automation that changes the dim level to 255 at sunrise. So that lights turned on during the day are set to max brightness
The following 3 scripts are really where the magic happens. These scripts loop through each light, checks if the light is on, compares the brightness to the input number and if it passes those checks within a threshold of 25 brightness units the light and input number is adjusted to the new level. The initial script just uses the current master dim level while the other 2 use an adjusted level for lights that need to be balanced because they are either too bright or too dim:
light_dimmer:
alias: light dimmer
sequence:
- service: light.turn_on
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 25 and state.entity_id != 'light.office' and state.entity_id != 'light.family_room' and state.entity_id != 'light.bar' and state.entity_id != 'light.fireplace' %}{% if loop.first %}{% else %},{% endif %}{{state.entity_id}}{% endfor %}"
brightness: '{{ states.input_number.dim_level.state|int }}'
- service: input_number.set_value
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 25 and state.entity_id != 'light.office' and state.entity_id != 'light.family_room' and state.entity_id != 'light.bar' and state.entity_id != 'light.fireplace' %}{% if loop.first %}{% else %},{% endif %}{{'input_number.' ~ state.entity_id.split('.')[1]}}{% endfor %}"
value: '{{ states.input_number.dim_level.state|int }}'
light_dimmer_low:
alias: light dimmer low
sequence:
- service: light.turn_on
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 50 and (state.entity_id == 'light.office' or state.entity_id == 'light.bar') %}{% if loop.first %}{% else %},{% endif %}{{state.entity_id}}{% endfor %}"
brightness: '{% if states.input_number.dim_level.state|int < 235 %} {{ states.input_number.dim_level.state|int +20 }}{% else %}{{ states.input_number.dim_level.state|int}}{% endif %}'
- service: input_number.set_value
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 50 and (state.entity_id == 'light.office' or state.entity_id == 'light.bar') %}{% if loop.first %}{% else %},{% endif %}{{'input_number.' ~ state.entity_id.split('.')[1]}}{% endfor %}"
value: '{% if states.input_number.dim_level.state|int < 235 %} {{ states.input_number.dim_level.state|int +20 }}{% else %}{{ states.input_number.dim_level.state|int}}{% endif %}'
light_dimmer_high:
alias: light dimmer high
sequence:
- service: light.turn_on
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 50 and (state.entity_id == 'light.family_room' or state.entity_id == 'light.fireplace') %}{% if loop.first %}{% else %},{% endif %}{{state.entity_id}}{% endfor %}"
brightness: '{% if states.input_number.dim_level.state|int > 30 %} {{ states.input_number.dim_level.state|int -20 }}{% else %}{{ states.input_number.dim_level.state|int}}{% endif %}'
- service: input_number.set_value
data_template:
entity_id: "{% for state in states.light if state.state=='on' and (state_attr(state.entity_id, 'brightness') | int - states('input_number.' ~ state.entity_id.split('.')[1]) | int) | abs < 50 and (state.entity_id == 'light.family_room' or state.entity_id == 'light.fireplace') %}{% if loop.first %}{% else %},{% endif %}{{'input_number.' ~ state.entity_id.split('.')[1]}}{% endfor %}"
value: '{% if states.input_number.dim_level.state|int > 30 %} {{ states.input_number.dim_level.state|int -20 }}{% else %}{{ states.input_number.dim_level.state|int}}{% endif %}'
So what do you think? Is this overly complicated or stupid? Can we make this better/simpler somehow?