feat(sensors): sysfs thermal fallback + SBC fleet coverage [#341][#457][#458]

lm-sensors is silent on SBCs (Pis, Jetson-class), so the wrapper now falls
back to /sys/class/thermal zones rendered in lm-sensors format when native
sensors output is empty; suppressed when native output exists to avoid
duplicates. TDD: 3 new unit tests (12 green). Sensor stack deployed to
jetson + pfvsvrpi + 3 subopis (verified from poller), HA pack extended to
16 sensors across 14 hosts, high-temp automation covers the SBC fleet.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-27 12:00:43 -05:00
parent 2b6fc2b388
commit 5977eba503
4 changed files with 178 additions and 3 deletions
+58 -1
View File
@@ -131,7 +131,7 @@ test_main_merge() {
test_main_neither_fails() {
make_stub_lsusb noprobe
if PATH="$STUB_DIR:$PATH" NATIVE_SENSORS_BIN=/nonexistent \
VENV_PY=/nonexistent main >/dev/null 2>&1; then
VENV_PY=/nonexistent SYSFS_THERMAL_ROOT=/nonexistent main >/dev/null 2>&1; then
echo "❌ main: succeeded with no sensor sources"
((++failed))
else
@@ -139,6 +139,60 @@ test_main_neither_fails() {
fi
}
# sysfs thermal-zone fallback (SBCs: Pis, Jetson-class, /sys/class/thermal)
make_sysfs_fixture() {
SYSFS_FIXTURE="$STUB_DIR/sysfs/class-thermal"
mkdir -p "$SYSFS_FIXTURE/thermal_zone0" "$SYSFS_FIXTURE/thermal_zone1"
printf 'cpu-thermal\n' >"$SYSFS_FIXTURE/thermal_zone0/type"
printf '45200\n' >"$SYSFS_FIXTURE/thermal_zone0/temp"
printf 'gpu-thermal\n' >"$SYSFS_FIXTURE/thermal_zone1/type"
printf '52350\n' >"$SYSFS_FIXTURE/thermal_zone1/temp"
}
test_format_sysfs_block() {
local got expected
expected=$(printf 'soc-thermal-virtual-0\nAdapter: Virtual device\ntemp1: +45.2°C\ntemp2: +52.4°C')
got="$(printf 'cpu-thermal 45200\ngpu-thermal 52350\n' | format_sysfs_block "$(printf 'cpu-thermal 45200\ngpu-thermal 52350\n')")"
if [[ "$got" == "$expected" ]]; then
echo "✅ format_sysfs_block renders lm-sensors format from millicelsius"
else
echo "❌ format_sysfs_block output mismatch"
printf 'got:\n%s\nexpected:\n%s\n' "$got" "$expected"
((++failed))
fi
}
test_main_sysfs_fallback() {
make_stub_lsusb noprobe
make_sysfs_fixture
local got
got="$(PATH="$STUB_DIR:$PATH" NATIVE_SENSORS_BIN=/nonexistent VENV_PY=/nonexistent \
SYSFS_THERMAL_ROOT="$SYSFS_FIXTURE" main)"
if grep -q 'temp1: *+45.2°C' <<<"$got" && grep -q 'soc-thermal-virtual-0' <<<"$got"; then
echo "✅ main: sysfs fallback when lm-sensors silent (SBC)"
else
echo "❌ main: sysfs fallback composition wrong"
printf 'got:\n%s\n' "$got"
((++failed))
fi
}
test_main_native_suppresses_sysfs() {
make_stub_lsusb noprobe
make_stub_sensors
make_sysfs_fixture
local got
got="$(PATH="$STUB_DIR:$PATH" NATIVE_SENSORS_BIN="$STUB_DIR/sensors" VENV_PY=/nonexistent \
SYSFS_THERMAL_ROOT="$SYSFS_FIXTURE" main)"
if grep -q 'Package id 0' <<<"$got" && ! grep -q 'soc-thermal-virtual-0' <<<"$got"; then
echo "✅ main: native output present, sysfs fallback suppressed (no duplicates)"
else
echo "❌ main: sysfs fallback leaked alongside native output"
printf 'got:\n%s\n' "$got"
((++failed))
fi
}
test_format_two_sensors
test_format_single_sensor
test_format_negative
@@ -147,6 +201,9 @@ test_main_native_only
test_main_temper_only
test_main_merge
test_main_neither_fails
test_format_sysfs_block
test_main_sysfs_fallback
test_main_native_suppresses_sysfs
if ((failed > 0)); then
echo "❌ lm-sensors extend: $failed test(s) failed"