Im trying to use relative_time() to show a time (in a sensible format) since a sensor changed state.
Ive created a sensor template and set the value_template to "relative_time(states.device_tracker.SPIDERMAN.last_changed)}}"
This kind of works and i see a time for the sensor in the UI but this never updates.
Checking the value_template value in the template editor shows the correct time since last_changed.
I therefore think its an update problem. Reading other people with this problem has led me to the entity_id parameter of the sensor template. But having set this to both device_tracker.SPIDERMAN and sensor.time (which supposedly triggers every minute?) the value still doesnt update.
How can I fix this to show an accurate, updating value of when the sensor last changed state in the UI?
here is my config. By “sensible” i mean minutes when its minutes, hours when its hours etc - this seems to be done internally in relative_time hence using it:
Ok thanks. But it does add a day always, it’s weird…
Don’t know why I can’t simply transform my last_updated into a proper datetime so that relative_time works…
it will. what you’ll need to do is a test on your value with a series of if statement to cater your display to only the units you actually want to see.
I know I’ve seen this topic being discussed in the past on this forum, but can’t remember where exactly but a quick search returned these:.
last_changed is not always a datetime object. Relative time will not update properly in the UI without using now(). So you’ll need to add it in or do the math yourself.
If you are using a simple UI element to display your sensor, like entities card… You can utilize the device_class: timestamp and the UI will handle the rest.
Thanks! I am actually in a loop to check whether any zigbee device is unavailable for a long time or not.
So I would prefer not to create a template sensor for each of them…
message: |
Some devices haven't been seen lately:
{% for state in states.sensor -%}
{%- if state.attributes.linkquality %}
{%- if "linkquality" in state.name and (as_timestamp(now()) - as_timestamp(state.attributes.last_seen) > (12 * 60 * 60) ) -%}
. {{ relative_time(state.attributes.last_seen) }} ago for {{ state.name | lower }} .
{%- endif -%}
{%- endif -%}
{%- endfor %}
But it works like these (even though it’s not what I want):
message: |
Some devices haven't been seen lately:
{% for state in states.sensor -%}
{%- if state.attributes.linkquality %}
{%- if "linkquality" in state.name and (as_timestamp(now()) - as_timestamp(state.last_changed) > (12 * 60 * 60) ) -%}
. {{ relative_time(state.last_changed) }} ago for {{ state.name | lower }} .
{%- endif -%}
{%- endif -%}
{%- endfor %}
message: |
Some devices haven't been seen lately:
{% for state in states.sensor -%}
{%- if state.attributes.linkquality %}
{%- if "linkquality" in state.name and (as_timestamp(now()) - as_timestamp(state.last_changed) > (12 * 60 * 60) ) -%}
. {{ relative_time(strptime(state.attributes.last_seen, "%Y-%m-%dT%H:%M:%S%z")) }} ago for {{ state.name | lower }} .
{%- endif -%}
{%- endif -%}
{%- endfor %}
This works because relative_time is expecting a datetime object, and the attributes are stored as strings. You apparently need to convert to a datetime, and the only way I see available to do that in the template is via strptime.
EDIT: Updated to use last_seen in both places and account for lqi=0.
message: |
Some devices haven't been seen lately:
{% for state in states.sensor -%}
{%- if not state_attr(state.entity_id, 'linkquality') == None and state.attributes.last_seen %}
{%- if "linkquality" in state.name and (as_timestamp(now()) - as_timestamp(strptime(state.attributes.last_seen, "%Y-%m-%dT%H:%M:%S%z")) > (12 * 60 * 60) ) %}
{{ relative_time(strptime(state.attributes.last_seen, "%Y-%m-%dT%H:%M:%S%z")) }} ago for {{ state.name | lower }},
{%- endif -%}
{%- endif -%}
{%- endfor %}
Thank you, I will gove it a try.
Could you please explain me this line you added please? {%- if not state_attr(state.entity_id, 'linkquality') == None and state.attributes.last_seen %}
tl;dr: To make sure we also include devices with a link quality of 0.
Long Version: the original code has this: if state.attributes.linkquality, to narrow down and only test sensors which have a linkquality attribute. This evaluates to FALSE under two conditions: 1) If linkquality is not an attribute (what we want) or 2) it’s value is some other “falsy” value, in this case that could mean if linkquality == 0 (which is bad, because we also want to include these in the list).
So to make sure the devices with linkquality = 0 are included, I use a more explicit built-in function (see the Template docs page) to test if the sensor has the attribute linkquality. The test is a double negative – state_attr() function is a “safe” function to get the attribute value–we’re just making sure its a value and not “None”.
The 2nd half (state.attributes.last_seen) just uses the original way to test if the device has a last_seen attribute and it’s not zero, because we’re not going to be able to do the next step without that.
thanks for this. I had an issue with my ever solar monitor which would disconnect if the usb rs485 stick was physically disturbed. I wanted a binary sensor which would tell me whether the update time was longer than 45 seconds in the past so that i could be notified to go and reboot the raspberry pi which had stopped being able to collect the data from the inverters. Just about managed to cobble one together now.