Notification on Low Battery Levels

Thanks
Works right from the box

1 Like

@woodmj74 This is awesome! But I noticed my Hue motion sensor batteries are not showing up. I think it might be because battery level is an attribute but doesn’t show as an actual sensor/entity. Any idea how to incorporate this?

image

Thanks for the feedback @_Mike

Sorry not sure, I’m just a learner myself! The posts at the top describe nicely the route for battery attributes…combining the two I’m not sure about at the moment (but like a challenge, I’ll have a go and see if I can figure it out!!).

Short term you could have two automations, the MrNick solution for the attributes and the other for an entity ones. Like I said, I’ll have a crack at it and see what happens :slight_smile:

1 Like

Thanks @woodmj74. I’m going to try to get them merged into one automation on my end as well. Let me know if you figure it out, and I’ll do the same!

@woodmj74 I’m sure you can do better than this, but I did get it to work. I basically took your version and appended MrNick’s version to it. I ended up with some duplicates, so I had to screen them out of MrNick’s version. I set the threshold to 100 for my testing to make sure everything was coming through. Let me know if/when you come up with a WAY better version of what I did! :grin:

        message: >-
          {%- set ns = namespace(message = '') -%}
          {%- set threshold = 100 -%}
          {%- for battery in states.sensor -%}
          {%- if "battery" in battery.name | lower and battery.state | int < threshold and battery.state | int != 0 -%}
          {%- set ns.message = ns.message + '- ' + battery.name + ' (' + battery.state + '%)\n' -%}
          {%- endif -%}
          {%- endfor -%}
          
          {%- set low_batteries = states | selectattr('attributes.battery_level', 'defined') | selectattr('attributes.battery_level','<=', threshold ) -%} 
          {%- for battery in low_batteries -%}
          {%- if "light level" not in battery.name and "temperature" not in battery.name and "Door" not in battery.name -%}
          {%- set ns.message = ns.message + '- ' + battery.name + ' (' + (battery.attributes.battery_level | round(0) | string) + '%)\n' -%}
          {%- endif -%}
          {%- endfor -%}
          
          {{ ns.message }}

Do you know if there is an easy way to exclude a certain sensor?

@_Mike - not sure if I’ve improved or made things worse but had some fun along the way and learnt a lot of new stuff. If you’re at the same place as me, added some comments to what I’ve done which may help (or not!!). The net result is functionally nothing different other than building my knowledge.

I changed my approach here and did the following

  1. created a group of battery devices (in my groups.yaml) that I wanted to monitor, this includes the entities which are a battery level and those with a battery level attribute (the only ones I’ve got are mobile devices, wouldn’t normally include them but for testing added them in) - reason for this is I can specifically define the ones to include:
battery_levels:
  name: Battery Level Monitor
  entities:
    - sensor.office_switch_battery_level
    - sensor.lounge_switch_battery_level
    - sensor.utility_sensor_battery_level
    - sensor.under_stairs_battery_level
    - device_tracker.mike_wood_iphonex
    - device_tracker.deborah_s_iphone
  1. I then created a sensor in the configuration.yaml that keeps an eye on the battery levels, this sensor has a state of the count of battery devices below the threshold and also a payload in “matched devices” of the device name and battery level - effectively the logic that was originally in the automation - but this way I can also include the sensor in a Lovelace dashboard:
sensor:
  - platform: template
    sensors:  
    low_batteries:
        friendly_name: Low Battery Devices
        value_template: >
            {%- set ns = namespace(counter=0) -%} 
            {%- set threshold = 80 -%}
            {%- for item in expand('group.battery_levels') -%}
            {%- if "battery" in item.name | lower and item.state | int <= threshold -%}
            {%- set ns.counter = ns.counter + 1 -%}
            {%- endif -%}
            {%- endfor -%}
            {% set temp = expand('group.battery_levels')
            | selectattr('attributes.battery_level', 'defined')
            | selectattr('attributes.battery_level','<=', threshold ) %}
            {%- for item in temp -%}
            {%- set ns.counter = ns.counter + 1 -%}
            {%- endfor -%}
            {{ ns.counter }}
        attribute_templates:
          matched_devices: >
            {%- set threshold = 80 -%}
            {%- for item in expand('group.battery_levels') -%}
            {%- if "battery" in item.name | lower and item.state | int <= threshold -%}
            {{ item.name }} - {{ item.state }}:
            {%- endif -%}
            {%- endfor -%}
            {% set temp = expand('group.battery_levels')
            | selectattr('attributes.battery_level', 'defined')
            | selectattr('attributes.battery_level','<=', threshold ) %}
            {%- for item in temp -%}
            {{ item.name }} - {{ item.attributes.battery_level }}:
            {%- endfor -%}

Had a play around with how to refresh this sensor and came up with two options; one was to add in the time platform Time/Date and the following line in the sensor to refresh it every minute:

        entity_id: sensor.time # updates sensor every minute

However, some of the switches I noticed the battery level bounces around quite frequently so I opted to use the service: homeassistant.update_entity service to do it. For this I have another automation which every 20 minutes refreshes this sensor (and in my case another couple I built having found this), this way if the battery level drops low and then pings back up, assuming it didn’t happen on the refresh frequency time it would be ignored:

- id: '1598775093902'
  alias: System - Update Sensors (20 min refresh)
  description: ''
  trigger:
  - minutes: /20
    platform: time_pattern
  condition: []
  action:
  - data: {}
    entity_id:
    - sensor.unavailable_entities
    - sensor.under_stairs_temperature
    - sensor.low_batteries
    service: homeassistant.update_entity
  mode: single
  1. Then finally I have an automation built based around the state change on the low_batteries sensor, when this changes the automation runs with a condition to ignore a zero count (i.e. if it was previous two and then went to zero I don’t want to be told about that). But what I was pleased with was the much lighter message which took the matched devices from the sensor and outputs them.
- id: '1596619302402'
  alias: System - Low Battery Warning 
  description: ''
  trigger:
  - entity_id: sensor.low_batteries
    platform: state
  condition:
  - condition: template
    value_template: '{% if not is_state(''sensor.low_batteries'', ''0'') %}true{% endif
      %}'
  action:
  - data:
      message: '{%- set ns = namespace(message = ''The battery level of the following
        device(s) is low:\n'') -%}  {%- set ns.message = ns.message + state_attr(''sensor.low_batteries'',''matched_devices'').split('':'')
        | join(''% \n'')  -%} {{ ns.message }}'
      title: '** Battery Level Warning **'
    service: notify.mike
  mode: single

What I’m peeved at is being unable to check both the battery level attribute items and battery level entities in a single action within the ‘for’ loop. Something to keep plugging at.

Anyway, that’s where I’m at – sure there is better ways (and easier) out there which I’ll find one day but personally this has been a voyage of discovery!

1 Like

Thank you @woodmj74, this worked right away and I like how you can customize which sensors to include in the notification by adding them to the group.

I’m not as proficient in the coding, but is there a way where you don’t validate the name of the sensor (see below) and simply validate all the sensors that are in the group, regardless of what their name is?

{%- if “battery” in item.name | lower and item.state | int <= threshold -%}

I ask because some of my sensors didn’t have BATTERY in their name, and I had to modify them to include it, which was no big deal, but was wondering if your logic could be further simplified.

Also, would it be possible to expand on the automation that sends a message, so that it includes a message that, if none of the batteries are below the threshold, it either doesn’t send a message, or the message itself contains something to the effect of, “Your batteries are A-OK!”?

Much appreciated!

@celoberger (just to point out I’m finding my way also so don’t take this necessarily as the best advice!!)

I have battery power devices that 1) represent their battery levels as an entity such as a sensor and 2) represent their battery level as an attribute of the entity such as a mobile phone:

image image

That’s why I’ve two different techniques in the sensor to spot them.

If you’re only tracking devices with a battery entity another approach could be instead of using battery in the name, try using the device class (which is an attribute of those):

image

Something like this I think would get those without the name having to contain battery (although if my assumptions are correct and these are entities, having battery in the name makes life easier for understanding your set up):

{%- set ns = namespace(counter=0) -%} 
{%- set threshold = 20 -%}
{% set temp = expand('group.battery_levels')
| selectattr('attributes.device_class', 'eq', 'battery') %}
{%- for item in temp -%}
{%- if item.state | int <= threshold -%}
{%- set ns.counter = ns.counter + 1 -%}
{%- endif -%}
{%- endfor -%}
{{ ns.counter }}

For those with battery level attributes, the other method works without any reference to the entity name so you should be fine

I specifically added a condition to not notify me if the batteries are fine. The sensor updates every 20 minutes in my case so won’t want bombarding all day with okay messages (you could change that to daily if you wished for example).

I’d approach that by removing the condition and adding a choose criteria to the automation (one choice being is the sensor above zero (i.e. we have some low batteries) and the second below one (i.e. we have no low batteries so it’s a-okay)):

(disclaimer, not tested this but the principle should be fine!)

- id: '1596096097125'
  alias: xxTest
  description: ''
  trigger:
  - entity_id: sensor.low_batteries
    platform: state
  condition: []
  action:
  - choose:
    - conditions:
      - above: '0'
        condition: numeric_state
        entity_id: sensor.low_batteries
      sequence:
      - data:
          message: '{%- set ns = namespace(message = ''The battery level of the following
            device(s) is low:\n'') -%}  {%- set ns.message = ns.message + state_attr(''sensor.low_batteries'',''matched_devices'').split('':'')
            | join(''% \n'')  -%} {{ ns.message }}'
          title: '** Battery Level Warning **'
        service: notify.email
    - conditions:
      - below: '1'
        condition: numeric_state
        entity_id: sensor.low_batteries
      sequence:
      - data:
          message: Batteries are a-okay
        service: notify.mike

Hi,

Phew a lot of code here :stuck_out_tongue:
Anyway, im trying to do the same, to get notifications when my batteries run low.
What i did so far, is using the battery state card and included some filter, and renamed them in bulk

filter:
                include: # filters for auto-adding
                  - name: entity_id # entities which id ends with "_battery_level"
                    value: "*_battery_level"
                  - name: entity_id # entities which id ends with ".battery_level"
                    value: "*.battery_level"  
        bulk rename:
          - from: "Battery Level" # simple string replace (note: "to" is not required if you want to remove string)
            to: "sensor" 

Now how do i get those with value: “_battery_level" and with value: ".battery_level” to give notifications?
Like said before here, i have entitties showing the battery state, but i have entities too where the battery is an attribute

Thx for pointing me in the right way

@skank - made any headway, not clear what exactly you’re up to but interested. I assume you’re trying to build a dashboard here, is it a custom card? Any chance you can post the full context?

So, some new lessons learnt….

Changes in 0.115 mean that the sensors I’d set up get automatically updated meaning the call from service: homeassistant.update_entity isn’t needed any longer.

This then highlighted a gap in my knowledge.

  • The State trigger in my automation using sensor.low_batteries didn’t carry a ‘To’ or ‘From’ value
  • The sensor is updating on demand now, as the batteries ran down the matched value attribute was being updated each time that happened (instead of every 20 minutes)
  • I’ve now learnt that the trigger of state in an automation, without a To and From, will also check the attributes

So whilst the state could remain, for example, at 1 with a matched value attribute of “something battery 10%”, it would update to state 1 matched value attribute “something battery 9%” etc. etc. as the battery ran down, therefore triggering the automation each time.

Not my desired result.

So, added a new condition that the state triggered value must not be the same as the previous state triggered value. Might effect some of you on the same journey as me….

- id: '1596619302402'
  alias: System - Low Battery Warning
  description: ''
  trigger:
  - entity_id: sensor.low_batteries
    platform: state
  condition:
  - condition: not
    conditions:
    - condition: state
      entity_id: sensor.low_batteries
      state: '0'
  - condition: template
    value_template: '{{ trigger.from_state.state != trigger.to_state.state }}'
  action:
  - data:
      message: '{%- set ns = namespace(message = ''The battery level of the following
        device(s) are low:\n'') -%}  {%- set ns.message = ns.message +  state_attr(''sensor.low_batteries'',''matched_devices'').split('':'')
        | join(''% \n'')  -%} {{ ns.message }}'
      title: '** Battery Level Warning **'
    service: notify.mike
  mode: single
1 Like

Thank you @woodmj74 for updating your thread with updates to the code. This automation has been really useful to me, and it has actually prompted me to think of a similar automation, but for a different purpose.

I can submit a new post, but I feel the implementation would be very similar, so I’ll pose you this question/challenge here, and maybe you’ll also have a similar use case.

I have a bunch of wyzesense sensors for windows and doors. I also use Life360 to track my wife’s and my location. I’ve created a binary sensor grouping of my windows, which simply indicates Open or Closed if any of the windows in that group are open, or if ALL are closed.

#Grouping of Windows
  - platform: template
    sensors:  
      window_status:
        friendly_name: 'Window Status'
        device_class: window
        value_template: >
          {% set sensors = [states.binary_sensor.wyzesense_basement_window,
                            states.binary_sensor.wyzesense_basementroom_window,
                            states.binary_sensor.wyzesense_kid1_window,
                            states.binary_sensor.wyzesense_kid2_window,
                            states.binary_sensor.wyzesense_mudroom_window,
                            states.binary_sensor.wyzesense_office_window,
                            states.binary_sensor.wyzesense_master1_window,
                            states.binary_sensor.wyzesense_master2_window,
                            states.binary_sensor.wyzesense_masterbath1_window,
                            states.binary_sensor.wyzesense_masterbath2_window,
                            states.binary_sensor.wyzesense_tvroom1_window,
                            states.binary_sensor.wyzesense_tvroom2_window,
                            states.binary_sensor.wyzesense_fronthouse1_window,
                            states.binary_sensor.wyzesense_fronthouse2_window,
                            states.binary_sensor.wyzesense_bay1_window,
                            states.binary_sensor.wyzesense_bay2_window,
                            states.binary_sensor.wyzesense_bay3_window] %}
          {{ sensors | selectattr('state','eq','on') | list | count > 0 }} 

I also have a binary sensor that groups both users, and will have the value ON if either person, or both are home, and OFF when both are away.

#Grouping of Life360 users
      someone_home:
        friendly_name: "Is someone home" 
        icon_template: >-
          {% if is_state('binary_sensor.someone_home','on')%}
            mdi:home-account
          {% else %}
            mdi:home-outline
          {% endif %}
        value_template:
          "{{is_state('device_tracker.life360_husband_berger', 'home') or
             is_state('device_tracker.life360_wife_berger','home')}}"

I then have a simple automation that notifies me if any of the windows are open when both of us leave the house.

alias: Notify if Windows are Open and We Left the House
  description: ''
  trigger:
  - entity_id: binary_sensor.window_status
    platform: state
    to: open
  - entity_id: binary_sensor.someone_home
    from: 'on'
    platform: state
    to: 'off'
  condition: []
  action:
  - data:
      message: You left windows open 
    service: notify.notify
  mode: single			 

All of this works perfectly, but I’d like to refine it. I’d like my notification to tell me WHICH windows I left open when I leave the house, and that’s where I feel your solution, with the necessary adaptations to deal with the window sensors, would be a nice fit.

Do you think you could shed some light on what those adaptations should be in order to make this work?

Again, I’m happy to open a new thread if you feel this is deviating too much from the original intention of this post, but I at least wanted to get this out there.

As always, appreciate any feedback you and the community can provide.

All the best!

Have you thought about this, instead of a sensor using a group - when one member of the group is on, the group is on, when all members are off the group is off

group.yaml

windows:
  name: Window Sensors
  entities:
    - binary_sensor.wyzesense_basement_window
    - binary_sensor.wyzesense_basementroom_window
    - binary_sensor.wyzesense_kid1_window

....etc...

Then your action in the automation could be:

  action:
  - service: notify.notify
    data:
      message: '{%- set ns = namespace(message = ''The following window(s) are open:\n'')
        -%} {%- for item in expand(''group.windows'') -%} {% if item.state == ''on''
        -%} {%- set ns.message = ns.message + '' - '' + item.name + '' \n''  -%} {%-
        endif -%} {%- endfor -%}  {{ ns.message }}'
      title: '** Open Window Warning **'

You might want to think about the logic on the triggers, I fell into this trap a bit ago. The triggers are OR not AND. Yours currently would read, if we’re not home OR the windows are open. So it would trigger if you left the home regardless of the windows being open. Perhaps you might want to check this and consider something a bit like:

- id: '1596277325558'
  alias: xTest
  description: Test only.....
  trigger:
  - platform: state
    entity_id: binary_sensor.someone_home
    from: 'on'
    to: 'off'
  condition:
  - condition: state
    entity_id: group.windows
    state: 'on'
  action:
  - service: notify.notify
    data:
      message: '{%- set ns = namespace(message = ''The following window(s) are open:\n'')
        -%} {%- for item in expand(''group.windows'') -%} {% if item.state == ''on''
        -%} {%- set ns.message = ns.message + '' - '' + item.name + '' \n''  -%} {%-
        endif -%} {%- endfor -%}  {{ ns.message }}'
      title: '** Open Window Warning **'
  mode: single

This way when the house is empty, it will check if the windows are open, and if they are send the notification…(I may have missed the intent but got stuck myself a while ago here)

Edit: had the wrong group name in the message!

1 Like

Will this work for zwave and Zigbee devices?

You @woodmj74 are a good man from long line of good ancestors. This worked like a peach!

image

Can’t thank you enough!

1 Like

Hi, thx for replying.

At the moment i use the frontend to show the battery states indeed, using a custom card, here’s the code

title: Energie
icon: mdi:power-plug
path: Energie
panel: true
cards: 
  - type: custom:layout-card
    layout: vertical
    min_columns: 1
    max_columns: 3
    cards:
      - type: custom:battery-state-card
        title: "Batterij niveaus"
        color_gradient:
          - "#ff0000" # red
          - "#ffff00" # yellow
          - "#00ff00" # green
        filter:
                include: # filters for auto-adding
                  - name: entity_id # entities which id ends with "_battery_level"
                    value: "*_battery_level"
                  - name: entity_id # entities which id ends with ".battery_level"
                    value: "*.battery_level"  
        bulk rename:
          - from: "Battery Level" # simple string replace (note: "to" is not required if you want to remove string)
            to: "sensor" 

But what i want to do now… is get notification (through android messages and notifications in the frontend) as soon as the battery goes to low, say 10%
It would rock if it tells me which battery (or multiple) it is too…

Hi, your card configuration in Lovelace won’t drive notifications or other actions (to the best of my knowledge), you’re simply doing presentation logic in what you’ve described.

I would be looking to put a separate automation in similar to the ones we’ve hacked/described/edited above to handle the notifications. You could probably lift one of the these and be up an running.

Shout if you get stuck doing this (I struggled initially so happy to help :slight_smile: )

Ok and which one would you advise to start with? There are a lot here :wink:
Which post?

Looking at your lovelace config I assume all the batteries you’re interested in are entities and not battery attributes - I would do the following (similar to the above but with minor and continuing tweaking!) …

  1. create a group in groups.yaml
battery_levels:
  name: Battery Level Monitor
  entities:
    - sensor.office_switch_battery_level
    - sensor.lounge_switch_battery_level
    - sensor.utility_sensor_battery_level
    - sensor.under_stairs_battery_level
    - sensor.lounge_light_level_battery_level
    - sensor.lounge_temperature_battery_level  ...etc...
  1. create a sensor (configuration.yaml) that when a battery in the group drops below a threshold counts them. It also drops their name and current level in an attribute we use in the notify later:
sensor:
  - platform: template
    sensors:
      low_batteries:
        friendly_name: Low Battery Devices
        unit_of_measurement: Entities
        icon_template: "{{ 'mdi:checkbox-marked-circle-outline' if states('sensor.low_batteries')|int == 0 else 'mdi:alert-circle-outline' }}"
        value_template: >
            {%- set ns = namespace(counter=0) -%} 
            {%- set threshold = 10 
            {%- for item in expand('group.battery_levels') -%}
            {%- if item.state | int <= threshold -%}
            {%- set ns.counter = ns.counter + 1 -%}
            {%- endif -%}
            {%- endfor -%}
            {{ ns.counter }}
        attribute_templates:
          matched_devices: >
            {%- set threshold = 10 -%}
            {%- for item in expand('group.battery_levels') -%}
            {%- if item.state | int <= threshold -%}
             - {{ item.name }} - {{ item.state }}|
            {%- endif -%}
            {%- endfor -%}
  1. Create an automation to send the notification when the sensor is greater than zero:
- id: '1596619302402'
  alias: System - Low Battery Warning
  description: ''
  trigger:
  - entity_id: sensor.low_batteries
    platform: state
    for: 00:01:00
  condition:
  - condition: not
    conditions:
    - condition: state
      entity_id: sensor.low_batteries
      state: '0'
  - condition: template
    value_template: '{{ trigger.from_state.state != trigger.to_state.state }}'
  action:
  - data:
      message: '{%- set ns = namespace(message = ''The battery level of the following
        device(s) are low:\n'') -%}  {%- set ns.message = ns.message +  state_attr(''sensor.low_batteries'',''matched_devices'').split(''|'')
        | join(''% \n'')  -%} {{ ns.message }}'
      title: '** Battery Level Warning **'
    service: notify.mike
  mode: single
  • the trigger checks that the sensor value has changed (from anything to anything)
  • the 1 minute For in the trigger ensures this doesn’t fire when restarting HomeAssistant and gives enough time for the sensors to refresh
  • the condition not 0 (zero) suppresses the notification if the sensor returns to zero from above zero
  • the other condition of from_state != to_state is to catch the situation where the sensor state is the same (e.g. 1 low battery) but the battery level has reduced so the attributes have changed (matched devices)
1 Like