fw-fanctrl-gui
July 12, 2026 · View on GitHub
A graphical fan curve editor, live telemetry dashboard, and tray profile switcher for fw-fanctrl on Framework laptops.



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-fanctrlsystemd 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.