Time calculations

Hi,
I want to do some time calculations, and I’m confused as to how to do this.

My car reports “last connected time” as a timestamp, and I’d like to show that as a badge in Lovelace as “1 minute ago” or “3 hours ago” or something, how to do that?

Also, when the car is charging, I have an automation which sends me a message with the expected number of minutes to full, I’d like that to be shown like “Battery full time: 13:45”, and I don’t know how to do that either.

Any advice or documentation you could recommend?

Thanks a bunch!

Documentation probably wont help here because every time format string is different for just about every scenario. You should just post the state of your device and people can help make a template for you.

Hi,
For time-to-charge, it’s simple number of minutes left, eg. 127

For last connected, it’s like this: 2018-12-16 14:20:00

So this will take some setup.

You need to add a sensor.time sensor to your sensor section in configuration.yaml. Once that’s in place, we can make the 2 templates.

The first template is last connected. It will output in hours or minutes only. It will not say “3 hours and 4 minutes ago” . For example it will either return “x hours ago” if the duration was over an hour. It will output “x minutes ago” if under an hour. You will need to change the string in line {% set entity_id = 'sensor.last_updated' %} to whatever your current last_connected entity id is. For example, if it is ‘sensor.tesla_last_updated’, change that line to {% set entity_id = 'sensor.tesla_last_updated' %}.

The second just takes the current time (should be when the car is plugged in) and outputs it. You may want to completely remove this template and just do the calculation in your notification message (which is what I suggest) Just copy the value template into your message section. Make sure you replace data: with data_template. And add the > after message.

sensor:
  - platform: time_date
    display_options:
      - 'time'
  - platform: template
    sensors:
      car_last_connected:
        entity_id: sensor.time
        value_template: >
          {% set entity_id = 'sensor.last_updated' %}
          {% set last_updated = as_timestamp(strptime(entity_id), '%Y-%m-%d  %H:%M:%S')) %}
          {% set seconds = now().timestamp()-last_updated %}
          {% set hours = seconds / 60 %}
          {% if seconds / ( 60 * 60 ) > 1 %}
            {{ seconds //  ( 60 * 60 ) }} hours ago
          {% else %}
            {{ seconds // 60 }} minutes ago
          {% endif %}
      car_full_time:
        value_template: >
          {% set entity_id = 'sensor.time_to_charge' %}
          Battery full time: {{ ( now().timestamp() + states(entity_id) | float * 60 ) | timestamp_custom("%H:%M") }}

the second calc inside your automation notification would look something like this:

action: 
  - service: notifiy.notify
    data_template:
      message: >
        {% set entity_id = 'sensor.time_to_charge' %}
        Battery full time: {{ ( now().timestamp() + states(entity_id) | float * 60 ) | timestamp_custom("%H:%M") }}
5 Likes

You sir, are a wizard. Would have taken me ages to get this to work on my lonesome, thank you.

Only change I needed to make was to replace single quotes around the date format string with double quotes, and it looks like it’s working nicely (although the last connected time shows “unknown” for now, although that may be because the car has not re-connected since I made the changes.

I will reply on the thread when I get final results.

Thanks again!

1 Like

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