A NitroSense-style desktop app for controlling fan speeds on ASUS laptops: live temp/RPM gauges, built-in and custom presets, a software fan curve ("Automatic (Override)"), and a system tray.
Built on top of Karmel0x/AsusFanControl —
this project adds a UI, elevation flow, presets, and curve logic on top of the
same driver calls that project's AsusFanControl.exe CLI makes. The app calls
ASUS's AsusWinIO64.dll directly (no subprocess per reading) and does not
bundle or run that CLI. Credits and notices are in
THIRD_PARTY_NOTICES.txt.
AsusWinIO64.dll is not redistributable, so it is not bundled: the app copies
it from the local ASUS driver store (installed by MyASUS) at startup — see
fan_control.py.
To reach SYSTEM (which the fan driver requires), the app duplicates a SYSTEM
process token itself rather than shipping PsExec — see
system_launch.py.
- Windows (uses Task Scheduler, UAC, and the Win32 API — Windows-only by design)
- MyASUS / ASUS System Control Interface installed (provides
AsusWinIO64.dlland the fan driver; the ASUS System Analysis service must be running) - Python 3.11+ to run from source or build the
.exe
- Live CPU temp + per-fan RPM/% gauges, with a rolling history graph
- Automatic (Default) — hands control back to the motherboard's EC
- Automatic (Override) — a draggable temp→speed curve you define, applied with a minimum floor and hysteresis to avoid audible fan-speed hunting
- Built-in presets (Silent/Balanced/Performance/Turbo) plus your own, saved/deleted from the sidebar
- Minimizes to the system tray instead of quitting; polling and any active curve keep running in the background
- Optional "Start with Windows" (see below)
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python run.pyThe app self-elevates on launch (UAC to reach Administrator, then a token-
duplication step to reach SYSTEM — required for the fan driver to actually
respond; see elevation.py), so expect a
UAC prompt.
For UI development without hardware access, skip elevation:
set ASUSFANCONTROL_SKIP_ELEVATION=1
.venv\Scripts\python run.pyFan reads/writes will fail without SYSTEM privilege in this mode, but the UI is fully usable for layout/style work.
.venv\Scripts\python -m pytestDouble-click build.bat, or run it from a terminal:
build.batIt creates .venv if missing, installs requirements.txt, runs the tests,
then runs PyInstaller. Pass --skip-tests to skip the test run. Quit the app
from its tray icon first: a running exe can't be overwritten.
build.bat is a thin launcher for build.ps1, which pins the
current step to a status line at the top of the window and streams the build
logs below it (falling back to plain output where the console lacks ANSI
support).
To run PyInstaller directly instead:
.venv\Scripts\pyinstaller build.spec --noconfirmBoth produce dist/AsusFanControlUI.exe — a single-file, windowed, self-elevating
executable with the app icon embedded.
To publish a release, push a version tag. The Release workflow installs the
fully pinned dependencies from requirements-lock.txt,
runs the tests, builds the exe, and attaches it plus a SHA-256 checksum file to
a new GitHub Release:
git tag v1.0.0 && git push origin v1.0.0Regenerate requirements-lock.txt (instructions are in its header) whenever
requirements.txt changes. Dependabot opens weekly PRs for dependency and
GitHub Actions updates.
To verify a downloaded exe against the release's AsusFanControlUI.exe.sha256:
(Get-FileHash .\AsusFanControlUI.exe -Algorithm SHA256).Hash.ToLower()
# compare with the hash in AsusFanControlUI.exe.sha256The exe is not code-signed, so Windows SmartScreen may warn on first run.
The easiest way: open the app, go to Settings in the sidebar, and check Start with Windows.
This does not use a Startup-folder shortcut or a registry Run key — those
would trigger a fresh UAC prompt every single login, since this app
self-elevates on launch. Instead it registers a Task Scheduler task named
_ZAsusFanController, set to run at logon with the highest privileges, so it
launches already elevated with no prompt. See
autostart.py.
You can also add or remove that task without opening the app at all, using
add_startup_task.bat / remove_startup_task.bat
(both prompt for elevation, then register/unregister the same
_ZAsusFanController task — add_startup_task.bat expects the exe to
already be built at dist\AsusFanControlUI.exe).
Or do it manually, from an elevated PowerShell prompt (this is what the app
and the .bat files actually run — schtasks /Create has no way to set a
description, so this uses the ScheduledTasks module instead):
# register
# (use the actual logged-on user here, not $env:USERNAME — if this is run
# from a SYSTEM-elevated context, as the app itself does, $env:USERNAME
# won't resolve to a valid account and Register-ScheduledTask will fail
# with "No mapping between account names and security IDs was done")
$UserName = (Get-CimInstance -ClassName Win32_ComputerSystem).UserName
$Action = New-ScheduledTaskAction -Execute "C:\path\to\AsusFanControlUI.exe"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
$Principal = New-ScheduledTaskPrincipal -UserId $UserName -RunLevel Highest
Register-ScheduledTask -TaskName "_ZAsusFanController" -Action $Action -Trigger $Trigger `
-Principal $Principal -Description "Launches ASUS Fan Controller, already elevated, at logon." -Force
# check it's there
Get-ScheduledTask -TaskName "_ZAsusFanController"
# remove it
Unregister-ScheduledTask -TaskName "_ZAsusFanController" -Confirm:$falsesrc/asusfancontrol/
assets/ app icon (AsusWinIO64.dll copied here at runtime)
ui/ PySide6 widgets (sidebar, gauges, graph, curve editor, tray, splash)
fan_control.py the only module that talks to the fan driver (AsusWinIO64.dll via ctypes)
worker.py background thread that owns all fan I/O, so the UI never blocks
app_controller.py mode/preset/curve state, wired to the UI via Qt signals
elevation.py UAC -> SYSTEM self-elevation, mirroring the original FanController.bat
autostart.py Task Scheduler registration for "Start with Windows"
config.py JSON persistence (%ProgramData%\AsusFanControlUI\config.json;
%APPDATA%\... when ASUSFANCONTROL_SKIP_ELEVATION=1)
tests/ unit tests for curve math, config persistence, fan-driver call sequences (against a fake library)
docs/superpowers/specs/ design notes from planning this project
add_startup_task.bat registers the _ZAsusFanController startup task
remove_startup_task.bat removes it