Skip to content

Conversation

@SimonFair
Copy link
Contributor

@SimonFair SimonFair commented Sep 28, 2025

Add CPU Total Power
Change CPU layout to split physical CPUs.
Add per physical CPU power.

image

Mockup on 6.12 Dual CPU.

image

Summary by CodeRabbit

  • New Features

    • Live CPU power and temperature telemetry via resilient subscription (retry/backoff) with automatic cleanup; shows total, per-package and per-core power/temperature and supports selectable temperature unit (C/F).
  • Improvements

    • Detects multiple physical CPU packages and adds per-package headers.
    • Reflows display into a nested per-package → per-core layout while preserving existing CPU load and hyper‑threading presentation.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Adds per-physical-CPU-package handling: new get_cpu_packages() helper, per-package rendering (headers, total power, optional temperature), per-core power/temp updates via updateCPUPower(), and a retry-enabled GraphQL subscription for CPU power/temperature with proper subscription lifecycle management.

Changes

Cohort / File(s) Summary of Changes
Dashboard CPU rendering & telemetry
emhttp/plugins/dynamix/DashStats.page
Replaced flat per-CPU rendering with per-physical-package rendering; emits per-package header and DOM elements (cpu-total-power, cpu-powerN, cpu-tempN); added updateCPUPower() to update total/per-core power and temps; added a retry/backoff GraphQL subscription for CPU power/temperature and lifecycle cleanup on unload; preserved existing percentTotal usage subscription and HT logic.
CPU package helpers
emhttp/plugins/dynamix/include/Helpers.php, emhttp/plugins/dynamix/include/cpulist.php
Added get_cpu_packages(string $separator = ','): array which scans /sys/devices/system/cpu/*/topology/thread_siblings_list and physical_package_id, groups/deduplicates per-package sibling lists, sorts them, and returns a mapping keyed by package id.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as User
  participant Dash as DashStats.page
  participant Helper as get_cpu_packages()
  participant Sys as /sys/devices/system/cpu/...

  User->>Dash: Open dashboard
  Dash->>Helper: get_cpu_packages(',')
  Helper->>Sys: read thread_siblings_list + physical_package_id
  Sys-->>Helper: sibling lists + package IDs
  Helper-->>Dash: package -> siblings mapping

  rect rgb(240,250,240)
    note right of Dash: Per-package render & telemetry setup
    loop for each package
      Dash->>Dash: Render "Physical CPU N Power" header and `cpu-total-power`
      Dash->>Dash: Render per-core / HT rows and `cpu-powerN` / `cpu-tempN`
      Dash->>Dash: subscribe GraphQL power/temp (retry/backoff)
      Dash->>Dash: on telemetry -> updateCPUPower() updates DOM
    end
    Dash->>Dash: subscribe existing percentTotal usage telemetry
  end

  User->>Dash: Close page
  Dash->>Dash: Unsubscribe/cleanup telemetry subscriptions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

In my burrow I count package and core,
Watts and temps I gently store.
I hop through DOM and sensors bright,
Updating power through day and night.
A little rabbit, watching CPUs roar 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: adding power and temperature display features, and splitting/reorganizing the CPU display by physical packages.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@SimonFair SimonFair marked this pull request as draft September 28, 2025 09:02
@github-actions
Copy link

github-actions bot commented Sep 28, 2025

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2025.10.23.1109
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2407/webgui-pr-2407.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix/DashStats.page
emhttp/plugins/dynamix/include/Helpers.php
emhttp/plugins/dynamix/include/cpulist.php

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2407, or run:

plugin remove webgui-pr-2407

🤖 This comment is automatically generated and will be updated with each new push to this PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c815e55 and f5d204e.

📒 Files selected for processing (2)
  • emhttp/plugins/dynamix/DashStats.page (3 hunks)
  • emhttp/plugins/dynamix/include/Helpers.php (1 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
emhttp/plugins/dynamix/DashStats.page (1)

403-423: Fix malformed HTML structure in per-package header (flagged in previous review).

The per-package header has two critical HTML issues that were flagged in the previous review but remain unfixed:

  1. Missing opening <tr> tag: Line 405 opens a <td> without a preceding <tr>, breaking table structure.
  2. Wrong closing order: Line 407 closes </td></span></tr>, but since the <span> is inside the <td>, the correct order must be </span></td></tr>.

These issues will cause rendering problems and DOM manipulation failures.

Apply this diff:

     foreach ($cpus as $cpu_index=>$package)  {
       if (count($cpus) > 1) {
-        echo "<td><span class='cpu_open w72'>"._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A </span> ";
+        echo "<tr class='cpu_open'><td><span class='w72'>"._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A</span> ";
         if (count($cpus)>1) echo _("Temp").": <span id='cpu-temp$cpu_index'>N/A</span>";
-        echo "</td></span></tr>";
+        echo "</span></td></tr>";
       }
       foreach ($package as $pair) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4ed589 and 2f66cd3.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix/DashStats.page (5 hunks)
🔇 Additional comments (2)
emhttp/plugins/dynamix/DashStats.page (2)

97-97: LGTM!

The call to get_cpu_packages() is clean and straightforward.


349-355: LGTM!

The conditional logic correctly shows temperature in the header only for single-CPU-package systems, while multi-package systems display per-package temperatures below.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
emhttp/plugins/dynamix/DashStats.page (2)

410-414: Fix markup for per-package header row.

The header is still missing its opening <tr> and closes elements out of order (</td></span>), leaving invalid table structure. Wrap the header in its own <tr> and close the spans before the cell so browsers don’t have to guess.

-                        echo "<td><span class='cpu_open w72'><i class='fa fa-plug'></i> "._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A </span> ";
-                        if (count($cpus)>1) echo "<i class='fa fa-thermometer'></i> "._("Temperature").": <span id='cpu-temp$cpu_index'>N/A</span>";
-                        echo "</td></span></tr>";
+                        echo "<tr class='cpu_open'><td><span class='cpu_open w72'><i class='fa fa-plug'></i> "._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A</span> ";
+                        if (count($cpus) > 1) echo "<i class='fa fa-thermometer'></i> "._("Temperature").": <span id='cpu-temp$cpu_index'>N/A</span>";
+                        echo "</span></td></tr>";

2825-2864: Fix power subscription retry counter typo.

The retry branch increments cpuPInitPWRAttempts, but the variable is declared as cpuInitPWRAttempts. The first retry throws a ReferenceError and halts the subscription, so power data never updates when Apollo isn’t ready on load. Use the correct identifier.

-      cpuPInitPWRAttempts++;
+      cpuInitPWRAttempts++;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f66cd3 and e799c91.

📒 Files selected for processing (2)
  • emhttp/plugins/dynamix/DashStats.page (6 hunks)
  • emhttp/plugins/dynamix/include/Helpers.php (1 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
emhttp/plugins/dynamix/DashStats.page (1)

406-428: Critical: Fix malformed package header markup and orphaned tags.

The per-package header rows have several critical markup issues:

  1. Line 406: The <tr> opened here becomes an orphan when count($cpus) == 1 (never closed).
  2. Line 410-412: For multi-CPU systems, only the first package header uses the <tr> from line 406; subsequent package headers (2nd, 3rd, etc.) have no opening <tr> tag.
  3. Line 412: Wrong closing tag order—</td></span></tr> should be </span></td></tr>.
  4. Line 411: Redundant condition if (count($cpus)>1)—already inside the same check from line 409.

Apply this diff:

-                    <tr>
                     <?
                     foreach ($cpus as $cpu_index=>$package)  {
                       if (count($cpus) > 1) {
-                        echo "<td><span class='cpu_open w72'><i class='fa fa-plug'></i> "._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A </span> ";
-                        if (count($cpus)>1) echo "<i class='fa fa-thermometer'></i> "._("Temperature").": <span id='cpu-temp$cpu_index'>N/A</span>";
-                        echo "</td></span></tr>";
+                        echo "<tr class='cpu_open'><td><span class='w72'><i class='fa fa-plug'></i> "._("Physical")." CPU $cpu_index "._("Power").": <span id='cpu-power$cpu_index'>N/A</span> ";
+                        echo "<i class='fa fa-thermometer'></i> "._("Temperature").": <span id='cpu-temp$cpu_index'>N/A</span>";
+                        echo "</span></td></tr>";
                       }
                       foreach ($package as $pair) {
🧹 Nitpick comments (1)
emhttp/plugins/dynamix/DashStats.page (1)

2819-2872: Optional: Remove extraneous blank lines.

The CPU power subscription logic is correct, but there are extra blank lines at 2820, 2825, 2843, and 2871 that can be removed for consistency.

Apply this diff:

   },500);
-

- 
-
   // Start GraphQL CPU power subscription with retry logic
   let cpuInitPWRAttempts = 0, cpuPWRRetryMs = 100;
   function initPwrCpuSubscription() {
-  
-
     if (window.gql && window.apolloClient) {
       // Define the subscription query when GraphQL is available
       ...
       }).subscribe({
         next: (result) => {
-

-
           if (result.data?.systemMetricsCpuTelemetry){
             cpupower = result.data.systemMetricsCpuTelemetry;
             
   	   updateCPUPower();
           }
         },
         ...
       });
     }
   });
-  
-

     
   // Cleanup GraphQL subscription on page unload
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5083f85 and 117a494.

📒 Files selected for processing (1)
  • emhttp/plugins/dynamix/DashStats.page (6 hunks)

Comment on lines +1711 to +1742
function updateCPUPower() {
if (!cpupower) return;

// Update total power
const totalEl = document.getElementById('cpu-total-power');
const totalPower = cpupower.totalPower ?? 0;
if (totalEl) {
totalEl.innerHTML = `<i class="fa fa-fw fa-plug"></i> _(Total)_ _(Power)_: ${totalPower.toFixed(2)} W`;
}

// Update each core's span
const cpuspower = cpupower.power ?? [];
cpuspower.forEach((power, index) => {
const coreEl = document.getElementById(`cpu-power${index}`);
if (coreEl) {
coreEl.innerHTML = `${power.toFixed(2)} W`;
}
});

const cpustemps = cpupower.temp ?? [];
cpustemps.forEach((temp, index) => {
const coreTempEl = document.getElementById(`cpu-temp${index}`);
if (coreTempEl) {
tempdisplay = temp.toFixed(0);
if (tempunit === "F") {
tempdisplay = ((temp.toFixed(0))* 9 / 5) + 32;
}
coreTempEl.innerHTML = Math.round(tempdisplay)+`&#8201;&#176;`+tempunit;;
}
});

}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix translation markers, variable declaration, and temperature conversion logic.

Several issues in updateCPUPower():

  1. Line 1718: The translation markers _(Total)_ and _(Power)_ inside the JavaScript template literal will not be processed by PHP—they'll appear as literal text. You need to pre-translate these server-side or use a different approach for localization.
  2. Line 1734: tempdisplay is assigned without let, const, or var, creating an unintended global variable.
  3. Line 1736: temp.toFixed(0) returns a string; while JavaScript coerces it to a number in arithmetic, it's cleaner to convert the raw number: temp * 9 / 5 + 32, then round once.
  4. Line 1738: Double semicolon at end (tempunit;;) and inconsistent indentation.

Apply this diff:

   const totalEl = document.getElementById('cpu-total-power');
   const totalPower = cpupower.totalPower ?? 0;
   if (totalEl) {
-    totalEl.innerHTML = `<i class="fa fa-fw fa-plug"></i> _(Total)_ _(Power)_: ${totalPower.toFixed(2)} W`;
+    totalEl.innerHTML = `<i class="fa fa-fw fa-plug"></i> <?=_('Total')?> <?=_('Power')?>: ${totalPower.toFixed(2)} W`;
   }
   
   ...
   
   const cpustemps = cpupower.temp ?? [];
   cpustemps.forEach((temp, index) => {
     const coreTempEl = document.getElementById(`cpu-temp${index}`);
-      if (coreTempEl) {
-        tempdisplay = temp.toFixed(0);
-        if (tempunit === "F") {
-          tempdisplay = ((temp.toFixed(0))* 9 / 5) + 32;
-        } 
-      coreTempEl.innerHTML = Math.round(tempdisplay)+`&#8201;&#176;`+tempunit;;
+    if (coreTempEl) {
+      let tempdisplay = temp;
+      if (tempunit === "F") {
+        tempdisplay = (temp * 9 / 5) + 32;
+      }
+      coreTempEl.innerHTML = Math.round(tempdisplay) + `&#8201;&#176;` + tempunit;
     }
   });

@ljm42 ljm42 added the 7.3 label Nov 5, 2025
@SimonFair SimonFair changed the title Feat:Add power placeholder and Split physical CPUS Feat:Add power, temps and Split physical CPUS Nov 23, 2025
@SimonFair SimonFair marked this pull request as ready for review November 23, 2025 16:37
@limetech limetech merged commit b835950 into unraid:master Nov 25, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants