I created a card to show me batteries in my Z-Wave and BTHome devices that are running low. I couldn't find any relevant examples that did exactly what I want so I used Gemini to help me with the solution. The card looks OK but I really don't like how it's done so I'm open to advice for making it better. I created a template sensor called Low Battery Report defined as follows:
# templates.yaml
- sensor:
- name: "Low Battery Report"
unique_id: low_battery_report_markdown
state: >
{% set mobile_app_entities = integration_entities('mobile_app') %}
{% set filtered_sensors = states.sensor
| map(attribute='entity_id')
| select('search', '_battery$|_battery_level$')
| reject('in', mobile_app_entities)
| list
%}
{% set low_battery_lines = namespace(lines=[]) %}
{% for sensor_entity_id in filtered_sensors %}
{% set device_id_val = device_id(sensor_entity_id) %}
{% set device_name = device_attr(device_id_val, 'name_by_user')
or device_attr(device_id_val, 'name') or 'Unknown Device' %}
{% set battery_percentage = states(sensor_entity_id) |
float(default=0) | round(0, 'half') | int(default=0) %}
{% if battery_percentage <= 10 %}
{% set color_var = 'var(--warning-color)' %}
{% if battery_percentage <= 5 %}
{% set color_var = 'var(--error-color)' %}
{% endif %}
{% set low_battery_lines.lines = low_battery_lines.lines + [
'<div style="margin-top: 4px;">' ~
'<span style="color: ' ~ color_var ~ '; font-weight: bold;">' ~ device_name ~ '</span>' ~
' (' ~ battery_percentage ~ '%)' ~
'</div>'
] %}
{% endif %}
{% endfor %}
{% if low_battery_lines.lines | length > 0 %}
{{ low_battery_lines.lines | join('\n') }}
{% else %}
All batteries are OK. :green_circle:
{% endif %}
The goal of this sensor is to ignore all the batteries in the Mobile App integration because my phone running the HA companion app isn't a concern. I want to pick up all the _battery_level entities from Z-Wave and all the _battery entities from BTHome. Then I get all the device names to which these entities belong because those are more meaningful than the entity names in the report. When the battery percentage is 10 or below, I attach HTML for the warning color and when 5 or below, I use the error color. If there are any sensors with low percentages in the list, I concatenate them into the result, including the device name and battery percentage. If there are no low batteries, I just return All batteries are OK :green_circle:. The markdown card that uses this sensor looks like this:
type: markdown
title: Low Battery Report 🔋
content: >
{% set report_content = states('sensor.low_battery_report') %}
{% if report_content == 'All batteries are OK. 🟢' %}
<div style="text-align: center; padding: 10px;">
<ha-icon icon="mdi:check-circle" style="color: var(--success-color);"></ha-icon>
{{ report_content }}
</div>
{% else %}
<p style="margin-bottom: 8px;">The following devices are at 10% or less:</p>
{{ report_content }}
{% endif %}
This works okay but I think it's messy. It just feels wrong generating HTML from a sensor and then injecting that into a markdown card. The sentinel value check {% if report_content == 'All batteries are OK. :green_circle:' %} feels particularly wrong to me. Is there a better way?