Howto create battery alert without creating a template for every device

Okay, this is absolutely beautiful. I tried playing around with it to see if I could figure it out on my own with no luck. So, how would one extract the friendly name for the sensor that falls below the threshold? Also, how often would this poll checking for battery levels?

Sorry, I completely misread your first post, I thought you were having problems getting it working.

1 Like

this is great thank you…

I updated my post. For friendly names, just use item.attributes.friendly_name instead of item.name.

I honestly don’t know how often it’ll run, probably every single event loop (i.e. a lot). I don’t know how to reduce that without either specifying every entity to track in the template sensor or duplicating the template in both the condition and action. Maybe we can get the template sensor updated to allow a config option that specifies how often it should update.

1 Like

Here’s a bit of a hack to configure how often it updates:

First, you need to create some entity that will change states to trigger the update. I used an input_boolean:

input_boolean:
  update_low_battery:

Then set the template sensor to only update when that entity changes states:

sensor:
- platform: template
  sensors:
    low_battery:
      ....(see previous comment)...
      entity_id:
      - input_boolean.update_low_battery

Finally, add an automation to toggle the state of the entity whenever you want the template sensor to update:

- alias: Update Low Battery
  trigger:
  - platform: time
    minutes: '/30'
  action:
  - alias: Update Low Battery
    service: input_boolean.toggle
    data:
      entity_id: input_boolean.update_low_battery

In this example, input_boolean.update_low_battery will toggle states every 30 minutes, which then triggers sensor.low_battery to update. You could also set it up to update just before your alert automation runs as well if you wanted. That way you only update the sensor right before the automation runs to check for low battery.

6 Likes

@tboyce1, thanks so much! That’s definitely a lot cleaner and I like having that persistent notification. I made a few (mostly cosmetic) tweaks to what you sent. The trigger on your “Update Low Battery” automation needs a seconds: 00 attribute to keep it from running every second when the minutes match. I changed the condition on the automation to prevent empty alerts when there are no batteries. Finally, I added the current battery level to the notification.

I’ve got a few more ideas that can make this better, but I wanted to share the latest code that’s working for me.

input_boolean:
  battery_status_update:
    name: Battery Status Update

sensor:
- platform: template
  sensors:
    battery_status:
      friendly_name: "Battery Status"
      entity_id:
      - input_boolean.battery_status_update
      value_template: >
        {%- set threshold = 40 -%}
        {%- set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] -%}
        {%- for domain in domains -%}
        {%- for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
        {%- if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
        {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
        {%- if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){%- if not loop.last %}, {% endif -%} {% endif -%}
        {%- endfor -%}
        {%- endfor -%}

automation:
- alias: Battery Status Update
  trigger:
  - platform: time
    minutes: '/5'
    seconds: 00
  action:
  - alias: Battery Status Update
    service: input_boolean.toggle
    data:
      entity_id: input_boolean.battery_status_update

- alias: 'Battery Alert'
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  condition:
    condition: template
    value_template: "{% if states('sensor.battery_status') %}{{ true }}{% else %}{{ false }}{% endif %}"
  action:
    - service: persistent_notification.create
      data_template:
        title: Low Battery levels
        message: "{{ states('sensor.battery_status') }}"
        notification_id: low-battery-alert
    - service: notify.slack_notify
      data_template:
        message: >-
          These devices have low battery levels: {{ states('sensor.battery_status') }}
16 Likes

This is a great idea!

I took the last change from @NotoriousBDG and converted it into a package that can pretty much just be dropped in and will work. Just update the notification to your preference.

https://hastebin.com/ligoxabema.coffeescript

4 Likes

Awesome @sjabby! You beat me to it. That’s what I was planning to attempt next :slight_smile:. I’ve never created a package before, so this saved me a lot of time.

Cleaned up even more with help from @skalavala !

Removed the need for input boolean and separate sensor. Put the template code within the automation itself.


################################################################
## Packages / Battery levels
################################################################

################################################
## Customize
################################################

homeassistant:
  customize:
    ################################################
    ## Node Anchors
    ################################################

    package.node_anchors:
      customize: &customize
        package: 'battery_alert'

      expose: &expose
        <<: *customize
        haaska_hidden: false
        homebridge_hidden: false

    ################################################
    ## Group
    ################################################

    group.battery_alert:
      <<: *customize
      friendly_name: "Battery Alert"
      icon: mdi:steam

    ################################################
    ## Automation
    ################################################

    automation.battery_alert:
      <<: *customize
      friendly_name: "Battery Alert"

################################################
## Group
################################################

group:
  battery_alert:
    control: hidden
    entities:
      - automation.battery_alert

################################################
## Automation
################################################

automation:
- alias: battery_alert
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  condition:
    - condition: template
      value_template: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level() |trim != "" }}
  action:
    - service: persistent_notification.create
      data_template:
        title: Low Battery levels
        notification_id: low-battery-alert
        message: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level() }}
    - service: notify.pushover
      data_template:
        title: "Battery status"
        message: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level()}}


14 Likes

Very neat, great work, time to give it a test :slight_smile:

@sjabby can you help me understand what the Node Anchors do? I have never seen that before and not sure what homebridge is.

Node Anchors is used to simplify your configurations and avoid repetitions of settings. So instead of repeting all settings for each entity you just add an anchor to the setting. Example:

Without anchors:

################################################
## Without Node Anchors and Merge Key Tags
################################################

homeassistant:
  customize:
    light.back_porch:
      friendly_name: "Back Porch"
      package: 'philips_hue'

    light.chandelier:
      friendly_name: "Chandelier"
      package: 'philips_hue'

    light.dining_room:
      friendly_name: "Dining Room"
      package: 'philips_hue'

    light.entry_lamp:
      friendly_name: "Entry Lamp"
      package: 'philips_hue'

    light.front_porch:
      friendly_name: "Front Porch"
      package: 'philips_hue'

The same config with anchors:

################################################
## With Node Anchors and Merge Key Tags
################################################

homeassistant:
  customize:
    package.node_anchors: # This is just a dummy entry
      customize: &customize # Also a dummy entry that allows us to define the node anchor
        package: 'philips_hue'

    light.back_porch:
      <<: *customize # This merges the keys/values from "&customize"
      friendly_name: "Back Porch"

    light.chandelier:
      <<: *customize
      friendly_name: "Chandelier"

    light.dining_room:
      <<: *customize
      friendly_name: "Dining Room"

    light.entry_lamp:
      <<: *customize
      friendly_name: "Entry Lamp"

    light.front_porch:
      <<: *customize
      friendly_name: "Front Porch"

Full example from @dale3h: https://github.com/dale3h/homeassistant-config/blob/master/examples/yaml_anchors.yaml

5 Likes

That’s perfect! Thanks @sjabby, @skalavala, and @tboyce1 for all your help.

Here’s an update to the package with an alert that automatically clears the persistent notification when the low batteries are fixed.

################################################################
## Packages / Battery levels
################################################################

################################################
## Customize
################################################

homeassistant:
  customize:
    ################################################
    ## Node Anchors
    ################################################

    package.node_anchors:
      customize: &customize
        package: 'battery_alert'

      expose: &expose
        <<: *customize
        haaska_hidden: false
        homebridge_hidden: false

    ################################################
    ## Group
    ################################################

    group.battery_alert:
      <<: *customize
      friendly_name: "Battery Alert"
      icon: mdi:steam

    ################################################
    ## Automation
    ################################################

    automation.battery_alert:
      <<: *customize
      friendly_name: "Battery Alert"

    automation.battery_alert_clear:
      <<: *customize
      friendly_name: "Battery Alert Clear"

################################################
## Group
################################################

group:
  battery_alert:
    control: hidden
    entities:
      - automation.battery_alert
      - automation.battery_alert_clear

################################################
## Automation
################################################

automation:
- alias: battery_alert
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  condition:
    - condition: template
      value_template: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level() |trim != "" }}
  action:
    - service: persistent_notification.create
      data_template:
        title: "Low Battery levels"
        notification_id: low-battery-alert
        message: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level() }}
    - service: notify.slack_notify
      data_template:
        message: "Low Battery Levels"
        data:
          attachments:
          - color: '#52c0f2'
            title: "These devices have low battery levels"
            text: >
              {% macro battery_level() %}
              {%- set threshold = 40 -%}
              {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
              {% for domain in domains -%}
              {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
              {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
              {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
              {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
              {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
              {% endfor %}
              {%- endfor %}
              {% endmacro %}
              {{ battery_level() }}

- alias: battery_alert_clear
  trigger:
    - platform: time
      minutes: '/30'
      seconds: 00
  condition:
    - condition: template
      value_template: >
          {% macro battery_level() %}
          {%- set threshold = 40 -%}
          {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock'] %}
          {% for domain in domains -%}
          {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) or ("battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown"))) -%}
          {% if (item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{% endif -%}
          {% if "battery" in item.name | lower and ((item.state | int < threshold and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {%- endif %} {% endif -%}
          {% endfor %}
          {%- endfor %}
          {% endmacro %}
          {{ battery_level() |trim == "" }}
  action:
    - service: persistent_notification.dismiss
      data:
        notification_id: low-battery-alert
8 Likes

The fantastic power of collaboration :slight_smile: Nice work @NotoriousBDG

1 Like

This is great. Anyway to have a space separating the various entities that are pulled up by the automation in the persistant notification box?

There should already be a comma and space between each item like this Sensor 1 (32), Sensor 2 (30), Sensor 3 (Low). What does yours look like?

Here is the screenshot.

29 PM

works perfectly! Thanks!

Brilliant work thanks to everyone involved. I need to lower the thresholds as I have Xiaomi sensors as well and most of them are shown as low :smiley: