Think I found a bug in timer.start. Can someone verify?

I think I found a bug in the timer.start service. I need somebody to verify this for me.

I have two windows open, one to the dev-states page, monitoring the state of a timer, and the other to the dev-services page ready to cal timer.start on that timer. Start with this in the data:

entity_id: timer.my_timer
duration: '00:30:00'

The timer should start just fine. Then call the same service again, just re-press the ‘Call Service’ button. Nothing happens for me at all.

Then it gets weird. change the duration of your call and press the ‘call service’ button a third time. Not only does the timer NOT get reset, but the time-remaining increases by the difference between the old and new durations. E.g., if I have 15 minutes left in a 30 minute timer, and then I call timer.start with a duration of 40 minutes, the duration changes to 40 minutes, but the time remaining doesn’t go to 40, but the existing time remaining increases by (40 - 30 = ) 10 minutes, so I end up with 25 minutes remaining.

This behaviour directly contradicts what is in the documentation:

Please tell me I’m not crazy.

A call for timer resets the value to original time

You on 0.102.1?

0.102.2 but what’s that got to do with the price of fish ?

Just seeing if you’re on the same version as I am.

I would have expected that internally it cancels the timer if it’s running and then just starts it again normally. This is screwing with my “turn the lights off if no motion detected” automation :slight_smile:

You’re right though…I upgraded to 0.102.2, and it’s no different for me. Same problem. I think I’m going to have to prefix all my timer.start calls with timer.cancel.

I ran tests using version 0.102.0 and 0.89.1 and the results were identical.

The timer I used was defined exactly as you specified:

entity_id: timer.my_timer
duration: '00:30:00'

Observations:

  • After the timer begins its countdown, subsequent calls to timer.start (without specifying a duration) have no effect. The timer is not reset and simply continues counting down.
  • Calling timer.start with duration set to 30 minutes, has no effect. The timer is not reset and simply continues counting down.
  • Calling timer.start with duration set to a higher amount, like 40 minutes, does have an effect. The elapsed time is retained but the new remaining time is increased by 40 - 30 = 10 minutes.
  • Calling timer.start with duration set to a lower amount, like 15 minutes, also has an effect. If the elapsed time is greater than the new duration (i.e. 16 minutes have already elapsed and now the new duration is 15 minutes), the UI ceases to show a countdown value. However, the States page reports the timer is still active. Perplexing, to say the least.

The documentation’s description doesn’t do justice to how the timer behaves:

When calling the start service on a timer that is already running, it resets the duration it will need to finish and restart the timer without triggering a canceled or finished event.

Ok. Phew! I’m glad I’m not crazy. Also curious, that no matter what the timer’s remaining time is, it always shows the duration value when I retrieve that attribute in the template editor.

I guess I’ll log this bug. In the meantime, always calling timer.cancel before timer.start seems to have the desired effect.
Wait, are you saying that this is the defined behaviour? I had always read this as: If you call timer.start again, it basically behaves as if you cancelled and restarted the timer

The behaviour it currently has seems pretty damn useless.

Don’t know if you’ve ever come across this custom component but it looks pretty interesting. Just started looking at it myself to see if it’s worthwhile utilizing for my motion activated lights. Might be a little overkill if you’ve only got a couple lights though. It’s available in HACS if you’re using it.

That’s pretty cool, but I was more concerned that I had never properly understood what a timer does :slight_smile:

It’s been like that at least as long as I’ve used Home Assistant (just over a year). The reason I’ve read (whether factual or hearsay) is to minimize updates to the UI.

The rest of the behavior is just completely out of sync with the docs.

If someone had asked me if calling timer.start on a running timer would reset it, I would have said “Of course.” … right up to a few moments ago. The test results indicate the timer doesn’t just work differently from my understanding of it, it also works differently from its documentation.

Not only does the behaviour not agree with the documentation, the documentation doesn’t even agree with itself. The preamble about timers doesn’t jive with the timer.start blurb:

SERVICE TIMER.START

Starts or restarts a timer with the provided duration. If no duration is given, it will either restart with its initial value, or continue a paused timer with the remaining duration. If a new duration is provided, this will be the new default for the timer until Home Assistant is restarted (which loads your default values). The duration can be specified as a number of seconds or the easier to read 01:23:45 format.
You can also use entity_id: all and all active timers will be started.

This is what I was expecting when I called timer.start.

Knowing what I know now, reading that paragraph makes me realize it tells you nothing about the timer’s elapsed time. Basically, no subsequent calls to timer.start affect the elapsed time.

Perhaps I find this 'cancel before (re)start` behavior so odd is because I’ve grown accustomed to working with systems that allow you to reset a running timer by simply starting it again. Old instance of timer is scrapped and a new instance is created.

This is what I understand when I read, “If no duration is given, it will…restart with its initial value”.

restart = start over from the beginning

There has been an issue open for a while:

It would appear we have not discovered something new but something very old. The problem dates back to, at least, 0.62 (almost a year ago).

Given the war of words halfway through the thread, I think this ‘broken timer’ issue was demoted to someone’s “DO NOT FIX” list.

If you think I’m kidding, consider the fact that timers are a very useful part of home automation logic and Home Assistant’s timers will be celebrating their 1-year FUBAR birthday in a few weeks …

… that’s a long time for something so useful to go unfixed.

That bug was a good read. I never thought it could be so complicated to figure out what the actions of a timer should be. Effectively cancelling and starting the timer again had always made sense to me as what should happen when you “start” a running timer.

At least you can achieve this functionality by always calling ‘cancel’ first. I know I surely don’t want to accidentally call ‘start’ on a running timer, because who knows what the hell will happen!

Who doesn’t love a bit of fubar ?

@tom_l nice find on ‘issue’ find, may I ask did you just find that or was it on your peripheral awareness? (sorry, too many ‘finds’ there)

I get the idea of requiring timer.cancel first, before allowing for a restart with timer.start. (I don’t like it, but I get it.)

What I don’t get is why it currently allows for this loophole:

  • Calling timer.start with a different duration will alter the running timer’s behavior (notably in a bad way if the new duration is shorter than the timer’s elapsed time).

If you’re obliged to cancel a running timer before you can restart it, this loophole should be closed. You should first have to cancel the running the timer and then restart it with a new duration.

Truly FUBAR … and left hanging in the wind for a year. :man_shrugging:

I personally think that there would be less confusion if there was a “timer.resume” service to start a paused timer running again separate and apart from the timer.start service.

So the only time a timer.start service would do anything is when the timer is already idle with zero time remaining.

If a timer is idle (paused?) with time remaining a timer.resume would start the timer active and start the time count down again.

And we can then also have a timer.restart to start the timer from the beginning using either the default duration or a different supplied duration.

To recap, the services would be:

timer.start - starts an idle timer with no time remaining with default/latest duration
timer.pause - pauses a timer before it is finished and maintains the remaining time
timer.resume - starts an idle timer with any time remaining
timer.restart - starts an active or idle timer, with or without time remaining, with default/latest duration
timer.cancel - stops a timer, sets the time remaining to 0
timer.finish - timer has programatically expired and sets time remaining to 0

Am I missing something or should it be this easy?

Tell me if you think this is useful and I should cross-post this over to the github issue page linked above.