Files
LEDMatrix/web_interface
9b2f02681d 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>
2026-07-07 09:35:55 -04:00
..
2025-12-27 14:15:49 -05:00
2025-12-27 14:15:49 -05:00
2025-12-27 14:15:49 -05:00

LED Matrix Web Interface V3

Modern, production web interface for controlling the LED Matrix display.

Overview

This directory contains the active V3 web interface with the following features:

  • Real-time display preview via Server-Sent Events (SSE)
  • Plugin management and configuration
  • System monitoring and logs
  • Modern, responsive UI
  • RESTful API

Directory Structure

web_interface/
├── app.py                    # Main Flask application
├── start.py                  # Startup script
├── run.sh                    # Shell runner script
├── requirements.txt          # Python dependencies
├── blueprints/               # Flask blueprints
│   ├── api_v3.py            # API endpoints
│   └── pages_v3.py          # Page routes
├── templates/                # HTML templates
│   └── v3/
│       ├── base.html
│       ├── index.html
│       └── partials/
└── static/                   # CSS/JS assets
    └── v3/
        ├── app.css
        └── app.js

Running the Web Interface

Standalone (Development)

From the project root:

python3 web_interface/start.py

Or using the shell script:

./web_interface/run.sh

As a Service (Production)

The web interface can run as a systemd service that starts automatically based on the web_display_autostart configuration setting:

sudo systemctl start ledmatrix-web
sudo systemctl enable ledmatrix-web  # Start on boot

Accessing the Interface

Once running, access the web interface at:

Configuration

The web interface reads configuration from:

  • config/config.json - Main configuration
  • config/config_secrets.json - API keys and secrets

API Documentation

The V3 API is mounted at /api/v3/ (app.py:144). For the complete list and request/response formats, see docs/REST_API_REFERENCE.md. Quick reference for the most common endpoints:

Configuration

  • GET /api/v3/config/main - Get main configuration
  • POST /api/v3/config/main - Save main configuration
  • GET /api/v3/config/secrets - Get secrets configuration
  • POST /api/v3/config/raw/main - Save raw main config (Config Editor)
  • POST /api/v3/config/raw/secrets - Save raw secrets

Display & System Control

  • GET /api/v3/system/status - System status
  • POST /api/v3/system/action - Control display (action body: start_display, stop_display, restart_display_service, restart_web_service, git_pull, reboot_system, shutdown_system, enable_autostart, disable_autostart)
  • GET /api/v3/display/current - Current display frame
  • GET /api/v3/display/on-demand/status - On-demand status
  • POST /api/v3/display/on-demand/start - Trigger on-demand display
  • POST /api/v3/display/on-demand/stop - Clear on-demand

Plugins

  • GET /api/v3/plugins/installed - List installed plugins
  • GET /api/v3/plugins/config?plugin_id=<id> - Get plugin config
  • POST /api/v3/plugins/config - Update plugin configuration
  • GET /api/v3/plugins/schema?plugin_id=<id> - Get plugin schema
  • POST /api/v3/plugins/toggle - Enable/disable plugin
  • POST /api/v3/plugins/install - Install from registry
  • POST /api/v3/plugins/install-from-url - Install from GitHub URL
  • POST /api/v3/plugins/uninstall - Uninstall plugin
  • POST /api/v3/plugins/update - Update plugin

Plugin Store

  • GET /api/v3/plugins/store/list - List available registry plugins
  • GET /api/v3/plugins/store/github-status - GitHub authentication status
  • POST /api/v3/plugins/store/refresh - Refresh registry from GitHub

Real-time Streams (SSE)

SSE stream endpoints are defined directly on the Flask app (app.py:607-619 — includes the CSRF exemption and rate-limit hookup alongside the three route definitions), not on the api_v3 blueprint:

  • GET /api/v3/stream/stats - System statistics stream
  • GET /api/v3/stream/display - Display preview stream
  • GET /api/v3/stream/logs - Service logs stream

Development

When making changes to the web interface:

  1. Edit files in this directory
  2. Test changes by running python3 web_interface/start.py
  3. Restart the service if running: sudo systemctl restart ledmatrix-web

Notes

  • Templates and static files use the v3/ prefix to allow for future versions
  • The interface uses Flask blueprints for modular organization
  • SSE streams provide real-time updates without polling