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
+39 -2
View File
@@ -14,6 +14,7 @@ set -euo pipefail
VENV_PY="${VENV_PY:-/opt/temper-venv/bin/python}"
NATIVE_SENSORS_BIN="${NATIVE_SENSORS_BIN:-/usr/bin/sensors}"
SYSFS_THERMAL_ROOT="${SYSFS_THERMAL_ROOT:-/sys/class/thermal}"
CHIP_NAME="${CHIP_NAME:-temper-usb-1}"
# Render lm-sensors format block from internal/external Celsius values.
@@ -71,10 +72,41 @@ run_native_sensors() {
"$NATIVE_SENSORS_BIN" 2>/dev/null
}
# SBC fallback (Raspberry Pi et al.): lm-sensors often reports nothing; SoC
# temperatures live in /sys/class/thermal/thermal_zone*/{type,temp} (mC).
read_sysfs_thermal() {
[[ -d "$SYSFS_THERMAL_ROOT" ]] || return 1
local z type mc found=0
for z in "$SYSFS_THERMAL_ROOT"/thermal_zone[0-9]*; do
[[ -r "$z/type" && -r "$z/temp" ]] || continue
type="$(<"$z/type")" mc="$(<"$z/temp")"
[[ "$mc" =~ ^-?[0-9]+$ ]] || continue
printf '%s %s\n' "$type" "$mc"
found=1
done
((found)) || return 1
}
# Render lm-sensors format block from read_sysfs_thermal lines "type millicelsius".
format_sysfs_block() {
local input="${1:-}" type mc signed n=0
[[ -n "$input" ]] || return 1
printf 'soc-thermal-virtual-0\nAdapter: Virtual device\n'
while read -r type mc; do
((++n))
signed="$(awk -v mc="$mc" 'BEGIN { printf "%.1f", mc / 1000 }')"
signed="${signed#+}"
[[ "$signed" == -* ]] || signed="+$signed"
printf 'temp%d: %s°C\n' "$n" "$signed"
done <<<"$input"
}
main() {
local out="" block internal external native
local out="" block internal external native="" sysinput
if native="$(run_native_sensors)" && [[ -n "$native" ]]; then
out+="$native"$'\n'
else
native=""
fi
if has_temper_probe; then
if block="$(read_temper 2>/dev/null)" \
@@ -85,8 +117,13 @@ main() {
echo "warn: TEMPer probe present but read failed" >&2
fi
fi
if [[ -z "$native" ]] \
&& sysinput="$(read_sysfs_thermal 2>/dev/null)" \
&& block="$(format_sysfs_block "$sysinput")"; then
out+="$block"$'\n'
fi
[[ -n "$out" ]] || {
echo "error: no sensor sources available (lm-sensors pkg? TEMPer venv?)" >&2
echo "error: no sensor sources available (lm-sensors pkg? TEMPer venv? sysfs thermal?)" >&2
return 1
}
printf '%s' "$out"