Time calculations

You can build an if statement for that case if you need to.

Hi,
It looks like the attribute is not correctly fetched on this line:

          {% set entity_id = 'sensor.last_updated' %}
          {% set last_updated = as_timestamp(strptime(entity_id), '%Y-%m-%d  %H:%M:%S')) %}

I think I need to somehow fetch the value, as now I think the entity_id is somehow not fetching the right value. The formatting parts works correctly (tried in Python console).

missing states() func

          {% set entity_id = 'sensor.last_updated' %}
          {% set last_updated = as_timestamp(strptime(states(entity_id)), '%Y-%m-%d  %H:%M:%S')) %}
1 Like

you can use built-in relative time as following:

{{ relative_time(states.binary_sensor.door_window_sensor_xxxxxxxx.last_changed) }}

That’s for HA state objects, his car outputs a sensor that has a time. I just labeled it last_updated because I’m lazy. The two are not related.

Hi guys, I got it to work after some fiddling, most of which was due to my own lack of understanding of how HA works.

Here is the final solution I used:

- platform: time_date
    display_options:
      - 'time'
  - platform: template
    sensors:
      car_last_connected:
        value_template: >-
          {% set value = states('sensor.SENSORNAMEHERE') %}
          {% set last_updated = as_timestamp(strptime(value, "%Y-%m-%d  %H:%M:%S")) %}
          {% set seconds = now().timestamp()-last_updated %}
          {% set hours = seconds / 3600 %}
          {% if seconds / ( 60 * 60 ) > 1 %}
            {{ seconds //  ( 60 * 60 ) }} hours ago
          {% else %}
            {{ seconds // 60 }} minutes ago
          {% endif %}

Edit: Thanks so much for the help, hopefully someone else finds this useful too.

Also, a tip for others struggling with Templates: remember to use the template testing tool instead of repeatedly editing / committing / pushing / pulling your config file like an idiot (like I did). :slight_smile:

1 Like

you might want to have a look at this:

 rpi_last_boot_macro:
    friendly_name: RPi Last boot macro
    value_template: >
      {% set up_time = as_timestamp(now()) - as_timestamp(states('sensor.last_boot')) %}

      {% set days = (up_time // (60 * 60 * 24)) | int %}
      {% set weeks = (days // 7) | int %}
      {% set hours = (up_time // (60 * 60)) | int %}
      {% set hours =  hours - days * 24 %}
      {% set minutes = (up_time // 60) | int %}
      {% set minutes = minutes - (days * 24 * 60) - (hours * 60) %}

      {% set days = (days | int) - (weeks * 7) %}

      {% macro phrase(value, name) %}
                {%- set value = value | int %}
                {%- set end = 's' if value > 1 else '' %}
                {{- '{} {}{}'.format(value, name, end) if value | int > 0 else '' }}
      {%- endmacro %}
          
      {% set text = [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), 
                      phrase(minutes, 'min') ] | select('!=','') | list | join(', ') %}
      {% set last_comma = text.rfind(',') %}
      {% set text = text[:last_comma] + ' and' + text[last_comma + 1:] %}

      {{ text }}

which formats the time a bit nicer :wink:

all honors go to @klogg and @petro

1 Like

If you get to the point of it being less than an hour text will be

XXminand XX mins

(because last_comma is -1)

        {% set up_time = as_timestamp(state_attr('calendar.work_audio','end_time')) - as_timestamp(states('sensor.date')+ ' ' + states('sensor.time')) %}
  
        {% set days = (up_time // (60 * 60 * 24)) | int %}
        {% set weeks = (days // 7) | int %}
        {% set hours = (up_time // (60 * 60)) | int %}
        {% set hours =  hours - days * 24 %}
        {% set minutes = (up_time // 60) | int %}
        {% set minutes = minutes - (days * 24 * 60) - (hours * 60) %}
  
        {% set days = (days | int) - (weeks * 7) %}
  
        {% macro phrase(value, name) %}
                  {%- set value = value | int %}
                  {%- set end = 's' if value > 1 else '' %}
                  {{- '{} {}{}'.format(value, name, end) if value | int > 0 else '' }}
        {%- endmacro %}
            
        {% set text = [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), 
                        phrase(minutes, 'min') ] | select('!=','') | list | join(', ') %}
        {% set last_comma = text.rfind(',') %}
        {% if last_comma > 0 %}
          {% set text = text[:last_comma] + ' and' + text[last_comma + 1:] %}
        {%- endif %}
        {{ text }}

The last condition fixes this

XX mins

I streamlined that quite some time ago, you can easily add any timeframe or remove it from divisors.

{% set uptime = as_timestamp(state_attr('calendar.work_audio','end_time')) - as_timestamp(states('sensor.date')+ ' ' + states('sensor.time')) %}
{%- set divisors = [('week', 7*60*60*24), ('day', 60*60*24), ('hr', 60*60), ('min', 60 )] %}
{%- set ns = namespace(remainder=uptime, result=[]) %}
{%- for desc, div in divisors %}
  {%- set num = ns.remainder//div %}
  {%- if num %}
    {%- set ns.result = ns.result + [ num ~ ' '~ desc ~ ('s' if num > 1 else '') ] %}
    {%- set ns.remainder = ns.remainder % div %}
  {%- elif num == 0 and desc == divisors[-1][0] and ns.remainder < 60 %}
    {%- set ns.result = ns.result + [ 'Less than 1 '~desc ] %}
  {%- endif %}
{%- endfor %}
{% if ns.result | count > 1 %}
  {{ ns.result[:-1] | join(', ') ~ ' and ' ~ ns.result[-1] }}
{% else %}
  {{ ns.result }}
{% endif %}

EDIT: I Just realized that there’s a mistake in here. Let me revise it.

2 Likes

@petro I was wondering if you still used this version of the time calculation template?

I am looking to store my sensors last trigger ie. binary_sensor.garage_sliding_internal_door_contact

As currently all of my sensors reset the last trigger when you reboot HA

This wouldn’t solve the reboot issue. I don’t use this template, the most recent version is here:

Thanks for the reply @petro. So neither the old version or this new version you posted would retain the info after reboot? Is there any method that would?

Hi guys, I’m not a programming expert, so sorry for my dump question. It might be some really simple code I guess. I have a Shelly plug that shows the seconds since may dryer is running. I want to show the running time on my dashboard - the time needs to be converted to hours, minute and seconds. Can anyone help me on that peace of code and where I need to add it on HASS? I wasn’t successful till now.
Thanks Vic

Hi there, sorry for the necrobump.
I was wondering if you could help me with a very similar issue. I need the same thing but I cannot adapt your code to my format string that results in the state: 2023-01-26T14:13:17+00:00.
Having “x minutes ago” or “x hours ago” would be perfect for me using the sensor.time to make the calculation.

I tried something like this:

{% set value = states('sensor.bagno_ping_last_changed') %}
{% set last_updated = as_timestamp(strptime(value, "%Y-%m-%d  %H:%M:%S")) %}
{% set seconds = now().timestamp()-last_updated %}
{% set hours = seconds / 3600 %}
{% if seconds / ( 60 * 60 ) > 1 %}
  {{ seconds //  ( 60 * 60 ) }} hours ago
{% else %}
  {{ seconds // 60 }} minutes ago
{% endif %}

But I have this error as result:

ValueError: Template error: strptime got invalid input '2023-01-26T14:13:17+00:00' when rendering template '{% set value = states('sensor.bagno_ping_last_changed') %}
{% set last_updated = as_timestamp(strptime(value, "%Y-%m-%d  %H:%M:%S")) %}
{% set seconds = now().timestamp()-last_updated %}
{% set hours = seconds / 3600 %}
{% if seconds / ( 60 * 60 ) > 1 %}
  {{ seconds //  ( 60 * 60 ) }} hours ago
{% else %}
  {{ seconds // 60 }} minutes ago
{% endif %}

This is fantastic and it should be a built in feature in HA to compare two input_datetime helpers if you ask me.
Sorry for reviving the thread, but I am getting a decimal as well as brackets and quotes when comparing two time stamps – eg.- ['55.0 minutes'], and this code is a little too complex for me to figure out where I need to slice the string in order to have something that’s TTS friendly. I’ve been staring at it for a while now and I think I’m stumped.
Any chance you could help me out? Thanks in advance!

Edit:
I think I figured it out:
{%- set ns.result = ns.result + [ num|float|round(0) ~ ' '~ desc ~ ('s' if num > 1 else '') ] %}
Thanks for the code in any case

This code is very old. I’ve updated it in another post.

for calendar entities…

          {%- set word_for_and = 'and' %}
          {%- set up_time = (states('calendar.xyz', 'end_time') | as_datetime | as_local - now()).total_seconds() %}

          {%- macro phrase(name, plural_name, divisor, mod=None) %}
            {%- set value = ((up_time // divisor) % (mod if mod else divisor)) | int %}
            {%- set name = plural_name if value > 1 else name %}
            {{- '{} {}'.format(value, name) if value | int > 0 else '' }}
          {%- endmacro %}
          
          {%- set values = [ 
                     phrase('week', 'weeks', 60*60*24*7), 
                     phrase('day', 'days', 60*60*24, 7),
                     phrase('hour', 'hours', 60*60, 24),
                     phrase('minute', 'minutes', 60), 
                     phrase('second', 'seconds', 1, 60) 
                 ] | select('!=','') | list %}
                        
          {{ values[:-1] | join(', ') ~ ' ' ~ word_for_and ~ ' ' ~ values[-1] if values | length > 1 else values | first  | default }}
1 Like

Sweet! I’m sure this will come in handy in the future. I’m using your last version to tell me how long I’ve been in bed when I get up in the morning rather than uptime and I plan to set something similar up to report how long I’ve been absent from home when I go away for days or weeks at a time.
Aside from being more streamlined, are there any advantages of using your new method? I already have your previous work doing a great job with that first task so I’m wondering if I should bother replacing that older code in what I’ve done so far.
Thanks again!

I was looking how to do this and found this old thread and after a lot of troubleshooting, I figured out that you can simply use the relative_time function.

Example:

{{ relative_time(states.vacuum.litter_robot_litter_box.last_changed)}}

Gives:

3 minutes

Which is exactly what I was looking for my notifications!

Just keep in mind that relative_time does not work with dates that are in the future. That’s why we have to create the large template for future events.

You can also get around this by subtracting the time difference from the current time, e.g.:

{{ relative_time(now() + (now() - strptime(states('sensor.washer_dryer_remaining_program_time'), "%Y-%m-%dT%H:%M:%S%z"))) }}