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:
@@ -120,6 +120,11 @@
|
|||||||
- sensor.pfv_tsys6_cpu_temperature
|
- sensor.pfv_tsys6_cpu_temperature
|
||||||
- sensor.pfv_tsys7_cpu_temperature
|
- sensor.pfv_tsys7_cpu_temperature
|
||||||
- sensor.pfv_tsys9_cpu_temperature
|
- sensor.pfv_tsys9_cpu_temperature
|
||||||
|
- sensor.pfv_jetson_temperature
|
||||||
|
- sensor.pfv_svrpi_temperature
|
||||||
|
- sensor.pfv_subopi3_temperature
|
||||||
|
- sensor.pfv_subopi_dev_3_temperature
|
||||||
|
- sensor.pfv_subopi_dev_4_temperature
|
||||||
above: 104
|
above: 104
|
||||||
actions:
|
actions:
|
||||||
- action: notify.send_message
|
- action: notify.send_message
|
||||||
|
|||||||
@@ -197,6 +197,82 @@ sensor:
|
|||||||
{% set m = value | regex_findall('Package id 0:\s*\+?(-?\d+\.?\d*)') %}
|
{% set m = value | regex_findall('Package id 0:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
|
# --- SBC fleet (sysfs thermal fallback in lmsensors-extend v2; temp1 = zone0/SoC) ---
|
||||||
|
- platform: snmp
|
||||||
|
name: "PFV jetson temperature"
|
||||||
|
host: pfv-jetson-nano-1.knel.net
|
||||||
|
version: 2c
|
||||||
|
community: kn3lmgmt
|
||||||
|
baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115
|
||||||
|
accept_errors: true
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "°F"
|
||||||
|
unique_id: pfv_jetson_temperature
|
||||||
|
value_template: >-
|
||||||
|
{% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
|
- platform: snmp
|
||||||
|
name: "PFV svrpi temperature"
|
||||||
|
host: pfvsvrpi.knel.net
|
||||||
|
version: 2c
|
||||||
|
community: kn3lmgmt
|
||||||
|
baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115
|
||||||
|
accept_errors: true
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "°F"
|
||||||
|
unique_id: pfv_svrpi_temperature
|
||||||
|
value_template: >-
|
||||||
|
{% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
|
- platform: snmp
|
||||||
|
name: "PFV subopi3 temperature"
|
||||||
|
host: subopi3.knel.net
|
||||||
|
version: 2c
|
||||||
|
community: kn3lmgmt
|
||||||
|
baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115
|
||||||
|
accept_errors: true
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "°F"
|
||||||
|
unique_id: pfv_subopi3_temperature
|
||||||
|
value_template: >-
|
||||||
|
{% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
|
- platform: snmp
|
||||||
|
name: "PFV subopi-dev-3 temperature"
|
||||||
|
host: subopi-dev-3.knel.net
|
||||||
|
version: 2c
|
||||||
|
community: kn3lmgmt
|
||||||
|
baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115
|
||||||
|
accept_errors: true
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "°F"
|
||||||
|
unique_id: pfv_subopi_dev_3_temperature
|
||||||
|
value_template: >-
|
||||||
|
{% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
|
- platform: snmp
|
||||||
|
name: "PFV subopi-dev-4 temperature"
|
||||||
|
host: subopi-dev-4.knel.net
|
||||||
|
version: 2c
|
||||||
|
community: kn3lmgmt
|
||||||
|
baseoid: .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115
|
||||||
|
accept_errors: true
|
||||||
|
device_class: temperature
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "°F"
|
||||||
|
unique_id: pfv_subopi_dev_4_temperature
|
||||||
|
value_template: >-
|
||||||
|
{% set m = value | regex_findall('temp1:\s*\+?(-?\d+\.?\d*)') %}
|
||||||
|
{{ ((m | first | float * 9 / 5) + 32) | round(1) if m else 'unknown' }}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# PLACEHOLDERS — enable after dependencies land (do not invent OIDs live):
|
# PLACEHOLDERS — enable after dependencies land (do not invent OIDs live):
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
VENV_PY="${VENV_PY:-/opt/temper-venv/bin/python}"
|
VENV_PY="${VENV_PY:-/opt/temper-venv/bin/python}"
|
||||||
NATIVE_SENSORS_BIN="${NATIVE_SENSORS_BIN:-/usr/bin/sensors}"
|
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}"
|
CHIP_NAME="${CHIP_NAME:-temper-usb-1}"
|
||||||
|
|
||||||
# Render lm-sensors format block from internal/external Celsius values.
|
# Render lm-sensors format block from internal/external Celsius values.
|
||||||
@@ -71,10 +72,41 @@ run_native_sensors() {
|
|||||||
"$NATIVE_SENSORS_BIN" 2>/dev/null
|
"$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() {
|
main() {
|
||||||
local out="" block internal external native
|
local out="" block internal external native="" sysinput
|
||||||
if native="$(run_native_sensors)" && [[ -n "$native" ]]; then
|
if native="$(run_native_sensors)" && [[ -n "$native" ]]; then
|
||||||
out+="$native"$'\n'
|
out+="$native"$'\n'
|
||||||
|
else
|
||||||
|
native=""
|
||||||
fi
|
fi
|
||||||
if has_temper_probe; then
|
if has_temper_probe; then
|
||||||
if block="$(read_temper 2>/dev/null)" \
|
if block="$(read_temper 2>/dev/null)" \
|
||||||
@@ -85,8 +117,13 @@ main() {
|
|||||||
echo "warn: TEMPer probe present but read failed" >&2
|
echo "warn: TEMPer probe present but read failed" >&2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [[ -z "$native" ]] \
|
||||||
|
&& sysinput="$(read_sysfs_thermal 2>/dev/null)" \
|
||||||
|
&& block="$(format_sysfs_block "$sysinput")"; then
|
||||||
|
out+="$block"$'\n'
|
||||||
|
fi
|
||||||
[[ -n "$out" ]] || {
|
[[ -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
|
return 1
|
||||||
}
|
}
|
||||||
printf '%s' "$out"
|
printf '%s' "$out"
|
||||||
|
|||||||
+58
-1
@@ -131,7 +131,7 @@ test_main_merge() {
|
|||||||
test_main_neither_fails() {
|
test_main_neither_fails() {
|
||||||
make_stub_lsusb noprobe
|
make_stub_lsusb noprobe
|
||||||
if PATH="$STUB_DIR:$PATH" NATIVE_SENSORS_BIN=/nonexistent \
|
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"
|
echo "❌ main: succeeded with no sensor sources"
|
||||||
((++failed))
|
((++failed))
|
||||||
else
|
else
|
||||||
@@ -139,6 +139,60 @@ test_main_neither_fails() {
|
|||||||
fi
|
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_two_sensors
|
||||||
test_format_single_sensor
|
test_format_single_sensor
|
||||||
test_format_negative
|
test_format_negative
|
||||||
@@ -147,6 +201,9 @@ test_main_native_only
|
|||||||
test_main_temper_only
|
test_main_temper_only
|
||||||
test_main_merge
|
test_main_merge
|
||||||
test_main_neither_fails
|
test_main_neither_fails
|
||||||
|
test_format_sysfs_block
|
||||||
|
test_main_sysfs_fallback
|
||||||
|
test_main_native_suppresses_sysfs
|
||||||
|
|
||||||
if ((failed > 0)); then
|
if ((failed > 0)); then
|
||||||
echo "❌ lm-sensors extend: $failed test(s) failed"
|
echo "❌ lm-sensors extend: $failed test(s) failed"
|
||||||
|
|||||||
Reference in New Issue
Block a user