OpenHAB Animation zur Sun2000
Für die schnelle Übersicht für die Lage auf dem heimischen Stromsektor habe ich mir ein kleines Widget mit Animation und Popups erstellt. Als Grundlage für die Entwicklung diente mir
- das Deye Inverter Widget von Peter Kretz
- und die Tipps von JustinG in den Community-Beiträgen Popup for Shutter-Widget und f7-icon with custom icons.
Das Widget
Hier nur als Screenshot im OpenHAB sind die Kreise animiert. Die Geschwindigkeit dieser Animation hängt von der jeweiligen Leistung ab.
Das Bild stellt die wesentlichen Stellen im Netz als bunte Rechtecke dar:
- PV-Module
- Speicher
- Wechselrichter
- Stromnetz
- Haus als Verbraucher
Bei den Rechtecken ist jeweils die Leistung angegeben, die die jeweilige Komponente bezieht oder liefert. Die Richtung ist zusätzlich durch einen farbigen Pfeil gekennzeichnet:
- Grün: Strom wird abgegeben
- Rot: Strom wird bezogen
Die Icons und Texte in den Rechtecken dienen als Link, beim Anklicken öffnet sich ein Popup mit weiteren Werten:
Die Kästen sind jeweils durch eine Linie mit dem Wechselrichter verbunden. Fließt auf dieser Strecke ein Strom, so besteht die Animation darin, dass eine kleine Kugel sich in der Stromrichtung bewegt. Die Geschwindigkeit der Animation ist von der jeweiligen Stromstärke abhängig.
Das Listing zum Widget
uid: Sun2000_Widget
tags:
- Für Huawei Sun2000 Wechselrichter gem. https://www.debacher.de/wiki/Huawei_Sun2000_mit_OpenHAB
- Version 2.0
props:
parameters:
- description: Widget Titel
label: Widget Title
name: title
required: false
type: TEXT
- context: item
description: String1 Spannung (32016)
label: PV1 Voltage
name: item32016
required: false
type: TEXT
- context: item
description: String1 Strom (32017)
label: PV1 current
name: item32017
required: false
type: TEXT
- context: item
description: String2 Spannung (32018)
label: PV2 Voltage
name: item32018
required: false
type: TEXT
- context: item
description: String2 Strom (32019)
label: PV2 current
name: item32019
required: false
type: TEXT
- context: item
description: Solar Ertrag (32064)
label: Solar Production
name: item32064
required: false
type: TEXT
- context: item
description: Maximaler Ertrag (32078)
label: Peak active today
name: item32078
required: false
type: TEXT
- context: item
description: Leistung der Module (32080)
label: Active Power
name: item32080
required: false
type: TEXT
- context: item
description: Netz-Frequenz (32085)
label: Grid frequency
name: item32085
required: false
type: TEXT
- context: item
description: Inverter Temperatur (32087)
label: Inverter Temperature
name: item32087
required: false
type: TEXT
- context: item
description: Erzeugte Energie (32106)
label: Acc. energy yield
name: item32106
required: false
type: TEXT
- context: item
description: Heutiger Solar Ertrag (32114)
label: Solar Daily Production
name: item32114
required: false
type: TEXT
- context: item
description: Batterie Temperatur (37022)
label: Battery Temperature
name: item37022
required: false
type: TEXT
- context: item
description: Einspeisung/Bezug vom Netz (37113)
label: Active power
name: item37113
required: false
type: TEXT
- context: item
description: Gesamt-Einspeisung Netz (37119)
label: Positive active electricity
name: item37119
required: false
type: TEXT
- context: item
description: Gesamt-Bezug Netz (37121)
label: Reverse active power
name: item37121
required: false
type: TEXT
- context: item
description: Batterie Ladung (37760)
label: Battery SOC
name: item37760
required: false
type: TEXT
- context: item
description: Verbrauch/Einspeisung Batterie (37765)
label: Charge/ Discharge power
name: item37765
required: false
type: TEXT
- context: item
description: Heutige Einspeisung Batterie (37784)
label: Daily Battery Charge
name: item37784
required: false
type: TEXT
- context: item
description: Heutiger Verbrauch von Batterie (37786)
label: Daily Battery Consumption
name: item37786
required: false
type: TEXT
- context: item
description: Heutiger Netz-Bezug
label: Daily Consumption From Grid
name: itemDailyConsumptionFromGrid
required: false
type: TEXT
- context: item
description: Heutige Netz-Einspeisung
label: Daily Production To Grid
name: itemDailyProductionToGrid
required: false
type: TEXT
parameterGroups: []
timestamp: Mar 20, 2024, 8:47:11 PM
component: f7-card
config:
title: = props.title
slots:
content:
- component: f7-block
config:
style:
margin: 0
padding: 0
slots:
default:
- component: f7-block
config:
style:
--f7-block-font-size: 12px
--f7-theme-color: var(--f7-text-color)
display: flex
justify-content: center
margin: 0
padding: 0
slots:
default:
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: f7-block
config:
style:
align-items: center
border: 3px solid orange
border-radius: 20px
display: flex
flex-direction: column
height: 100px
justify-content: center
width: 100px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-solar"
slots:
default:
- component: oh-icon
config:
color: orange
height: 50px
icon: if:game-icons:solar-power
- component: oh-link
config:
action: popover
iconColor: green
iconF7: arrow_right
iconSize: 12px
popoverOpen: =".popover.info-solar"
style:
font-size: 14px
white-space: nowrap
text: =items[props.item32064].state+" kW"
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 70px
justify-content: center
width: 100px
- component: f7-block
config:
style:
align-items: center
border: 3px solid teal
border-radius: 20px
display: flex
flex-direction: column
height: 100px
justify-content: center
width: 100px
slots:
default:
- component: Label
config:
style:
white-space: nowrap
text: =items[props.item37760].displayState+" %"
- component: oh-link
config:
action: popover
align-item: center
popoverOpen: =".popover.info-batt"
slots:
default:
- component: oh-icon
config:
height: 40px
icon: battery
state: =items[props.item37760].displayState
- component: oh-link
config:
action: popover
iconColor: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'red' : 'green'"
iconF7: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'arrow_right' : 'arrow_left'"
iconSize: 12px
popoverOpen: =".popover.info-batt"
style:
font-size: 14px
white-space: nowrap
text: = items[props.item37765].state+" kW"
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: svg
config:
style:
width: 20px
height: 270px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M0,65 h2 q8,0 8,35 t8,35 h2
fill: none
id: solarpath
stroke: orange
stroke-width: 2
- component: circle
config:
fill: orange
r: 4
style:
stroke-width: 4
visible: "=(Math.abs(Number.parseFloat(items[props.item32064].state)) < 0.01) ? false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Number.parseFloat(items[props.item32064].state) > 8) ? '1s' :
(Number.parseFloat(items[props.item32064].state) > 4) ? '2s' :
(Number.parseFloat(items[props.item32064].state) > 2) ? '3s' :
(Number.parseFloat(items[props.item32064].state) > 1) ? '4s' :
(Number.parseFloat(items[props.item32064].state) > 0.5) ? '6s' :
(Number.parseFloat(items[props.item32064].state) > 0.1) ? '8s' :'16s'
keyPoints: "=(Number.parseFloat(items[props.item32064].state) < 0) ? '1;0' : '0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#solarpath"
- component: path
config:
d: M0,230 h2 q8,0 8,-35 t8,-35 h2
fill: none
id: batterypath
stroke: teal
stroke-width: 2
- component: circle
config:
fill: teal
r: 4
style:
stroke-width: 4
visible: "=(Number.parseFloat(items[props.item37765].state) == 0) ? false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Math.abs(Number.parseFloat(items[props.item37765].state)) > 8) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 4) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 2) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 1) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.5) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.1) ? '8s' :'16s'
keyPoints: "=(Number.parseFloat(items[props.item37765].state) > 0) ? '1;0' : '0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#batterypath"
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 82px
justify-content: center
width: 100px
- component: f7-block
config:
style:
align-items: center
border: 2px solid green
border-radius: 20px
display: flex
flex-direction: column
height: 100px
justify-content: center
width: 100px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-inverter"
slots:
default:
- component: oh-icon
config:
color: lightgreen
height: 50px
icon: if:cbi:huawei-solar-inverter
- component: oh-link
config:
action: popover
iconColor: green
iconF7: arrow_right
iconSize: 12px
popoverOpen: =".popover.info-inverter"
style:
font-size: 14px
white-space: nowrap
text: =items[props.item32080].state+" kW"
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: svg
config:
preserveAspectRatio: xMidYMid slice
style:
width: 20px
height: 270px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M20,65 h-2 q-8,0 -8,35 t-8,35 h-2
fill: none
id: gridpath
stroke: darkred
stroke-width: 2
- component: circle
config:
fill: darkred
r: 4
style:
stroke-width: 4
tag: circle
visible: "=(Math.abs(Number.parseFloat(items[props.item37113].state)) < 0.01) ? false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Math.abs(Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
keyPoints: "=(Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#gridpath"
- component: path
config:
d: M20,230 h-2 q-8,0 -8,-35 t-8,-35 h-2
fill: none
id: loadpath
stroke: blue
stroke-width: 2
- component: circle
config:
fill: blue
r: 4
style:
stroke-width: 4
tag: circle
visible: "=(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) <= 0.01) ? false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
keyPoints: "=(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#loadpath"
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: f7-block
config:
style:
align-items: center
border: 3px solid darkred
border-radius: 20px
display: flex
flex-direction: column
height: 100px
justify-content: center
width: 100px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-grid"
slots:
default:
- component: oh-icon
config:
color: darkred
height: 50px
icon: if:mdi:transmission-tower
- component: oh-link
config:
action: popover
iconColor: "=(Number.parseFloat(items[props.item37113].state) < 0) ? 'red' : 'green'"
iconF7: "=(Number.parseFloat(items[props.item37113].state) > 0) ? 'arrow_right' : 'arrow_left'"
iconSize: 12px
popoverOpen: =".popover.info-grid"
style:
font-size: 14px
white-space: nowrap
text: = Number.parseFloat(items[props.item37113].state).toFixed(3)+" kW"
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 70px
justify-content: center
width: 100px
- component: f7-block
config:
style:
align-items: center
border: 3px solid blue
border-radius: 20px
display: flex
flex-direction: column
height: 100px
justify-content: center
width: 100px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-home"
slots:
default:
- component: oh-icon
config:
color: blue
height: 50px
icon: if:healthicons:home-outline
- component: oh-link
config:
action: popover
iconColor: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'red' : 'green'"
iconF7: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'arrow_right' : 'arrow_left'"
iconSize: 12px
popoverOpen: =".popover.info-home"
style:
font-size: 14px
white-space: nowrap
text: >
=(Number.parseFloat(items[props.item32080].state)
- Number.parseFloat(items[props.item37113].state)).toFixed(3) + ' kW'
- component: f7-popover
config:
class: ="info-solar"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
style:
background-color: snow2
color: rgb(238,118,0)
height: auto
width: 250px
slots:
default:
- component: oh-list
config:
simpleList: false
slots:
default:
- component: oh-list-item
config:
after: >
=(Number.parseFloat(items[props.item32114].state)
+ Number.parseFloat(items[props.item37784].state)
- Number.parseFloat(items[props.item37786].state)).toFixed(3) + ' kWh'
title: Ertrag Module
- component: oh-list-item
config:
after: =items[props.item32078].displayState
title: Maximal Leistung
- component: oh-list-item
config:
after: =(Number.parseFloat(items[props.item32016].state*items[props.item32017].state)/1000).toFixed(3) + ' kW'
title: Leistung String 1
- component: oh-list-item
config:
after: =(Number.parseFloat(items[props.item32018].state*items[props.item32019].state)/1000).toFixed(3) + ' kW'
title: Leistung String 2
- component: f7-popover
config:
class: ="info-batt"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
style:
background-color: snow2
color: teal
height: auto
width: 250px
slots:
default:
- component: oh-list
config:
simpleList: false
slots:
default:
- component: oh-label-item
config:
item: =props.item37784
title: Geladen
- component: oh-label-item
config:
item: =props.item37786
title: Entladen
- component: oh-label-item
config:
item: =props.item37022
title: Temp. Batterie
- component: f7-popover
config:
class: ="info-inverter"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
style:
background-color: snow2
color: green
height: auto
width: 250px
slots:
default:
- component: oh-list
config:
simpleList: false
slots:
default:
- component: oh-list-item
config:
after: =(Number.parseFloat(items[props.item32114].state)).toFixed(3) + ' kWh'
title: Ertrag Inverter
- component: oh-list-item
config:
after: =(Number.parseFloat(items[props.item32087].state)).toFixed(1) + ' °C'
title: Temperatur
- component: oh-label-item
config:
item: =props.item32085
title: Frequenz
- component: f7-popover
config:
class: ="info-grid"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
style:
background-color: snow2
color: darkred
height: auto
width: 250px
slots:
default:
- component: oh-list
config:
simpleList: false
slots:
default:
- component: oh-label-item
config:
item: =props.itemDailyProductionToGrid
title: Netzeinspeisung
- component: oh-label-item
config:
item: =props.itemDailyConsumptionFromGrid
title: Netzbezug
- component: f7-popover
config:
class: ="info-home"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
style:
background-color: snow2
color: blue
height: auto
width: 250px
slots:
default:
- component: oh-list
config:
simpleList: false
slots:
default:
- component: oh-list-item
config:
after: >
= (Number.parseFloat(items[props.item32114].state)
-Number.parseFloat(items[props.itemDailyProductionToGrid].state)
+Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state) ).toFixed(2)+ ' kWh'
title: Haus gesamt
- component: oh-list-item
config:
after: >
= (Number.parseFloat(items[props.item32114].state)
-Number.parseFloat(items[props.itemDailyProductionToGrid].state) ).toFixed(2)+ ' kWh'
title: Haus von PV
- component: oh-list-item
config:
after: >
=(100-(Number.parseFloat(items[props.itemDailyProductionToGrid].state)
/ (Number.parseFloat(items[props.item32114].state)
- Number.parseFloat(items[props.item37786].state)
+ Number.parseFloat(items[props.item37784].state)) )*100).toFixed(2) + ' %'
title: Eigenverbrauch heute
- component: oh-list-item
config:
after: >
= (100*(Number.parseFloat(items[props.item32114].state)
-Number.parseFloat(items[props.itemDailyProductionToGrid].state))
/(Number.parseFloat(items[props.item32114].state)
-Number.parseFloat(items[props.itemDailyProductionToGrid].state)
+Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state))).toFixed(2)+ ' %'
title: Autarkie
- component: oh-list-item
config:
after: >
=((Number.parseFloat(items[props.item32106].state)
- Number.parseFloat(items[props.item37119].state))
/ Number.parseFloat(items[props.item32106].state)*100).toFixed(1) + ' %'
title: Eigenverbrauch global
- component: oh-list-item
config:
after: >
=100-((Number.parseFloat(items[props.item37121].state))
/ (Number.parseFloat(items[props.item32106].state)
+ Number.parseFloat(items[props.item37121].state)
- Number.parseFloat(items[props.item37119].state) )*100).toFixed(1) + ' %'
title: Autarkie global
Das Listing besteht grob aus drei Abschnitten:
- Zeile 1 bis 139: Interfacedefinition, hier werden die Elemente festgelegt, die in dem Widget benutzt werden
- Zeile 140 bis 560: Der Aufbau der Grafik mit den Kästen und der Animation
- Ab Zeile 561: Definition der fünf Popups, für jeden Kasten eines
Interface
Bei allen Daten-Elementen, die direkt vom Wechselrichter stammen habe ich darauf Wert gelegt, dass die Registernummer im ItemBezeichner auftaucht. Das macht die Zurodnung bei einer Sun2000 eindeutig und hilft bei der Auswahl im Konfigurationsdialog.
Am Ende der Interface-Liste tauchen noch auf:
- Heutiger Netz-Bezug
- Heutige Netz-Einspeisung
Diese Tageswerte werden vom Inverter leider nicht geliefert. Ich werde sie später über ein Homatic-Gerät direkt am Stromzähler ablesen. Aktuell sind es aber Variable, die ich über Rules befülle:
Die erste Regel wird um Mitternacht aufgerufen, setzt den Hausverbrauch auf "0" und den Startwert für Verbrauch und Einspeisung auf die Werte der über die Lebensdauer laufenden Daten.
Die Werte müssen dann jeweils, wenn sich das zugehörige Datenfeld der Sun2000 ändert, aktualisiert werden:
Grafik und Animation
Die eigentliche Grafik besteht dann aus 5 Spalten.
- Die Spalten 1, 3 und 5 beinhalten Blöcke mit Daten und Grafik
- Die Spalten 2 und 4 beinhalten Pfade und Animationen
Bei der Realisierung werden einige f7 Elemente benutzt.
Die Animationen sind im Prinzip SVG-Elemente, die von OpenHAB und f7 entsprechend umgesetzt werden.
Informationen zu den Grundlagen finden sich unter:
- https://www.w3.org/TR/svg-paths/#PathDataMovetoCommands
- https://www.w3schools.com/graphics/svg_path.asp
- https://www.w3schools.com/graphics/tryit.asp?filename=trysvg_path2
Ein wichtiges Element ist jeweils die Linie, der Pfad, hier der Solarpfad:
d: M0,65 h2 q8,0 8,35 t8,35 h2
Konkret wird hier definiert (das Komma steht immer zwischen den Komponenten eines Punktes)
- MoveTo (0,65)
- Linie horizontel 2 Punkte
- Quadratische Bézier-Kurve zum Punkt (8,35) mit dem Kontrollpunkt (8,0)
- Quadratische Bézier-Kurve wie vor, aber am Punkt (8,35) gespiegelt
- Linie horizontal 2 Punkte
Bei kleinen Buchstaben sind die Angaben relativ, bei großen Buchstaben absolut. Diese Art der Kurvenbeschreibung ist leichter nachvollziehbar und anpassbar, als die über kubische Kurven.
In Zeile 275 bekommt der Pfad dann noch eine id zugeordnet, hier solarpath
In den Zeilen darauf wird dann eine Animation definiert, die auf einem kleinen farbigen Kreis beruht und über
xlink:href: "#solarpath"
an den oben definierte Pfad gekoppelt wird.
Für die Animation wird noch festgelegt:
- dur: die Dauer in Sekunden, hier als komplexe Fallunterscheidung
- keyPoints: "=(Number.parseFloat(items[props.item32064].state) < 0) ? '1;0' : '0;1'" je nach Vorzeichen ist die Bewegungsrichtung unterschiedlich
- keyTimes: 0;1 eine gleichmäßige Bewegung von Anfang bis Ende
Popups
Mein Ziel war es, die eigentliche Grafik nicht mit Informationen zu überfrachten, aber trotzdem weitere Informationen zu ermöglichen. Dazu dienen die Popups. Wichtig war mit, dass für den Inhalt der Popups kein weiteres Widget benötigt wird, also diese Elemente im gleichen Widget untergebracht sind.
Das Element oh-link erlaubt Action Elemente
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-solar"
iconColor: green
iconF7: arrow_right
iconSize: 12px
Das Action-Element ist vom Typ popover, in Zeile 197 wird ihm eine Klasse zugewiesen. Über diese Klasse wird dann das richtige Element angesteuert
- component: f7-popover
config:
class: ="info-solar"
closeByBackdropClick: true
closeByOutsideClick: true
closeOnEscape: true
Wichtig ist, dass sich die Klassenbezeichnungen der Popover-Elemente unterscheiden.
Textzeilen vom Typ oh-link für ein Popup zu nutzen ist relativ direkt. Wesentlich aufwändiger war es auch eine Lösung für das große Icon zu finden. Ein Block vom Type oh-icon ermöglicht kein Popup und ein Elemente vom Type oh-link keine freie Icon-Wahl. Auf eine Anleitung von JustinG hin habe ich eine Lösung gefunden:
- component: oh-link
config:
action: popover
popoverOpen: =".popover.info-solar"
slots:
default:
- component: oh-icon
config:
icon: if:game-icons:solar-power
height: 50px
color: orange
Der oh-link besitzt einen slot, der ein oh-icon erlaubt. Damit kann man auch das große Icon anklickbar machen.
Bei den Listenelemente der Popups habe ich momentan zwei verschiedene Arten
- component: oh-label-item
config:
item: =props.item32078
title: Max. Leistung heute
Das ist die wohl einfachste Version, sie erlaubt aber keine Berechnungen an dem Item.
- component: oh-list-item
config:
title: Max. Leistung heute
after: =(Number.parseFloat(items[props.item32078].state)).toFixed(3) + ' kW'
Auf diese Art lassen sich auch mehrere Items miteinander verknüpfen und generell kann die Ausgabe manipuliert werden.
Der Solar-Ertrag
Mit dem Solar-Ertrag gibt es ein kleines Problem, sofern man auch über eine Batterie verfügt. Es können hier zwei unterschiedliche Leistungs Werte zum Einsatz kommen:
- Register 32064 (Input power - DC Leistung des WR)
- Register 32080 (Active power - AC Leistung der Module)
Die Leistungswerte unterschieden sich (+/- 2,5 kW), wenn die Batterie geladen oder entladen wird. Natürlich betrifft das im Endeffekt auch erzeugten Energiemengen. Zähle ich die Energiemenge von/zur Batterie mit oder nicht? In meinen Listings rechne ich mit dem Register 32114 (Daily energy yield) welches sich auf den Ausgang des Wechselrichters bezieht. Die Batterie wird quasi ignoriert.
Offene Punkte
Ein paar Punkte habe ich bisher nicht klären können, sondern nur umschifft.
- Wo finde ich eine Dokumentation dazu, welche Elemente popoverOpen unterstützen?
- Ein Problem tritt bei der Mischung auf, im ersten Fall wird ein Dezimalkomma gesetzt, zumindest bei entsprechender Einstellung der Lokale. Im zweiten Fall erzeugt Javascript die Ausgabe, hier wird dann ein Dezimalpunkt gesetzt. Wenn das stört, dann muss man entweder immer die oh-list-item Version nutzen oder über Javascript im oh-list-item generell den Punkt durch ein Komma ersetzen.