Documentation

Get up and running in five minutes.

From install to first spin-test. Bring your own fans.

Install

The simplest way is the prebuilt binary attached to the latest release on Gitea — no Python, no virtualenv, no pip.

wget https://git.energy-bg.org/attachments/9634efb1-ba70-4d9f-b5d3-ced48d238327 -O FanControl
chmod +x FanControl
./FanControl

Prefer to run from source? See Build from source below.

udev rule (run without root)

The Linux kernel's hwmon subsystem assigns sensor indices dynamically — hwmon0, hwmon1 and friends can shuffle on every boot. The bundled 99-hwmon-stable.rules udev rule pins your sensor chip to a stable path at /run/hwmon_main and grants read/write to the adm group.

1. Install lm-sensors

# Ubuntu / Debian
sudo apt update
sudo apt install lm-sensors

# Fedora
sudo dnf install lm_sensors

2. Detect your sensors

sudo sensors-detect    # accept the defaults
sensors                # confirm readings appear

3. Identify your sensor chip

cat /sys/class/hwmon/hwmon*/name

Note the name (nct6799, nct6775, it8792, k10temp, …). If yours isn't nct6799, edit 99-hwmon-stable.rules and update the ATTR{name}=="nct6799" line accordingly.

4. Install the rule and join the adm group

sudo cp 99-hwmon-stable.rules /etc/udev/rules.d/
sudo usermod -aG adm $USER
sudo udevadm control --reload-rules
sudo udevadm trigger
Group changes take effect on next login. Log out and back in (or reboot) before launching the app.

5. Verify

ls -la /run/hwmon_main

You should see a symlink pointing into /sys/class/hwmon/....

First run

Launch the binary. If no fans.ini exists at the configured path, the app will offer to open the Fan Discovery wizard or pick a file. Pick the wizard the first time — it spin-tests channels, lets you name them, and writes the config for you.

Read-only mode? If the udev rule isn't installed, the app starts in read-only mode and shows a banner with a one-click Relaunch with privileges button (uses pkexec).

fans.ini reference

Per-fan configuration lives in a simple INI file. Default location is whatever path you saved from the wizard — the active path is remembered in ~/.config/Fan Control/Fan Control.conf.

[FAN1]
name = Case
pwm_path = /run/hwmon_main/pwm3
pwm_enable_path = /run/hwmon_main/pwm3_enable
rpm_path = /run/hwmon_main/fan3_input
; optional default temperature source for curve mode
temp_source = hwmon:nct6799:temp7

Per-fan curves persist separately at ~/.local/share/fan-control/curves.json.

Modes

Each fan has one of three modes, indicated by a colored badge on its card:

  • BIOS — kernel auto. The motherboard's own logic drives the PWM.
  • Manual — fixed PWM, set by you (gauge or preset).
  • Auto — temperature-curve driven by the app.

Activating Auto requires a temperature source. If none is set, the curve editor opens so you can pick one.

Build from source

git clone https://git.energy-bg.org/public/fan-control-desktop.git
cd fan-control-desktop
python3 -m venv .venv
source .venv/bin/activate
pip install PySide6
python fan_control_app.py

Bundle a standalone binary

source .venv/bin/activate
pip install pyinstaller
pyinstaller --noconfirm FanControl.spec
# output: dist/FanControl

Troubleshooting

Permission denied when writing PWM

The udev rule probably isn't loaded, or you haven't been added to adm yet (and re-logged in). Re-run the steps in udev rule.

Fans report ±3 PWM drift

Some chips report a slightly different PWM than what was written. The preset highlight tolerates the drift, so a button still lights up at the matching preset.

Writing PWM=255 puts the chip in "0" mode

Some Nuvoton chips (e.g. NCT6799) silently flip pwm_enable to 0 ("full-speed override") when you write 255. The app treats 0 and 1 as equivalent for our purposes — you can still write PWM normally.

More guides coming soon — covering temperature sources, curve tuning, and per-distro tips. Open an issue on the repo if there's something specific you'd like documented.