Date formatting

Here comes another question or maybe even a challenge.
I have a motion sensor that I want to show the “last motion detected” date/time. One of the attributes of the sensor has “last motion updated” However, the timezone is incorrect and once I pull the information to the front, the formatting is less then attractive: [‘2019-01-27’, ‘23:57:03’]

How would I go about formatting a template with the following:

Dream setup:

  1. Correct time zone.
  2. Day/Month and 24h time formatting.
  3. If motion was detected less then an 1 min ago just show > Less then a min. ago
  4. If motion was detected less then an hour ago just show the minutes. X min. ago
  5. If motion was detected today. I would like it to show > Today at 23:57
  6. If yesterday > Yesterday at 23:57
  7. If later then two days ago, show the day name and time > Monday at 23:57
  8. If later then 7 days ago, the day/month and time > 28/01 at 23:57

Acceptable setup:

Also, alternatively. Just showing the correct date and time for each last movement sensed. 28/01 at 23:57

//Cheers and thanks for any inputs and help!

Why is the timezone incorrect and what does the device look like in the states page. Whats the wrong timezone and what’s the correct timezone?

I have 4 motion sensors, two different brands.

One of them is a Hue Motion sensor and thats the only one showing an attribute with motion last_updated date/time as shown above. The other three do not have an attribute for time, but the logs register it obviously. I have no idea why the time zone is off by three hours on the Hue. The hue bridge is set to the correct time zone… Instead of using the sensors own time, maybe using the logs time-stamp would be more “practical” for all 4 of them?

All 4 sensors are binary, on the states only show > On or Off

Other attributes of the hue sensor:

light_level: 9344
battery: 100
last_updated: 2019-01-28,20:19:59
lx: 8.6
dark: true
daylight: false
temperature: 23.02
on: true
reachable: true
friendly_name: Hue Motion Sensor
device_class: motion


Other Motion sensors:

node_id: 6
value_index: 0
value_instance: 1
value_id: 82057594076357481
friendly_name: Kitchen Motion Sensor
device_class: motion
icon: mdi:run

so last_updated for hue is the wrong time zone? How many hours off and what time zone are you in?

Correct. It is off by three hours, so +3 on whatever the time is saying on the sensor.
What about the idea of pulling the “latest” information/time stamp from the logs, is that even possible?

Continuing our earlier chat in Discord,

The Hue sensor has the entity_id > binary_sensor.kitchen_motion_motion_sensor
And since it’s showing UTC time, adding +3 should be correct.

Sine the update to lovelace I have been having problems with the underscores. See, the Hue Motion Sensor is a custom component that I recently installed. But thats another problem for later. If you are planning to create a sample, use that e-id, I will have to change it later if need be. Thanks a million!

I’m having @Ice’s identical issue really, apart from underscores seem to be working. Looking for a way to format the timestamp of the Hue sensor YYYY-MM-DD,HH:MM:SS into something palatable to use in a relative_time template for ‘X minutes ago’

ok, so you’ll have to create a template sensor

This template does the following:

  1. We get the state of your last_updated attribute and set it equal to t. Do not confuse this last_updated attribute with last_updated datetime object. The last_updated datetime object is a homeassistant object that is OUTSIDE the attributes. The last_updated attribute is only supplied by your sensor. All home assistant state objects contain the last_updated datetime object. So why am i saying this? This method will only work on your binary sensor it will not work on any other home assistant object you have. Unless that have the last_updated attribute.
  2. The next thing we do is convert your last_updated into a datetime object in UTC. So we add +0000 to your state which indicates UTC. Then strptime converts it to a datetime object with the correct format. The format is all those funky characters in the strptime function. They basically say “this is the format of the date that we will always get from your sensor”.
  3. Once we have the datetime object, we convert it to a timestamp and then output the correct format we want. This uses the same funky characters in step 2.
sensor:
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        value_template: >
          {% set t = state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated') %}
          {% set t = strptime(t+"-+0000", "%Y-%m-%d,%H:%M:%S-%z") %}
          {{ as_timestamp(t) | timestamp_custom('%m/%d at %H:%M') }}

I don’t think you should implement this part of your statement, it will complicate your template and it won’t really give you what you want as it will only update once a minute.

I guess I’ll just post the solutions for future people who stumble on this post. These templates will output in the following cases:

  1. If motion was detected less then an 1 min ago just show > Less then a min. ago
  2. If motion was detected less then an hour ago just show the minutes. X min. ago
  3. If motion was detected today. I would like it to show > Today at 23:57
  4. If yesterday > Yesterday at 23:57
  5. If later then two days ago, show the day name and time > Monday at 23:57
  6. If later then 7 days ago, the day/month and time > 28/01 at 23:57

last_updated as an attribute

sensor:
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        entity_id: sun.sun
        value_template: >
          {%- set t_string = state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated') %}
          {%- if t_string != None %}
            {%- set t = strptime(t_string+"-+0000", "%Y-%m-%d,%H:%M:%S-%z") %}
            {%- set t = as_timestamp(t) %}
            {%- set n = now().timestamp() %}
            {%- set d = n-t %}
            {%- set midnight_today = as_timestamp(strptime(now().date() | string, "%Y-%m-%d")) %}
            {%- set midnight_yesterday = midnight_today - 86400 %}
            {%- set midnight_week_ago = midnight_today - 604800 %}
            {%- if d < 60 %}
              Less then a min. ago
            {%- elif d < 3600 %}
              {{ (d // 60) | int }} min. ago
            {%- elif d < n-midnight_today %}
              Today at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_yesterday %}
              Yesterday at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_week_ago %}
              {{ t | timestamp_custom('%A at %H:%M') }}
            {%- else %}
              {{ t | timestamp_custom('%m/%d at %H:%M') }}
            {%- endif %}
          {% else %}
            unknown
          {% endif %}

Anyone using this template may need to edit the format in this line to have it work with there last_updated attribute: {%- set t = strptime(t+"-+0000", "%Y-%m-%d,%H:%M:%S-%z") %}.


last_updated datetime on state_objects

(99% users should use this template). Just remember to replace binary_sensor.kitchen_motion_motion_sensor with your entity_id inside the first line of the template.

sensor:
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        entity_id: sun.sun
        value_template: >
          {%- set entity_id = 'binary_sensor.kitchen_motion_motion_sensor' %}
          {%- set domain, object_id = entity_id.split('.') %}
          {%- set state_object = states[domain][object_id] %}
          {%- if state_object is defined and state_object.last_updated is defined %}
            {%- set t = as_timestamp(state_object.last_updated) %}
            {%- set n = now().timestamp() %}
            {%- set d = n-t %}
            {%- set midnight_today = as_timestamp(strptime(now().date() | string, "%Y-%m-%d")) %}
            {%- set midnight_yesterday = midnight_today - 86400 %}
            {%- set midnight_week_ago = midnight_today - 604800 %}
            {%- if d < 60 %}
              Less then a min. ago
            {%- elif d < 3600 %}
              {{ (d // 60) | int }} min. ago
            {%- elif d < n-midnight_today %}
              Today at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_yesterday %}
              Yesterday at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_week_ago %}
              {{ t | timestamp_custom('%A at %H:%M') }}
            {%- else %}
              {{ t | timestamp_custom('%m/%d at %H:%M') }}
            {%- endif %}
          {% else %}
            unknown
          {% endif %}

If you are getting unknown in either situation, check your entity_id and verify that it contains last_updated as an attribute. Unknown can also occur during startup but it should rectify when last_updated changed or a minute has passed.

4 Likes

I tried this and I’m getting the following error:

ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.kitchen_motion_last_updated fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 221, in async_update_ha_state
    await self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 347, in async_device_update
    await self.async_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/sensor/template.py", line 196, in async_update
    self._state = self._template.async_render()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/template.py", line 138, in async_render
    return self._compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "
TypeError: can only concatenate list (not "str") to list

put this in the template editor and tell me the output

state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        value_template: >
          {% set t = state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated') %}

And output is Unknown

no… the template editor. It’s in the UI

Sorry my bad, Output is [‘2019-01-29’, ‘14:47:28’]

change

{%- set t_string = state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated') %}

to

{%- set t_string = ''.join(state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')) %}

so…

sensor:
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        entity_id: sun.sun
        value_template: >
          {%- set t_string = ''.join(state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')) %}
          {% if t_string != None %}
            {%- set t = strptime(t_string+"-+0000", "%Y-%m-%d,%H:%M:%S-%z") %}
            {%- set t = as_timestamp(t) %}
            {%- set n = now().timestamp() %}
            {%- set d = n-t %}
            {%- set midnight_today = as_timestamp(strptime(now().date() | string, "%Y-%m-%d")) %}
            {%- set midnight_yesterday = midnight_today - 86400 %}
            {%- if d < 60 %}
              Less then a min. ago
            {%- elif d < 3600 %}
              {{ (d // 60) | int }} min. ago
            {%- elif d < n-midnight_today %}
              Today at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_yesterday %}
              Yesterday at {{ t | timestamp_custom('%H:%M') }}
            {%- else %}
              {{ t | timestamp_custom('%m/%d at %H:%M') }}
            {%- endif %}
          {% else %}
            unknown
          {% endif %}
Update for sensor.kitchen_motion_last_updated fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 221, in async_update_ha_state
    await self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 347, in async_device_update
    await self.async_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/sensor/template.py", line 196, in async_update
    self._state = self._template.async_render()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/template.py", line 138, in async_render
    return self._compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "
TypeError: unsupported operand type(s) for -: 'float' and 'NoneType'

what does this return

{{ ''.join(state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')) }}

Returns: 2019-01-2915:16:55

Without the space in between the time and date.

bah, do this

{{ ','.join(state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')) }}

and this

sensor:
  - platform: template
    sensors:
      kitchen_motion_last_updated:
        friendly_name: Last Kitchen Motion
        entity_id: sun.sun
        value_template: >
          {%- set t_string = ','.join(state_attr('binary_sensor.kitchen_motion_motion_sensor','last_updated')) %}
          {%- if t_string != None %}
            {%- set t = strptime(t_string+"-+0000", "%Y-%m-%d,%H:%M:%S-%z") %}
            {%- set t = as_timestamp(t) %}
            {%- set n = now().timestamp() %}
            {%- set d = n-t %}
            {%- set midnight_today = as_timestamp(strptime(now().date() | string, "%Y-%m-%d")) %}
            {%- set midnight_yesterday = midnight_today - 86400 %}
            {%- set midnight_week_ago = midnight_today - 604800 %}
            {%- if d < 60 %}
              Less then a min. ago
            {%- elif d < 3600 %}
              {{ (d // 60) | int }} min. ago
            {%- elif d < n-midnight_today %}
              Today at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_yesterday %}
              Yesterday at {{ t | timestamp_custom('%H:%M') }}
            {%- elif d < n-midnight_week_ago %}
              {{ t | timestamp_custom('%A at %H:%M') }}
            {%- else %}
              {{ t | timestamp_custom('%m/%d at %H:%M') }}
            {%- endif %}
          {% else %}
            unknown
          {% endif %}
6 Likes

Now it’s working :smiley:
Amazing, thanks a million!

Going to try the other sensors now too.

Update - They work great too!