Does anyone have an example of Template Sensors / Triggers?

I would like to use templates for two use cases:

(1) I would like to have a single battery sensor which state shows the lowest battery value of any battery operated device in Home Assistant. Is it possible to iterate through all battery operated devices, e.g. I don’t want to explicitly to refer to the devices. I should not have to update the template sensor if I add a new device.

(2) I would like to create a template sensor (template trigger) which would trigger any of sensors in that template sensor triggers. For example, I have a “First Floor Motion” template sensor which triggers when any of the motion sensors on the first floor triggers.

Does anyone have any examples for what I try to achieve?

Howto create battery alert without creating a template for every device - sorry it is long.

Actually I think the solution to the motion sensors is just to group them :slight_smile:

I might be wrong, but I seems to remember that I saw somewhere that groups were not good for triggers?

I haven’t read that.

For Part 1: The following template will return the entity_id of lowest battery value.

{{ states.sensor | selectattr('attributes.device_class', 'eq', 'battery')| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state')|map(attribute='entity_id')| first }}

For Part 2:
I agree with @nickrout, you should put the motion sensors in a group. I have a bunch of motion sensor groups and use their states as trigger in many automations.

1 Like

@nickrout @Didgeridrew thanks for the suggestions. As I am a newbie, it will take a bit of time to digest the information, hence the acknowledgement here !

FWIW

It is possible to create a template binary sensor to find the state of all the motion sensors in a specified area or list of areas. That would reduce your need to do code maintenance on your groups if you change a sensor’s physical location, but you would still have to do some maintenance updating the entity’s area…


{% set ns = namespace(m_areas = []) %}
{% set x = states.binary_sensor|selectattr('attributes.device_class', 'eq', 'motion')|map(attribute='entity_id')|list %}
{% for y in x %}
{% if area_name(y) == 'Kitchen' %}
  {% set ns.m_areas = ns.m_areas + ['{}'.format(states(y))] %}
{% endif %}{% endfor %}
{{ 'on' if 'on' in (ns.m_areas|join(', ')) else 'off'}}

To check a list of areas ( like your “First Floor Motion” example) you would modify the “{% if area_name(y)…” line :


{% if area_name(y) in ['Kitchen', 'Living Room', 'Dining Room'] %}

I don’t know for sure, but I think this approach would use more processing resources than using a group… the template is going to render any time any binary_sensor updates.

Thanks @Didgeridrew. I more interested in learning about Home Assistant than being worried about performance. This is a great example from which I will learn a lot.

Cool code. I have two questions. How would the code look if I would want to return the lowest value rather than the entity? It did return my Apple Watch as the entity which has the lowest battery which is totally correct, but not what I am looking for. What is I want to exclude the devices from my Apple iCloud integration?

I would just set a variable equal to the original code, then you can use the variable like you would an entity_id in any other template…

{% set x = states.sensor | selectattr('attributes.device_class', 'eq', 'battery')| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state')|map(attribute='entity_id')| first %}
{{state_attr(x, 'friendly_name')}}: {{states(x)}}%

The above will return something like:

Tokyo’s Apple Watch: 50%

I don’t use the iCloud integration, so you’ll have to help me figure out a way to filter those entities. Some integrations create a state attribute called “attribution” whose value will be something like “Data provided by Accuweather”… does iCloud do anything like that…?

The way to do it without having to figure any of that out is to just make a list of the iCloud related battery sensors, then reject any entity_id in that list:

{% set icloud_batteries = ['sensor.apple_watch_battery', 'sensor.iphone_battery']%}
{% set x = states.sensor | rejectattr('entity_id', 'in', icloud_batteries)| selectattr('attributes.device_class', 'eq', 'battery')| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state')|map(attribute='entity_id')| first %}
{{state_attr(x, 'friendly_name')}}: {{states(x)}}

Yes, the iCloud integration does the same. “Data provided by Apple uCloud”

Great! You should be able to use that as the attribute in a rejectattr filter so it automatically filters out any iCloud devices:

{% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
{{state_attr(x, 'friendly_name')}}: {{states(x)}}
template:
    - sensor:
        - name: "Lowest Battery Level"
          unit_of_measurement: "%"
          state_class: measurement
          device_class: battery
          state: >
            {% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
            {{states(x)}}

I am making good progress with the template sensor, but somehow it does not return the sensor information I expected. It returns a sensor with battery 100% while one of the sensors have battery 50%.

STATE: 50.0
ATTRIBUTES:
state_class: measurement
unit_of_measurement: '%'
friendly_name: 'MultiSensor 6: Battery level'
device_class: battery

Any suggestions on how to troubleshoot?

Also, I want to assign the friendly_name of that sensor to the friendly_name of the template sensor. HA complains friendly_name is not a valid option.

          state: >
            {% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
            {{states(x)}}
          friendly_name: >
            {% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
            {{state_attr(x, 'friendly_name')}}

Setting up the template sensor like you have may lead to some issues…

By putting it in the battery device class, it becomes self-referential. You can filter it out by adding another rejectattr, but I would just leave the device class as default.

The measurement state class isn’t really appropriate here.

The entity’s friendly name is created based on the name you give the sensor. As far as I know, it can’t be defined by a template. You can, however, use templates to define a custom attribute.

Below is a slightly different method that may work better for you.

template:
  sensor:
    - name: "Lowest Battery Level"
      unit_of_measurement: "%"
      state: >-
        {{ (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | map(attribute='state')) | list | map('int') | select("greaterthan", 0)| min }}
      attributes:
        sensor_name: >-
          {% set b = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery')| map(attribute='state')) | list | map('int') | select("greaterthan", 0)| min | string%}
          {% set c = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | selectattr('state', 'eq', b) | first) %}
          {{ c.name }}

Keep in mind that both methods have the same drawback… if a battery gets so low that the device goes offline, its state will be “unknown” or “unavailable” and it will not be displayed by this sensor.

Thanks for all the patience. I have been Googling, but have not come across any good tutorials. I understand the template scripts are based on Jinja2; that by itself is a bit of a paradigm shift for me. I hope this post will be helpful for others as well. Home Assistant is a bit overwhelming for a newbie, but I have realized that taking baby steps helps to overcome. Not to mention, the help from the community as well.

I did make some changes to the code since my last post and by putting friendly_name under attributes, it is possible to update it.

template:
    - sensor:
        - unique_id: lowest_battery_level
          state_class: measurement
          device_class: battery
          state: >
            {% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
            {{states(x)}}
          attributes:
            friendly_name: >
              {% set x = states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging', '100']) | sort( attribute = 'state') | map(attribute='entity_id') | first %}
              {{state_attr(x, 'friendly_name')}}

As you correctly noted, it does not make sense to use device_class: battery.

I will play around with your sample code provided here and let you know how it goes.

This what I have so far and seems to work well. Thanks @Didgeridrew

        - unique_id: lowest_battery_level
          name: "Lowest Battery Level"
          unit_of_measurement: "%"
          state: >-
            {{ (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | map(attribute='state')) | list | map('int') | select("greaterthan", 0)| min }}
          icon: >-
            {% set battery_level = states.sensor.lowest_battery_level.state|default(0)|int %}
            {% set battery_round = (battery_level / 10) |int * 10 %}
            {% if battery_round >= 100 %}
              mdi:battery
            {% elif battery_round > 0 %}
              mdi:battery-{{ battery_round }}
            {% else %}
              mdi:battery-alert
            {% endif %}
          attributes:
            sensor_name: >-
              {% set b = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery')| map(attribute='state')) | list | map('int') | select("greaterthan", 0)| min | string%}
              {% set c = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | selectattr('attributes.device_class', 'eq', 'battery') | selectattr('state', 'eq', b) | first) %}
              {{ c.name }}
1 Like

I am still struggling with getting the list ordered in increased state value. The following code seems to put the values with a .0 at the end.

{% set x = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | rejectattr('state', 'in', ['unavailable']) | selectattr('attributes.device_class', 'eq', 'battery') | sort(attribute='state',reverse=true) | map(attribute='state') | list) %}

[
  "87",
  "58",
  "50.0",
  "100.0",
  "100.0"
]

When putting an [int] in the sort, the 58 and 50.0 trade places, but the 87 is now listed last before 100.0.

{% set x = (states.sensor | rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud') | rejectattr('state', 'in', ['unavailable']) | selectattr('attributes.device_class', 'eq', 'battery') | sort(attribute='state'[int],reverse=true) | map(attribute='state') | list) %}

[
  "50.0",
  "65",
  "100.0",
  "100.0",
  "87"
]

Does anyone have any thoughts?

You have to remember that, unless you change it, everything produced by a template is a string… so what your first template is doing is sorting like you would sort words… by evaluating the left-most “letter” (in this case 8, 5, 5, 1, 1). The order is from highest to lowest because you reversed the sort.

In your second template the sort filter doesn’t make sense, so it is being ignored completely. If you erase it, the output will be the same.

Below is a variation on what I posted in post #15 above which will produce a list sorted by increasing state value.

{% set x = (states.sensor 
| rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud')
| selectattr('attributes.device_class', 'eq', 'battery') 
| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging']) 
| map(attribute='state') 
| map('int') 
| sort()
| list) %}
{{ x }}

What if I would like to sort the entities based on the numerical value of the state? This code will treat the state as a string.

{% set x = (states.sensor 
| rejectattr('attributes.attribution', 'eq', 'Data provided by Apple iCloud')
| selectattr('attributes.device_class', 'eq', 'battery') 
| rejectattr('state', 'in', ['unavailable', 'unknown', 'discharging'])
| sort(attribute='state', reverse=true) | list) %}
{{ x }}