<div align="center">
  <img src="static/icon-512.png" alt="Battery Tracker" width="160">
  <h1>Battery Tracker</h1>
  <p><strong>Stop losing track of your batteries.</strong></p>
  <p>A fast, self-hosted web app that tells you exactly where every rechargeable battery is,<br>
  how healthy it is, and when it was last charged — all from any device on your network.</p>
</div>

---

## Why Battery Tracker?

Rechargeable batteries are everywhere — remotes, flashlights, sensors, toys, tools. Without a system, you end up guessing which batteries are dead, buying new ones you didn't need, and finding half-charged stragglers rattling around a drawer.

Battery Tracker gives you a single source of truth:

- **Know where every battery is.** Each battery is assigned to a device or a storage location. No more mystery batteries.
- **Track health over time.** Log capacity tests and see a trend chart. Spot batteries that are degrading before they let you down.
- **Never forget to charge.** Log charge events, track charge cycles, and see at a glance which batteries are running low.
- **Works with Home Assistant.** If you already have smart battery sensors, Battery Tracker will pull the live percentage automatically — no manual updates needed.
- **Runs on a Pi.** Lightweight enough for a Raspberry Pi Zero. No cloud, no subscription, no data leaving your home.

---

## Features

### Batteries
- Add batteries in bulk (auto-generated labels like "Eneloop 001", "Eneloop 002")
- Track status: **Available**, **Installed**, **Retired**
- Store metadata: size (AA/AAA/18650/…), chemistry (NiMH/Li-ion/…), capacity, tested capacity, charge cycles, purchase date, storage location, notes
- Visual health indicator when tested capacity is recorded (good / warn / bad)
- Battery percentage — set manually or synced live from Home Assistant; resets to 100% when a charge is logged
- Retire and unretire batteries

> **Tip — measuring tested capacity:** The Panasonic BQ-CC65 charger (AA/AAA) can
> measure the actual capacity of a cell during a **refresh cycle**. Use the reading it
> reports as the "Tested capacity" value when logging a capacity test.

### Devices
- Add devices with a name, battery slot count, type (Remote Control, Flashlight, Sensor, …), and notes
- Install batteries into devices — by brand/quantity or by picking specific batteries
- Bulk install multiple batteries at once from the dashboard
- Mixed-brand warning when batteries from different brands share a device
- Link a device to a Home Assistant battery sensor for automatic live percentage tracking

### Dashboard
- 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
- 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
- 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, Log Charged

### History & Charts
- **Capacity history** — track tested capacity over time with a trend chart; full log in a modal with year filter and pagination
- **Charge history** — log every charge event; full log in a modal with year filter and pagination
- **Percentage history** — see a live chart of battery percentage over time; full log in a modal

### Home Assistant Integration (optional)
- Link any device to a Home Assistant entity (e.g. `sensor.tv_remote_battery`)
- Background poller fetches the current percentage on a configurable interval (default 5 min) and updates all installed batteries in that device
- Low-battery warning badge (< 20%) on the dashboard, device detail, and battery detail pages
- Fully optional — when `HOMEASSISTANT_URL` is not set the app works exactly as before

### UI
- **PWA** — installable on Android, iOS, and desktop; works as a home screen app
- **Mobile-friendly** — card-style table rows on small screens, full-width filter controls, stacked form buttons
- **Dark mode** — follows OS system preference automatically (`prefers-color-scheme`)
- No JavaScript framework — vanilla JS only

---

## Tech Stack

| Layer | Technology |
|---|---|
| Web framework | Flask 3.x |
| ORM | SQLAlchemy 2.x (raw session, no Flask-SQLAlchemy) |
| Database | SQLite (dev) / MariaDB (prod) |
| WSGI server | Waitress |
| Process manager | systemd user service |
| Tests | pytest (255 tests, 99% coverage) |

---

## Setup

### Prerequisites
- Python 3.10+
- `python3-venv`

### Install

```bash
git clone <repo-url>
cd battery-tracker-app

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

### Run (development)

```bash
source .venv/bin/activate
flask run
# → http://127.0.0.1:5000
```

### Run (production — systemd user service)

```bash
bash sbin/install-service.sh   # prompts for host/port + optional HA config, installs and enables the service
systemctl --user start battery-tracker
systemctl --user status battery-tracker
journalctl --user -u battery-tracker -f
```

### Database

SQLite is used by default (`batteries.db` in the project root). To switch to MariaDB, set the environment variable before starting:

```bash
export DATABASE_URL="mysql+pymysql://user:pass@host/dbname"
```

See `MIGRATION.md` for migrating an existing SQLite database to MariaDB, and for the schema migration commands needed when upgrading an existing database.

### Home Assistant integration

Set these environment variables before starting (or enter them when running `install-service.sh`):

```bash
export HOMEASSISTANT_URL="http://homeassistant.local:8123"
export HOMEASSISTANT_API_KEY="<long-lived access token>"
export HOMEASSISTANT_POLL_INTERVAL="300"   # seconds, default 300
```

Then link a device to its HA sensor entity ID on the device's edit page (e.g. `sensor.tv_remote_battery`). The app will poll HA every interval and update the battery percentage for all installed batteries in that device.

### Seed data (optional)

```bash
source .venv/bin/activate
python seed.py
```

---

## Running Tests

```bash
source .venv/bin/activate
pytest tests/ -v
```

Tests use a fresh temporary SQLite database per test and exercise the full HTTP stack.

---

## Project Structure

```
app.py                  Flask app factory + all routes
models.py               SQLAlchemy models (Battery, Device)
config.py               Database URL + HA config
ha_client.py            Home Assistant REST API wrapper
ha_poller.py            Background thread — polls HA, updates battery_percentage
seed.py                 Optional seed data script
migrate_to_mariadb.py   SQLite → MariaDB migration script
MIGRATION.md            Schema migration instructions + MariaDB procedure
requirements.txt
sbin/
  install-service.sh    systemd user service installer (prompts for HA config)
static/
  icon-192.png          PWA icon (192×192)
  icon-512.png          PWA icon (512×512)
  favicon.ico           Browser favicon
  manifest.json         PWA manifest
  sw.js                 Service worker
templates/
  base.html             Layout, CSS variables, dark mode, nav
  dashboard.html        Battery list with filters, bulk actions, HA% column
  battery_add.html      Add batteries form
  battery_detail.html   Battery detail + history charts + modals
  battery_delete.html   Delete confirmation
  assign.html           Assign battery to device
  device_list.html      Device list with type filter
  device_add.html       Add device form
  device_detail.html    Device detail + install + edit
tests/
  conftest.py           pytest fixtures
  test_acceptance.py    acceptance tests
  test_ha_integration.py  HA integration tests
```
