A lightweight, cross-platform tool for measuring and estimating power consumption of PC components in real-time. Provides three interfaces: a CLI for scripts and one-off checks, a terminal dashboard for live monitoring, and a browser-based dashboard with charts and history.
PowerWatch reports real or estimated power draw (in watts) for each major component: CPU, GPU (NVIDIA/AMD), RAM, and disk. Every reading is tagged as either (measured) or (estimated), and the total reflects the least-trustworthy input, so you always know what you can rely on.
Linux is the primary, fully tested platform. Windows and macOS are supported in code but haven't been validated on real hardware yet.
| Component | Source | Kind |
|---|---|---|
| CPU | RAPL (/sys/class/powercap) |
Measured |
| GPU (NVIDIA) | NVML | Measured |
| GPU (AMD) | hwmon (power1_average) |
Measured |
| RAM | Heuristic (GB used Γ watts/GB) | Estimated |
| Disk | Idle/active watts by type (NVMe uses drive's own power-state table when nvme-cli is available) |
Estimated |
| Total | Sum of the above | Mixed (tagged) |
Sensors that aren't available on your machine are silently omitted. Intel iGPUs aren't supported yet.
On NVMe drives, if nvme-cli is installed and the drive is readable, PowerWatch uses the manufacturer's declared max power per power state (nvme id-ctrl) rather than a generic guess. It falls back to the heuristic automatically when nvme-cli isn't available or the drive isn't NVMe.
You need a recent Rust toolchain (stable, 1.75+). No system libraries are required: SQLite is bundled, and the NVML binding loads dynamically only when the NVIDIA driver is present.
cargo build --workspace --releaseBinaries are placed under target/release/: powerwatch (CLI), powerwatch-tui (terminal dashboard), and powerwatch-web (browser dashboard). Copy them anywhere on your PATH if you like:
sudo cp target/release/powerwatch target/release/powerwatch-tui target/release/powerwatch-web /usr/local/bin/RAPL is root-only by default. Either run the CLI with sudo, or install the included udev rule:
sudo ./scripts/setup-udev.shSee docs/setup-udev.md for what the rule does and why the restriction exists.
# One-off snapshot
powerwatch
# JSON output (for scripts or pipes)
powerwatch --json
# Live terminal dashboard, refreshing once a second
powerwatch-tui
# Browser dashboard (local only, no auth)
powerwatch-web # http://127.0.0.1:3000
powerwatch-web --port 4000 # custom port
powerwatch-web --log # also write to the history databaseThe CLI can record readings to a local SQLite database for later analysis:
# Log for 60 seconds
powerwatch --log --duration 60s
# Also accepts "5m", "1h", or a bare number of seconds
powerwatch --log --duration 5mHistory lives at ~/.local/share/powerwatch/history.db. You can inspect it directly:
sqlite3 ~/.local/share/powerwatch/history.db "SELECT * FROM readings ORDER BY timestamp DESC LIMIT 20;"Query averaged readings from a logged session:
powerwatch history --since 1h
powerwatch history --since 30mGet notified when a component stays above a threshold for a configured duration. Works in both the CLI and TUI:
# CLI: needs --watch so there's something to sustain
powerwatch --watch --alert-above 50 --alert-for 30s \
--alert-run "notify-send 'PowerWatch' 'total power is high'"
# TUI: always live, no --watch needed
powerwatch-tui --alert-component cpu --alert-above 20 --alert-for 10s| Flag | Meaning |
|---|---|
--alert-above <watts> |
Enable the alert (required) |
--alert-component <name> |
Which sensor to watch (cpu, gpu, ram, disk, or total) |
--alert-for <duration> |
How long the threshold must be sustained (10s, 2m, default: immediate) |
--alert-run <command> |
Shell command to run once when the alert fires |
The alert fires once per excursion above the threshold (not once per second) so a hook won't spam you. It can fire again after dropping back below and crossing upward a second time. Without --alert-run, the CLI prints π alert active each cycle and the TUI shows a red banner.
powerwatch-tuiOpens a full-screen dashboard updating once per second: the total at top, one block per sensor below (current watts, confidence, a sparkline of the last minute, and min/max), and a footer listing available keys.
| Key | Action |
|---|---|
q |
Quit |
p |
Pause/resume sampling (freezes the last frame, shows PAUSED) |
l |
Start/stop logging to the history database |
s |
Open/close the suggestions panel |
If every sensor fails mid-session, the total line reports the failure directly instead of going quiet.
The TUI can evaluate power-draw patterns and surface energy-saving suggestions. Press s to open the panel:
| Key | Action |
|---|---|
s |
Toggle suggestions panel |
Enter |
Confirm the first actionable suggestion |
y / Y |
Apply the selected action |
n / N |
Dismiss the suggestion |
s again |
Close the panel |
Each suggestion carries a confirmation token to prevent accidental or remote-triggered changes, you must explicitly confirm before any action runs.
powerwatch-web # http://127.0.0.1:3000
powerwatch-web --port 4000 # custom port
powerwatch-web --log # also log continuously to the history databaseA local HTTP server with a single self-contained HTML page: no separate assets, no CDN, no external JS. Everything is embedded at compile time. The dashboard polls the same live data the CLI and TUI show, plus a chart of the last 10 minutes pulled from the history database:
- Live table: current watts and confidence per sensor, refreshed automatically.
- Chart: click a legend entry to toggle that series; the y-axis rescales so smaller signals stay visible. Hover over the chart for exact values at a given point.
- Suggestions: energy-saving proposals appear in a dedicated section, each with a confirmation modal.
- Process monitor: when CPU draw stays high, a "Show top processes" suggestion opens a modal displaying the five most CPU-intensive processes, refreshing every 2 seconds.
- Screensaver: when the system appears idle with the display active, a suggestion to enable the screensaver appears.
- --log makes the server write to
~/.local/share/powerwatch/history.dbcontinuously, the same database the CLI's--log/historyand the TUI'slkey use. Without--log, the chart only shows what something else logged.
API endpoints:
GET /api/snapshot # latest power readings as JSON
GET /api/history?since=10m # readings since a given timestamp
GET /api/suggestions # active energy-saving suggestions
POST /api/suggestions/apply # apply an action (requires token)
GET /api/processes/top # top 5 CPU-consuming processes
This is intended for your own machine only, there is no authentication or HTTPS. It's not recommended to expose it on a network.
PowerWatch can detect sustained high power draw and propose actions to reduce it. Every action requires explicit confirmation through a modal dialog with a one-time token; nothing is applied automatically.
| Suggestion | Condition | Action | Safety |
|---|---|---|---|
| Switch to power saver | Total > 15W for 3s, or CPU > 8W for 3s | Change power profile to power-saver |
Modifies system profile |
| Enable screensaver | Total > 20W for 30s (idle heuristic) | Suspend screensaver (xdg-screensaver suspend) |
Safe, reversible |
| Show top processes | CPU > 8W for 10s | Display top 5 CPU processes in a modal | Read-only, no system changes |
| Show sleep timer | System appears idle | Display current sleep configuration | Read-only, no system changes |
| Profile | Linux | macOS | Windows |
|---|---|---|---|
| Power Saver | powerprofilesctl set power-saver |
pmset -a lowpowermode 1 |
powercfg /setactive SCHEME_MAX |
| Balanced | powerprofilesctl set balanced |
pmset -a lowpowermode 0 |
powercfg /setactive SCHEME_BALANCED |
| Performance | powerprofilesctl set performance |
pmset -a lowpowermode 0 |
powercfg /setactive SCHEME_MIN |
The web dashboard can execute system commands from a browser click. While it binds only to 127.0.0.1 and has no authentication, consider these risks:
- Confirmation tokens mitigate CSRF. Each token is single-use and server-generated.
- Never expose the port on a network. If someone reaches the dashboard remotely, a crafted page could trigger actions via CSRF.
- Double confirmation is shown in the browser (
confirm()dialog) in addition to the token.
Use the web dashboard only on trusted local machines.
Linux: direct filesystem reads (/sys/class/powercap for RAPL, hwmon for AMD GPU, NVML for NVIDIA, iostat/nvme-cli for disk).
Windows: LibreHardwareMonitor WMI provider when available (real CPU RAPL/MSR), falling back to GetSystemTimes Γ assumed TDP for CPU; NVML unchanged; RAM uses GlobalMemoryStatusEx; disk uses the PDH performance counter API.
macOS: powermetrics for CPU/GPU (requires sudo), vm_stat + sysctl hw.memsize for RAM, iostat for disk. CPU and GPU are measured; disk and RAM are estimated.
cargo test --workspace # unit tests - no real hardware required
cargo clippy --workspace # lints
cargo fmt # formatting
cargo build --release # optimized binariesHardware-facing sensors (RAPL, NVML, hwmon) take a path or handle as a constructor argument, so tests exercise parsing and calculation logic against fixture data rather than live hardware. Manual validation on real hardware is still recommended before trusting numbers on an unfamiliar machine.
Building on Windows:
cargo build --release
.\target\release\powerwatch.exe
.\target\release\powerwatch-tui.exeMost likely failure points, in rough order of confidence:
- LibreHardwareMonitor WMI query (
powerwatch-core/src/sensors/windows/lhm.rs) β COM/WMI via thewmicrate is the least verifiable piece blind. - Disk sensor (
powerwatch-core/src/sensors/windows/disk.rs) β PDH API behavior can vary. - CPU fallback path β
GetSystemTimessemantics (kernel time includes idle) are easy to get subtly wrong. - RAM sensor β simplest of the four, most likely to just work.
CPU and GPU are measured via powermetrics (Apple reports real wattage). The trade-off: powermetrics always requires root, so the whole process must run under sudo:
sudo ./target/release/powerwatch
sudo ./target/release/powerwatch-tuiMost likely failure points:
- Disk sensor (
powerwatch-core/src/sensors/macos/disk.rs) βiostatcolumn layout depends on disk count. - CPU/GPU sensor β the plist parsing is simple and fixture-tested, but the exact
powermetricsinvocation is unverified. - RAM sensor β
vm_stat"available" memory is an approximation; numbers should be in the right ballpark but may not match Activity Monitor exactly.

