From 520ebb7e3c87a40c91e04ccb7794199b553adbb9 Mon Sep 17 00:00:00 2001 From: cayossarian <23534755+cayossarian@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:26:25 -0700 Subject: [PATCH 001/101] feat: current mode with NEC breaker limit lines and panel amperage Show amps instead of watts when chart_metric is set to current. Fix Y-axis to 0-125% of breaker rating with NEC reference lines at 80% (yellow, continuous load limit) and 100% (red, breaker rating). Add total current stat to panel header from main meter amperage attribute. --- CHANGELOG.md | 31 ++++++++++++++++++++++++++++ src/card/span-panel-card.js | 40 ++++++++++++++++++++++++++++++++++--- src/chart/chart-options.js | 34 ++++++++++++++++++++++++++++++- src/chart/chart-update.js | 4 ++-- src/constants.js | 2 +- 5 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5d3e98b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## 0.8.9 + +- Show amps instead of watts above circuit graphs when chart metric is set to `current` +- Fix Y-axis scale to 0–125% of breaker rating in current mode +- Add red horizontal line at 100% of breaker rating (NEC trip point) +- Add yellow dashed line at 80% of breaker rating (NEC continuous load limit) +- Add total current (amps) stat to panel header + +## 0.8.8 + +- Chart Y-axis formatting uses absolute values for power metric + +## 0.8.7 + +- Use dynamic `panel_size` from topology instead of hardcoded 32 + +## 0.8.6 + +- Fix editor storing default 5 minutes when user only changes days/hours +- Use statistics API for long-duration charts +- Fix 0-minute config bug + +## 0.8.5 + +- Add project tooling, CI, and HACS support + +## 0.8.4 + +- Initial SPAN Panel custom Lovelace card diff --git a/src/card/span-panel-card.js b/src/card/span-panel-card.js index f5bd20e..8452bc0 100644 --- a/src/card/span-panel-card.js +++ b/src/card/span-panel-card.js @@ -335,9 +335,20 @@ export class SpanPanelCard extends HTMLElement { const consumptionEl = root.querySelector(".stat-consumption .stat-value"); if (consumptionEl) consumptionEl.textContent = formatKw(totalConsumption); + + const currentEl = root.querySelector(".stat-current .stat-value"); + if (currentEl) { + const panelPowerEid = this._findPanelEntity("current_power"); + const panelPowerState = panelPowerEid ? hass.states[panelPowerEid] : null; + const amperage = panelPowerState ? parseFloat(panelPowerState.attributes?.amperage) : NaN; + currentEl.textContent = Number.isFinite(amperage) ? amperage.toFixed(1) : "--"; + } const solarEl = root.querySelector(".stat-solar .stat-value"); if (solarEl) solarEl.textContent = solarProduction > 0 ? formatKw(solarProduction) : "--"; + const chartMetric = getChartMetric(this._config); + const showCurrent = chartMetric.entityRole === "current"; + for (const [uuid, circuit] of Object.entries(topo.circuits)) { const slot = root.querySelector(`[data-uuid="${uuid}"]`); if (!slot) continue; @@ -353,7 +364,14 @@ export class SpanPanelCard extends HTMLElement { const powerVal = slot.querySelector(".power-value"); if (powerVal) { - powerVal.innerHTML = `${formatPowerSigned(powerW)}${formatPowerUnit(powerW)}`; + if (showCurrent) { + const currentEid = circuit.entities?.current; + const currentState = currentEid ? hass.states[currentEid] : null; + const amps = currentState ? parseFloat(currentState.state) || 0 : 0; + powerVal.innerHTML = `${chartMetric.format(amps)}A`; + } else { + powerVal.innerHTML = `${formatPowerSigned(powerW)}${formatPowerUnit(powerW)}`; + } } const toggle = slot.querySelector(".toggle-pill"); @@ -370,7 +388,7 @@ export class SpanPanelCard extends HTMLElement { if (chartContainer) { const history = this._powerHistory.get(uuid) || []; const h = slot.classList.contains("circuit-col-span") ? 200 : 100; - updateChart(chartContainer, hass, history, durationMs, getChartMetric(this._config), isProducer, h); + updateChart(chartContainer, hass, history, durationMs, chartMetric, isProducer, h, circuit.breaker_rating_a); } } @@ -491,6 +509,10 @@ export class SpanPanelCard extends HTMLElement { Panel consumption
Global monitoring settings are managed through the SPAN Panel integration options.
+To change global thresholds, go to Settings → Devices & Services → SPAN Panel → Configure.
+Individual circuit thresholds can be configured by clicking the gear icon on a circuit row and switching to Custom mode.
+Dashboard loading...
`; + break; + case "monitoring": + container.innerHTML = `Monitoring tab — coming in Task 14
`; + break; + case "settings": + container.innerHTML = `Settings tab — coming in Task 15
`; + break; + } + } +} From cf95269d9a22614f61d25b1aafe260eedfef3a40 Mon Sep 17 00:00:00 2001 From: cayossarian <23534755+cayossarian@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:43:24 -0700 Subject: [PATCH 015/101] feat: implement dashboard tab reusing core rendering modules --- src/panel/span-panel.js | 17 +++++- src/panel/tab-dashboard.js | 111 +++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/panel/tab-dashboard.js diff --git a/src/panel/span-panel.js b/src/panel/span-panel.js index b826460..8f2eb56 100644 --- a/src/panel/span-panel.js +++ b/src/panel/span-panel.js @@ -1,5 +1,6 @@ import { INTEGRATION_DOMAIN } from "../constants.js"; import "../core/side-panel.js"; +import { DashboardTab } from "./tab-dashboard.js"; const PANEL_STYLES = ` :host { @@ -57,6 +58,7 @@ export class SpanPanelElement extends HTMLElement { this._selectedPanelId = null; this._activeTab = "dashboard"; this._discovered = false; + this._dashboardTab = new DashboardTab(); } set hass(val) { @@ -147,13 +149,24 @@ export class SpanPanelElement extends HTMLElement { } async _renderTab() { + this._dashboardTab.stop(); + const container = this.shadowRoot.getElementById("tab-content"); if (!container) return; switch (this._activeTab) { - case "dashboard": - container.innerHTML = `Dashboard loading...
`; + case "dashboard": { + container.innerHTML = ""; + const config = { + chart_metric: "power", + history_minutes: 5, + show_panel: true, + show_battery: true, + show_evse: true, + }; + await this._dashboardTab.render(container, this._hass, this._selectedPanelId, config); break; + } case "monitoring": container.innerHTML = `Monitoring tab — coming in Task 14
`; break; diff --git a/src/panel/tab-dashboard.js b/src/panel/tab-dashboard.js new file mode 100644 index 0000000..6daa01d --- /dev/null +++ b/src/panel/tab-dashboard.js @@ -0,0 +1,111 @@ +import { discoverTopology } from "../card/card-discovery.js"; +import { buildHeaderHTML } from "../core/header-renderer.js"; +import { buildGridHTML } from "../core/grid-renderer.js"; +import { buildSubDevicesHTML } from "../core/sub-device-renderer.js"; +import { updateCircuitDOM, updateSubDeviceDOM } from "../core/dom-updater.js"; +import { loadHistory } from "../core/history-loader.js"; +import { MonitoringStatusCache } from "../core/monitoring-status.js"; +import { CARD_STYLES } from "../card/card-styles.js"; +import { getHistoryDurationMs, recordSample, getMaxHistoryPoints, getMinGapMs } from "../helpers/history.js"; +import { getCircuitChartEntity } from "../helpers/chart.js"; +import { LIVE_SAMPLE_INTERVAL_MS } from "../constants.js"; + +export class DashboardTab { + constructor() { + this._topology = null; + this._panelSize = 0; + this._powerHistory = new Map(); + this._monitoringCache = new MonitoringStatusCache(); + this._updateInterval = null; + this._hass = null; + this._config = null; + } + + async render(container, hass, deviceId, config) { + this.stop(); + this._hass = hass; + this._config = config; + + try { + const result = await discoverTopology(hass, deviceId); + this._topology = result.topology; + this._panelSize = result.panelSize; + } catch (err) { + container.innerHTML = `${err.message}
`; + return; + } + + await this._monitoringCache.fetch(hass); + + const topo = this._topology; + const totalRows = Math.ceil(this._panelSize / 2); + const durationMs = getHistoryDurationMs(config); + const monitoringStatus = this._monitoringCache.status; + + const headerHTML = buildHeaderHTML(topo, config); + const gridHTML = buildGridHTML(topo, totalRows, durationMs, hass, config, monitoringStatus); + const subDevHTML = buildSubDevicesHTML(topo, hass, config, durationMs); + + container.innerHTML = ` + + ${headerHTML} + ${ + config.show_panel !== false + ? ` +Monitoring tab — coming in Task 14
`; + container.innerHTML = ""; + await this._monitoringTab.render(container, this._hass); break; case "settings": - container.innerHTML = `Settings tab — coming in Task 15
`; + container.innerHTML = ""; + this._settingsTab.render(container); break; } } diff --git a/src/panel/tab-monitoring.js b/src/panel/tab-monitoring.js new file mode 100644 index 0000000..2522430 --- /dev/null +++ b/src/panel/tab-monitoring.js @@ -0,0 +1,102 @@ +import { INTEGRATION_DOMAIN } from "../constants.js"; +import { escapeHtml } from "../helpers/sanitize.js"; + +export class MonitoringTab { + async render(container, hass) { + let status; + try { + const resp = await hass.callService(INTEGRATION_DOMAIN, "get_monitoring_status", {}, undefined, true); + status = resp?.response || null; + } catch { + container.innerHTML = ` ++ Monitoring is not enabled. Enable it in the integration's + options flow (Settings > Devices & Services > + SPAN Panel > Configure > Monitoring). +
++ Global monitoring settings are managed in the integration's options flow. + All monitored circuits and mains legs are shown below. +
+ +| Name | +Continuous | +Spike | +Window | ++ |
|---|
+ No monitored points found. +
+ ` + } ++ General integration settings (entity naming, device prefix, + circuit numbers) are managed through the integration's options flow. +
+ + Open SPAN Panel Integration Settings → + +- Global monitoring settings are managed in the integration's options flow. - All monitored circuits and mains legs are shown below. + Global monitoring thresholds are configured in the integration's options flow + (Settings > Devices & Services > SPAN Panel > Configure > Monitoring). + Per-circuit overrides are listed below. Use Reset to Default to clear + a custom override and restore the circuit to global defaults.
- No monitored points found. + All circuits using global defaults. No per-circuit overrides are configured.
` } From f6fdd73b3e6b7b1419c7eb1ded2b0ad053e822ac Mon Sep 17 00:00:00 2001 From: cayossarian <23534755+cayossarian@users.noreply.github.com> Date: Sun, 29 Mar 2026 19:57:48 -0700 Subject: [PATCH 021/101] feat: add global monitoring settings section and panel config link --- src/core/side-panel.js | 34 +++++++++++++++++++++++++++------- src/panel/tab-monitoring.js | 23 ++++++++++++++++------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/core/side-panel.js b/src/core/side-panel.js index c8093b9..52ac22d 100644 --- a/src/core/side-panel.js +++ b/src/core/side-panel.js @@ -234,18 +234,38 @@ class SpanSidePanel extends HTMLElement { } _renderPanelMode(panel) { - const header = this._createHeader("Panel Settings", null); + const header = this._createHeader("Panel Monitoring", "Global defaults for all circuits"); panel.appendChild(header); const body = document.createElement("div"); body.className = "panel-body"; - body.innerHTML = ` -Global monitoring settings are managed through the SPAN Panel integration options.
-To change global thresholds, go to Settings → Devices & Services → SPAN Panel → Configure.
-Individual circuit thresholds can be configured by clicking the gear icon on a circuit row and switching to Custom mode.
-Global monitoring thresholds apply to all circuits that don't have custom overrides. + Use the integration's options flow to change global settings.
+Individual circuit thresholds can be configured by clicking the gear icon on a circuit row + and switching to Custom mode.
`; + body.appendChild(info); + + const link = document.createElement("a"); + link.href = "/config/integrations/integration/span_panel"; + link.textContent = "Configure Global Thresholds"; + Object.assign(link.style, { + display: "inline-block", + marginTop: "8px", + padding: "8px 16px", + background: "var(--primary-color, #4dd9af)", + color: "var(--text-primary-color, #000)", + borderRadius: "4px", + textDecoration: "none", + fontSize: "0.85em", + fontWeight: "500", + }); + body.appendChild(link); + panel.appendChild(body); } diff --git a/src/panel/tab-monitoring.js b/src/panel/tab-monitoring.js index 948a56b..79a9983 100644 --- a/src/panel/tab-monitoring.js +++ b/src/panel/tab-monitoring.js @@ -54,14 +54,23 @@ export class MonitoringTab { container.innerHTML = `- Global monitoring thresholds are configured in the integration's options flow - (Settings > Devices & Services > SPAN Panel > Configure > Monitoring). - Per-circuit overrides are listed below. Use Reset to Default to clear - a custom override and restore the circuit to global defaults. -
-+ Global monitoring thresholds apply to all circuits without custom overrides. + Use the integration's options flow to change global settings. +
+ + Configure Global Thresholds + ++ Use Reset to Default to clear a custom override and restore the circuit to global defaults. +
${ allEntries.length > 0 ? ` From 1e6701e61dfc2b649bfb44fadfb1f70509297b50 Mon Sep 17 00:00:00 2001 From: cayossarian <23534755+cayossarian@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:37:00 -0700 Subject: [PATCH 022/101] feat: use topology panel_entities instead of pattern matching Replace fragile hass.states pattern-matching helpers (_findPanelEntity, _hasPanelEntity, _hasSolarEntity) with direct lookups from topology.panel_entities. Remove the helper functions entirely and drop the now-unnecessary hass parameter from buildHeaderHTML. --- src/card/span-panel-card.js | 2 +- src/core/dom-updater.js | 26 ++++++------------- src/core/header-renderer.js | 50 ++++++------------------------------- src/panel/tab-dashboard.js | 2 +- 4 files changed, 16 insertions(+), 64 deletions(-) diff --git a/src/card/span-panel-card.js b/src/card/span-panel-card.js index 1c8f3e4..0de7e36 100644 --- a/src/card/span-panel-card.js +++ b/src/card/span-panel-card.js @@ -283,7 +283,7 @@ export class SpanPanelCard extends HTMLElement { const totalRows = Math.ceil(this._panelSize / 2); const durationMs = this._durationMs; - const headerHTML = buildHeaderHTML(topo, this._config, hass); + const headerHTML = buildHeaderHTML(topo, this._config); const monitoringStatus = this._monitoringCache.status; const monitoringSummaryHTML = buildMonitoringSummaryHTML(monitoringStatus); const gridHTML = buildGridHTML(topo, totalRows, durationMs, hass, this._config, monitoringStatus); diff --git a/src/core/dom-updater.js b/src/core/dom-updater.js index ef27c51..41565ea 100644 --- a/src/core/dom-updater.js +++ b/src/core/dom-updater.js @@ -5,18 +5,6 @@ import { findSubDevicePowerEntity } from "../helpers/entity-finder.js"; import { getHistoryDurationMs } from "../helpers/history.js"; import { updateChart } from "../chart/chart-update.js"; -// ── Private helper ───────────────────────────────────────────────────────── - -function _findPanelEntity(hass, _topology, suffix) { - if (!hass) return null; - for (const entityId of Object.keys(hass.states)) { - if (entityId.startsWith("sensor.span_panel_") && entityId.endsWith(`_${suffix}`)) { - return entityId; - } - } - return null; -} - // ── Header stats ─────────────────────────────────────────────────────────── function _updateHeaderStats(root, hass, topology, config, totalConsumption, solarProduction) { @@ -26,13 +14,13 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola const consumptionEl = root.querySelector(".stat-consumption .stat-value"); const consumptionUnitEl = root.querySelector(".stat-consumption .stat-unit"); if (isAmpsMode) { - const siteEid = _findPanelEntity(hass, topology, "current_power"); + const siteEid = topology.panel_entities?.current_power; const siteState = siteEid ? hass.states[siteEid] : null; const amps = siteState ? parseFloat(siteState.attributes?.amperage) : NaN; if (consumptionEl) consumptionEl.textContent = Number.isFinite(amps) ? Math.abs(amps).toFixed(1) : "--"; if (consumptionUnitEl) consumptionUnitEl.textContent = "A"; } else { - const panelPowerEntity = _findPanelEntity(hass, topology, "current_power"); + const panelPowerEntity = topology.panel_entities?.current_power; if (panelPowerEntity) { const state = hass.states[panelPowerEntity]; if (state) totalConsumption = Math.abs(parseFloat(state.state) || 0); @@ -45,7 +33,7 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola const upstreamEl = root.querySelector(".stat-upstream .stat-value"); const upstreamUnitEl = root.querySelector(".stat-upstream .stat-unit"); if (upstreamEl) { - const upEid = _findPanelEntity(hass, topology, "current_power"); + const upEid = topology.panel_entities?.current_power; const upState = upEid ? hass.states[upEid] : null; if (isAmpsMode) { const amps = upState ? parseFloat(upState.attributes?.amperage) : NaN; @@ -62,7 +50,7 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola const downstreamEl = root.querySelector(".stat-downstream .stat-value"); const downstreamUnitEl = root.querySelector(".stat-downstream .stat-unit"); if (downstreamEl) { - const downEid = _findPanelEntity(hass, topology, "feedthrough_power"); + const downEid = topology.panel_entities?.feedthrough_power; const downState = downEid ? hass.states[downEid] : null; if (isAmpsMode) { const amps = downState ? parseFloat(downState.attributes?.amperage) : NaN; @@ -80,7 +68,7 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola const solarUnitEl = root.querySelector(".stat-solar .stat-unit"); if (solarEl) { if (isAmpsMode) { - const solarEid = _findPanelEntity(hass, topology, "solar_inverter_instant_power"); + const solarEid = topology.panel_entities?.pv_power; const solarState = solarEid ? hass.states[solarEid] : null; const amps = solarState ? parseFloat(solarState.attributes?.amperage) : NaN; solarEl.textContent = Number.isFinite(amps) ? Math.abs(amps).toFixed(1) : "--"; @@ -94,7 +82,7 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola // Battery SoC (always %) const batteryEl = root.querySelector(".stat-battery .stat-value"); if (batteryEl) { - const battEid = _findPanelEntity(hass, topology, "battery_percentage"); + const battEid = topology.panel_entities?.battery_level; const battState = battEid ? hass.states[battEid] : null; if (battState) batteryEl.textContent = `${Math.round(parseFloat(battState.state) || 0)}`; } @@ -102,7 +90,7 @@ function _updateHeaderStats(root, hass, topology, config, totalConsumption, sola // Grid / DSM state const gridStateEl = root.querySelector(".stat-grid-state .stat-value"); if (gridStateEl) { - const gridEid = _findPanelEntity(hass, topology, "dsm_state"); + const gridEid = topology.panel_entities?.dsm_state; const gridState = gridEid ? hass.states[gridEid] : null; gridStateEl.textContent = gridState ? gridState.state : "--"; } diff --git a/src/core/header-renderer.js b/src/core/header-renderer.js index e00b1ca..c2e41b3 100644 --- a/src/core/header-renderer.js +++ b/src/core/header-renderer.js @@ -1,59 +1,23 @@ import { escapeHtml } from "../helpers/sanitize.js"; -/** - * Check whether a panel-level entity with the given suffix exists in hass.states. - * Searches for entities starting with "sensor.span_panel_" and ending with "_Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("a");s.href="/config/integrations/integration/span_panel",s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",textDecoration:"none",fontSize:"0.85em",fontWeight:"500"}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(o);const i=document.createElement("a");i.href="/config/integrations/integration/span_panel",i.textContent="Configure Global Thresholds",Object.assign(i.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",textDecoration:"none",fontSize:"0.85em",fontWeight:"500"}),n.appendChild(i),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,o=this._createHeader(u(e.name),n);t.appendChild(o);const i=document.createElement("div");i.className="panel-body",t.appendChild(i);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",i.appendChild(a),this._renderRelaySection(i,e),this._renderSheddingSection(i,e),this._renderMonitoringSection(i,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const o=document.createElement("div");o.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(H(o),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),o=u(t.serial||""),i=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n\n Monitoring is not enabled. Enable it in the integration\'s\n options flow (Settings > Devices & Services >\n SPAN Panel > Configure > Monitoring).\n
\n\n Global monitoring thresholds apply to all circuits without custom overrides.\n Use the integration's options flow to change global settings.\n
\n \n Configure Global Thresholds\n \n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${s.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration\'s options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("a");s.href="/config/integrations/integration/span_panel",s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",textDecoration:"none",fontSize:"0.85em",fontWeight:"500"}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(o);const i=document.createElement("a");i.href="/config/integrations/integration/span_panel",i.textContent="Configure Global Thresholds",Object.assign(i.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",textDecoration:"none",fontSize:"0.85em",fontWeight:"500"}),n.appendChild(i),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,o=this._createHeader(u(e.name),n);t.appendChild(o);const i=document.createElement("div");i.className="panel-body",t.appendChild(i);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",i.appendChild(a),this._renderRelaySection(i,e),this._renderSheddingSection(i,e),this._renderMonitoringSection(i,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const o=document.createElement("div");o.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(H(o),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),o=u(t.serial||""),i=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n\n Monitoring is not enabled. Enable it in the integration\'s\n options flow (Settings > Devices & Services >\n SPAN Panel > Configure > Monitoring).\n
\n\n Global monitoring thresholds apply to all circuits without custom overrides.\n Use the integration's options flow to change global settings.\n
\n \n Configure Global Thresholds\n \n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${s.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration\'s options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const s=document.createElement("div");s.className="error-msg",s.id="error-msg",s.style.display="none",o.appendChild(s),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const a=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),s="current"===(e.chart_metric||"power");return`\n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${c.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \n- Monitoring is not enabled. Enable it in the integration's - options flow (Settings > Devices & Services > - SPAN Panel > Configure > Monitoring). -
-- Global monitoring thresholds apply to all circuits without custom overrides. - Use the integration's options flow to change global settings. -
- - Configure Global Thresholds - +Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const s=document.createElement("div");s.className="error-msg",s.id="error-msg",s.style.display="none",o.appendChild(s),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const a=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),s="current"===(e.chart_metric||"power");return`\n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${c.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const s=document.createElement("div");s.className="error-msg",s.id="error-msg",s.style.display="none",o.appendChild(s),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const a=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),s="current"===(e.chart_metric||"power");return`\n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${c.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const s=document.createElement("div");s.className="error-msg",s.id="error-msg",s.style.display="none",o.appendChild(s),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const a=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),s="current"===(e.chart_metric||"power");return`\n\n Use Reset to Default to clear a custom override and restore the circuit to global defaults.\n
\n ${c.length>0?`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|
\n All circuits using global defaults. No per-circuit overrides are configured.\n
\n '}\n\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),l=(j(i),this._monitoringCache.status),c=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \n- Use Reset to Default to clear a custom override and restore the circuit to global defaults. -
- ${ - allEntries.length > 0 - ? ` -| Name | -Continuous | -Spike | -Window | -- |
|---|
- All circuits using global defaults. No per-circuit overrides are configured. -
- ` - } +| Name | +Continuous | +Spike | +Window | ++ |
|---|---|---|---|---|
| + + | +||||
Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),l=(j(i),this._monitoringCache.status),c=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \n\n |
|---|---|---|---|---|
| \n \n | \n||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),c=(j(i),this._monitoringCache.status),l=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n| Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),l=(R(i),this._monitoringCache.status),c=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n{name} {entity_id} {alert_type}\n {current_a} {breaker_rating_a} {threshold_pct}\n {utilization_pct} {window_m}\n | Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \n{name} {entity_id} {alert_type}
+ {current_a} {breaker_rating_a} {threshold_pct}
+ {utilization_pct} {window_m}
+ Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const s=document.createElement("button");s.textContent="Configure Global Thresholds",Object.assign(s.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),s.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(s),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const s=document.createElement("div");s.className="panel-body",t.appendChild(s);const o=document.createElement("div");o.className="error-msg",o.id="error-msg",o.style.display="none",s.appendChild(o),this._renderRelaySection(s,e),this._renderSheddingSection(s,e),this._renderMonitoringSection(s,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${t("sidepanel.global_info")}
\n${t("sidepanel.custom_info_prefix")} ${t("sidepanel.custom")} ${t("sidepanel.custom_info_suffix")}
\n `,i.appendChild(o);const a=document.createElement("button");a.textContent=t("sidepanel.configure_global"),Object.assign(a.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),a.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),i.appendChild(a),e.appendChild(i)}_renderCircuitMode(e,t){const n=`${g(String(t.breaker_rating_a))}A · ${g(String(t.voltage))}V · Tabs [${g(String(t.tabs))}]`,i=this._createHeader(g(t.name),n);e.appendChild(i);const o=document.createElement("div");o.className="panel-body",e.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,t),this._renderSheddingSection(o,t),this._renderMonitoringSection(o,t)}_createHeader(e,t){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`Global monitoring thresholds apply to all circuits that don't have custom overrides.\n Use the integration's options flow to change global settings.
\nIndividual circuit thresholds can be configured by clicking the gear icon on a circuit row\n and switching to Custom mode.
\n ",n.appendChild(i);const o=document.createElement("button");o.textContent="Configure Global Thresholds",Object.assign(o.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),o.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),n.appendChild(o),t.appendChild(n)}_renderCircuitMode(t,e){const n=`${u(String(e.breaker_rating_a))}A · ${u(String(e.voltage))}V · Tabs [${u(String(e.tabs))}]`,i=this._createHeader(u(e.name),n);t.appendChild(i);const o=document.createElement("div");o.className="panel-body",t.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,e),this._renderSheddingSection(o,e),this._renderMonitoringSection(o,e)}_createHeader(t,e){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${e.message}
`)}await this._monitoringCache.fetch(e);const s=this._topology,r=Math.ceil(this._panelSize/2),l=(R(i),this._monitoringCache.status),c=function(t,e){const n=u(t.device_name||"SPAN Panel"),i=u(t.serial||""),o=u(t.firmware||""),a="current"===(e.chart_metric||"power");return`\n{name} {entity_id} {alert_type}\n {current_a} {breaker_rating_a} {threshold_pct}\n {utilization_pct} {window_m}\n | Name | \nContinuous | \nSpike | \nWindow | \nCooldown | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n General integration settings (entity naming, device prefix,\n circuit numbers) are managed through the integration's options flow.\n
\n \n Open SPAN Panel Integration Settings →\n \n${n("sidepanel.global_info")}
\n${n("sidepanel.custom_info_prefix")} ${n("sidepanel.custom")} ${n("sidepanel.custom_info_suffix")}
\n `,i.appendChild(o);const a=document.createElement("button");a.textContent=n("sidepanel.configure_global"),Object.assign(a.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),a.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),i.appendChild(a),e.appendChild(i)}_renderCircuitMode(e,t){const n=`${m(String(t.breaker_rating_a))}A · ${m(String(t.voltage))}V · Tabs [${m(String(t.tabs))}]`,i=this._createHeader(m(t.name),n);e.appendChild(i);const o=document.createElement("div");o.className="panel-body",e.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,t),this._renderSheddingSection(o,t),this._renderMonitoringSection(o,t)}_createHeader(e,t){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${t.message}
`)}await this._monitoringCache.fetch(t);const a=this._topology,s=Math.ceil(this._panelSize/2),c=(O(o),this._monitoringCache.status),d=function(e,t){const i=m(e.device_name||n("header.default_name")),o=m(e.serial||""),a=m(e.firmware||""),s="current"===(t.chart_metric||"power"),r=!!e.panel_entities?.site_power,l=!!e.panel_entities?.dsm_state,c=!!e.panel_entities?.current_power,d=!!e.panel_entities?.feedthrough_power,p=!!e.panel_entities?.pv_power,u=!!e.panel_entities?.battery_level;return`\n{name} {entity_id} {alert_type}\n {current_a} {breaker_rating_a} {threshold_pct}\n {utilization_pct} {window_m}\n | ${n("monitoring.col.name")} | \n${n("monitoring.col.continuous")} | \n${n("monitoring.col.spike")} | \n${n("monitoring.col.window")} | \n${n("monitoring.col.cooldown")} | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n ${n("settings.description")}\n
\n \n ${n("settings.open_link")} →\n \nGlobal monitoring thresholds apply to all circuits that don't have custom overrides. - Use the integration's options flow to change global settings.
-Individual circuit thresholds can be configured by clicking the gear icon on a circuit row - and switching to Custom mode.
+${t("sidepanel.global_info")}
+${t("sidepanel.custom_info_prefix")} ${t("sidepanel.custom")} ${t("sidepanel.custom_info_suffix")}
`; body.appendChild(info); const link = document.createElement("button"); - link.textContent = "Configure Global Thresholds"; + link.textContent = t("sidepanel.configure_global"); Object.assign(link.style, { display: "inline-block", marginTop: "8px", @@ -318,14 +317,14 @@ class SpanSidePanel extends HTMLElement { const section = document.createElement("div"); section.className = "section"; - section.innerHTML = `{name} {entity_id} {alert_type}
+ ${t("notification.placeholders")} {name} {entity_id} {alert_type}
{current_a} {breaker_rating_a} {threshold_pct}
{utilization_pct} {window_m}
| Name | -Continuous | -Spike | -Window | -Cooldown | +${t("monitoring.col.name")} | +${t("monitoring.col.continuous")} | +${t("monitoring.col.spike")} | +${t("monitoring.col.window")} | +${t("monitoring.col.cooldown")} | \n ${i}\n | `}class ae{constructor(){this._debounceTimer=null,this._configEntryId=null}async render(e,t,i){let a;void 0!==i&&(this._configEntryId=i),this._notifyCloseHandler&&(document.removeEventListener("click",this._notifyCloseHandler),this._notifyCloseHandler=null);try{const e={};this._configEntryId&&(e.config_entry_id=this._configEntryId);const n=await t.callWS({type:"call_service",domain:o,service:"get_monitoring_status",service_data:e,return_response:!0});a=n?.response||null}catch{a=null}const s=a?.global_settings||{},r=!0===a?.enabled,l=a?.circuits||{},c=a?.mains||{},d=new Set;for(const[e,n]of Object.entries(t.states||{})){if(!e.startsWith("person."))continue;const t=n.attributes?.device_trackers||[];for(const e of t){const t=e.split(".")[1];t&&d.add(`notify.mobile_app_${t}`)}}for(const e of Object.keys(t.states||{}))e.startsWith("notify.")&&d.add(e);for(const e of Object.keys(t.services?.notify||{}))d.add(`notify.${e}`);const p=[...d].sort(),u=s.notify_targets||"notify.notify",g=("string"==typeof u?u.split(","):u).map(e=>e.trim()).filter(Boolean),h=s.notification_title_template||"SPAN: {name} {alert_type}",f=s.notification_message_template||"{name} at {current_a}A ({utilization_pct}% of {breaker_rating_a}A rating)",_=!1!==s.enable_persistent_notifications,b=!1!==s.enable_event_bus,v=s.notification_priority||"default",y=Object.entries(l).sort(([,e],[,t])=>(e.name||"").localeCompare(t.name||"")),x=Object.entries(c),w=[...y,...x],$=w.length>0&&w.every(([,e])=>!1!==e.monitoring_enabled),S=w.some(([,e])=>!1!==e.monitoring_enabled),k=y.map(([e,t])=>{const i=m(t.name||e),o=!1!==t.monitoring_enabled,a=!0===t.has_override,s=o?"":"opacity:0.4;",r=m(e);return`\n
|---|---|---|---|---|---|---|---|---|---|---|
| \n \n | \n ${oe(r,"continuous_threshold_pct",t.continuous_threshold_pct,"%","circuit")}\n ${oe(r,"spike_threshold_pct",t.spike_threshold_pct,"%","circuit")}\n ${oe(r,"window_duration_m",t.window_duration_m,"m","circuit")}\n ${oe(r,"cooldown_duration_m",t.cooldown_duration_m,"m","circuit")}\n\n ${a?` | \n |||||||||
| \n \n | \n ${oe(r,"continuous_threshold_pct",t.continuous_threshold_pct,"%","mains")}\n ${oe(r,"spike_threshold_pct",t.spike_threshold_pct,"%","mains")}\n ${oe(r,"window_duration_m",t.window_duration_m,"m","mains")}\n ${oe(r,"cooldown_duration_m",t.cooldown_duration_m,"m","mains")}\n\n ${a?` | \n |||||||||
| ${n("monitoring.col.name")} | \n${n("monitoring.col.continuous")} | \n${n("monitoring.col.spike")} | \n${n("monitoring.col.window")} | \n${n("monitoring.col.cooldown")} | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n ${n("settings.description")}\n
\n \n ${n("settings.open_link")} →\n \n${n("sidepanel.global_info")}
\n${n("sidepanel.custom_info_prefix")} ${n("sidepanel.custom")} ${n("sidepanel.custom_info_suffix")}
\n `,i.appendChild(o);const a=document.createElement("button");a.textContent=n("sidepanel.configure_global"),Object.assign(a.style,{display:"inline-block",marginTop:"8px",padding:"8px 16px",background:"var(--primary-color, #4dd9af)",color:"var(--text-primary-color, #000)",borderRadius:"4px",border:"none",cursor:"pointer",fontSize:"0.85em",fontWeight:"500"}),a.addEventListener("click",()=>{this.close(),this.dispatchEvent(new CustomEvent("navigate-tab",{detail:"monitoring",bubbles:!0,composed:!0}))}),i.appendChild(a),e.appendChild(i)}_renderCircuitMode(e,t){const n=`${m(String(t.breaker_rating_a))}A · ${m(String(t.voltage))}V · Tabs [${m(String(t.tabs))}]`,i=this._createHeader(m(t.name),n);e.appendChild(i);const o=document.createElement("div");o.className="panel-body",e.appendChild(o);const a=document.createElement("div");a.className="error-msg",a.id="error-msg",a.style.display="none",o.appendChild(a),this._renderRelaySection(o,t),this._renderSheddingSection(o,t),this._renderMonitoringSection(o,t)}_createHeader(e,t){const n=document.createElement("div");n.className="panel-header";const i=document.createElement("div");i.innerHTML=`${t.message}
`)}await this._monitoringCache.fetch(t);const a=this._topology,s=Math.ceil(this._panelSize/2),c=(O(o),this._monitoringCache.status),d=function(e,t){const i=m(e.device_name||n("header.default_name")),o=m(e.serial||""),a=m(e.firmware||""),s="current"===(t.chart_metric||"power"),r=!!e.panel_entities?.site_power,l=!!e.panel_entities?.dsm_state,c=!!e.panel_entities?.current_power,d=!!e.panel_entities?.feedthrough_power,p=!!e.panel_entities?.pv_power,u=!!e.panel_entities?.battery_level;return`\n{name} {entity_id} {alert_type}\n {current_a} {breaker_rating_a} {threshold_pct}\n {utilization_pct} {window_m}\n | ${n("monitoring.col.name")} | \n${n("monitoring.col.continuous")} | \n${n("monitoring.col.spike")} | \n${n("monitoring.col.window")} | \n${n("monitoring.col.cooldown")} | \n\n |
|---|---|---|---|---|---|
| \n \n | \n|||||
\n ${n("settings.description")}\n
\n \n ${n("settings.open_link")} →\n \n