Tutorial: Get notified when a device needs a new battery
In this tutorial, you will build a daily notification that tells you which devices need new batteries. It looks at every battery sensor in your home, picks out the ones below a threshold you choose, and sends you a message listing each device with its location and current percentage.
This is one of the most popular templates in the Home Assistant community, and it is a great first real-world template. You will learn how to loop over a collection of sensors, filter by state, build up a list, and format a message. All of these skills carry over to many other templates you will write later.
What you will build
A notification that arrives each morning with a message like this:
2 devices need new batteries: Front door lock in Hallway (15%), Motion sensor in Kitchen (12%).
If everything is healthy, you get a reassuring message instead:
All batteries are healthy.
Before you start
You need:
- At least one battery-powered device reporting a percentage through Home Assistant. Zigbee, Z-Wave, and Matter devices usually do this automatically.
- A notification service set up on your phone, usually the Home Assistant Companion app. The examples below use
notify.mobile_app_your_phone. Replace that with your ownnotifyaction when you get there. - Five minutes with the Developer tools template editor open.
Step 1: See your battery sensors
Before you write a single template, it helps to see what you are working with. Open Settings > Developer tools > States and filter on battery.
You should see a list of sensors with device_class: battery in their attributes and a number (like 85 or 12) in the state column. Those are the entities this automation will watch.
If you do not see any, your devices might be using the old binary_sensor battery class (which reports on/off instead of a percentage) or they do not report battery at all. This tutorial focuses on the numeric sensor kind.
Step 2: List the low batteries
Open Developer tools > Template and paste this in:
{% set low = namespace(batteries=[]) %}
{% for sensor in states.sensor
| selectattr('attributes.device_class', 'eq', 'battery')
| rejectattr('state', 'in', ['unknown', 'unavailable']) %}
{% if sensor.state | float(100) < 100 %}
{% set low.batteries = low.batteries + [device_name(sensor.entity_id)] %}
{% endif %}
{% endfor %}
{{ low.batteries }}
['Front door lock', 'Motion sensor', 'Bedroom sensor']
Read it left to right:
- Create a namespace called
lowwith an emptybatterieslist inside. - For each sensor whose
device_classisbattery, skipping any that areunknownorunavailable… - If its state (converted to a number) is below
20… - Add the name of the device that sensor belongs to to
low.batteries.
Why the device name? With modern Home Assistant naming, a battery sensor’s own name is often only “Battery”, which is not very helpful in a notification. The device_name function gives you back the friendly name of the device the sensor is attached to (like “Front door lock” or “Motion sensor”).
You should see a list of device names with low batteries. If the list is empty, you can temporarily raise the threshold (change 20 to 100) to confirm the template is working.
Variables set inside a for loop do not survive outside the loop, so low is created with a namespace. That is the object whose attribute (low.batteries) can be updated inside the loop and still hold the full list afterwards. See Loops and conditions for more on this quirk.
Step 3: Format the message
A bare list is not what you want to send to your phone. Add the area the device is in, the current battery level, and turn the result into a friendly sentence. Replace the template with this:
{% set low = namespace(batteries=[]) %}
{% for sensor in states.sensor
| selectattr('attributes.device_class', 'eq', 'battery')
| rejectattr('state', 'in', ['unknown', 'unavailable']) %}
{% if sensor.state | float(100) < 20 %}
{% set device = device_name(sensor.entity_id) %}
{% set area = area_name(sensor.entity_id) %}
{% set label = device ~ (' in ' ~ area if area else '')
~ ' (' ~ sensor.state ~ '%)' %}
{% set low.batteries = low.batteries + [label] %}
{% endif %}
{% endfor %}
{% if low.batteries | count == 0 %}
All batteries are healthy.
{% elif low.batteries | count == 1 %}
1 device needs a new battery: {{ low.batteries[0] }}.
{% else %}
{{ low.batteries | count }} devices need new batteries: {{ low.batteries | join(', ') }}.
{% endif %}
3 devices need new batteries: Front door lock in Hallway (15%),
Motion sensor in Kitchen (12%), Window sensor in Bedroom (8%).
A few things changed from the previous step:
- Two helper variables:
device_namegives you the device’s friendly name, andarea_namegives you the name of the areaAn area in Home Assistant is a logical grouping of devices and entities that are meant to match areas (or rooms) in the physical world: your home. For example, theliving roomarea groups devices and entities in your living room. [Learn more] it is assigned to. -
labelbuilds one formatted piece of text per device, combining the device name, the area (only if it is set), and the battery percentage. The~symbol glues text together. - An
if/elif/elsepicks between three messages based on how many items are in the list. No low batteries get a friendly “all healthy” message. One low battery uses singular wording. Two or more get the full list joined with commas.
Play with the template. Change the threshold, remove the %, reword the messages. The template editor updates as you type, so you can see the result of every change right away.
Step 4: Create the automation
Now turn the template into an actual automation.
- Go to Settings > Automations & scenes.
- Select Create automation, then Create new automation.
- Give it a name:
Low battery notification.
Add the trigger
You want this to run once every morning.
- Under When, select Add trigger.
- Choose Time.
- Set the time to something reasonable, like
09:00:00.
Add the action
- Under Then do, select Add action.
- Choose Call service, then pick your
notifyaction (likenotify.mobile_app_your_phone). - Fill in the message field with the template you built in step 3.
In YAML, the action looks like this:
- action: notify.mobile_app_your_phone
data:
title: "Battery check"
message: >
{% set low = namespace(batteries=[]) %}
{% for sensor in states.sensor
| selectattr('attributes.device_class', 'eq', 'battery')
| rejectattr('state', 'in', ['unknown', 'unavailable']) %}
{% if sensor.state | float(100) < 20 %}
{% set device = device_name(sensor.entity_id) %}
{% set area = area_name(sensor.entity_id) %}
{% set label = device ~ (' in ' ~ area if area else '')
~ ' (' ~ sensor.state ~ '%)' %}
{% set low.batteries = low.batteries + [label] %}
{% endif %}
{% endfor %}
{% if low.batteries | count == 0 %}
All batteries are healthy.
{% elif low.batteries | count == 1 %}
1 device needs a new battery: {{ low.batteries[0] }}.
{% else %}
{{ low.batteries | count }} devices need new batteries: {{ low.batteries | join(', ') }}.
{% endif %}
- Save the automation.
Step 5: Test it
You do not want to wait until tomorrow morning to find out if it works.
- Open your automation.
- Select the Run button (or use the three-dots menu and choose Run actions).
The notification should arrive on your phone within a few seconds. If it does not, check that the notify action name matches what you have on your setup, and look at the automation’s trace for clues.
Going further
A few ways to take this further:
-
Quiet “healthy” days. Skip the notification entirely when the list is empty. Put the “all healthy” message in an
ifbranch that does nothing, or use a template condition before the notification action. -
Different thresholds. Replace
20with a helper input so you can tune the threshold from the dashboard without editing the automation. - Weekly instead of daily. Change the time trigger to a more specific schedule like “every Monday at 9am”.
-
Include unavailable devices. The current template skips sensors that are
unknownorunavailable, but those might indicate a completely dead battery. You can add a second list for offline devices and mention them in the message. - Sort by percentage. If you have many devices, sort the list with the most-drained device first so you know what to replace first.
Next steps
- The Loops and conditions page explains the
forloop andif/elif/elseused here. - The Common template patterns page has more cookbook-style recipes.
- Try the Tutorial: show the average home temperature on your dashboard next to learn how to build a template sensorSensors return information about a thing, for instance the level of water in a tank. [Learn more].