Architecture¶
powermonitor is a Python 3.13 package with a src/ layout. The command-line interface, Textual TUI, power collectors, configuration loader, and SQLite persistence are kept in separate modules.
High-level flow¶
macOS power APIs
├─ IOKit/SMC collector (preferred)
└─ ioreg collector (fallback)
↓
PowerReading dataclass
↓
SQLite database + live TUI widgets
↓
CLI history, stats, health, cleanup, and export commands
Package layout¶
src/powermonitor/
├── cli.py # Typer entry point and subcommands
├── config.py # Validated PowerMonitorConfig dataclass
├── config_loader.py # TOML loading and validation
├── database.py # SQLite persistence through Peewee
├── logger.py # Loguru setup
├── models.py # PowerReading model
├── collector/
│ ├── base.py # Collector protocol
│ ├── factory.py # Automatic collector selection
│ ├── ioreg.py # ioreg fallback collector
│ └── iokit/ # IOKit/SMC ctypes integration
└── tui/
├── app.py # Textual application
├── layout.py # Adaptive layout decisions
└── widgets.py # Live data, stats, and chart widgets
Data collection¶
powermonitor uses automatic fallback:
- IOKitCollector is preferred. It reads power data through macOS IOKit/SMC integration via
ctypesand avoids subprocess overhead. - IORegCollector falls back to
ioreg -rw0 -c AppleSmartBattery -aand parses plist output with the Python standard library.
This keeps the tool useful across Mac models where some sensors or charger fields may not be exposed consistently.
TUI architecture¶
The Textual app has three primary widgets:
LiveDataPanel: current watts, battery percentage, voltage, amperage, charger state, and charger metadata.StatsPanel: rolling statistics from recent readings.ChartWidget: recent power trend visualization.
src/powermonitor/tui/layout.py selects the most readable layout for the current terminal size. The app applies layout changes with stable widget IDs so resize events do not replace widgets or reset state.
Persistence¶
Readings are saved to SQLite through Database, with context managers handling connection lifecycle. The default database path is:
The database stores readings used by history, stats, export, cleanup, and health commands.
Configuration¶
PowerMonitorConfig owns validated runtime values. config_loader.py loads ~/.powermonitor/config.toml, applies field-level fallback to defaults, and provides strict validation for powermonitor config validate.
Configuration priority is:
- CLI arguments
- Config file
- Built-in defaults
CLI design¶
The Typer app launches the TUI when invoked without a subcommand. Subcommands reuse the same config loader and database path so CLI output and TUI history stay consistent.