v_2.1
This commit is contained in:
parent
d5a4eb33ef
commit
181b64c590
6 changed files with 1242 additions and 1046 deletions
125
dist/power-flux-card.js
vendored
125
dist/power-flux-card.js
vendored
|
|
@ -14,6 +14,7 @@ const lang_de = {
|
||||||
"editor.consumers_section": "Zusätzliche Verbraucher",
|
"editor.consumers_section": "Zusätzliche Verbraucher",
|
||||||
"editor.options_section": "Darstellung & Optionen",
|
"editor.options_section": "Darstellung & Optionen",
|
||||||
"editor.flow_rate_title": "Flussraten (W) an Röhren anzeigen",
|
"editor.flow_rate_title": "Flussraten (W) an Röhren anzeigen",
|
||||||
|
"editor.invert_battery": "Wert umkehren (+/-)",
|
||||||
"editor.label_toggle": "Label im Kreis anzeigen",
|
"editor.label_toggle": "Label im Kreis anzeigen",
|
||||||
"editor.compact_view": "Kompakte Ansicht (evcc)",
|
"editor.compact_view": "Kompakte Ansicht (evcc)",
|
||||||
"editor.hide_inactive": "Inaktive Röhren ausblenden",
|
"editor.hide_inactive": "Inaktive Röhren ausblenden",
|
||||||
|
|
@ -42,6 +43,7 @@ const lang_en = {
|
||||||
"editor.consumers_section": "Additional Consumers",
|
"editor.consumers_section": "Additional Consumers",
|
||||||
"editor.options_section": "Appearance & Options",
|
"editor.options_section": "Appearance & Options",
|
||||||
"editor.flow_rate_title": "Show Flow Rates (W) on pipes",
|
"editor.flow_rate_title": "Show Flow Rates (W) on pipes",
|
||||||
|
"editor.invert_battery": "Invert Power Value (+/-)",
|
||||||
"editor.label_toggle": "Show Label in Bubble",
|
"editor.label_toggle": "Show Label in Bubble",
|
||||||
"editor.compact_view": "Compact View (evcc)",
|
"editor.compact_view": "Compact View (evcc)",
|
||||||
"editor.hide_inactive": "Hide Inactive Pipes",
|
"editor.hide_inactive": "Hide Inactive Pipes",
|
||||||
|
|
@ -69,6 +71,19 @@ cardTranslations['en'] = lang_en.card;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const editorTranslations = {
|
||||||
|
"en": lang_en.editor,
|
||||||
|
"de": lang_de.editor
|
||||||
|
};
|
||||||
|
|
||||||
|
const cardTranslations = {
|
||||||
|
"en": lang_en.card,
|
||||||
|
"de": lang_de.card
|
||||||
|
};
|
||||||
|
|
||||||
const fireEvent = (node, type, detail, options) => {
|
const fireEvent = (node, type, detail, options) => {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
detail = detail === null || detail === undefined ? {} : detail;
|
detail = detail === null || detail === undefined ? {} : detail;
|
||||||
|
|
@ -129,6 +144,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
const entityKeys = [
|
const entityKeys = [
|
||||||
'solar', 'grid', 'grid_export',
|
'solar', 'grid', 'grid_export',
|
||||||
'battery', 'battery_soc',
|
'battery', 'battery_soc',
|
||||||
|
'house',
|
||||||
'consumer_1', 'consumer_2', 'consumer_3'
|
'consumer_1', 'consumer_2', 'consumer_3'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -440,6 +456,15 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
|
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="switch-row">
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config.invert_battery === true}
|
||||||
|
.configValue=${'invert_battery'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
<div class="switch-label">${this._localize('editor.invert_battery')}</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,6 +477,21 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
<h2>${this._localize('editor.consumers_section')}</h2>
|
<h2>${this._localize('editor.consumers_section')}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="consumer-group">
|
||||||
|
<div class="consumer-title">🏠 Gesamthausverbrauch (Optional)</div>
|
||||||
|
<ha-selector
|
||||||
|
.hass=${this.hass}
|
||||||
|
.selector=${entitySelectorSchema}
|
||||||
|
.value=${entities.house || ""}
|
||||||
|
.configValue=${'house'}
|
||||||
|
.label=${'Sensor für Hausverbrauch (Optional)'}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-selector>
|
||||||
|
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
|
||||||
|
Wird benötigt, damit das Haus-Icon anklickbar ist.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="consumer-group">
|
<div class="consumer-group">
|
||||||
<div class="consumer-title" style="color: #a855f7;">🚗 Links (Lila)</div>
|
<div class="consumer-title" style="color: #a855f7;">🚗 Links (Lila)</div>
|
||||||
<ha-selector
|
<ha-selector
|
||||||
|
|
@ -681,13 +721,26 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
customElements.define("power-flux-card-editor", PowerFluxCardEditor);
|
customElements.define("power-flux-card-editor", PowerFluxCardEditor);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"%c⚡ Power-Flux-Card v_2.0 ready",
|
"%c⚡ Power Flux Card v_2.1 ready",
|
||||||
"background: #2ecc71; color: #000; padding: 2px 6px; border-radius: 4px; font-weight: bold;"
|
"background: #d19525ff; color: #000; padding: 2px 6px; border-radius: 4px; font-weight: bold;"
|
||||||
);
|
);
|
||||||
|
|
||||||
class PowerFluxCard extends LitElement {
|
(function () {
|
||||||
|
const cardTranslations = {
|
||||||
|
"en": lang_en.card,
|
||||||
|
"de": lang_de.card
|
||||||
|
};
|
||||||
|
|
||||||
|
const LitElement = customElements.get("ha-lit-element") || Object.getPrototypeOf(customElements.get("home-assistant-main"));
|
||||||
|
const html = LitElement.prototype.html;
|
||||||
|
const css = LitElement.prototype.css;
|
||||||
|
|
||||||
|
class PowerFluxCard extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
|
|
@ -731,6 +784,7 @@ class PowerFluxCard extends LitElement {
|
||||||
grid_export: "",
|
grid_export: "",
|
||||||
battery: "",
|
battery: "",
|
||||||
battery_soc: "",
|
battery_soc: "",
|
||||||
|
house: "",
|
||||||
consumer_1: "",
|
consumer_1: "",
|
||||||
consumer_2: "",
|
consumer_2: "",
|
||||||
consumer_3: ""
|
consumer_3: ""
|
||||||
|
|
@ -738,6 +792,16 @@ class PowerFluxCard extends LitElement {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleClick(entityId) {
|
||||||
|
if (!entityId) return;
|
||||||
|
const event = new Event("hass-more-info", {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
});
|
||||||
|
event.detail = { entityId };
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config.entities) {
|
if (!config.entities) {
|
||||||
// Init allow
|
// Init allow
|
||||||
|
|
@ -948,7 +1012,7 @@ class PowerFluxCard extends LitElement {
|
||||||
.node-c2 { top: 370px; left: 165px; }
|
.node-c2 { top: 370px; left: 165px; }
|
||||||
.node-c3 { top: 370px; left: 325px; }
|
.node-c3 { top: 370px; left: 325px; }
|
||||||
|
|
||||||
svg { position: absolute; top: 7; left: 25; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
|
svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
|
||||||
|
|
||||||
.bg-path { fill: none; stroke-width: 6; transition: opacity 0.3s ease; }
|
.bg-path { fill: none; stroke-width: 6; transition: opacity 0.3s ease; }
|
||||||
.bg-solar { stroke: var(--neon-yellow); }
|
.bg-solar { stroke: var(--neon-yellow); }
|
||||||
|
|
@ -1081,7 +1145,10 @@ class PowerFluxCard extends LitElement {
|
||||||
const solar = entities.solar ? Math.max(0, getVal(entities.solar)) : 0;
|
const solar = entities.solar ? Math.max(0, getVal(entities.solar)) : 0;
|
||||||
const gridMain = entities.grid ? getVal(entities.grid) : 0;
|
const gridMain = entities.grid ? getVal(entities.grid) : 0;
|
||||||
const gridExportSensor = entities.grid_export ? getVal(entities.grid_export) : 0;
|
const gridExportSensor = entities.grid_export ? getVal(entities.grid_export) : 0;
|
||||||
const battery = entities.battery ? getVal(entities.battery) : 0;
|
let battery = entities.battery ? getVal(entities.battery) : 0;
|
||||||
|
if (this.config.invert_battery) {
|
||||||
|
battery *= -1;
|
||||||
|
}
|
||||||
const c1Val = entities.consumer_1 ? getVal(entities.consumer_1) : 0; // EV Value
|
const c1Val = entities.consumer_1 ? getVal(entities.consumer_1) : 0; // EV Value
|
||||||
|
|
||||||
// 2. Logic Calculation
|
// 2. Logic Calculation
|
||||||
|
|
@ -1159,7 +1226,7 @@ class PowerFluxCard extends LitElement {
|
||||||
const barSegments = [];
|
const barSegments = [];
|
||||||
let currentX = 0;
|
let currentX = 0;
|
||||||
|
|
||||||
const addSegment = (val, color, type, label) => {
|
const addSegment = (val, color, type, label, entityId) => {
|
||||||
if (val <= threshold) return;
|
if (val <= threshold) return;
|
||||||
const pct = val / totalFlux;
|
const pct = val / totalFlux;
|
||||||
const width = pct * fullWidth;
|
const width = pct * fullWidth;
|
||||||
|
|
@ -1170,14 +1237,15 @@ class PowerFluxCard extends LitElement {
|
||||||
widthPx: width,
|
widthPx: width,
|
||||||
startPx: currentX,
|
startPx: currentX,
|
||||||
type,
|
type,
|
||||||
label
|
label,
|
||||||
|
entityId
|
||||||
});
|
});
|
||||||
currentX += width;
|
currentX += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSegment(srcBattery, 'var(--neon-yellow)', 'battery', 'battery');
|
addSegment(srcBattery, 'var(--neon-yellow)', 'battery', 'battery', entities.battery);
|
||||||
addSegment(srcSolar, 'var(--neon-green)', 'solar', 'solar');
|
addSegment(srcSolar, 'var(--neon-green)', 'solar', 'solar', entities.solar);
|
||||||
addSegment(srcGrid, 'var(--grid-grey)', 'grid', 'grid');
|
addSegment(srcGrid, 'var(--grid-grey)', 'grid', 'grid', entities.grid);
|
||||||
|
|
||||||
// --- GENERATE TOP BRACKETS (Based on Bar Segments) ---
|
// --- GENERATE TOP BRACKETS (Based on Bar Segments) ---
|
||||||
const topBrackets = barSegments.map(s => {
|
const topBrackets = barSegments.map(s => {
|
||||||
|
|
@ -1188,7 +1256,7 @@ class PowerFluxCard extends LitElement {
|
||||||
if (s.type === 'grid') { icon = 'mdi:transmission-tower'; iconColor = 'var(--grid-grey)'; }
|
if (s.type === 'grid') { icon = 'mdi:transmission-tower'; iconColor = 'var(--grid-grey)'; }
|
||||||
if (s.type === 'battery') { icon = 'mdi:battery-high'; iconColor = 'var(--neon-yellow)'; }
|
if (s.type === 'battery') { icon = 'mdi:battery-high'; iconColor = 'var(--neon-yellow)'; }
|
||||||
|
|
||||||
return { path, width: s.widthPx, center: s.startPx + (s.widthPx/2), icon, iconColor };
|
return { path, width: s.widthPx, center: s.startPx + (s.widthPx / 2), icon, iconColor, val: s.val, entityId: s.entityId };
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- GENERATE BOTTOM BRACKETS (Independent Calculation) ---
|
// --- GENERATE BOTTOM BRACKETS (Independent Calculation) ---
|
||||||
|
|
@ -1196,7 +1264,7 @@ class PowerFluxCard extends LitElement {
|
||||||
const bottomBrackets = [];
|
const bottomBrackets = [];
|
||||||
let bottomX = 0;
|
let bottomX = 0;
|
||||||
|
|
||||||
const addBottomBracket = (val, type) => {
|
const addBottomBracket = (val, type, entityId = null) => {
|
||||||
if (val <= threshold) return;
|
if (val <= threshold) return;
|
||||||
const pct = val / totalFlux;
|
const pct = val / totalFlux;
|
||||||
const width = pct * fullWidth;
|
const width = pct * fullWidth;
|
||||||
|
|
@ -1207,6 +1275,7 @@ class PowerFluxCard extends LitElement {
|
||||||
if (type === 'house') { icon = 'mdi:home'; iconColor = 'var(--primary-text-color)'; }
|
if (type === 'house') { icon = 'mdi:home'; iconColor = 'var(--primary-text-color)'; }
|
||||||
if (type === 'car') { icon = 'mdi:car-electric'; iconColor = '#a855f7'; }
|
if (type === 'car') { icon = 'mdi:car-electric'; iconColor = '#a855f7'; }
|
||||||
if (type === 'export') { icon = 'mdi:arrow-right-box'; iconColor = 'var(--export-purple)'; }
|
if (type === 'export') { icon = 'mdi:arrow-right-box'; iconColor = 'var(--export-purple)'; }
|
||||||
|
if (type === 'battery') { icon = 'mdi:battery-charging-high'; iconColor = 'var(--neon-green)'; }
|
||||||
|
|
||||||
const path = this._createBracketPath(bottomX, width, 'up');
|
const path = this._createBracketPath(bottomX, width, 'up');
|
||||||
bottomBrackets.push({
|
bottomBrackets.push({
|
||||||
|
|
@ -1214,14 +1283,17 @@ class PowerFluxCard extends LitElement {
|
||||||
width: width,
|
width: width,
|
||||||
center: bottomX + (width / 2),
|
center: bottomX + (width / 2),
|
||||||
icon,
|
icon,
|
||||||
iconColor
|
iconColor,
|
||||||
|
val,
|
||||||
|
entityId
|
||||||
});
|
});
|
||||||
bottomX += width;
|
bottomX += width;
|
||||||
};
|
};
|
||||||
|
|
||||||
addBottomBracket(destHouse, 'house');
|
addBottomBracket(destHouse, 'house', entities.house);
|
||||||
addBottomBracket(destEV, 'car');
|
addBottomBracket(destEV, 'car', entities.consumer_1);
|
||||||
addBottomBracket(destExport, 'export');
|
addBottomBracket(destExport, 'export', entities.grid_export || entities.grid);
|
||||||
|
addBottomBracket(batteryCharge, 'battery', entities.battery);
|
||||||
|
|
||||||
// Note: If there is Battery Charging happening, bottomX will not reach fullWidth.
|
// Note: If there is Battery Charging happening, bottomX will not reach fullWidth.
|
||||||
// This leaves a gap at the end (or between segments depending on logic), which is visually correct
|
// This leaves a gap at the end (or between segments depending on logic), which is visually correct
|
||||||
|
|
@ -1236,7 +1308,10 @@ class PowerFluxCard extends LitElement {
|
||||||
${topBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
${topBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
||||||
</svg>
|
</svg>
|
||||||
${topBrackets.map(b => b.width > 20 ? html`
|
${topBrackets.map(b => b.width > 20 ? html`
|
||||||
<div class="compact-icon-wrapper" style="left: ${b.center}px; transform: translateX(-50%); top: 4px;">
|
<div class="compact-icon-wrapper"
|
||||||
|
style="left: ${b.center}px; transform: translateX(-50%); top: 4px; cursor: ${b.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(b.val)}"
|
||||||
|
@click=${() => b.entityId && this._handleClick(b.entityId)}>
|
||||||
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
||||||
</div>` : '')}
|
</div>` : '')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1244,7 +1319,10 @@ class PowerFluxCard extends LitElement {
|
||||||
<!-- MAIN BAR -->
|
<!-- MAIN BAR -->
|
||||||
<div class="compact-bar-wrapper">
|
<div class="compact-bar-wrapper">
|
||||||
${barSegments.map(s => html`
|
${barSegments.map(s => html`
|
||||||
<div class="bar-segment" style="width: ${s.widthPct}%; background: ${s.color}; color: ${s.color === 'var(--export-purple)' ? 'white' : 'black'};">
|
<div class="bar-segment"
|
||||||
|
style="width: ${s.widthPct}%; background: ${s.color}; color: ${s.color === 'var(--export-purple)' ? 'white' : 'black'}; cursor: ${s.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(s.val)}"
|
||||||
|
@click=${() => s.entityId && this._handleClick(s.entityId)}>
|
||||||
${s.widthPx > 35 ? this._formatPower(s.val) : ''}
|
${s.widthPx > 35 ? this._formatPower(s.val) : ''}
|
||||||
</div>
|
</div>
|
||||||
`)}
|
`)}
|
||||||
|
|
@ -1256,7 +1334,10 @@ class PowerFluxCard extends LitElement {
|
||||||
${bottomBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
${bottomBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
||||||
</svg>
|
</svg>
|
||||||
${bottomBrackets.map(b => b.width > 20 ? html`
|
${bottomBrackets.map(b => b.width > 20 ? html`
|
||||||
<div class="compact-icon-wrapper" style="left: ${b.center}px; transform: translateX(-50%); top: -3px;">
|
<div class="compact-icon-wrapper"
|
||||||
|
style="left: ${b.center}px; transform: translateX(-50%); top: -3px; cursor: ${b.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(b.val)}"
|
||||||
|
@click=${() => b.entityId && this._handleClick(b.entityId)}>
|
||||||
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
||||||
</div>` : '')}
|
</div>` : '')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1337,7 +1418,10 @@ class PowerFluxCard extends LitElement {
|
||||||
const solar = hasSolar ? getVal(entities.solar) : 0;
|
const solar = hasSolar ? getVal(entities.solar) : 0;
|
||||||
const gridMain = hasGrid ? getVal(entities.grid) : 0;
|
const gridMain = hasGrid ? getVal(entities.grid) : 0;
|
||||||
const gridExpSensor = (hasGrid && entities.grid_export) ? getVal(entities.grid_export) : 0;
|
const gridExpSensor = (hasGrid && entities.grid_export) ? getVal(entities.grid_export) : 0;
|
||||||
const battery = hasBattery ? getVal(entities.battery) : 0;
|
let battery = hasBattery ? getVal(entities.battery) : 0;
|
||||||
|
if (this.config.invert_battery) {
|
||||||
|
battery *= -1;
|
||||||
|
}
|
||||||
const battSoc = (hasBattery && entities.battery_soc) ? getVal(entities.battery_soc) : 0;
|
const battSoc = (hasBattery && entities.battery_soc) ? getVal(entities.battery_soc) : 0;
|
||||||
|
|
||||||
const solarVal = Math.max(0, solar);
|
const solarVal = Math.max(0, solar);
|
||||||
|
|
@ -1655,6 +1739,7 @@ class PowerFluxCard extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("power-flux-card", PowerFluxCard);
|
customElements.define("power-flux-card", PowerFluxCard);
|
||||||
|
})();
|
||||||
|
|
||||||
window.customCards = window.customCards || [];
|
window.customCards = window.customCards || [];
|
||||||
window.customCards.push({
|
window.customCards.push({
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,28 @@ export default {
|
||||||
"editor.consumers_section": "Zusätzliche Verbraucher",
|
"editor.consumers_section": "Zusätzliche Verbraucher",
|
||||||
"editor.options_section": "Darstellung & Optionen",
|
"editor.options_section": "Darstellung & Optionen",
|
||||||
"editor.flow_rate_title": "Flussraten (W) an Röhren anzeigen",
|
"editor.flow_rate_title": "Flussraten (W) an Röhren anzeigen",
|
||||||
|
"editor.invert_battery": "Wert umkehren (+/-)",
|
||||||
"editor.label_toggle": "Label im Kreis anzeigen",
|
"editor.label_toggle": "Label im Kreis anzeigen",
|
||||||
"editor.compact_view": "Kompakte Ansicht (evcc)",
|
"editor.compact_view": "Kompakte Ansicht (evcc)",
|
||||||
"editor.hide_inactive": "Inaktive Röhren ausblenden",
|
"editor.hide_inactive": "Inaktive Röhren ausblenden",
|
||||||
"editor.entity": "Entität (Watt)",
|
"editor.entity": "Entität (Watt)",
|
||||||
"editor.label": "Beschriftung",
|
"editor.label": "Beschriftung",
|
||||||
"editor.icon": "Icon",
|
"editor.icon": "Icon",
|
||||||
|
"editor.back": "Zurück",
|
||||||
|
"editor.battery_soc_label": "Ladestand (%)",
|
||||||
|
"editor.house_total_title": "🏠 Gesamtverbrauch (optional)",
|
||||||
|
"editor.house_sensor_label": "Sensor für Hausverbrauch (optional)",
|
||||||
|
"editor.house_sensor_hint": "Wird benötigt, damit das Haus-Icon anklickbar ist.",
|
||||||
|
"editor.consumer_1_title": "🚗 Links (Lila)",
|
||||||
|
"editor.consumer_2_title": "♨️ Mitte (Orange)",
|
||||||
|
"editor.consumer_3_title": "🏊 Rechts (Türkis)",
|
||||||
|
"editor.zoom_label": "🔍 Zoom (Standard View)",
|
||||||
|
"editor.neon_glow": "Neon Glow",
|
||||||
|
"editor.donut_chart": "Donut Chart (Grid/Haus)",
|
||||||
|
"editor.comet_tail": "Comet Tail Effect",
|
||||||
|
"editor.dashed_line": "Dashed Line Effect",
|
||||||
|
"editor.colored_values": "Farbige Textwerte",
|
||||||
|
"editor.hide_consumer_icons": "Icons unten ausblenden",
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
"card.label_solar": "Solar",
|
"card.label_solar": "Solar",
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,28 @@ export default {
|
||||||
"editor.consumers_section": "Additional Consumers",
|
"editor.consumers_section": "Additional Consumers",
|
||||||
"editor.options_section": "Appearance & Options",
|
"editor.options_section": "Appearance & Options",
|
||||||
"editor.flow_rate_title": "Show Flow Rates (W) on pipes",
|
"editor.flow_rate_title": "Show Flow Rates (W) on pipes",
|
||||||
|
"editor.invert_battery": "Invert Power Value (+/-)",
|
||||||
"editor.label_toggle": "Show Label in Bubble",
|
"editor.label_toggle": "Show Label in Bubble",
|
||||||
"editor.compact_view": "Compact View (evcc)",
|
"editor.compact_view": "Compact View (evcc)",
|
||||||
"editor.hide_inactive": "Hide Inactive Pipes",
|
"editor.hide_inactive": "Hide Inactive Pipes",
|
||||||
"editor.entity": "Entity (Watt)",
|
"editor.entity": "Entity (Watt)",
|
||||||
"editor.label": "Label",
|
"editor.label": "Label",
|
||||||
"editor.icon": "Icon",
|
"editor.icon": "Icon",
|
||||||
|
"editor.back": "Back",
|
||||||
|
"editor.battery_soc_label": "State of Charge (%)",
|
||||||
|
"editor.house_total_title": "🏠 Total Consumption (optional)",
|
||||||
|
"editor.house_sensor_label": "Sensor for House Consumption (optional)",
|
||||||
|
"editor.house_sensor_hint": "Required to make the house icon clickable.",
|
||||||
|
"editor.consumer_1_title": "🚗 Left (Purple)",
|
||||||
|
"editor.consumer_2_title": "♨️ Center (Orange)",
|
||||||
|
"editor.consumer_3_title": "🏊 Right (Cyan)",
|
||||||
|
"editor.zoom_label": "🔍 Zoom (Standard View)",
|
||||||
|
"editor.neon_glow": "Neon Glow",
|
||||||
|
"editor.donut_chart": "Donut Chart (Grid/House)",
|
||||||
|
"editor.comet_tail": "Comet Tail Effect",
|
||||||
|
"editor.dashed_line": "Dashed Line Effect",
|
||||||
|
"editor.colored_values": "Colored Text Values",
|
||||||
|
"editor.hide_consumer_icons": "Hide Consumer Icons",
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
"card.label_solar": "Solar",
|
"card.label_solar": "Solar",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
import lang_en from "./lang-en.js";
|
||||||
|
import lang_de from "./lang-de.js";
|
||||||
|
|
||||||
|
const editorTranslations = {
|
||||||
|
"en": lang_en.editor,
|
||||||
|
"de": lang_de.editor
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const fireEvent = (node, type, detail, options) => {
|
const fireEvent = (node, type, detail, options) => {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
detail = detail === null || detail === undefined ? {} : detail;
|
detail = detail === null || detail === undefined ? {} : detail;
|
||||||
|
|
@ -58,6 +67,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
const entityKeys = [
|
const entityKeys = [
|
||||||
'solar', 'grid', 'grid_export',
|
'solar', 'grid', 'grid_export',
|
||||||
'battery', 'battery_soc',
|
'battery', 'battery_soc',
|
||||||
|
'house',
|
||||||
'consumer_1', 'consumer_2', 'consumer_3'
|
'consumer_1', 'consumer_2', 'consumer_3'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -178,7 +188,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="back-btn" @click=${this._goBack}>
|
<div class="back-btn" @click=${this._goBack}>
|
||||||
<ha-icon icon="mdi:arrow-left"></ha-icon> Zurück
|
<ha-icon icon="mdi:arrow-left"></ha-icon> ${this._localize('editor.back')}
|
||||||
</div>
|
</div>
|
||||||
<h2>${this._localize('editor.solar_section')}</h2>
|
<h2>${this._localize('editor.solar_section')}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -216,7 +226,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config.show_label_solar !== false}
|
.checked=${this._config.show_label_solar === true}
|
||||||
.configValue=${'show_label_solar'}
|
.configValue=${'show_label_solar'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
|
|
@ -238,7 +248,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="back-btn" @click=${this._goBack}>
|
<div class="back-btn" @click=${this._goBack}>
|
||||||
<ha-icon icon="mdi:arrow-left"></ha-icon> Zurück
|
<ha-icon icon="mdi:arrow-left"></ha-icon> ${this._localize('editor.back')}
|
||||||
</div>
|
</div>
|
||||||
<h2>${this._localize('editor.grid_section')}</h2>
|
<h2>${this._localize('editor.grid_section')}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -285,7 +295,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config.show_label_grid !== false}
|
.checked=${this._config.show_label_grid === true}
|
||||||
.configValue=${'show_label_grid'}
|
.configValue=${'show_label_grid'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
|
|
@ -307,7 +317,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="back-btn" @click=${this._goBack}>
|
<div class="back-btn" @click=${this._goBack}>
|
||||||
<ha-icon icon="mdi:arrow-left"></ha-icon> Zurück
|
<ha-icon icon="mdi:arrow-left"></ha-icon> ${this._localize('editor.back')}
|
||||||
</div>
|
</div>
|
||||||
<h2>${this._localize('editor.battery_section')}</h2>
|
<h2>${this._localize('editor.battery_section')}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -326,7 +336,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.selector=${entitySelectorSchema}
|
.selector=${entitySelectorSchema}
|
||||||
.value=${entities.battery_soc}
|
.value=${entities.battery_soc}
|
||||||
.configValue=${'battery_soc'}
|
.configValue=${'battery_soc'}
|
||||||
.label=${"Ladestand (%)"}
|
.label=${this._localize('editor.battery_soc_label')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-selector>
|
></ha-selector>
|
||||||
|
|
||||||
|
|
@ -354,7 +364,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config.show_label_battery !== false}
|
.checked=${this._config.show_label_battery === true}
|
||||||
.configValue=${'show_label_battery'}
|
.configValue=${'show_label_battery'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
|
|
@ -369,6 +379,15 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
|
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="switch-row">
|
||||||
|
<ha-switch
|
||||||
|
.checked=${this._config.invert_battery === true}
|
||||||
|
.configValue=${'invert_battery'}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
<div class="switch-label">${this._localize('editor.invert_battery')}</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,13 +395,28 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="back-btn" @click=${this._goBack}>
|
<div class="back-btn" @click=${this._goBack}>
|
||||||
<ha-icon icon="mdi:arrow-left"></ha-icon> Zurück
|
<ha-icon icon="mdi:arrow-left"></ha-icon> ${this._localize('editor.back')}
|
||||||
</div>
|
</div>
|
||||||
<h2>${this._localize('editor.consumers_section')}</h2>
|
<h2>${this._localize('editor.consumers_section')}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="consumer-group">
|
<div class="consumer-group">
|
||||||
<div class="consumer-title" style="color: #a855f7;">🚗 Links (Lila)</div>
|
<div class="consumer-title">${this._localize('editor.house_total_title')}</div>
|
||||||
|
<ha-selector
|
||||||
|
.hass=${this.hass}
|
||||||
|
.selector=${entitySelectorSchema}
|
||||||
|
.value=${entities.house || ""}
|
||||||
|
.configValue=${'house'}
|
||||||
|
.label=${this._localize('editor.house_sensor_label')}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-selector>
|
||||||
|
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
|
||||||
|
${this._localize('editor.house_sensor_hint')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="consumer-group">
|
||||||
|
<div class="consumer-title" style="color: #a855f7;">${this._localize('editor.consumer_1_title')}</div>
|
||||||
<ha-selector
|
<ha-selector
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${entitySelectorSchema}
|
.selector=${entitySelectorSchema}
|
||||||
|
|
@ -412,7 +446,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="consumer-group">
|
<div class="consumer-group">
|
||||||
<div class="consumer-title" style="color: #f97316;">♨️ Mitte (Orange)</div>
|
<div class="consumer-title" style="color: #f97316;">${this._localize('editor.consumer_2_title')}</div>
|
||||||
<ha-selector
|
<ha-selector
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${entitySelectorSchema}
|
.selector=${entitySelectorSchema}
|
||||||
|
|
@ -442,7 +476,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="consumer-group">
|
<div class="consumer-group">
|
||||||
<div class="consumer-title" style="color: #06b6d4;">🏊 Rechts (Türkis)</div>
|
<div class="consumer-title" style="color: #06b6d4;">${this._localize('editor.consumer_3_title')}</div>
|
||||||
<ha-selector
|
<ha-selector
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${entitySelectorSchema}
|
.selector=${entitySelectorSchema}
|
||||||
|
|
@ -525,7 +559,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.selector=${{ number: { min: 0.5, max: 1.5, step: 0.05, mode: "slider" } }}
|
.selector=${{ number: { min: 0.5, max: 1.5, step: 0.05, mode: "slider" } }}
|
||||||
.value=${this._config.zoom !== undefined ? this._config.zoom : 0.9}
|
.value=${this._config.zoom !== undefined ? this._config.zoom : 0.9}
|
||||||
.configValue=${'zoom'}
|
.configValue=${'zoom'}
|
||||||
.label=${"🔍 Zoom (Standard View)"}
|
.label=${this._localize('editor.zoom_label')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-selector>
|
></ha-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -536,7 +570,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'show_neon_glow'}
|
.configValue=${'show_neon_glow'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Neon Glow</div>
|
<div class="switch-label">${this._localize('editor.neon_glow')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
@ -545,7 +579,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'show_donut_border'}
|
.configValue=${'show_donut_border'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Donut Chart (Grid/Haus)</div>
|
<div class="switch-label">${this._localize('editor.donut_chart')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
@ -554,7 +588,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'show_comet_tail'}
|
.configValue=${'show_comet_tail'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Comet Tail Effect</div>
|
<div class="switch-label">${this._localize('editor.comet_tail')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
@ -563,7 +597,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'show_dashed_line'}
|
.configValue=${'show_dashed_line'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Dashed Line Animation</div>
|
<div class="switch-label">${this._localize('editor.dashed_line')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
@ -572,7 +606,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'use_colored_values'}
|
.configValue=${'use_colored_values'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Farbige Textwerte</div>
|
<div class="switch-label">${this._localize('editor.colored_values')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
@ -581,7 +615,7 @@ class PowerFluxCardEditor extends LitElement {
|
||||||
.configValue=${'hide_consumer_icons'}
|
.configValue=${'hide_consumer_icons'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
<div class="switch-label">Icons unten ausblenden</div>
|
<div class="switch-label">${this._localize('editor.hide_consumer_icons')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,23 @@
|
||||||
|
import { } from "./power-flux-card-editor.js";
|
||||||
|
import lang_en from "./lang-en.js";
|
||||||
|
import lang_de from "./lang-de.js";
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"%c⚡ Power-Flux-Card v_2.0 ready",
|
"%c⚡ Power Flux Card v_2.1 ready",
|
||||||
"background: #2ecc71; color: #000; padding: 2px 6px; border-radius: 4px; font-weight: bold;"
|
"background: #d19525ff; color: #000; padding: 2px 6px; border-radius: 4px; font-weight: bold;"
|
||||||
);
|
);
|
||||||
|
|
||||||
class PowerFluxCard extends LitElement {
|
(function (lang_en, lang_de) {
|
||||||
|
const cardTranslations = {
|
||||||
|
"en": lang_en.card,
|
||||||
|
"de": lang_de.card
|
||||||
|
};
|
||||||
|
|
||||||
|
const LitElement = customElements.get("ha-lit-element") || Object.getPrototypeOf(customElements.get("home-assistant-main"));
|
||||||
|
const html = LitElement.prototype.html;
|
||||||
|
const css = LitElement.prototype.css;
|
||||||
|
|
||||||
|
class PowerFluxCard extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
|
|
@ -48,6 +61,7 @@ class PowerFluxCard extends LitElement {
|
||||||
grid_export: "",
|
grid_export: "",
|
||||||
battery: "",
|
battery: "",
|
||||||
battery_soc: "",
|
battery_soc: "",
|
||||||
|
house: "",
|
||||||
consumer_1: "",
|
consumer_1: "",
|
||||||
consumer_2: "",
|
consumer_2: "",
|
||||||
consumer_3: ""
|
consumer_3: ""
|
||||||
|
|
@ -55,6 +69,16 @@ class PowerFluxCard extends LitElement {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleClick(entityId) {
|
||||||
|
if (!entityId) return;
|
||||||
|
const event = new Event("hass-more-info", {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
});
|
||||||
|
event.detail = { entityId };
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
if (!config.entities) {
|
if (!config.entities) {
|
||||||
// Init allow
|
// Init allow
|
||||||
|
|
@ -265,7 +289,7 @@ class PowerFluxCard extends LitElement {
|
||||||
.node-c2 { top: 370px; left: 165px; }
|
.node-c2 { top: 370px; left: 165px; }
|
||||||
.node-c3 { top: 370px; left: 325px; }
|
.node-c3 { top: 370px; left: 325px; }
|
||||||
|
|
||||||
svg { position: absolute; top: 7; left: 25; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
|
svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
|
||||||
|
|
||||||
.bg-path { fill: none; stroke-width: 6; transition: opacity 0.3s ease; }
|
.bg-path { fill: none; stroke-width: 6; transition: opacity 0.3s ease; }
|
||||||
.bg-solar { stroke: var(--neon-yellow); }
|
.bg-solar { stroke: var(--neon-yellow); }
|
||||||
|
|
@ -398,7 +422,10 @@ class PowerFluxCard extends LitElement {
|
||||||
const solar = entities.solar ? Math.max(0, getVal(entities.solar)) : 0;
|
const solar = entities.solar ? Math.max(0, getVal(entities.solar)) : 0;
|
||||||
const gridMain = entities.grid ? getVal(entities.grid) : 0;
|
const gridMain = entities.grid ? getVal(entities.grid) : 0;
|
||||||
const gridExportSensor = entities.grid_export ? getVal(entities.grid_export) : 0;
|
const gridExportSensor = entities.grid_export ? getVal(entities.grid_export) : 0;
|
||||||
const battery = entities.battery ? getVal(entities.battery) : 0;
|
let battery = entities.battery ? getVal(entities.battery) : 0;
|
||||||
|
if (this.config.invert_battery) {
|
||||||
|
battery *= -1;
|
||||||
|
}
|
||||||
const c1Val = entities.consumer_1 ? getVal(entities.consumer_1) : 0; // EV Value
|
const c1Val = entities.consumer_1 ? getVal(entities.consumer_1) : 0; // EV Value
|
||||||
|
|
||||||
// 2. Logic Calculation
|
// 2. Logic Calculation
|
||||||
|
|
@ -476,7 +503,7 @@ class PowerFluxCard extends LitElement {
|
||||||
const barSegments = [];
|
const barSegments = [];
|
||||||
let currentX = 0;
|
let currentX = 0;
|
||||||
|
|
||||||
const addSegment = (val, color, type, label) => {
|
const addSegment = (val, color, type, label, entityId) => {
|
||||||
if (val <= threshold) return;
|
if (val <= threshold) return;
|
||||||
const pct = val / totalFlux;
|
const pct = val / totalFlux;
|
||||||
const width = pct * fullWidth;
|
const width = pct * fullWidth;
|
||||||
|
|
@ -487,14 +514,15 @@ class PowerFluxCard extends LitElement {
|
||||||
widthPx: width,
|
widthPx: width,
|
||||||
startPx: currentX,
|
startPx: currentX,
|
||||||
type,
|
type,
|
||||||
label
|
label,
|
||||||
|
entityId
|
||||||
});
|
});
|
||||||
currentX += width;
|
currentX += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSegment(srcBattery, 'var(--neon-yellow)', 'battery', 'battery');
|
addSegment(srcBattery, 'var(--neon-yellow)', 'battery', 'battery', entities.battery);
|
||||||
addSegment(srcSolar, 'var(--neon-green)', 'solar', 'solar');
|
addSegment(srcSolar, 'var(--neon-green)', 'solar', 'solar', entities.solar);
|
||||||
addSegment(srcGrid, 'var(--grid-grey)', 'grid', 'grid');
|
addSegment(srcGrid, 'var(--grid-grey)', 'grid', 'grid', entities.grid);
|
||||||
|
|
||||||
// --- GENERATE TOP BRACKETS (Based on Bar Segments) ---
|
// --- GENERATE TOP BRACKETS (Based on Bar Segments) ---
|
||||||
const topBrackets = barSegments.map(s => {
|
const topBrackets = barSegments.map(s => {
|
||||||
|
|
@ -505,7 +533,7 @@ class PowerFluxCard extends LitElement {
|
||||||
if (s.type === 'grid') { icon = 'mdi:transmission-tower'; iconColor = 'var(--grid-grey)'; }
|
if (s.type === 'grid') { icon = 'mdi:transmission-tower'; iconColor = 'var(--grid-grey)'; }
|
||||||
if (s.type === 'battery') { icon = 'mdi:battery-high'; iconColor = 'var(--neon-yellow)'; }
|
if (s.type === 'battery') { icon = 'mdi:battery-high'; iconColor = 'var(--neon-yellow)'; }
|
||||||
|
|
||||||
return { path, width: s.widthPx, center: s.startPx + (s.widthPx/2), icon, iconColor };
|
return { path, width: s.widthPx, center: s.startPx + (s.widthPx / 2), icon, iconColor, val: s.val, entityId: s.entityId };
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- GENERATE BOTTOM BRACKETS (Independent Calculation) ---
|
// --- GENERATE BOTTOM BRACKETS (Independent Calculation) ---
|
||||||
|
|
@ -513,7 +541,7 @@ class PowerFluxCard extends LitElement {
|
||||||
const bottomBrackets = [];
|
const bottomBrackets = [];
|
||||||
let bottomX = 0;
|
let bottomX = 0;
|
||||||
|
|
||||||
const addBottomBracket = (val, type) => {
|
const addBottomBracket = (val, type, entityId = null) => {
|
||||||
if (val <= threshold) return;
|
if (val <= threshold) return;
|
||||||
const pct = val / totalFlux;
|
const pct = val / totalFlux;
|
||||||
const width = pct * fullWidth;
|
const width = pct * fullWidth;
|
||||||
|
|
@ -524,6 +552,7 @@ class PowerFluxCard extends LitElement {
|
||||||
if (type === 'house') { icon = 'mdi:home'; iconColor = 'var(--primary-text-color)'; }
|
if (type === 'house') { icon = 'mdi:home'; iconColor = 'var(--primary-text-color)'; }
|
||||||
if (type === 'car') { icon = 'mdi:car-electric'; iconColor = '#a855f7'; }
|
if (type === 'car') { icon = 'mdi:car-electric'; iconColor = '#a855f7'; }
|
||||||
if (type === 'export') { icon = 'mdi:arrow-right-box'; iconColor = 'var(--export-purple)'; }
|
if (type === 'export') { icon = 'mdi:arrow-right-box'; iconColor = 'var(--export-purple)'; }
|
||||||
|
if (type === 'battery') { icon = 'mdi:battery-charging-high'; iconColor = 'var(--neon-green)'; }
|
||||||
|
|
||||||
const path = this._createBracketPath(bottomX, width, 'up');
|
const path = this._createBracketPath(bottomX, width, 'up');
|
||||||
bottomBrackets.push({
|
bottomBrackets.push({
|
||||||
|
|
@ -531,14 +560,17 @@ class PowerFluxCard extends LitElement {
|
||||||
width: width,
|
width: width,
|
||||||
center: bottomX + (width / 2),
|
center: bottomX + (width / 2),
|
||||||
icon,
|
icon,
|
||||||
iconColor
|
iconColor,
|
||||||
|
val,
|
||||||
|
entityId
|
||||||
});
|
});
|
||||||
bottomX += width;
|
bottomX += width;
|
||||||
};
|
};
|
||||||
|
|
||||||
addBottomBracket(destHouse, 'house');
|
addBottomBracket(destHouse, 'house', entities.house);
|
||||||
addBottomBracket(destEV, 'car');
|
addBottomBracket(destEV, 'car', entities.consumer_1);
|
||||||
addBottomBracket(destExport, 'export');
|
addBottomBracket(destExport, 'export', entities.grid_export || entities.grid);
|
||||||
|
addBottomBracket(batteryCharge, 'battery', entities.battery);
|
||||||
|
|
||||||
// Note: If there is Battery Charging happening, bottomX will not reach fullWidth.
|
// Note: If there is Battery Charging happening, bottomX will not reach fullWidth.
|
||||||
// This leaves a gap at the end (or between segments depending on logic), which is visually correct
|
// This leaves a gap at the end (or between segments depending on logic), which is visually correct
|
||||||
|
|
@ -553,7 +585,10 @@ class PowerFluxCard extends LitElement {
|
||||||
${topBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
${topBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
||||||
</svg>
|
</svg>
|
||||||
${topBrackets.map(b => b.width > 20 ? html`
|
${topBrackets.map(b => b.width > 20 ? html`
|
||||||
<div class="compact-icon-wrapper" style="left: ${b.center}px; transform: translateX(-50%); top: 4px;">
|
<div class="compact-icon-wrapper"
|
||||||
|
style="left: ${b.center}px; transform: translateX(-50%); top: 4px; cursor: ${b.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(b.val)}"
|
||||||
|
@click=${() => b.entityId && this._handleClick(b.entityId)}>
|
||||||
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
||||||
</div>` : '')}
|
</div>` : '')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -561,7 +596,10 @@ class PowerFluxCard extends LitElement {
|
||||||
<!-- MAIN BAR -->
|
<!-- MAIN BAR -->
|
||||||
<div class="compact-bar-wrapper">
|
<div class="compact-bar-wrapper">
|
||||||
${barSegments.map(s => html`
|
${barSegments.map(s => html`
|
||||||
<div class="bar-segment" style="width: ${s.widthPct}%; background: ${s.color}; color: ${s.color === 'var(--export-purple)' ? 'white' : 'black'};">
|
<div class="bar-segment"
|
||||||
|
style="width: ${s.widthPct}%; background: ${s.color}; color: ${s.color === 'var(--export-purple)' ? 'white' : 'black'}; cursor: ${s.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(s.val)}"
|
||||||
|
@click=${() => s.entityId && this._handleClick(s.entityId)}>
|
||||||
${s.widthPx > 35 ? this._formatPower(s.val) : ''}
|
${s.widthPx > 35 ? this._formatPower(s.val) : ''}
|
||||||
</div>
|
</div>
|
||||||
`)}
|
`)}
|
||||||
|
|
@ -573,7 +611,10 @@ class PowerFluxCard extends LitElement {
|
||||||
${bottomBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
${bottomBrackets.map(b => this._renderSVGPath(b.path, b.iconColor))}
|
||||||
</svg>
|
</svg>
|
||||||
${bottomBrackets.map(b => b.width > 20 ? html`
|
${bottomBrackets.map(b => b.width > 20 ? html`
|
||||||
<div class="compact-icon-wrapper" style="left: ${b.center}px; transform: translateX(-50%); top: -3px;">
|
<div class="compact-icon-wrapper"
|
||||||
|
style="left: ${b.center}px; transform: translateX(-50%); top: -3px; cursor: ${b.entityId ? 'pointer' : 'default'};"
|
||||||
|
title="${this._formatPower(b.val)}"
|
||||||
|
@click=${() => b.entityId && this._handleClick(b.entityId)}>
|
||||||
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
<ha-icon icon="${b.icon}" class="compact-icon" style="color: ${b.iconColor};"></ha-icon>
|
||||||
</div>` : '')}
|
</div>` : '')}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -654,7 +695,10 @@ class PowerFluxCard extends LitElement {
|
||||||
const solar = hasSolar ? getVal(entities.solar) : 0;
|
const solar = hasSolar ? getVal(entities.solar) : 0;
|
||||||
const gridMain = hasGrid ? getVal(entities.grid) : 0;
|
const gridMain = hasGrid ? getVal(entities.grid) : 0;
|
||||||
const gridExpSensor = (hasGrid && entities.grid_export) ? getVal(entities.grid_export) : 0;
|
const gridExpSensor = (hasGrid && entities.grid_export) ? getVal(entities.grid_export) : 0;
|
||||||
const battery = hasBattery ? getVal(entities.battery) : 0;
|
let battery = hasBattery ? getVal(entities.battery) : 0;
|
||||||
|
if (this.config.invert_battery) {
|
||||||
|
battery *= -1;
|
||||||
|
}
|
||||||
const battSoc = (hasBattery && entities.battery_soc) ? getVal(entities.battery_soc) : 0;
|
const battSoc = (hasBattery && entities.battery_soc) ? getVal(entities.battery_soc) : 0;
|
||||||
|
|
||||||
const solarVal = Math.max(0, solar);
|
const solarVal = Math.max(0, solar);
|
||||||
|
|
@ -972,6 +1016,7 @@ class PowerFluxCard extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("power-flux-card", PowerFluxCard);
|
customElements.define("power-flux-card", PowerFluxCard);
|
||||||
|
})(lang_en, lang_de);
|
||||||
|
|
||||||
window.customCards = window.customCards || [];
|
window.customCards = window.customCards || [];
|
||||||
window.customCards.push({
|
window.customCards.push({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue