-
-
Save AllardKatan/705b993278ff780bae43e13f0e413dcf to your computer and use it in GitHub Desktop.
Color Loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
blueprint: | |
name: Color Loop | |
description: '# Color Loop | |
### Version: `2.0.x` (2023-11-25) | |
#### Tested on Home Assistant Version: `2023.11.3` | |
#### Blueprints Page: <https://community.home-assistant.io/t/427682> | |
Loops through predetermined colors for a chosen light. (version edited by Allard Katan to add randomizer) | |
Can be configured to either: | |
- Loop through colors whenever the light is on. | |
- Loop through colors only when both the light and a toggle switch are on. | |
## READ THIS FIRST | |
**Before using this blueprint, be warned that it can put 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](https://www.reddit.com/r/homeassistant/comments/jvwtv1/friendly_reminder_dont_use_a_sd_card_on_a_pi/). | |
Refer to the next section regarding solutions to mitigate this issue. If you do | |
not understand these issues, this blueprint should probably be avoided.** | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click here for solutions | |
to help avoid overloading Home Assistant</u></b></big></summary> <hr> | |
### Solution 1: Exclude lights from recorder: | |
With that out of the way need a solution for the database spam, one solution that | |
works at the moment is to literally prevent the light(s) from getting logged for | |
*anything*. The following configuration could be added and populated with the | |
chosen lights and the entity ID of this script. | |
<code> <!-- Code Blocks in markdown seem to be broken, the following is a work-around | |
--> recorder: | |
exclude: | |
entities: | |
\# Exclude any logging of the chosen light. | |
- light.my_light | |
\# Exclude any logging of this color loop | |
script. | |
- automation.my_light_color_loop | |
event_types: | |
\# Do not record ANY service calls | |
- call_service </code> | |
If a light group is used in this blueprint, you should add all individual light | |
entities to the exclusion above as well as the group ID. | |
Refer to [Recorder Exclude Docs](https://www.home-assistant.io/integrations/recorder#exclude) | |
### Solution 2: Increase Commit Interval: | |
Another solution is to increase the commit interval. By default the DB is written | |
to once a second. Increasing this value will prevent the Database from being spammed | |
with frequent requests. | |
This will allow the lights to still be logged, but depending on how often this | |
light is configured to change colors, this can end up significantly increasing | |
the size of the DB. | |
<code> <!-- Code Blocks in markdown seem to be broken, the following is a work-around | |
--> recorder: | |
\# Write to the DB every 15 seconds instead of once a second. | |
commit_interval: 15 </code> | |
Refer to [Recorder Commit Interval Docs](https://www.home-assistant.io/integrations/recorder#commit_interval) | |
### Future solution: | |
A feature request has been added to see if there is a better way to do this without | |
needing to *always* exclude logging: [Allow an ability to call a service without | |
any logging/recording](https://community.home-assistant.io/t/allow-an-ability-to-call-a-service-without-any-logging-recording/428693) | |
<hr> </details> ' | |
domain: automation | |
input: | |
light: | |
name: Light | |
description: ' The Light entity or Light Group entity to loop through the colors | |
chosen below. ' | |
selector: | |
entity: | |
domain: | |
- light | |
- group | |
multiple: false | |
helper_toggle: | |
name: Color Loop Toggle | |
description: 'An ON/OFF toggle to enable/disable enable/disable the color looping | |
for the chosen Light. | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here For More Info</u></b></big></summary> | |
<hr> | |
A single Color Loop Toggle can be used across multiple Color Loop automations | |
to control multiple lights at the same time with each light having their own | |
set of colors / rules. | |
**If you don''t want to add a separate toggle and want the light to _always_ | |
loop colors _whenever it is on_ then set this value to be the same light entity | |
as above.** | |
### General rules in how the toggle affects the color loop: | |
- If the `Toggle` is turned `ON` while the `Light` is `ON`: `Color looping | |
starts`. | |
- If the `Toggle` is turned `OFF` while the `Light` is `ON`: `Light is set | |
to current color and stops color-looping`. | |
- If the `Toggle` is turned `ON`/`OFF` while the `Light` is `OFF`: `Nothing | |
Happens`. | |
- If the `Light` is turned `ON` while the `Toggle` is `ON`: `Light is turned | |
on and Color looping starts`. | |
- If the `Light` is turned `ON` while the `Toggle` is `OFF`: `Light is turned | |
on and no Color Looping occurs`. | |
- If the `Light` is turned `OFF` while the `Toggle` is `ON`/`OFF`: `The light | |
turns OFF and Color Looping stops`. | |
<hr> </details> | |
<br> | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here For Configuration | |
Instructions</u></b></big></summary> <hr> | |
Configure using one of the options below. | |
### Option 1: Create Toggle via UI: | |
1. Go to `Settings` > `Devices & Services` > `Helpers`. | |
2. Press `+ Create Helper`. | |
3. Choose `Toggle`. | |
4. Add a `Name` and save. | |
5. Select this new Toggle for this blueprint. | |
### Option 2: Create Toggle via YML Configuration: | |
Add the following YML configuration: | |
<code> <!-- Code Blocks in markdown seem to be broken, the following is a | |
work-around --> input_boolean: | |
my_color_loop_toggle: | |
name: My Color Loop Toggle | |
icon: mdi:palette-outline </code> | |
Select this new Toggle for this blueprint. | |
### Option 3: Configure Color Loop to ALWAYS be running whenever the light | |
is on: | |
Set this value to be the same light entity as above. | |
_Note: Light Groups are intentionally excluded from being added as a toggle | |
as turning off individual lights would be problematic. You will need to add | |
a toggle entity here in order to add color looping to groups._ | |
<hr> </details> ' | |
default: | |
selector: | |
entity: | |
domain: | |
- light | |
- input_boolean | |
multiple: false | |
transition: | |
name: Transition Time | |
description: ' Choose the time it takes to cycle through each color. ' | |
selector: | |
duration: {} | |
default: | |
hours: 0 | |
minutes: 0 | |
seconds: 15 | |
randomizer: | |
name: Random start offset | |
description: 'A random time between zero and this number times the total cycle is given as a delay to the start of the automation. | |
Use a value >0 if you have multiple lights with multiple automations and you want them to not all change at the same moment' | |
default: 0 | |
selector: | |
number: | |
min: 0 | |
max: 1 | |
step: 0.02 | |
mode: slider | |
max_color_distance: | |
name: Max Color Distance | |
description: ' Helps control how the colors are transitioned. | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here For More Info</u></b></big></summary> | |
<hr> | |
This value controls how colors in between the chosen colors are dynamically | |
chosen in order to prevent issues where: | |
- The color fades to white (or a lighter color) in between the chosen colors. | |
- The color does not transition smoothly or at all. | |
The color wheel uses (Hue) which are degrees (0-359) which represent where | |
colors are along the curve of the wheel. | |
This `Max Color Distance` value basically means the cut-off amount of degrees | |
on the color wheel which should result in new in-between color(s) to be added. | |
If these extra colors are not added, then the color transition will follow | |
a straight path across the wheel, likely resulting in colors we don''t want | |
along the way. | |
For example: If we wanted to transition from Red to Cyan which are on the | |
opposite side of the color wheel (180 degrees apart): | |
- Setting `Max Color Distance` to `180` will cause the color transition to | |
go straight across resulting in a fade to white in between the colors. | |
![Image](https://user-images.githubusercontent.com/22206300/232822236-8cca7b37-0a72-4cbc-b198-4d1fb1dd2325.png) | |
- Setting `Max Color Distance` to `90` will have a new color added every `90` | |
degrees. In this case a new transitional color will be added at Purple. | |
![Image](https://user-images.githubusercontent.com/22206300/232822243-2a3b710b-b72f-4d5c-b4f3-e0f35374b602.png) | |
- Setting `Max Color Distance` to `60` will have a new color added every `60` | |
degrees. In this case two transitional colors will be added at Blue and Pink. | |
![Image](https://user-images.githubusercontent.com/22206300/232822244-a187e691-8c85-4266-a904-d6f179a0a0af.png) | |
- Setting `Max Color Distance` to `30` will have a new color added every `30` | |
degrees. In this case three transitional colors will be added at Red/Pink, | |
Purple, and Blue. | |
![Image](https://user-images.githubusercontent.com/22206300/232822245-60aea03f-845e-47ec-a4a1-4602c7601717.png) | |
- Setting `Max Color Distance` to `1` will have a new color added every `1` | |
degree. In this case the light will have a command sent to change to a new | |
color for EVERY color along the way. | |
![Image](https://user-images.githubusercontent.com/22206300/232822249-2db23295-3cde-430c-b482-be303b84f23f.png) | |
<hr> </details> | |
**For performance reasons, _keep this value as high as possible_. Lower numbers | |
will result in more commands being sent to the light putting more burden on | |
Home Assistant** | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here If You Are | |
Unsure What Value To Set This To</u></b></big></summary> <hr> | |
Try saving the Color Loop with `Max Color Distance` set to `180` and `Transition | |
Time` set to something quick like `3 seconds`. | |
- If the light fades smoothly but gets brighter/whiter between colors, then | |
reduce `Max Color Distance` by about `60` and save. Keep doing this until | |
the color fading is acceptable. | |
- **If there is no fading at all between colors** (it jumps directly from | |
color to color every 3 seconds) then this light does not support color transitions. | |
You may want to choose a number between `1` - `15` depending on how close | |
the colors are to each other and the desired `Transition Time`. This will | |
essentially fake the color transitions by sending color-change commands to | |
the light more often. | |
<hr> </details> ' | |
default: 60 | |
selector: | |
number: | |
min: 1.0 | |
max: 180.0 | |
step: 1.0 | |
mode: slider | |
max_changes_per_second: | |
name: Max Changes Per Second | |
description: ' The maximum number of commands (per second) that can be performed | |
for color changes. | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here For More Info</u></b></big></summary> | |
<hr> | |
Without `Max Changes Per Second`, if a combination of `Transition Time` and | |
`Max Color Distance` were both being set to a low value, it would 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. | |
Note: This being set to 1 second does not mean that it will always be sending | |
commands every second. The transition times and/or how close the colors are | |
to each other will cause the time between commands to vary quite a bit, this | |
is basically just a speed limit. | |
<hr> </details> | |
**For performance reasons, this should be set as low as possible**, but with | |
lights which do not support color transitions, this can be increased in order | |
to have smoother color transitions. ' | |
default: 1 | |
selector: | |
number: | |
min: 1.0 | |
max: 5.0 | |
step: 1.0 | |
mode: slider | |
color_1: | |
name: Color 1 | |
description: ' Set to black to omit this color. | |
<!-- For some reason we need something directly above the details tag to have | |
it render properly --> <details> <summary><big><b><u>Click Here For More Info</u></b></big></summary> | |
<hr> | |
These are the main colors that will be looped 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.* | |
<hr> </details> ' | |
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: {} | |
source_url: https://gist.github.com/AllardKatan/705b993278ff780bae43e13f0e413dcf | |
mode: restart | |
variables: | |
color_loop_light: !input light | |
helper_toggle: !input helper_toggle | |
toggle_is_used: '{{ color_loop_light != helper_toggle }}' | |
toggle_transition_check_time: 1.5 | |
transition: !input transition | |
transition_seconds: '{{ ((transition.hours)*60*60) + ((transition.minutes)*60) + | |
transition.seconds }}' | |
randomizer_value: !input randomizer | |
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 | |
color_hsv_list_csv: "{%- set data = namespace(entries=[]) -%} {%- for color_rgb | |
in color_rgbs -%}\n {%- if color_rgb != [0,0,0] -%}\n {%- set r = (color_rgb[0]/255) | |
-%}\n {%- set g = (color_rgb[1]/255) -%}\n {%- set b = (color_rgb[2]/255) | |
-%}\n {%- set maxRGB = max(r,g,b) -%}\n {%- set minRGB = min(r,g,b) -%}\n | |
\ {%- set chroma = maxRGB - minRGB -%}\n {%- if chroma == 0 -%}\n {%- | |
set h = 0 -%}\n {%- set s = 0 -%}\n {%- set v = maxRGB -%}\n {%- | |
else -%}\n {%- if r == minRGB -%}\n {%- set h = 3-((g-b)/chroma) -%}\n | |
\ {%- elif b == minRGB -%}\n {%- set h = 1-((r-g)/chroma) -%}\n {%- | |
else -%}\n {%- set h = 5-((b-r)/chroma) -%}\n {%- endif -%}\n {%- | |
set h = 60 * h -%}\n {%- set s = chroma / maxRGB -%}\n {%- set v = maxRGB | |
-%}\n {%- endif -%}\n {%- set h = h|round(2)|string -%}\n {%- set s = | |
s|round(2)|string -%}\n {%- set v = v|round(2)|string -%}\n {%- set comma_sep | |
= h + \"|\" + s + \"|\" + v -%}\n {%- set data.entries = data.entries + [comma_sep] | |
-%}\n {%- endif -%}\n{%- endfor -%} {{ data.entries | join(\",\") }}" | |
color_hsv_list: '{{ color_hsv_list_csv.split(",") }}' | |
color_count: '{{ color_hsv_list|length }}' | |
trigger: | |
- platform: state | |
entity_id: | |
- !input light | |
from: 'off' | |
to: 'on' | |
- platform: state | |
entity_id: | |
- !input helper_toggle | |
from: 'off' | |
to: 'on' | |
- platform: event | |
event_type: automation_reloaded | |
condition: | |
- condition: and | |
conditions: | |
- condition: state | |
entity_id: !input helper_toggle | |
state: 'on' | |
- condition: state | |
entity_id: !input light | |
state: 'on' | |
- '{{ toggle_is_used or trigger.id != "1" }}' | |
action: | |
if: | |
condition: template | |
value_template: '{{randomizer_value>0}}' | |
then: | |
- delay: '{{ (range(256)|random) *transition_seconds*randomizer_value/256 }}' | |
- alias: Change Color | |
repeat: | |
while: | |
- condition: and | |
conditions: | |
- condition: state | |
entity_id: !input light | |
state: 'on' | |
- condition: state | |
entity_id: !input helper_toggle | |
state: 'on' | |
sequence: | |
- variables: | |
i_total: '{{ repeat.index }}' | |
i_cur: '{{ (i_total - 1) % color_count }}' | |
i_next: '{{ i_total % color_count }}' | |
hsv_cur_csv: '{{ color_hsv_list[i_cur] }}' | |
hue_cur: '{{ hsv_cur_csv.split("|")[0] }}' | |
sat_cur: '{{ (hsv_cur_csv.split("|")[1])|float * 100 }}' | |
hsv_next_csv: '{{ color_hsv_list[i_next] }}' | |
hue_next: '{{ hsv_next_csv.split("|")[0] }}' | |
sat_next: '{{ (hsv_next_csv.split("|")[1])|float * 100 }}' | |
color_distance: '{{ ((hue_next - hue_cur + 540) % 360) - 180 }}' | |
color_distance_abs: '{{ color_distance|abs }}' | |
iterations_desired: "{% if color_distance_abs < max_color_distance %}\n {{ | |
1 }}\n{% else %}\n {{ (color_distance_abs / max_color_distance)|round(0, | |
'floor') }}\n{% endif %}" | |
fade_iterations: '{{ min(iterations_desired, (max_changes_per_second * transition_seconds)) | |
}}' | |
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: and | |
conditions: | |
- condition: state | |
entity_id: !input light | |
state: 'on' | |
- condition: state | |
entity_id: !input helper_toggle | |
state: 'on' | |
- variables: | |
lights_currently_on: '{{ expand(color_loop_light) | selectattr(''domain'', | |
''eq'', ''light'') | selectattr(''state'', ''eq'', ''on'') | map(attribute=''entity_id'') | |
| list }}' | |
t_total: '{{ repeat.index }}' | |
t_cur: '{{ (t_total - 1) % fade_iterations }}' | |
transition_time_length: '{{ transition_seconds / fade_iterations }}' | |
transition_time_start: '{{ as_timestamp(utcnow()) }}' | |
transition_time_end: '{{ as_timestamp(utcnow()) + transition_time_length | |
}}' | |
transition_hue_calc: '{{ hue_cur + (hue_step * t_cur)|round(2) }}' | |
transition_hue: "{% if transition_hue_calc > 360 %}\n {{ transition_hue_calc | |
- 360 }}\n{% elif transition_hue_calc < 0 %}\n {{ transition_hue_calc | |
+ 360 }}\n{% else %}\n {{ transition_hue_calc }}\n{% endif %}" | |
transition_sat: '{{ min(max((sat_cur + (sat_step * t_cur))|round(2), 0), | |
100) }}' | |
- service: light.turn_on | |
target: | |
entity_id: '{{ lights_currently_on }}' | |
data: | |
hs_color: '{{ [transition_hue, transition_sat] }}' | |
transition: '{{ transition_time_length }}' | |
- if: | |
- condition: or | |
conditions: | |
- '{{ toggle_is_used == false }}' | |
- '{{ transition_time_length <= toggle_transition_check_time }}' | |
then: | |
- delay: '{{ transition_time_length }}' | |
else: | |
- alias: Check to see if toggle is turned off during transition. | |
repeat: | |
until: | |
- condition: or | |
conditions: | |
- '{{ as_timestamp(utcnow()) >= transition_time_end }}' | |
- condition: state | |
entity_id: !input light | |
state: 'off' | |
sequence: | |
- if: | |
- condition: state | |
entity_id: !input helper_toggle | |
state: 'off' | |
then: | |
- service: light.turn_on | |
target: | |
entity_id: '{{ lights_currently_on }}' | |
data: | |
hs_color: '{{ state_attr(color_loop_light, ''hs_color'') }}' | |
transition: 0 | |
- stop: Stopped because the toggle was switched off | |
else: | |
- variables: | |
delay: "{% set time_now = as_timestamp(utcnow()) %} {% if (time_now | |
+ toggle_transition_check_time) > transition_time_end %}\n {{ | |
max(0, (transition_time_end - time_now)) }}\n{% else %}\n {{ | |
toggle_transition_check_time }}\n{% endif %}\n" | |
- delay: '{{ delay }}' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment