You are reading a how to for ‘triggered dynamic brightness based on time of the day’ or call it ‘time to brightness.’
This is very easy to implement, it’s just a somewhat big post to explain the ins and outs.
Latest update here: Dynamic brightness and color temperature based on time of the day - setup with Lovelace sliders - #7 by Saturnus
Here is how it looks in Lovelace:
The following setup will be useful when you want to illuminate an area with different brightness levels at different times. For example: brightness 16 at night, brightness 192 during the day, fade in from night to day 6:00-9:00, and fade out from day to night 22:00-23:30.
These brightness levels will be set when the automation is triggered, so the light does not have to be on all day. This setup also does not interfere with other automations that set specific brightness levels because you can obviously use conditions. For example use this setup with a motion sensor, and override it with a manual button that maybe temporarily disables the automation we will set up now.
To get a better understanding of dynamic brightness, have a look at the graph I made at Desmos.
The graph describes the brightness of the light during the day (only when triggered). It has the minutes of the day (0-1439) on the x-axis and the brightness (0-255) on the y-axis. Now in Desmos, open the “3c. Formulas components (dynamic)” folder in the left pane. What appears are the sliders just as how they will work in HA via Lovelace after setup.
- The lower level brightness (m / y1)
- The upper level brightness (l / y2)
- The fade in start time (a / x1)
- The fade in finish time (b / x2)
- The fade out start time (c / x3)
- The fade out finish time (d / x4)
Play with the sliders to get an understanding.
To the code then…
Latest update here: Dynamic brightness and color temperature based on time of the day - setup with Lovelace sliders - #7 by Saturnus
Original post:
In the code below, the example of hallway_light is used. It is easy to change this but make sure you do change everything systematically then.
We need 6 input_numbers in the configuration.yaml per light:
input_number:
hallway_light_x1:
name: Hallway light x1
min: 0
max: 12
step: 0.25
hallway_light_x2:
name: Hallway light x2
min: 0
max: 12
step: 0.25
hallway_light_x3:
name: Hallway light x3
min: 12
max: 24
step: 0.25
hallway_light_x4:
name: Hallway light x4
min: 12
max: 24
step: 0.25
hallway_light_y1:
name: Hallway light y1
min: 0
max: 255
step: 5
hallway_light_y2:
name: Hallway light y2
min: 0
max: 255
step: 5
This is the automation for HA:
automation:
- id: '001'
alias: Hallway light dynamic brightness < Motion
trigger:
- platform: state
entity_id: binary_sensor.motion_sensor
to: 'on'
condition:
- condition: state
entity_id: light.hallway
state: 'off'
action:
- service: light.turn_on
data_template:
entity_id: light.hallway
color_temp: 250
brightness: >-
{% if ( ( states('input_number.hallway_light_x1') | float ) *60 ) > ( ( ( now().hour ) *60 ) + ( now().minute ) ) >= ( 0 ) %}
{{ states('input_number.hallway_light_y1') | int }}
{% elif ( ( states('input_number.hallway_light_x1') | float ) *60 ) <= ( ( ( now().hour ) *60 ) + ( now().minute ) ) < ( ( states('input_number.hallway_light_x2') | float ) *60 ) %}
{{ ( (now().hour*60 + now().minute) * (states('input_number.hallway_light_y2' ) | int - states('input_number.hallway_light_y1') | int ) / (states('input_number.hallway_light_x2') | int *60 - states('input_number.hallway_light_x1') | int *60 ) + (states('input_number.hallway_light_y2' ) | int - states('input_number.hallway_light_y1') | int ) / (states('input_number.hallway_light_x2') | int *60 - states('input_number.hallway_light_x1') | int *60 ) * -1 *states('input_number.hallway_light_x1') | int *60 + states('input_number.hallway_light_y1') | int ) | int }}
{% elif ( ( states('input_number.hallway_light_x2') | float ) *60 ) <= ( ( ( now().hour ) *60 ) + ( now().minute ) ) < ( ( states('input_number.hallway_light_x3') | float ) *60 ) %}
{{ states('input_number.hallway_light_y2') | int }}
{% elif ( ( states('input_number.hallway_light_x3') | float ) *60 ) <= ( ( ( now().hour ) *60 ) + ( now().minute ) ) < ( ( states('input_number.hallway_light_x4') | float ) *60 ) %}
{{ ( (now().hour*60 + now().minute) * (states('input_number.hallway_light_y1' ) | int - states('input_number.hallway_light_y2') | int ) / (states('input_number.hallway_light_x4') | int *60 - states('input_number.hallway_light_x3') | int *60 ) + (states('input_number.hallway_light_y1' ) | int - states('input_number.hallway_light_y2') | int ) / (states('input_number.hallway_light_x4') | int *60 - states('input_number.hallway_light_x3') | int *60 ) * -1 *states('input_number.hallway_light_x3') | int *60 + states('input_number.hallway_light_y2') | int ) | int }}
{% elif ( ( states('input_number.hallway_light_x4') | float ) *60 ) <= ( ( ( now().hour ) *60 ) + ( now().minute ) ) < ( 1440 ) %}
{{ states('input_number.hallway_light_y1') | int }}
{% else %}
{{ ( 3 ) | int }}
{% endif %}
The if state is selected based on which minute of the day it currently is, and according to that the brightness will be calculated.
Here the UI Lovelace YAML code to get the sliders:
views:
- title: Preferences
icon: mdi:tune-vertical
cards:
- type: vertical-stack
cards:
- type: entities
title: Hallway
show_header_toggle: false
entities:
- entity: input_number.hallway_light_y1
name: Brightness night
icon: mdi:brightness-3
- entity: input_number.hallway_light_y2
name: Brightness day
icon: mdi:white-balance-sunny
- entity: input_number.hallway_light_x1
name: Fade in start
icon: mdi:arrow-expand-up
- entity: input_number.hallway_light_x2
name: Fade in done
icon: mdi:check
- entity: input_number.hallway_light_x3
name: Fade out start
icon: mdi:arrow-expand-down
- entity: input_number.hallway_light_x4
name: Fade out done
icon: mdi:check
If you used Desmos to generate your preferences, then note that [a-d] / x[1-4] are in minutes in Desmos, but in ‘mathematical hours’ in Lovelace. Thus 540 equals 09:00 and thus 9 in Lovelace. 570 equals 09:30 and thus 9,5 in Lovelace. 585 equals 09:45 and thus 9,75 in Lovelace.
Notes that shouldn’t matter but provide more detail in case you are going to customize:
This setup has been working without problems for 6 weeks now, except what seems to be an unrelated 1-2x per week minor issue with my Hue colour bulb. Wrong brightness from time in template if statement
Any minute of the day equal or greater than 1440 and smaller than 0 will result in brightness=3. This should not happen because ‘minute 1440’ does not exist; it will be 0 again. Let alone anything >1440 and <0. This is added for debugging/closing the if statement.
All the if states are exclusive. Even though the first one that is valid will be executed, there still is only one valid each time.
You can change the input_number details (min, max, step) of the sliders, but do note that weird things will happen when you: 1) Change the order of the x/y-points in the graph; e.g. have x1 after x3. 2) Use numbers outside the interval of 0-24 for min and max. These will not be converted to corresponding next cycle hours; e.g. 25 → 1 is not happening. This also means that there is a limitation for the fade out finish time (d / x4); 24 is the maximum in this setup. My suggestions is to keep this general shape when trying out via Desmos: low, increasing and reaching max before 12:00, going steady for a while, then decreasing.
Why input_number and not input_datetime? Because the latter one is ridiculous to adjust in Lovelace.
Brightness is set on minute precision. Minute 1440 however will never occur (because 24:00 does not exist, it will be 0:00 instead) and thus the corresponding brightness will never be set. This might result in brightness at 00:00 being off, but only in extremely weird unlogical use cases.
Currently brightness is only updated when the automation is triggered, although it is very easy to cause ‘retriggers’ for as long as something (motion maybe) is detected.
Future improvements?
-
I will try to covert it to a script ‘soon’ that uses the entity_id as variable, so that one instance can be easily used for multiple lights.Done.
Problems?
Let me know. In this topic I had to generalize some of my personal data, maybe I did something wrong so feedback is welcome.