- No external dependencies: Uses framework_lib instead of ectool
- Single binary: 2.8MB release build, no Python runtime needed
- Memory safe: No GC, lower memory footprint than Python
- Fast: Native performance for real-time fan control
Rust implementation of fw-fanctrl - controls Framework Laptop fan speeds based on configurable temperature/speed curves.
Supports all Framework Laptop models (13" / 16", Intel/AMD).
- Configurable temperature/speed curves
- Multiple built-in strategies (quiet → performance)
- AC/battery automatic strategy switching
- Unix socket for runtime control
- Compatible with Gnome extension
- systemd service support
- Diagnostic sanity-check command
- Safe shutdown handling (SIGINT/SIGTERM restores EC auto fan mode)
- Framework Laptop (13" / 16", Intel/AMD)
- Rust 1.81+ (for building)
- Root access (EC communication)
cargo build --release
sudo cp target/release/fw-fanctrl /usr/local/bin/Create /etc/systemd/system/fw-fanctrl.service:
[Unit]
Description=Framework Fan Controller (Rust)
After=multi-user.target
[Service]
Type=simple
Restart=always
RestartSec=5
ExecStart=/usr/local/bin/fw-fanctrl run --config /etc/fw-fanctrl/config.json --silent --no-battery-sensors
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable fw-fanctrl
sudo systemctl start fw-fanctrlNote: The service automatically restores EC fan control to automatic mode on shutdown, so no ExecStopPost is needed.
| Command | Description |
|---|---|
fw-fanctrl run |
Start the fan control service |
fw-fanctrl use <strategy> |
Switch to a specific strategy |
fw-fanctrl reset |
Reset to default strategy |
fw-fanctrl reload |
Reload configuration file |
fw-fanctrl pause |
Pause fan control (EC auto) |
fw-fanctrl resume |
Resume fan control |
fw-fanctrl print [all|list|speed] |
Print status info |
fw-fanctrl sanity-check |
Run diagnostic checks |
| Option | Description |
|---|---|
-c, --config <path> |
Config file path (default: /etc/fw-fanctrl/config.json) |
-s, --silent |
Disable console output |
--no-battery-sensors |
Exclude battery temperature sensors |
--output-format [natural|json] |
Output format (default: natural) |
# Start service
sudo fw-fanctrl run
# Start with custom config
sudo fw-fanctrl run -c ./config.json
# Switch strategy
fw-fanctrl use performance
# List strategies
fw-fanctrl print list
# Get current status (JSON for Gnome extension)
fw-fanctrl --output-format json print all
# Run diagnostics
sudo fw-fanctrl sanity-checkWhen running fw-fanctrl run, the service handles both SIGINT (Ctrl+C) and SIGTERM
(for example from systemctl stop). On startup and shutdown it switches EC fan control
back to automatic mode to avoid leaving the fan in manual mode.
You can stop the service safely with:
sudo systemctl stop fw-fanctrlConfiguration file: /etc/fw-fanctrl/config.json
{
"defaultStrategy": "lazy",
"strategyOnDischarging": "",
"strategies": {
"laziest": {
"fanSpeedUpdateFrequency": 5,
"movingAverageInterval": 40,
"speedCurve": [
{ "temp": 0, "speed": 0 },
{ "temp": 45, "speed": 0 },
{ "temp": 65, "speed": 25 },
{ "temp": 85, "speed": 100 }
]
},
"lazy": {
"fanSpeedUpdateFrequency": 5,
"movingAverageInterval": 30,
"speedCurve": [
{ "temp": 0, "speed": 15 },
{ "temp": 50, "speed": 15 },
{ "temp": 85, "speed": 100 }
]
}
}
}| Field | Description |
|---|---|
fanSpeedUpdateFrequency |
How often to update fan speed (seconds) |
movingAverageInterval |
Temperature averaging window (seconds) |
speedCurve |
Temperature → fan speed mapping |
The service reads temperature from the EC (Embedded Controller) via framework_lib. Multiple temperature sensors are available on Framework laptops:
| Sensor | Description |
|---|---|
| CPU | Processor temperature |
| DDR | Memory temperature |
| Battery | Battery temperature (Intel platforms) |
| APU | AMD processor temperature |
| PECI | Platform Environmental Control Interface |
| Skin | Chassis temperature (some models) |
Platform-specific behavior:
- Intel platforms (Gen 11/12/13): Battery sensor at index 3
- Intel Core Ultra 1: Battery sensor at index 2
- AMD platforms (7040/AI300): No separate battery sensor - uses max of all sensors
- Framework 16: Different sensor layout with dGPU sensors
Excluding battery sensor:
Use --no-battery-sensors flag to exclude battery sensor from temperature calculation.
Debugging:
Set Environment=RUST_LOG=debug in service file to see sensor readings:
[Service]
Environment=RUST_LOG=debug
StandardOutput=journalThen check logs: journalctl -u fw-fanctrl -f
For design details, see docs/temperature-sensor-discovery.md.
fw-fanctrl-revived-gnome-shell-extension - Control fan profiles from Gnome Quick Settings.
Install from: https://extensions.gnome.org/extension/7864/framework-fan-control/
# Build
cargo build # debug
cargo build --release # optimized
# Run
cargo run # debug
cargo run --release # optimized
# Test
cargo test # all tests
cargo test <name> # specific test
# Lint
cargo clippy -- -D warnings
cargo clippy -- -D warnings --fix # auto-fix
# Format
cargo fmt --check
cargo fmt
# All checks
cargo fmt --check && cargo clippy -- -D warnings && cargo testReleases are automated via GitHub Actions. To create a new release:
# Install cargo-release (one time)
cargo install cargo-release
# Create a release (bumps version, creates tag, pushes)
cargo release minor --no-publish --allow-dirty --execute
git push --follow-tagsThis will:
- Bump the version in Cargo.toml
- Create a git tag (e.g., v0.2.0)
- Push to GitHub
- CI will build and create a GitHub Release with the binary
Use major, minor, or patch as needed.
- Original Python implementation: TamtamHero/fw-fanctrl
- Framework Rust library: FrameworkComputer/framework-system
- Gnome extension: ghostdevv/fw-fanctrl-revived-gnome-shell-extension
MIT