Skip to content
yePublic

About

battcurve is a Rust tool that logs and charts laptop battery curves

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

battcurve

Sample your laptop battery and visualize its charge / discharge curves, dQ/dV differential-capacity curve, and health / wear — the curve families from battery literature (Battery University, BioLogic) — built from your own machine's data.

A single Rust binary with a shared core (sampler + storage + analysis) and three faces: a background logger, an htop-style TUI, and a local web UI with interactive charts.

Quick start

just build                 # cargo build
just run-log 30s           # background logger -> SQLite (Ctrl-C to stop)
just run-tui               # live htop-style monitor (press q / Esc to quit)
just serve 8787            # open http://127.0.0.1:8787 for the analysis charts
just capture               # one-shot: capture a session, then print a summary
just paths                 # show where data is stored

The logger, TUI, and web UI all share the same store, so you can log in one terminal and watch live in another. Run the logger across at least one full charge or discharge cycle before expecting clean V-vs-SoC and dQ/dV curves.

Command-line interface

The just recipes are thin wrappers around a single binary, battcurve. You can run it directly without just.

cargo build --release          # build → target/release/battcurve
just install                   # or: cargo install --path .  (→ ~/.cargo/bin/battcurve)
battcurve --help               # top-level help; --version for the version

Once installed (or via ./target/release/battcurve), the subcommands are:

# Background logger — sample forever at an interval; Ctrl-C to stop.
battcurve log   [--interval 10s] [--store sqlite|csv] [--battery BAT0]

# One-shot capture — sample until a stop condition, then print a summary.
battcurve capture [--interval 10s] [--store sqlite|csv] [--battery BAT0] \
                  [--until ctrl-c|full|empty]

# htop-style live terminal monitor (press q / Esc to quit).
battcurve tui   [--store sqlite|csv] [--battery BAT0]

# Local web UI with the interactive analysis charts.
battcurve serve [--port 8787] [--store sqlite|csv]

# Print the resolved data-file paths.
battcurve paths

Flags (all optional):

Flag Applies to Default Meaning
--interval log, capture 10s Sample period; accepts 500ms, 5s, 1m, 1h, or a bare number (seconds).
--store all but paths sqlite Storage backend: sqlite (WAL, concurrent) or csv (human-readable).
--battery log, capture, tui first BAT* Which /sys/class/power_supply device to read.
--until capture ctrl-c Stop on ctrl-c, when full, or when empty.
--port serve 8787 Port to bind on 127.0.0.1.

The charts

  • Charge / discharge over time — SoC %, voltage, and power on a shared time axis.
  • Voltage vs State of Charge — the classic discharge plateau / CC–CV charge climb.
  • dQ/dV (differential capacity) — peaks mark electrochemical phase transitions; used to track aging. Smoothed with a moving average since it is intrinsically noisy.
  • Health summary — full-charge capacity vs design capacity, wear %, and cycle count.

How it reads the battery

Reads /sys/class/power_supply/BAT* (override with --battery BATx), normalizing both reporting styles found in the wild:

  • energy-based (energy_now / energy_full in µWh, voltage_now in µV) — what this HP laptop uses.
  • charge-based (charge_now / charge_full in µAh, current_now in µA) — energy and power are derived via voltage.

Deriving power when the EC won't tell us

Some batteries (including the HP this was developed on) expose power_now only intermittently — or report No such device — and have no current_now at all. But energy_now updates reliably. Since power is just the signed energy slope, battcurve derives it from the energy delta over a short trailing window (analysis::fill_derived_power) wherever a real power reading is missing. Native power_now readings, when present, are used as-is. The derived power also feeds the current used for dQ/dV integration (I = power / voltage).

Storage

Default backend is SQLite (rusqlite, bundled, WAL mode) at $XDG_DATA_HOME/battcurve/battcurve.db, so the logger can write while the TUI and web server read concurrently. Pass --store csv to any command to use a human-readable, append-only CSV at samples.csv instead. Stored samples are the raw sysfs readings; power derivation happens at read time, keeping the log a faithful record.

Background service

just install-service       # installs target/release binary + enables a systemd --user unit
systemctl --user status battcurve-logger

The unit (systemd/battcurve-logger.service) runs battcurve log --interval 30s and restarts on failure.

Layout

justfile       command runner (build / install / test / lint / run-* / serve / install-service)
src/core/      sample, reader (sysfs), storage (csv|sqlite), analysis (sessions, dQ/dV, CC/CV, health)
src/cmd/       log, capture, tui, serve
src/web/       index.html + vendored uplot.min.js / uplot.min.css (inlined when served)
systemd/       user logger unit

Tests

just test      # unit tests: analysis curves, power derivation, storage round-trips,
               # WAL + concurrent reader, reader normalization (energy- and charge-based)
just lint      # clippy with -D warnings

Notes

  • The web UI refreshes by polling the JSON API every 5 s (no WebSocket).
  • The page is fully self-contained and offline: uPlot's JS/CSS are vendored in src/web/ (uplot.min.js, uplot.min.css) and inlined into the served HTML at compile time, so no CDN or external requests are made. To upgrade uPlot, replace those two files.
  • Charts follow the OS light/dark theme (canvas axis colors are read from CSS tokens).
  • A clean dQ/dV or full discharge curve only appears after a real charge/discharge cycle has been logged.

License

MIT © 2026 Ye Wang

About

battcurve is a Rust tool that logs and charts laptop battery curves

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages