Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔋 battracker

Per-app battery consumption for any past time window on macOS. Ask "over the last hour, which apps ate my 13%?" — and get a real answer.

  Window : 2026-06-04 13:02 → 14:02  (1.0h, 60 samples)
  Battery: 84% → 71%   consumed 13%
  Avg draw: 7.8W  (CPU 3.1W · GPU 1.9W · ANE 0.1W · display/other 2.7W)

  Per-app share of that 13%   [measured subsystem power]

   4.81%  ████████░░░░░░░░░░░░░  Google Chrome
   2.95%  █████░░░░░░░░░░░░░░░░  Xcode
   2.10%  ████░░░░░░░░░░░░░░░░░  Display/DRAM/other hardware (system)
   1.20%  ██░░░░░░░░░░░░░░░░░░░  Slack
   0.74%  █░░░░░░░░░░░░░░░░░░░░  (everything else)

macOS can't tell you this. Activity Monitor shows a live snapshot; System Settings shows device-level history with no per-app split (unlike iOS). battracker records continuously so you can ask about any window after the fact.

✨ What this is

  • 🔍 Per-app battery % for any window: --last 1h, --last 45m, --from "09:00" --to "12:30".
  • 📊 Total % consumed is measured truth — read straight from battery levels; charging is reported separately, never netted against drain.
  • ⚡ Three attribution modes, always labeled: measured subsystem power (best), Apple Energy Impact (Intel fallback), CPU-time proxy (no-sudo fallback).
  • 🛠 Background logger via launchd, one sample/minute, zero cost while the lid is closed.
  • 🕰 Backfill battery history from the system PowerLog DB so totals work for windows before you installed it.
  • 🔬 --detail adds per-app CPU/GPU ms/s, disk bytes, network bytes, wakeups.
  • 🤖 Agent-friendly: --json (with live, killable PIDs per app) and --paths (raw log locations + format) so an AI can drive a battery-manager bot.
  • 🔒 100% local — plain text files under ~/.battery-tracker/data/. No network, no telemetry, no third-party deps.
  • 🧩 Stdlib Python 3 + POSIX shell. Nothing to compile.

❌ What this isn't

  • 🚫 Not a live monitor — for real-time menubar stats use stats. battracker answers historical questions.
  • 🚫 Not a magic per-app meter for the past — per-app split only exists for windows where the logger was running.
  • 🚫 Not exact physics — the per-app split is an honest attribution model, not lab-grade measurement. The report says which mode produced it.
  • 🚫 Not a battery-health/charge-limit tool (no SMC writes, ever — that path causes the AlDente PowerLog-bloat bug).
  • 🚫 Not cross-platform — macOS only; tuned for Apple Silicon, with an Intel fallback.
  • 🚫 Not a cloud service — it never phones home.

💪 Why this exists

macOS gives you no way to answer "in this arbitrary window, how much battery went where, per app?" Every existing tool stops short:

Tool What it does Why it's not enough
Activity Monitor Live Energy Impact + 12h average Snapshot only; no stored per-app timeline
System Settings → Battery 24h/10d battery-level history Device-level only; no per-app split (iOS has it, macOS doesn't)
exelban/stats Excellent live monitor "Top processes" is a live snapshot, not stored history
lzt1008/powerflow Logs whole-device power to SQLite Tells you when drain spiked, not which app
powermetrics (Apple) Per-process energy over an interval Only prospectively — can't reconstruct the past
PowerLog DB System battery-level history On macOS holds no reliable per-app energy

Nothing stores retroactive per-app energy on macOS. Something has to record continuously. So I built it.

👥 Who this is for

Use this if you:

  • 💻 Live on a MacBook and want to know what's actually draining it
  • 🧑‍💻 Are comfortable in a terminal and running sudo once for the full-power mode
  • 🔋 Want honest, labeled attribution rather than a made-up single number
  • 🗄 Care that your power data stays 100% local

Don't use this if you:

  • 🖥 Want a pretty menubar GUI (use stats instead)
  • ⏱ Need real-time, sub-second monitoring
  • 🪟 Are on Windows or Linux
  • 🔬 Expect lab-grade per-app wattage to three decimals

🔧 Tech stack

Layer Tech
Report CLI Python 3 (stdlib: plistlib, sqlite3, argparse)
Logger / installer POSIX sh
Scheduling launchd (LaunchAgent + optional root LaunchDaemon)
Data sources pmset, ps, powermetrics, system PowerLog SQLite DB
Storage Plain text files in ~/.battery-tracker/data/
Dependencies None (no pip, no compile)

⚠️ Requirements

Requirement Why How to verify
macOS pmset/powermetrics/PowerLog are macOS-only sw_vers
Python 3 Runs the report python3 --version
Apple Silicon (recommended) Mode A measured power needs per-subsystem mW uname -marm64
sudo (optional) Tier 2 powermetrics + PowerLog import need root sudo -v
Let the logger run ≥5 min Per-app split needs ≥2 samples in the window launchctl list | grep battery-tracker

On Intel Macs, Mode A is unavailable; battracker falls back to Mode B (Apple Energy Impact) automatically.

🧠 How it works under the hood

Big picture

  every 60s (launchd, never wakes a sleeping Mac)
        │
        ├─ Tier 1 (no sudo) ─ pmset ──► battery.log  (epoch,pct,state)
        │                     ps ──────► procs.log   (per-proc cputime)
        │
        └─ Tier 2 (root)  ─ powermetrics ─► energy.log (per-subsystem mW + per-task)

  battracker --last 1h
        │
        ├─ battery.log ──► TOTAL % consumed   (measured truth)
        └─ energy.log / procs.log ──► per-app SPLIT, scaled to the total
                                        Mode A › B › C  (best available, labeled)

In one paragraph. A launchd job samples once a minute: battery level + state from pmset, cumulative per-process CPU time from ps, and — if you opted into the root daemon — a 5-second powermetrics hardware sample. The report reads battery levels to compute the measured total consumed, then picks the best available model to split that total across apps: measured subsystem power (Mode A), Apple Energy Impact (Mode B), or CPU-time deltas (Mode C). The split is scaled so its parts sum to the measured total.

Why this architecture works

  • 📐 Total is measured, split is modeled — the report never blurs the two, and always prints which mode it used.
  • 🔌 StartInterval never wakes a sleeping Mac — cost while the lid is closed is zero; awake cost is ~0.5%/day for full mode, ~0.04 Wh/day for Tier 1.
  • 🧱 Unattributable power gets its own line — ANE and display/DRAM/other are shown explicitly, never silently smeared across your apps.
  • 🗂 Plain text, one dir — no database service, trivially greppable, trivially deletable.
  • 🛡 Defensive parsing — powermetrics key names vary by macOS/hardware; the parser uses candidate lists and skips bad samples instead of crashing.

Things you can configure

# in the Tier 2 LaunchDaemon environment (com.battery-tracker.energy.plist)
SAMPLE_EVERY_N_MIN = 1     # raise to 5 to drop battery cost to ~0.1%/day

📖 More

  • USAGE.md — install, every CLI flag, energy-cost table, acceptance tests, troubleshooting.
  • Honesty notes on the attribution model live in USAGE.md and in battracker.py's header.

🚧 Status

Working end-to-end on Apple Silicon (macOS 26). Modes A/B/C, PowerLog backfill, cycle history, and HTML export are implemented and tested. Roadmap: --watch live mode polish, richer SVG charts. Issues and PRs welcome.

About

Battery tracker for Mac, App & Hour Wise Usage -> Find the culprit for "Battery drain"

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages