Skip to content

Modernize como: packaging, battery collection, SQLite storage and CI - #7

Merged
cwoebker merged 6 commits into
masterfrom
claude/battery-tracker-modernize-WVj8X
Aug 5, 2026
Merged

Modernize como: packaging, battery collection, SQLite storage and CI#7
cwoebker merged 6 commits into
masterfrom
claude/battery-tracker-modernize-WVj8X

Conversation

@cwoebker

@cwoebker cwoebker commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Brings como up to current Python standards, fixes battery collection on hardware and kernels released since it was last touched, and replaces the flat-file database with SQLite.

Why

The tool had stopped working correctly on modern systems. macOS collection matched ioreg output by line position with branches pinned to 10.14/10.15, so it read wrong values on anything newer — and on Apple Silicon the capacity keys report a percentage rather than mAh, making health readings meaningless. Linux read /proc/acpi/battery/, removed from the kernel years ago. Both paxo and clint are unmaintained.

What changed

Commit
eccd289 Modernize packaging, CLI and battery collection
a0f1c24 Add multi-battery support, power normalization, --since and exit codes
05cd8cc Switch storage to SQLite with transparent migration
6695d46 Add pytest test suite
eb44fc1 Add CI, pre-commit hooks, coverage and Dependabot
0b22d45 Update README and HISTORY for 0.8.0

Packagingpyproject.toml + uv replace setup.py/Pipfile; Python >= 3.10; ruff and ty configured and clean.

Battery collection — macOS parses ioreg -a output with plistlib (works on Intel and Apple Silicon, no version branches); Linux reads /sys/class/power_supply/ handling both charge_* and energy_* drivers; Windows queries CIM through PowerShell, dropping the pywin32/wmi dependency.

New capability — multiple battery packs are reported individually and aggregated for storage; power draw is normalized to milliwatts across all three platforms (Windows exposes only milliwatts, so the old voltage × current calculation never worked there); como data --since 30d filters by time window; failures exit non-zero instead of printing and returning success.

Storage — SQLite replaces the zlib-compressed JSON blob that was rewritten in full on every save. The schema now retains voltage, power and charge state, which como info displayed but never recorded.

Upgrading

Existing databases are migrated automatically on first run, from both the old format and the old filename, and the original is kept alongside as como.bak. No action needed, but the migration is one-way — the new database is not readable by 0.7.x.

Two other things to be aware of: the licence changes from BSD to MIT, and the upload, open and init commands are gone, since the server they talked to no longer exists.

Verification

76 tests covering all three modules and all three platforms' collection paths, with no dependence on real battery hardware. ruff, ruff-format, ty and pytest all pass; CI runs them as separate jobs. The upgrade path was also exercised end to end against a real pre-0.8 database rather than only in tests.

@cwoebker
cwoebker force-pushed the claude/battery-tracker-modernize-WVj8X branch 3 times, most recently from 8e8c3a2 to 63c1d60 Compare June 2, 2026 20:01
@cwoebker cwoebker changed the title Modernize como to current Python/packaging standards Modernize como to current Python & packaging standards Aug 5, 2026
@cwoebker
cwoebker force-pushed the claude/battery-tracker-modernize-WVj8X branch from 2237145 to 0b22d45 Compare August 5, 2026 18:19
@cwoebker cwoebker changed the title Modernize como to current Python & packaging standards Modernize como: packaging, battery collection, SQLite storage and CI Aug 5, 2026
Bring the project up to current Python standards and fix battery collection
on hardware and kernels released since it was last touched.

Packaging:
- Replace setup.py and Pipfile with pyproject.toml + uv (Python >=3.10)
- Drop .travis.yml, MANIFEST.in and the como_run wrapper
- Configure ruff (lint + format) and ty (type checking)
- Relicense from BSD to MIT

Dependencies:
- Replace paxo (CLI framework) and clint (text UI), both unmaintained, with
  click and rich; sparkline rendering is now a small local function

Battery collection:
- macOS: parse `ioreg -r -c AppleSmartBattery -a` with plistlib instead of
  matching grep output by line position, which was pinned to 10.14/10.15
- Linux: read /sys/class/power_supply/, since /proc/acpi/battery/ was removed
  from the kernel; handle both charge_* and energy_* driver variants
- Windows: query CIM via PowerShell, replacing the pywin32/wmi dependency
- Drop the server upload/open/init commands; como.cwoebker.com is gone

Everything is type-annotated and uses pathlib, f-strings and timezone-aware
datetimes throughout.
Four functional improvements on top of the modernized battery layer.

Multiple batteries:
- get_batteries() returns one entry per physical pack; get_battery() keeps
  returning a single aggregate for saving, summing capacities and taking the
  highest cycle count
- `como info` lists each pack separately when more than one is present

Power reporting:
- BatteryInfo gains power_mw and is_charging, computed consistently on every
  platform. Windows reports ChargeRate/DischargeRate in milliwatts and has no
  current reading at all, so deriving watts from voltage x current only worked
  on macOS and Linux
- macOS capacity now reads AppleRawMaxCapacity/AppleRawCurrentCapacity where
  present; on Apple Silicon the legacy keys report a percentage rather than
  mAh, which made health readings meaningless
- Windows raises when no battery is present instead of reporting zeroes, since
  each CIM query is wrapped in try/catch and yields an empty result

Time filtering:
- `como data --since 30d` (also w/m/y) limits output to a recent window

Exit codes:
- Failures raise ComoError, which the CLI turns into a non-zero exit instead
  of printing a message and returning success
The database was a zlib-compressed JSON blob handled through tablib, rewritten
in full on every save. Replace it with SQLite from the standard library.

- Schema records the full battery snapshot: time, capacity, cycles, voltage_mv,
  power_mw and is_charging. Previously only capacity and cycles were kept, so
  the readings shown by `como info` were never retained
- time is UNIQUE, making saves idempotent within a second
- --since is applied as a SQL WHERE clause rather than filtering in Python
- The file moves to como.db; databases from earlier versions are migrated on
  first use from both the old format and the old unsuffixed filename, keeping
  a .bak of the original
- tablib is no longer a runtime dependency; import and export use stdlib csv,
  and export/import now round-trip every column rather than silently dropping
  voltage, power and charge state
- Saving and importing report what actually changed instead of assuming the
  write succeeded, since duplicate rows are ignored by the UNIQUE constraint
76 tests covering the three modules, with no reliance on real battery hardware.

- test_battery.py: macOS plist parsing including the unsigned-amperage
  conversion and Apple Silicon raw-capacity keys, Linux sysfs across both the
  charge_* and energy_* driver variants, Windows CIM JSON parsing, multi-pack
  aggregation and platform dispatch
- test_core.py: sparkline rendering, SQLite schema and round-trips, migration
  from the legacy format and legacy path, --since parsing and filtering, and
  each command's success and failure paths
- test_cli.py: every subcommand through Click's CliRunner, including exit codes

Relative timestamps are used wherever a test depends on the current date, so
the --since cases cannot rot as they age.
- ci.yml runs ruff, ty and pytest as three parallel jobs on pull requests and
  on pushes to master. Limiting the push trigger to master keeps branch pushes
  from duplicating the pull-request run
- .pre-commit-config.yaml runs ruff lint and format, plus ty through uv so it
  resolves against the project environment
- pytest-cov is wired into the default pytest invocation with branch coverage,
  and CI uploads coverage.xml as an artifact
- dependabot.yml tracks uv and GitHub Actions dependencies weekly, with Python
  updates grouped into a single pull request. The alerts on master predate
  pyproject.toml and no longer describe what the project depends on
- mise.toml pins the toolchain for local development
Document the supported platforms, the SQLite database location and schema,
the migration from earlier versions, and the current command set. The previous
README still described the server upload workflow and the paxo-based install.
@cwoebker
cwoebker force-pushed the claude/battery-tracker-modernize-WVj8X branch from 0b22d45 to acb12e3 Compare August 5, 2026 18:24
@cwoebker
cwoebker merged commit 364d072 into master Aug 5, 2026
3 checks passed
@cwoebker
cwoebker deleted the claude/battery-tracker-modernize-WVj8X branch August 5, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant