Howto create battery alert without creating a template for every device

I’ve been searching for ages to try and work out what the node_anchors bit is all about and come up completely blank. Only the devs seem to use them, so I don’t know whether it helps for debugging something or other, but there doesn’t seem to be any advantage.

Example:

  • the above package (with comments removed)
homeassistant:
  customize:

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

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

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

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

    automation.battery_alert_clear:
      <<: *customize
      friendly_name: "Battery Alert Clear"
  • Is no different in behaviour that I can see from doing it like this:
homeassistant:
  customize:

    group.battery_alert:
      friendly_name: "Battery Alert"
      icon: mdi:steam

    automation.battery_alert:
      friendly_name: "Battery Alert"

    automation.battery_alert_clear:
      friendly_name: "Battery Alert Clear"

… And the latter is 10 lines fewer code.

Anybody enlighten us?

(also, from a code reduction point of view, the group name and automation names could be set as part of their declaration, which would reduce the customize section to only 2 lines (to set the icon for the group)

1 Like

I don’t completely understand the differences between the 2 formats either. I guess it’s a matter of personal preference or perhaps a more complex package would make the reasons more obvious…

Would love a definitive answer, if nothing else to satisfy my curiosity :joy::joy:

@CCOSTAN , @dale3h I’m sure I’ve seen you guys use the node_anchors dooberies in your packages?

Scratch that… I did create the package version, though with the earlier code (haven’t tried your latest) and triggering manually does work sending to my notify.android_alerts and it shows the “Low Battery Levels [Dismiss]” card without any sensors.

core-ssh:/config# vim includes/packages/battery_alert.yaml 
################################################################
## 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: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = 0 -%}
        {% macro battery_level() %}
        {% set domains = ['light', 'switch', 'sensor', 'zwave', 'lock', 'device_tracker'] %}
        {% for domain in domains -%}
        {% for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold_high and item.attributes['battery_level'] | int > threshold_low) or ("battery" in item.name | lower and ((item.state | int < threshold_high and item.state | int > threshold_low 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_high and item.attributes['battery_level'] | int > threshold_low) -%}
        {{ item.name }}{% endif -%}
        {% if "battery" in item.name | lower and ((item.state | int < threshold_high and item.state | int > threshold_low and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }}{% endif -%}
        {% endfor %}
        {{ item.name }}{% endif -%}
        {% if "battery" in item.name | lower and ((item.state | int < threshold_high and item.state | int > threshold_low and item.state|int != 0) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }}{% endif -%}
        {% endfor %}
        {%- endfor %}
        {% endmacro %}
        {{ battery_level() |trim != "" }}
  action:
    - service: persistent_notification.create
      data_template:
        title: "Low Battery levels"
        notification_id: low-battery-alert
        message: >
          {%- set threshold_high = 40 -%}
          {%- set threshold_low = 0 -%}
          {% macro battery_level(domain) %}
          {%- for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold_high and item.attributes['battery_level'] | int > threshold_low) or ("battery" in item.name | lower and ((item.state | int < threshold_high 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_high and item.attributes['battery_level'] | int > threshold_low) -%}
          {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{%- endif -%}
          {%- if "battery" in item.name | lower and ((item.state | int < threshold_high and item.state | int > threshold_low 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 -%}
          {% endmacro %}
          {%- set domains = ['light', 'switch', 'sensor', 'zwave', 'lock', 'device_tracker'] -%}
          {%- for domain in domains if battery_level(domain) |trim != ""-%}
          {{ battery_level(domain) }}{%- if not loop.last %}, {% endif -%}
          {%- endfor -%}
    - service: notify.android_alerts
      data_template:
        message: "Low Battery Levels"
        data:
          attachments:
          - color: '#52c0f2'
            title: "These devices have low battery levels"
            text: >
              {%- set threshold_high = 40 -%}
              {%- set threshold_low = 0 -%}
              {% macro battery_level(domain) %}
              {%- for item in states[domain] if ((item.attributes.battery_level is defined and item.attributes['battery_level'] | int < threshold_high and item.attributes['battery_level'] | int > threshold_low) or ("battery" in item.name | lower and ((item.state | int < threshold_high 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_high and item.attributes['battery_level'] | int > threshold_low) -%}
              {{ item.name }} ({{ item.attributes['battery_level'] }}){%- if not loop.last %}, {% endif -%}{%- endif -%}
              {%- if "battery" in item.name | lower and ((item.state | int < threshold_high and item.state | int > threshold_low 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 -%}
              {% endmacro %}
              {%- set domains = ['light', 'switch', 'sensor', 'zwave', 'lock', 'device_tracker'] -%}
              {%- for domain in domains if battery_level(domain) |trim != ""-%}
              {{ battery_level(domain) }}{%- if not loop.last %}, {% endif -%}
              {%- endfor -%}

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

Triggering the automation manually will send an empty notification because it skips the condition that prevents it. You shouldn’t get the notification if it’s triggered by the automation’s trigger.

I’ve done some refactoring of the package to add some new features and fix a couple of issues. The biggest improvement is that it automatically creates a separate sensor for each entity that has an attribute named battery_level or battery numeric. This new feature leverages MQTT Discovery to create the battery sensors.

I also split out the Slack and persistent notification into 2 automations. the Slack one runs twice daily and the persistent notification one runs every 15 minutes. For anyone that doesn’t use Slack or uses some other notification component, it should be easier to disable and/or modify.

It’s quite a big change and I haven’t done extensive testing, so there might be issues that I haven’t encountered yet. The biggest issue that I don’t like is the icon isn’t based on battery level, and I don’t want to create templates just to set the icons. I think adding the ability to set the icon via MQTT Discovery would be the best option, but that’s not available today.

This version of the package now requires MQTT and MQTT Discovery to be enabled. I’ve hard coded the prefix to homeassistant in the package, so you may need to change that if you want to use something different. To enable MQTT DIscovery, add discovery: true and discovery_prefix: homeassistant under the mqtt: section of your configuration.yaml.

mqtt:
  discovery: true
  discovery_prefix: homeassistant

Finally, here is the new battery_alert.yaml package.

################################################################
## Packages / Battery Alert
################################################################

################################################################
## 1. Enable MQTT using your preferred MQTT broker
##    https://home-assistant.io/components/mqtt/
##
## 2. Enable MQTT Discovery by adding `discovery: true` and
##    `discovery_prefix: homeassistant` under the `mqtt:` section
##    of your configuration.yaml
##
##    mqtt:
##      discovery: true
##      discovery_prefix: homeassistant
##
## 3. Save this file as CONFIGDIR/package/battery_alert.yaml
##
## 4. Add `packages: !include_dir_named packages` under the
##    `homeassistant:` section of your configuration.yaml
##
##    homeassistant:
##      packages: !include_dir_named packages
################################################################

################################################
## 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_notification:
      <<: *customize
      friendly_name: "Battery Notification"

    automation.battery_notification_clear:
      <<: *customize
      friendly_name: "Battery Notification Clear"

    automation.battery_alert_slack:
      <<: *customize
      friendly_name: "Battery Slack Notification"

    automation.battery_sensor_from_battery_level_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery_level Attribute"

    automation.battery_sensor_from_battery_numeric_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery numeric Attribute"

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

group:
  battery_alert:
    control: hidden
    entities:
      - automation.battery_notification
      - automation.battery_notification_clear
      - automation.battery_alert_slack
      - automation.battery_sensor_from_battery_level_attribute
      - automation.battery_sensor_from_battery_numeric_attribute

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

automation:
- alias: battery_notification
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - service: persistent_notification.create
      data_template:
        title: "Low Battery levels"
        notification_id: low-battery-alert
        message: >
          {%- set threshold_high = 40 -%}
          {%- set threshold_low = -1 -%}
          {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower) %}
          {%- if (((item.state is number or item.state | length == item.state | int | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
          {%- endfor -%}

- alias: battery_notification_clear
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim == "" }}
    - service: persistent_notification.dismiss
      data:
        notification_id: low-battery-alert

- alias: battery_alert_slack
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - service: notify.slack_notify
      data_template:
        message: "Low Battery Levels"
        data:
          attachments:
          - color: '#52c0f2'
            title: "These devices have low battery levels"
            text: >
              {%- set threshold_high = 40 -%}
              {%- set threshold_low = -1 -%}
              {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower) %}
              {%- if (((item.state is number or item.state | length == item.state | int | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
              {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
              {%- endfor -%}

- alias: battery_sensor_from_battery_level_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes.battery_level is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    - delay: 00:00:05
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ trigger.event.data.new_state.attributes.battery_level }}"

- alias: battery_sensor_from_battery_numeric_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes['Battery numeric'] is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    - delay: 00:00:05
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ (trigger.event.data.new_state.attributes['Battery numeric'] | int + 1) * 10 }}"
5 Likes

Hi Notorious

Your code is really nice! I’ve been looking for something like this for a long time, been using separate packages and adding each sensor manually.

One issue I have is that some of my sensors are reporting “unknown” battery state and I can’t figure out why.
I copied your code without any changes except for the slack notification.

Capture

If I check the first “unknown” entry “3D Temp Battery” here’s what my dev-state page shows this

First I saw was that 3d_fukt reports 37% but the actual battery level of “sensor.humidity_158…” is 35%

And the temp sensor reporting “unknown” does in fact show a battery level of 35% in dev-state.

Capture2

Any good idéas on why this is happening? :slight_smile:

The issue is your battery levels are floats and the automation didn’t account for that. Please try this updated version.

For others using this package, I’ve added another automation that creates sensors for entities that have a battery attribute such as Owntracks.

################################################################
## Packages / Battery Alert
################################################################

################################################################
## 1. Enable MQTT using your preferred MQTT broker
##    https://home-assistant.io/components/mqtt/
##
## 2. Enable MQTT Discovery by adding `discovery: true` and
##    `discovery_prefix: homeassistant` under the `mqtt:` section
##    of your configuration.yaml
##
##    mqtt:
##      discovery: true
##      discovery_prefix: homeassistant
##
## 3. Save this file as CONFIGDIR/packages/battery_alert.yaml
##
## 4. Add `packages: !include_dir_named packages` under the
##    `homeassistant:` section of your configuration.yaml
##
##    homeassistant:
##      packages: !include_dir_named packages
################################################################

################################################
## 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_notification:
      <<: *customize
      friendly_name: "Battery Notification"

    automation.battery_notification_clear:
      <<: *customize
      friendly_name: "Battery Notification Clear"

    automation.battery_alert_slack:
      <<: *customize
      friendly_name: "Battery Slack Notification"

    automation.battery_sensor_from_battery_level_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery_level Attribute"

    automation.battery_sensor_from_battery_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery Attribute"

    automation.battery_sensor_from_battery_numeric_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery numeric Attribute"

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

group:
  battery_alert:
    control: hidden
    entities:
      - automation.battery_notification
      - automation.battery_notification_clear
      - automation.battery_alert_slack
      - automation.battery_sensor_from_battery_level_attribute
      - automation.battery_sensor_from_battery_attribute
      - automation.battery_sensor_from_battery_numeric_attribute

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

automation:
- alias: battery_notification
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower and not "charge" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length or item.state | length == item.state | float | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - service: persistent_notification.create
      data_template:
        title: "Low Battery levels"
        notification_id: low-battery-alert
        message: >
          {%- set threshold_high = 40 -%}
          {%- set threshold_low = -1 -%}
          {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower and not "charge" in item.name | lower) %}
          {%- if (((item.state is number or item.state | length == item.state | int | string | length or item.state | length == item.state | float | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
          {%- endfor -%}

- alias: battery_notification_clear
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower and not "charge" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length or item.state | length == item.state | float | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim == "" }}
    - service: persistent_notification.dismiss
      data:
        notification_id: low-battery-alert

- alias: battery_alert_slack
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  action:
    - condition: template
      value_template: >
        {%- set threshold_high = 40 -%}
        {%- set threshold_low = -1 -%}
        {% macro battery_level() %}
        {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower and not "charge" in item.name | lower) %}
        {%- if (((item.state is number or item.state | length == item.state | int | string | length or item.state | length == item.state | float | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
        {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
        {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - service: notify.slack_notify
      data_template:
        message: "Low Battery Levels"
        data:
          attachments:
          - color: '#52c0f2'
            title: "These devices have low battery levels"
            text: >
              {%- set threshold_high = 40 -%}
              {%- set threshold_low = -1 -%}
              {%- for item in states.sensor if ("battery" in item.name | lower and not "voltage" in item.name | lower and not "runtime" in item.name | lower and not "setpoint" in item.name | lower and not "charge" in item.name | lower) %}
              {%- if (((item.state is number or item.state | length == item.state | int | string | length or item.state | length == item.state | float | string | length) and item.state | int < threshold_high and item.state | int > threshold_low) or item.state | lower == "low" or item.state | lower == "unknown") -%}
              {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}{% endif -%}
              {%- endfor -%}

- alias: battery_sensor_from_battery_level_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes.battery_level is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    # - delay: 00:00:05
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ trigger.event.data.new_state.attributes.battery_level }}"

- alias: battery_sensor_from_battery_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes.battery is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    # - delay: 00:00:05
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ trigger.event.data.new_state.attributes.battery }}"

- alias: battery_sensor_from_battery_numeric_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes['Battery numeric'] is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    # - delay: 00:00:05
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ (trigger.event.data.new_state.attributes['Battery numeric'] | int + 1) * 10 }}"
3 Likes

Worked like a charm! Thanks alot Notorious! It was nice to see some advanced templating as well, gave me a lot of idéas and “how-to’s” for other functions for my home :slight_smile:

1 Like

Hi @NotoriousBDG - just installed this, thanks for the work

Please note there is a typo in your installation comment. It should read

## 3. Save this file as CONFIGDIR/packages/battery_alert.yaml

ie plural packageS

1 Like

Thanks. Fixed…

Workd like a charm. Thank you so much. The earlier one also works like a charm.

I implemented the last version, MQTT, and all smiles.

Just one thing though. The code reports my phone, and a Fibaro water sensor which has battery and AC. For some reason the battery level is constant 4.

Code that could ignore some selected sensors would be nice.

1 Like

@Tomahawk, that’s an awesome idea. I’ve updated the package to add that feature. To disable alerts for a specific entities, set battery_alert_disabled: true in customize for each sensor you wish to ignore.

homeassistant:
  customize:
    sensor.sensor_name_to_ignore_battery:
      battery_alert_disabled: true

I’ve updated the package to move the min and max alert thresholds to input_number entities, which makes them configurable via the UI. These values must initially be set via the UI to your desired alert thresholds for alerts to work correctly. Note: A working recorder component is recommended to persist the input_number states through restarts. However, an initial value can be forced by un-remarking the initial lines in the package yaml if you don’t have the recorder component configured.

image

Lastly, I’ve adjusted the code to make it much easier to read.

################################################################
## Packages / Battery Alert
################################################################

################################################################
## 1. Enable MQTT using your preferred MQTT broker
##    https://home-assistant.io/components/mqtt/
##
## 2. Enable MQTT Discovery by adding `discovery: true` and
##    `discovery_prefix: homeassistant` under the `mqtt:` section
##    of your configuration.yaml
##
##    mqtt:
##      discovery: true
##      discovery_prefix: homeassistant
##
## 3. Save this file as CONFIGDIR/packages/battery_alert.yaml
##
## 4. Add `packages: !include_dir_named packages` under the
##    `homeassistant:` section of your configuration.yaml
##
##    homeassistant:
##      packages: !include_dir_named packages
##
## 5. Restart Home Assistant
##
## 6. Adjust Min Alert Threshold slider to the minimum battery
##    level to alert on.  Note: the input slider requires the
##    recorder component to keep it's state through restarts.
##    If recorder is not enabled, the threshold can be set by
##    un-remarking `initial` and setting the preferred default.
##
## 7. Adjust Max Alert Threshold slider to the maximum battery
##    level to alert on.  Note: the input slider requires the
##    recorder component to keep it's state through restarts.
##    If recorder is not enabled, the threshold can be set by
##    un-remarking `initial` and setting the preferred default.
##
## 8. To disable alerts for a specific entity, use customize to
##    set `battery_alert_disabled` to `true`
##
##    homeassistant:
##      customize:
##        sensor.sensor_name_to_ignore_battery:
##          battery_alert_disabled: true
##
################################################################

################################################
## 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_notification:
      <<: *customize
      friendly_name: "Battery Notification"

    automation.battery_notification_clear:
      <<: *customize
      friendly_name: "Battery Notification Clear"

    automation.battery_alert_slack:
      <<: *customize
      friendly_name: "Battery Slack Notification"

    automation.battery_sensor_from_battery_level_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery_level Attribute"

    automation.battery_sensor_from_battery_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery Attribute"

    automation.battery_sensor_from_battery_numeric_attribute:
      <<: *customize
      friendly_name: "Battery Sensor from battery numeric Attribute"

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

group:
  battery_alert:
    control: hidden
    entities:
      - input_number.battery_alert_threshold_min
      - input_number.battery_alert_threshold_max
      - automation.battery_notification
      - automation.battery_notification_clear
      - automation.battery_alert_slack
      - automation.battery_sensor_from_battery_level_attribute
      - automation.battery_sensor_from_battery_attribute
      - automation.battery_sensor_from_battery_numeric_attribute

################################################
## Input Number
################################################

input_number:
  battery_alert_threshold_max:
    name: Max Alert Threshold
    min: -1
    max: 100
    # initial: 40
    mode: slider
    icon: mdi:arrow-collapse-up
  battery_alert_threshold_min:
    name: Min Alert Threshold
    min: -1
    max: 100
    # initial: -1
    mode: slider
    icon: mdi:arrow-collapse-down

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

automation:
- alias: battery_notification
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {% macro battery_level() %}
          {%- for item in states.sensor if (
            "battery" in item.name | lower
            and not "voltage" in item.name | lower
            and not "runtime" in item.name | lower
            and not "setpoint" in item.name | lower
            and not "charge" in item.name | lower
            and not is_state_attr(item.entity_id, 'battery_alert_disabled', true)
          ) and (
            (
              (
                item.state is number
                or item.state | length == item.state | int | string | length
                or item.state | length == item.state | float | string | length
              )
              and item.state | int < states.input_number.battery_alert_threshold_max.state | int
              and item.state | int > states.input_number.battery_alert_threshold_min.state | int
            )
            or item.state | lower == "low"
            or item.state | lower == "unknown"
          ) -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}
          {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - service: persistent_notification.create
      data_template:
        title: "Low Battery levels"
        notification_id: low-battery-alert
        message: >
          {% macro battery_level() %}
            {%- for item in states.sensor if (
              "battery" in item.name | lower
              and not "voltage" in item.name | lower
              and not "runtime" in item.name | lower
              and not "setpoint" in item.name | lower
              and not "charge" in item.name | lower
              and not is_state_attr(item.entity_id, 'battery_alert_disabled', true)
            ) and (
              (
                (
                  item.state is number
                  or item.state | length == item.state | int | string | length
                  or item.state | length == item.state | float | string | length
                )
                and item.state | int < states.input_number.battery_alert_threshold_max.state | int
                and item.state | int > states.input_number.battery_alert_threshold_min.state | int
              )
              or item.state | lower == "low"
              or item.state | lower == "unknown"
            ) -%}
            {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}
            {%- endfor -%}
          {% endmacro %}
          {{ battery_level() }}

- alias: battery_notification_clear
  trigger:
    - platform: time
      minutes: '/15'
      seconds: 00
  action:
    - condition: template
      value_template: >
        {% macro battery_level() %}
          {%- for item in states.sensor if (
            "battery" in item.name | lower
            and not "voltage" in item.name | lower
            and not "runtime" in item.name | lower
            and not "setpoint" in item.name | lower
            and not "charge" in item.name | lower
            and not is_state_attr(item.entity_id, 'battery_alert_disabled', true)
          ) and (
            (
              (
                item.state is number
                or item.state | length == item.state | int | string | length
                or item.state | length == item.state | float | string | length
              )
              and item.state | int < states.input_number.battery_alert_threshold_max.state | int
              and item.state | int > states.input_number.battery_alert_threshold_min.state | int
            )
            or item.state | lower == "low"
            or item.state | lower == "unknown"
          ) -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}
          {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim == "" }}
    - service: persistent_notification.dismiss
      data:
        notification_id: low-battery-alert

- alias: battery_alert_slack
  trigger:
    - platform: time
      at: '10:00:00'
    - platform: time
      at: '18:00:00'
  action:
    - condition: template
      value_template: >
        {% macro battery_level() %}
          {%- for item in states.sensor if (
            "battery" in item.name | lower
            and not "voltage" in item.name | lower
            and not "runtime" in item.name | lower
            and not "setpoint" in item.name | lower
            and not "charge" in item.name | lower
            and not is_state_attr(item.entity_id, 'battery_alert_disabled', true)
          ) and (
            (
              (
                item.state is number
                or item.state | length == item.state | int | string | length
                or item.state | length == item.state | float | string | length
              )
              and item.state | int < states.input_number.battery_alert_threshold_max.state | int
              and item.state | int > states.input_number.battery_alert_threshold_min.state | int
            )
            or item.state | lower == "low"
            or item.state | lower == "unknown"
          ) -%}
          {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}
          {%- endfor -%}
        {% endmacro %}
        {{ battery_level() | trim != "" }}
    - 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() %}
                {%- for item in states.sensor if (
                  "battery" in item.name | lower
                  and not "voltage" in item.name | lower
                  and not "runtime" in item.name | lower
                  and not "setpoint" in item.name | lower
                  and not "charge" in item.name | lower
                  and not is_state_attr(item.entity_id, 'battery_alert_disabled', true)
                ) and (
                  (
                    (
                      item.state is number
                      or item.state | length == item.state | int | string | length
                      or item.state | length == item.state | float | string | length
                    )
                    and item.state | int < states.input_number.battery_alert_threshold_max.state | int
                    and item.state | int > states.input_number.battery_alert_threshold_min.state | int
                  )
                  or item.state | lower == "low"
                  or item.state | lower == "unknown"
                ) -%}
                {{ item.name }} ({{ item.state }}){% if not loop.last %}, {% endif %}
                {%- endfor -%}
              {% endmacro %}
              {{ battery_level() }}

- alias: battery_sensor_from_battery_level_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes.battery_level is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ trigger.event.data.new_state.attributes.battery_level }}"

- alias: battery_sensor_from_battery_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes.battery is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ trigger.event.data.new_state.attributes.battery }}"

- alias: battery_sensor_from_battery_numeric_attribute
  trigger:
    - platform: event
      event_type: state_changed
  condition:
    - condition: template
      value_template: "{{ trigger.event.data is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.old_state is not none }}"
    - condition: template
      value_template: "{{ trigger.event.data.new_state.attributes['Battery numeric'] is defined }}"
  action:
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/config"
        retain: true
        payload: >-
          {
            "device_class": "sensor",
            "name": "{{ trigger.event.data.new_state.name }} Battery",
            "state_topic": "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state",
            "unit_of_measurement": "%"
          }
    - service: mqtt.publish
      data_template:
        topic: "homeassistant/sensor/{{ trigger.event.data.new_state.object_id }}_battery/state"
        retain: true
        payload: "{{ (trigger.event.data.new_state.attributes['Battery numeric'] | int + 1) * 10 }}"
9 Likes

Excellente!

People like you making stuff like this, gives N00bs like me the ability to take part in the fun.

3 Likes

This is good stuff. I appreciated the hard work from everyone here.

One question. I added this and the sensors and automations loaded correctly. Problem I have is that it doesn’t show up in my front end. I have a heavily modified groups.yaml with only certain groups set to view: yes. I tried adding group.battery_alert to one of my tabs set for view: yes, but it never shows up. I am going to try and just manually dump the entities into my groups.yaml, but out of curiosity, what am I doing wrong with the packages file to make it show up in front end?

Thanks.

Are you reloading groups or automations via the UI? If so, that’s the reason why they’re disappearing. Unfortunately, that’s a known issue when using packages. Your best option is to restart Home Assistant after changing groups and automations.

Thanks. That explains it.

How do i get this project to pick up the IKEA Tradfri remotes battery levels?

The battery entity should get created the next time there is a state change for that sensor. After that, you should start getting alerts for it.