fw-fanctrl-gui

July 12, 2026 · View on GitHub

CI Release License: MIT

A graphical fan curve editor, live telemetry dashboard, and tray profile switcher for fw-fanctrl on Framework laptops.

Dashboard

Curve editor

Settings

Features

  • Draggable fan curve editor — drag points on an SVG chart, add points by clicking a segment, remove them with double-click. A live marker shows the current effective temperature and interpolated fan speed on the curve you are editing.
  • Strategy management — create, duplicate, rename, and delete strategies; set the default strategy and the strategy used on battery (discharging).
  • Import/export — share a single strategy as a small JSON file, or export the whole configuration (drop-in compatible with /etc/fw-fanctrl/config.json). Imports are validated and added to the draft with collision-free names. Ready-made profiles live in curves/ — contributions welcome.
  • Live dashboard — current temperature, effective (smoothed) temperature, target fan speed percentage, and real fan RPM read from sysfs.
  • Tray icon — switch profiles, pause/resume fan control, or open the window from a StatusNotifierItem tray menu. Closing the window hides it to the tray; the app keeps running.
  • Charging strategy — optionally apply a more aggressive strategy while the battery is charging (charging adds heat that fw-fanctrl's on-battery handling doesn't cover); the default returns automatically when charging finishes.
  • Service control — start/stop/restart/enable/disable the fw-fanctrl systemd unit with native polkit authentication prompts.
  • Follows the system theme — light/dark mode tracks your desktop, with a manual override in Settings.
  • Runs fully unprivileged — no sudo, no pkexec, ever. See Architecture.
  • Safety rails — configs are validated before apply, a local backup of the previous config is kept (with restore), and external config changes are detected while editing.

Installation

Arch Linux (AUR)

paru -S fw-fanctrl-gui-bin   # prebuilt from the latest release (or yay, etc.)
paru -S fw-fanctrl-gui-git   # build from the latest main

Debian / Ubuntu / Fedora / AppImage

Download the .deb, .rpm, or .AppImage from the latest release.

# Debian/Ubuntu
sudo apt install ./fw-fanctrl-gui_*.deb
# Fedora
sudo dnf install ./fw-fanctrl-gui-*.rpm
# AppImage (type2 runtime; no libfuse2 required)
chmod +x fw-fanctrl-gui_*_amd64.AppImage
./fw-fanctrl-gui_*_amd64.AppImage

The AppImage bundles the GUI only. Install fw-fanctrl and start its systemd service before launching. Host libraries are also required: webkit2gtk-4.1, gtk3, and libappindicator-gtk3 (listed in packaging/appimage/bundle.yml inside the AppImage). On Arch you can use the AUR wrapper instead of running the file directly:

paru -S fw-fanctrl-gui-appimage

From source

git clone https://github.com/jslay88/fw-fanctrl-gui && cd fw-fanctrl-gui
asdf install          # or provide Node.js + Rust yourself (.tool-versions has versions)
npm install
npm run tauri build   # bundles land in src-tauri/target/release/bundle/

Requirements

  • fw-fanctrl >= 1.0 installed with its systemd service (the GUI is a client; it does not control fans itself)
  • A desktop with StatusNotifierItem tray support. KDE Plasma works out of the box; GNOME needs the AppIndicator extension.
  • Runtime libraries: webkit2gtk-4.1, gtk3, libappindicator-gtk3 (pulled in automatically by the packages above)

Usage

  • Dashboard — live temperature/fan readouts, quick strategy switching, and pause/resume.
  • Fan Curves — select a strategy in the sidebar and edit its curve. Changes are kept as a local draft until you press Apply, which validates and sends the full config to the fw-fanctrl service. Revert discards the draft. If something else edits the config while you have unsaved changes, the editor warns you before overwriting. Import/export lives here too; community profiles are collected in curves/.
  • Service — unit status plus start/stop/restart/enable/disable. Expect a polkit password prompt; the app itself never elevates.
  • Settings — theme override, autostart on login, start minimized to tray, and telemetry poll interval.

Architecture

Tauri 2: a Rust backend drives fw-fanctrl and systemd, and a React/TypeScript frontend renders the UI over Tauri IPC.

The app never runs as root and never calls pkexec/sudo:

  • Profile switching, pause/resume, and saving fan curves go through the fw-fanctrl command socket (/run/fw-fanctrl/.fw-fanctrl.commands.sock). The socket is world-writable by design; the root-owned service validates every command and writes its own config file (/etc/fw-fanctrl/config.json).
  • systemd unit operations run plain systemctl, which triggers a native polkit authentication dialog in a desktop session.

Backend effect boundaries (FanctrlClient, SystemdClient, HwmonReader) are traits with production and mock implementations, keeping the core logic fully unit-testable. A background poller pushes telemetry snapshots to the frontend and keeps the tray menu in sync.

Development

asdf install        # Node.js + Rust pinned via .tool-versions
npm install         # also installs the pre-commit hook (runs npm run lint)
npm run tauri dev   # run the app with hot reload
npm run lint        # eslint + prettier + clippy (-D warnings) + rustfmt
npm run test        # vitest + cargo test
npm run coverage    # both coverage gates (80% minimum)

Commits are blocked if lint fails. Use git commit --no-verify only when you intend to skip the check.

On Arch, build dependencies are webkit2gtk-4.1 libappindicator-gtk3 base-devel; on Debian/Ubuntu see the package list in .github/workflows/quality.yml.

Release

Releases are cut by tagging:

npm run bump -- 0.2.0                       # syncs package.json, Cargo.toml, tauri.conf.json
git add -A && git commit -m "Release v0.2.0"
git tag -s v0.2.0 -m "v0.2.0"
git push origin main v0.2.0

The release workflow re-runs lint/tests/coverage, verifies the tag matches the project versions, builds .deb/.rpm/.AppImage bundles, publishes a GitHub Release, and pushes an updated fw-fanctrl-gui-bin PKGBUILD to the AUR.

License

MIT