Compare commits

...

35 commits
v_2.0 ... main

Author SHA1 Message Date
f19730ff1d Fix: Secondary sensor display - Wh/EUR precision
- Separate Wh from W in getSecondaryVal: Wh values >= 1000
  now display as kWh (e.g., 794 Wh -> 0.79 kWh)
- EUR/ct/€ units now show 2 decimal places (0.28 EUR/kWh
  instead of 0.3)
- W values continue using _formatPower() as before

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 10:27:27 +01:00
jayjojayson
1031af5b3d
v_2.4 2026-03-04 13:04:31 +01:00
jayjojayson
97fd58f671
v_2.4 2026-03-04 01:24:16 +01:00
jayjojayson
2dc24eca1b v_2.4 2026-03-04 01:05:14 +01:00
jayjojayson
9430bc9bcf power-flux-card Auto-build 2026-03-03 22:31:03 +00:00
jayjojayson
4ad407f030 v_2.4 2026-03-03 23:30:52 +01:00
jayjojayson
67f6021cd5 power-flux-card Auto-build 2026-03-01 23:38:55 +00:00
jayjojayson
7750f75162 v_2.3 2026-03-02 00:38:38 +01:00
jayjojayson
7ecf0d53b6 v_2.3 2026-03-02 00:31:47 +01:00
jayjojayson
dba01153ba power-flux-card Auto-build 2026-02-23 23:29:20 +00:00
jayjojayson
e2f070941d v_2.2 2026-02-24 00:29:01 +01:00
jayjojayson
7f6e44e938
v_2.1 2026-02-21 02:46:59 +01:00
jayjojayson
e897990d6a
v_2.1 2026-02-21 02:46:06 +01:00
jayjojayson
a163f4c86e
v_2.1 2026-02-19 23:41:42 +01:00
jayjojayson
b2b381a719
v_2.1 2026-02-19 23:41:19 +01:00
jayjojayson
85f7f2c9d4 v_2.1 2026-02-19 23:40:24 +01:00
jayjojayson
a874c989fe Merge branch 'main' of https://github.com/jayjojayson/power-flux-card 2026-02-19 23:39:27 +01:00
jayjojayson
c7ce762878
v_2.1 2026-02-19 23:36:38 +01:00
jayjojayson
b742bcd981 v_2.1 2026-02-19 14:35:26 +01:00
jayjojayson
a2dab3d45b
v_2.1 2026-02-18 00:26:24 +01:00
jayjojayson
f10b5c546b
v_2.1 2026-02-18 00:25:16 +01:00
jayjojayson
1dedfa09cc
Update README-de.md 2026-02-18 00:24:59 +01:00
jayjojayson
8bce6492ee power-flux-card Auto-build 2026-02-17 22:40:22 +00:00
jayjojayson
7f6534880d v_2.1 2026-02-17 23:40:08 +01:00
jayjojayson
2f43a045c8 power-flux-card Auto-build 2026-02-17 22:22:20 +00:00
jayjojayson
41cc4e81cb v_2.1 2026-02-17 23:22:08 +01:00
jayjojayson
168edb3df6 power-flux-card Auto-build 2026-02-17 22:01:58 +00:00
jayjojayson
181b64c590 v_2.1 2026-02-17 23:01:12 +01:00
jayjojayson
d5a4eb33ef
v_2.0 2026-02-15 18:39:43 +01:00
jayjojayson
2c1f9255d1
v_2.0 2026-02-15 18:38:47 +01:00
jayjojayson
465005ce0e
v_2.0 2026-02-14 15:18:39 +01:00
jayjojayson
0aa8ef9f0f
v_2.0 2026-02-14 15:17:19 +01:00
jayjojayson
5cb5d010fc v_2.0 2026-02-14 15:15:26 +01:00
jayjojayson
f8b93ef4c6
v_2.0 2026-02-14 13:44:18 +01:00
jayjojayson
650acf2c97
v_2.0 2026-02-14 13:41:37 +01:00
12 changed files with 3013 additions and 1410 deletions

1
.github/FUNDING.yml vendored
View file

@ -1,2 +1,3 @@
---
ko_fi: jayjojayson
custom: ["https://www.paypal.me/quadFlyerFW"]

179
README.md
View file

@ -7,12 +7,14 @@
[![Stars](https://img.shields.io/github/stars/jayjojayson/power-flux-card)](https://github.com/jayjojayson/power-flux-card/stargazers)
# Power Flux Card
# Power Flux Card
The ⚡ Power Flux Card is an advanced, animated energy flow card for Home Assistant. It visualizes the power distribution between Solar, Grid, Battery, and Consumers with beautiful neon effects and smooth animations.
The ⚡ Power Flux Card is an advanced, animated energy flow card for Home Assistant. It visualizes the power distribution between Solar, Grid, Battery, and Consumers with beautiful neon effects and diffrent animations.
<img width="45%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card.jpg" />
<img width="45%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact.jpg" />
If you like the Card, I would appreciate a Star rating ⭐ from you. 🤗
<img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-ani.gif" /> <img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card.jpg" />
<img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact.jpg" /> <img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact2.jpg" />
### ✨ Features
@ -24,9 +26,19 @@ The ⚡ Power Flux Card is an advanced, animated energy flow card for Home Assis
- **Donut Chart**: Optional donut chart around the house icon showing energy mix.
- **Comet Tail / Dashed Lines**: Choose your preferred animation style.
- **Zoom**: Adjustable scale to fit your dashboard.
- **Custom Colors**: Define custom colors for each source and consumer via the editor.
- **Background Color**: Enable a slightly tinted background for the circles in the default view.
- **More Info**: Click on any source/consumer for detailed information in a more-info dialog.
- **Grid Import/Export**: Supports both separate Import/Export entities or a combined entity with positive/negative values.
- **Grid-to-Battery**: Optional direct sensor for Grid-to-Battery flow, bypassing the standard calculation.
- **Secondary Sensors**: Optionally display a secondary sensor value in the main circles (e.g., daily yield for Solar, current charge/discharge power for Battery) and consumer bubbles.
- **Localization**: Fully translated in English and German.
- **Visual Editor**: easy configuration via the Home Assistant UI.
[![Support](https://img.shields.io/badge/Features-Video%20german-steelblue?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=HGFBJJRWGW0)
---
### 🚀 Installation
### HACS (Recommended)
@ -35,10 +47,9 @@ The ⚡ Power Flux Card is an advanced, animated energy flow card for Home Assis
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=jayjojayson&repository=power-flux-card&category=plugin)
- The "Detailed Charts Panel" should now be available in HACS. Click on "INSTALL".
- The "Power Flux Card" should now be available in HACS. Click on "INSTALL".
- The resource will be automatically added to your Lovelace configuration.
- Create the file `detail-charts-views.js` in the `/config/www/` folder.
-
- Create the file `power-flux-card.js` in the `/config/www/` folder.
#### HACS (manual)
1. Ensure HACS is installed.
@ -48,11 +59,13 @@ The ⚡ Power Flux Card is an advanced, animated energy flow card for Home Assis
#### Manual Installation
1. Download `power-flux-card.js` from the [Releases](../../releases) page.
2. Upload it to your `www` folder in Home Assistant.
2. Upload it to `www/community/power-flux-card/` folder in Home Assistant.
3. Add the resource in your Dashboard configuration:
- URL: `/local/power-flux-card.js`
- URL: `/local/community/power-flux-card/power-flux-card.js`
- Type: JavaScript Module
---
### ⚙️ Configuration
You can configure the card directly via the visual editor in Home Assistant.
@ -71,3 +84,151 @@ You can configure the card directly via the visual editor in Home Assistant.
- **Donut Chart**: Show the energy mix as a ring around the house.
- **Comet Tail / Dashed Line**: Change the flow animation style.
- **Compact View**: Switch to the bar chart layout.
- **Color Options**: Define custom colors for each source and consumer.
- **Grid Import/Export**: Configure separate or combined entities.
- **Grid-to-Battery**: Optional direct sensor for Grid-to-Battery flow.
- **Separate Battery Sensors**: Optional separate sensors for battery charge and discharge.
- **Secondary Sensors**: Display alternative values in the main circles (e.g., daily yield, current charge power).
<details>
<summary> <b>Custom Colors with card_mod and Jinja2 Templates</b></summary>
With the [card_mod](https://github.com/thomasloven/lovelace-card-mod) integration, you can dynamically override the CSS variables of the Power Flux Card using Jinja2 templates. This allows you to change colors based on sensor values — e.g., green solar icon during production, grey when idle.
### Available CSS Variables
| Variable | Description |
|---|---|
| `--neon-yellow` | Bubble color Solar |
| `--neon-blue` | Bubble color Grid |
| `--neon-green` | Bubble color Battery |
| `--neon-pink` | Bubble color House |
| `--pipe-solar-color` | Pipe color Solar |
| `--pipe-grid-color` | Pipe color Grid |
| `--pipe-battery-color` | Pipe color Battery |
| `--icon-solar-color` | Icon color Solar |
| `--icon-grid-color` | Icon color Grid |
| `--icon-battery-color` | Icon color Battery |
| `--icon-house-color` | Icon color House |
| `--icon-consumer-1-color` | Icon color Consumer 1 |
| `--text-solar-color` | Text color Solar |
| `--text-grid-color` | Text color Grid |
| `--text-battery-color` | Text color Battery |
| `--text-house-color` | Text color House |
| `--text-consumer-1-color` | Text color Consumer 1 |
| `--consumer-1-color` | Bubble color Consumer 1 |
| `--consumer-2-color` | Bubble color Consumer 2 |
| `--consumer-3-color` | Bubble color Consumer 3 |
| `--export-color` | Color for Export |
### Example 1: Solar Icon — green during production, grey when idle
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% if states('sensor.solar_power') | float > 0 %}
--icon-solar-color: #00ff88;
{% else %}
--icon-solar-color: #9e9e9e;
{% endif %}
}
```
### Example 2: Grid text color — red on export, blue on import
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid_combined: sensor.grid_power_combined
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% if states('sensor.grid_power_combined') | float < 0 %}
--text-grid-color: #ff3333;
{% else %}
--text-grid-color: #3b82f6;
{% endif %}
}
```
### Example 3: Battery bubble — color based on State of Charge (SoC)
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% set soc = states('sensor.battery_soc') | float %}
{% if soc > 80 %}
--neon-green: #00ff88;
{% elif soc > 30 %}
--neon-green: #f59e0b;
{% else %}
--neon-green: #ff3333;
{% endif %}
}
```
### Example 4: Consumer 1 pipe — visible only at high power, otherwise transparent
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
consumer_1: sensor.wallbox_power
card_mod:
style: |
:host {
{% if states('sensor.wallbox_power') | float > 500 %}
--pipe-consumer-1-color: #a855f7;
--icon-consumer-1-color: #a855f7;
{% else %}
--pipe-consumer-1-color: rgba(168, 85, 247, 0.2);
--icon-consumer-1-color: #9e9e9e;
{% endif %}
}
```
### Example 5: Multiple colors at once — night mode (everything dimmed when Solar = 0)
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
consumer_1: sensor.wallbox_power
card_mod:
style: |
:host {
{% if states('sensor.solar_power') | float == 0 %}
--icon-solar-color: #555555;
--text-solar-color: #777777;
--neon-yellow: #666633;
--pipe-solar-color: #444422;
{% endif %}
}
```
> **Note:** card_mod must be installed separately via HACS. Templates are evaluated on every state update, so colors change in real time.
</details>

View file

@ -6,7 +6,7 @@ const DIST_DIR = 'dist';
const OUTPUT_FILE = path.join(DIST_DIR, 'power-flux-card.js');
// Ensure dist dir exists
if (!fs.existsSync(DIST_DIR)){
if (!fs.existsSync(DIST_DIR)) {
fs.mkdirSync(DIST_DIR);
}
@ -24,17 +24,17 @@ const cardTranslations = {};
langFiles.forEach(file => {
const langCode = file.replace('lang-', '').replace('.js', '');
let content = fs.readFileSync(path.join(SRC_DIR, file), 'utf8');
// Remove export default
content = content.replace('export default', '').trim();
if (content.endsWith(';')) {
content = content.slice(0, -1);
}
// Assign to a variable
langDefinitions += `const lang_${langCode} = ${content};\n`;
// Add to merge logic
mergeScript += `editorTranslations['${langCode}'] = lang_${langCode}.editor;\n`;
mergeScript += `cardTranslations['${langCode}'] = lang_${langCode}.card;\n`;
@ -49,6 +49,9 @@ console.log('Processing editor...');
let editorContent = fs.readFileSync(path.join(SRC_DIR, 'power-flux-card-editor.js'), 'utf8');
// Remove imports/exports if any
editorContent = editorContent.replace(/import .* from .*/g, '').replace(/export .*/g, '');
// Remove editorTranslations/cardTranslations declarations (already created by build merge script)
editorContent = editorContent.replace(/const editorTranslations\s*=\s*\{[^}]*\};/gs, '');
editorContent = editorContent.replace(/const cardTranslations\s*=\s*\{[^}]*\};/gs, '');
// Process Main Card
console.log('Processing main card...');

2017
dist/power-flux-card.js vendored

File diff suppressed because it is too large Load diff

View file

@ -9,10 +9,12 @@
# Power Flux Card
Die ⚡Power Flux Card ist eine erweiterte, animierte Energiefluss-Karte für Home Assistant. Sie visualisiert die Energieverteilung zwischen Solar, Netz, Batterie und Verbrauchern mit wunderschönen Neon-Effekten und flüssigen Animationen.
Die ⚡Power Flux Card ist eine erweiterte, animierte Energiefluss-Karte für Home Assistant. Sie visualisiert die Energieverteilung zwischen Solar, Netz, Batterie und Verbrauchern mit wunderschönen Neon-Effekten und verschiedenen Animationen.
<img width="45%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card.jpg" />
<img width="45%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact.jpg" />
Wenn euch die custom Card gefällt, würde ich mich sehr über eine Sternebewertung ⭐ freuen. 🤗
<img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-ani.gif" /> <img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card.jpg" />
<img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact.jpg" /> <img width="49%" height="auto" alt="power-flux-card" src="https://github.com/jayjojayson/power-flux-card/blob/main/docs/images/power-flux-card-compact2.jpg" />
### ✨ Funktionen
@ -24,9 +26,21 @@ Die ⚡Power Flux Card ist eine erweiterte, animierte Energiefluss-Karte für Ho
- **Donut Chart**: Optionales Donut-Diagramm um das Haus-Icon, das den Energiemix zeigt.
- **Kometenschweif / Gestrichelte Linien**: Wählen Sie Ihren bevorzugten Animationsstil.
- **Zoom**: Anpassbare Größe für Ihr Dashboard.
- **Benutzerdefinierte Farben**: Definiere benutzerdefinierte Farben für jede Quelle und jeden Verbraucher über den Editor.
- **Hintergrundfarbe**: Aktiviere einen leicht getönten Hintergrund für die Kreise in der Standard-Ansicht.
- **Dynamische Animationsgeschwindigkeit**: Partikelgeschwindigkeit und -dichte passen sich dem aktuellen Energiefluss an.
- **Weitere Informationen**: Klicke auf eine beliebige Quelle/Verbraucher, um detaillierte Informationen in einem More-Info-Dialog anzuzeigen.
- **Netz-Import/Export**: Unterstützt sowohl separate Import/Export-Entitäten als auch eine kombinierte Entität mit positiven/negativen Werten.
- **Netz-zu-Batterie**: Optionaler direkter Sensor für den Netz-zu-Batterie-Fluss, der die Standardberechnung umgeht.
- **Sekundäre Sensoren**: Optional können sekundäre Sensorwerte in den Hauptkreisen (z.B. Tagesertrag für Solar, aktuelle Lade-/Entladeleistung für Batterie) angezeigt werden.
- **Lokalisierung**: Vollständig übersetzt in Deutsch und Englisch.
- **Visueller Editor**: Einfache Konfiguration über die Home Assistant UI.
[![Watch the video](https://img.youtube.com/vi/HGFBJJRWGW0/0.jpg)](https://www.youtube.com/watch?v=HGFBJJRWGW0
)
---
### 🚀 Installation
### HACS (Empfohlen)
@ -35,25 +49,28 @@ Die ⚡Power Flux Card ist eine erweiterte, animierte Energiefluss-Karte für Ho
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=jayjojayson&repository=power-flux-card&category=plugin)
- Das "Detailed Charts Panel" sollte nun in HACS verfügbar sein. Klicke auf "INSTALLIEREN" ("INSTALL").
- Das "Power Flux Card" sollte nun in HACS verfügbar sein. Klicke auf "INSTALLIEREN" ("INSTALL").
- Die Ressource wird automatisch zu deiner Lovelace-Konfiguration hinzugefügt.
-
#### HACS (manuell)
1. Stellen Sie sicher, dass HACS installiert ist.
2. Fügen Sie dieses Repository als benutzerdefiniertes Repository in HACS hinzu.
3. Suchen Sie nach "Power Flux Card" und installieren Sie es.
4. Laden Sie die Ressourcen neu, falls Sie dazu aufgefordert werden.
1. Stelle sicher, dass HACS installiert ist.
2. Füge dieses Repository als benutzerdefiniertes Repository in HACS hinzu.
3. Suche nach "Power Flux Card" und installieren Sie es.
4. Lade die Ressourcen neu, falls Sie dazu aufgefordert werden.
#### Manuelle Installation
1. Laden Sie die Datei `power-flux-card.js` von der [Releases](../../releases)-Seite herunter.
2. Laden Sie sie in Ihren `www`-Ordner in Home Assistant hoch.
3. Fügen Sie die Ressource in Ihrer Dashboard-Konfiguration hinzu:
- URL: `/local/power-flux-card.js`
1. Lade die Datei `power-flux-card.js` von der [Releases](../../releases)-Seite herunter.
2. Lade sie in Ihren `www/community/power-flux-card/`-Ordner in Home Assistant hoch.
3. Füge die Ressource in Ihrer Dashboard-Konfiguration hinzu:
- URL: `/local/community/power-flux-card/power-flux-card.js`
- Typ: JavaScript Module
---
### ⚙️ Konfiguration
Sie können die Karte direkt über den visuellen Editor in Home Assistant konfigurieren.
Du kannst die Karte direkt über den visuellen Editor in Home Assistant konfigurieren.
**Haupt-Entitäten:**
- **Solar**: Erzeugung (W).
@ -61,7 +78,7 @@ Sie können die Karte direkt über den visuellen Editor in Home Assistant konfig
- **Batterie**: Batterieleistung (W) und Ladestand (%).
**Zusätzliche Verbraucher:**
- Sie können bis zu 3 individuelle Verbraucher (z.B. Auto, Heizung, Pool) mit eigenen Icons und Beschriftungen hinzufügen.
- Du kannst bis zu 3 individuelle Verbraucher (z.B. Auto, Heizung, Pool) mit eigenen Icons und Beschriftungen hinzufügen.
**Optionen:**
- **Zoom**: Passen Sie die Größe der Karte an.
@ -69,3 +86,151 @@ Sie können die Karte direkt über den visuellen Editor in Home Assistant konfig
- **Donut Chart**: Zeigt den Energiemix als Ring um das Haus an.
- **Kometenschweif / Gestrichelte Linie**: Ändern Sie den Stil der Flussanimation.
- **Kompakte Ansicht**: Wechseln Sie zum Balkendiagramm-Layout.
- **Farboptionen**: Definieren Sie benutzerdefinierte Farben für jede Quelle und Verbraucher.
- **Netz-Import/Export**: Konfigurieren Sie separate oder kombinierte Entitäten.
- **Netz-zu-Batterie**: Optionaler direkter Sensor für den Netz-zu-Batterie-Fluss.
- **Batterie getrennte Sensoren**: Optional separate Sensoren für Batterie-Ladung und -Entladung.
- **Sekundäre Sensoren**: Zeigen Sie alternative Werte in den Hauptkreisen an (z.B. Tagesertrag, aktuelle Ladeleistung).
<details>
<summary> <b>Custom Farben mit card_mod und Jinja2 Templates</b></summary>
Mit der [card_mod](https://github.com/thomasloven/lovelace-card-mod) Integration können die CSS-Variablen der Power Flux Card dynamisch per Jinja2-Templates überschrieben werden. So lassen sich Farben abhängig von Sensorwerten ändern — z.B. Solar-Icon grün bei Produktion, grau bei Stillstand.
### Verfügbare CSS-Variablen
| Variable | Beschreibung |
|---|---|
| `--neon-yellow` | Bubble-Farbe Solar |
| `--neon-blue` | Bubble-Farbe Grid |
| `--neon-green` | Bubble-Farbe Batterie |
| `--neon-pink` | Bubble-Farbe Haus |
| `--pipe-solar-color` | Pipe-Farbe Solar |
| `--pipe-grid-color` | Pipe-Farbe Grid |
| `--pipe-battery-color` | Pipe-Farbe Batterie |
| `--icon-solar-color` | Icon-Farbe Solar |
| `--icon-grid-color` | Icon-Farbe Grid |
| `--icon-battery-color` | Icon-Farbe Batterie |
| `--icon-house-color` | Icon-Farbe Haus |
| `--icon-consumer-1-color` | Icon-Farbe Consumer 1 |
| `--text-solar-color` | Text-Farbe Solar |
| `--text-grid-color` | Text-Farbe Grid |
| `--text-battery-color` | Text-Farbe Batterie |
| `--text-house-color` | Text-Farbe Haus |
| `--text-consumer-1-color` | Text-Farbe Consumer 1 |
| `--consumer-1-color` | Bubble-Farbe Consumer 1 |
| `--consumer-2-color` | Bubble-Farbe Consumer 2 |
| `--consumer-3-color` | Bubble-Farbe Consumer 3 |
| `--export-color` | Farbe für Export |
### Beispiel 1: Solar-Icon — grün bei Produktion, grau bei Stillstand
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% if states('sensor.solar_power') | float > 0 %}
--icon-solar-color: #00ff88;
{% else %}
--icon-solar-color: #9e9e9e;
{% endif %}
}
```
### Beispiel 2: Grid-Textfarbe — rot bei Export, blau bei Import
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid_combined: sensor.grid_power_combined
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% if states('sensor.grid_power_combined') | float < 0 %}
--text-grid-color: #ff3333;
{% else %}
--text-grid-color: #3b82f6;
{% endif %}
}
```
### Beispiel 3: Batterie-Bubble — Farbe nach Ladestand (SoC)
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
card_mod:
style: |
:host {
{% set soc = states('sensor.battery_soc') | float %}
{% if soc > 80 %}
--neon-green: #00ff88;
{% elif soc > 30 %}
--neon-green: #f59e0b;
{% else %}
--neon-green: #ff3333;
{% endif %}
}
```
### Beispiel 4: Consumer-1-Pipe — sichtbar nur bei hoher Leistung, sonst transparent
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
consumer_1: sensor.wallbox_power
card_mod:
style: |
:host {
{% if states('sensor.wallbox_power') | float > 500 %}
--pipe-consumer-1-color: #a855f7;
--icon-consumer-1-color: #a855f7;
{% else %}
--pipe-consumer-1-color: rgba(168, 85, 247, 0.2);
--icon-consumer-1-color: #9e9e9e;
{% endif %}
}
```
### Beispiel 5: Mehrere Farben gleichzeitig — Nachtmodus (alles gedimmt wenn Solar = 0)
```yaml
type: custom:power-flux-card
entities:
solar: sensor.solar_power
grid: sensor.grid_power
battery: sensor.battery_power
battery_soc: sensor.battery_soc
consumer_1: sensor.wallbox_power
card_mod:
style: |
:host {
{% if states('sensor.solar_power') | float == 0 %}
--icon-solar-color: #555555;
--text-solar-color: #777777;
--neon-yellow: #666633;
--pipe-solar-color: #444422;
{% endif %}
}
```
> **Hinweis:** card_mod muss separat über HACS installiert werden. Die Templates werden bei jedem State-Update ausgewertet, die Farben ändern sich also in Echtzeit.
</details>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -9,12 +9,47 @@ export default {
"editor.consumers_section": "Zusätzliche Verbraucher",
"editor.options_section": "Darstellung & Optionen",
"editor.flow_rate_title": "Flussraten (W) an Röhren anzeigen",
"editor.invert_battery": "Wert umkehren (+/-)",
"editor.label_toggle": "Label im Kreis anzeigen",
"editor.compact_view": "Kompakte Ansicht (evcc)",
"editor.hide_inactive": "Inaktive Röhren ausblenden",
"editor.entity": "Entität (Watt)",
"editor.entity": "Kombinierter Batterie Sensor (W)",
"editor.label": "Beschriftung",
"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 (more-details). Ansonsten wird der Hausverbrauch berechnet.",
"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.tinted_background": "Farbiger Hintergrund in Kreisen",
"editor.colored_values": "Farbige Textwerte",
"editor.hide_consumer_icons": "Icons unten ausblenden",
"editor.invert_consumer_1": "Sensorwert invertieren (+/-)",
"editor.secondary_sensor": "Zweiter Sensor (nur Anzeige)",
"editor.grid_to_battery_sensor": "Netz-zu-Batterie Sensor (W, Optional)",
"editor.grid_to_battery_hint": "Optional: separater Sensor für den Netz-zu-Batterie Fluss. Wenn leer, wird der Wert automatisch berechnet.",
"editor.grid_combined_sensor": "Kombinierter Netz-Sensor (W, Optional)",
"editor.grid_combined_hint": "Ein Sensor für Import UND Export: positiv = Import, negativ = Export. Überschreibt den kombinierten Import/Export Sensor.",
"editor.color_picker": "Bubble",
"editor.pipe_color": "Pipe",
"editor.export_color": "Export",
"editor.consumer_unit_kw": "Sensor meldet in kW",
"editor.show_consumer_always": "Verbraucher bei null Watt anzeigen",
"editor.battery_charge_sensor": "Batterie-Ladung Sensor (W, Optional)",
"editor.battery_discharge_sensor": "Batterie-Entladung Sensor (W, Optional)",
"editor.battery_separate_hint": "Optional: Separate Sensoren für Laden/Entladen. Überschreiben den Hauptsensor für die Berechnung.",
"editor.consumer_1_hide_pipe": "Pipe bei geringer Leistung ausblenden",
"editor.consumer_pipe_threshold": "Pipe-Schwellenwert (Watt)",
"editor.text_color": "Text",
"editor.icon_color": "Icon",
},
card: {
"card.label_solar": "Solar",
@ -22,7 +57,7 @@ export default {
"card.label_battery": "Batterie",
"card.label_house": "Verbrauch",
"card.label_car": "E-Auto",
"card.label_import": "Import",
"card.label_export": "Export",
"card.label_heater": "Heizung",
"card.label_pool": "Pool",
}
};

View file

@ -9,12 +9,47 @@ export default {
"editor.consumers_section": "Additional Consumers",
"editor.options_section": "Appearance & Options",
"editor.flow_rate_title": "Show Flow Rates (W) on pipes",
"editor.invert_battery": "Invert Power Value (+/-)",
"editor.label_toggle": "Show Label in Bubble",
"editor.compact_view": "Compact View (evcc)",
"editor.hide_inactive": "Hide Inactive Pipes",
"editor.entity": "Entity (Watt)",
"editor.entity": "Combined Battery Sensor (W)",
"editor.label": "Label",
"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 (more-details). Otherwise, the house consumption is calculated.",
"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.tinted_background": "Tinted Background in Bubbles",
"editor.colored_values": "Colored Text Values",
"editor.hide_consumer_icons": "Hide Consumer Icons",
"editor.invert_consumer_1": "Invert Sensor Value (+/-)",
"editor.secondary_sensor": "Secondary Sensor (display only)",
"editor.grid_to_battery_sensor": "Grid to Battery Sensor (W, optional)",
"editor.grid_to_battery_hint": "Optional: separate sensor for grid-to-battery flow. If empty, the value is calculated automatically.",
"editor.grid_combined_sensor": "Combined Grid Sensor (W, Optional)",
"editor.grid_combined_hint": "Single sensor for import AND export: positive = import, negative = export. Overrides combined import/export sensor.",
"editor.color_picker": "Bubble Color",
"editor.pipe_color": "Pipe Color",
"editor.export_color": "Export Color",
"editor.consumer_unit_kw": "Sensor reports in kW",
"editor.show_consumer_always": "Show Consumers at zero watts",
"editor.battery_charge_sensor": "Battery Charge Sensor (W, Optional)",
"editor.battery_discharge_sensor": "Battery Discharge Sensor (W, Optional)",
"editor.battery_separate_hint": "Optional: Separate sensors for charge/discharge. Override the main sensor for calculations.",
"editor.consumer_1_hide_pipe": "Hide pipe at low power",
"editor.consumer_pipe_threshold": "Pipe Threshold (Watts)",
"editor.text_color": "Text Color",
"editor.icon_color": "Icon Color",
},
card: {
"card.label_solar": "Solar",
@ -22,7 +57,7 @@ export default {
"card.label_battery": "Battery",
"card.label_house": "Consumption",
"card.label_car": "Car",
"card.label_import": "Import",
"card.label_export": "Export",
"card.label_heater": "Heater",
"card.label_pool": "Pool",
}
};

View file

@ -1,14 +1,23 @@
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) => {
options = options || {};
detail = detail === null || detail === undefined ? {} : detail;
const event = new Event(type, {
bubbles: options.bubbles === undefined ? true : options.bubbles,
cancelable: Boolean(options.cancelable),
composed: options.composed === undefined ? true : options.composed,
});
event.detail = detail;
node.dispatchEvent(event);
return event;
options = options || {};
detail = detail === null || detail === undefined ? {} : detail;
const event = new Event(type, {
bubbles: options.bubbles === undefined ? true : options.bubbles,
cancelable: Boolean(options.cancelable),
composed: options.composed === undefined ? true : options.composed,
});
event.detail = detail;
node.dispatchEvent(event);
return event;
};
const LitElement = customElements.get("ha-lit-element") || Object.getPrototypeOf(customElements.get("home-assistant-main"));
@ -16,83 +25,174 @@ const html = LitElement.prototype.html;
const css = LitElement.prototype.css;
class PowerFluxCardEditor extends LitElement {
static get properties() {
return {
hass: {},
_config: { state: true },
_subView: { state: true } // Controls which sub-page is open (null = main)
};
}
setConfig(config) {
this._config = config;
}
_localize(key) {
const lang = this.hass && this.hass.language ? this.hass.language : 'en';
const dict = editorTranslations[lang] || editorTranslations['en'];
return dict[key] || editorTranslations['en'][key] || key;
}
_valueChanged(ev) {
if (!this._config || !this.hass) return;
const target = ev.target;
const key = target.configValue || this._currentConfigValue;
let value;
if (target.tagName === 'HA-SWITCH') {
value = target.checked;
} else if (ev.detail && 'value' in ev.detail) {
value = ev.detail.value;
} else {
value = target.value;
}
if (value === null || value === undefined) {
value = "";
static get properties() {
return {
hass: {},
_config: { state: true },
_subView: { state: true } // Controls which sub-page is open (null = main)
};
}
if (key) {
const entityKeys = [
'solar', 'grid', 'grid_export',
'battery', 'battery_soc',
'consumer_1', 'consumer_2', 'consumer_3'
];
setConfig(config) {
this._config = config;
}
let newConfig = { ...this._config };
_localize(key) {
const lang = this.hass && this.hass.language ? this.hass.language : 'en';
const dict = editorTranslations[lang] || editorTranslations['en'];
return dict[key] || editorTranslations['en'][key] || key;
}
if (entityKeys.includes(key)) {
const currentEntities = newConfig.entities || {};
const newEntities = { ...currentEntities, [key]: value };
newConfig.entities = newEntities;
_valueChanged(ev) {
if (!this._config || !this.hass) return;
const target = ev.target;
const key = target.configValue;
let value;
if (target.tagName === 'HA-SWITCH') {
value = target.checked;
} else if (ev.detail && 'value' in ev.detail) {
value = ev.detail.value;
} else {
newConfig[key] = value;
if (key === 'show_comet_tail' && value === true) {
newConfig.show_dashed_line = false;
}
if (key === 'show_dashed_line' && value === true) {
newConfig.show_comet_tail = false;
}
value = target.value;
}
if (value === null || value === undefined) {
value = "";
}
if (key) {
const entityKeys = [
'solar', 'grid', 'grid_export', 'grid_combined',
'battery', 'battery_soc', 'grid_to_battery',
'battery_charge', 'battery_discharge',
'house',
'consumer_1', 'consumer_2', 'consumer_3',
'secondary_solar', 'secondary_grid', 'secondary_battery',
'secondary_consumer_1', 'secondary_consumer_2', 'secondary_consumer_3'
];
let newConfig = { ...this._config };
if (entityKeys.includes(key)) {
const currentEntities = newConfig.entities || {};
const newEntities = { ...currentEntities, [key]: value };
newConfig.entities = newEntities;
} else {
newConfig[key] = value;
if (key === 'show_comet_tail' && value === true) {
newConfig.show_dashed_line = false;
}
if (key === 'show_dashed_line' && value === true) {
newConfig.show_comet_tail = false;
}
}
this._config = newConfig;
fireEvent(this, "config-changed", { config: this._config });
}
}
_goSubView(view) {
this._subView = view;
}
_goBack() {
this._subView = null;
}
_clearEntity(key) {
const newConfig = { ...this._config };
const currentEntities = newConfig.entities || {};
const newEntities = { ...currentEntities, [key]: "" };
newConfig.entities = newEntities;
this._config = newConfig;
fireEvent(this, "config-changed", { config: this._config });
}
}
_goSubView(view) {
this._subView = view;
}
_colorChanged(key, ev) {
const newConfig = { ...this._config, [key]: ev.target.value };
this._config = newConfig;
fireEvent(this, "config-changed", { config: this._config });
}
_goBack() {
this._subView = null;
}
_resetColor(key) {
const newConfig = { ...this._config };
delete newConfig[key];
this._config = newConfig;
fireEvent(this, "config-changed", { config: this._config });
}
static get styles() {
return css`
_renderEntitySelector(entitySelectorSchema, value, configValue, label) {
const val = value || "";
return html`
<div class="entity-picker-wrapper">
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${val}
.configValue=${configValue}
.label=${label}
@value-changed=${this._valueChanged}
></ha-selector>
${val ? html`<ha-icon
class="clear-entity-btn"
icon="mdi:close-circle"
@click=${() => this._clearEntity(configValue)}
></ha-icon>` : ''}
</div>
`;
}
_renderColorPicker(key, label, defaultColor) {
const currentColor = this._config[key] || defaultColor;
const hasCustom = !!this._config[key];
return html`
<div class="color-picker-row">
<input type="color"
.value=${currentColor}
@input=${(e) => this._colorChanged(key, e)}>
<span class="color-label">${label}</span>
${hasCustom ? html`<ha-icon class="color-reset-btn"
icon="mdi:refresh"
@click=${() => this._resetColor(key)}></ha-icon>` : ''}
</div>
`;
}
_renderColorPickerQuad(bubbleKey, pipeKey, textKey, iconKey, defaultColor) {
const items = [
{ key: bubbleKey, label: this._localize('editor.color_picker'), default: defaultColor },
];
if (pipeKey) items.push({ key: pipeKey, label: this._localize('editor.pipe_color'), default: defaultColor });
items.push({ key: textKey, label: this._localize('editor.text_color'), default: defaultColor });
items.push({ key: iconKey, label: this._localize('editor.icon_color'), default: defaultColor });
return html`
<div class="color-picker-quad">
${items.map(item => {
const color = this._config[item.key] || item.default;
const hasCustom = !!this._config[item.key];
return html`
<div class="color-picker-row">
<input type="color"
.value=${color}
@input=${(e) => this._colorChanged(item.key, e)}>
<span class="color-label">${item.label}</span>
${hasCustom ? html`<ha-icon class="color-reset-btn"
icon="mdi:refresh"
@click=${() => this._resetColor(item.key)}></ha-icon>` : ''}
</div>
`;
})}
</div>
`;
}
static get styles() {
return css`
.card-config {
display: flex;
flex-direction: column;
@ -169,28 +269,82 @@ class PowerFluxCardEditor extends LitElement {
border-bottom: 1px solid var(--divider-color);
margin: 10px 0;
}
.entity-picker-wrapper {
position: relative;
display: flex;
align-items: center;
gap: 4px;
}
.entity-picker-wrapper ha-selector {
flex: 1;
}
.clear-entity-btn {
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
cursor: pointer;
flex-shrink: 0;
margin-top: -12px;
}
.clear-entity-btn:hover {
color: var(--error-color, #db4437);
}
.color-picker-row {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 0;
}
.color-picker-row input[type="color"] {
-webkit-appearance: none;
border: 2px solid var(--divider-color);
border-radius: 50%;
width: 30px;
height: 30px;
padding: 2px;
cursor: pointer;
background: transparent;
}
.color-picker-row input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
.color-picker-row input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 50%;
}
.color-label {
flex: 1;
font-size: 14px;
}
.color-reset-btn {
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
cursor: pointer;
}
.color-reset-btn:hover {
color: var(--primary-color);
}
.color-picker-quad {
display: flex;
gap: 8px;
}
.color-picker-quad .color-picker-row {
flex: 1;
}
`;
}
}
// --- SUBVIEW RENDERING ---
// --- SUBVIEW RENDERING ---
_renderSolarView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
_renderSolarView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
<div class="header">
<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>
<h2>${this._localize('editor.solar_section')}</h2>
</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.solar}
.configValue=${'solar'}
.label=${this._localize('editor.entity')}
@value-changed=${this._valueChanged}
></ha-selector>
${this._renderEntitySelector(entitySelectorSchema, entities.solar, 'solar', this._localize('editor.entity'))}
<div class="separator"></div>
@ -212,11 +366,15 @@ class PowerFluxCardEditor extends LitElement {
@value-changed=${this._valueChanged}
></ha-selector>
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_solar || "", 'secondary_solar', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_solar', 'color_pipe_solar', 'color_text_solar', 'color_icon_solar', '#ffdd00')}
<div class="separator"></div>
<div class="switch-row">
<ha-switch
.checked=${this._config.show_label_solar !== false}
.checked=${this._config.show_label_solar === true}
.configValue=${'show_label_solar'}
@change=${this._valueChanged}
></ha-switch>
@ -232,34 +390,29 @@ class PowerFluxCardEditor extends LitElement {
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
</div>
`;
}
}
_renderGridView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
_renderGridView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
<div class="header">
<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>
<h2>${this._localize('editor.grid_section')}</h2>
</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.grid}
.configValue=${'grid'}
.label=${this._localize('card.label_import') + " (W)"}
@value-changed=${this._valueChanged}
></ha-selector>
${this._renderEntitySelector(entitySelectorSchema, entities.grid_combined || "", 'grid_combined', this._localize('editor.grid_combined_sensor'))}
<div class="separator"></div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.grid_export}
.configValue=${'grid_export'}
.label=${this._localize('card.label_export') + " (W, Optional)"}
@value-changed=${this._valueChanged}
></ha-selector>
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
${this._localize('editor.grid_combined_hint')}
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.grid, 'grid', this._localize('card.label_import') + " (W)")}
${this._renderEntitySelector(entitySelectorSchema, entities.grid_export, 'grid_export', this._localize('card.label_export') + " (W, Optional)")}
<div class="separator"></div>
@ -281,11 +434,17 @@ class PowerFluxCardEditor extends LitElement {
@value-changed=${this._valueChanged}
></ha-selector>
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_grid || "", 'secondary_grid', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_grid', 'color_pipe_grid', 'color_text_grid', 'color_icon_grid', '#3b82f6')}
${this._renderColorPicker('color_export', this._localize('editor.export_color'), '#ff3333')}
<div class="separator"></div>
<div class="switch-row">
<ha-switch
.checked=${this._config.show_label_grid !== false}
.checked=${this._config.show_label_grid === true}
.configValue=${'show_label_grid'}
@change=${this._valueChanged}
></ha-switch>
@ -301,35 +460,27 @@ class PowerFluxCardEditor extends LitElement {
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
</div>
`;
}
}
_renderBatteryView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
_renderBatteryView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
<div class="header">
<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>
<h2>${this._localize('editor.battery_section')}</h2>
</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.battery}
.configValue=${'battery'}
.label=${this._localize('editor.entity')}
@value-changed=${this._valueChanged}
></ha-selector>
${this._renderEntitySelector(entitySelectorSchema, entities.battery, 'battery', this._localize('editor.entity'))}
<div class="separator"></div>
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
${this._localize('editor.battery_separate_hint')}
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.battery_charge || "", 'battery_charge', this._localize('editor.battery_charge_sensor'))}
${this._renderEntitySelector(entitySelectorSchema, entities.battery_discharge || "", 'battery_discharge', this._localize('editor.battery_discharge_sensor'))}
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.battery_soc}
.configValue=${'battery_soc'}
.label=${"Ladestand (%)"}
@value-changed=${this._valueChanged}
></ha-selector>
<div class="separator"></div>
<ha-selector
@ -349,12 +500,27 @@ class PowerFluxCardEditor extends LitElement {
.label=${this._localize('editor.icon') + " (Optional)"}
@value-changed=${this._valueChanged}
></ha-selector>
<div class="separator"></div>
${this._renderEntitySelector(entitySelectorSchema, entities.battery_soc, 'battery_soc', this._localize('editor.battery_soc_label'))}
<div class="separator"></div>
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
${this._localize('editor.grid_to_battery_hint')}
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.grid_to_battery || "", 'grid_to_battery', this._localize('editor.grid_to_battery_sensor'))}
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_battery || "", 'secondary_battery', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_battery', 'color_pipe_battery', 'color_text_battery', 'color_icon_battery', '#00ff88')}
<div class="separator"></div>
<div class="switch-row">
<ha-switch
.checked=${this._config.show_label_battery !== false}
.checked=${this._config.show_label_battery === true}
.configValue=${'show_label_battery'}
@change=${this._valueChanged}
></ha-switch>
@ -369,28 +535,40 @@ class PowerFluxCardEditor extends LitElement {
></ha-switch>
<div class="switch-label">${this._localize('editor.flow_rate_title')}</div>
</div>
`;
}
_renderConsumersView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
<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>
`;
}
_renderConsumersView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema) {
return html`
<div class="header">
<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>
<h2>${this._localize('editor.consumers_section')}</h2>
</div>
<div class="consumer-group">
<div class="consumer-title" style="color: #a855f7;">🚗 Links (Lila)</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.consumer_1}
.configValue=${'consumer_1'}
.label=${this._localize('editor.entity')}
@value-changed=${this._valueChanged}
></ha-selector>
<div class="consumer-title">${this._localize('editor.house_total_title')}</div>
${this._renderEntitySelector(entitySelectorSchema, entities.house || "", 'house', this._localize('editor.house_sensor_label'))}
<div style="font-size: 0.8em; color: var(--secondary-text-color); margin-top: 4px;">
${this._localize('editor.house_sensor_hint')}
</div>
${this._renderColorPickerQuad('color_house', null, 'color_text_house', 'color_icon_house', '#ff0080')}
</div>
<div class="consumer-group">
<div class="consumer-title" style="color: #a855f7;">${this._localize('editor.consumer_1_title')}</div>
${this._renderEntitySelector(entitySelectorSchema, entities.consumer_1, 'consumer_1', this._localize('editor.entity'))}
<ha-selector
.hass=${this.hass}
@ -409,18 +587,53 @@ class PowerFluxCardEditor extends LitElement {
.label=${this._localize('editor.icon')}
@value-changed=${this._valueChanged}
></ha-selector>
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px;">
<span>${this._localize('editor.invert_consumer_1')}</span>
<ha-switch
.checked=${this._config.invert_consumer_1 === true}
.configValue=${'invert_consumer_1'}
@change=${this._valueChanged}
></ha-switch>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px;">
<span>${this._localize('editor.consumer_1_hide_pipe')}</span>
<ha-switch
.checked=${this._config.consumer_1_hide_pipe === true}
.configValue=${'consumer_1_hide_pipe'}
@change=${this._valueChanged}
></ha-switch>
</div>
${this._config.consumer_1_hide_pipe === true ? html`
<ha-selector
.hass=${this.hass}
.selector=${{ number: { min: 0, max: 2000, step: 10, mode: "slider" } }}
.value=${this._config.consumer_1_pipe_threshold !== undefined ? this._config.consumer_1_pipe_threshold : 0}
.configValue=${'consumer_1_pipe_threshold'}
.label=${this._localize('editor.consumer_pipe_threshold')}
@value-changed=${this._valueChanged}
></ha-selector>
` : ''}
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px; margin-bottom: 8px;">
<span>${this._localize('editor.consumer_unit_kw')}</span>
<ha-switch
.checked=${this._config.consumer_1_unit_kw === true}
.configValue=${'consumer_1_unit_kw'}
@change=${this._valueChanged}
></ha-switch>
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_consumer_1 || "", 'secondary_consumer_1', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_consumer_1', 'color_pipe_consumer_1', 'color_text_consumer_1', 'color_icon_consumer_1', '#a855f7')}
</div>
<div class="consumer-group">
<div class="consumer-title" style="color: #f97316;"> Mitte (Orange)</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.consumer_2}
.configValue=${'consumer_2'}
.label=${this._localize('editor.entity')}
@value-changed=${this._valueChanged}
></ha-selector>
<div class="consumer-title" style="color: #f97316;">${this._localize('editor.consumer_2_title')}</div>
${this._renderEntitySelector(entitySelectorSchema, entities.consumer_2, 'consumer_2', this._localize('editor.entity'))}
<ha-selector
.hass=${this.hass}
@ -439,18 +652,24 @@ class PowerFluxCardEditor extends LitElement {
.label=${this._localize('editor.icon')}
@value-changed=${this._valueChanged}
></ha-selector>
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px; margin-bottom: 8px;">
<span>${this._localize('editor.consumer_unit_kw')}</span>
<ha-switch
.checked=${this._config.consumer_2_unit_kw === true}
.configValue=${'consumer_2_unit_kw'}
@change=${this._valueChanged}
></ha-switch>
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_consumer_2 || "", 'secondary_consumer_2', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_consumer_2', 'color_pipe_consumer_2', 'color_text_consumer_2', 'color_icon_consumer_2', '#f97316')}
</div>
<div class="consumer-group">
<div class="consumer-title" style="color: #06b6d4;">🏊 Rechts (Türkis)</div>
<ha-selector
.hass=${this.hass}
.selector=${entitySelectorSchema}
.value=${entities.consumer_3}
.configValue=${'consumer_3'}
.label=${this._localize('editor.entity')}
@value-changed=${this._valueChanged}
></ha-selector>
<div class="consumer-title" style="color: #06b6d4;">${this._localize('editor.consumer_3_title')}</div>
${this._renderEntitySelector(entitySelectorSchema, entities.consumer_3, 'consumer_3', this._localize('editor.entity'))}
<ha-selector
.hass=${this.hass}
@ -469,30 +688,43 @@ class PowerFluxCardEditor extends LitElement {
.label=${this._localize('editor.icon')}
@value-changed=${this._valueChanged}
></ha-selector>
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px; margin-bottom: 8px;">
<span>${this._localize('editor.consumer_unit_kw')}</span>
<ha-switch
.checked=${this._config.consumer_3_unit_kw === true}
.configValue=${'consumer_3_unit_kw'}
@change=${this._valueChanged}
></ha-switch>
</div>
${this._renderEntitySelector(entitySelectorSchema, entities.secondary_consumer_3 || "", 'secondary_consumer_3', this._localize('editor.secondary_sensor'))}
${this._renderColorPickerQuad('color_consumer_3', 'color_pipe_consumer_3', 'color_text_consumer_3', 'color_icon_consumer_3', '#06b6d4')}
</div>
`;
}
render() {
if (!this.hass || !this._config) {
return html``;
}
const entities = this._config.entities || {};
render() {
if (!this.hass || !this._config) {
return html``;
}
const entitySelectorSchema = { entity: { domain: ["sensor", "input_number"] } };
const textSelectorSchema = { text: {} };
const iconSelectorSchema = { icon: {} };
const entities = this._config.entities || {};
// SUBVIEW ROUTING
if (this._subView === 'solar') return this._renderSolarView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'grid') return this._renderGridView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'battery') return this._renderBatteryView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'consumers') return this._renderConsumersView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
const entitySelectorSchema = { entity: { domain: ["sensor", "input_number"] } };
const textSelectorSchema = { text: {} };
const iconSelectorSchema = { icon: {} };
// SUBVIEW ROUTING
if (this._subView === 'solar') return this._renderSolarView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'grid') return this._renderGridView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'battery') return this._renderBatteryView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
if (this._subView === 'consumers') return this._renderConsumersView(entities, entitySelectorSchema, textSelectorSchema, iconSelectorSchema);
// MAIN MENU VIEW
return html`
// MAIN MENU VIEW
return html`
<div class="card-config">
<div class="section-title">${this._localize('editor.main_title')}</div>
@ -525,7 +757,7 @@ class PowerFluxCardEditor extends LitElement {
.selector=${{ number: { min: 0.5, max: 1.5, step: 0.05, mode: "slider" } }}
.value=${this._config.zoom !== undefined ? this._config.zoom : 0.9}
.configValue=${'zoom'}
.label=${"🔍 Zoom (Standard View)"}
.label=${this._localize('editor.zoom_label')}
@value-changed=${this._valueChanged}
></ha-selector>
</div>
@ -536,7 +768,7 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'show_neon_glow'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Neon Glow</div>
<div class="switch-label">${this._localize('editor.neon_glow')}</div>
</div>
<div class="switch-row">
@ -545,7 +777,7 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'show_donut_border'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Donut Chart (Grid/Haus)</div>
<div class="switch-label">${this._localize('editor.donut_chart')}</div>
</div>
<div class="switch-row">
@ -554,7 +786,7 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'show_comet_tail'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Comet Tail Effect</div>
<div class="switch-label">${this._localize('editor.comet_tail')}</div>
</div>
<div class="switch-row">
@ -563,7 +795,16 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'show_dashed_line'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Dashed Line Animation</div>
<div class="switch-label">${this._localize('editor.dashed_line')}</div>
</div>
<div class="switch-row">
<ha-switch
.checked=${this._config.show_tinted_background === true}
.configValue=${'show_tinted_background'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">${this._localize('editor.tinted_background')}</div>
</div>
<div class="switch-row">
@ -572,7 +813,7 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'use_colored_values'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Farbige Textwerte</div>
<div class="switch-label">${this._localize('editor.colored_values')}</div>
</div>
<div class="switch-row">
@ -581,7 +822,7 @@ class PowerFluxCardEditor extends LitElement {
.configValue=${'hide_consumer_icons'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">Icons unten ausblenden</div>
<div class="switch-label">${this._localize('editor.hide_consumer_icons')}</div>
</div>
<div class="switch-row">
@ -593,6 +834,15 @@ class PowerFluxCardEditor extends LitElement {
<div class="switch-label">${this._localize('editor.hide_inactive')}</div>
</div>
<div class="switch-row">
<ha-switch
.checked=${this._config.show_consumer_always === true}
.configValue=${'show_consumer_always'}
@change=${this._valueChanged}
></ha-switch>
<div class="switch-label">${this._localize('editor.show_consumer_always')}</div>
</div>
<div class="switch-row">
<ha-switch
.checked=${this._config.compact_view === true}
@ -604,7 +854,7 @@ class PowerFluxCardEditor extends LitElement {
</div>
`;
}
}
}
customElements.define("power-flux-card-editor", PowerFluxCardEditor);

File diff suppressed because it is too large Load diff