Easy Time Macros for Templates!

probably, but it’s not necessarily worth it.

One thing you made want to note is that changing Hacs to experimental makes the sensor.hacs unavailable. Took me awhile to figure out why my system monitor card suddenly stopped working.

1.0.7 Release

New Features

  • clock icon macro
  • days away counter (integer)
  • days away counter (speach)

New Languages

  • Italian

Fixes

  • Fixed issue with past French relative times
  • Various fixes and improvements
1 Like

like the new clock_icon :wink:

can I ask for some explanation though please? I remember having had a wrestle getting the hour hand to turn the clock correctly at 12 and had to do this

            {% set clock = ['skipthis','one','two','three','four','five','six','seven',
                           'eight','nine','ten','eleven','twelve','one'] %}
            {%- set hour = now().hour %}
            {%- set minute = now().minute %}
            {%- set hand = hour if minute <= 30 else hour + 1 %}mdi:clock-time-{{clock[hand]}}

but now see you doing:

{%- set _clock_icon = ['twelve','one','two','three','four','five','six','seven','eight','nine','ten','eleven'] %}

{%- macro clock_icon(hour=None) -%}
{%- if hour is none or hour is not integer -%}
  {%- set t = now() -%}
  {%- set h = (t.hour + t.minute // 30) % 12 %}
  {{- 'mdi:clock-time-' ~ _clock_icon[h] -}}
{%- else -%}
  {{- 'mdi:clock-time-' ~ _clock_icon[hour % 12] -}}
{%- endif -%}
{%- endmacro -%}

which both eliminates the ‘skip_this’ and the double ‘one’. which is much better ofc.

not sure I understand why this works and why that couldnt be done in the format I used

I’m not sure how yours even works in the first place. now().hour has a range of 0 to 23. a clock has a range of 0 to 11. 0 representing 12. All I’m doing is getting the remainder of i / 12 using modulo %. 0 / 12 has a remainder of 0, 1 / 12 has a remainder of 1… 13 / 12 has a remainder of 1… 15 / 12 has a remainder of 3, etc. So all I need to do is account for 0 to 11 because the maximum remainder will be 11.

see for yourself.

{%- for i in range(100) %}
{{ i }} -> {{ i % 12 }}
{%- endfor %}
1 Like

yes, I see that now. Ive changed it to:

      - unique_id: hour_icon
        name: Hour icon
        state: >
            {% set clock = ['twelve','one','two','three','four','five','six','seven',
                            'eight','nine','ten','eleven'] %}
            {%- set t = now() %}
            {%- set hand = (t.hour + t.minute // 30) % 12 %}mdi:clock-time-{{clock[hand]}}
            {{- '-outline' if states('sun.sun') == 'below_horizon' }}

btw, this is where my original template started to see the light:

and on the extra ‘one’:

This is awesome! What a useful and exciting introduction to Template Macros.

I do have one question/feature request: I’m trying to display elapsed minutes and hours from the last time a state changed. custom_time('states.entity.last_changed, 'hour, minute')" works great for this, except when there is less than a minute elapsed it says either “now” or “0 hours and 0 minutes”. I would prefer to say “less than a minute” in this case. Is there any to change/overwrite that?

I can probably add that but I’d need translations for all the included languages.

1.0.8 Release

New Features

  • Time between macros

New Languages

  • Portuguese
1 Like

Thanks for this release. It seems to be just what I am after as I have just started to tidy up some of my lovelace views with my own macros however there is no point in reinventing the wheel for my “Last Updated” type information.

However, im struggling to get this to work for some reason and im unsure why.

I just keep getting a question mark returned from the macro. Ive tried all sorts of variations and macros from the file but still get the same results.

Can anyone point me in the right direction please as at present when I input;

{% from 'easy_time.jinja' import clock, easy_time %}
## Checking that the easy_time.jinja is importing correctly
{{ clock() }}

## Verifying a date is being passed to the macro
{{ states.sensor.zigbee_repeater_living_room_rssi.last_changed }}

## Trying out easy_time
{{ easy_time('states.sensor.zigbee_repeater_living_room_rssi.last_changed') }}

This is my output (in dev console);

## Checking that the easy_time.jinja is importing correctly
8:55 AM

## Verifying a date is being passed to the macro
2023-05-10 07:50:18.109501+00:00

## Trying out easy_time
?

Any help much appreciated. Thank you.

Don’t put the states….last_updated in quotes.

Brilliant. Thanks - i just managed to get that working a few seconds before and came here to update my query! Thank you again for this great set of macros.

Im loving these macros so far.
Just whilst fiddling at work ive managed to reduce my overall lovelace frontend from 22800 lines down to 21600 lines and ive only been through the first 7000 lines or so! I know there is much more to come off too!

One thing that I would like to see but I am not sure is currently possible is to output the time when your passed just the seconds from an entity.

For example, at present I have the uptime of my nvr as a sensor reading such as 340015 (seconds).

However, there is no way to output this as “3 days, x hours”. It would be amazing if this could be considered in a future release.

Thanks

You can do that now without much effort, but yes I can add that

{% from 'easy_time.jinja' import custom_time, easy_time, big_time %}
{{ easy_time(now() - timedelta(seconds=340015)) }}
{{ big_time(now() - timedelta(seconds=340015)) }}
{{ custom_time(now() - timedelta(seconds=340015), 'hour,minute') }}
{{ custom_time(now() - timedelta(seconds=340015), 'day,hour') }}

as a side note, if the sensor is a duration device_class sensor, it works with just

{% from 'easy_time.jinja' import custom_time, easy_time, big_time %}
{{ easy_time('sensor.xyz') }}
{{ big_time('sensor.xyz') }}
{{ custom_time('sensor.xyz', 'hour,minute') }}
{{ custom_time('sensor.xyz', 'day,hour') }}

Many thanks for this.

The below code works for me - is this the tidiest way I can achieve it?

      {% from 'easy_time.jinja' import easy_time %}
      {{ easy_time(now() - timedelta(seconds=int(states('sensor.nvr_uptime')))) }}

Unfortunately, the sensor isnt a duration device_class sensor so I cant make the code as tidy as your second set of suggestions to me.

Thanks again.

now this would be something to actually use multiple times in a config:

a jinja replacement for a date countdown, we’ve all worked on: Python_script for countdown to birthdays/anniversaries/significant dates - #58 by petro

my personal v version is like this now:

today = datetime.datetime.now().date()

name = data.get('name')
type = data.get('type')
event = data.get('event')
sensorName = 'sensor.{}_{}'.format(type ,name.replace(' ','_'))

dateStr = data.get('date')
dateSplit = dateStr.split('/')

dateDay = int(dateSplit[0])
dateMonth = int(dateSplit[1])
dateYear = int(dateSplit[2])
date = datetime.date(dateYear,dateMonth,dateDay)

thisYear = today.year
nextOccur = datetime.date(thisYear,dateMonth,dateDay)

numberOfDays = 0
years = int(thisYear) - dateYear


if today < nextOccur:
  numberOfDays = (nextOccur - today).days

elif today > nextOccur:
  nextOccur = datetime.date(thisYear+1,dateMonth,dateDay)
  numberOfDays = int((nextOccur - today).days)
  years = years+1

phrase = 'dag' if numberOfDays == 1 else 'dagen'

hass.states.set(sensorName,numberOfDays,
  {
    'entity_picture': '/local/family/{}.jpg'.format(name.lower()),
    'unit_of_measurement': phrase,
    'friendly_name': '{} wordt {} over:'.format(name,years),
    'persoon': name,
    'type': '{}'.format(type.title()),
    'years': years,
    'datum': '{}-{}-{}'.format(dateDay,dateMonth,dateYear)
  }
)

binarysensorName = 'binary_sensor.' + sensorName.split('.')[-1]
binarysensorState = 'on' if numberOfDays == 0 else 'off'
hass.states.set(binarysensorName,binarysensorState,
  {
    'persoon': name,
    'type': '{}'.format(type.title()),
    'years': years,
    'datum': '{}-{}-{}'.format(dateDay,dateMonth,dateYear),
    'event': event,
    'name': name
  }
)

and I would hope to be able to move that all to some jinja wizardry

could it be done in your macro’s?

probably need some adaptation of GitHub - Petro31/easy-time-jinja: Easy Time calculations for Home Assistant templates because I can not do:

{% from 'easy_time.jinja' import count_the_days %}


{{ count_the_days("2023-07-27 00:00:00", utc=True) }}

because of

TypeError: unsupported operand type(s) for -: 'NoneType' and 'datetime.datetime'

this however:

{% from 'easy_time.jinja' import count_the_days %}


{{ count_the_days("2023-07-27 00:00:00") }}

or even

{% from 'easy_time.jinja' import count_the_days %}


{{ count_the_days("2023-07-27") }}

return a day count alright,

ofc that is not the same, as the python is built upon an original date of an event (birthday…) and calculate from that, showing current age, and next date day count…

update

this seems to bring me the correct year count:

{{(states('sensor.date') | as_timestamp - as_timestamp('1964-08-24'))/( 86400*365)}}

so that could be done with a setter:

 {% set date = '1964-08-24' %}
 {{(states('sensor.date') | as_timestamp - as_timestamp(date))/( 86400*365)}}

but then we’d need some easy way to use that same date and calculate the

{% from 'easy_time.jinja' import count_the_days %}
{% set current_date = date.replace('1964', (now().year)|string) %}
{{count_the_days(current_date)}}

had hoped to use a true datetime for those, but we cant… my dates precede the 1970 epoch.

this can be done:

{% set event = '1964-08-24' %}
 {{((states('sensor.date') | as_timestamp - 
     as_timestamp(event))/( 86400*365))|int}}
 

{% from 'easy_time.jinja' import count_the_days %}
{% set current_date = event.replace((event|as_datetime).year|string, (now().year)|string) %}
{{count_the_days(current_date)}}

Petro,

This must be a brute force method, but ive got most of it working:

{% from 'easy_time.jinja' import count_the_days %}
{% set event = '1902-08-28' %}
{% set month = (event|as_datetime).month %}
{% set day = (event|as_datetime).day %}

{% if month < now().month or
      month == now().month and day < now().day %} {% set yearnext = 1 %} {% set yearlast = 0 %}
{% else %} {% set yearnext = 0 %} {% set yearlast = -1 %}
{% endif %}

{% set next_date = event.replace((event|as_datetime).year|string, (now().year + yearnext)|string) %}
{% set last_date = event.replace((event|as_datetime).year|string, (now().year + yearlast)|string) %}

days to next event = {{count_the_days(next_date)}}
days since last event = {{count_the_days(last_date)|int|abs}}


days = {{count_the_days(event)|int|abs}}
years = {{(count_the_days(event)|int|abs/365)|int}}
age =   {{((states('sensor.date') | as_timestamp - as_timestamp(event))/( 86400*365))|int}}

if you feel this would be a nice addition to the easy time macros, ill leave it in this thread.
if not, I would love some feedback in the Discord or maybe a separate post, please let me know ?

Not sure if you are still looking for feature suggestions, but here is one:

In addition to big_*, it would be nice to have a “small_time” function for including relative times in relatively small lovelace cards. small_time would abbreviate the string to something like “1 hr 30 min”, possibly with max_period never exceeding hour? (or I guess you could do “1 day 11 hr 30 min 20 sec” for a complete verison). Potentially could use a small_custom version too to be more specific on which parts to use as abbreviations. I would use this in the secondary field of a mushroom card.

Thanks for considering!

That’s custom_*