Skip to content

Instantly share code, notes, and snippets.

Star You must be signed in to star a gist
Save sbyx/1f6f434f0903b872b84c4302637d0890 to your computer and use it in GitHub Desktop.
Home Assistant Blueprint: Low battery level detection & notification for all battery sensors
blueprint:
name: Low battery level detection & notification for all battery sensors
description: Regularly test all sensors with 'battery' device-class for crossing
a certain battery level threshold and if so execute an action.
domain: automation
input:
threshold:
name: Battery warning level threshold
description: Battery sensors below threshold are assumed to be low-battery (as
well as binary battery sensors with value 'on').
default: 20
selector:
number:
min: 5.0
max: 100.0
unit_of_measurement: '%'
mode: slider
step: 5.0
time:
name: Time to test on
description: Test is run at configured time
default: '10:00:00'
selector:
time: {}
day:
name: Weekday to test on
description: 'Test is run at configured time either everyday (0) or on a given
weekday (1: Monday ... 7: Sunday)'
default: 0
selector:
number:
min: 0.0
max: 7.0
mode: slider
step: 1.0
exclude:
name: Excluded Sensors
description: Battery sensors (e.g. smartphone) to exclude from detection. Only entities are supported, devices must be expanded!
default: {entity_id: []}
selector:
target:
entity:
device_class: battery
actions:
name: Actions
description: Notifications or similar to be run. {{sensors}} is replaced with
the names of sensors being low on battery.
selector:
action: {}
source_url: https://gist.github.com/sbyx/1f6f434f0903b872b84c4302637d0890
variables:
day: !input 'day'
threshold: !input 'threshold'
exclude: !input 'exclude'
sensors: >-
{% set result = namespace(sensors=[]) %}
{% for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') %}
{% if 0 <= state.state | int(-1) < threshold | int and not state.entity_id in exclude.entity_id %}
{% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state ~ ' %)'] %}
{% endif %}
{% endfor %}
{% for state in states.binary_sensor | selectattr('attributes.device_class', '==', 'battery') | selectattr('state', '==', 'on') %}
{% if not state.entity_id in exclude.entity_id %}
{% set result.sensors = result.sensors + [state.name] %}
{% endif %}
{% endfor %}
{{result.sensors|join(', ')}}
trigger:
- platform: time
at: !input 'time'
condition:
- '{{ sensors != '''' and (day | int == 0 or day | int == now().isoweekday()) }}'
action:
- choose: []
default: !input 'actions'
mode: single
@colmbuckley
Copy link

colmbuckley commented Jun 30, 2021

Great Blueprint.
Not sure what to add to the "Actions" part, I want to get a notification if any device have low battery.
Notification in HA Notifications area is good enough.

Action type: "Call Service"
Service: notify.notify
Fill in the message you want to be notified with.

(notify.persistent_notification to add a notification to the "notifications" section of the HAss app. notify.notify to send a push notification to your devices. you can also use something like notify.email to send emails.)

@Mollegarden
Copy link

Great Blueprint.

To get all my 22 battery powered devices, including those that displays voltage instead of percent, I modified the sensors part with this code:

sensors: >-
{% set result = namespace(sensors=[]) %}
{% for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') %}
{% if state.state | int < threshold and not state.state == 'unavailable' %}
{% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state ~ '%)'] %}
{% endif %}
{% endfor %}
{% for sensor in states.binary_sensor %}
{% if 'battery_level' in sensor.attributes %}
{% if ((state_attr(sensor.entity_id, 'battery_level')-2) |float/0.01)|round(1) | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ ((state_attr(sensor.entity_id, 'battery_level')-2)|float/0.01)|round(1) ~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{% for sensor in states.camera %}
{% if 'battery_level' in sensor.attributes %}
{% if state_attr(sensor.entity_id, 'battery_level') | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ state_attr(sensor.entity_id, 'battery_level')~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{{result.sensors|join('\n')}}

I also joined the result with '\n' instead of ', ' which displays one device per row when I do a persistent notification.

I have 2 actions:

service: persistent_notification.create
data:
title: Warning low battery level!
message: Low battery level{{ ':' }}{{ '\n' -}}{{sensors}}

service: notify.notify
data:
title: Warning low battery level!
message: 'Low battery level: {{sensors}}'

@AseKarlsson
Copy link

AseKarlsson commented Jul 1, 2021

Thanks Guys for helping me out, now both HA and email notification in place. So obvious after seeing your answers.

@justinlawrence
Copy link

Great Blueprint.

To get all my 22 battery powered devices, including those that displays voltage instead of percent, I modified the sensors part with this code:

sensors: >-
{% set result = namespace(sensors=[]) %}
{% for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') %}
{% if state.state | int < threshold and not state.state == 'unavailable' %}
{% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state ~ '%)'] %}
{% endif %}
{% endfor %}
{% for sensor in states.binary_sensor %}
{% if 'battery_level' in sensor.attributes %}
{% if ((state_attr(sensor.entity_id, 'battery_level')-2) |float/0.01)|round(1) | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ ((state_attr(sensor.entity_id, 'battery_level')-2)|float/0.01)|round(1) ~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{% for sensor in states.camera %}
{% if 'battery_level' in sensor.attributes %}
{% if state_attr(sensor.entity_id, 'battery_level') | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ state_attr(sensor.entity_id, 'battery_level')~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{{result.sensors|join('\n')}}

I also joined the result with '\n' instead of ', ' which displays one device per row when I do a persistent notification.

I have 2 actions:

service: persistent_notification.create
data:
title: Warning low battery level!
message: Low battery level{{ ':' }}{{ '\n' -}}{{sensors}}

service: notify.notify
data:
title: Warning low battery level!
message: 'Low battery level: {{sensors}}'

Super helpful, thank you!

@BeachyHeadCode
Copy link

Ecobee motion sensor puts the battery level in the attribute column not as a separate class. This blueprint does not work with this type of battery level logging.
image

@anarro
Copy link

anarro commented Aug 19, 2021

Great Blueprint.

To get all my 22 battery powered devices, including those that displays voltage instead of percent, I modified the sensors part with this code:

sensors: >-
{% set result = namespace(sensors=[]) %}
{% for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') %}
{% if state.state | int < threshold and not state.state == 'unavailable' %}
{% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state ~ '%)'] %}
{% endif %}
{% endfor %}
{% for sensor in states.binary_sensor %}
{% if 'battery_level' in sensor.attributes %}
{% if ((state_attr(sensor.entity_id, 'battery_level')-2) |float/0.01)|round(1) | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ ((state_attr(sensor.entity_id, 'battery_level')-2)|float/0.01)|round(1) ~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{% for sensor in states.camera %}
{% if 'battery_level' in sensor.attributes %}
{% if state_attr(sensor.entity_id, 'battery_level') | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ state_attr(sensor.entity_id, 'battery_level')~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{{result.sensors|join('\n')}}

I also joined the result with '\n' instead of ', ' which displays one device per row when I do a persistent notification.

I have 2 actions:

service: persistent_notification.create
data:
title: Warning low battery level!
message: Low battery level{{ ':' }}{{ '\n' -}}{{sensors}}

service: notify.notify
data:
title: Warning low battery level!
message: 'Low battery level: {{sensors}}'

Hi,

I made the modifications in a blueprint but when I receive the notification (Telegram) it shows me in a single line, how can I see it in two rows?

image

It's possible?

Thank you.

@anarro
Copy link

anarro commented Aug 20, 2021

Great Blueprint.
To get all my 22 battery powered devices, including those that displays voltage instead of percent, I modified the sensors part with this code:
sensors: >-
{% set result = namespace(sensors=[]) %}
{% for state in states.sensor | selectattr('attributes.device_class', '==', 'battery') %}
{% if state.state | int < threshold and not state.state == 'unavailable' %}
{% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state ~ '%)'] %}
{% endif %}
{% endfor %}
{% for sensor in states.binary_sensor %}
{% if 'battery_level' in sensor.attributes %}
{% if ((state_attr(sensor.entity_id, 'battery_level')-2) |float/0.01)|round(1) | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ ((state_attr(sensor.entity_id, 'battery_level')-2)|float/0.01)|round(1) ~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{% for sensor in states.camera %}
{% if 'battery_level' in sensor.attributes %}
{% if state_attr(sensor.entity_id, 'battery_level') | int < threshold and not sensor.state == 'unavailable' %}
{% set result.sensors = result.sensors + [sensor.name ~ ' (' ~ state_attr(sensor.entity_id, 'battery_level')~ '%)'] %}
{% endif %}
{% endif %}
{% endfor %}
{{result.sensors|join('\n')}}
I also joined the result with '\n' instead of ', ' which displays one device per row when I do a persistent notification.
I have 2 actions:
service: persistent_notification.create
data:
title: Warning low battery level!
message: Low battery level{{ ':' }}{{ '\n' -}}{{sensors}}
service: notify.notify
data:
title: Warning low battery level!
message: 'Low battery level: {{sensors}}'

Hi,

I made the modifications in a blueprint but when I receive the notification (Telegram) it shows me in a single line, how can I see it in two rows?

image

It's possible?

Thank you.

Hi,

I can get it with:

image

@colmbuckley
Copy link

I don't use Telegram for notifications, but the screenshot you shared looks very much as though you had written '/n' instead of '\n' in your join(). Are you sure the new version is the one which is being used?

@cweakland
Copy link

the 433MHz sensor send out 3 codes. I have seens on the forums users making thoer own automation to alert when the "Low Batt" code is recived from the Sensor via the RF Bridge.

example
12F70A - open
12F70E - closed
12F707 - tamper
12F706 - low battery
Ref: https://community.home-assistant.io/t/sonoff-dw1-433mhz-door-window-sensor-low-battery-warning-capable/43073/8
platform: mqtt
topic: rfbridge_A12345/tele/RESULT
condition:
condition: template
value_template: ‘"{{ trigger.payload_json.RfReceived.Sync > 10200 }}"’
condition: and
conditions:
condition: template
value_template: ‘"{{ trigger.payload_json.RfReceived.Data == ‘‘9CEA29’’ }}"’
action:
data:
message: change battery because sync is high for device code 9CEA29
service: notify.notify

Not sure if you figured this out, but you need to create a binary_sensor for these mqtt style battery high/low sensors. This is what one will look like:

  • platform: mqtt
    name: family_motion_battery
    state_topic: 'rtl_433/9b13b3f4-rtl433/devices/Interlogix-Security/motion/4cf72a/battery_ok'
    payload_off: "1"
    payload_on: "0"
    device_class: battery

@amitubale
Copy link

12F706

you have 433MHz sensor working via Sonoff RF Bridge ? and this binary_sensor config is working for you ?..On my RF bridge......the code it sends out for low batt is XXXX06 (XXXX is unique to a sensor, XXXX0A for Open/On & XXXX0E for closed/Off)..if you say its working for you then i will giv e it a try..but i dont see this topic sent out by my Tasmotized Sonoff RF Bridge "rtl_433/9b13b3f4-rtl433/devices/Interlogix-Security/motion/4cf72a/battery_ok"

@PrzemekSkw
Copy link

Ho to remove that blueprint. It don't work and after I delete it from schema tab and automation tab every restart I get notification:

Invalid config
The following integrations and platforms could not be set up:

automation
Please check your config and logs.

Logs:

Logger: homeassistant.config
Source: components/blueprint/models.py:211
First occurred: 12:23:08 (1 occurrences)
Last logged: 12:23:08

Invalid config for [automation]: Failed to load blueprint: Unable to find sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml (See /mnt/dietpi_userdata/homeassistant/configuration.yaml, line 10).
Traceback (most recent call last):
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/blueprint/models.py", line 209, in _load_blueprint
    blueprint_data = yaml.load_yaml(self.blueprint_folder / blueprint_path)
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/util/yaml/loader.py", line 110, in load_yaml
    with open(fname, encoding="utf-8") as conf_file:
FileNotFoundError: [Errno 2] No such file or directory: '/mnt/dietpi_userdata/homeassistant/blueprints/automation/sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/automation/config.py", line 106, in _try_async_validate_config_item
    config = await async_validate_config_item(hass, config, full_config)
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/automation/config.py", line 70, in async_validate_config_item
    return await blueprints.async_inputs_from_config(config)
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/blueprint/models.py", line 297, in async_inputs_from_config
    blueprint = await self.async_get_blueprint(bp_conf[CONF_PATH])
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/blueprint/models.py", line 275, in async_get_blueprint
    blueprint = await self.hass.async_add_executor_job(
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/concurrent/futures/thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/homeassistant/.pyenv/versions/3.9.7/lib/python3.9/site-packages/homeassistant/components/blueprint/models.py", line 211, in _load_blueprint
    raise FailedToLoad(
homeassistant.components.blueprint.errors.FailedToLoad: Failed to load blueprint: Unable to find sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml

How to delete this blueprint?
Regards.

@colmbuckley
Copy link

How to delete this blueprint?

You must still have an automation which uses it. Look through your automations for instances of this blueprint and remove any you find.

Given that the blueprint file is deleted, the automations will be disabled, but you need to remove them completely for this error to be resolved.

   Colm

@PrzemekSkw
Copy link

OK, thanks. I remove lines from automations.yaml file.
Regards.

@nofuse1971
Copy link

Was using but it suddenly stopped working. Tried to reinstall after deleting automation and blueprint and when I try to reinstall and configure I get an error “Message malformed: Missing input actions”

Help please

@romedtino
Copy link

I think it is because the selectors action and time on this have hanging {} removing those seem to fix the triggering for me.

@w1ll1am23
Copy link

I've been using this for awhile and just recently added a device thats reports battery as a voltage (two AA batteries reporting at 3.14v at the moment) I know I could just exclude this device, but wondering if there would be a way to only look at devices that have a unit of measure of %? (unit in my case is v)

@leonardus1973
Copy link

leonardus1973 commented Apr 24, 2022

{{ '\n' -}}

Is possible to use {{ '\n' -}} in telegram message? I have tried but nothing happen. thank you.

Another question:

Test is run at configured time either everyday (0) or on a given weekday (1: Monday ... 7: Sunday)

Value 0 for every day is not accepted (min is 1). why? Thank you.

@JackPo
Copy link

JackPo commented Jul 3, 2022

Is there a way to only get fire the action when {{sensors}} is not empty?

@Marck
Copy link

Marck commented Oct 19, 2022

Is there a way to only get fire the action when {{sensors}} is not empty?

This is fixed in my repo (it does send a message when testing) and doesn't errors when run with no batteries matching the percentage. This also ignores batteries on entities that have the mobile app (phones for example)

https://github.com/Marck/home-assistant-automation-blueprints

@howiehowie93
Copy link

This is a great blueprint - TYVM for posting it.

Is there a way to have it continually checking the level dropping below 20% instead of at one time daily ?

@nevesenin
Copy link

Hi!
I use this for a while and it worked great until some integrations (homematic, unifi) add sensors without device_class attribute. I get a UndefinedError: 'homeassistant.util.read_only_dict.ReadOnlyDict object' has no attribute 'device_class' error. I fixed that by adding another filter selectattr('attributes.device_class', 'defined') when iterating over sensors.

@jamesearl63
Copy link

Great Blueprint. Works great. I'm new to all of this so I hope I'm posting in the right place.

I get the following error in HA log when either making a change to the automation (i.e changing the time to trigger) or reloading the automation or restarting HA:

Logger: homeassistant.components.automation
Source: components/automation/config.py:207
Integration: Automation (documentation, issues)
First occurred: 11:55:17 AM (4 occurrences)
Last logged: 11:55:28 AM

Blueprint 'Low battery level detection & notification for all battery sensors' generated invalid automation with inputs OrderedDict([('threshold', 100), ('actions', [OrderedDict([('service', ''), ('data', OrderedDict())])])]): Service does not match format . for dictionary value @ data['action'][0]['default'][0]['service']. Got ''

I don't really understand the above. Please advise.

I generally just clear the log and the automation run without generating the above error.

@Sk1nk
Copy link

Sk1nk commented Mar 12, 2023

Hi, I am new to HA and stumbled across this (my first) template while reading through the subreddit. When I try to create an automation from the template I get:
„Message malformed: Missing input actions“
But I don‘t know how to add the sensors/buttons I want to test. Could someone help me?

@colmbuckley
Copy link

To be honest, I recommend at this point using this package for low battery alerting and sensing; it's more capable and extensible than this blueprint, and covers more of my use cases.

@EQNish
Copy link

EQNish commented Mar 13, 2023

I'm really kind of liking the idea of this, but I have not been able to get the percent to show anything other then 0%, and it doesn't see any of my door sensors ZSE41 or my Multi-sensors ZSE40

@thilander
Copy link

This is fantastic, thanks

@emanuelpsilva
Copy link

emanuelpsilva commented Aug 11, 2023

Awesome script sbyx 👍
Also, battery powered devices are prune to go from 80% into no communications (i.e. Shelly DW sensores). These scenarios will not be catch by the above script hence I wrote a very similar script that will check for the last interaction with these battery powered devices and report those above a certain threshold (i.e.: 1 day => 86400 seconds. Here is the script case someone is interested:

`

{% set result = namespace(devices=[]) %}

{% for device in states.sensor |rejectattr('attributes.device_class', 'undefined') | selectattr('attributes.device_class', '==', 'battery') %}
{% set age = (as_timestamp(now()) - as_timestamp(device.last_updated)) | int %}
{% if not (device.state == 'unavailable') and age > threshold %}
{% set result.devices = result.devices + [device.name ~ ' (' ~ (age / 3600) | int ~ 'h)'] %}
{% endif %}

{% endfor %}

{{result.devices|join(', ')}}

{% set result = namespace(devices=[]) %}
`

@kitty99hub
Copy link

Hi and many thanks for sharing this blue print.
I tested it and it works very well with the usual devices with a corresponding battery display in %
But now I'm facing a problem because I also have devices that don't use the device_class "battery" but "voltage"..... and they are devices with different voltage levels.
This means, for example, some devices work with a 1.5V battery and others with 3V or 9V and therefore all of these device/battery types reach their alarm value at a different voltage level.
For 1.5V devices this could be 1.2V, for 3V devices it could be 2.5V etc...

Now I'm asking myself whether I may have overlooked something that I could use to implement this with this blue print or whether it would be possible to integrate this function into this template?

Or are there other approaches for this?
I would be very happy about any help.
regards,

@KayGundhardt
Copy link

Hi sbyx,

Many thanks for your cool blueprint!

🫶

I must admit: before using it, I've spent a few dark hours in toilets in my life because I only realized too late that the battery of the motion detector was almost empty...

🙈

Anyway, I tried to create a blueprint for an open window notification based on your example, but I have to admit that I'm a real templating noob and have never created a blueprint before.

Of course it doesn't work and is certainly full of bugs: could you please help me out? That would be great!

blueprint:
  name: Open Window detection & notification for all contact sensors
  description: Regularly test all sensors with 'contact' device-class 
    for being open and if so execute an action.
  domain: automation
  input:
    time_period:
      name: Time period to test on
      description: Test is run at configured time period
      default: '5'
      selector:
        number:
          min: 1
          max: 30
          unit_of_measurement: 'min'
          mode: slider
          step: 1
    exclude:
      name: Excluded Sensors
      description: Contact sensors (e.g. smartphone) to exclude from detection. Only
        entities are supported, devices must be expanded!
      default:
        entity_id: []
      selector:
        target:
          entity:
            device_class: binary_sensor
    actions:
      name: Actions
      description: Notifications or similar to be run. {{sensors}} is replaced with
        the names of sensors being open.
      selector:
        action: {}
variables:
  time_period: !input time_period
  exclude: !input exclude
  sensors: "{% set result = namespace(sensors=[]) %} {% for state in states.sensor
    | selectattr('attributes.device_class', '==', 'binary_sensor') %}\n  {% if 'on' <= state.state
    | int and not state.entity_id in exclude.entity_id %}\n
    \   {% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state
    ~ ' %)'] %}\n  {% endif %}\n{% endfor %} {% for state in states.binary_sensor
    | selectattr('attributes.device_class', '==', 'binary_sensor') | selectattr('state',
    '==', 'on') %}\n  {% if not state.entity_id in exclude.entity_id %}\n    {% set
    result.sensors = result.sensors + [state.name] %}\n  {% endif %}\n{% endfor %}
    {{result.sensors|join(', ')}}"
trigger:
- platform: time_pattern
  minutes: /!input time_period
condition:
- '{{ is_state (sensors != '''', 'on') }}'
action:
- choose: []
  default: !input actions
mode: single

Thank you in advance!

Kay

@Oli-BY
Copy link

Oli-BY commented Feb 12, 2024

To be honest, I recommend at this point using this package for low battery alerting and sensing; it's more capable and extensible than this blueprint, and covers more of my use cases.

I cant import this blueprint

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment