mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-01 08:48:05 +00:00
feat(web-ui): detect and surface Raspberry Pi under-voltage/throttling (#383)
* feat(web-ui): detect and surface Raspberry Pi under-voltage/throttling Adds a vcgencmd get_throttled check to the system-status SSE stream and surfaces it in the web UI: - A header badge (next to CPU/Memory/Temp) that stays hidden when healthy, turns red when under-voltage/throttling is happening right now, and yellow if it happened earlier this session but has since cleared. - A dismissible top banner (same pattern as the update-available banner) that appears while under-voltage/throttling is actively occurring, with guidance to check the power supply. Re-appears on a fresh occurrence even if a previous one was dismissed. - A "Power Supply" card on the Overview tab alongside CPU/Memory/Temp/ Display Status. Motivated by a real device showing intermittent brightness flicker that turned out to be ~1 under-voltage event every 30-90s (visible live via `vcgencmd get_throttled` and dmesg's "Undervoltage detected!" messages) -- there was no way to see this from the web UI, only by SSHing in. Returns None on non-Pi platforms (no vcgencmd on PATH), matching the existing guard pattern used for the CPU temperature read. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ * feat(web-ui): add power supply diagnostics detail to Tools tab The header badge/banner/Overview card added in the previous commit only show a collapsed "is it bad right now" signal. This adds a "Power Supply" section to the Tools tab with the full 8-flag breakdown (under-voltage, throttled, freq-capped, soft-temp-limit -- each split into "right now" vs "occurred since boot") for actually troubleshooting a recurring issue, plus a pointer to the README's power supply sizing guidance when something is or was flagged. Reuses the existing stats SSE stream (window.statsSource) rather than adding a new endpoint -- the same payload already drives the header/ banner/Overview card, so this just listens for it too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ * fix(web-ui): address review findings on power supply monitoring - Overview card left its "--" placeholder forever on non-Pi platforms since the update only handled a truthy data.power. Now explicitly renders "Not available" with a neutral icon/color for that case. - vcgencmd failures logged at debug, invisible in default remote log output. Bumped to warning to match the nearby systemctl failure logging. - The banner/badge/card and the Tools summary line only looked at under_voltage_now/throttled_now (+ occurred), silently ignoring freq_capped_now/occurred and soft_temp_limit_now/occurred from _get_power_status() -- a Pi that's actively soft-thermal-limited or frequency-capped showed a green "OK" everywhere except the detailed flag table buried in Tools. All four surfaces now fold all four "now"/ "occurred" flags into the same active/occurred state. - The banner text was hardcoded to "Under-voltage detected..." even when the actual active condition was throttling/freq-capping/thermal limiting. Added _activePowerConditionLabels() (shared, non-module global scope) to build the banner/tooltip text from whichever flags are actually set. Skipped: TTL-caching _get_power_status() to avoid "multiplying forks across browser tabs" -- that premise doesn't hold against this codebase. _StreamBroadcaster (its own docstring says as much) already runs exactly one shared generator per tick regardless of client count, identical to the uncached cpu_temp file-read two lines above it; there's nothing to multiply. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ * fix(web-ui): address second round of review findings on power monitoring - updatePowerStatus's falsy-power branch only hid power-stat, leaving power-warning-banner visible with stale text if _get_power_status() fails transiently. Now hides the banner too and resets the dismissed flag, same as the "not active" branch. - The Tools tab's status badge (and the pre-existing dirty/clean badge right next to it) build class names like bg-${color}-100/text-${color}-800 at runtime. This project hand-rolls its own Tailwind-named utility classes in app.css rather than running a real Tailwind build, and the light-mode base rules for bg-red-100/bg-yellow-100/bg-green-100/ text-red-800/text-yellow-800/text-green-800 were simply never defined -- only some had dark-mode overrides, which are no-ops without a base rule in light mode. Added the missing light-mode bases plus the two missing dark-mode overrides (bg-yellow-100/bg-green-100), fixing both badges. - The "occurred earlier" tooltip was a hardcoded string regardless of which flag(s) actually fired. Generalized _activePowerConditionLabels() to take a suffix ('_now' or '_occurred') and reused it for both tooltips. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ * refactor(web-ui): move Power Supply status off the Overview tab The Overview tab's "Power Supply" stat card duplicated what the Tools tab's diagnostics section already shows (summary badge + full flag breakdown), so drop the card and its now-dead JS rather than keep two copies in sync. The header badge and warning banner (visible on every page) are unaffected -- only the Overview-tab card is removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+35
-1
@@ -3,6 +3,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import subprocess
|
||||
@@ -27,6 +28,7 @@ from src.plugin_system.health_monitor import PluginHealthMonitor
|
||||
|
||||
_JOURNALCTL = shutil.which('journalctl')
|
||||
_SYSTEMCTL = shutil.which('systemctl')
|
||||
_VCGENCMD = shutil.which('vcgencmd')
|
||||
|
||||
# Create Flask app
|
||||
app = Flask(__name__)
|
||||
@@ -498,6 +500,37 @@ class _StreamBroadcaster:
|
||||
except queue.Full:
|
||||
pass
|
||||
|
||||
def _get_power_status():
|
||||
"""Check Raspberry Pi under-voltage/throttling status via vcgencmd.
|
||||
|
||||
Returns a dict of decoded flags, or None on non-Pi platforms (no
|
||||
vcgencmd) or if the call fails for any reason. See:
|
||||
https://www.raspberrypi.com/documentation/computers/os.html#get_throttled
|
||||
"""
|
||||
if not _VCGENCMD:
|
||||
return None
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[_VCGENCMD, 'get_throttled'], capture_output=True, text=True, timeout=2
|
||||
)
|
||||
match = re.search(r'0x([0-9a-fA-F]+)', result.stdout)
|
||||
if not match:
|
||||
return None
|
||||
bits = int(match.group(1), 16)
|
||||
return {
|
||||
'under_voltage_now': bool(bits & 0x1),
|
||||
'freq_capped_now': bool(bits & 0x2),
|
||||
'throttled_now': bool(bits & 0x4),
|
||||
'soft_temp_limit_now': bool(bits & 0x8),
|
||||
'under_voltage_occurred': bool(bits & 0x10000),
|
||||
'freq_capped_occurred': bool(bits & 0x20000),
|
||||
'throttled_occurred': bool(bits & 0x40000),
|
||||
'soft_temp_limit_occurred': bool(bits & 0x80000),
|
||||
}
|
||||
except (subprocess.SubprocessError, OSError, ValueError) as e:
|
||||
app.logger.warning("vcgencmd get_throttled failed: %s", e)
|
||||
return None
|
||||
|
||||
# System status generator for SSE
|
||||
def system_status_generator():
|
||||
"""Generate system status updates"""
|
||||
@@ -544,7 +577,8 @@ def system_status_generator():
|
||||
'cpu_percent': cpu_percent,
|
||||
'memory_used_percent': memory_used_percent,
|
||||
'cpu_temp': cpu_temp,
|
||||
'disk_used_percent': 0
|
||||
'disk_used_percent': 0,
|
||||
'power': _get_power_status()
|
||||
}
|
||||
yield status
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user