Time Templates... Frustrated and Confused

I actually remembered to categorize this post myself… Sorry about the other ones @Tom if you see this, you put my other ones where they belong. I’m new to using the forums. I finally had to start asking y’all for help instead of just trying to teach myself all of this through Google. I try, but when I’m at a loss, I don’t have much other choice than to reach out for guidance, so sorry if anyone is bothered.

Can someone tell me what I am doing wrong here, how to fix it, and possibly explain what we did and why? My end goal is to have a sensor that simply states the Days, Hours, and Minutes (preferably would like to figure out how to add weeks, months and seconds to this as well, for similar sensors) until my next work shift. I need to trigger a ton of automations using it instead of offset naming my Calendar… because I don’t want 9 different entries for work… From what I’ve found this is a popular template, I just can’t figure out how to use it properly. Well, maybe popular for one person, they had their config on GitHub and everything had this attached. All I can get it to return with this setup is “Less Than 1 Minute…” It’s not until tomorrow.

#Calendar Sensor - Pronown Work
  - platform: time_date
    display_options:
      - 'time'
  - platform: template
    sensors:
      work_countdown_pronown:
        friendly_name: Work Countdown - Pronown
        entity_id: sensor.time
        value_template: >-
          {% set time = (states('calendar.work_pronown.attributes.start_time') | int)  | int %}
          {% set minutes = ((time % 360000) / 6000) | int %}
          {% set hours = ((time % 8640000) / 360000) | int %}
          {% set days = (time / 8640000) | int %}

          {%- if time < 6000 -%}
            Less than a minute
          {%- else -%}
            {%- if days > 0 -%}
              {{ days }}d
            {%- endif -%}
            {%- if hours > 0 -%}
              {%- if days > 0 -%}
                {{ ' ' }}
              {%- endif -%}
              {{ hours }}h
            {%- endif -%}
            {%- if minutes > 0 -%}
              {%- if days > 0 or hours > 0 -%}
                {{ ' ' }}
              {%- endif -%}
              {{ minutes }}m
            {%- endif -%}
          {%- endif -%}

I can’t comment on its popularity but it looks like a very old template because it still uses this line containing the entity_id option that was deprecated over a year ago.

entity_id: sensor.time

Post an example of the value contained by the attribute start_time.

I know it’s old, I saw it in a few posts, but one guy took it and ran with it lol. I have legacy enabled for purposes like these, the new formats don’t always seem to work for me, I may have a legacy style brain, but:

message: Work - Pronown
all_day: false
start_time: '2022-05-29 08:00:00'
end_time: '2022-05-29 09:00:00'
location:
description: ''
offset_reached: false
friendly_name: Work - Pronown

The use of the legacy format for Template Sensors wasn’t deprecated; I still use it. It’s the entity_id option that was deprecated. Home Assistant ignores it. That means the inclusion of sensor.time is ignored. It was put there to force the template to update every minute but it no longer does that (meaning this old template no longer gets updated every minute).

In addition, based on the example you posted, start_time contains a datetime string like this 2022-05-29 08:00:00. Yet the first thing the template attempts to do is convert it to an integer value using the int filter. That’ll never work.

Yeah, so… stupid question that probably stems from a very basic High School education in which I moved around a lot…

What’s an Integer? That was the part of the code I was most confused about. That and I’m not sure what FLOAT means… Most of teaching myself yaml and whatnot has been rather simple, and I can make one heck of an excel workbook, complete with some sloppy Java and everything… so I can’t figure out why all of this confuses me as bad as it does. Time math has always been a problem to me too.

Integers are whole numbers like 1, 7, 15, 456, etc.

Floating point numbers have a fractional value like 1.24, 6.3, 18.671, 753.5, etc.

A string is a quote-delimited collection of characters like 'cat' or '1999' or 'on'.

The int filter attempts to convert whatever it is given into an integer value. If you give it a string like 'cat' it cannot convert it to an integer. However if you give it a numeric string like '2048' it will be able to convert it to an integer value 2048. Given '2022-05-29 08:00:00' the int filter will fail to convert it to an integer.

Just guessing but maybe when this template was first created, the start_time attribute contained something different from what it currently contains (something that could be converted to an integer).

1 Like

So from what I am reading I need to incorporate now() into it somehow to get the updates. Shouldn’t it be as simple as converting now and the event into numbers, subtracting them, converting it back and format it how I’d like, followed by the if then statements in order to show the format how I’d like?
I think I have the basic idea, just too new to templating… Also, what kind of templating is this? Is this Python?

It (jinja) is closely related to python.

yes indeed you want to convert now() to a timestamp and the start_date to a timestamp, and subtract. That will give you the seconds until your shift, which can be converted to minutes, hours days etc.

1 Like

Also your entity start_date might already have a timestamp attribute - look at the attributes in the states tab of developer tools Open your Home Assistant instance and show your state developer tools.

Then develop your templates bit by bit in the template tab Open your Home Assistant instance and show your template developer tools.

1 Like

There’s no need to convert to timestamp and then back:

{% set time = 
( state_attr('calendar.work_pronown', 'start_time')
| as_datetime | as_local
- now() ).total_seconds() %}

{% set minutes = ((time % 3600) / 60) | int %}
{% set hours = ((time % 86400) / 3600) | int %}
{% set days = (time / 86400) | int %}

{% if time < 60 -%}
  Less than a minute
{%- else -%}
  {%- if days > 0 -%}
    {{ days~"d "}}
  {%- endif -%}
  {%- if hours > 0 -%}
    {{ hours~"h "}}
  {%- endif -%}
  {%- if minutes > 0 -%}
    {{ minutes~"m "}}
  {%- endif -%}
{%- endif %}
2 Likes

Quick and easy alternative:

Format start_time to appear as a datetime containing your timezone and set the sensor’s device_class to timestamp. It will make Home Assistant automatically display how much time remains between now and start_time.

  - platform: template
    sensors:
      work_countdown_pronown:
        friendly_name: Work Countdown - Pronown
        value_template: "{{ state_attr('calendar.work_pronown', 'start_time') | as_datetime | as_local }}"
        device_class: timestamp

Note:
The main drawback is you won’t have fine-grained control of the remaining time’s appearance.

In the UI, it only displays the largest unit of remaining time (examples: ‘In 5 days’ or ‘In 6 hours’ or ‘In 23 minutes’) and not a combination of days, hours, and minutes. If you absolutely require that then use Didgeridrew’s example.

1 Like

Hot dog! That did it!
So I can learn… we converted the start_time to a date_time and subtracted the now() converted into total seconds.
Two questions:

  1. Is date_time already in seconds? Is that why that didn’t need to convert as well?
  2. Is now() already in the same format as date_time?

@Taras this is awesome too, I do need the countdown to be very detailed though. I’m considering trying to add weeks, months, and years into it for other counters as well, like a personal anniversary card without the hacs one.

What are you talking about when you say date_time…?

When called, the now() function returns a timezone-aware, datetime object of the current date and time in your local timezone. As Taras mentioned earlier, the start_time attribute of the calendar sensor is being stored as a datetime string.

We need them both to be the same type to compare them or find their difference. That is the purpose of applying the | as_datetime | as_local filters to the start_time attribute. We want them both to be datetime objects.

The result of subtracting 2 datetime objects is a timedelta object which stores the time difference as days, seconds, and microseconds. In order to make it work in your formatting templates we use the total_seconds() function to convert and add together the days, seconds, and microseconds.

Second quick and easy alternative:

  - platform: template
    sensors:
      work_countdown_pronown:
        friendly_name: Work Countdown - Pronown
        value_template: >
          {% set r = state_attr('calendar.work_pronown', 'start_time') | as_datetime | as_local - now() %}
          {{ (r|string)[:-10] if r.total_seconds() > 60 else 'Past due' if r.total_seconds() < 0 else 'Less than 1 minute' }}

The result looks like this:

1 day, 4:36

Remaining time is 1 day, 4 hours and 36 minutes. If the remaining time is less than a minute, it’ll say just that and if the date is in the past it’ll report ‘Past due’.

I was exhausted when I posted it. I meant datetime, not with the underscore. Thank you for explaining! You’ve given me enough to probably get a much better understanding when I resear h further.

@123 I’m gonna have to play around with that. Looks fairly simple, actually looks like the math might be easier than the if/else I had going on.