Add optional Home Assistant integration for battery percentage tracking

This commit is contained in:
2026-04-13 20:10:23 -05:00
parent 9d2b1d0d51
commit 8c06478bca
13 changed files with 607 additions and 5 deletions
+19
View File
@@ -67,6 +67,18 @@
{{ meta_row("Charge Cycles", battery.charge_cycles) }}
{{ meta_row("Purchase Date", battery.purchase_date) }}
{{ meta_row("Storage", battery.storage_location) }}
{% if battery.battery_percentage is not none %}
<tr>
<td style="padding:0.3rem 1rem 0.3rem 0;font-weight:600;color:#64748b;border:none;">Battery %</td>
<td style="border:none;">
{% if battery.battery_percentage < 20 %}
<span class="badge badge-warning">⚠ {{ battery.battery_percentage }}% — consider replacing</span>
{% else %}
{{ battery.battery_percentage }}%
{% endif %}
</td>
</tr>
{% endif %}
{% if battery.notes %}
<tr>
<td style="padding:0.3rem 1rem 0.3rem 0;font-weight:600;color:#64748b;border:none;">Notes</td>
@@ -261,6 +273,13 @@
value="{{ battery.purchase_date or '' }}">
</div>
<div class="form-group">
<label for="battery_percentage">Battery % (optional)</label>
<input type="number" id="battery_percentage" name="battery_percentage" min="0" max="100"
value="{{ battery.battery_percentage if battery.battery_percentage is not none else '' }}"
placeholder="e.g. 85">
</div>
</div>
<div class="form-group">
+22 -2
View File
@@ -26,6 +26,15 @@
<div style="font-size:1.8rem;font-weight:700;color:var(--count-retired);">{{ retired }}</div>
<div class="text-muted">Retired</div>
</div>
{% if ha_enabled %}
{% set low_pct = batteries | selectattr('battery_percentage', 'ne', none) | selectattr('battery_percentage', 'lt', 20) | list %}
{% if low_pct %}
<div class="card" style="flex:1;min-width:120px;text-align:center;border:2px solid #f59e0b;">
<div style="font-size:1.8rem;font-weight:700;color:#f59e0b;">{{ low_pct|length }}</div>
<div class="text-muted">Low Battery</div>
</div>
{% endif %}
{% endif %}
</div>
<div class="card">
@@ -38,7 +47,8 @@
<label style="display:block;cursor:pointer;margin-bottom:0.3rem;font-size:0.875rem;"><input type="checkbox" data-col="capacity" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Capacity</label>
<label style="display:block;cursor:pointer;margin-bottom:0.3rem;font-size:0.875rem;"><input type="checkbox" data-col="storage" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Storage Location</label>
<label style="display:block;cursor:pointer;margin-bottom:0.3rem;font-size:0.875rem;"><input type="checkbox" data-col="purchase" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Purchase Date</label>
<label style="display:block;cursor:pointer;font-size:0.875rem;"><input type="checkbox" data-col="cycles" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Charge Cycles</label>
<label style="display:block;cursor:pointer;margin-bottom:0.3rem;font-size:0.875rem;"><input type="checkbox" data-col="cycles" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Charge Cycles</label>
{% if ha_enabled %}<label style="display:block;cursor:pointer;font-size:0.875rem;"><input type="checkbox" data-col="ha-pct" onchange="toggleCol(this)" style="margin-right:0.4rem;"> Battery %</label>{% endif %}
</div>
</div>
</div>
@@ -150,6 +160,7 @@
<th class="col-storage" style="display:none;">Storage</th>
<th class="col-purchase" style="display:none;">Purchase Date</th>
<th class="col-cycles" style="display:none;">Cycles</th>
{% if ha_enabled %}<th class="col-ha-pct" style="display:none;">Bat %</th>{% endif %}
<th>Status</th>
<th>Assigned To</th>
<th>Actions</th>
@@ -172,6 +183,15 @@
<td data-label="Storage" class="col-storage" style="display:none;">{{ b.storage_location or '—' }}</td>
<td data-label="Purchase" class="col-purchase" style="display:none;">{{ b.purchase_date or '—' }}</td>
<td data-label="Cycles" class="col-cycles" style="display:none;">{{ b.charge_cycles or '—' }}</td>
{% if ha_enabled %}
<td data-label="Bat %" class="col-ha-pct" style="display:none;">
{% if b.battery_percentage is not none %}
{% if b.battery_percentage < 20 %}
<span class="badge badge-warning" title="Low — consider replacing">⚠ {{ b.battery_percentage }}%</span>
{% else %}{{ b.battery_percentage }}%{% endif %}
{% else %}—{% endif %}
</td>
{% endif %}
<td data-label="Status">
<span class="badge badge-{{ b.status }}">{{ b.status|capitalize }}</span>
</td>
@@ -358,7 +378,7 @@ updateToolbar();
// Column picker
var COL_KEY = 'battery_cols';
var ALL_COLS = ['chemistry','capacity','storage','purchase','cycles'];
var ALL_COLS = ['chemistry','capacity','storage','purchase','cycles'{% if ha_enabled %},'ha-pct'{% endif %}];
function toggleCol(cb) {
var col = cb.dataset.col;
+24 -1
View File
@@ -28,6 +28,12 @@
<td style="border:none;">{{ device.notes }}</td>
</tr>
{% endif %}
{% if ha_enabled and device.ha_entity_id %}
<tr>
<td style="padding:0.3rem 1rem 0.3rem 0;font-weight:600;color:#64748b;border:none;">HA Entity</td>
<td style="border:none;"><code>{{ device.ha_entity_id }}</code></td>
</tr>
{% endif %}
</table>
</div>
@@ -99,13 +105,22 @@ function addInstallRow() {
<div class="table-wrap">
<table class="responsive-table">
<thead>
<tr><th>Label</th><th>Brand</th><th>Notes</th><th>Actions</th></tr>
<tr><th>Label</th><th>Brand</th>{% if ha_enabled %}<th>Bat %</th>{% endif %}<th>Notes</th><th>Actions</th></tr>
</thead>
<tbody>
{% for b in installed %}
<tr>
<td data-label="Label"><a href="{{ url_for('battery_detail', battery_id=b.id) }}">{{ b.label }}</a></td>
<td data-label="Brand">{{ b.brand }}</td>
{% if ha_enabled %}
<td data-label="Bat %">
{% if b.battery_percentage is not none %}
{% if b.battery_percentage < 20 %}
<span class="badge badge-warning" title="Low — consider replacing">⚠ {{ b.battery_percentage }}%</span>
{% else %}{{ b.battery_percentage }}%{% endif %}
{% else %}—{% endif %}
</td>
{% endif %}
<td data-label="Notes" class="text-muted">{{ b.notes or '—' }}</td>
<td data-label="Actions">
<form class="inline" method="post" action="{{ url_for('battery_unassign', battery_id=b.id) }}">
@@ -180,6 +195,14 @@ function addInstallRow() {
<label for="edit-notes">Notes</label>
<textarea id="edit-notes" name="notes">{{ device.notes or '' }}</textarea>
</div>
{% if ha_enabled %}
<div class="form-group">
<label for="edit-ha-entity">Home Assistant Entity ID</label>
<input type="text" id="edit-ha-entity" name="ha_entity_id"
value="{{ device.ha_entity_id or '' }}"
placeholder="e.g. sensor.tv_remote_battery">
</div>
{% endif %}
<button class="btn btn-primary" type="submit">Save Changes</button>
</form>
</div>