Alert when devices have low battery

I was looking at the recent blueprint for Send Notifications for Battery Level and thought I would share what I use in case it helps people.

Personally I’ve found that an automation for low battery is really only useful for your phone. Since your phone’s battery gets drained daily, if you get a notification about low battery you’re probably going to go plug it in pretty quickly. But with IOT devices most of them take months, a year or even longer to go from 100% to 0. So a low battery alert probably isn’t urgent at all. It’s something you have to take care of but definitely not an immediate problem.

For this reason, I prefer something that nags me periodically until I do get the job done. This is possible but difficult with an automation. I prefer an alert since that is exactly its purpose, a nagging periodic notification about something until you resolve it.

What I have is this:

  1. A template sensor which collects all sensors with a device_class of battery and sets it state equal to the number of those sensors with a state <= 10
  2. A threshold sensor which is true any time that template sensor’s value is above 0
  3. An alert which notifies me every 12 hours with a count of the number of devices with low battery (if any have low battery)

If anyone is interested, here’s a package you can use to do the same:

sensor:
- platform: template
  sensors:
    devices_with_low_battery:
      friendly_name: 'Devices with low battery'
      unit_of_measurement: devices
      value_template: >-
        {{ states.sensor
           | selectattr('attributes.device_class', 'eq', 'battery')
           | map(attribute='state')
           | reject('in', ['unknown', 'unavailable', 'Ok'])
           | map('int', -1) | select('le', 10)
           | list | count
        }}
      icon_template: >-
        {% if is_state('sensor.devices_with_low_battery', '0') %}
          mdi:check-circle
        {% else %}
          mdi:battery-alert
        {% endif %}
binary_sensor:
- platform: threshold
  entity_id: sensor.devices_with_low_battery
  name: Devices with low battery
  upper: 0.5
alert:
  low_battery_devices:
    name: Devices have low battery
    entity_id: binary_sensor.devices_with_low_battery
    title: "{{ states('sensor.devices_with_low_battery') }} devices in house with low battery"
    message: "Devices all have 10% or less battery, you should change them"
    state: 'on'
    can_acknowledge: true
    repeat: 720
    notifiers:
    - 'me'

Note - This uses me as the notifier in the alert (which is really a call to notify.me). This was intended as a generic placeholder, you should put in the name of the notifier you want to use after import. Sorry for not mentioning this initially

Also personally I prefer to exclude phones from this notification since I have other automation specifically around a phone having a low battery. I took that bit out of the package above since I figured people would prefer to just have one low battery alert for everything. If you do want to have separate automation for low phone battery as well though, you can do this by just adjusting the value_template of the sensor.devices_with_low_battery to this:

value_template: >-
  {% set ignore_entities = ['sensor.phone_1_battery_level', 'sensor.phone_2_battery_level', '...'] %}
  {{ states.sensor
    | selectattr('attributes.device_class', 'eq', 'battery')
    | rejectattr('entity_id', 'in', ignore_entities)
    | map(attribute='state')
    | reject('in', ['unknown', 'unavailable', 'Ok'])
    | map('int', -1) | select('le', 10)
    | list | count
  }}

Obviously fill in your own values for the phone battery level sensors you have.

Update - non-numeric battery sensors

In the discussion below I was reminded of something I forgot to mention, handling non-numeric battery sensors. Specifically this line:

| reject('in', ['unknown', 'unavailable', 'Ok'])

unknown and unavailable are obviously common states for sensors with issues so filtering them out makes sense. But Ok here is being filtered out due to a use case I have. Nest Protect devices have a battery level sensor but they don’t report a numeric state, instead they simply say either Ok or Replace. I filter out Ok because that means the battery is fine. I let Replace through because then the int filter makes the non-numeric value into -1 and that alerts me since that is <= 10.

Obviously this doesn’t apply to everyone. If you have non-numeric battery sensors like mine then you should modify Ok to fit your use cases. Or alternatively you can add an additional filter of select('ge', 0) to the end of the filters before the list to remove all non-numeric sensors (since non-numeric values will get converted to -1 by the int filter whereas all numeric battery sensors would have state >= 0).

19 Likes

Thanks for sharing your project. This is a high-quality contribution, leveraging several of Home Assistant’s strengths. Well done!

3 Likes

This blueprint works well for me.
Though I see you can change the code for a phone, what other automation do you use for your phone besides this one.
Thanks

Hi.
The sensors.devices_with_low_battery is saying I have one that is less than 10%.

{{ states.sensor
   | selectattr('attributes.device_class', 'eq', 'battery')
   | map(attribute='state')
   | reject('in', ['unknown', 'unavailable', 'Ok'])
   | map('int', -1) | select('le', 10)
   | list | count
}}

However, listing all my devices with a class of battery in the developer tool I do not see any devices that is 10% or less. Is the code check not thorough enough?

{%- for item in states.sensor if (
  (
    is_state_attr(item.entity_id, 'device_class', 'battery')
    ) 
  ) -%}
  {{ item.entity_id }}
  name: {{ item.name }}
  state: {{ item.state }}
  
{% endfor -%}

The closes would be this device.

sensor.computer_room_battery
  name: computer_room_battery
  state: 34

Strange. I’ve been using this setup for quite a while without issue so I’m pretty confident in the template. Do you have a sensor with device_class of battery that has a non-numeric state? Like my Nest Protect devices have battery sensor but report their state as either Ok or Replace (that’s actually why Ok is being filtered out as a state there, forgot to mention that). If you have a sensor like that that could be the issue since the int filter will convert that to -1 (which is obviously less then 10).

Do you have a sensor with device_class of battery that has a non-numeric state?

Ah. You got me going back to the list and recheck. There is one device that is none integer.

sensor.id_battery_state
  name: iD Battery State
  state: Not Charging

Added Not Charging to the list and now it reads as 0.

reject('in', ['unknown', 'unavailable', 'Ok', 'Not Charging'])
1 Like

Makes sense. I updated the main post to mention this detail about non-numeric battery sensors specifically as it should be called out. I put that in a while ago so I forgot why the Ok was in there or I would’ve mentioned it initially.

Typo? This is a Template Sensor.

You are correct. Had been reading about blue prints most of the day and the fingers just typed it instead of sensor.

I hope some day we can make blueprints of packages, that would be pretty cool. Then you could use whatever capabilities of HA you need to build some cool thing, wrap it all up as a package and just ask users to fill in a few inputs to use it. Blueprints of automations are cool but its a bit limiting and you end up needing to make some seriously complicated logic since you can’t split it up into dependent template sensors or other entities.

2 Likes

Instead of saying “a device have <10% of battery” in the notification can we have the list of devices matching this criteria?

I am looking at starting off with Alerts and came across this thread.
I tested the Developer → Notify → Mobile App and I get the test notification

But when I use the code above, I am not getting the notification using the notifiers. Is there something special I am missing?
The Alert shows up on my automated dashboard as a switch, so I am trying to test by toggling it. Nothing shows up on the phone. The sensor does state there are 3 devices with lower than 10% battery.
Is there a way to easily test the notifier Alert?

Here is a modified version of the package (named package) that will lists devices and battery levels in the notification.
All thanks go to @CentralCommand

Some code could be optimized or improved, but I left it like it is here for readibility and because it could help other people:

  • Some variable could be bypassed or removed (bse, bss, bsn, bsb, as well as battery_sensors)
  • other variables could be set through helpers (ignore_entities, battery_threshold)
  • the alert is sent to homeassistant notifications, but should be sent to your phone if the companion has a persistent connexion to your HA instance (which I do not have when I leave home)
  • I did not succeed in using the map() filter function anywhere, so I used basic if statements
low_batteries_notify:
#
# Adapted from :
# https://community.home-assistant.io/t/alert-when-devices-have-low-battery/258072
#
  template:
    - sensor:
      # state gives number of devices with low battery level
      - name: 'Devices with low battery'
        unique_id: devices_with_low_battery
        unit_of_measurement: devices
        state: >-
          {{ this.attributes.devices | count }}
        icon: >-
          {% if is_state('sensor.devices_with_low_battery', '0') %}
          mdi:check-circle
          {% else %}
          mdi:battery-alert
          {% endif %}
        
        attributes:
          # the 'devices' attribute lists the devices having low battery level 
          # as well as the battery level
          ##################################
          # Change ignore_entities below
          # Change battery_threshold below
          ##################################
          # These variables could probably be set in a input_number helper for the threshold, 
          # and in a group for ignore_entities
          devices: 
            "
            {% set ignore_entities = ['sensor.myphone_battery_level', '...'] %}
            {% set battery_threshold = 15 %}

            {% set battery_sensors = states.sensor
              | selectattr('attributes.device_class', 'defined')
              | selectattr('attributes.device_class', 'eq', 'battery')
              | rejectattr('entity_id', 'in', ignore_entities)
              | list
            %}

            {% set devices = namespace(list=[]) %}
            {% for bs in battery_sensors %}
              {% set bse = bs.entity_id %}
              {% set bsn = bs.name %}
              {% set bss = bs.state %}
              {% set bsb = bs.attributes.battery %}
              {% if bss not in ['unknown', 'unavailable', 'ok', 'on', 'off', 'not charging', 'charging'], 'discharging' %}
                {% if bss | int <= battery_threshold %}
                  {% set devices.list = devices.list + [ [bsn, bss] ] %}
                {% endif %}
              {% endif %}
            {% endfor %}
            
            {{devices.list}}
            "
#
# Are there devices with low battery ?
# set the binary_sensor for the alert
#
  binary_sensor:
    - name: "Devices have low battery ?"
      platform: threshold
      entity_id: "sensor.devices_with_low_battery"
      upper: 0

#
# Alert to send to 'Notifiers:' <##### Change the value below
# The notification title includes the number of devices
# The message includes the list of devices with their battery level
  alert:
      low_battery_devices_alert:
        name: 'Devices have low battery alert'
        entity_id: binary_sensor.devices_have_low_battery
        title: "{{ states('sensor.devices_with_low_battery') }} devices in house with low battery"
        message: "{{ state_attr('sensor.devices_with_low_battery','devices') }}"
        state: 'on'
        can_acknowledge: true
        repeat: 720
        notifiers:
        - 'persistent_notification'

1 Like

Thank you. There’s a typo in below line

{% if bss not in ['unknown', 'unavailable', 'ok', 'on', 'off', 'not charging', 'charging'], 'discharging' %}

the square bracket in the end needs to go after ‘discharging’