Light Fader by Transition Time

Taking inspiration from this post by Finny Samuel (Light Fader - Quick and Dirty), I wanted to create a script that would operate on the concept of specifying total transition time, and then have the script calculate the number and size of the incremental brightness changes required to arrive at the target brightness in the specified number of seconds.

The script requires entity_id, start_level_pct ( optional - assumes current brightness if omitted), end_level_pct, and transition. It then determines the sleep_delay and step_pct and iterates through a loop that looks very much like the one posted by Finny :grinning:!

The script will do either a fade in or a fade out (i.e. negative step_pct).

This Python script requires the Python Script component.

Hope some find this useful.

NOTES:

  • First time ever posting code … feedback appreciated

  • I found my light system will only permit values to be set as even %'s (values in increments of 2.55, rounded down). That is why I had to work with brightness_pct.

  • This code works with a Lutron Hub (basic consumer version, not pro).

Light Fader

## INPUTS
entity_id  = data.get('entity_id')
states = hass.states.get(entity_id)
current_level = states.attributes.get('brightness') or 0
current_level_pct = current_level/2.55 or 0     # start_level_pct is optional
start_level_pct = int(data.get('start_level_pct', current_level_pct ))
end_level_pct = int(data.get('end_level_pct'))
transition = data.get('transition')

## CALCULATE PARAMETERS FOR LOOP, BASED ON TRANSITION TIME FOR FADE
transition_secs = (int(transition[:2])*3600 + int(transition[3:5])*60
                   + int(transition[-2:]))    # convert string to total secs'
step_pct  = 1
sleep_delay = abs(transition_secs/(end_level_pct - start_level_pct))

# If fading out the step_pct will be negative (decrement each iteration)

if end_level_pct < start_level_pct: step_pct = step_pct * -1


## DOES THE WORK ...

# Since we check for equality of new_level_pct and current_level_pct
# in each loop -  and break if !=, we must initialize new_level_pct
# to equal start_level_pct, and then set actual light brightness_pct
# (a.k.a. current_level_pct) to equal start_level_pct.

new_level_pct = start_level_pct
data = { "entity_id" : entity_id, "brightness_pct" : round(start_level_pct) }
hass.services.call('light', 'turn_on', data)
time.sleep(1)  # without delay,'hass.states.get' would not get the new value

while round(new_level_pct) != end_level_pct :     ## until we get to new level
    states = hass.states.get(entity_id)           ##  acquire status of entity
    current_level = states.attributes.get('brightness') or 0
    current_level_pct = current_level/2.55 or 0
    if (round(current_level_pct) != round(new_level_pct)):
        logger.info('Exiting Fade In')                ## this indicates manual
        break;                                        ## intervention, so exit
    else :
        new_level_pct = new_level_pct + step_pct
        logger.info('Setting brightness of ' + str(entity_id) + ' from '
          + str(current_level_pct) + ' to ' + str(new_level_pct))
        data = { "entity_id" : entity_id,
                "brightness_pct" : round(new_level_pct) }
        hass.services.call('light', 'turn_on', data)
        time.sleep(sleep_delay)


##  Some test json input for Services in Developer tools
##{
##  "entity_id": "light.your_light_name",
##  "start_level_pct": "0",
##  "end_level_pct": "100",
##  "transition": "00:00:19"
##}
2 Likes

Thank you for this! Works great for my Lutron switches. Didn’t work at all with my rgb strips. Is there any downside to changing step levels more than once per second? The transitions can be really jarring when trying to go from 0 to 100 in 10 seconds.

Glad you found it useful.

I kind of arbitrarily decided that no more than once per second was desirable, but I totally get the lack of “smooth” with the once per second change over a short transition time. I’m not experienced enough with Python or Raspberry Pi computing to know for sure, but I sort of guessed that making, say, 10 service calls per second (as it would be in your 0 to 100 in 10 seconds scenario) might not work out so well. It would make sense that there is probably some practical limit to how small that sleep_delay can be, but I’m honestly not sure what that might be. So short replied made long, that was just a totally untested guess on my part.

I’ve mostly been using this for fades over 5 minutes, where it is really seamless, but if you do some experimenting I’d be curious to hear how it works out. If you comment out the “if” line, and the entire “else” section where the script calculates these parameters you would always get …

    step_pct  = 1
    sleep_delay = abs(transition_secs/(end_level_pct - start_level_pct))

… and then it would try to do a 1% brightness change in whatever fraction of a second sleep_delay turns out to be. If you or someone could determine what that threshold actually should be, it would be easy enough to modify that “if” to be a smaller increment than one second.

Side note: home assistant complains about using “sleep” in python scripts, indicating it might slow home assistant down, but - again not scientific - I haven’t noticed any issue so far.

Nothing seemed to happen if I commented out the “if” line and the “else” section. I messed around with the sleep delay and it works smoothly around 0.1-0.2 but screws up the time for the transition. Setting the transition time higher works as a bandaid to the solution. It’d be great if it could dynamically adjust the service call rate depending on the length of the transition.

Another option if you’re using node red: https://github.com/cflurin/node-red-contrib-dsm/wiki/Fade-in--out

Been busy and just now had time to do the testing with the small change intervals for myself. I had really been more interested in the long duration, gradual, fades, so the short fades didn’t really interest me. It actually seems to work just fine if you call the service more than once per second (incorrect assumption on my part!). Smooths it right out. I removed the logic that limits the brightness changes to no more than once per second in my original post above.

What I tried to suggest above, but maybe not very clearly, was to simply change this …

if transition_secs >= abs((end_level_pct - start_level_pct)):
    # This is a case where we change brightness 1% every 'sleep_delay' seconds
    step_pct  = 1
    sleep_delay = abs(transition_secs/(end_level_pct - start_level_pct))
else:
    # In this case we change 'step_pct' % every 1 seconds
    step_pct = abs(((end_level_pct - start_level_pct)/
               transition_secs))
    sleep_delay = 1

… to this:

step_pct  = 1
sleep_delay = abs(transition_secs/(end_level_pct - start_level_pct))

When I did that, and tried 0 to 100 in 10 seconds, it looked pretty good using a bulb with smooth transitions.

I couldn’t get your newer version to work. When I tried to transition from 0-100, it would light up at 1% but just stays there.