Update README, MIGRATION.md, and CLAUDE.md for current feature set

This commit is contained in:
2026-04-15 03:55:14 -05:00
parent 75d0e05f59
commit 8721254476
3 changed files with 85 additions and 3 deletions
+4
View File
@@ -53,6 +53,10 @@ journalctl --user -u battery-tracker -f
- Unretire: sets status back to `available` - Unretire: sets status back to `available`
- Unassign with `next` form field → redirects to that URL (must start with `/`); falls back to dashboard - Unassign with `next` form field → redirects to that URL (must start with `/`); falls back to dashboard
- Logging a charge entry → sets `battery.battery_percentage = 100` - Logging a charge entry → sets `battery.battery_percentage = 100`
- Retired batteries are excluded from `needs_attention` and `low_pct` warnings — both use the `active` list (`status in ("available", "installed")`); the template-side `low_pct` filter and the in-table ⚠ badge also guard against retired status
### Dashboard filtering
The dashboard route builds an `active` list (`status in ("available", "installed")`) used for all warning logic. Client-side filtering uses `data-status` attributes on each table row and `applyFilters()` in JS. The default filter state is `"active"` (retired rows hidden on page load); the Reset button restores `"active"`, not an empty filter. Column visibility choices are stored in `localStorage`.
### Home Assistant integration (optional) ### Home Assistant integration (optional)
`ha_client.py` wraps the HA REST API (`GET /api/states/<entity_id>`). `ha_poller.py` runs a daemon thread started in `create_app` only when `HOMEASSISTANT_URL` and `HOMEASSISTANT_API_KEY` are set. The poller queries all `Device` rows with `ha_entity_id IS NOT NULL`, fetches the current percentage from HA, and writes it to `battery_percentage` on each installed battery in that device. The poller uses its own `sessionmaker` session (not the request-scoped `scoped_session`). When HA is not configured the app behaves exactly as before — all HA UI is gated on `ha_enabled` passed to templates. `ha_client.py` wraps the HA REST API (`GET /api/states/<entity_id>`). `ha_poller.py` runs a daemon thread started in `create_app` only when `HOMEASSISTANT_URL` and `HOMEASSISTANT_API_KEY` are set. The poller queries all `Device` rows with `ha_entity_id IS NOT NULL`, fetches the current percentage from HA, and writes it to `battery_percentage` on each installed battery in that device. The poller uses its own `sessionmaker` session (not the request-scoped `scoped_session`). When HA is not configured the app behaves exactly as before — all HA UI is gated on `ha_enabled` passed to templates.
+76
View File
@@ -1,5 +1,81 @@
# Schema Migrations # Schema Migrations
## Adding battery metadata fields, device type, and history tables
These columns and tables were added over several feature commits. `create_all()` does not add columns to existing tables, so existing installations need the `ALTER TABLE` commands below. New tables (`capacity_test`, `charge_log`) are created automatically by `create_all()` on next restart for SQLite — the explicit SQL below is provided for reference and for MariaDB operators who manage schema manually.
**Always snapshot first:**
```bash
cp batteries.db batteries.db.$(date +%Y-%m-%d).snapshot
```
**SQLite:**
```bash
sqlite3 batteries.db "ALTER TABLE device ADD COLUMN device_type VARCHAR(50);"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN size VARCHAR(20);"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN chemistry VARCHAR(20);"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN capacity_mah INTEGER;"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN tested_capacity_mah INTEGER;"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN tested_date VARCHAR(10);"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN charge_cycles INTEGER;"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN purchase_date VARCHAR(10);"
sqlite3 batteries.db "ALTER TABLE battery ADD COLUMN storage_location VARCHAR(100);"
```
New tables (auto-created on restart, shown here for reference):
```python
import sqlite3
conn = sqlite3.connect('batteries.db')
conn.execute('''CREATE TABLE IF NOT EXISTS capacity_test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
battery_id INTEGER NOT NULL REFERENCES battery(id) ON DELETE CASCADE,
tested_capacity_mah INTEGER NOT NULL,
tested_date VARCHAR(10) NOT NULL,
notes TEXT
)''')
conn.execute('''CREATE TABLE IF NOT EXISTS charge_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
battery_id INTEGER NOT NULL REFERENCES battery(id) ON DELETE CASCADE,
charged_date VARCHAR(10) NOT NULL,
increment_cycles INTEGER NOT NULL DEFAULT 0,
notes TEXT
)''')
conn.commit(); conn.close()
```
**MariaDB / MySQL:**
```sql
ALTER TABLE device ADD COLUMN device_type VARCHAR(50) NULL;
ALTER TABLE battery ADD COLUMN size VARCHAR(20) NULL;
ALTER TABLE battery ADD COLUMN chemistry VARCHAR(20) NULL;
ALTER TABLE battery ADD COLUMN capacity_mah INT NULL;
ALTER TABLE battery ADD COLUMN tested_capacity_mah INT NULL;
ALTER TABLE battery ADD COLUMN tested_date VARCHAR(10) NULL;
ALTER TABLE battery ADD COLUMN charge_cycles INT NULL;
ALTER TABLE battery ADD COLUMN purchase_date VARCHAR(10) NULL;
ALTER TABLE battery ADD COLUMN storage_location VARCHAR(100) NULL;
CREATE TABLE IF NOT EXISTS capacity_test (
id INT AUTO_INCREMENT PRIMARY KEY,
battery_id INT NOT NULL,
tested_capacity_mah INT NOT NULL,
tested_date VARCHAR(10) NOT NULL,
notes TEXT,
CONSTRAINT fk_ct_battery FOREIGN KEY (battery_id) REFERENCES battery(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS charge_log (
id INT AUTO_INCREMENT PRIMARY KEY,
battery_id INT NOT NULL,
charged_date VARCHAR(10) NOT NULL,
increment_cycles INT NOT NULL DEFAULT 0,
notes TEXT,
CONSTRAINT fk_cl_battery FOREIGN KEY (battery_id) REFERENCES battery(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
---
## Adding battery_pct_log table ## Adding battery_pct_log table
This table was added to track battery percentage change history. This table was added to track battery percentage change history.
+5 -3
View File
@@ -45,11 +45,13 @@ Battery Tracker gives you a single source of truth:
### Dashboard ### Dashboard
- Overview stats (total / available / installed / retired) + **Low Battery** count when any battery is below 20% - Overview stats (total / available / installed / retired) + **Low Battery** count when any battery is below 20%
- **Needs Attention** panel — highlights batteries with low capacity (< 80% of rated) or low percentage (< 20%, requires HA)
- Charge summary: total charge events and events in the last year - Charge summary: total charge events and events in the last year
- Full battery table with client-side filtering by status, brand, size, storage location, and free-text search - Full battery table with client-side filtering by status, brand, size, storage location, and free-text search; **retired batteries hidden by default** (change the Status filter to show them)
- Sortable columns
- Column picker (Chemistry, Capacity, Storage, Purchase Date, Cycles, Battery %) stored in localStorage - Column picker (Chemistry, Capacity, Storage, Purchase Date, Cycles, Battery %) stored in localStorage
- Quick-assign available batteries to a device without leaving the page - Quick-assign available batteries to a device without leaving the page
- Bulk actions: Unassign, Retire, Delete, Set Field (storage location or brand), Install in Device - Bulk actions: Unassign, Retire, Delete, Set Field (storage location or brand), Install in Device, Log Charged
### History & Charts ### History & Charts
- **Capacity history** — track tested capacity over time with a trend chart; full log in a modal with year filter and pagination - **Capacity history** — track tested capacity over time with a trend chart; full log in a modal with year filter and pagination
@@ -79,7 +81,7 @@ Battery Tracker gives you a single source of truth:
| Database | SQLite (dev) / MariaDB (prod) | | Database | SQLite (dev) / MariaDB (prod) |
| WSGI server | Waitress | | WSGI server | Waitress |
| Process manager | systemd user service | | Process manager | systemd user service |
| Tests | pytest (80 acceptance tests) | | Tests | pytest (82 tests) |
--- ---