Yet Another Fan Control (YAFC) is a systemd daemon and Textual TUI for automatic fan control on the HP Victus 16 (Ryzen 7 7840HS + RTX 4050).
On the HP Victus 16, the hp-wmi kernel driver resets fan settings on every boot, requiring manual intervention to restore a sensible fan curve.
A lightweight Python daemon running as a systemd service. It monitors CPU, iGPU, and dGPU temperatures simultaneously and adjusts fan speeds automatically according to a configurable curve — with no manual intervention after boot.
This project was developed and tested only on a single machine: HP Victus 16 with Ryzen 7 7840HS + RTX 4050, running CachyOS.
Use at your own risk. This software directly writes to kernel sysfs interfaces to control fan hardware. While it has caused no issues on the author's machine, it may behave differently on other hardware configurations, kernel versions, or Linux distributions.
The author provides no warranty of any kind. By using this software you accept full responsibility for any consequences, including but not limited to: hardware damage, thermal issues, system instability, or data loss.
Before using on your own machine:
- Verify that your system has the
hp-wmikernel module - Check that your hwmon sysfs paths match what this daemon expects
- Monitor temperatures closely during initial use
- Keep stock fan control available as a fallback
Trademark Notice: HP and HP Victus are trademarks of HP Inc. This project is not affiliated with, endorsed by, or sponsored by HP Inc. in any way.
- Monitors CPU (
k10temp), iGPU (amdgpu), and dGPU (nvidia-smi) temperatures simultaneously - Determines fan level based on the dominant (hottest) sensor
- Hysteresis: step-down is delayed by 5°C to prevent rapid fan oscillation
- MAX / AUTO / GAMING / MANUAL mode support
- Configurable via
fan-curve.conf; daemon reloads on SIGHUP — no restart required - Textual TUI with live sensor monitoring, mode switching, fan curve editing, and log viewer
- English and Turkish UI (
--lang en|tr) - KDE/Plasma
.desktopentry installed automatically when KDE is detected
┌─ YAFC ───────────────────────────────────────────────────────┐
│ [1:MAX] [2:AUTO ●] [3:MANUAL] [4:GAMING] ● Service: active │
│ [Start] [Stop] [Restart] │
├───────────────────────┬──────────────────────────────────────┤
│ TEMPERATURES │ FAN CURVE │
│ CPU: 74.1°C │ ○ 90°C → 5800 RPM │
│ iGPU: 61.0°C │ ○ 84°C → 5000 RPM │
│ dGPU: 66.0°C │ ● 65°C → 3000 RPM ← ACTIVE │
│ │ ○ 58°C → 2500 RPM │
│ FANS │ ... │
│ Fan 1: 3500 RPM │ │
│ Fan 2: 3500 RPM │ │
├───────────────────────┴──────────────────────────────────────┤
│ LOG (journalctl -u yafc) │
│ 16:04:17 [AUTO][CPU] CPU: 74.1°C | iGPU: 61.0°C | ... │
└──────────────────────────────────────────────────────────────┘
Hardware:
- HP Victus 16 (or another HP laptop with the
hp-wmikernel module and sysfs fan control)
Software:
- Linux with systemd
- Python 3.10+
python-textual(for the TUI)- NVIDIA driver +
nvidia-smi(optional — for dGPU temperature monitoring)
sudo pacman -S python-textualThe daemon (yafc-daemon.py) uses stdlib only — no additional dependencies required.
sudo bash install.shThe installer will:
- Copy the daemon to
/usr/local/bin/yafc-daemon - Copy TUI files to
/opt/yafc/ - Create the
yafc-tuicommand at/usr/local/bin/yafc-tui - Install config to
/etc/yafc/fan-curve.conf - Enable and start the systemd service
- Install a
.desktopentry to/usr/share/applications/if KDE/Plasma is detected
sudo yafc-tui
sudois required — the TUI usessystemctlfor service start/stop/restart.
To force a specific UI language:
sudo yafc-tui --lang en
sudo yafc-tui --lang trIf --lang is not set, the language is determined by the $LANG environment variable (defaults to Turkish when LANG starts with tr, English otherwise).
Config file: /etc/yafc/fan-curve.conf
{
"mode": "auto",
"curve": [
{"temp": 90, "rpm": 5800},
{"temp": 0, "rpm": 1450}
]
}| Field | Values | Description |
|---|---|---|
mode |
"max" | "auto" | "gaming" | "manual" |
Active fan control mode |
curve |
array of {temp, rpm} objects |
Fan curve levels used in MANUAL mode, sorted descending by temperature |
The daemon reloads the config on SIGHUP — no restart required:
sudo systemctl kill -s HUP yafcDefault quiet curve (AUTO mode):
| Level | Threshold (°C) | Fan RPM |
|---|---|---|
| 10 | ≥ 90 | 5800 |
| 9 | ≥ 84 | 5000 |
| 8 | ≥ 78 | 4200 |
| 7 | ≥ 72 | 3600 |
| 6 | ≥ 65 | 3000 |
| 5 | ≥ 58 | 2500 |
| 4 | ≥ 50 | 2000 |
| 3 | ≥ 42 | 1650 |
| 2 | ≥ 35 | 1450 |
| 1 | ≥ 0 | 1450 |
Thresholds and RPM values can be edited via fan-curve.conf or through the TUI in MANUAL mode.
These user-defined values are applied only in MANUAL mode.
| Mode | Behavior |
|---|---|
| MAX | Fixed maximum RPM (5800) regardless of temperature |
| AUTO | Uses the built-in default curve |
| GAMING | Uses the built-in aggressive gaming curve |
| MANUAL | Uses the user-defined curve from fan-curve.conf (editable via TUI) |
| Key | Action |
|---|---|
1 |
Switch to MAX mode |
2 |
Switch to AUTO mode |
3 |
Switch to MANUAL mode |
4 |
Switch to GAMING mode |
r |
Refresh sensors and config |
c |
Copy current status to clipboard |
n |
Add a new fan curve level (MANUAL mode only) |
d |
Delete selected fan curve level (MANUAL mode only, minimum 2 levels) |
Enter (table row) |
Edit selected level (MANUAL mode only) |
q |
Quit |
# Status
systemctl status yafc
# Live log
journalctl -u yafc -f
# Restart
sudo systemctl restart yafc
# Reload config without restart
sudo systemctl kill -s HUP yafcTested on HP Victus 16 (Ryzen 7 7840HS + RTX 4050) running CachyOS.
Sensors are discovered dynamically by reading the hwmon name file — hwmon indices can change between reboots without issue.
| Sensor | hwmon name | Method |
|---|---|---|
| CPU (Tctl) | k10temp (fallback: acpitz) |
sysfs temp1_input |
| iGPU | amdgpu |
sysfs temp1_input |
| dGPU (RTX 4050) | — | nvidia-smi |
| Fan control | hp (hp-wmi) |
sysfs fan1_target, fan2_target |
Reports and testing from other HP Victus / hp-wmi users are welcome.
Issues and pull requests are welcome at github.com/isotjs/yet-another-fan-control.
If you test this on a different HP model, please report your hwmon paths, kernel version, and whether it works — this helps broaden compatibility.
This project is licensed under the GNU General Public License v3.0.