Calculating minutes until a time?

HI all,

I need some help, I’m totally stuck on what I thought would be simple.

I have an input for a time where I need some automations to run by. I want to look up how many minutes from now until then. I cannot for the life of me work out how to calculate the minutes as the input_datetime doesn’t have a date, so I get scrambled answers.

My failed attempts are below. Any suggestions?


This is the input I’m working with (currently set to “9:00pm”)

input_datetime:
  need_to_be_ready:
    name: Hot Tub Auto Time
    has_date: false
    has_time: true

Not working sensor:

- template:
  - sensor:
      name: "minutes_until_needed"
      state: >
        {{ (((states.input_datetime.need_to_be_ready | int)) - ((as_timestamp (now()) | int ))) }}

And another sensor attempt which also didn’t work:

- template:
  - sensor:
      name: "minutes_until_needed"
      #state: >
      #  {{ (((states.input_datetime.need_to_be_ready | int)) ))) | int }}
      value_template: >
        {% set d = now().strftime("%Y-%m-%d ") %}
        {% set t = now().timestamp() %}
        {{ d + states('input_datetime.need_to_be_ready'), '%Y-%m-%d %H:%M:%S').timestamp() - t }}

You could compare the times only using “%H”.

Pseudo;

If now() "%h" > input "%h" {
Set variable = tomorrow's date ~ input time
}else{
Set variable = today's date ~ input time
}

This will show wrong time if the input has the time 09:01 and the time is 09:02.
But you could fix that by nesting and looking at the minutes also.

Time stuff is never as simple as you think it should be… but it has gotten easier in the past 8 months with the addition of today_at, timedelta, etc.

If it’s a time only input_datetime:

{{ ((today_at(states('input_datetime.time_only')) - now()).total_seconds() / 60) |int }}

If it’s a date and time input_datetime:

{{ ((states('input_datetime.date_and_time')|as_datetime|as_local - now()).total_seconds() / 60) |int }}

EDIT: Updated to use total_seconds() as suggested below.

4 Likes

This worked perfectly - thank you so much! Also unlike the other options I tried I can see the logic int his so can tweak and re-use elsewhere, and learning is always the aim on these things, so thank you!

1 Like

If the timespan goes over a day (difference between input_datetime.date_and_time and now is more than a day), the correct template is:

{{ ((states('input_datetime.date_and_time')|as_datetime|as_local - now()).total_seconds() / 60) |int }}

Note .total_seconds() instead of .seconds.

3 Likes

I agree this worked flawlessly and was the best explanation I’ve found on the issue. Question on the “If it’s a time only input_datetime:” version… Is there a way to have it countdown hours and minutes instead of just minutes?

Yes, but the exact method depends on the format you want it to output.

2024.05 added a time_until function to the template engine which can be used as follows:

{{ time_until(today_at(states('input_datetime.time_only')), 2) }}

The above method will return a string like: 3 hours 57 minutes, 2 hours, or 1 minute. If you need a different format it is possible to use additional filters and functions to modify it to your needs. You’ll need to share what format you want returned for us to help, make sure to specify what should happen when only one unit has a value to return.

You may also want to take a look at a couple custom template macros made by community members for relative time calculations:

Relative Time Plus

Easy Time

2 Likes

You nailed it again! Thanks and thanks more for a fast response!

Neat, that would simplify my templates a bit.

{{ (as_timestamp(states('input_datetime.workshop_dehumidifier_stop_time')) - as_timestamp(now()))|timestamp_custom('%H:%M',false) }}

{{ time_until(states('input_datetime.workshop_dehumidifier_stop_time')|as_datetime,2) }}

The long display format might not work well on mobile screens though.

Original: 02:59

New: 2 hours 59 minutes
2 Likes

It is a little too long to fit in the space I want to use it. I’m not sure if the New: can be altered to display as 2hr 59mins or not.

You can probably do something like this:

{{ ("2 hours 59 minutes").replace("ours","r").replace("ute","") }}

Which means:

{{ time_until(states('input_datetime.workshop_dehumidifier_stop_time')|as_datetime,2).replace("ours","r").replace("ute","")  }}