Core: timer: timer.start doesn't restart the timer if already running

Created on 28 Jan 2018  ·  56Comments  ·  Source: home-assistant/core

Make sure you are running the latest version of Home Assistant before reporting an issue.

You should only file an issue if you found a bug. Feature and enhancement requests should go in the Feature Requests section of our community forum:

Home Assistant release (hass --version): 0.62.0

Python release (python3 --version): Python 3.6.3

Component/platform: timer

Description of problem: The timer does not restart if timer.start is called on a running timer.

Expected: The timer restarts.

Problem-relevant configuration.yaml entries and steps to reproduce:

frontend:

homeassistant:
  name: Home

http:

script:
  start_timer:
    alias: Start Timer
    sequence:
      - service: timer.start
        entity_id: timer.timer
  pause_timer:
    alias: Pause Timer
    sequence:
      - service: timer.pause
        entity_id: timer.timer
  cancel_timer:
    alias: Cancel Timer
    sequence:
      - service: timer.cancel
        entity_id: timer.timer
  finish_timer:
    alias: Finish Timer
    sequence:
      - service: timer.finish
        entity_id: timer.timer

timer:
  timer:
    duration: '00:01:00'
  1. Start the timer
  2. Wait a few seconds
  3. Press the start button again
  4. Observe that the timer hasn't restarted

Additional info:

I've done some investigation into this, and it seems to be due to the fact that the timer's state isn't changing, so HASS is discarding any changes made to the timer(?).

The logic to adjust the end time also isn't quite right.

This patch fixes the logic and also fixes the restart, however updating the state twice doesn't seem like the right way to do it.

index 84d2d3f34..b441e2380 100644
--- a/homeassistant/components/timer/__init__.py
+++ b/homeassistant/components/timer/__init__.py
@@ -249,17 +249,20 @@ class Timer(Entity):
         # pylint: disable=redefined-outer-name
         start = dt_util.utcnow()
         if self._remaining and newduration is None:
-            self._end = start + self._remaining
+            self._remaining = self._duration
         else:
             if newduration:
                 self._duration = newduration
                 self._remaining = newduration
             else:
                 self._remaining = self._duration
-            self._end = start + self._duration
+        self._end = start + self._duration
         self._listener = async_track_point_in_utc_time(self._hass,
                                                        self.async_finished,
                                                        self._end)
+        self._state = STATUS_PAUSED
+        yield from self.async_update_ha_state()
+        self._state = STATUS_ACTIVE
         yield from self.async_update_ha_state()
timer

Most helpful comment

I've been debugging this as well. It actually does work internally but the interface doesn't show it as running anymore.

A workaround for now is to call cancel followed by a start. That seems to work for me.

All 56 comments

Hmm, starting a running timer again sounds like a reset service. It's a bit like calling pause on a paused timer to restart it.

For manual interactions by a human it might be clear what happens but using service calls which have different outcome depending on a state in automation rules will become cumbersome. You need pretty much to track the state because a second start will reset the timer and not start it again with the current remaining time.

Ah I didn't think of the repercussion of the fix I suggested - you can't resume a paused timer! A timer.restart service may be better in this case.

Don't know whether this is 'working around a bug' or just 'working with what we've got', but this is how I've handled it from the perspective of having a slider in the ui that you can drag to set the timer. Dragging it again sets the timer again, dragging it to zero cancels it...

https://github.com/mf-social/Home-Assistant/blob/master/packages/virtual_devices/timer.yaml

Hope it helps.

Plus one. This is a change in behavior from prior versions, and the documentation clearly states that the timer.start service "Starts or restarts a timer with the provided duration."

I have an automation that starts an already started timer with a new time and it seems to work correctly. Am I missing something?

603 - door opens triggers automation to turn on light and start timer for 10 minutes
603 - light comes on
610 - door opens again and triggers automation to start timer for 10 minutes
620 - timer ends automation fires to turn off the light.

Mine did too, up until 0.62, then no.

I just noticed the same behavior.
A timer.start on a running timer used to restart the timer from the initial or specified duration, just like the documentation specifies.
Now, a timer start on a running timer does not do anything, unless a duration different from the initial one is specified.
A workaround would be to add a random number of seconds to the desired duration everytime timer.start is called.

Now at 0.65.6 and this is still an issue. To recap, the documentation states that timer.start on a running timer restarts the timer (optionally with a new duration if specified). Current behavior is that the timer does not restart, whether a duration is specified or not. And in fact, I can get a timer that shows a duration and is in running state, but never decrements and never finishes. Please fix this core, fundamental component.

This is still working correctly for me. Every time the back door opens or closes an automation sets the timer to 10 minutes. The timer should end 10 minutes after the last time the door opened or closed.

I also thought this wasn't working because the timer on the front end doesn't visually reset. However, I've noticed that even after the timer ends on the front end, it appears that it actually did reset because my trigger doesn't occur until later, when I'm expecting it to.

Any news with this issue?

Sorry, just got back from vacation. Will test ASAP

OK. I am currently running 0.73. In this version, sending timer.start to a running timer does the following:

  1. With no "duration" specified, the timer continues from where it is, it does not restart from the last or default (originally configured on the timer) duration.
  2. When specifying a duration in the timer.start action that is longer than the current remaining time, the duration is ignored. That is, with 57m:30s remaining on the timer, sending timer.start with a duration of 01:00:00, the timer continues counting from 00:57:30, it does not reset to 01:00:00.
  3. When specifying a duration shorter than the current remaining time on the timer, the duration resets down to the specified duration--if 00:57:30 is remaining on the timer, and the timer.start action is sent with a duration of 00:30:00, the timer starts counting down from 30 minutes.
  4. After test #3, if I specify a longer duration, I get a longer duration than remaining but shorter than specified (with about 20-ish minutes remaining, specifying a duration of 1 hour gave me 50 minutes on the timer). I feel like I'm in the twilight zone.

I am on 0.73, and this version does not have the @jackwilsdon changes. I'll make them in-situ and retest. Let me know if you have any additional instructions.

After the @jackwilsdon patch, timers work as expected:

  1. Sending timer.start on a running timer with no duration restarts with the last duration;
  2. Sending timer.start with a duration restarts with that duration, and no odd effects to duration being shorter or longer than remaining time.

As @joshbish noted earlier, it is clear that the UI is a completely unreliable indicator of what the timer is actually doing (I added logging statements so I could see reality). If a separate issue hasn't been opened for this, I think it should be.
EDIT: To be more precise: the UI does not show the countdown accurately if changed; however, if the timer is started (from stopped) or cancelled, it does reflect that state correctly.

+1 on this issue

@rbflurry the issue is actually only regarding the time display for me.
In the front end the timer appear to never restart, but my automation work just fine and have been restarted.

So try to add one of your timer to the front end and you might see the problem :-)

I've detailed the front end display issue in this forum post.

See posts 1 and 2.

It's super rude to randomly tag people because you think you deserve more attention then all the other things going on @tubalainen. Stop this immediately.

--edited--
Ok Thank you.

With 0.83.3 Im still experiencing this issue. If I call timer.start the timer does not restart while it is running.

Currently calling timer.cancel right before timer.start which seems to work most of the times but is not ideal

I'm a new user just installed hass.io and experiencing the timer.start not resetting the duration - is this going to be fixed, does it normally take this long for bugs to be resolved?

Thanks.

I've been waiting for a fix to this too. Surely a timer is a basic necessity of any Home Automation system and deserves resource ahead of other elements used only by a few. I'd have thought HA was for the benefit of the many.

@tubalainen you are being incredibly disrespectful here - you are the one in the wrong. This is an open-source project maintained by the community and a few core developers, they cannot spend their time looking at every issue (there's currently 902 issues open on the backend repository alone!).

A discussion needs to be had around how this should be implemented, see fabaff's reply for reasoning as to why my patch is not quite the right way to do it. The best way to start this discussion is most likely to open a PR with my patch from above and allow the core developers to comment on it when they see fit, as opposed to just complaining about it in this thread and not providing any constructive feedback towards fixing the issue.

@tubalainen you are being incredibly disrespectful here - you are the one in the wrong. This is an open-source project maintained by the community and a few core developers, they cannot spend their time looking at every issue (there's currently 902 issues open on the backend repository alone!).

A discussion needs to be had around how this should be implemented, see fabaff's reply for reasoning as to why my patch is not quite the right way to do it. The best way to start this discussion is most likely to open a PR with my patch from above and allow the core developers to comment on it when they see fit, as opposed to just complaining about it in this thread and not providing any constructive feedback towards fixing the issue.

There are feelings involved here. My blood is boiling equally on the very ignorant disrespectful answers given by the contributors. I feel deeply offended as well.

Lets move this discussion users/dev should behave to a forum instead of this PR. Agreed on that.
Maybe also how to tackle the 902 open issues?

Still an Issue on 0.94.3, my details are there, I closed it out I didn't see this one.

I do not believe this is related to the front end, I monitored the timer status from the states page. Is that considered "front end"

Still appears to be an issue on 0.97.1.

Even still in 0.98.2

I've been debugging this as well. It actually does work internally but the interface doesn't show it as running anymore.

A workaround for now is to call cancel followed by a start. That seems to work for me.

Yeah that's what I discovered too @WoLpH — because the state (i.e. running/stopped) doesn't change, HA doesn't seem pick up any changes made to the timer.

For me the timer disappears as if there's no time left, but it's running without any part of the interface showing it and when you look at the timer details you only see a frozen time with the set duration.

It seems to only happen if you decrease the time remaining

I experience the same problem here after following https://www.home-assistant.io/cookbook/turn_on_light_for_10_minutes_when_motion_detected/

The timer doesn't reset, and lights will go off even if there is lots of activity, before turning back on again and starting a new timer.

After a bunch more debugging and modding of the code I noticed even more weird stuff... not sure if any devs can chime in but I'm not familiar enough with the HASS code to understand what's happening here.

Issue 1 (probably?): state_changed events are only sent when actual data is changed

When calling timer.start with the same duration as the previous run, it won't register. When adding end as part of the state that issue is solved, but it doesn't really change the outcome.

Issue 2, here it gets weird... minutes and seconds are modified separately

To replicate:

timer.start:
    duration: 00:10:00

# Wait 10 seconds here

timer.start:
    duration: 00:05:00

The result of these two timer.start calls is not 5:00 as you would expect. It will be 4:50 because it did change the amount of minutes internally, but it leaves the seconds alone.

Actually... I kind of figured the 2nd one already, it changes the duration but doesn't reset the start time for the timer.

I also ran into this timer issue. However, as mentioned above too, I found that a cancel followed by a start does reset the timer.

A separate service call for resetting the timer would be a clean and intentful way to start the timer at its default duration. That would be more clear than calling start again. But either way, I would be happy when this would be fixed. Until then, I'm okay with the cancel/start work-around.

running into this issue myself and while I can find several workarounds, they aren't elegant and are causing me more headaches as I try to make stuff more complex. Any word on this getting corrected?

Just ran into this myself. I always thought that internally, it was as if the timer was cancelled and started again with the specified (or previous) duration. I have had to modify all instances of timer.start to be preceeded with timer.cancel.

If we are not allowed to restart a running timer by simply calling timer.start then the following loophole should be closed:

Calling timer.start with a different duration _will alter the running timer_.

  • If you set a _longer_ duration, the additional time (new duration minus old duration) is _added to the timer's remaining time_ (i.e. elapsed time is preserved but remaining time is increased).

  • If you set a _shorter_ duration, strange things can happen. If the running timer's elapsed time exceeds the new duration, the UI ceases to show the timer's countdown _yet the timer's state remains active_. It's completely unclear which one is telling the truth. This behavior is undocumented.

Ideally we should be able to restart a running timer by simply calling timer.start. Every system I've used works this way. However, if we can't have that, then make things consistent; disallow modifying a running timer's duration. If you have to first call timer.cancel before you can restart a timer with its _default_ duration, then you should have to do the same to start it with a _different_ duration.

FWIW, I'm cross posting this here from the recent forum discussion about this. Hopefully it's helpful...

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?

I was also just hit by this bug. I expect a start of an already running timer to simply start again at the value I give it. I cannot make any sense in the current behaviour.

Even still in 0.102.3

+1
same for me on

Home Assistant 0.102.3

arch | armv7l
-- | --
dev | false
docker | true
hassio | true
os_name | Linux
python_version | 3.7.4
timezone | Europe/London
version | 0.102.3
virtualenv | false

Hmmm - I seem to have encountered this. The documentation (https://www.home-assistant.io/integrations/timer/) states:

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. This, for example, makes it easy to create timed lights that get triggered by motion

I've just set up a 10 minute timer in my kitchen, but the timer doesn't reset when motion is redetected which results in the lights being switched off at the end of the timer - regardless of whether someone is in the room or not.

Now I've discovered the timer doesn't reset and now wondering if this may be the real cause of the bug I posted #30448 - it's plausible the timer was actually completing and restarting from scratch each time rather than be restarted mid timer.

I'm on version 0.103.6

For all that reach this due to the documentation...

If you simply run your "restart" of the timer like this:

- service: timer.cancel
  entity_id: timer.my_timer
- service: timer.start
  entity_id: timer.my_timer

The event_type: timer.finished is never called from my experience... which would be the desired result.
This may just be best to update the documentation.

This appears to work for me - so @jfrux method to extend the life of a timer without the timer.finished event is effective.

However, since I have an automation that triggers on the timer.start event, this isn't ideal.

For context, I'm using scene.create on when a timer is started to record the state of a light before changing the colour while a timer is active. Because a subsequent timer.start event is emitted; my ephemeral scene is now overwritten.

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue now has been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

Still not fixed

Yeah, this still is an issue. I do use the stop/start work-around, but it does impact some of my automations. I can no longer directly react to a timer not being active anymore, but have to use for: 1 to not trigger on a stop/start timer reset. Not a real problem, but a bit of an ecstatic thing: looking at the history for the timer, the stop/start events are visible and the timer is not a continuous block.

Same here,
When timer is re/started with new greater value, seems it's not take in to account.
And timer expires much earlier.

timer:
  xevent:
    duration: 3

script:
  dbg:
    sequence:
      - service: timer.start
        entity_id: timer.xevent
        data:
          duration: 10
      - delay: '00:00:05'
      - service: timer.finish
        entity_id: timer.xevent
      - delay: '00:00:01'
      - service: timer.start
        entity_id: timer.xevent
        data:
          duration: 20

This is how I have done it since 2018.

Examples for the "action part" of an automation.

To reset active running timer

- action:
  - data:
      entity_id: timer.timer
    service: timer.cancel
  - data:
      duration: 08:00:00
      entity_id: timer.timer
    service: timer.start

To restart the timer if finished/idle/not started

- action:
  - data:
      entity_id: timer.timer
    service: timer.start

@tubalainen
Then there is a bug since 2018, I think.
For official documentation - "Starts or restarts a timer with the provided duration."

@tubalainen
Then there is a bug since 2018, I think.
For official documentation - "Starts or restarts a timer with the provided duration."

Correct!

In my setup, cancel/start not work every time.

I think I'm also running into a version of this issue. I have a 00:05:00 timer that starts on home assistant start. When a periodic MQTT heartbeat message is received (~once a minute), timer.start is run again and correctly restarts the timer. If timer.finished happens, a notification is sent. If the MQTT message is received while the timer is 'idle' a different notification is sent.

When I start home assistant, the timer starts and restarts generally as expected, though the UI doesn't give an accurate display. When home assistant first starts, the timer displays a coutndown starting from 00:05:00. It counts down to 0 (ignoring that it should have received 4 timer.start calls in that time). When it reaches 0, it then displays 'active' instead of the time, and this doesn't seem to change until I restart home assistant. But otherwise it all works, the notifications fire on timer.finished and when it restarts from idle.

The really buggy part is if I manually run timer.finish, the first notification fires as expected. When the next MQTT message arrives, the second notification fires also as expected. The trouble is the next 4 times the message is received, timer.start does not reset the timer to 00:05:00 so after 5 minutes, timer.finished fires again, sending the first notification. Then the MQTT message arrives, the timer restarts, and the second notification fires. Then the timer counts down again and this cycle repeats, ignoring any timer.start calls while the timer is active. I've tried manually calling timer.start and timer.reload but the timer keeps counting down. The only fix is to restart home assistant. The obvious solution is 'don't manually call timer.finish' (I was just using it for testing) but the underlying issue is worrisome. The relevant config is below.

timer:
  a_side_main_keypad_heartbeat:
    duration: '00:05:00'`
automation:
  - alias: Start A Main Keypad Heartbeat Timer
    trigger: 
      platform: homeassistant
      event: start
    action:
    - service: timer.start
      entity_id: timer.a_side_main_keypad_heartbeat

  - alias: Restart A Main Keypad Heartbeat Timer
    trigger:
    - platform: mqtt
      topic: i3/doors/a-side-main/sync
    action:
    - service: timer.start
      entity_id: timer.a_side_main_keypad_heartbeat

  - alias: A Main Keypad Heartbeat Timeout
    trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.a_side_main_keypad_heartbeat
    action:
    - service: notify.slack_statusbots
      data:
        message: "A-side main door keypad heartbeat has timed out (5 min since last heartbeat)"

  - alias: A Main Keypad Back Online
    trigger:
    - platform: mqtt
      topic: i3/doors/a-side-main/sync
    condition:
      condition: state
      entity_id: timer.a_side_main_keypad_heartbeat
      state: 'idle'
    action:
    - service: notify.slack_statusbots
      data:
        message: "A-side main door keypad back online. Hearbeat received."

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue now has been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

👍 Works in 0.114.4

Was this page helpful?
0 / 5 - 0 ratings