GNOME Shell extension: live GPU power draw in the panel, and what your AI tokens actually cost to generate
  • JavaScript 80.4%
  • Shell 10.5%
  • Makefile 8.3%
  • CSS 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jake Gaylor 6630f8f357 Port to the GNOME 45+ ES module extension API
The extension targeted shell-version 50 but was written against the legacy
GNOME 3.x API. Shell 45+ loads extensions as ES modules, so `imports.ui.main`
pulled in a module through the classic script loader and the shell refused to
load it:

  SyntaxError: import declarations may only appear at top level of a module
    @ resource:///org/gnome/shell/ui/main.js:1:1

The error points at the shell's own main.js rather than at this extension,
which makes it read like a shell bug.

Ported both entry points to ESM: extension.js now exports a default class
extending Extension with the indicator as a registered PanelMenu.Button
subclass, and prefs.js implements fillPreferencesWindow with Adw rows bound
via settings.bind.

Four further bugs would each have broken it after the port:

- GLib.UTF8_TO_STRING_TRIMMED does not exist; use TextDecoder.
- Gio.File.new_for_uri on an http URI needs the gvfs http backend, which is
  not always installed. Use Soup.Session instead.
- A second PopupMenu was constructed and its actor parented to the button.
  PanelMenu.Button already provides this.menu.
- PopupMenuItem.label is a St.Label, not a string. Assigning a string to it
  breaks the widget; set item.label.text.

Also locate the GPU by reading each hwmonN/name rather than hardcoding
hwmon6, since those indices are not stable across boots; treat hal0's
tokens_completed as the rolling one hour window it is, rather than
differencing it as a cumulative counter; read update-interval with get_int to
match its schema type; and tear down the timeout, Soup session and cancellable
on disable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:42:44 -04:00
schemas Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
.gitignore Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
extension.js Port to the GNOME 45+ ES module extension API 2026-09-20 12:42:44 -04:00
LICENSE Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
Makefile Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
metadata.json Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
prefs.js Port to the GNOME 45+ ES module extension API 2026-09-20 12:42:44 -04:00
README.md Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
stylesheet.css Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00
test-nested.sh Initial commit: GPU power and AI token cost tracker 2026-09-20 12:42:33 -04:00

Power & Token Tracker

A GNOME Shell extension that shows live GPU power draw in the panel and works out what your AI tokens actually cost to generate.

The panel button shows current GPU watts. Opening it shows current and average power, tokens generated in the last hour, your energy cost per hour, and the resulting cost per million tokens.

Requirements

  • GNOME Shell 45 or later (developed against 50)
  • A GPU exposing power via hwmon (amdgpu, nvidia, i915 or xe)
  • A hal0 instance for token metrics

Install

make install

Then log out and back in — on Wayland the shell cannot load a new extension without restarting the session — and enable it:

gnome-extensions enable power-token-tracker@jhgaylor.local

For development, make link symlinks this checkout into place instead of copying, so edits only need a session restart rather than a reinstall.

Configuration

gnome-extensions prefs power-token-tracker@jhgaylor.local
Setting Default Meaning
Cost per kWh 0.12 Your electricity rate, in dollars
Update interval 10 Seconds between refreshes
Show cost per token on Whether to compute cost per million tokens
hal0 API URL http://localhost:8080 Base URL; /api/stats is appended

How it works

Power is read from /sys/class/hwmon/hwmonN/power1_average (falling back to power1_input), in microwatts. The hwmonN index is not stable across boots, so the extension locates the GPU by reading each hwmonN/name rather than hardcoding an index. A rolling 360-sample window backs the average.

Tokens come from hal0's /api/stats, via totals.tokens_completed. That field is a rolling one hour window, not a cumulative counter. Since it is already per-hour, and energy cost is already per-hour, cost per million tokens is:

costPerHour = (avgWatts / 1000) * costPerKwh
costPerMillionTokens = costPerHour / tokensPerHour * 1_000_000

This is a measure of marginal generation cost — electricity to run the GPU. It does not include hardware, idle draw outside the sample window, or anything upstream.

Testing

The running shell caches extension modules and cannot re-import them, so a syntax error normally costs you a log out / log in to discover. make check loads the extension in a throwaway headless shell on a private D-Bus session and reports whether it reached ACTIVE with no errors, without touching your live session.

License

MIT