This one has been baffling me for a while. I have a sensor template that reports back the most recently activated motion sensor. It works fine until I’m still and all sensors are in the off state. Then the template reports back as unavailable. So I researched a bit and learned of the “default” setting. It bypasses the unavailable but rather than simply reporting back the last value, it gives the text of the sensor definition (please see below, it’s hard to explain):
{% set a = states.sensor.last_motion %} {## Added most recently, hoping to retrieve the last state before reevaluating ##}
{% set t = expand('binary_sensor.master_motion_all') | selectattr('state','eq','on') | map(attribute='last_changed') | max | default(0) %} {## Adding the "default(0)" prevents the "Unavailable" state ##}
{% if( t != 0 ) -%} {## Added to prevent reevaluation when t variable = 0 ##}
{% set s = (expand('binary_sensor.master_motion_all') | selectattr('state','eq','on') | selectattr('last_changed', 'eq', t) | list)[0] %}
{% if( s | selectattr('state', 'eq', 'on') ) -%}
{% set i = s.name %}
{% set j = i.split(' ')[0] %}
{% set a = j %}
{%- endif %}
{%- else %}
{% set a = a %}
{%- endif %}
{{ a }}
As mentioned above, I added the first line most recently, but instead it returns:
The template doesn’t care, which surprised me, as the ‘a’ on the right isn’t yet defined. However, once you change it to the following, you get the undefined error.
{% set a = a %}
{{ a }}
So the upshot is it doesn’t like {{ a }} if it doesn’t exist. Without the first line, you have two conditions where this can occur. The first is that set a = a and the second is the lack of an else on the second if. These conditions should probably be code like you’ve got in the first line, but using the recommended format (see the warning here).
{% set t = expand('binary_sensor.master_motion_all') | selectattr('state','eq','on') | map(attribute='last_changed') | max | default(0) %} {## Adding the "default(0)" prevents the "Unavailable" state ##}
{% if( t != 0 ) -%} {## Added to prevent reevaluation when t variable = 0 ##}
{% set s = (expand('binary_sensor.master_motion_all') | selectattr('state','eq','on') | selectattr('last_changed', 'eq', t) | list)[0] %}
{% if( s | selectattr('state', 'eq', 'on') ) -%}
{% set i = s.name %}
{% set j = i.split(' ')[0] %}
{% set a = j %}
{%- else %}
{% set a = states('sensor.last_motion') %}
{%- endif %}
{%- else %}
{% set a = states('sensor.last_motion') %}
{%- endif %}
{{ a }}
Actually, thinking what you’re actually trying to achieve, you can do it with a trigger-based template. Then you don’t need to worry about the previous state, because you can set it up to only update when there is motion. Without doing this, your entity will update every minute because of the use of now().
In my example below, the sensor.last_motion will only update when binary_sensor.all_motion changes from “off” to “on”. At all other times the value does not change, even after a restart.
- trigger:
- platform: state
entity_id:
- binary_sensor.all_motion
from: "off"
to: "on"
sensor:
- name: Last Motion
unique_id: last_motion
state: >-
{% set x = expand('binary_sensor.all_motion')
| selectattr('state', 'eq', 'on')
| list
| sort(attribute='last_changed') %}
{% if (x|length != 0) %}
{{ x[0].name }}
{% else %}
Should never happen
{% endif %}
attributes:
when: >-
{{ now().strftime('%d/%m/%Y %-I:%M%p') }}
triggered: >-
{{ now() }}
I’m sorry, this seems like it would work but there must be some aspect that I’m failing to grasp. The whole bit of code goes into config right? Not split between config and automations? Here’s how I have it now:
in one section of config:
trigger:
- platform: state
entity_id:
- binary_sensor.master_motion_all
from: "off"
to: "on"
in another section (where my sensors are grouped):
sensor:
- name: Last Motion
unique_id: last_motion
state: >-
{% set x = expand('binary_sensor.master_motion_all')
| selectattr('state', 'eq', 'on')
| list
| sort(attribute='last_changed') %}
{% if (x|length != 0) %}
{{ x[0].name }}
{% else %}
Should never happen
{% endif %}
attributes:
when: >-
{{ now().strftime('%d/%m/%Y %-I:%M%p') }}
triggered: >-
{{ now() }}
I feel like perhaps it’s missing a platform? But I tried to add
- platform: template
sensors:
and it remains unavailable.
I’ve never used a trigger-based template before so I’m not even certain how to begin to troubleshoot. The state code did work properly in dev tools so it’s I’m sure something silly but I haven’t the foggiest. Thank you man.