Files
PFVCluster/ups/status.sh
T
mrcharles 5456c783c6 feat(ups): NUT setup for APC Smart-UPS C 1500 on pfv-tsys1
Deploy Network UPS Tools to monitor the server-room UPS units and feed data to
Home Assistant for power-usage tracking.

APC Smart-UPS C 1500 is fully operational: battery charge, runtime, load,
voltage, and status all reporting via usbhid-ups + APC HID 0.100 subdriver.
upsd listens on Tailscale (100.121.189.98:3493) for HA polling; local upsmon
provides graceful hypervisor shutdown on battery-low.

Key deployment fix: the udev rule must cover SUBSYSTEM=="usb" (raw
/dev/bus/usb) in addition to hidraw, because usbhid-ups opens the raw USB
device file after dropping to the nut user via setuid(). Matching only hidraw
causes EACCES on driver start.

Tripp Lite UPS (09ae:3016) is blocked — driver matches TrippLite HID 0.85
subdriver but fails reading the 878-byte HID Report Descriptor (EAGAIN via USB
hub). Needs physical reseat to direct motherboard USB port. Driver masked to
prevent restart-loop spam.

Files: ups/discover.sh, ups/setup.sh (idempotent), ups/status.sh, ups/README.md

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 09:34:05 -05:00

60 lines
2.3 KiB
Bash

#!/usr/bin/bash
#
# ups/status.sh — query NUT UPS state + service health (read-only)
#
# Usage (run ON the target host via remote.sh):
# PROX_HOST=pfv-tsys1 bash tests/remote.sh prox-file ups/status.sh
#
# shellcheck disable=SC2012 # ss/awk field extraction is intentional
set -uo pipefail
APC_NAME="${APC_NAME:-apc-smartups-c1500}"
TRIPP_NAME="${TRIPP_NAME:-tripp-lite-ups}"
echo "======================================================"
echo " NUT UPS Status on $(hostname)"
echo "======================================================"
echo ""
echo "--- Services ---"
for svc in "nut-driver@${APC_NAME}" "nut-driver@${TRIPP_NAME}" nut-server nut-monitor; do
if systemctl list-unit-files "$svc" >/dev/null 2>&1; then
printf " %-42s " "$svc"
systemctl is-active "$svc" 2>/dev/null || echo "(unknown)"
fi
done
for ups in "$APC_NAME" "$TRIPP_NAME"; do
echo ""
echo "--- ${ups} ---"
if upsc "${ups}@localhost" >/tmp/.nutstatus.$$ 2>&1; then
awk -v u="$ups" '
BEGIN{printf " %s\n", u}
/^battery\.charge:/ {printf " battery.charge: %s\n", $3}
/^battery\.runtime:/ {printf " battery.runtime: %ss (%.0f min)\n", $3, $3/60}
/^battery\.voltage:/ {printf " battery.voltage: %s\n", $3}
/^ups\.status:/ {printf " ups.status: %s\n", $3}
/^ups\.load:/ {printf " ups.load: %s%%\n", $3}
/^ups\.power:/ {printf " ups.power: %s\n", $3}
/^ups\.realpower:/ {printf " ups.realpower: %s W\n", $3}
/^input\.voltage:/ {printf " input.voltage: %s\n", $3}
/^output\.voltage:/ {printf " output.voltage: %s\n", $3}
/^ups\.model:/ {printf " ups.model: %s\n", $3}
/^ups\.serial:/ {printf " ups.serial: %s\n", $3}
/^device\.mfr:/ {printf " device.mfr: %s\n", $3}
' /tmp/.nutstatus.$$
echo " (full dump: upsc ${ups}@localhost)"
else
echo " NOT RESPONDING:"
sed 's/^/ /' /tmp/.nutstatus.$$
fi
rm -f /tmp/.nutstatus.$$
done
echo ""
echo "--- upsd LISTEN sockets ---"
ss -ltnp 2>/dev/null | grep -E "3493|nut" | sed 's/^/ /' || echo " (upsd not listening on 3493)"
echo ""
echo "======================================================"