Skip to content

OmenMon v1.4.2-reborn: AC-flicker fan-switch fix & hibernation-guard hardening

Choose a tag to compare

@github-actions github-actions released this 25 May 11:02
542d317

OmenMon-Reborn v1.4.2

Power-management reliability release: stops spurious fan-profile switching from rief AC dropouts, hardens the hibernation guard, and adds three more laptop models.

Highlights

  • No more random fan-profile switches mid-game from brief AC dropouts (#70).
    Some Omen/Victus SKUs report AC as "Offline" for a few seconds while still
    plugged in; OmenMon now debounces this (10 s hold + multi-sample confirmation +
    multi-source AC check + passive poll) instead of instantly swapping
    Power↔Silent and throttling the CPU.
  • Hibernation / battery-glitch guard hardened (#59). The wake-lock that
    suppresses Windows' bogus Critical-Battery hibernate now asserts and releases
    reliably (no leaks, no phantom-active state). BatteryGlitchGuardHoldAlways is
    now an independent "never sleep while OmenMon runs" switch — the bulletproof
    option if your machine still misreports battery.
  • GPU fan no longer ramps on CPU temperature when the dGPU is idle/off (#66).
  • Auto-Calibration fixes: detects low idle-RPM fans (#64) and no longer
    leaves fans locked at ~3500 RPM after a run (#65).

New laptop models

  • HP OMEN 17 ck1000nw (8A18) — #72
  • OMEN 16z-n000 (8A42, 2022) — #68
  • HP Victus 16 (8BB3, 2024 AMD) — #64
  • HP Victus 16 (88F4, 2022)

New configurable settings (OmenMon.xml)

AcFlickerGuard, AcFlickerHoldMs, AcFlickerConfirmSamples,
AcFlickerConfirmIntervalMs, AcFlickerMaxDeferralMs, AcFlickerPassivePoll,
plus BatteryGlitchGuardOnBattery / DisableTimeout / HoldAlways. See the
Battery-Glitch-Guard wiki page.

Closes #59, #64, #65, #66, #68, #70, #72.

Fixed

  • Random fan-profile switches mid-game caused by brief AC dropouts (issue #70, reported by @MartinSalg818; #59 reported by @NotDarkn confirms the cluster). Some HP Omen / Victus laptops occasionally report PowerLineStatus as Offline for a few seconds even though the laptop is physically plugged in (confirmed in the user's powercfg /batteryreport against AC adapter telemetry). Before this fix, SystemEvents.PowerModeChangedGuiTray.EventPowerChangeGuiOp.PowerChange() reacted immediately and — when AutoConfig=true and a fan program was active — switched from FanProgramDefault (Power) to FanProgramDefaultAlt (Silent → caps the system to Base Power), producing visible CPU/power-throttling stutters during gameplay until AC was reported back ~2 s later. The same handler also flipped the BIOS heartbeat enable/disable state. The fix is a five-layer defence:

    1. AC-flicker debounce. On a StatusChange event the actual reaction (fan-program switch + heartbeat toggle + main-form refresh) is deferred to the next EventTimerTick that arrives after AcFlickerHoldMs (default 10000 ms — bumped from the first-pass 8 s after re-reading the field reports, several of which describe ~10 s flickers).
    2. Multi-sample confirmation. When the hold window elapses the deferred handler now reads IsFullPowerConfirmed AcFlickerConfirmSamples times (default 3) with AcFlickerConfirmIntervalMs (default 250 ms) between reads and only acts if every sample agrees. A dissenter re-queues the deferral; the cascade caps at AcFlickerMaxDeferralMs (default 60000 ms) so a pathological flapper still reaches a decision in bounded time.
    3. Multi-source AC check (Settings.IsFullPowerConfirmed). Cross-references three independent signals — Windows PowerLineStatus, BatteryChargeStatus.Charging, and the HP firmware smart-adapter query (BIOS Cmd Legacy 0x0F) — and treats AC as connected when any of them confirm it. The BIOS call is gated so it only runs when both Windows signals report battery, keeping the common-case cost identical to IsFullPower. Used by the new confirmation gate, the passive poll, the heartbeat-resume path, and Op.PowerChange.
    4. Passive AC-state poll. The GUI timer tick now checks PowerLineStatus and synthesises a deferred change when the live state diverges from Op.FullPower without a PowerModeChanged event already in flight, recovering from cases where Windows drops or coalesces the event during rapid-fire flickers. Controlled by AcFlickerPassivePoll (default true).
    5. PowerGuard AC-offline release debounce. The percent-glitch guard's "release wake-lock on AC offline" branch (which existed to ensure a genuine critical-battery state could still hibernate) used to fire the instant PowerLineStatus flipped to Offline, silently defeating the guard whenever an AC flicker coincided with a percent torn-read (#59-style symptom on the same SKUs). The release is now gated on AcFlickerHoldMs of sustained Offline and an IsFullPowerConfirmed cross-check. Inside that window the percent-glitch state machine keeps running normally so a coinciding torn percent read is still detected.

    Configurable in OmenMon.xml: AcFlickerGuard (boolean, default true), AcFlickerHoldMs (default 10000, range 0..60000), AcFlickerConfirmSamples (default 3, range 1..20), AcFlickerConfirmIntervalMs (default 250, range 10..2000), AcFlickerMaxDeferralMs (default 60000, range 1..60000), AcFlickerPassivePoll (boolean, default true). Set AcFlickerGuard=false or AcFlickerHoldMs=0 to restore the immediate-switch behaviour from earlier builds. The guard is independent of the existing BatteryGlitchGuard (which targets a different symptom — a torn battery-percent read while still on AC) and the two run side by side; the PowerGuard change above is the bridge that makes them coexist correctly when both fire at once.

  • Auto-Calibration Wizard locked fans at ~3500 RPM after running (issue #65, reported by @MartinSalg818). In the wizard teardown finally cleanup block, if the system was on automatic BIOS control before running calibration, OmenMon now writes 0xFF, 0xFF to clear custom levels and release control back to the BIOS.

  • GPU fan curves reacted to CPU temperature when GPU is idle/off (issue #66, reported by @Bart82). Gated the CPU temperature fallback in FanProgram.Update() using a new helper Platform.HasObservedGpuTemperature(), which returns true once GPTM has produced a single non-zero reading since startup. When a physical GPU is present but powered off (0 °C), the flag stays latched true from earlier samples and the GPU fan stays at idle instead of ramping up under CPU load; on boards that genuinely have no GPU temp sensor the flag never latches and the CPU-temp fallback runs as in v1.4.1. (Initial v1.4.2 PR proposed a HasGpuTemperatureSensor() helper that inspected the configured sensor list, but the default global <Temperature> config always contains GPTM regardless of hardware, so the check evaluated true everywhere — Copilot review #1 on the v1.4.2 PR; corrected before release.)

  • PowerGuard.AssertGuard could leave internal state ahead of OS state (Copilot review #3 on the first v1.4.2 PR pass + Copilot review #1 on the second pass). Two bugs in the same code path: (1) when the underlying SetThreadExecutionState P/Invoke threw, the original BatteryGlitchGuardHoldAlways branch still wrote guardUntil = DateTime.MaxValue, so IsGuardActive() would have returned true permanently while the OS never received the wake-lock request; and (2) SetThreadExecutionState actually signals failure via its return value + GetLastWin32Error() rather than by throwing, so the first-pass fix that only caught exceptions still rubber-stamped failed calls as successful. AssertGuard now returns bool reflecting real success: it captures the ExecutionState return value, and when it is None (0) disambiguates a genuine failure from the legitimate "previous state was None on the first call after process start" case via Marshal.GetLastWin32Error(). The P/Invoke in External/Kernel.cs was already declared SetLastError=true for exactly this purpose. HoldAlways and transient-glitch latches both only advance on a confirmed success. Round-3 re-review hardened this further: (a) the last-error is now explicitly cleared via a new Kernel32.SetLastError(0) P/Invoke immediately before the call, because Windows does not guarantee clearing last-error on success and a stale value would otherwise misread the "previous state was None" path as a failure; (b) BatteryGlitchGuardHoldAlways is now evaluated before the BatteryGlitchGuard master-switch early-return, so it works as an independent "block sleep/hibernate while OmenMon runs" toggle even when the percent-glitch guard is disabled; and (c) IsGuardActive() now keys off guardUntil rather than the config flags, so a failed SetThreadExecutionState is never reported as an active guard.

  • Auto-Calibration Wizard failed to detect CPU fan on low idle speeds on 8BB3 (issue #64, reported by @jpcaldwell30). Lowered DirectMultByteMin threshold in EcDiffScanner.cs from 10 to 2 to support low fan idle speeds (e.g. 300 RPM / 0x03). Also fixed the 2023+ Layout B heuristic check in AutoDetector.cs to allow cput == 0x0F (observed on AMD-based 8BB3).

Added

  • HP Victus 16 (8BB3, 2024, AMD) in the native model database (OmenMon.xml). Stamped CPU fan speed register at 0xF1 (DirectMultiplier8) and mapped it natively in AutoCal.KnownBoards.
  • OMEN by HP Gaming Laptop 16z-n000 (8A42, 2022) in the native model database (OmenMon.xml, issue #68 reported by @GGoose). Confirmed via the user's -Diag report: AutoDetector matched the canonical 2022 layout — CPUT plausible at 0x57, 16-bit LE tach at 0xB0/0xB2 — and BIOS reports GetFanCount=2, GetGpuMode=Optimus, Fan1=Cpu / Fan2=Gpu. GPTM at 0xB7 flips between 0x00 and ~0x27 (39 °C) as the discrete GPU parks under Optimus, so the new HasObservedGpuTemperature sticky-flag from this release correctly latches once the dGPU produces a single non-zero sample. The remaining 2022-template registers (FanLevel 0x34/0x35, FanRateWrite 0x2C/0x2D, Mode 0x95, Switch 0xF4, etc.) are inferred from the AutoDetector template and mirror the values v1.4.1's auto-detection has been applying for this ProductId, so the entry is non-regressive — it just trades "Auto-detected" for a named native preset and removes the per-startup detection latency. XML comment documents the caveat that sibling 8A4C/8A4D use the newer 0x3A/0x3B rate-write pair in case a real 8A42 owner later reports custom-rate writes have no effect.
  • HP OMEN 17 ck1000nw (8A18, 2022) in the native model database (OmenMon.xml, issue #72 reported by @xenon205). Confirmed via the user's Auto-Calibration report: the raw EC dumps at 0/30/70/100 % decode exactly to the reported live RPM (CPU 0/1476/3218/3422, GPU 0/1448/3115/3363) for a 16-bit LE tach at 0xB0/0xB2, and FanLevel 0x34/0x35 + FanRate 0x2C-0x2F track the fan setting across all four steps — i.e. the canonical 2022 layout (identical register set to the 8A42 entry above). BIOS reports GetFanCount=2. The mode/switch/manual/countdown registers are inferred from the 2022 template and mirror what v1.4.1 auto-detection already applied for this ProductId, so the entry is non-regressive. As with 8A42, the XML comment notes the 0x2C/0x2D0x3A/0x3B rate-write caveat for the 8A4C/8A4D family in case a real owner reports custom-rate writes have no effect.
  • HP Victus 16 (88F4, 2022) in the native model database (OmenMon.xml) and safety list (FanArray.HasMaxFanFreeze). Added to safety list due to physical fan ceiling rate-limiter, with CPU/GPU speed registers overridden to 0x2E/0xB0.
  • Configurable hibernation guard settings (Library/PowerGuard.cs). Added three new XML configuration parameters to customize the battery-glitch guard or completely prevent Windows sleep/hibernation:
    • BatteryGlitchGuardOnBattery (boolean, default false): Enables the glitch guard to run even on battery power.
    • BatteryGlitchGuardDisableTimeout (boolean, default false): Disables the 60-second safety timeout for sustained glitches, preventing the wake-lock from releasing during long loads.
    • BatteryGlitchGuardHoldAlways (boolean, default false): Permanently asserts ES_SYSTEM_REQUIRED (wake-lock) while OmenMon is running to block sleep/hibernation completely.
  • AC-flicker debounce settings (App/Gui/GuiTray.cs, Library/PowerGuard.cs, Hardware/Settings.cs, issues #70 / #59). Six XML configuration parameters governing the five-layer flicker fix described in the Fixed section above:
    • AcFlickerGuard (boolean, default true): Master switch; set false to react immediately to every PowerModeChanged StatusChange event (the pre-v1.4.2 behaviour).
    • AcFlickerHoldMs (integer ms, default 10000, range 0..60000): How long the new line-status must remain stable before OmenMon applies the corresponding fan-program / heartbeat change. 0 is equivalent to AcFlickerGuard=false. Also gates the PowerGuard AC-offline release.
    • AcFlickerConfirmSamples (integer, default 3, range 1..20): Number of consecutive samples the deferred handler reads at the end of the hold window — every sample must agree before the change applies. 1 disables multi-sample confirmation (single read, the first-pass v1.4.2 behaviour).
    • AcFlickerConfirmIntervalMs (integer ms, default 250, range 10..2000): Gap between confirmation samples.
    • AcFlickerMaxDeferralMs (integer ms, default 60000, range 1..60000): Safety ceiling on cascaded re-deferrals when multi-sample confirmation keeps disagreeing. Once exceeded the change applies on the next read regardless of confirmation.
    • AcFlickerPassivePoll (boolean, default true): Synthesises a deferred change from the GUI timer tick when PowerLineStatus diverges from Op.FullPower without a PowerModeChanged event — recovers from cases where Windows drops or coalesces the event during rapid-fire flickers.
  • Settings.IsFullPowerConfirmed (Hardware/Settings.cs). New multi-source AC-presence check used by the AC-flicker debounce, the passive poll, the heartbeat-resume path, Op.PowerChange, the AutoConfig startup path, and the PowerGuard AC-offline release. Cross-references Windows PowerLineStatus, BatteryChargeStatus.Charging, and the HP firmware smart-adapter query (BIOS Cmd Legacy 0x0F, returning MeetsRequirement / BelowRequirement / BatteryPower / ...) and treats AC as connected if any source confirms it. The BIOS call is gated to fire only when both Windows signals report battery, so the common-case cost is identical to the existing IsFullPower.