Hm, yes it looks like you’re correct. So started will then ALWAYS be defined as false (as set in the beginning) and thus, even though is_state(entity_id, "on") should now catch it, and stop the script. It won’t because started == true … Yes this now is starting to make sense.
Now to figure out a way to fix it. Would it be too easy to say that the point where started becomes true should be outside the IF/THEN scope and perhaps come right after the delay in the sequence?
Due to the scoping rule, If you define started prior to the repeat you can’t change its value within the repeat (or within the if-then). Your script effectively created two variables with the same name (started) where the first one’s value is independent of the second one.
I see, makes sense. I guess I’ll need to look closer at the other versions people have made. I believe they did some other checks to have the script halt if one were to override the light manually.
My experience/knowledge on the HA-way/templating stuff is still fresh and limited. Back to the ol’ drawing board we go!
That feature was included in the original script I posted a long time ago … and it was based on work created by mrsnyds. It checks if the light’s brightness differs from what it had set it to (implying someone changed it manually).
I see, and yes I did try that version. It never worked for me. I believe it might be due to the way the transition time input is shown on my end. Using a time input instead of a number of seconds.
I believe someone fixed it by changing the transition time to an integer value. However, I haven’t followed all subsequent changes to it. My only interest (a long time ago) was to simplify mrsnyds original work by employing a repeat (newly introduced at the time); I don’t actually use this fader script (because all my lighting natively supports transition).
Cool, I think I’ve dialed it down to where I’m pretty sure this is the key part I’m looking for:
while:
- condition: template
value_template: >-
{% set b = state_attr(light, 'brightness') %}
{% set b = (b/255*100)|round(0, default=0) if b != none else b %}
{{ repeat.index <= 102 and
( (sign == 1 and (b == none or b < end_pct)) or
(sign == -1 and (b != none or b|int(0) > end_pct)) ) and
(b | round(0, default=0) == start_pct + ((repeat.index - 1) * sign)) }}
Let me see how i can incorporate this in the version that DOES work for me ! Cheers for the help so far @123
It’s checking if light’s current brightness value is equal to the previous value that it had set. If it isn’t then (someone/something external to the automation had changed it) the Template Condition reports false and the while stops looping.
- repeat:
while:
- condition: template
value_template: >-
{% set b = state_attr(light, 'brightness') %}
{% set b = (b/255*100)|round(0, default=0) if b != none else b %}
{{ repeat.index <= n and
( (sign == 1 and (b == none or b < end_pct)) or
(sign == -1 and (b != none or b|int(0) > end_pct)) ) and
(b | round(0, default=0) == start_pct + ((repeat.index - 1) * sign)) }}
The light only turns on and it halts/stops. So far I can confirm these two lines by themselves don’t cause any issues:
{% set b = state_attr(light, 'brightness') %}
{% set b = (b/255*100)|round(0, default=0) if b != none else b %}
So I think it’s down to this bit:
{{ repeat.index <= n and
( (sign == 1 and (b == none or b < end_pct)) or
(sign == -1 and (b != none or b|int(0) > end_pct)) ) and
(b | round(0, default=0) == start_pct + ((repeat.index - 1) * sign)) }}
This together with a slightly longer time-frames worked. I was testing it with 60 seconds, setting this to 180 seconds at least made everything work flawlessly. Works for me as I don’t intend to use this sort of a timeframe for transitions.
When manually switched off, the script will halt.
Final version:
fade_light_v2:
description: "Fades lights to a desired level over a specified transition period."
fields:
light:
name: Light
description: Entity_id of light.
selector:
entity:
domain: light
example: light.kitchen
end_pct:
name: End brightness Level
description: Integer value from 0 to 100 representing the desired final brightness level.
selector:
number:
min: 0
max: 100
step: 1
mode: slider
default: 50
example: "50"
transition:
name: Transition Time
description: Transition time for fading in seconds.
selector:
number:
min: 0
max: 1800
step: 1
mode: slider
default: 10
example: "10"
mode: parallel
sequence:
- variables:
start_pct: "{{ ((state_attr(light, 'brightness') | int(0))/255*100) | round(0, default=0) }}"
end_pct: "{{ end_pct | int(0) | round(0, default=0) }}"
delay_msec: >-
{{ ([100, (((transition / (end_pct - start_pct)) | abs) | round(3, default=0) * 1000) | int(0)]|sort)[1] }}
sign: "{{ 1 if start_pct < end_pct else -1 }}"
n: "{{ 1000 * transition / delay_msec | int(0) }}"
- repeat:
while:
- condition: template
value_template: >-
{% set b = state_attr(light, 'brightness') %}
{% set b = (b/255*100)|round(0, default=0) if b != none else b %}
{{ repeat.index <= n and
( (sign == 1 and (b == none or b < end_pct)) or
(sign == -1 and (b != none or b|int(0) > end_pct)) ) and
(b | round(0, default=0) - (start_pct + ((repeat.index - 1) * sign))) | abs < 3 }}
sequence:
- delay:
milliseconds: "{{ delay_msec }}"
- service: light.turn_on
target:
entity_id: "{{ light }}"
data:
brightness_pct: "{{ ([0, (start_pct + (repeat.index * sign)), 100]|sort)[1] }}"
What kind of lighting do you have that its brightness doesn’t match what it was set to?
My guess is whatever lighting integration you’re using experiences communication problems (‘flooding’) when the interval between successive commands is very short.
I’m trying to do the same thing as everyone else here but over long transition times (15m). I’m using the transition as a morning “sunrise” alarm. After running cleanly for 30 seconds or so, the script fails with:
Stopped because an error was encountered at 5 January 2023 at 07:00:37 (runtime: 36.88 seconds)
string indices must be integers
The script is called from an automation that is currently set to trigger at 7am weekdays. I’m passing in target brightness of 100% and a transition time of 900s, along with my light entity.
Where do I put this code? In my script.yaml with your inintal code or do I add this somewhere else. It is just I do not know where to put the fade out light code. I do not see where you say to put it.
We don’t know how your config is structured and whether you do everything in YAML or also via the UI. If you look at the script documentation, it’s telling you it must go under a singular script: key in your configuration.yaml, but if your config is split it could be different (packages can repeat keys). Do you have a script.yaml? Then it would be a yes (most likely).
Hey all, I modified ReanuKeeves01 script to fade lights slightly differently.
I found the script ReanuKeeves01 posted worked great except it only ever faded the light in ticks of 1%. This meant to go from 100% to 10%, you needed 90 ticks. Ticks are limited to a minimum of 100ms. Assuming this limit is a common smart light hardware limitation; mine didn’t change faster when I went below 100ms.
This all meant to go from 100% to 10%, the minimum transition time you need to put is 9 seconds. The fastest rate is 10% every 1 second.
I’ve updated the script to now always fade in the time you set on the transition. It does this by calculating the perfect brightness % tick and delay MS.
For example if you want to go from 100% to 10% in 4 seconds, it calculates that it needs to tick 3% every 133ms, and it fades at that rate without issue.
tl;dr This script works very well when you want to fade large % amounts quickly.
The updated script:
alias: Light Fader
description: Fades lights to a desired level over a specified transition period.
fields:
light:
name: Light
description: Entity_id of light.
selector:
entity:
domain: light
example: light.kitchen
end_pct:
name: End brightness Level
description: >-
Integer value from 0 to 100 representing the desired final brightness
level.
selector:
number:
min: 0
max: 100
step: 1
mode: slider
default: 50
example: "50"
transition:
name: Transition Time
description: >-
Transition time for fading in seconds. Tick rate of brightness % is
dependant on this and total brightness % traversed.
selector:
number:
min: 0
max: 1800
step: 1
mode: slider
default: 10
example: "10"
mode: parallel
sequence:
- variables:
start_pct: >-
{{ ((state_attr(light, 'brightness') | int(0))/255*100) | round(0,
default=0) }}
end_pct: "{{ end_pct | int(0) | round(0, default=0) }}"
delay_msec: >-
{% set min = (((transition / (end_pct - start_pct)) | abs) |
round(3,default=0) * 1000) %} {{ (min * ((100 / min) | round(0,
"ceil"))) | int(0) }}
tick_pct: >-
{{ ([1, (((start_pct - end_pct)|abs) / (transition * (1000 /
delay_msec)))|round]|sort)[1] }}
sign: "{{ tick_pct if start_pct < end_pct else tick_pct * -1 }}"
"n": "{{ transition * (1000 / delay_msec) | int(0) }}"
- repeat:
while:
- condition: template
value_template: >-
{% set b = state_attr(light, 'brightness') %} {% set b =
(b/255*100)|round(0, default=0) if b != none else b %} {{
repeat.index <= n and ( (sign >= 1 and (b == none or b < end_pct))
or (sign <= -1 and (b != none or b|int(0) > end_pct)) ) and (b |
round(0, default=0) - (start_pct + ((repeat.index - 1) * sign))) |
abs < 3 }}
sequence:
- delay:
milliseconds: "{{ delay_msec }}"
- service: light.turn_on
target:
entity_id: "{{ light }}"
data:
brightness_pct: "{{ ([0, (start_pct + (repeat.index * sign)), 100]|sort)[1] }}"
max: 10
@TimeBomb First off, I just want to say that I so appreciate all the effort that you put into this script!
And while I don’t at all want to take away from that, I think that I may have come across a potential bug that—just based on my Spidey-sense alone—miiiight potentially stem from a rounding issue somewhere?
Known-good scenario:
If I have a light that starts at 0% (off), and I set it to fade to 20% over a span of 10 seconds, that works as expected.
On the other hand, if I have a light that starts at 0% (off), and I set it to fade to 20% over a span of 11 seconds, then that doesn’t quite seem to work?
In this scenario, the lamp fades from 0% to 11%—and then it stops.