Skip to content

feat(web-ui): detect and surface Raspberry Pi under-voltage/throttling#383

Merged
ChuckBuilds merged 5 commits into
mainfrom
feat/power-supply-monitoring
Jul 7, 2026
Merged

feat(web-ui): detect and surface Raspberry Pi under-voltage/throttling#383
ChuckBuilds merged 5 commits into
mainfrom
feat/power-supply-monitoring

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a vcgencmd get_throttled check to the existing system-status SSE stream (system_status_generator in web_interface/app.py), decoded into under_voltage_now/throttled_now/soft_temp_limit_now (+ _occurred variants for "happened earlier this boot, not currently").
  • Header badge next to CPU/Memory/Temp: hidden when healthy, red when actively under-voltage/throttled, yellow if it happened earlier but has since cleared.
  • Dismissible top banner (same pattern as the existing update-available banner) while the condition is actively occurring, with guidance to check the power supply. Re-appears on a fresh occurrence even if a previous one was dismissed.
  • New "Power Supply" card on the Overview tab, alongside CPU/Memory/Temp/Display Status.
  • Returns None on non-Pi platforms (no vcgencmd on PATH), matching the existing guard pattern already used for the CPU temperature read.

Motivation

Diagnosed a real device showing intermittent brightness flicker on the LED panels — turned out to be an active under-voltage condition (vcgencmd get_throttled0xd0005, ~1 event every 30-90s per dmesg's "Undervoltage detected!" messages). There was no way to see this from the web UI at all, only by SSHing in and running vcgencmd/dmesg directly. This makes that diagnostic visible to any user without a terminal.

Test plan

  • _get_power_status() bit-decode logic verified against real captured vcgencmd outputs (0xd0000 historical-only, 0xd0005 active-now, 0x0 all-clear)
  • Booted the Flask app in emulator mode, confirmed the SSE generator yields a well-formed power dict
  • Rendered the actual dashboard page (following the login redirect) and confirmed all new markers (power-warning-banner, power-stat, power-status-icon, updatePowerStatus) are present in the HTML
  • Ran on real Pi hardware (this dev machine) — _get_power_status() returned a correctly-shaped all-clear dict
  • Full test/web_interface/ suite passes (2 pre-existing, unrelated failures in test_state_reconciliation.py confirmed present on main too, not caused by this change)

🤖 Generated with Claude Code

https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

Summary by CodeRabbit

  • New Features
    • Added live power and thermal health monitoring to the web interface, including a “Power Supply” overview card, header power indicator, and a dedicated Tools diagnostics panel.
    • Introduced a dismissible warning banner with dynamic messaging based on active and previously occurred power conditions.
  • Bug Fixes
    • Improved resilience when system power data is unavailable or can’t be read, showing “Not available” instead of breaking the UI.

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
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 4 complexity · 0 duplication

Metric Results
Complexity 4
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f9ddf63-698a-4cfa-b648-7c6c15ccd5b2

📥 Commits

Reviewing files that changed from the base of the PR and between d87f25d and 4ec052e.

📒 Files selected for processing (2)
  • web_interface/static/v3/app.css
  • web_interface/templates/v3/base.html
📝 Walkthrough

Walkthrough

Adds Raspberry Pi power/throttling monitoring: the backend parses vcgencmd get_throttled and emits power state in SSE stats, while the UI adds a header warning banner, overview status card, and tools diagnostics panel for the new power data.

Changes

Power Monitoring Feature

Layer / File(s) Summary
Power status detection and SSE exposure
web_interface/app.py
Adds _VCGENCMD detection, _get_power_status() to parse throttling and under-voltage flags, and includes power status in the SSE status dictionary.
Header stat and warning banner
web_interface/templates/v3/base.html, web_interface/static/v3/app.css
Adds a hidden header power stat, a dismissible warning banner with dismissal state/handler, updatePowerStatus() logic wired into updateSystemStats, and CSS styling for light and dark themes.
System overview power status card
web_interface/templates/v3/index.html, web_interface/templates/v3/base.html
Expands the overview grid to 5 columns, adds a Power Supply card, and updates its status text and icon based on current or historical power issues.
Tools power diagnostics
web_interface/templates/v3/partials/tools.html
Adds a Power Supply diagnostics panel and renders power condition details from the SSE stream when data.power is present.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant system_status_generator
  participant vcgencmd
  participant updateSystemStats
  participant updatePowerStatus
  participant renderPowerInfo

  system_status_generator->>vcgencmd: get_throttled
  vcgencmd-->>system_status_generator: throttled hex mask
  system_status_generator-->>updateSystemStats: status.power
  updateSystemStats->>updatePowerStatus: data.power
  updateSystemStats->>renderPowerInfo: data.power
  updatePowerStatus->>updatePowerStatus: toggle power-stat and warning banner
  renderPowerInfo->>renderPowerInfo: build power diagnostics summary
Loading

Related Issues: Not specified in the provided changes.

Related PRs: Not specified in the provided changes.

Suggested labels: enhancement, web-interface

Suggested reviewers: ChuckBuilds

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Raspberry Pi power/throttling detection and surfacing it in the web UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/power-supply-monitoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
web_interface/app.py (2)

530-532: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider warning instead of debug for unexpected failures.

The nearby systemctl check logs failures at warning level (Line 569), but this catch-all logs at debug, which is easy to miss during remote troubleshooting on headless Pi deployments. As per coding guidelines, "Implement comprehensive logging for remote debugging on Raspberry Pi."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/app.py` around lines 530 - 532, The catch-all failure logging
in the vcgencmd path is too quiet for remote troubleshooting. Update the
exception handler around the vcgencmd get_throttled call to use
app.logger.warning instead of app.logger.debug, matching the nearby systemctl
failure logging and the remote-debugging expectations. Keep the same exception
coverage and message context in the relevant vcgencmd helper.

Source: Coding guidelines


503-533: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add caching for _get_power_status() to avoid a subprocess fork every 10s per SSE client.

Unlike the systemctl is-active check just above (cached via _ledmatrix_service_cache with a TTL), _get_power_status() shells out to vcgencmd on every generator loop iteration, once per connected client. With multiple browser tabs/clients this multiplies subprocess forks on a resource-constrained Pi.

♻️ Suggested caching pattern (mirrors existing `_ledmatrix_service_cache`)
+_power_status_cache = {'status': None, 'timestamp': 0}
+_POWER_STATUS_CACHE_TTL = 10  # seconds
+
 def _get_power_status():
     """Check Raspberry Pi under-voltage/throttling status via vcgencmd. ..."""
     if not _VCGENCMD:
         return None
+    now = time.time()
+    if (now - _power_status_cache['timestamp']) < _POWER_STATUS_CACHE_TTL:
+        return _power_status_cache['status']
     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:
+            _power_status_cache.update(status=None, timestamp=now)
             return None
         bits = int(match.group(1), 16)
-        return {
+        status = {
             'under_voltage_now': bool(bits & 0x1),
             ...
         }
+        _power_status_cache.update(status=status, timestamp=now)
+        return status
     except (subprocess.SubprocessError, OSError, ValueError) as e:
         app.logger.debug("vcgencmd get_throttled failed: %s", e)
+        _power_status_cache.update(status=None, timestamp=now)
         return None

As per coding guidelines, "Optimize code for Raspberry Pi's limited RAM and CPU capabilities."

Also applies to: 573-582

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/app.py` around lines 503 - 533, Add TTL caching to
`_get_power_status()` so it does not run `vcgencmd get_throttled` on every SSE
loop iteration for each client. Mirror the existing `_ledmatrix_service_cache`
pattern used for the `systemctl is-active` check by storing the parsed
power-status result plus a timestamp, and return the cached value until the TTL
expires. Keep the subprocess call and parsing inside `_get_power_status()`, but
make callers reuse the cached result so multiple browser tabs do not multiply
forks.

Source: Coding guidelines

web_interface/templates/v3/base.html (2)

961-964: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Banner text is hardcoded to "Under-voltage" even though it can also trigger from throttled_now alone.

activeNow is under_voltage_now || throttled_now (Line 1492), but the static banner copy at Line 963 only mentions under-voltage. If throttling occurs without under-voltage, the message is inaccurate.

💬 Suggested dynamic text
-                        Under-voltage detected right now — the display may flicker. Check your power supply.
+                        <!-- populate dynamically in updatePowerStatus() based on which flag(s) are active -->
const bannerText = document.getElementById('power-warning-banner-text');
if (bannerText) {
    bannerText.textContent = power.under_voltage_now
        ? 'Under-voltage detected right now — the display may flicker. Check your power supply.'
        : 'Throttling detected right now — the display may flicker. Check your power supply.';
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/templates/v3/base.html` around lines 961 - 964, The power
warning banner text is hardcoded to under-voltage even though the banner can be
shown for throttling as well; update the existing banner text in base.html so it
reflects the actual active condition. Use the power state values already
available where the banner is updated (around the logic that sets activeNow) and
the existing element id power-warning-banner-text to choose between
under-voltage and throttling copy, keeping the rest of the message consistent.

954-974: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Banner/header only surface under_voltage_now/throttled_now; soft_temp_limit_* and freq_capped_* from the SSE payload are silently unused.

_get_power_status() (app.py) returns freq_capped_now/occurred and soft_temp_limit_now/occurred, but neither the header stat, banner, nor overview card (Lines 1567-1568 below) reference them. If this is intentional scope-narrowing to under-voltage/throttling only, consider a brief comment noting it; otherwise these flags should feed into the same active/occurred logic.

Also applies to: 1483-1521

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/templates/v3/base.html` around lines 954 - 974, The power
warning UI is only using under_voltage_now and throttled_now, while
soft_temp_limit_* and freq_capped_* from _get_power_status() are being ignored.
Update the banner/header logic in the power warning rendering path (including
the banner markup in base.html and the related overview/stat handling) to either
incorporate soft_temp_limit_now/occurred and freq_capped_now/occurred into the
same active/occurred state handling, or add a brief inline comment in the
relevant template/controller code if the reduced scope is intentional. Refer to
_get_power_status() and the power-warning-banner / overview card rendering so
the behavior stays consistent across all surfaces.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web_interface/templates/v3/base.html`:
- Around line 1564-1585: The power status update logic in the block that uses
powerStatusEl and powerStatusIconEl only handles truthy data.power, so non-Pi
cases leave the initial placeholder unchanged. Update this rendering path to
explicitly handle a falsy data.power case by setting a clear “Not available” (or
equivalent) status and appropriate styling/icon state, while keeping the
existing under-voltage and OK branches for the current data.power checks.

---

Nitpick comments:
In `@web_interface/app.py`:
- Around line 530-532: The catch-all failure logging in the vcgencmd path is too
quiet for remote troubleshooting. Update the exception handler around the
vcgencmd get_throttled call to use app.logger.warning instead of
app.logger.debug, matching the nearby systemctl failure logging and the
remote-debugging expectations. Keep the same exception coverage and message
context in the relevant vcgencmd helper.
- Around line 503-533: Add TTL caching to `_get_power_status()` so it does not
run `vcgencmd get_throttled` on every SSE loop iteration for each client. Mirror
the existing `_ledmatrix_service_cache` pattern used for the `systemctl
is-active` check by storing the parsed power-status result plus a timestamp, and
return the cached value until the TTL expires. Keep the subprocess call and
parsing inside `_get_power_status()`, but make callers reuse the cached result
so multiple browser tabs do not multiply forks.

In `@web_interface/templates/v3/base.html`:
- Around line 961-964: The power warning banner text is hardcoded to
under-voltage even though the banner can be shown for throttling as well; update
the existing banner text in base.html so it reflects the actual active
condition. Use the power state values already available where the banner is
updated (around the logic that sets activeNow) and the existing element id
power-warning-banner-text to choose between under-voltage and throttling copy,
keeping the rest of the message consistent.
- Around line 954-974: The power warning UI is only using under_voltage_now and
throttled_now, while soft_temp_limit_* and freq_capped_* from
_get_power_status() are being ignored. Update the banner/header logic in the
power warning rendering path (including the banner markup in base.html and the
related overview/stat handling) to either incorporate
soft_temp_limit_now/occurred and freq_capped_now/occurred into the same
active/occurred state handling, or add a brief inline comment in the relevant
template/controller code if the reduced scope is intentional. Refer to
_get_power_status() and the power-warning-banner / overview card rendering so
the behavior stays consistent across all surfaces.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 37eb83fd-e8b5-4278-a0d5-e93565339b51

📥 Commits

Reviewing files that changed from the base of the PR and between bea0044 and ac207c4.

📒 Files selected for processing (4)
  • web_interface/app.py
  • web_interface/static/v3/app.css
  • web_interface/templates/v3/base.html
  • web_interface/templates/v3/index.html

Comment thread web_interface/templates/v3/base.html Outdated
ChuckBuilds and others added 2 commits July 6, 2026 15:29
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
- 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
web_interface/templates/v3/base.html (1)

1518-1521: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

"Occurred earlier" tooltip is generic, unlike the active-now path.

Line 1516 dynamically builds the active-condition label via _activePowerConditionLabels, but the "occurred earlier" title at Line 1521 is a hardcoded string listing all four condition types regardless of which one(s) actually occurred. Consider building an equivalent "occurred" label list for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web_interface/templates/v3/base.html` around lines 1518 - 1521, The “occurred
earlier” tooltip in the power-status rendering logic is hardcoded and does not
match the specific condition(s) that were detected. Update the same
status-building path in the template’s power-condition display logic to generate
an occurrence label list from the actual condition flags, similar to how the
active-now branch uses _activePowerConditionLabels, and use that computed text
for the statEl.title in the occurredEarlier branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web_interface/templates/v3/base.html`:
- Around line 1496-1504: The updatePowerStatus function only hides power-stat
when power is falsy, leaving power-warning-banner and power-warning-banner-text
visible with stale content. Update updatePowerStatus in base.html so the falsy
branch also hides the power-warning-banner element (and clears or updates its
text as needed) before returning, keeping the banner state consistent when
_get_power_status() fails transiently.

In `@web_interface/templates/v3/partials/tools.html`:
- Around line 359-372: The status badge in the tools template is using dynamic
Tailwind-style color utilities that are not defined in the shipped stylesheet,
so the badge won’t render with the intended colors. Update the markup in the
summary badge block (and the existing dirty/clean badge nearby) to use only
static classes that already exist in app.css, or add the missing selectors
there; use the badge logic around summaryColor/summaryText as the place to
change.

---

Nitpick comments:
In `@web_interface/templates/v3/base.html`:
- Around line 1518-1521: The “occurred earlier” tooltip in the power-status
rendering logic is hardcoded and does not match the specific condition(s) that
were detected. Update the same status-building path in the template’s
power-condition display logic to generate an occurrence label list from the
actual condition flags, similar to how the active-now branch uses
_activePowerConditionLabels, and use that computed text for the statEl.title in
the occurredEarlier branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14bdee98-cdb8-4760-b8bb-216e5341f105

📥 Commits

Reviewing files that changed from the base of the PR and between ac207c4 and d87f25d.

📒 Files selected for processing (3)
  • web_interface/app.py
  • web_interface/templates/v3/base.html
  • web_interface/templates/v3/partials/tools.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • web_interface/app.py

Comment thread web_interface/templates/v3/base.html
Comment thread web_interface/templates/v3/partials/tools.html
ChuckBuilds and others added 2 commits July 7, 2026 08:52
- 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
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
@ChuckBuilds ChuckBuilds merged commit 9b2f026 into main Jul 7, 2026
8 checks passed
@ChuckBuilds ChuckBuilds deleted the feat/power-supply-monitoring branch July 7, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant