I’d like to run a countdown timer that controls a WLED strip. I’ve got the general outline working, but it’s using a constant delay and increment, which is ugly - I’d rather set a duration, update at a sensible rate and check what proportion of the duration has passed. Can anyone point at a good example of doing that?
I don’t quite understand what you mean. Unless your “sensible rate” actually means “as fast as possible”, I don’t see how you can get around using a delay either way. If you do want it to update as fast as possible, you could probably do something like this:
Thanks! Yes ‘sensible rate’ wasn’t clear - I was more thinking in the other direction, that updating every few seconds was probably plenty. The template you’ve given is exactly what I was hoping for, though - it does the math independent of the update rate or counters.
Ahh, sorry about that, that last closing parenthesis shouldn’t have been there. Was probably a remnant from when I first used datetime and timedelta objects, before I decided it was easier to switch to timestamps throughout. Always some small errors/typos that make their way in when writing code that is never tested
Here’s my finished script - makes a very nice countdown timer with WLED devices:
alias: MQTT Timed Countdown Test
sequence:
- variables:
start: "{{ now().timestamp() }}"
duration: 10 # countdown in seconds
update_interval: 200 # milliseconds
seg_id: 1 # experimental - seems to crash if not 1
finish_time: 2000 # how long to show finish FX for (ms)
finish_fx: 87 # ID of finish FX
device_name: study # ID of the strip - the channel is /wled/{{strip_name}}
- action: mqtt.publish # Freeze the bottom segment, set the start/end of the countdown strip and turn it on with the right FX
data:
topic: "wled/{{device_name}}/api"
payload: "{\"seg\":[{\"id\":0,\"frz\":true},{\"id\":{{seg_id}},\"frz\":false,\"on\":true,\"fx\":98,\"ix\":30,\"start\":60,\"stop\":120}]}"
- repeat:
until: "{{ now().timestamp() >= (start + duration) }}"
sequence:
- action: mqtt.publish # Update the IX value according to the time
data:
topic: "wled/{{device_name}}/api"
payload: "{\"seg\":[{\"id\":{{seg_id}},\"fx\":98,\"ix\":{{ (100 * (now().timestamp() - start) / duration)|int }}}]}"
- delay:
milliseconds: "{{update_interval}}"
- action: mqtt.publish # Set the segment to display the finish FX
data:
topic: "wled/{{device_name}}/api"
payload: "{\"seg\":[{\"id\":{{seg_id}},\"fx\":{{finish_fx}}}]}"
- delay: # Display finish animation for a bit
milliseconds: "{{finish_time}}"
- action: mqtt.publish # Reset the segment (works better if we do this before turning it off)
data:
topic: "wled/{{device_name}}/api"
payload: "{\"seg\":[{\"id\":{{seg_id}},\"fx\":98,\"ix\":0}]}"
- action: mqtt.publish # Turn off segment and restart bottom segment
data:
topic: "wled/{{device_name}}/api"
payload: "{\"seg\":[{\"id\":0,\"frz\":false},{\"id\":{{seg_id}},\"on\":false,\"start\":0,\"stop\":0}]}"
description: Countdown timer for a given number of seconds, using MQTT to talk to a WLED strip
Neat. For your MQTT payloads, you may in the future just want to build up a dict/mapping and pass to the to_json filter. Much less chance of typos than if manually escaping quotes etc.
Unfortunately not. In Jinja, you cannot modify an existing dict. You would have to disassemble it into a paired list with items, operate on that, then convert it back to a dict().
But that does not work in a loop due to the annoying variable scope limits in Jinja. The only variable you can modify inside a loop and have persist on the outside is a namespace.