Running Timer Blueprint/Script

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?

Current code:

alias: Countdown Timer Test
sequence:
  - device_id: 657f7d66421ee47a1c4e1a438be101ab
    domain: select
    entity_id: c081f43ba5e9e7f9aec80fa985608a59
    type: select_option
    option: Countdown On
  - repeat:
      sequence:
        - action: number.set_value
          metadata: {}
          data:
            value: "{{repeat.index * 5}}"
          target:
            entity_id: number.study_strip_segment_1_intensity
        - delay:
            hours: 0
            minutes: 0
            seconds: 0.5
            milliseconds: 0
      count: 20
  - device_id: 657f7d66421ee47a1c4e1a438be101ab
    domain: select
    entity_id: c081f43ba5e9e7f9aec80fa985608a59
    type: select_option
    option: Count Complete
  - delay:
      hours: 0
      minutes: 0
      seconds: 3
      milliseconds: 0
  - device_id: 657f7d66421ee47a1c4e1a438be101ab
    domain: select
    entity_id: c081f43ba5e9e7f9aec80fa985608a59
    type: select_option
    option: Countdown Off
description: |+

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:

- variables:
    start: "{{ now().timestamp() }}"
    duration: 10
- repeat:
    sequence:
      - action: number.set_value
        data:
          value: "{{ 100 * (now().timestamp() - start) / duration }}"
        target:
          entity_id: number.study_strip_segment_1_intensity
    until: "{{ now().timestamp() >= start + duration }}"

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.

Sorry, newbie question, but I’m getting stuck with the until. If I do:

  - repeat:
      sequence: []
      until: "{{ now().timestamp() >= start + duration) }}"

I get: Message malformed: Expected a dictionary @ data[‘sequence’][3][‘repeat’][‘until’][0]

Ah, my bad - there’s a missing bracket that I hadn’t seen…

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 :smiley:

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.

Can you point to an example of how to do that? It sounds great, and would be useful in other things I’m doing.

Just write out a literal dict/list like you would in Python or JS, then pass it off to the to_json filter:

{{ {'num': 0, 'desc': 'test'} | to_json }}'

Ah gotcha! I thought maybe there was a way to build a dictionary once, and then just update particular keys on it in the loop.

Thanks!

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.

If you’re interested, this is a simplified demonstration of the technique described by Mayhem_SWE.

I have defined the payload as a dictionary in the cmd variable. The value of the payload’s ix key is incremented each time the repeat iterates.

  mqtt_var_test:
    alias: MQTT Variable Test
    sequence:
      - variables:
          start: "{{ now().timestamp() }}"
          duration: 10
          seg_id: 1
          device_name: study
          cmd:
            seg:
              - id: "{{seg_id}}"
                fx: 98
                ix: 0
      - repeat:
          count: 4
          sequence:
            - variables:
                ix_val: "{{ (100 * (now().timestamp() - start) / duration)|int }}"
            - action: mqtt.publish
              data:
                topic: "wled/{{device_name}}/api"
                payload: "{{ dict(seg=[dict(cmd.seg[0], ix=ix_val)]) | to_json }}"
            - delay:
                seconds: 2

The following template is responsible for modifying the value of the ix key, taken from the cmd variable, and using it to build a new dictionary.

{{ dict(seg=[dict(cmd.seg[0], ix=ix_val)]) | to_json }}

Here’s a screenshot showing the value of ix increasing from 0 to 60 in 4 steps.