From 7b922c507c39ee31cf8dae60e7c660bbca439f49 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Tue, 1 Sep 2026 18:02:09 -0500 Subject: [PATCH] feat(sensors): numeric -n mode + lmsensors_n extend for HA SNMP migration [#618] --- dcinfra/sensors/temper/deploy.sh | 1 + dcinfra/sensors/temper/lmsensors-extend.sh | 35 +++++++++++- tests/unit/lmsensors-numeric.sh | 64 ++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/unit/lmsensors-numeric.sh diff --git a/dcinfra/sensors/temper/deploy.sh b/dcinfra/sensors/temper/deploy.sh index d0b01b1..5217c5d 100644 --- a/dcinfra/sensors/temper/deploy.sh +++ b/dcinfra/sensors/temper/deploy.sh @@ -127,6 +127,7 @@ sed -i 's/^agentaddress/#agentaddress/' "$SNMPD_CONF" echo "rocommunity $COMMUNITY $src" done echo "extend lmsensors $WRAP_DST" + echo "extend lmsensors_n $WRAP_DST -n" echo "$MARK_END" } >>"$SNMPD_CONF" log "agentaddress: $AGENT_ADDRS" diff --git a/dcinfra/sensors/temper/lmsensors-extend.sh b/dcinfra/sensors/temper/lmsensors-extend.sh index 6e98156..9b10699 100644 --- a/dcinfra/sensors/temper/lmsensors-extend.sh +++ b/dcinfra/sensors/temper/lmsensors-extend.sh @@ -37,6 +37,39 @@ format_block() { } # True when a TEMPer USB dongle (0c45:7401) is attached and the venv exists. + +# Numeric mode: print ONE Celsius float for snmpd extend lmsensors_n [#618]. +# Preference: TEMPer external > TEMPer internal > first native/sysfs temp +# reading. Consumed by HA's SNMP config-entry integration (device_class +# temperature; HA renders C in the instance display unit). +numeric_pick() { + local internal="${1:-}" external="${2:-}" native="${3:-}" pick + local num_re='^-?[0-9]+(\.[0-9]+)?$' + for pick in "$external" "$internal"; do + pick="${pick#+}" + [[ "$pick" =~ $num_re ]] && { printf '%s\n' "$pick"; return 0; } + done + if [[ -n "$native" ]]; then + pick="$(grep -oE '\-?[0-9]+(\.[0-9]+)?' <<<"$native" | head -1)" + [[ "$pick" =~ $num_re ]] && { printf '%s\n' "$pick"; return 0; } + fi + return 1 +} + +numeric_main() { + local internal external line + if has_temper_probe; then + if read -r internal external <<<"$(read_temper 2>/dev/null)"; then + numeric_pick "${internal:-}" "${external:-}" "" && return 0 + fi + fi + line="$(run_native_sensors | grep -m1 '°C')" + [[ -n "$line" ]] && numeric_pick "" "" "$line" && return 0 + line="$(read_sysfs_thermal 2>/dev/null | head -1 | awk '{printf "+%.1f°C", $2/1000}')" + [[ -n "$line" ]] && numeric_pick "" "" "$line" && return 0 + echo "error: numeric mode found no sensor source" >&2 + return 1 +} has_temper_probe() { command -v lsusb >/dev/null 2>&1 || return 1 [[ -x "$VENV_PY" ]] || return 1 @@ -130,5 +163,5 @@ main() { } if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then - main + if [[ "${1:-}" == "-n" ]]; then numeric_main; else main; fi fi diff --git a/tests/unit/lmsensors-numeric.sh b/tests/unit/lmsensors-numeric.sh new file mode 100644 index 0000000..2fa17b2 --- /dev/null +++ b/tests/unit/lmsensors-numeric.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Unit tests for lmsensors-extend.sh -n numeric mode [#618] +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +SCRIPT="$PROJECT_ROOT/dcinfra/sensors/temper/lmsensors-extend.sh" + +failed=0 +if [[ -f "$SCRIPT" ]] && (source "$SCRIPT"); then + echo "✅ script exists + sources cleanly" +else + echo "❌ script missing or fails to source: $SCRIPT" + exit 1 +fi +# shellcheck disable=SC1091 +source "$SCRIPT" + +# numeric_pick: TEMPer external > TEMPer internal > first native/sysfs value +test_numeric_prefers_external() { + if [[ "$(numeric_pick "22.5" "18.2" "" )" == "18.2" ]]; then + echo "✅ numeric_pick prefers external probe" + else + echo "❌ numeric_pick external preference broken" + ((++failed)) + fi +} + +test_numeric_falls_back_internal() { + if [[ "$(numeric_pick "22.5" "" "")" == "22.5" ]]; then + echo "✅ numeric_pick falls back to internal" + else + echo "❌ numeric_pick internal fallback broken" + ((++failed)) + fi +} + +test_numeric_uses_first_line_value() { + if [[ "$(numeric_pick "" "" "+27.5°C")" == "27.5" ]]; then + echo "✅ numeric_pick parses first native temp" + else + echo "❌ numeric_pick native parse broken: got '$(numeric_pick "" "" "+27.5°C")'" + ((++failed)) + fi +} + +test_numeric_empty_is_error() { + if numeric_pick "" "" "" >/dev/null 2>&1; then + echo "❌ numeric_pick should fail on no data" + ((++failed)) + else + echo "✅ numeric_pick errors on no data" + fi +} + +test_numeric_prefers_external +test_numeric_falls_back_internal +test_numeric_uses_first_line_value +test_numeric_empty_is_error + +if ((failed > 0)); then + echo "❌ $failed numeric-mode test(s) failed" + exit 1 +fi +echo "✅ all numeric-mode tests passed"