From 20a8b3b903786033c2195865d6ee5c07da9a2bd8 Mon Sep 17 00:00:00 2001 From: IamBenny <9753186420a@gmail.com> Date: Mon, 10 Aug 2026 23:08:22 +0800 Subject: [PATCH 1/2] fix: treat thermal zones with never-changing readings as unavailable Some firmware exposes an ACPI thermal zone that is not wired to a real sensor and always reports the same fixed value (e.g. 301 K / 27.85 C). Track whether performance counter readings have ever varied since startup; after 24 identical consecutive reads (~2 minutes at the current fetch cadence) treat the zone as a placeholder and hide the temperature instead of showing a misleading constant. Detection is self-correcting: if a reading later changes, the temperature is shown again permanently. Co-Authored-By: Claude Fable 5 --- RunCat365/TemperatureRepository.cs | 45 +++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/RunCat365/TemperatureRepository.cs b/RunCat365/TemperatureRepository.cs index ecbe1a0..a5975ab 100644 --- a/RunCat365/TemperatureRepository.cs +++ b/RunCat365/TemperatureRepository.cs @@ -75,10 +75,14 @@ internal class TemperatureRepository private const float MIN_VALID_TEMPERATURE_CELSIUS = -50.0f; private const float MAX_VALID_TEMPERATURE_CELSIUS = 150.0f; private const int REFRESH_INTERVAL_TICKS = 30; + private const int STALE_READ_THRESHOLD = 24; private readonly TemperaturePerformanceCounters? counters; + private readonly List baselineRawValues = []; private TemperatureInfo? temperatureInfo; private int ticksSinceLastRefresh; + private int identicalReadCount; + private bool hasEverVaried; internal bool IsAvailable => counters is not null; @@ -99,6 +103,8 @@ internal void Update() } var rawValues = counters.ReadValues(); + TrackValueVariation(rawValues); + var temperaturesCelsius = new List(rawValues.Count); foreach (var temperatureKelvin in rawValues) { @@ -108,7 +114,7 @@ internal void Update() temperaturesCelsius.Add(temperatureCelsius); } - temperatureInfo = temperaturesCelsius.Count == 0 + temperatureInfo = temperaturesCelsius.Count == 0 || IsLikelyPlaceholder() ? null : new TemperatureInfo { @@ -117,6 +123,43 @@ internal void Update() }; } + // Some firmware exposes an ACPI thermal zone that is not wired to a real + // sensor and always reports the same fixed value (e.g. 301 K). Track whether + // the readings have ever changed so such zones can be treated as unavailable + // instead of showing a misleading constant temperature. + private void TrackValueVariation(List rawValues) + { + if (hasEverVaried || rawValues.Count == 0) return; + + var sortedValues = new List(rawValues); + sortedValues.Sort(); + + if (baselineRawValues.Count != sortedValues.Count) + { + baselineRawValues.Clear(); + baselineRawValues.AddRange(sortedValues); + identicalReadCount = 1; + return; + } + + for (var i = 0; i < sortedValues.Count; i++) + { + if (baselineRawValues[i] != sortedValues[i]) + { + hasEverVaried = true; + baselineRawValues.Clear(); + return; + } + } + + identicalReadCount += 1; + } + + private bool IsLikelyPlaceholder() + { + return !hasEverVaried && STALE_READ_THRESHOLD <= identicalReadCount; + } + internal TemperatureInfo? Get() { return temperatureInfo; From f9eebc9442d9115a2a82111c935b80bb8210850f Mon Sep 17 00:00:00 2001 From: IamBenny <9753186420a@gmail.com> Date: Mon, 10 Aug 2026 23:36:34 +0800 Subject: [PATCH 2/2] fix: track thermal zone variation on the high precision counter when available The whole-Kelvin Temperature counter can legitimately stay flat for minutes on an idle machine, which would misclassify a real sensor as a placeholder. The High Precision Temperature counter (0.1 K granularity) jitters on live sensors even at idle, so prefer it for variation tracking; fall back to the Temperature counter values when it is not available. The tracking source is fixed at construction so readings are never compared across the two counters' different scales. Co-Authored-By: Claude Fable 5 --- RunCat365/TemperatureRepository.cs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/RunCat365/TemperatureRepository.cs b/RunCat365/TemperatureRepository.cs index a5975ab..311d33b 100644 --- a/RunCat365/TemperatureRepository.cs +++ b/RunCat365/TemperatureRepository.cs @@ -69,6 +69,18 @@ internal sealed class TemperaturePerformanceCounters : InstancedPerformanceCount } } + internal sealed class TemperatureHighPrecisionPerformanceCounters : InstancedPerformanceCounters + { + protected override string CategoryName => "Thermal Zone Information"; + protected override string CounterName => "High Precision Temperature"; + + internal static TemperatureHighPrecisionPerformanceCounters? TryCreate() + { + var instance = new TemperatureHighPrecisionPerformanceCounters(); + return instance.TryInitialize() ? instance : null; + } + } + internal class TemperatureRepository { private const float KELVIN_TO_CELSIUS_OFFSET = 273.15f; @@ -78,6 +90,7 @@ internal class TemperatureRepository private const int STALE_READ_THRESHOLD = 24; private readonly TemperaturePerformanceCounters? counters; + private readonly TemperatureHighPrecisionPerformanceCounters? highPrecisionCounters; private readonly List baselineRawValues = []; private TemperatureInfo? temperatureInfo; private int ticksSinceLastRefresh; @@ -89,6 +102,7 @@ internal class TemperatureRepository internal TemperatureRepository() { counters = TemperaturePerformanceCounters.TryCreate(); + highPrecisionCounters = TemperatureHighPrecisionPerformanceCounters.TryCreate(); } internal void Update() @@ -100,10 +114,12 @@ internal void Update() { ticksSinceLastRefresh = 0; counters.RefreshInstances(); + highPrecisionCounters?.RefreshInstances(); } var rawValues = counters.ReadValues(); - TrackValueVariation(rawValues); + var variationValues = highPrecisionCounters is null ? rawValues : highPrecisionCounters.ReadValues(); + TrackValueVariation(variationValues); var temperaturesCelsius = new List(rawValues.Count); foreach (var temperatureKelvin in rawValues) @@ -126,7 +142,12 @@ internal void Update() // Some firmware exposes an ACPI thermal zone that is not wired to a real // sensor and always reports the same fixed value (e.g. 301 K). Track whether // the readings have ever changed so such zones can be treated as unavailable - // instead of showing a misleading constant temperature. + // instead of showing a misleading constant temperature. Variation is tracked + // on the "High Precision Temperature" counter (0.1 K granularity) when it is + // available, because a live sensor jitters at that resolution even when the + // whole-Kelvin "Temperature" counter stays flat on an idle machine; the + // tracking source is fixed at construction so values are never compared + // across the two counters' different scales. private void TrackValueVariation(List rawValues) { if (hasEverVaried || rawValues.Count == 0) return; @@ -168,6 +189,7 @@ private bool IsLikelyPlaceholder() internal void Close() { counters?.Close(); + highPrecisionCounters?.Close(); } } }