OmenMon v1.4.2-reborn: AC-flicker fan-switch fix & hibernation-guard hardening
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).BatteryGlitchGuardHoldAlwaysis
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
PowerLineStatusasOfflinefor a few seconds even though the laptop is physically plugged in (confirmed in the user'spowercfg /batteryreportagainst AC adapter telemetry). Before this fix,SystemEvents.PowerModeChanged→GuiTray.EventPowerChange→GuiOp.PowerChange()reacted immediately and — whenAutoConfig=trueand a fan program was active — switched fromFanProgramDefault(Power) toFanProgramDefaultAlt(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:- AC-flicker debounce. On a
StatusChangeevent the actual reaction (fan-program switch + heartbeat toggle + main-form refresh) is deferred to the nextEventTimerTickthat arrives afterAcFlickerHoldMs(default 10000 ms — bumped from the first-pass 8 s after re-reading the field reports, several of which describe ~10 s flickers). - Multi-sample confirmation. When the hold window elapses the deferred handler now reads
IsFullPowerConfirmedAcFlickerConfirmSamplestimes (default 3) withAcFlickerConfirmIntervalMs(default 250 ms) between reads and only acts if every sample agrees. A dissenter re-queues the deferral; the cascade caps atAcFlickerMaxDeferralMs(default 60000 ms) so a pathological flapper still reaches a decision in bounded time. - Multi-source AC check (
Settings.IsFullPowerConfirmed). Cross-references three independent signals — WindowsPowerLineStatus,BatteryChargeStatus.Charging, and the HP firmware smart-adapter query (BIOS Cmd Legacy0x0F) — 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 toIsFullPower. Used by the new confirmation gate, the passive poll, the heartbeat-resume path, andOp.PowerChange. - Passive AC-state poll. The GUI timer tick now checks
PowerLineStatusand synthesises a deferred change when the live state diverges fromOp.FullPowerwithout aPowerModeChangedevent already in flight, recovering from cases where Windows drops or coalesces the event during rapid-fire flickers. Controlled byAcFlickerPassivePoll(defaulttrue). PowerGuardAC-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 instantPowerLineStatusflipped toOffline, 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 onAcFlickerHoldMsof sustainedOfflineand anIsFullPowerConfirmedcross-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, defaulttrue),AcFlickerHoldMs(default10000, range0..60000),AcFlickerConfirmSamples(default3, range1..20),AcFlickerConfirmIntervalMs(default250, range10..2000),AcFlickerMaxDeferralMs(default60000, range1..60000),AcFlickerPassivePoll(boolean, defaulttrue). SetAcFlickerGuard=falseorAcFlickerHoldMs=0to restore the immediate-switch behaviour from earlier builds. The guard is independent of the existingBatteryGlitchGuard(which targets a different symptom — a torn battery-percent read while still on AC) and the two run side by side; thePowerGuardchange above is the bridge that makes them coexist correctly when both fire at once. - AC-flicker debounce. On a
-
Auto-Calibration Wizard locked fans at ~3500 RPM after running (issue #65, reported by @MartinSalg818). In the wizard teardown
finallycleanup block, if the system was on automatic BIOS control before running calibration, OmenMon now writes0xFF, 0xFFto 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 helperPlatform.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 aHasGpuTemperatureSensor()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.AssertGuardcould 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 underlyingSetThreadExecutionStateP/Invoke threw, the originalBatteryGlitchGuardHoldAlwaysbranch still wroteguardUntil = DateTime.MaxValue, soIsGuardActive()would have returned true permanently while the OS never received the wake-lock request; and (2)SetThreadExecutionStateactually 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.AssertGuardnow returnsboolreflecting real success: it captures theExecutionStatereturn value, and when it isNone(0) disambiguates a genuine failure from the legitimate "previous state was None on the first call after process start" case viaMarshal.GetLastWin32Error(). The P/Invoke inExternal/Kernel.cswas already declaredSetLastError=truefor 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 newKernel32.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)BatteryGlitchGuardHoldAlwaysis now evaluated before theBatteryGlitchGuardmaster-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 offguardUntilrather than the config flags, so a failedSetThreadExecutionStateis 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
DirectMultByteMinthreshold inEcDiffScanner.csfrom 10 to 2 to support low fan idle speeds (e.g. 300 RPM / 0x03). Also fixed the 2023+ Layout B heuristic check inAutoDetector.csto allowcput == 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 at0xF1(DirectMultiplier8) and mapped it natively inAutoCal.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-Diagreport: AutoDetector matched the canonical 2022 layout — CPUT plausible at0x57, 16-bit LE tach at0xB0/0xB2— and BIOS reportsGetFanCount=2,GetGpuMode=Optimus,Fan1=Cpu / Fan2=Gpu. GPTM at0xB7flips between0x00and ~0x27(39 °C) as the discrete GPU parks under Optimus, so the newHasObservedGpuTemperaturesticky-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 newer0x3A/0x3Brate-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 (CPU0/1476/3218/3422, GPU0/1448/3115/3363) for a 16-bit LE tach at0xB0/0xB2, andFanLevel 0x34/0x35+FanRate 0x2C-0x2Ftrack the fan setting across all four steps — i.e. the canonical 2022 layout (identical register set to the 8A42 entry above). BIOS reportsGetFanCount=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 the0x2C/0x2D→0x3A/0x3Brate-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, defaultfalse): Enables the glitch guard to run even on battery power.BatteryGlitchGuardDisableTimeout(boolean, defaultfalse): Disables the 60-second safety timeout for sustained glitches, preventing the wake-lock from releasing during long loads.BatteryGlitchGuardHoldAlways(boolean, defaultfalse): Permanently assertsES_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, defaulttrue): Master switch; setfalseto react immediately to everyPowerModeChanged StatusChangeevent (the pre-v1.4.2 behaviour).AcFlickerHoldMs(integer ms, default10000, range0..60000): How long the new line-status must remain stable before OmenMon applies the corresponding fan-program / heartbeat change.0is equivalent toAcFlickerGuard=false. Also gates thePowerGuardAC-offline release.AcFlickerConfirmSamples(integer, default3, range1..20): Number of consecutive samples the deferred handler reads at the end of the hold window — every sample must agree before the change applies.1disables multi-sample confirmation (single read, the first-pass v1.4.2 behaviour).AcFlickerConfirmIntervalMs(integer ms, default250, range10..2000): Gap between confirmation samples.AcFlickerMaxDeferralMs(integer ms, default60000, range1..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, defaulttrue): Synthesises a deferred change from the GUI timer tick whenPowerLineStatusdiverges fromOp.FullPowerwithout aPowerModeChangedevent — 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, theAutoConfigstartup path, and thePowerGuardAC-offline release. Cross-references WindowsPowerLineStatus,BatteryChargeStatus.Charging, and the HP firmware smart-adapter query (BIOS Cmd Legacy0x0F, returningMeetsRequirement/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 existingIsFullPower.