As promised here’s the latest version…
READ THIS FIRST
Before using this blueprint, be warned that it puts a real heavy load on Home Assistant and its database if configured to change colors at a quick pace. While these color changes do not show up in the History or the Logbook, they are still logged in the database unless manually configured to not do so. If Home Assistant is running on an SD card on a Raspberry Pi, large amounts of DB changes can technically cause damage over time. As of this post, this blueprint should be avoided unless you understand these issues.
With that out of the way we do need a solution for the database spam, the only thing I can think of that works at the moment is to literally exclude the light(s) from getting logged for anything.
recorder:
exclude:
entities:
# Exclude any logging of the light.
- light.my_light
# Exclude any logging of the color loop script for this light.
- script.my_light_color_loop
event_types:
# Don't record ANY service calls
- call_service
I have added a feature request to see if there’s a better way: Allow an ability to call a service without any logging/recording
All the notes above are now added to the main post.
Now to the updated blueprint:
blueprint:
name: Color Loop
description: '## Color Loop:
Version: `0.1.0` (2022-06-06)
Loops through predetermined colors for a chosen light.
This will continue indefinitely until the either the light is turned off or the
script is manually disabled.
## Configuration:
### Light:
For the Light, choose the light entity to loop through the colors chosen below.
***Note: This will NOT work for lights which do not gradually transition colors.
In these cases, the light will instantly change to the next color after the transition
time instead of gradually changing.***
TODO: Update this to explain that its possible with certain settings.
TODO: Explain to use groups if multiple lights are desired.
TODO: We need to find out how to avoid DB spamming...
### Colors:
Set the colors to loop through.
Any colors set to black (R:0 G:0 B:0) will be omitted from the loop.
*Note: There is no current way to re-order the chosen colors, you must change
or remove (set to black) colors to change the order.*
*Note: Since RGB colors do not translate well to colored lights (the current
brightness of the light does not get changed by this script) the color previews
may not look accurate to the final result. In order to get the closest result
set the color picker to the TOP of the color selection.
TODO: Refactor above or explain that this is different on mobile.
### Transition Time:
Choose the time it takes to cycle through each color.
### Max Color Distance:
Determines how many colors in between the chosen colors should be transitioned to.
This prevents issues where the color fades to white in between colors by
dynamically picking colors in between the chosen colors.
This value basically means the minimum amount of degrees of the color wheel
should result in an in-between color to be added.
For Example: Red and Cyan are on the opposite side of the color wheel (180 degrees apart):
- Choosing `180` will cause the color transition going straight across resulting in a fade to white in between the colors.
- Choosing `90` will have a new color added every 90 degrees, so in this case a new transitional color will be added at Purple.
- Choosing `60` will have a new color added every 60 degrees, so in this case two transitional colors will be added at Blue and Pink.
- Choosing `30` will have a new color added every 30 degrees, so in this case three transitional colors will be added at Red/Pink, Purple, and Blue.
- Choosing `1` will have a new color added every 1 degrees, so in this case the light will be set to a new color for EVERY color along the way.
If using a light which does not support color transitions, this can be set
to a very low number (eg 1-10) in order to fake the color transition by
updating the colors gradually along the way.
In summary, for performance reasons, keep this value as high as possible and
lower it as needed to avoid the color going brighter in between the
chosen colors.
### Max Changes Per Second:
With the combination of `Transition Time` and `Max Color Distance` both being
set to a low value, this could end up resulting in WAY too many calls to the
light to update its color (up to 180 calls per second). In order to avoid
overloading Home Assistant, this value can be used in order to set a maximum
limit of how many calls per second to change the light during transition can
be made.
For performance reasons, this should be as low as possible, but with lights
which do not support color transitions, this can be increased in order to
have smoother color transitions.
'
domain: script
input:
light:
name: Light
selector:
entity:
domain:
- light
- group
multiple: false
transition:
name: Transition Time
selector:
duration: {}
default:
hours: 0
minutes: 0
seconds: 15
max_color_distance:
name: Max Color Distance
default: 60
selector:
number:
min: 1
max: 180
max_changes_per_second:
name: Max Changes Per Second
default: 1
selector:
number:
min: 1
max: 5
color_1:
name: Color 1
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_2:
name: Color 2
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_3:
name: Color 3
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_4:
name: Color 4
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_5:
name: Color 5
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_6:
name: Color 6
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_7:
name: Color 7
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_8:
name: Color 8
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_9:
name: Color 9
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_10:
name: Color 10
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_11:
name: Color 11
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
color_12:
name: Color 12
description: 'Set to black to omit this color'
default:
- 0
- 0
- 0
selector:
color_rgb: {}
# If we trigger the script while its running, restart the script.
mode: restart
sequence:
- alias: Set up main variables
variables:
transition: !input 'transition'
transition_seconds: '{{ ((transition.hours)*60*60) + ((transition.minutes)*60)
+ transition.seconds }}'
max_color_distance: !input 'max_color_distance'
max_changes_per_second: !input 'max_changes_per_second'
color_rgbs:
- !input 'color_1'
- !input 'color_2'
- !input 'color_3'
- !input 'color_4'
- !input 'color_5'
- !input 'color_6'
- !input 'color_7'
- !input 'color_8'
- !input 'color_9'
- !input 'color_10'
- !input 'color_11'
- !input 'color_12'
# Convert the array of RGB colors to a comma separated list of HSV values.
# Exclude any colors set to black.
# https://community.home-assistant.io/t/using-hsv-hsb-to-set-colored-lights/15472
# https://github.com/home-assistant/core/issues/33678#issuecomment-609424851
# https://stackoverflow.com/a/56141280/4147996
color_hsv_list: >-
{%- set data = namespace(entries=[]) -%}
{%- for color_rgb in color_rgbs -%}
{%- if color_rgb != [0,0,0] -%}
{%- set r = (color_rgb[0]/255) -%}
{%- set g = (color_rgb[1]/255) -%}
{%- set b = (color_rgb[2]/255) -%}
{%- set maxRGB = max(r,g,b) -%}
{%- set minRGB = min(r,g,b) -%}
{%- set chroma = maxRGB - minRGB -%}
{%- if chroma == 0 -%}
{%- set h = 0 -%}
{%- set s = 0 -%}
{%- set v = maxRGB -%}
{%- else -%}
{%- if r == minRGB -%}
{%- set h = 3-((g-b)/chroma) -%}
{%- elif b == minRGB -%}
{%- set h = 1-((r-g)/chroma) -%}
{%- else -%}
{%- set h = 5-((b-r)/chroma) -%}
{%- endif -%}
{%- set h = 60 * h -%}
{%- set s = chroma / maxRGB -%}
{%- set v = maxRGB -%}
{%- endif -%}
{%- set h = h|round(2)|string -%}
{%- set s = s|round(2)|string -%}
{%- set v = v|round(2)|string -%}
{%- set comma_sep = h + "|" + s + "|" + v -%}
{%- set data.entries = data.entries + [comma_sep] -%}
{%- endif -%}
{%- endfor -%}
{{ data.entries | join(",") }}
color_count: '{{ color_hsv_list.split(",")|length }}'
- alias: Change Color
repeat:
while:
- condition: state
entity_id: !input 'light'
state: 'on'
sequence:
- variables:
# Total iterations so-far.
i_total: '{{ repeat.index }}'
# Current color index we're on.
i_cur: '{{ (i_total - 1) % color_count }}'
# Next color index to change to.
i_next: '{{ i_total % color_count }}'
# Get the H/S of current colors.
hsv_cur: '{{ color_hsv_list.split(",")[i_cur] }}'
hue_cur: '{{ hsv_cur.split("|")[0] }}'
sat_cur: '{{ (hsv_cur.split("|")[1])|float * 100 }}'
# Get the H/S of next colors.
hsv_next: '{{ color_hsv_list.split(",")[i_next] }}'
hue_next: '{{ hsv_next.split("|")[0] }}'
sat_next: '{{ (hsv_next.split("|")[1])|float * 100 }}'
# Get the hue color distance.
# https://stackoverflow.com/questions/1878907#comment119528981_7869457
color_distance: '{{ ((hue_next - hue_cur + 540) % 360) - 180 }}'
color_distance_abs: '{{ color_distance|abs }}'
# Determine the ideal amount of steps to make between colors.
iterations_desired: >-
{% if color_distance_abs < max_color_distance %}
{{ 1 }}
{% else %}
{{ (color_distance_abs / max_color_distance)|round(0, 'floor') }}
{% endif %}
# Limit the fade iterations based on configuration.
fade_iterations: '{{ min(iterations_desired, (max_changes_per_second * transition_seconds)) }}'
# Calculate what we need to increase/decrease the Hue/Sat by for each fade interation.
hue_step: '{{ (color_distance / fade_iterations) }}'
sat_step: '{{ ((sat_next - sat_cur) / fade_iterations) }}'
- alias: Fade to Next Color
repeat:
count: '{{ fade_iterations }}'
sequence:
- condition: state
entity_id: !input 'light'
state: 'on'
- variables:
# Total fade iterations so-far.
t_total: '{{ repeat.index }}'
# Current fade iterations we're on.
t_cur: '{{ (t_total - 1) % fade_iterations }}'
# Divide the transition in by # of iterations.
transition_fade: '{{ transition_seconds / fade_iterations }}'
# Get the current Hue value and compensate if it rolls over 0 or 360.
transition_hue_calc: '{{ hue_cur + (hue_step * t_cur)|round(2) }}'
transition_hue: >-
{% if transition_hue_calc > 360 %}
{{ transition_hue_calc - 360 }}
{% elif transition_hue_calc < 0 %}
{{ transition_hue_calc + 360 }}
{% else %}
{{ transition_hue_calc }}
{% endif %}
transition_sat: '{{ min(max((sat_cur + (sat_step * t_cur))|round(2), 0), 100) }}'
# Transition to the next mid-way color or final color.
- service: light.turn_on
target:
entity_id: !input 'light'
data:
hs_color: '{{ [transition_hue, transition_sat] }}'
transition: '{{ transition_fade }}'
- delay: '{{ transition_fade }}'
This adds the following changes:
- Colors are now picked via UI color picker:
- There is no such thing as a multi-value color picker selector (yet) so we have to have a fixed number of colors.
- There is a max limit of 12 colors (easy enough to add more, but not sure one would want to do so)
- Setting a color to black will omit the color from being used.
- Groups are now allowed as well as light entities.
- The color loop will now pick colors in between the main colors to avoid fading to white mid-transition.
- In order to fine-tune the in-between colors a
Max Color Distance
setting has been added:
- This will determine how many colors to add in between, based on the setting as well as how “far apart” the colors are.
- In order to prevent colors from changing too quickly there is a
Max Changes Per Second
setting.
Future changes:
- We really need to fix the problem of DB spamming without needing to exclude the light from ANY logging.
- I would like to rename
Max Color Distance
, the name is a bit misleading and I can’t think of anything better at the moment.
-
Max Changes Per Second
should probably be changed to Max Changes Per Minute
so we can have limits slower than once a second.