Homeassistant is missing a native function to create a last motion sensor. It should take a list of all motion sensors and extract the name of the last triggered entity changing to state on.
That way it’s possible to determine in which room/area someone is currently in. This is very useful for every single person household e.g. you can use it to automatically turn off lights and media players in room where nobody is in.
Currently this is only possible with custom components that offer a variable like:
Until someone creates a Last Motion Sensor, you can use a Template Sensor to produce the desired result.
- platform: template
sensors:
last_motion:
friendly_name: 'Last Motion'
value_template: >
{% set sensors = [states.binary_sensor.kitchen,
states.binary_sensor.living,
states.binary_sensor.bathroom] %}
{% set t = sensors | map(attribute='last_changed') | max %}
{% set s = (sensors | selectattr('last_changed', 'eq', t) | list)[0] %}
{{s.name}} {{s.last_changed.timestamp() | timestamp_local() }}
If you just want the sensor’s name and not the time it triggered, remove this from the last line: {{s.last_changed.timestamp() | timestamp_local() }}
If you already have a group of all motion sensors, you can write it like this:
- platform: template
sensors:
last_motion:
friendly_name: 'Last Motion'
value_template: >
{% set t = expand('group.all_motion') | map(attribute='last_changed') | max %}
{% set s = (expand('group.all_motion') | selectattr('last_changed', 'eq', t) | list)[0] %}
{{s.name}} {{s.last_changed.timestamp() | timestamp_local() }}
If you don’t wish to specify the motion sensors by name or including them in a group, they can be extracted by their device_class. Here are the two relevant lines in the template:
{% set t = states.binary_sensor | selectattr('attributes.device_class', 'eq', 'motion') | map(attribute='last_changed') | max %}
{% set s = (states.binary_sensor | selectattr('attributes.device_class', 'eq', 'motion') | selectattr('last_changed', 'eq', t) | list)[0] %}
But your solution would also track if a motion sensor’s state switches to off. Imagine walking around your home. If you do this fast multiple motion sensors will have the state on (triggered). So the first one that went to on will also be the first one that switches to off and that is not the information I need to determine in which room I am. Am I wrong?
Thanks! Identify the motion sensors by their device class is a good idea. So the sensor would be more flexible if I add another sensor. Can you please provide a full example? Where do I have to add the part with stateattr('state', 'eq', 'on') that you mentioned?
Give it a try; it’s easy. It will be more satisfying to do it yourself than to have it done for you. I’ve already provided two full examples so you can use them for reference. Hint: just add it as an additional filter to each template. You can use the Template Editor to experiment with the template.
I know this is an old topic, but I have some troubles adding the filter (state -eq on) to this sensor.
I tried adding the filter to both lines as follows:
{% set t = sensors | selectattr('state','eq','on') | map(attribute='last_changed') | max %}
{% set s = (sensors | selectattr('state','eq','on') | selectattr('last_changed', 'eq', t) | list)[0] %}
This only works if any motion sensor has a state on. If all sensors are off, there will be errors:
ValueError: max() arg is an empty sequence
Which is expected, as you cannot max() on something that is empty. Do you have any idea how I could solve this? Maybe I should add an if around these two lines somehow to check if there are any sensors with state on?
I have been trying to modify my LAST MOTION sensor to show only motion detected. ON not OFF. This solution is as close as I can get, however, I would like the LAST MOTION to remain listed. It looks like after 60 seconds of no PIR activity, “none” is triggered by ELSE statement.
Any guidance how I can keep the LAST MOTION identified, regardless of time passed?
This should work. Requires creation of a group with a list of motion sensors in it. But you can easily swap this out to use the sensors variable in the OP.
{% set t = expand('group.all_motion') | selectattr('state', 'eq', 'on') | map(attribute='last_changed') | max %}
{% set s = (expand('group.all_motion') | selectattr('last_changed', 'eq', t) | list)[0] %}
{{s.name}} {{s.last_changed.timestamp() | timestamp_local() }}
{% set t = expand('group.all_pir_sensors') | selectattr('state', 'eq', 'on') | map(attribute='last_changed') | max %}
{% set s = (expand('group.all_pir_sensors') | selectattr('last_changed', 'eq', t) | list)[0] %}
{{s.name}}
if there are two sensors in the group BOTH ‘on’ at the same time, it flips between the two, rather than only showing the most recent one that changed to ‘on’