How can i make automations with transition while the device doesnt support it?

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.

Did I miss one of the comments where this was addressed?

Edit: Also tried running this from the developer tools via yaml instead of ui, just turns the light on only. Nothing else.

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 this part here:

(b | round(0, default=0) == start_pct + ((repeat.index - 1) * sign))

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.

1 Like

Cool, thanks! But that also implies I’ll need these two lines, where b gets defined, right?

{% set b = state_attr(light, 'brightness') %}
{% set b = (b/255*100)|round(0, default=0) if b != none else b %}

Yes; I was merely pointing out the part responsible for what I mentioned earlier:

Again, thanks for your input dude, appreciate you taking the time answering!

Simply adding in those bits doesn’t seem to work for me. Once i replace:

 - repeat:
     while:
       - condition: template
         value_template: >-
           {{  repeat.index <= n }}

with:

- 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)) }}

Could it be simply an issue with my indentation?

Looked some deeper into the traces of this script… and it this line specifically that makes it returns false after the first iteration:

    (b | round(0, default=0) == start_pct + ((repeat.index - 1) * sign)) }}

Ok I’ve managed to get it working by adding more tolerance with this adjustment:

(b | round(0, default=0) - (start_pct + ((repeat.index - 1) * sign))) | abs < 3 }}

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] }}"
1 Like

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.

Anyone able to help me troubleshoot this?

The script I’m using is:

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: "{{  repeat.index <= n }}"
      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] }}"
alias: ""
max: 10

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
1 Like

@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.

service: script.light_fader
data:
  light: light.living_room_lamps
  end_pct: 20
  transition: 10

Weirdness scenario:

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.

service: script.light_fader
data:
  light: light.living_room_lamps
  end_pct: 20
  transition: 11

Would you happen to have any ideas there?

Anyone having light fader script issues now?

I updated HA after going a couple months and now my used-to-work scripts are going nuts with this error:

Error executing script. Error rendering template for call_service at pos 1: ZeroDivisionError: division by zero

Testing the once working values in developer tools ends up giving me this:

fade_light_v2: Error executing script. Error rendering template for variables at pos 1: ZeroDivisionError: division by zero

I went back and took anything that was divisible by 10 and changed it to all odd numbers. Some values work, some do not.

One example that is failing now:

  - service: script.fade_light_v2
    data:
      light: light.hallway_main_lights
      end_pct: 9
      transition: 120

The script I’ve been running on is:

    - 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] }}"    

Perplexed. Happy to send beer or two to someone who solves.