I’m trying to set up a timer, which I thought would be simple, but is proving not to be. What I want to do is have a timer that can be dynamically set.
I want to have a script that I can run to add a pre-defined amount of time to the timer. So let’s say the timer is currently running and there’s 5:30 left. I want to add an additional 10 minutes with a script. Apparently this isn’t a service that’s built into the timer. I figured I could just grab the remaining time and simply add 10 minutes to it, but I can’t seem to figure out what that syntax would look like.
I’ve tried to convert it in the template tester, but couldn’t get it to come out right. The remaining time is returned as a string. Can you help with the syntax?
I’ll have to do some research on it. It might be a bit before I can get back to you as life is calling. hopefully I can work something out by later tonight.
I believe you can only get the time remaining on a timer by pausing it, then the remaining attribute will be populated. At all other times it just shows the initial time it was set for
This isn’t exactly what you are asking for but life is calling me too and I thought this might help get you in the right direction in the mean time.
#=== Pause the timer to get remaining time
- service: timer.pause
entity_id: timer.hall_motion
#=== Extend the off time
- service: input_datetime.set_datetime
data_template:
entity_id: >
input_datetime.hall_light_off_time
time: >
{% set minutes = state_attr('timer.hall_motion', 'remaining').split(':')[1] %}
{% set seconds = state_attr('timer.hall_motion', 'remaining').split(':')[2] %}
{% set seconds_total = (minutes | int * 60) + (seconds | int) %}
{{ (as_timestamp(now()) + seconds_total) | timestamp_custom('%Y:%m:%d %H:%M:%S') }}
I think @klogg’s response will be the most elegant way to do it.
All of the other methods I’ve tried to capture the time remaining don’t work because the time remaining attribute HA doesn’t report it in a standard datetime object format and I can’t figure out a way with the tools I know to manipulate it into one.
I either get a time that is 5 hours off due to the shift from my timezone to UTC (should be 00:13:31 but comes out to 05:13:31) or it’s 23:15:00 off for some unknown reason (comes out 23:28:31). Either way neither of those will work so I think I’ve failed miserably here.
This converts a timer’s remaining attribute, which is a string representing hours, minutes, and seconds, into a single integer value representing total seconds. It adds 15 seconds to the total then converts it back into string format.
The first line gets remaining and converts it into a list with three items (hours, minutes, seconds).
The second line takes each one of the three list items and converts it into seconds, adds all three values together, then adds 15 seconds.
The third line converts the total seconds into hours, minutes, and seconds and formats them to appear as HH:MM:SS
HOWEVER, my preference is to convert the remaining attribute into a datetime object and take advantage of its capabilities. The resulting code is neater and easier to read than my previous example.
The first line gets remaining and converts it into a datetime object.
The second line create a ‘zero-time’ datetime object.
The third line subtracts the two datetime objects, then uses the datetime object’s total_seconds method to get the result in seconds, then adds 15 seconds to it.
The fourth line converts the total seconds into hours, minutes, and seconds and formats them to appear as HH:MM:SS
{% set x = strptime(state_attr('timer.my_timer', 'remaining'), '%H:%M:%S') %}
{% set y = strptime('00:00:00', '%H:%M:%S') %}
{% set ts = ((x - y).total_seconds() + 15) | int %}
{{ '{:02d}:{:02d}:{:02d}'.format(ts // 3600, (ts % 3600) // 60, (ts % 3600) % 60) }}
I tried that but I kept getting very strange results that I couldn’t do anything with (However I never tried just creating a 0 timestamp and then subtracting the raw datetime objects).
in the example I was using with a remaining time of 0:03:15 it gave me a result of 1900-01-01 00:03:15.
then when I tried converting it to a timestamp_custom using %H:M:%S I ended up with 23:38:15 and I had no idea where that number came from. If I added “false” to the end of the timestamp_custom I got 05:03:15 so it was 5 hours off because of the timezone shift. I couldn’t figure out a way to correct it.
You’d think it would provide 1970-01-01 which is the starting date for Unix timestamps. Instead it uses the start of the previous century which, when converted into a timestamp, results in a large negative number. Not sure why it uses that particular date.
The technique I used was to subtract two datetime objects where one is basically ‘zero-time’. The result of the subtraction is a timedelta object which has a method called total_seconds(). All of this avoids the issue you encountered with timestamp_custom.
Here’s yet another way to do it. I think I like this one best now.
This technique appends UTC timezone to the timer’s remaining attribute. Now when strptime converts it into a datetime object, the result is no longer ‘timezone naive’.
First line appends UTC timezone offset (-0000 hours) to remaining.
Second line converts the string into a (timezone-aware) datetime object.
Third line converts the datetime object to a timestamp, adds 15 seconds, then converts it into a string formatted as HH:MM:SS.
{% set x = state_attr('timer.my_timer', 'remaining') ~ '-0000' %}
{% set y = strptime(x, '%H:%M:%S%z') %}
{{ (as_timestamp(y) + 15) | timestamp_custom('%H:%M:%S', false) }}
It looks like there’s a few ways to do this. Now… The other problem is that you can’t “restart” a timer. You have to cancel or finish it, then start it with the new duration. How would you accomplish this in a script?
If you call the timer.start service with a duration attribute and the timer is already running, it actually resets the remaining time to (new duration - elapsed time). So if you set the timer for 10 minutes and started it, let it run for 1 minute, then started it again with a 20 minute duration, the timer would now have 19 minutes left.
So you need to cancel the timer first, the start it with the new calculated time. But if you cancel the timer, the remaining attribute becomes 0:00:00 and we have nothing to calculate off of.
As per @klogg’s advice, you must pause the running timer first and then its remaining attribute will contain the actual remaining time. Use that value to calculate the desired new duration. Then start the timer using the new duration.
EDIT
ninja’d by finity
I just noticed that when you pause a running timer, its remaining attribute contains a floating point number, not an integer. In other words, it shows fractional seconds. For example, here’s the result of pausing a 4-minute timer:
0:03:18.687535
That means my third and favorite solution won’t work because it assumes that seconds is an integer value (i.e. strptime fails to convert the string). There’s simple solution and that’s to simply add .%f to strptime so it can digest the decimal portion of the seconds:
{% set y = strptime(x, '%H:%M:%S.%f%z') %}
However, the problem with this technique is that when the timer’s state is idle, it displays the seconds in remaining as an integer value … and then the .%f causes strptime to fail. Bummer. Might need to use the first suggestion which doesn’t employ strptime.
I noticed that yesterday when I was playing around with this but I don’t think it matters since your strptime conversion ignores the fractions of a second.
If it does mess it up somehow you can format the strptime to pull the fraction using “%f” in the conversion then force the timestamp to an int before proceeding.
I was modifying my previous post when you replied. .%f isn’t a panacea for the reason I outlined (seconds is float when timer is paused and integer when idle).
A test could be added to check the timer’s state to decide if the conversion should, or should not, use .%f but that just bloats the template and negates the efficiency of using datetime objects.