I’m guessing that your sensors always update. The int is truncating everything less than a minute to zero.
One would think so, but that is not the case. In the template editor, I saw the value increasing to 6 minutes.
Ah, probably need to add
entity_id: sun.sun
Otherwise it will only update when those sensors update, effectively making it always zero. sun.sun updates once a minute.
- platform: template
sensors:
since_last_motion:
friendly_name: 'Minutes since last motion'
entity_id: sun.sun
value_template: >
{%- set sensors = [states.binary_sensor.pir_woonkamer, states.binary_sensor.pir_toilet, states.binary_sensor.pir_hal, states.binary_sensor.pir_badkamer, states.binary_sensor.door_trapkast] %}
{%- set filtered_sensors = sensors | selectattr('last_changed','==', sensors | map(attribute='last_changed') | max) | list %}
{{ (( as_timestamp(now()) - as_timestamp(filtered_sensors[0].last_changed)) / 60 ) | int }}
Makes sense, I will try that.
It works, thanks!
Hi there,
thanks for sharing this discussion. Your solution works really good also in my case using Fibaro Z-Wave sensors.
However, I tried to convert the outcome from seconds to hours and days, using other templates of the forum (Convert seconds to days, hours, minutes). Unfortunately I can’t get the output to state anything other than “Less then a minute”. I of course adjusted the divisor, sensor name, etc.
Have you maybe had any luck with that question in the meanwhile?
Thanks!
Can you show some of your code and what exactly you are trying to achieve?
I am using the following by your example in my sensor.yaml to display the time in minutes since my motion sensors detected the last motion and this works perfectly well:
since_last_motion:
friendly_name: 'Letzte Bewegung im Haus (min)'
value_template: >
{%- set sensors = [states.binary_sensor.bewegungssensor_kuche_home_security_motion_detected, states.binary_sensor.bewegungssensor_wohnzimmer_home_security_motion_detected, states.binary_sensor.bewegungssensor_abstellraum_home_security_motion_detected] %}
{%- set filtered_sensors = sensors | selectattr('last_changed','==', sensors | map(attribute='last_changed') | max) | list %}
{{ (( as_timestamp(now()) - as_timestamp(filtered_sensors[0].last_changed)) / 60 ) | int }}
I have now tried to merge my code, with the one that I linked in my previous post, to convert minutes into minutes/hours/days. With merging, i.e. having all in one step, I did not succeed at all. Building a sequence, i.e. have my piece of code followed by:
since_last_motion_mhd:
friendly_name: 'Since Last Motion'
value_template: >-
{% set time = (states.sensor.since_last_motion | int) | int %}
{% set minutes = ((time % 3600) / 60) | int %}
{% set hours = ((time % 86400) / 3600) | int %}
{% set days = (time / 86400) | int %}
{%- if time < 60 -%}
Less than a minute
{%- else -%}
{%- if days > 0 -%}
{{ days }}d
{%- endif -%}
{%- if hours > 0 -%}
{%- if days > 0 -%}
{{ ' ' }}
{%- endif -%}
{{ hours }}h
{%- endif -%}
{%- if minutes > 0 -%}
{%- if days > 0 or hours > 0 -%}
{{ ' ' }}
{%- endif -%}
{{ minutes }}m
{%- endif -%}
{%- endif -%}
I get to the point that it displays “Less than a minute”, but somehow it does not update anymore. I think it’s because of the sequence.
Any ideas or suggestions?
reading this whole thread makes me wonder why one wouldn’t simply use the last_triggered attribute of an automation which is triggered by all motion sensors? (thinking the automation will probably be there already)
No need for complex templates and always exact (not simply updating each minute)?
{{relative_time(states.automation.update_last_motion.attributes.last_triggered)}}
{{relative_time(state_attr('automation.update_last_motion','last_triggered'))}}
I now this is not core HA but you can easily use the variable integration to even record which sensors triggered lastly. But, even if you wouldn’t use the variable, check the last_changed of this sensor to see how easily that could be used for the op’s quest:
EDIT: Scratch that. Missing morning wake up crutch.
Watch your last_changed attributes and see if it’s changing constantly.
Marius’ idea sounds nice as well.
For this template: it results in minutes since last motion, not seconds. Does that help at all?
thanks for all your feedback.
@petro: in my attempt the two code elements run in sequence. the first part defines the sensor “since_last motion”, by combining all motion activities of all sensors into one, counting seconds and returning the result divided by 60, in minutes, as expected.
the second part takes this sensor as input to convert it into hours, days and weeks. So to your question, yes the status of the input sensor updates correctly every minute, as “since_last_motion” also displays the updates correctly. Only the second part does not seem to get beyond the initial sate of “Less than a minute”.
@Mariusthvdb & @Emphyrio - I just recently started to get back into coding after several years, so please excuse my missing knowledge if this is stupid, but using the last_triggered attribute of an automation puts me back to start, doesn’t it?
It should not really matter if I use last_triggered of a sensor, or from the automation? Both return regular updates in seconds and my problem is not converting seconds into minutes. My problem is to dynamically convert it into min/hour/day/week - whatever is relevant at that time.
In my Lovelace I display for example the state of my main entrance (OPEN/CLOSED) and the time since the last change. This looks nice, as long as the time is shorter than 60 min, after that it can become difficult to read, if the door is closed since, e.g. 3456 minutes…
it does matter, as it is much simpler to write that is, if you already have an automation triggering on the same sensors you intend to use here. And even if you dont have that yet, I think you will soon, because you would want to do something within that info, and for that you would need an automation.
the relative_time() template I suggested above does handle the minutes correctly, and, when over 60, it will show 1 hour ago
.
relative_time
converts datetime object to its human-friendly “age” string. The age can be in second, minute, hour, day, month or year (but only the biggest unit is considered, e.g., if it’s 2 days and 3 hours, “2 days” will be returned). Note that it only works for dates in the past .
It will do so with all units, (and that makes the relative_time() function too rough for precise feedback)
If you truly want better feed back, and have it say like ‘2 weeks, 1 day, 13 hours and 20 minutes ago’ you would need the other template by Petro floating here and have it use that same last_triggered:
{%- set up = as_timestamp(now())-as_timestamp(state_attr('automation.update_last_motion','last_triggered')) %}
{%- macro phrase(name,divisor,mod=None) %}
{%- set value = ((up//divisor) % (mod if mod else divisor))|int %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value,name,end) if value|int > 0 else ''}}
{%- endmacro %}
{%- set values = [phrase('week',60*60*24*7),
phrase('day',60*60*24,7),
phrase('hour',60*60,24),
phrase('min',60),
phrase('sec',1,60)]
|select('!=','')|list %}
{{values[:-1]|join(', ') ~ ' and ' ~ values[-1] if values|length > 1 else
values|first}}
Thanks again for the quick feedback. Now I understand your point. I will play a bit with both suggestions. Thanks
Yes, I understand how the code works. My point was: If the code is getting re-rendered because it’s updating every 30 seconds, the code will never give you anything other than less than a minute.
Thanks for all your help so far! Now I understand your point using automations as trigger, that really works brilliantly and together with Petro`s pice of code it delivers exactly what I was looking for. I only removed the seconds to limit the display to minutes.
One final question if you don’t mind. Occasionally it throws back the following error message, even though all is working. I understand that the root cause is the code I added to sensor.yaml and the system seems to be irritated by the attribute ‘None’. Any idea why?
Logger: homeassistant.components.template.template_entity
Source: components/template/template_entity.py:71
Integration: template (documentation, issues)
First occurred: 16:41:50 (5 occurrences)
Last logged: 16:41:50
- TemplateError(‘UndefinedError: ‘None’ has no attribute ‘attributes’’) while processing template ‘Template("{%- set up = as_timestamp(now())-as_timestamp(states.automation.letzte_bewegung_im_aussenbereich.attributes.last_triggered) %} {%- macro phrase(name,divisor,mod=None) %} {%- set value = ((up//divisor) % (mod if mod else divisor))|int %} {%- set end = ‘’ if value > 1 else ‘’ %} {{- ‘{} {}{}’.format(value,name,end) if value|int > 0 else ‘’}} {%- endmacro %} {%- set values = [phrase(‘w’,6060247), phrase(‘d’,606024,7), phrase(‘h’,6060,24), phrase(‘m’,60)] |select(’!=’,’’)|list %} {{values[:-1]|join(’, ‘) ~ ’ ’ ~ values[-1] if values|length > 1 else values|first}}")’ for attribute ‘_state’ in entity ‘sensor.since_last_motion_outdoor’
it’s because you’re using states.automation.letzte_bewegung_im_aussenbereich.attributes.last_triggered
. Change the syntax to
state_attr('automation.letzte_bewegung_im_aussenbereich', 'last_triggered')
That will not work for last_changed or last_updated.
yes, as I posted/edited above already.
fyi, it has to do with the way templates are dealt with when an entity isnt ‘ready yet’ see the warning at https://www.home-assistant.io/docs/configuration/templating/#states
It’s not looking for an attribute ‘None’, it’s in fact saying there is no attribute (because somehow the state isn’t available in HA’s state machine)
haha, like your ‘irritated’. that’s what we all can relate to in these circumstances…
Thanks guys…seems to work like a charm now - and so far without errors
Also thanks for the explanation. I guess I still need some time until I figure those things out in detail!