fix(host_agent): SYSTIN and other board inputs past 100 °C don't set the temperature (#5759)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m21s
CI / unit (push) Successful in 1m26s
CI / publish (push) Successful in 17s

#5759 "VDNT-Util01 reads 114 °C: its "hottest sensor" is a Super-I/O input
with nothing wired to it". b3885c3 left out AUXTIN*, but Util01's SYSTIN
reads 114 °C too, so temp_c_max stayed at 114. A board's Super-I/O inputs
(SYSTIN, CPUTIN, AUXTIN*, PCH_*) now count only below 100 °C: a wired one
reads the board, far below that, and still counts on other boards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 17:26:02 -04:00
co-authored by Claude Opus 5.5
parent b6c4ce510d
commit 82a2a3d41f
2 changed files with 26 additions and 7 deletions
+16 -6
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
import hashlib
import re
import time
from datetime import datetime, timezone
from typing import Any
@@ -90,15 +91,24 @@ def pick_host_address(current: str | None, reported: str | None) -> str | None:
return None
# A board's Super-I/O inputs (Nuvoton/ITE: SYSTIN, CPUTIN, AUXTIN0-4, PCH_*)
# are thermistor pins that are often left unwired, and a floating one reads a
# junk constant. Wired, they read the board, which runs far below this.
_BOARD_INPUT = re.compile(r"^(SYSTIN|CPUTIN|AUXTIN\d*|PCH_)", re.IGNORECASE)
BOARD_INPUT_CEILING_C = 100
def counts_as_hottest(label: str, celsius: float) -> bool:
"""Whether a sensor's reading may set the host's temperature (#5759).
A Super-I/O chip's AUXTIN inputs are spare thermistor pins, usually left
unconnected, and a floating one reads a junk constant (VDNT-Util01: 110 °C
flat at every load while its CPU package moved 49-87). No running host's
sensor reads 0 or below, and chips shut down before 125. Each sensor's own
row is kept either way, so a wired AUXTIN can still be charted by name."""
return 0 < celsius < 125 and not label.upper().startswith("AUXTIN")
VDNT-Util01's SYSTIN read 114 °C and AUXTIN0-4 105-112, flat at every
load, while its CPU package moved 49-87 and CPUTIN read 49: unwired
inputs. A board input at or past 100 °C is one of those; below it, it
counts. No running host's sensor reads 0 or below, and chips shut down
before 125. Each sensor's own row is kept either way."""
if not 0 < celsius < 125:
return False
return not (_BOARD_INPUT.match(label) and celsius >= BOARD_INPUT_CEILING_C)
def _expand_sample_to_metrics(
@@ -85,7 +85,8 @@ def test_a_sensor_with_nothing_wired_to_it_does_not_set_the_host_s_temperature()
moved 49-87, so the host read critical all week. Every sensor keeps its
own row; only readings that can be real set the max."""
sample = {"ts": "2026-10-14T21:00:00+00:00",
"temps": [{"label": "AUXTIN0", "celsius": 110.0},
"temps": [{"label": "SYSTIN", "celsius": 114.0},
{"label": "AUXTIN0", "celsius": 110.0},
{"label": "PCH_CHIP_CPU_MAX_TEMP", "celsius": 0.0},
{"label": "AUXTIN3", "celsius": -1.0},
{"label": "Package id 0", "celsius": 60.0}]}
@@ -104,3 +105,11 @@ def test_stalled_mounts_are_counted_and_have_no_figures():
assert idx[("mounts_stalled", "d3")] == 1
assert idx[("disk_used_pct_worst", "d3")] == 50.0
assert ("disk_used_pct", "d3:/nfs/media") not in idx
def test_a_wired_board_input_still_counts():
"""#5759: below 100 °C a board input is a real reading."""
from plugins.host_agent.routes import counts_as_hottest
assert counts_as_hottest("SYSTIN", 38.0) and counts_as_hottest("AUXTIN1", 60.0)
assert not counts_as_hottest("SYSTIN", 114.0) and not counts_as_hottest("PCH_CHIP_TEMP", 0.0)
assert counts_as_hottest("Package id 0", 101.0) # a CPU past 100 is a hot CPU