Skip to content

Add victus-fan: userspace stock-driver fan controller for Ubuntu / Secure Boot#17

Open
jayeshbarman wants to merge 1 commit into
Batuhan4:mainfrom
jayeshbarman:feat/victus-fan-userspace
Open

Add victus-fan: userspace stock-driver fan controller for Ubuntu / Secure Boot#17
jayeshbarman wants to merge 1 commit into
Batuhan4:mainfrom
jayeshbarman:feat/victus-fan-userspace

Conversation

@jayeshbarman

Copy link
Copy Markdown

What this adds

victus-fan/ — a self-contained userspace fan controller for HP Victus / Omen laptops, for environments where the patched hp-wmi DKMS module cannot be used.

The README already notes the project "does not currently ship a full Ubuntu installer." This addresses exactly that gap. On Ubuntu with Secure Boot enabled, the existing flow cannot run — Ubuntu ships no patched hp-wmi, and an unsigned out-of-tree module is rejected by Secure Boot. victus-fan works there because it uses only the stock, in-tree hp-wmi driver — nothing is compiled, signed, or DKMS-built.

How it works

Stock hp-wmi exposes a two-state pwm1_enable (0 = MAX, 2 = firmware AUTO; 1 is rejected, and there is no PWM-duty / fan-target file). So a small root daemon (victus-fand) maps your power profile + CPU / iGPU / NVIDIA temperatures onto those two states with hysteresis:

Power mode Behaviour
Performance always MAX
Balanced firmware AUTO, forced to MAX on heat
Power-saver / low battery firmware AUTO, MAX only at higher temps
  • Reads the NVIDIA dGPU temperature via nvidia-smi only when it is already awake (never wakes a runtime-suspended dGPU, so it does not cost battery).
  • Restores firmware AUTO whenever it stops (handles SIGTERM).
  • Controlled with victus-fanctl (status / override / thresholds). No GUI.

Scope & safety

  • Added entirely under victus-fan/no existing files are changed except a short pointer added to the top-level README.
  • Licensed GPL-3.0-or-later to match the project; SPDX headers on the sources.
  • Daemon + CLI are pure Python 3 stdlib (no extra dependencies).
  • Additive and opt-in: the existing Fedora/Arch + kernel-module workflow is untouched.

Tested

Built and verified end-to-end on an HP Victus 15-fb0xxx running Ubuntu 26.04 LTS (kernel 7.0, Secure Boot on): all power modes, heat-triggered MAX, manual overrides, the restore-fans-on-stop safety path, and the group-based permission model. Full report: victus-fan/docs/TESTING.md.

…e Boot)

victus-fan is a self-contained userspace fan controller for setups that
cannot load the patched hp-wmi DKMS module — notably Ubuntu with Secure Boot
enabled, where unsigned out-of-tree modules are rejected.

It drives ONLY the stock in-tree hp-wmi pwm1_enable (two-state MAX/AUTO) and
ties fan behaviour to the active power profile plus CPU / iGPU / NVIDIA
temperatures, with hysteresis. Ships a root systemd daemon (victus-fand), a
CLI (victus-fanctl), systemd packaging and an Ubuntu installer. No kernel
module, no GUI.

Added under victus-fan/ so it sits alongside the existing kernel-module
controller without touching it (only a pointer is added to the top-level
README). Licensed GPL-3.0-or-later to match the project. Tested end-to-end on
an HP Victus 15-fb0xxx running Ubuntu 26.04 (see victus-fan/docs/TESTING.md).
@Batuhan4 Batuhan4 requested a review from Copilot June 15, 2026 19:54
@Batuhan4

Copy link
Copy Markdown
Owner

Thank you for your pr , will review it soon

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new victus-fan/ subproject: a self-contained userspace (Python stdlib) fan-control daemon + CLI intended to work on Ubuntu with Secure Boot by using only the stock in-tree hp-wmi interface (pwm1_enable two-state control), plus install/uninstall and systemd packaging.

Changes:

  • Introduces victus-fand (root systemd daemon) and victus-fanctl (user CLI) that map power profile + temps to pwm1_enable with hysteresis and a status file.
  • Adds Ubuntu-oriented install/uninstall scripts and systemd unit/tmpfiles + default config.
  • Updates top-level README to point Ubuntu/Secure Boot users to victus-fan/.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
victus-fan/install.sh Installs daemon/CLI/docs, creates victusfan group, sets up config, enables systemd service.
victus-fan/uninstall.sh Stops/disables service, restores firmware AUTO, removes installed artifacts.
victus-fan/daemon/victus-fand Root daemon implementing discovery, polling, hysteresis, and sysfs writes to pwm1_enable.
victus-fan/daemon/victus-fanctl CLI to inspect status and atomically edit /etc/victus-fan/config.json.
victus-fan/packaging/victus-fan.service systemd unit for running victus-fand as root.
victus-fan/packaging/victus-fan.tmpfiles Creates /run/victus-fan/ for the daemon’s status output.
victus-fan/packaging/config.default.json Default thresholds/settings for the daemon/CLI.
victus-fan/README.md Full user documentation for install, operation, config, and troubleshooting.
victus-fan/docs/TESTING.md Hardware-specific test/verification record.
README.md Adds a top-level pointer to the new Ubuntu/Secure Boot userspace flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread victus-fan/uninstall.sh
Comment on lines +49 to +53
rm -f /usr/lib/victus-fan/victus-fand || true
rmdir /usr/lib/victus-fan 2>/dev/null || true
rm -f /usr/bin/victus-fanctl || true
rm -f /etc/systemd/system/victus-fan.service || true
rm -f /etc/tmpfiles.d/victus-fan.conf || true
Comment thread victus-fan/uninstall.sh

SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Re-exec with sudo if not root; keep SUDO_USER for the extension cleanup.
Comment thread victus-fan/install.sh
Comment on lines +44 to +46
echo "!! WARNING: could not resolve a non-root desktop user via SUDO_USER."
echo "!! Group membership and the GNOME extension will be skipped."
echo "!! Re-run with: sudo ./install.sh (from your user session)."
Comment on lines +2 to +3
# The daemon writes /run/victus-fan/status.json (mode 0644, world-readable) here
# each poll loop. UIs (GTK app, GNOME extension, CLI) read that status file.
NVIDIA_SMI_TIMEOUT_SEC = 2

# ----------------------------------------------------------------------------
# Default configuration (single source of truth mirrored in config.default.json)

def find_nvidia_pci_device():
"""
Locate the NVIDIA dGPU PCI device (vendor 0x10de, class starts 0x0300).
klass = read_text(os.path.join(d, "class"))
if vendor is None or klass is None:
continue
if vendor.lower() == NVIDIA_VENDOR and klass.lower().startswith("0x0300"):
Comment on lines +426 to +430
on_thr = thresholds.get(label + "_on")
off_thr = thresholds.get(label + "_off")
if on_thr is None or off_thr is None:
continue
present.append((label, val, on_thr, off_thr))
Comment on lines +475 to +477
override = config.get("override", "policy")
profile_map = config.get("profile_map", {})
low_battery_pct = config.get("low_battery_pct", 20)
Comment on lines +496 to +498
profiles = config.get("profiles", {})
thresholds = profiles.get(effective)
if not isinstance(thresholds, dict):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants