Ashley’s Light Fader 2.0✨: fade lights and/or color temperature with your choice of easing (curves), including ease-in, ease-out, and ease-in-out

I have a handful of lights that don’t support transitions natively, and so I wrote a script for that.

:sparkles: Main features:

  • If you fade a lamp linearly between two brightness values, that can look unnatural to the human eye. So this light fader lets you choose between any of these 12 easing curves—and there’s also an option to have the script do its best to automatically select one of these easing curves for you.

  • (:star2: new for 2.0) You can now have the script fade between color temperatures too. And you can either simultaneously fade a lamp’s brightness and color temperature, or just fade its brightness, or just fade its color temperature.

    • You can (optionally) specify an end-color-temperature entity to have the script use that entity’s value for the fade’s end color temperature.
    • Some quick caveats: The script only supports color temperatures in Kelvin. And if you’d like to fade to a new color temperature, the lamp’s current color temperature also has to be defined in Kelvin units.
    • [PS: Shout-outs to @damru, @kvikindi, and @ThatBlockyPenguin for requesting this feature!]
  • (:star2: new for 2.0) If you want, you can have the script automatically cancel its fade if you turn off the light during the fade sequence.

  • If you want, you can have the script automatically cancel its fade if the light’s brightness value is manually changed during the fade sequence.

  • If you want, you can also have the script ingest an entity’s value to use for the end-brightness value.

:gear: How to install this:

tl;dr: Copy the code from this fader’s gist, and paste that into a new/blank script.

not-too-long/I’d-like-to-read:

  1. Open the gist for this fader, open the gist in raw mode, and then copy that code to your clipboard.
  2. Then within Home Assistant, go to SettingsAutomations & Scenes.
  3. Select Scripts from the tabs across the top.
  4. Click the Add Script button.
  5. Once the blank script loads, open the “…” menu and choose Edit in YAML.
  6. Select all the boilerplate text within the script, and delete that. Then paste in the code from your clipboard.
  7. Lastly, choose Save Script.
  • (The write-up below the code goes over how you can use this script.)

:bulb: How to put this script to use:

In any automation, choose Add ActionCall Service. Then if you start typing “Ashley”, this script should show up as the top item.

:helicopter: This script’s fields and options:

  • Light

  • The lamp’s internal brightness scale

    • If you happen to match this setting to the lamp’s internal brightness scale (0-to-255 or 0%-to-100%), the fade may be smoother. (If you’re not sure, you can just leave this as is.).
  • Fade time

  • Easing type

    • You can choose from 12 different easing types—or you can have the script try to automatically select an easing type. You can also see visualizers for each of these at Easings.net.
      • You can’t go wrong with any of the Ease-In-Out X easings as those will always look good whenever you’re fading between two nonzero brightness values.
      • The Ease-Out X easings often tend to look good if you’re fading up very quickly from zero.
      • The Ease-In X easings are mostly only included for completeness—those only tend to look good if you’re quickly fading to zero.
  • (optional) End brightness level

  • (optional) Use an entity instead for the end-brightness value?

    • You can optionally have the script instead use the value from an input-number helper, an input-select helper, an input-text helper, or a numeric sensor.
  • The end-brightness entity’s brightness scale (if used)

    • If you enable the previous option, select whether that entity represents brightness with a 0%-to-100% or 0-to-255 scale.
  • (:star2: new) (optional) End color temperature (in Kelvin)

  • (:star2: new) (optional) Use an entity instead for the end-color-temperature value?

    • You can optionally have the script instead use the value from an input-number helper, an input-select helper, an input-text helper, or a numeric sensor.
  • (optional) Brightness-change threshold that auto-cancels the fade

    • I don’t recommend setting this to anything less than 3 since not all lamps instantly reflect new brightness values. In my home, I personally use a value of around 10 most of the time.
  • (:star2: new) Cancel the fade if the lamp is turned off during the fade?

    • (Don’t worry—this feature is smart enough to not automatically cancel the fade at points when the lamp’s intended brightness might be zero, such as if the script were to be fading down to zero or fading up from zero.)
  • (optional) Stop if a certain entity is turned on during the fade?

    • You can optionally have the script keep an eye on an input boolean or a binary sensor. And if that entity is then turned on during the fade, the script will automatically stop. So for example, if you create a “Stop Everything” input boolean, and if you set that entity at this point, you can stop your fade at any time by turning on that “Stop Everything” entity.
  • (optional) Reset that “stop” entity to off just before starting the fade? (if used)

    • If you make use of the stop entity (above), you can also optionally have the script automatically reset that entity to “off” at the start of the fade. (By default, the script won’t change the value of the stop entity.)
  • (:star2: new) (optional) Stop only if the “stop” entity is instead turned OFF? (if used)

    • If you make use of the stop entity (above), you can optionally have the script automatically stop only if that entity is turned off during the fade.
  • (:star2: new) [experimental] If available, use the lamp’s native transitions too?

    • If the lamp natively supports transitions, you can have the script make use the lamp’s native transition effect when moving between brightness steps or color steps. When enabled, this has the possibility of offering even smoother fades if those brightness steps or color steps are very gradually spaced. (And while I use this feature in my own house, this feature isn’t as thoroughly tested as the script’s other features, so this feature is considered experimental for now.)
  • Minimum delay per step

    • If your lamp isn’t fading evenly—and especially if your lamp might appear to be pumping or ungracefully stair-stepping as it goes through the fade—you might try bumping this up a bit, such as to 150 ms or 200 ms (or possibly even a little higher).
  • Enable debugging mode?

    • If enabled, the script will output status messages to your Home Assistant log along the way.

:fist:t2: Other bits:

  • If you have any questions or comments, feel free to tweet me at @FriendlyAshley or share any comments here.

  • This script is released under the Apache 2.0 license (same as Home Assistant).

:writing_hand:t2: Changelog:

  • 2.0 (2023-11-13) — Added a bunch of new features:
    • You can now also fade the lamp’s color temperature either at the same time as (or separately from) fading the lamp’s brightness. (:wave:t2: @damru, @kvikindi & @ThatBlockyPenguin)
    • You can (optionally) have the script automatically cancel its fade if you turn off the lamp during the fade sequence.
    • If you make use of the stop entity, you can (optionally) have the script stop only when the stop entity is turned off (instead of the usual behavior where the script would stop only when the stop entity is turned on).
    • If you set the light to fade to 1%, and if the lamp’s internal brightness scale is 0-to-255, the script will infer that you’d like for the lamp to fade down to its lowest level, and so the script will automagically fade the light to 1/255 brightness. (:wave:t2: @ronnieSVK)
    • As an experimental feature, you can (optionally) have the script try to make use of the lamp’s native transition effect too, for potentially even smoother fades.
  • 1.1 (2023-07-12) — Added an optional field for a stop entity, in which if that entity is set to “on,” the fader will automatically stop. Also added a few emoji to the GUI for readability. (:wave:t2: @damru)
  • 1.03 (2023-06-29) — more graceful handling if starting brightness = ending brightness
  • 1.02 (2023-06-21) — fixed a silly copy/paste bug
  • 1.01 (2023-06-21) — improved input checking
  • 1.0 (2023-06-20) — initial release
63 Likes

This is just awesome - very user-friendly! Thanks!

3 Likes

Thanks for the kind words, @b26x!

2 Likes

Good morning. Awesome script. Converted everything to it.

I noticed these errors in my logs.

First:

Ashley’s Light Fader: Error executing script. Error rendering template for variables at pos 3: ZeroDivisionError: float division by zero

Followed by:

  • Kitchen lights on with lots of cloud cover already on adjustment: Parallel action at step 1: parallel 1: Error executing script. Error rendering template for call_service at pos 1: ZeroDivisionError: float division by zero
  • Kitchen lights on with lots of cloud cover already on adjustment: Parallel action at step 1: parallel 2: Error executing script. Error rendering template for call_service at pos 1: ZeroDivisionError: float division by zero
  • Kitchen lights on with lots of cloud cover already on adjustment: Parallel action at step 1: parallel 3: Error executing script. Error rendering template for call_service at pos 1: ZeroDivisionError: float division by zero
  • Kitchen lights on with lots of cloud cover already on adjustment: Error executing script. Error rendering template for parallel at pos 1: ZeroDivisionError: float division by zero

Ending with:

Error while executing automation automation.kitchen_lights_on_with_lots_of_cloud_cover_already_on_adjustment: ZeroDivisionError: float division by zero

The automation is:

- id: '331899958999003'
  alias: Kitchen lights on with lots of cloud cover already on adjustment
  trigger:
  - platform: numeric_state
    entity_id: sensor.lightlevel_kitchen
    above: 0
    below: 467
    for:
      hours: 0
      minutes: 3
      seconds: 0
      milliseconds: 0
  condition:
  - condition: and
    conditions:
    - condition: sun
      before: sunset
      before_offset: -01:15:01
    - condition: time
      after: 09:10
    - condition: state
      entity_id: light.kitchen_under_cabinet
      state: 'on'
    - condition: state
      entity_id: cover.velux_left
      state: open
    - condition: state
      entity_id: cover.velux_right_2
      state: open
  action:
  - parallel:
    - service: script.ashley_fade_light
      data:
        lampBrightnessScale: zeroToOneHundred
        easingTypeInput: auto
        endBrightnessPercent: 50
        autoCancelThreshold: 10
        endBrightnessEntityScale: zeroToOneHundred
        minimumStepDelayInMilliseconds: 100
        isDebugMode: false
        light: light.kitchen_main_lights
        transitionTime:
          hours: 0
          minutes: 1
          seconds: 30
    - service: script.ashley_fade_light
      data:
        lampBrightnessScale: zeroToOneHundred
        easingTypeInput: auto
        endBrightnessPercent: 70
        autoCancelThreshold: 10
        endBrightnessEntityScale: zeroToOneHundred
        minimumStepDelayInMilliseconds: 100
        isDebugMode: false
        light: light.kitchen_under_cabinet
        transitionTime:
          hours: 0
          minutes: 1
          seconds: 30
    - service: script.ashley_fade_light
      data:
        lampBrightnessScale: zeroToOneHundred
        easingTypeInput: auto
        endBrightnessPercent: 50
        autoCancelThreshold: 10
        endBrightnessEntityScale: zeroToOneHundred
        minimumStepDelayInMilliseconds: 100
        isDebugMode: false
        light: light.kitchen_sink_light
        transitionTime:
          hours: 0
          minutes: 1
          seconds: 0

I’m wondering if the issue here is the same discussed with @handcoding and @123 on my thread about light fader v2 going south for me after recent HA updates. The thought there was that if start and ending percentages are equal then it will return a zero which can’t be divided for a transition.

I noticed other automations using the same layout, different values, worked just fine after conversion.

EDIT: Traces shows that it successfully ran this at 7:10:29PM. Conditions changed and it triggered again at 7:20:03PM and 7:23:31 but erred with this in Traces:

Stopped because an error was encountered at June 27, 2023 at 7:20:03 PM (runtime: 0.22 seconds)

ZeroDivisionError: float division by zero

The sensor shows a spike which would have been too short to trigger a different adjustment automation for being brighter outside and the drop would’ve retriggered this.

Edit #2: Happened again today at a different time in the afternoon, and seems like the lights were at the brightness level already from a previous automation run. Figuring it’s a short time where the lux sensor gets more light and then suddenly less but not long enough to cause another automation to adjust lighting. So, it falls back and triggers this one again which ends up being a zero division.

I’m wondering about using a NOT condition as a quick fix unless or until a if else could be added for when condition is already equal.

  condition:
  - condition: and
    conditions:
    - condition: sun
      before: sunset
      before_offset: -01:15:01
    - condition: time
      after: 09:10
    - condition: state
      entity_id: light.kitchen_under_cabinet
      state: 'on'
    - condition: state
      entity_id: cover.velux_left
      state: open
    - condition: state
      entity_id: cover.velux_right_2
      state: open
    - condition:
        not:
          - condition: numeric_state
            entity_id: light.kitchen_main_lights
            attribute: brightness
            below: '50'
            above: '50'

Never tried a NOT condition. Any thoughts?

Hi, @derekcentrico! Just to double-check, are you using the latest version of the script (which is v1.02 as of when I’m writing this)?

If you might not be sure which version you have, here’s how you can check—

  1. First, open any of your automations where the the script is in use, and then scroll down to one of the instances where you’re calling the script.
  2. From there, if you check the script’s description text—which is near the top of the script’s action box—it should hopefully say “v1.02”, such as like this:

(Or on the other hand, if perhaps you already have the latest version, I’ll be quite happy to do what I can to help debug this further.)

Sure, I’m using (v1.02).

I suppose the NOT condition is wrong, rather two ORs would be better to resolve this vs. worrying about an if then else check of the template to be sure start/end aren’t the same value to end up being zero.

    - condition: or
        conditions:
          - condition: numeric_state
            entity_id: light.kitchen_main_lights
            attribute: brightness
            above: '50'
          - condition: numeric_state
            entity_id: light.kitchen_main_lights
            attribute: brightness
            below: '50'

@derekcentrico I believe that you’re correct that this issue can come about if a given light’s starting brightness and ending brightness are the same.

And while that may be an edge case, that’s nonetheless an edge case that my script should be able to gracefully handle. I’ll write an update to the script to handle that edge case, and I’ll see if I can release that update later today.

(And thank you for bringing this to my attention. Bug reports—while perhaps not as exciting as other types of feedback—are very much a valuable part of the development process.)

1 Like

@derekcentrico By the way, I’ve just posted v1.03, which includes a fix for the edge case in which a lamp’s starting brightness were to be the same as its ending brightness.

And if you might run into any other weirdness, feel free to jot a comment here and I’ll be happy to chime in!

1 Like

Cool beans. As I said super awesome script. I appreciate your willingness to tweak it for my odd scenario. I updated to 1.03 and reverted out my condition workaround. Thanks again!!!

1 Like

It’s my pleasure, @derekcentrico—happy to do it!

That’s a really nice job here! Thanks @handcoding !
I’d have one request though.
In my case, I’m heavily relying on circadian lighting via Adaptive Lighting integration (ALI for short). Doing so, I’m facing a case where when turning a light on (from off state), I must disable the ALI before running the script. So far so good, but the thing is that the color temperature is completely off depending on when/how the light was turned off. So the light turns on, goes to target brightness, then I re-enable the ALI and the color only changes from there, which usually leads to eyes burning as it either changes from high to low temperature in the evening (eyes burning during dimming), or from low to high in the morning (eyes burning when temperature changes).
So, would it be possible to add an option to specify a color temperature ?
Thanks again for this awesome script :+1:

2 Likes

Hi, @damru—I stoked to hear that you’re digging the script!

I don’t happen to use Adaptive Lighting myself, but out of curiosity, when you’re about to do a fade, might it be possible to make use of Adaptive Lighting’s switch.adaptive_lighting_adapt_brightness_living_room switch to temporarily disable its brightness-adaptation feature (while leaving its color-adaptation feature enabled)?

1 Like

Ah, yes, good catch! :eyes: I completely missed it, thank you :pray:

And yes I’m digging it because it needs some love. The transition effects are really nice. This is a really handy script right and typically the type of things that could be part of the core if you ask me.

The only thing I’d miss is some kind of manual input_boolean which could be used to interrupt the script (instead of the % delta). I had that in a previous custom script, and it was really helpful when executing the script multiple times quickly on the same light. I used it to interrupt any running execution on the same light (I used to have 1 input_boolean per light managed by the script) before starting.
Basically:

if on, then set to off and wait for 'loop delay' (this way the other instance can stop)
set to on
while on, execute 

Anyways, thank you again for the help and the script.

1 Like

@damru While I’m not completely opposed to potentially adding that sort of feature, I think I may have a potential approach that could let you do that sort of thing now?

  • This involves using Trigger IDs, which are basically a technique that let you have multiple automation paths inside a single Home Assistant automation.

  • So for the sake of example, let’s suppose that you had a trigger ID for “bedroom lights turn on” that—when triggered—kick off a fade of the living room lights to 100%.

  • And then let’s suppose that you had a trigger ID for “dining room lights turn on” that—when triggered—kick off a fade of the living room lights to 10%.

  • And let’s suppose that you place both of those paths into one automation using trigger IDs, and you then set that automation’s mode to “restart” (which you can do through the automation’s “…” → “Change Mode” menu).

  • From there, if you turn on the bedroom lights, that will trigger the automation’s first path. And then—while that fade is still running—if you turn on the dining room lights, that should interrupt that first automation instance and then immediately begin running the automation’s second path.

I know that this might be a lot to take in, but I’d be quite happy to elaborate on any of this if it might help.

(And with all this being said, I’m still not necessarily opposed to perhaps adding that sort of feature. I just figured that I’d share this potential approach in case it might be able to take care of your use case even with the current state of the script.)

1 Like

The use of TriggerID is ok in most cases.
And I know this is really edgy, by here is the issue that I ran into:

:spiral_notepad: Prerequisites

  • The script must be able to run in parallel as you may trigger it from multiple automations at the same time, for different lights
  • Multiple automations targeting the same light. ex: 1 motion automation, 1 switch automation, 1 time-of-day automation. While each can be used with Restart mode to stop a run and start a new one, they also can be triggered at the same time
  • Each of these automations also do other things, so you cannot merge them into 1 do-it-all automation and split the sequences depending on Choose > TriggerID, or this will quickly become unmaintainable (for me at least :sweat_smile:)

:arrow_right_hook: Scenario

  • motion sensor is triggered : light is fading really slow from 0% to 100%
  • for any reason, I want to switch the light off while the “motion-fading” is still running
  • the result is the light going up (motion automation is still running) and down (switch automation is also running now) and never going neither to 100% or off as the 2 parallel script executions will end their respective “loops” at some point (worst case being that the script uses the target brightness as a stop condition to the loop, meaning it will never stop :infinity:)

:arrow_right: This is why I used the input_boolean as an optional script parameter, as this allowed me to prevent any parallel executions of the script for one specific light, while allowing parallel executions for different lights.

Still, there are probably other solutions out there that I didnt think of :eyes:

Ah—that all makes sense!

So I’ll see what I can do about adding support for that feature. It may be a couple of days, but it should be doable!

Oh, hi, @damru! By the way, I’ve added that feature in version 1.1 of the script (which is available at the top of this thread).

Thank you so much for this blueprint.

I’m trying to make a alarm clock and try to intergrade your script, but is is harder then I thought :wink:

Because I have a lot of things to take in account I can’t get it to work correctly.

Maybe you have some pointers for me :slight_smile:

alias: Wekker Met Fade In Sunrise En Muziek
description: ""
trigger:
  - platform: time
    at: input_datetime.wekker
condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: binary_sensor.bed_bezet_deur
        state: "on"
      - condition: state
        entity_id: binary_sensor.bed_bezet_kast
        state: "on"
    enabled: false
  - condition: or
    conditions:
      - condition: state
        entity_id: input_boolean.weekend_wekker
        state: "on"
      - condition: state
        entity_id: input_boolean.vakantie_wekker
        state: "on"
      - condition: state
        entity_id: binary_sensor.werkdagen
        state: "on"
    enabled: false
action:
  - service: switch.turn_off
    data: {}
    target:
      entity_id:
        - switch.adaptive_lighting_sleep_mode_slaapkamer_verlichting
        - switch.adaptive_lighting_adapt_brightness_slaapkamer_verlichting
  - service: media_player.turn_on
    data: {}
    target:
      entity_id: media_player.slaapkamer_apple_homepod_mini
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
  - service: media_player.volume_set
    data:
      volume_level: 0
    target:
      entity_id: media_player.slaapkamer_apple_homepod_mini
    enabled: true
  - parallel:
      - service: script.1688551121988
        data:
          lampBrightnessScale: zeroToOneHundred
          easingTypeInput: auto
          endBrightnessPercent: 40
          endBrightnessEntityScale: zeroToOneHundred
          minimumStepDelayInMilliseconds: 120
          isDebugMode: false
          light: light.slaapkamer_verlichting
          transitionTime:
            hours: 0
            minutes: 20
            seconds: 0
          stopEntity: binary_sensor.slaapkamer_bewegingsmelder
          shouldResetTheStopEntityToOffAtStart: true
      - service: script.wakker_met_muziek
        data:
          target_volume: 0.41
          curve: linear
          duration: 1140
          target_player: media_player.slaapkamer_apple_homepod_mini
        enabled: true
      - service: media_player.play_media
        target:
          entity_id: media_player.slaapkamer_apple_homepod_mini
        data:
          media_content_id: >-
            media-source://media_source/local/Muziek/Sounds Of Nature - Amazon
            Rainforest - Anton Hughes.mp3
          media_content_type: audio/mpeg
        metadata:
          title: Sounds Of Nature - Amazon Rainforest - Anton Hughes.mp3
          thumbnail: null
          media_class: music
          children_media_class: null
          navigateIds:
            - {}
            - media_content_type: app
              media_content_id: media-source://media_source
            - media_content_type: ""
              media_content_id: media-source://media_source/local/Muziek
        enabled: true
  - delay:
      hours: 0
      minutes: 20
      seconds: 0
      milliseconds: 0
  - service: switch.turn_on
    data: {}
    target:
      entity_id:
        - switch.adaptive_lighting_adapt_brightness_slaapkamer_verlichting
  - service: media_player.media_stop
    data: {}
    target:
      entity_id: media_player.slaapkamer_apple_homepod_mini
  - service: input_boolean.turn_on
    data: {}
    target:
      entity_id: input_boolean.ochtend_tts
  - device_id: 84455c9839744d35313600b1133395a6
    domain: cover
    entity_id: 74fe4c767d10da49163acd3b7b848078
    type: open
mode: queued
max: 10

1 Like

@edi023 I’m happy to help out if I can!

I have a few ideas—

  • Does it make any difference if you temporarily omit the stop entity?
  • Does the script work for you in a minimal test automation—one where you’re only calling the script and nothing else?
  • If you enable the script’s debug mode, is there anything of note in your Home Assistant log file?