Einbindung in Home Assistant - z-master42/solarflow GitHub Wiki
Zendure betreibt für den Abruf von Informationen für die Produkte SuperBase V, Satellite Battery und SolarFlow einen MQTT-Broker. Dies stellt aktuell auch die einzige offizielle Möglichkeit dar, Informationen außerhalb der von Zendure bereitgestellten App abzugreifen. Da Zendure die Daten über einen eigenen Broker auspielt läuft die Verbindung zwangsweise durchs Internet. Eine rein lokale Steuerung ist aktuell und offiziell noch nicht möglich. MQTT ist ein offenes Netzwerkprotokoll für eine Maschine-zu-Maschine-Kommunikation. Dabei stehen in der Regel mehrere Clients in Verbindung zu einem Broker. Die ausgetauschten Nachrichten werden hierachisch abgestuft durch Topics definiert. Um entsprechende Informationen abzugreifen oder Befehle zu senden muss das entprechende Topic abonniert werden.
Voraussetzung zur Nutzung ist ein Account bei Zendure (den ja jeder dadurch haben sollte, dass er sich die App zur Steuerung des SolarFlow installiert hat).
Für den Abruf der MQTT-Daten des eigenen SolarFlow benötigt man im weiteren einen appKey
und ein appSecret
.
Um diese beiden Werte zu erhalten, benötigt ihr, neben der Emailadresse mit der ihr euch bei Zendure registriert habt, die Seriennummer eures PV-Hubs.
Ich habe zum Abruf das Kommandozeilen-Tool curl verwendet.
Vorgehen auf einem Microsoft Betriebssystem
-
Öffnet mit
Windows-Taste + R
die Eingabeaufforderung. -
Gebt
cmd
ein. -
Gebt folgenden Befehl in der Kommandozeile ein:
Regionseinstellung in der Zendure-App auf "Global"
curl -i -v --json "{'snNumber': 'EureHubSeriennummer', 'account': 'EureEmailadresse'}" https://app.zendure.tech/v2/developer/api/apply
Regionseinstellung in der Zendure-App auf einem "Europäischen Land"
curl -i -v --json "{'snNumber': 'EureHubSeriennummer', 'account': 'EureEmailadresse'}" https://app.zendure.tech/eu/developer/api/apply
-
Zuvor habt ihr natürlich eure Seriennummer und eure verwendete Emailadresse anstelle der Platzhalter eingetragen.
Vorgehen auf einem Linux Betriebssystem
-
Öffnet mit
Strg+Alt+T
ein Terminalfenster. -
Gebt folgenden Befehl in der Kommandozeile ein:
Regionseinstellung in der Zendure-App auf "Global"
curl -i -X POST -H 'Content-Type: application/json' -d '{"snNumber": "EureHubSeriennummer", "account": "EureEmailadresse"}' https://app.zendure.tech/v2/developer/api/apply
Regionseinstellung in der Zendure-App auf einem "Europäischen Land"
curl -i -X POST -H 'Content-Type: application/json' -d '{"snNumber": "EureHubSeriennummer", "account": "EureEmailadresse"}' https://app.zendure.tech/eu/developer/api/apply
-
Zuvor habt ihr natürlich eure Seriennummer und eure verwendete Emailadresse anstelle der Platzhalter eingetragen.
Antwort
Habt ihr keine Fehler gemacht, sollte eine Antwort wie folgt erscheinen:
{"code":200,"success":true,"data":{"appKey":"EuerAppKey","secret":"EuerAppSecret","mqttUrl":"mqtt.zen-iot.com","port":1883},"msg":"Successful operation"}
Anstelle der Platzhalter findet ihr dann euren appKey
und euer appSecret
. Beides sind Buchstaben-Zahlen-Kombinationen.
Je nachdem wie weit ihr euch in Home Assistant schon ausgetobt habt, gibt es nun mehrere Wege zum Ziel. Ich empfehle hier aber grundsätzlich die Variante mit einem eigenem MQTT-Broker und manuell angelegten Entitäten (unter 2.).
-
Option: Noch kein MQTT-Gerät mit Home Assistant verbunden
Wenn dies eure erste Berührung mit MQTT ist, geht die Sache recht schnell.
-
Fügt über Einstellungen - Geräte & Dienste eine neue Integration hinzu. Sucht dort nach MQTT und wählt die ohne irgendwelche weiteren Bezeichnungen.
-
Der Benutzername ist euer
appKey
und das Passwort euerappSecret
. Die URL des Brokers und der Port wurden euch ebenfalls mit der o.a. Antwort geliefert:mqtt.zen-iot.com
odermqtt-eu.zen-iot.com
mit Port1883
. -
Damit auch Daten reinkommen müsst ihr wie eingangs erwähnt, noch ein Topic abonnieren. Dies geschieht hier in dem ihr auf der Konfigurationsseite Enable Discovery aktiviert und als Discovery prefix euren
appKey
eintragt.Hinweis: Es werden euch hierdurch nur Entitäten/Sensoren angelegt. Es wird kein dediziertes Gerät angelegt, welches die Entitäten/Sensoren enhält.
-
-
Option: Ihr nutzt bereits einen MQTT-Broker in Home Assistant
Wenn ihr bereits Erfahrungen mit MQTT gesammelt habt, weil ihr z. B. Sensoren oder Steckdosen darüber mit Home Assistant verbunden habt, ist die Wahrscheinlichkeit sehr groß, dass ihr das Mosquitto broker-Addon installiert und die MQTT-Integration für die Verbindung mit diesem konfiguriert habt.
Problem: In Home Assistant kann nur eine MQTT-Integration installiert werden.
Lösung: Ihr müsst eine Brücke zum MQTT-Broker von Zendure bauen. Hierbei gibt es dann zudem zwei Möglichkeiten des weiteren Vorgehens. Entweder ist lasst euch durch Home Assistant alle verfügbaren Sensoren automatisch anlegen oder ihr fügt diese manuell hinzu. Direkt vorweg: Ich habe meine manuell hinzugefügt, so hatte ich die Möglichkeit diese dabei noch anzupassen. Zudem kam es vermehrt vor, dass in Home Assistanten mit der Zeit Entitäten mehrfach angelegt worden sind.
-
Check vorweg
-
Um zu überprüfen, ob seitens des Zendure-Brokers überhaupt Daten eures SolarFlows ausgespielt werden, bietet sich das Programm MQTT-Explorer an, welches es für die gängisten Betriebssysteme gibt.
-
Erstellt dort eine neue Connection mit euren Zugangsdaten (
appKey
,appSecret
,mqttUrl
) wie im Screenshot. -
Unter Advanced (Button) müsst ihr dann noch euren
appKey
als Topic abonnieren, also als neue Subscription hinzufügen -->appKey/#
. -
Es sollten dann ziemlich zeitig Werte reinkommen. Wenn ihr alles aufklappt sieht es ungefähr so aus:
Unter der Broker-Adresse erscheint ein Eintrag der wie euer
appKey
lautet (alles in 🔴). Darunter gibt es drei weitere Einträge;switch
,sensor
, und einen der euren SolarFlow bezeichnet. Wir nennen ihn daher ab jetztdeviceID
(alles in 🔵).-
switch
enthält als Einträge die Bauanleitungen für die bisher verfügbaren Schalter. -
sensor
enthält als Einträge die Bauanleitungen für die bisher verfügbaren Sensoren. -
deviceID
enthält als Eintrag die Status der Sensoren, jedoch immer nur diejenigen, deren Wert sich geändert hat.
Aktuell verfügbar sind folgende Sensoren und Schalter (die Liste erhebt keinen Anspruch auf Vollständigkeit):
Feld Beschreibung Geräteklasse Automatische Erkennung [Hub 1200] (2. iii.) electricLevel Ladestand über alle Batterien in % Sensor Ja remainOutTime Verbleibende Zeit bis Batterien entladen in min. Ob die Entladegrenze berücksichtigt wird, ist mir nicht bekannt Sensor Ja remainInputTime Verbleibende Zeit bis Batterien geladen in min. Ob die Ladegrenze berücksichtigt wird, ist mir nicht bekannt Sensor Ja socSet (Obere) Ladegrenze in % * 10 Sensor Ja inputLimit Maximale Leistungsaufnahme aus dem Netz (z. B. beim AC-Laden mit dem Hyper 2000 oder Ace 1500) Sensor Nein outputLimit Maximale Leistungsabgabe ans "Haus" (Obergrenze) in W Sensor Ja solarInputPower Aktuelle Solarleistung über alle Eingänge in W Sensor Ja packInputPower Aktuelle Entladeleistung der Batterien in W Sensor Ja outputPackPower Aktuelle Ladeleistung der Batterien in W Sensor Ja outputHomePower Aktuelle Leistungsabgabe ans "Haus" in W Sensor Ja packNum Anzahl angeschlossener Batterien Sensor Ja packState Status über alle Batterien (0: Standby, 1: Mind. eine Batterie wird geladen, 2: Mind. eine Batterie wird entladen) Sensor Ja buzzerSwitch Bestätigungston Ein oder Aus Schalter Ja masterSwitch Funktion nicht bekannt. Steht bei mir immer auf Ein Schalter Ja wifiState WLAN-Verbindungsstatus (1: Verbunden, 0: Getrennt) Sensor Nein hubState Verhalten des Hub/Hyper bei Erreichen der Entladegrenze (0: Ausgabe beenden und Standby 1: Ausgabe beenden und Ausschalten) Sensor Nein inverseMaxPower Maximal zulässige Abgabeleistung ans "Haus" / Gesetzliche Obergrenze in W Sensor Nein packData Sammel-Topic über die Batteriewerte, filterbar über die jeweilige Batterieseriennummer (maxVol: Spannung der Zelle mit der höchsten Spannung in V * 100, minVol: Spannung der Zelle mit der niedrigsten Spannung in V * 100, maxTemp: Temperatur der Zelle mit der höchsten Temperatur in K, socLevel: Ladestand in %, sn: Seriennummer) Sensor Ja solarPower1 Aktuelle Solarleistung Eingang PV 1 in W Sensor Ja solarPower2 Aktuelle Solarleistung Eingang PV 2 in W Sensor Ja passMode Bypass-Modus (0: Automatisch, 1: Immer aus, 2: Immer ein) Sensor Ja autoRecover Automatisches Rücksetzen des Bypass-Modus (0: Aus 1: Ein) Schalter Ja sn Seriennummer des Hub/Hyper Sensor Nein heatState Batterieheizung AB2000 und S-Serie (0: Aus, 1: Ein) Sensor Nein acMode AC-Modus (2: Entladen, 1: Laden) Sensor Nein Spezielle Hyper 2000 und Ace 1500 Sensoren und Schalter:
Feld Beschreibung Geräteklasse Automatische Erkennung (2. iii.) gridInputPower Leistungsbezug aus dem Netz in W Sensor Ja hyperTmp Temperatur des Hyper in K * 10 Sensor Nein acOutputPower (Hyper 2000) Funktion nicht bekannt. Vermutlich Leistungsabgabe in Verbindung mit dem Zubehör Off-Grid-Steckdosenleiste in W Sensor Ja dcOutputPower (Ace 1500) Vermutlich Leistungsabgabe USB und XT-60 in W Sensor Ja acSwitch Vermutlich Leistungsabgabe über vorhandene Steckdosen (Ace 1500) bzw. die Off-Grid-Steckdosenleiste Schalter Ja dcSwitch (Ace 1500) Vermutlich Leistungsabgabe USB und XT-60 Schalter Ja Hinweis: Die Sensoren, welche als Schalter angeben sind, nutzen dies nur zur Informationsdarstellung. Über den Schalter lässt sich die Funktion nicht ein- oder ausschalten. Zudem sind alle Sensoren aus Sicht des Hubs benannt. Dies kann schon mal zu Verwirrungen führen, denn
outputPackPower
ist die Leistung, die in die Batterien geht undpackInputPower
die Leistung, die aus den Batterien entnommen wird. -
-
-
Gleicher Start
-
Der Anfang ist bei beiden Möglichkeiten gleich.
-
Zunächst muss die Brücke zum Zendure-Broker aufgebaut werden. Hiefür müsst ihr auf das
share
-Verzeichnis eures Home Assistant zugreifen. Über das File Editor-Addon ist dies z. B. nicht möglich. Über dieses habt ihr nämlich nur Zugriff auf dasconfig
-Verzeichnis. Ihr könnt dies über das Studio Code Server-Addon, das Samba share-Addon oder das Terminal & SSH-Addon machen. Die einzelnen Wege erkläre ich nachfolgend. Erstellt zunächst eine Datei mit folgendem Inhalt. Wie die Datei heißt ist völlig egal und welchen Editor oder welches Textverarbeitungsprogramm ihr dafür nutzt ebenfalls. -
Erstellt eine neue Textdatei.
-
Fügt folgenden Inhalt ein:
connection zendure-broker address <mqttUrl>:1883 remote_username <appKey> remote_password <appSecret> remote_clientid <appKey> topic <appKey>/# in
-
Alles zwischen <> ersetzt ihr inklusive, der <> natürlich wieder durch eure eigenen Daten. Es dürfen keine <> mehr vorhanden sein.
-
Studio Code Server-Addon
- Installiert das Studio Code Server-Addon im Addon-Bereich und wechselt danach auf die Konfigurationsseite des Addons. Klickt dort auf Nicht verwendete optionale Konfigurationsoptionen einblenden und tragt unter config_path
/root
ein. Klickt auf Speichern und startet anschließend das Addon und öffnet die Benutzeroberfläche. - In der Benutzeroberfläche geht ihr links oben über das
-Menu -> File -> Open Folder... oder nutzt alternativ die Tastenkombination
Strg + K
und dannStrg + O
. - In dem erscheinenden Auswahlmenü sucht ihr
share
und klickt anschließend auf OK. - Das Verzeichnis wird vermutlich leer sein. Erstellt über das entsprechende Icon einen neuen Ordner
und nennt in
mosquitto
und erstellt in diesem neuen Ordner eine neue Dateimit dem Namen
zendure.conf
. - Kopiert nun den Inhalt aus eurer zuvor angelegten Textdatei in die neue Datei.
- Wollt ihr, dass Home Assistant euch die Sensorentitäten automatisch anlegt übernehmt noch die weiteren Zeilen aus iii. .
- Sämtliche Änderungen die ihr in der Benutzeroberfläche macht, werden direkt gespeichert.
- Installiert das Studio Code Server-Addon im Addon-Bereich und wechselt danach auf die Konfigurationsseite des Addons. Klickt dort auf Nicht verwendete optionale Konfigurationsoptionen einblenden und tragt unter config_path
-
Samba share-Addon
- Installiert das Samba share-Addon im Addon-Bereich und wechselt danach auf die Konfigurationsseite des Addons. Vergebt dort einen Username und ein Password klickt auf Speichern. Startet anschließend das Addon.
- Fügt im Datei-Explorer eures Betriebssystems eine neue Netzwerkadresse hinzu und gebt als Pfad die IP-Adresse eures Home Assistant gefolgt von
\share
ein. Zum Beispiel\\192.168.1.42\share
. - Meldet euch mit den im Addon vergebenen Benutzernamen und Passwort an.
- Das Verzeichnis ist vermutlich leer. Erstellt eine neuen Ordner namens
mosquitto
und darin eine neue Datei namens 'zendure.conf'. - Kopiert nun den Inhalt aus eurer zuvor angelegten Textdatei in die neue Datei.
- Wollt ihr, dass Home Assistant euch die Sensorentitäten automatisch anlegt übernehmt noch die weiteren Zeilen aus iii. .
- Speichert die Datei.
-
Terminal & SSH-Addon
- Installiert das Terminal & SSH-Addon im Addon-Bereich, startet es danach und öffnet die Benutzeroberfläche.
- Wechselt mit dem Befehl
cd share
insshare
-Verzeichnis. - Überprüft mit
ls
ob es schon einen Ordnermosquitto
gibt. Erscheint nach dem Befehl einfach eine neue Eingabezeile, existiert der Ordner noch nicht, ansonsten erscheint ein Eintragmosquitto
. - Ist der Ordner nicht vorhanden, erstellt ihn mit
mkdir mosquitto
. - Wechselt mit
cd mosquitto
in das Verzeichnis. - Nun erstellen wir eine neue Datei und bearbeiten diese mit dem Editor nano. Gebt dazu
nano zendure.conf
ein. - Kopiert nun den Inhalt aus eurer zuvor angelegten Textdatei und fügt ihn in das Terminalfenster ein. Achtung: Hierfür müsst ihr die
Umschalttaste
eurer Tastatur gedrückt halten und mit derrechten Maustaste
ins Terminalfenster klicken und dort Einfügen auswählen. Nun sollte der Inhalt eurer Textdatei im Terminalfenster stehen. - Wollt ihr, dass Home Assistant euch die Sensorentitäten automatisch anlegt überspringt die weiteren Schritte zunächst und macht bei iii. weiter.
- Schließt mit
Strg + X
den nano-Editor. - Bestätigt die am unteren Terminalrand erscheinende Frage mit
y
. - Bestätigt die nächste Frage mit der
Eingabetaste
.
- In der Konfiguration des Mosquitto-Addons überprüft ihr jetzt noch ob unter Customize
active
auftrue
gesetzt ist. - Abschließend ist das Mosquitto-Addon neu zu starten.
- Im Log sollte dann ein Eintrag ähnlich
Connecting bridge external-bridge (mqtt.zen-iot.com:1883)
auftauchen. Ggf. müsst ihr das Log mehrmals aktualisieren (Geduld). Sollte hingehen irgendwas mit Timeout oder so kommen, einfach das Addon noch mal neu starten.
-
-
Automatische Einfügung
Damit Home Assistant die Sensoren und Schalter automatisch erstellt, muss es wissen wie diese aufgebaut sind. Zudem gibt es seitens Home Assistant Vorgaben, wie die Topics aussehen müssen damit dies funktioniert. Ergänzt hierzu in euer
zendure.conf
einfach noch zwei weitere Zeilen mit:topic # in 0 homeassistant/sensor/<appKey>/ <appKey>/sensor/device/ topic # in 0 homeassistant/switch/<appKey>/ <appKey>/switch/device/
Hinweis: Es werden euch hierdurch nur Entitäten/Sensoren angelegt. Es wird kein dediziertes Gerät angelegt, welches die Entitäten/Sensoren enhält. D. h. ihr findet die einzelnen Entitäten nur wenn ihr diese über ihren jeweiligen Namen sucht. Zudem kam es, wie bereits erwähnt, vermehrt dazu, dass die Entitäten mit der Zeit mehrfach angelegt wurden.
Macht nun oben da weiter, wo ihr eben hierhin abgesprungen seit.
-
Manuelle Einfügung
Das manuelle, also händische, Anlegen von Entitäten erfolgt in Home Assistant über die
configuration.yaml
. Diese liegt imconfig
-Verzeichnis. Auf dieses könnt ihr ebenso via Samba zugreifen. Genau so gut ist aber der Weg über das File Editor Addon oder das Studio Code Server Addon. In diesen wird der eingefügte Code zur besseren Lesbarkeit farblich markiert und sollten Formatierungs- oder Syntaxfehler vorliegen wird dies direkt angezeigt. An für sich könnt ihr alles in dieconfiguration.yaml
schreiben. Mit der Zeit wird diese dann aber etwas unübersichtlich, da alles untereinander in einer Textdatei steht. Schöner ist es hier, entsprechende Konfigurationen in neue Dateien auszulagern.-
Öffnet eure
configuration.yaml
. -
Ergänzt unter dem nachstehenden Block, welcher sich ziemlich am Anfang der Datei befindet eine neue Zeile mit:
mqtt: !include mqtt.yaml
.group: !include groups.yaml automation: !include automations.yaml script: !include scripts.yaml scene: !include scenes.yaml
Hinweis: Der Block muss bei euch nicht genau so aussehen. Hier ist das ebenfalls davon abhängig, wie weit ihr euch in Home Assistant schon ausgetobt habt. Wenn ich das richtig in Erinnerung habe, sollte aber wenigstens eine
!include
-Zeile schon vorhanden sein. -
Erstellt eine neue Datei
mqtt.yaml
und fügt nachstehenden Inhalt ein. -
Alles zwischen <> ersetzt ihr inklusive, der <> natürlich wieder durch eure eigenen Daten. Es dürfen keine <> mehr vorhanden sein.
# Die finalen Entitätsnamen setzen sich aus dem Sensornamen und dem Gerätenamen zusammen # Beim ersten Sensor hier also SolarFlow Hub State (sensor.solarflow_hub_state) sensor: - name: "Hub State" unique_id: "<deviceID>hubState" state_topic: "<appKey>/<deviceID>/state" value_template: "{% if value_json.hubState == 0 %}Standby{%elif value_json.hubState == 1 %}Ausschalten{%endif%}" icon: mdi:vector-link device_class: "enum" options: ['Standby','Ausschalten'] # Der gesamte device-Block muss nur einmal angegeben werden, daher ist bei den nachfolgenden Entitäten nur noch identifiers vorhanden device: name: "SolarFlow" identifiers: "<EurePVHubSeriennummer>" manufacturer: "Zendure" # Ggf. an euer Modell anpassen model: "SmartPV Hub 1200 Controller" - name: "Solar Input Power" unique_id: "<deviceID>solarInputPower" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "W" device_class: "power" value_template: > {% if states('sensor.solarflow_solar_input_power') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.solarInputPower, 0) }} {% else %} {{ int(0) }} {% endif %} state_class: "measurement" icon: mdi:solar-panel device: identifiers: "<EurePVHubSeriennummer>" - name: "Pack Input Power" unique_id: "<deviceID>packInputPower" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "W" device_class: "power" value_template: > {% if states('sensor.solarflow_pack_input_power') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.packInputPower, 0) }} {% else %} {{ int(0) }} {% endif %} state_class: "measurement" icon: mdi:battery-arrow-down-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Output Pack Power" unique_id: "<deviceID>outputPackPower" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "W" device_class: "power" value_template: > {% if states('sensor.solarflow_output_pack_power') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.outputPackPower, 0) }} {% else %} {{ int(0) }} {% endif %} state_class: "measurement" icon: mdi:battery-arrow-up-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Output Home Power" unique_id: "<deviceID>outputHomePower" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "W" device_class: "power" value_template: > {% if states('sensor.solarflow_output_home_power') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.outputHomePower, 0) }} {% else %} {{ int(0) }} {% endif %} state_class: "measurement" icon: mdi:home-import-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Output Limit" unique_id: "<deviceID>outputLimit" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.outputLimit | int }}" unit_of_measurement: "W" icon: mdi:home-lightning-bolt-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Input Limit" unique_id: "<deviceID>inputLimit" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.inputLimit | int }}" unit_of_measurement: "W" device: identifiers: "<EurePVHubSeriennummer>" - name: "Remain Out Time" unique_id: "<deviceID>remainOutTime" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.remainOutTime | int }}" device_class: "duration" unit_of_measurement: "min" device: identifiers: "<EurePVHubSeriennummer>" - name: "Remain Input Time" unique_id: "<deviceID>remainInputTime" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.remainInputTime | int }}" device_class: "duration" unit_of_measurement: "min" device: identifiers: "<EurePVHubSeriennummer>" - name: "Pack State" unique_id: "<deviceID>packState" state_topic: "<appKey>/<deviceID>/state" value_template: "{% if value_json.packState == 0 %}Standby{%elif value_json.packState == 1 %}Laden{%elif value_json.packState == 2 %}Entladen{%endif%}" device_class: "enum" options: ['Standby','Laden','Entladen'] icon: mdi:battery-sync-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Pack Num" unique_id: "<deviceID>packNum" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.packNum | int }}" icon: mdi:battery-check-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Electric Level" unique_id: "<deviceID>electricLevel" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "%" device_class: "battery" value_template: "{{ value_json.electricLevel | int }}" device: identifiers: "<EurePVHubSeriennummer>" - name: "SOC Set" unique_id: "<deviceID>socSet" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "%" value_template: "{{ value_json.socSet | int / 10 }}" icon: mdi:battery-plus device: identifiers: "<EurePVHubSeriennummer>" - name: "Inverse Max Power" unique_id: "<deviceID>inverseMaxPower" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.inverseMaxPower | int }}" unit_of_measurement: "W" icon: mdi:flash-alert device: identifiers: "<EurePVHubSeriennummer>" - name: "Solar Power 1" unique_id: "<deviceID>solarPower1" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if states('sensor.solarflow_solar_power_1') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.solarPower1, 0) }} {% else %} {{ int(0) }} {% endif %} unit_of_measurement: "W" device_class: "power" state_class: "measurement" icon: mdi:solar-panel device: identifiers: "<EurePVHubSeriennummer>" - name: "Solar Power 2" unique_id: "<deviceID>solarPower2" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if states('sensor.solarflow_solar_power_2') not in ['unknown'] %} {# Muss ggf. an euren Entitätsnamen angepasst werden. #} {{ int(value_json.solarPower2, 0) }} {% else %} {{ int(0) }} {% endif %} unit_of_measurement: "W" device_class: "power" state_class: "measurement" icon: mdi:solar-panel device: identifiers: "<EurePVHubSeriennummer>" - name: "Pass Mode" unique_id: "<deviceID>passMode" state_topic: "<appKey>/<deviceID>/state" value_template: "{% if value_json.passMode == 0 %}Automatisch{%elif value_json.passMode == 1 %}Immer aus{%elif value_json.passMode == 2 %}Immer ein{%endif%}" device_class: "enum" options: ['Automatisch','Immer aus','Immer ein'] icon: mdi:auto-mode device: identifiers: "<EurePVHubSeriennummer>" - name: "AC Mode" unique_id: "<deviceID>acMode" state_topic: "<appKey>/<deviceID>/state" value_template: "{% if value_json.acMode == 2 %}Entladen{%elif value_json.acMode == 1 %}Laden{%endif%}" options: ['Entladen','Laden'] device_class: "enum" icon: mdi:current-ac device: identifiers: "<EurePVHubSeriennummer>" - name: "Batterie <Nr> maxTemp" unique_id: "<deviceID>Batterie<Nr>maxTemp" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if (value_json.packData | is_defined) %} {% for i in value_json.packData %} {% if i.sn == "<EureBatterieSeriennummer>" %} {{ (i.maxTemp | float - 273.15) | round(2) }} {% endif %} {% endfor %} {% endif %} unit_of_measurement: "°C" device_class: "temperature" device: identifiers: "<EurePVHubSeriennummer>" - name: "Batterie <Nr> maxVol" unique_id: "<deviceID>Batterie<Nr>maxVol" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if (value_json.packData | is_defined) %} {% for i in value_json.packData %} {% if i.sn == "<EureBatterieSeriennummer>" %} {{ i.maxVol | float / 100 }} {% endif %} {% endfor %} {% endif %} unit_of_measurement: "V" device_class: "voltage" icon: mdi:alpha-v-circle-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Batterie <Nr> minVol" unique_id: "<deviceID>Batterie<Nr>minVol" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if (value_json.packData | is_defined) %} {% for i in value_json.packData %} {% if i.sn == "<EureBatterieSeriennummer>" %} {{ i.minVol | float / 100 }} {% endif %} {% endfor %} {% endif %} unit_of_measurement: "V" device_class: "voltage" icon: mdi:alpha-v-circle-outline device: identifiers: "<EurePVHubSeriennummer>" - name: "Batterie <Nr> socLevel" unique_id: "<deviceID>Batterie<Nr>socLevel" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if (value_json.packData | is_defined) %} {% for i in value_json.packData %} {% if i.sn == "<EureBatterieSeriennummer>" %} {{ i.socLevel | int }} {% endif %} {% endfor %} {% endif %} unit_of_measurement: "%" device_class: "battery" device: identifiers: "<EurePVHubSeriennummer>" binary_sensor: - name: "WiFi State" unique_id: "<deviceID>wifiState" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if (value_json.wifiState | is_defined) %} {{ value_json.wifiState | abs() }} {% endif %} payload_on: '1' payload_off: '0' device_class: "connectivity" entity_category: diagnostic device: identifiers: "<EurePVHubSeriennummer>" - name: "Heat State" unique_id: "<deviceID>heatState" state_topic: "<appKey>/<deviceID>/state" value_template: "{{ value_json.heatState | int }}" payload_on: '1' payload_off: '0' icon: mdi:heating-coil device: identifiers: "<EurePVHubSeriennummer>" switch: - unique_id: "<deviceID>masterSwitch" state_topic: "<appKey>/<deviceID>/state" state_off: false command_topic: "<appKey>/<deviceID>/masterSwitch/set" name: "Master Switch" device_class: "switch" icon: mdi:gesture-tap-button value_template: "{{ value_json.masterSwitch | default('') }}" payload_on: true payload_off: false state_on: true device: identifiers: "<EurePVHubSeriennummer>" - unique_id: "<deviceID>buzzerSwitch" state_topic: "<appKey>/<deviceID>/state" state_off: false command_topic: "<appKey>/<deviceID>/buzzerSwitch/set" name: "Buzzer Switch" device_class: "switch" icon: mdi:bullhorn-outline value_template: "{{ value_json.buzzerSwitch | default('') }}" payload_on: true payload_off: false state_on: true device: identifiers: "<EurePVHubSeriennummer>" - unique_id: "<deviceID>autoRecover" state_topic: "<appKey>/<deviceID>/state" state_off: false command_topic: "<appKey>/<deviceID>/autoRecover/set" name: "Auto Recover" device_class: "switch" icon: mdi:restore value_template: "{{ value_json.autoRecover | default('') }}" payload_on: true payload_off: false state_on: true device: identifiers: "<EurePVHubSeriennummer>"
-
Ggf. für Hyper 2000 und Ace 1500 dann noch zusätzlich:
# Einzufügen bei den anderen Sensoren - name: "Grid Input Power" unique_id: "<deviceID>gridInputPower" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if states('sensor.solarflow_grid_input_power') not in ['unknown'] %} {{ int(value_json.gridInputPower, 0) }} {% else %} {{ int(0) }} {% endif %} unit_of_measurement: "W" device_class: "power" state_class: "measurement" device: identifiers: "<EurePVHubSeriennummer>" # Nur Hyper 2000 - name: "AC Output Power" unique_id: "<deviceID>acOutputPower" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if states('sensor.solarflow_ac_output_power') not in ['unknown'] %} {{ int(value_json.acOutputPower, 0) }} {% else %} {{ int(0) }} {% endif %} unit_of_measurement: "W" device_class: "power" state_class: "measurement" device: identifiers: "<EurePVHubSeriennummer>" - name: "Hyper Tmp" unique_id: "<deviceID>hyperTmp" state_topic: "<appKey>/<deviceID>/state" unit_of_measurement: "°C" device_class: "temperature" value_template: "{{ value_json.hyperTmp | float / 10 - 273.15 }}" device: identifiers: "<EurePVHubSeriennummer>" # Nur Ace 1500 - name: "DC Output Power" unique_id: "<deviceID>dcOutputPower" state_topic: "<appKey>/<deviceID>/state" value_template: > {% if states('sensor.solarflow_dc_output_power') not in ['unknown'] %} {{ int(value_json.dcOutputPower, 0) }} {% else %} {{ int(0) }} {% endif %} unit_of_measurement: "W" device_class: "power" state_class: "measurement" device: identifiers: "<EurePVHubSeriennummer>" # Einzufügen bei den anderen Schaltern - unique_id: "<deviceID>acSwitch" state_topic: "<appKey>/<deviceID>/state" state_off: false command_topic: "<appKey>/<deviceID>/acSwitch/set" name: "AC Switch" device_class: "switch" icon: mdi:current-ac value_template: "{{ value_json.acSwitch | default('') }}" payload_on: true payload_off: false state_on: true device: identifiers: "<EurePVHubSeriennummer>" # Nur Ace 1500 - unique_id: "<deviceID>dcSwitch" state_topic: "<appKey>/<deviceID>/state" state_off: false command_topic: "<appKey>/<deviceID>/dcSwitch/set" name: "DC Switch" device_class: "switch" icon: mdi:current-dc value_template: "{{ value_json.dcSwitch | default('') }}" payload_on: true payload_off: false state_on: true device: identifiers: "<EurePVHubSeriennummer>"
-
Speichert die Datei.
-
Öffnet die Entwicklerwerkzeuge und überprüft, ob eure Konfiguration fehlerfrei ist, danach startet Home Assistant neu.
-
Wenn ihr im weiteren Änderungen an dieser Sensoren- und Schalter-Konfiguration vornehmt, etwas hinzufügt oder entfernt, reicht es wenn ihr im Bereich YAML-Konfiguration neuladen auf Manuell konfigurierte MQTT-Entitäten klickt.
-
Nun solltet ihr unter Geräte & Dienste in eurer MQTT-Integration ein neues Gerät namens
SolarFlow
vorfinden, welches unter den o. a. Namen die entsprechenden Sensoren und Schalter enthält. Die wenigsten werden von vorne herein bereits einen Wert haben, da wie anfänglich erwähnt der Zendure-Broker nur Werteänderungen ausspielt, sich also der jeweilige Wert im Vergleich zu seinem vorherigen Status geändert haben muss. Um eine Aktualisierung aller Sensoren zu erzwingen, könnt ihr einfach mal für ein paar Augenblicke in der Zendure-App auf der Detailansicht der Batterien, dort wo ihr auch die Temperaturen sehen könnt, verweilen. -
Abschließend noch ein paar Einlassungen zu den von mir gemachten Anpassungen, in Ergänzung zu der Zendure Sensorbauanleitung:
-
Ich habe alle Sensoren um einen Default-Wert in der
value_template
-Zeile ergänzt. Durch den Umstand, dass nur Werteänderungen übermittelt werden, schreibt euch Home Assistant für jeden Sensor bei dem beim letzten Datenaustausch kein Wert mit dabei war eine Warning (Template variable warning: 'dict object' has no attribute 'blablabla' when rendering '{{ value_json.blablabla }}'
) in euer Log, da seitens Home Assistant die Erwartung vorhanden ist zu jedem Sensor einen Wert zu erhalten. Alle Power-Sensoren habe ich mit einer zuzätzlichen Überprüfung versehen, ob der Sensor auch ein Wert hat. Ist dem nämlich nicht so, wird der Sensor initial auf 0 W gesetzt. Da gerade die Batteriesensoren aktuell nicht definiert auf 0 W gesetzt werden, habe ich zwei kleine Automatisierungen erstellt, die dies umsetzen, da die Batterien ja nur geladen oder entladen werden können. Zuvor hatte ich diese Sensoren nur mit dem Default-Wertint(0)
versehen. Dies sorgte dafür, dass Home Assistant weiterhin den letzten Wert angezeigte, bis ein neuer übermittelt wurde bzw., dass ein Sensor auch auf 0 gesetzt wurde, wenn er nicht mehr aktualisiert wurde. Dies klappt aber mittlerweile nicht mehr, was gerade bei den Batteriesensoren stört. Daher der u. a. neue Ansatz mit den Automatisierungen. Sicherlich nicht das Ende der Fahnenstange. Zudem habe ich eine Automatisierung erstellt, die einen jeweiligen Power-Sensor nach drei Minuten ohne Werteänderung auf 0 W setzt. -
Ihr findet die Automatisierungen auch als einzelne Dateien im Code-Bereich.
-
Beachtet zudem, dass ich dort die "Standard Entitätennamen" verwende. Solltet ihr eure Sensoren also anders benennen, müsst ihr dies in der Automatisierung entsprechend anpassen, sonst wird sie nicht funktionieren.
-
Hier und Hier findet ihr noch Beispiele von Erweiterungen/Verbesserungen aus der Community zu den folgenden Automatisierungsbeispielen, bei der der Zustand der Batterien zusätzlich berücksichtigt wird.
alias: Batterieladung description: "" trigger: - platform: numeric_state entity_id: - sensor.solarflow_output_pack_power above: 0 condition: - condition: not conditions: - condition: state entity_id: sensor.solarflow_pack_input_power state: "0" action: - service: mqtt.publish metadata: {} data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"packInputPower\":0}" mode: single
alias: Batterieentladung description: "" trigger: - platform: numeric_state entity_id: - sensor.solarflow_pack_input_power above: 0 condition: - condition: not conditions: - condition: state entity_id: sensor.solarflow_output_pack_power state: "0" action: - service: mqtt.publish metadata: {} data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"outputPackPower\":0}" mode: single
alias: SolarFlow Power-Sensoren nullen description: >- Wenn sich der Wert eines Power-Sensors innerhalb der letzten drei Minuten nicht geändert hat, wird er auf 0 W gesetzt. trigger: - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_pack_input_power.last_changed)) > 180 }} id: packInputPower - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_output_pack_power.last_changed)) > 180 }} id: outputPackPower - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_solar_input_power.last_changed)) > 180 }} id: solarInputPower - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_output_home_power.last_changed)) > 180 }} id: outputHomePower - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_solar_power_1.last_changed)) > 180 }} id: solarPower1 - platform: template value_template: >- {{ (as_timestamp(now()) - as_timestamp(states.sensor.solarflow_solar_power_2.last_changed)) > 180 }} id: solarPower2 condition: [] action: - choose: - conditions: - condition: trigger id: - packInputPower - condition: numeric_state entity_id: sensor.solarflow_pack_input_power above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"packInputPower\":0}" - conditions: - condition: trigger id: - outputPackPower - condition: numeric_state entity_id: sensor.solarflow_output_pack_power above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"outputPackPower\":0}" - conditions: - condition: trigger id: - solarInputPower - condition: numeric_state entity_id: sensor.solarflow_solar_input_power above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"solarInputPower\":0}" - conditions: - condition: trigger id: - outputHomePower - condition: numeric_state entity_id: sensor.solarflow_output_home_power above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"outputHomePower\":0}" - conditions: - condition: trigger id: - solarPower1 - condition: numeric_state entity_id: sensor.solarflow_solar_power_1 above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"solarPower1\":0}" - conditions: - condition: trigger id: - solarPower2 - condition: numeric_state entity_id: sensor.solarflow_solar_power_2 above: 0 sequence: - service: mqtt.publish data: qos: "0" topic: <appKey>/<deviceID>/state payload: "{\"solarPower2\":0}" mode: parallel
-
Solar Input Power, Pack Input Power, Output Pack Power und Output Home Power habe ich um
state_class: measurement
ergänzt, damit Home Assistant mit diesen auch rechnen kann. Ob das wirklich nötig ist weiß ich gerade gar nicht. Um die Werte aber im Energie Dashboard nutzen zu können müssen sie noch zu einem Verbrauchswert (Leistung mal Zeit) integriert werden. Dafür gibt es in der Helfersektion von Home Assistant den Riemann Summenintegralsensor. Die Methode habe ich aufLinks
gestellt und das metrische Präfix aufkilo
gestellt. Wenn dann ein paar Werte durchgelaufen sind könnt ihr diese dann im Energie Dashboard verwenden. -
Output Limit und Input Limit haben die
unit_of_measurement: "W"
erhalten. -
Remain Input Time und Remain Out Time haben die
device_class: "duration"
bekommen. Der übermittelte Wert ist die jeweilige Dauer in Minuten, sodass dieunit_of_measurement: "min"
ist. Durch diedevice_class
rechnet Home Assistant das automatisch in eine Zeitangabe in h:min:s um. -
SOC Set ist von Zendure schon mit
unit_of_measurement: "%"
angegeben, allerdings liefert der Sensor dann z.B. 1000 %, wenn die obere Ladegrenze 100 % ist. Keine Ahnung warum das so sein soll. Ich habe den Wert entsprechend noch durch 10 geteilt. -
Einträge die einen gewissen Zustand darstellen habe ich soweit möglich als
enum
angelegt. -
Ich habe alle Einträge um einen
device
-Block ergänzt und als eindeutigenidentifier
meine Seriennummer genommen. Durch diesen Block weiß Home Assistant, dass es sich um Entitäten des selben Gerätes handelt und erstellt entsprechend dieses Gerät, sodass ihr es unter Geräte & Dienste auch vorfinden könnt. -
Durch den Zendure-Broker werden auch Werte ausgespielt, für die es keine Bauanleitung gibt. Ich habe sie einfach mitangelegt.
-
Natürlich könnt ihr jeder Entität hier schon ein eigenes Symbol verpassen. Ergänzt dafür für die jeweilige Entität eine
icon
-Zeile, z. B.icon: mdi:flash
.
-
-
-
-
Vor- und Nachteile
Methode Vorteile Nachteile (1.) Keine vorherige Nutzung von MQTT in Home Assistant Schnell hinzugefügt und wenig Aufwand Nachträgliche Anpassung der Entitäten möglich aber ggf. umständig. Warnings im Log über fehlende dict object
[^1](2. iii.) Bereits MQTT in der Nutzung. Automatisches Anlegen der Entitäten durch Home Assistant Geringfügiger Mehraufwand ggü. (1.), aber in Home Assistant nicht anders möglich Nachträgliche Anpassung der Entitäten möglich aber ggf. umständig. Warings im Log über fehlende dict object
[^1](2. iv.) Bereits MQTT in der Nutzung. Manuelles Anlegen der Entitäten Vollständige Anpassungmöglichkeiten gegeben. Ausschließen von Warnings Ggf. umständlich und aufwendiger als (2. iii.). Nicht selbsterklärend, schwierig nachzuvollziehen für jemanden der nicht so in der Materie steckt
[^1]: Als "Lösung" ist mir hier bisher nur bekannt, die entsprechende Warning zu unterdrücken.