openHAB und SOLARMAN: Integrationsschritte im Detail
Die Integration von SOLARMAN in openHAB ermöglicht Hausbesitzern eine effiziente Ăberwachung und Steuerung ihrer Solaranlage. In diesem Beitrag werden verschiedene AnsĂ€tze vorgestellt, wie die Daten des Wechselrichters in die Open-Source-Plattform integriert werden können. Die Integration wird ĂŒber die SOLARMMANPV API realisiert. Damit können viele Wechselrichter (hier Deye oder Bosswerk) in openHAB integriert werden.
Aktivierung API
Im ersten Schritt muss die API von SOLARMANPV fĂŒr die externe Nutzung freigeschaltet werden. Dazu genĂŒgt eine kurze E-Mail an den Kundenservice (customerservice@solarmanpv.com):
Hello Solarman-Support, I need access to the Solarman-API. Account: xxx Best regards, xxx
Installation
Die Installation in openHAB 4.2.x erfolgt wie gewohnt ĂŒber das Webinterface:
- Add-on Store – Binding – HTTP-Binding – INSTALL
In den frĂŒheren Versionen war teilweise das HTTP-Binding von SmartHome/J zum Teil notwendig. In den alten Versionen gab es auch Probleme mit der Token-Authentifizierung von SOLARMANPV (das ist aber in den aktuellen Versionen behoben).
Vor der ersten Integration in openHAB kann der Webservice mit den entsprechenden Tools getestet werden. DafĂŒr habe ich Postman verwendet.
Thing
Eine mögliche SOLARMAN.things kann wie folgt aussehen (die Werte mit xxx mĂŒssen durch eigene Werte ersetzt werden):
Thing http:url:solarmanpv_account "SOLARMANPV - Account" [ baseURL="https://globalapi.solarmanpv.com/account/v1.0/token?appId=xxx&language=en", contentType="application/json", authMode="BASIC", stateMethod="POST", commandMethod="GET", // 86400 Sekunden = 24 Std. - 4752000 Sekunden = 55 Tage (60 Tage lĂ€uft ein Token) // 2592000 Sekunden = 30 Tage refresh=4752000] { Channels: Type string : config "config" [ stateContent="{ \"appSecret\" : \"xxx\", \"email\" : \"xxx@xxx.de\", \"password\" : \"xxx\" }" ] } Thing http:url:solarmanpv_station "SOLARMANPV - Station" [ baseURL="https://globalapi.solarmanpv.com/station/v1.0/list?language=en", contentType="application/json", authMode="TOKEN", password="xxx", stateMethod="POST", refresh=60] { Channels: // Fehlerbehandlung Type string : msg "msg" [ stateTransformation="JSONPATH:$.msg", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : code "code" [ stateTransformation="JSONPATH:$.code", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : success "success" [ stateTransformation="JSONPATH:$.success", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : requestId "requestId" [ stateTransformation="JSONPATH:$.requestId", stateContent="{ \"page\": 1, \"size\": 50 }" ] // Anzahl der Stationen Type string : total "total" [ stateTransformation="JSONPATH:$.total", stateContent="{ \"page\": 1, \"size\": 50 }" ] // xxx- Bosswerk BW-HY 3600 - Haus SĂŒd Type number : stationList_xxx_id "stationList_xxx_id" [ stateTransformation="JSONPATH:$.stationList[1].id", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : stationList_xxx_name "stationList_xxx_name" [ stateTransformation="JSONPATH:$.stationList[1].name", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type number : stationList_xxx_installedCapacity "stationList_xxx_installedCapacity" [ stateTransformation="JSONPATH:$.stationList[1].installedCapacity", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type number : stationList_xxx_batterySoc "stationList_xxx_batterySoc" [ stateTransformation="JSONPATH:$.stationList[1].batterySoc", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : stationList_xxx_networkStatus "stationList_xxx_networkStatus" [ stateTransformation="JSONPATH:$.stationList[1].networkStatus", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type number : stationList_xxx_generationPower "stationList_xxx_generationPower" [ stateTransformation="JSONPATH:$.stationList[1].generationPower", stateContent="{ \"page\": 1, \"size\": 50 }" ] Type string : stationList_xxx_lastUpdateTime "stationList_xxx_lastUpdateTime" [ stateTransformation="JSONPATH:$.stationList[1].lastUpdateTime", stateContent="{ \"page\": 1, \"size\": 50 }" ] } Thing http:url:solarmanpv_realTime "SOLARMANPV - realTime" [ baseURL="https://globalapi.solarmanpv.com/station/v1.0/realTime?language=en", contentType="application/json", authMode="TOKEN", password="xxx", stateMethod="POST", refresh=60] { Channels: // xxx - Bosswerk BW-HY 3600 - Haus SĂŒd Type string : realTime_xxx_code "realTime_xxx_code" [ stateTransformation="JSONPATH:$.code", stateContent="{\"stationId\": xxx}" ] Type string : realTime_xxx_msg "realTime_xxx_msg" [ stateTransformation="JSONPATH:$.msg", stateContent="{\"stationId\": xxx}" ] Type string : realTime_xxx_success "realTime_xxx_success" [ stateTransformation="JSONPATH:$.success", stateContent="{\"stationId\": xxx}" ] Type string : realTime_xxx_requestId "realTime_xxx_requestId" [ stateTransformation="JSONPATH:$.requestId", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_generationPower "realTime_xxx_generationPower" [ stateTransformation="JSONPATH:$.generationPower", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_usePower "realTime_xxx_usePower" [ stateTransformation="JSONPATH:$.usePower", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_purchasePower "realTime_xxx_purchasePower" [ stateTransformation="JSONPATH:$.purchasePower", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_batteryPower "realTime_xxx_batteryPower" [ stateTransformation="JSONPATH:$.batteryPower", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_batterySoc "realTime_xxx_batterySoc" [ stateTransformation="JSONPATH:$.batterySoc", stateContent="{\"stationId\": xxx}" ] Type number : realTime_xxx_generationTotal "realTime_xxx_generationTotal" [ stateTransformation="JSONPATH:$.generationTotal", stateContent="{\"stationId\": xxx}" ] Type string : realTime_xxx_lastUpdateTime "realTime_xxx_lastUpdateTime" [ stateTransformation="JSONPATH:$.lastUpdateTime", stateContent="{\"stationId\": xxx}" ] }
Zwei Webservices werden angesprochen:
- Account – optionale Nutzung fĂŒr das Auslesen der Token (funktionktioniert, erfordert aber manuelle Eingriffe alle 90 Tage)
- realTime – Auslesen der Werte von der API
Items
Die SOLARMAN.items kann wie folgt erstellt werden:
// // https://globalapi.solarmanpv.com/account/v1.0/token?appId=xxx&language=en // 23.12.2023 // String solarmanpv_account_config "SOLARMANPV - Account - config" { channel="http:url:solarmanpv_account:config"} // // https://globalapi.solarmanpv.com/station/v1.0/list?language=en // // Fehlerbehandlung String solarmanpv_station_msg "SOLARMANPV - Station - msg" { channel="http:url:solarmanpv_station:msg"} String solarmanpv_station_code "SOLARMANPV - Station - code" { channel="http:url:solarmanpv_station:code"} String solarmanpv_station_success "SOLARMANPV - Station - success" { channel="http:url:solarmanpv_station:success"} String solarmanpv_station_requestId "SOLARMANPV - Station - requestId" { channel="http:url:solarmanpv_station:requestId"} // Anzahl der Stationen String solarmanpv_station_total "SOLARMANPV - Station - total" { channel="http:url:solarmanpv_station:total"} // xxx - Bosswerk BW-HY 3600 - Haus SĂŒd Number solarmanpv_stationList_xxx_id "SOLARMANPV - stationList - xxx - id" { channel="http:url:solarmanpv_station:stationList_xxx_id"} String solarmanpv_stationList_xxx_name "SOLARMANPV - stationList - xxx - name" { channel="http:url:solarmanpv_station:stationList_xxx_name"} Number solarmanpv_stationList_xxx_installedCapacity "SOLARMANPV - stationList - xxx - installedCapacity" { channel="http:url:solarmanpv_station:stationList_xxx_installedCapacity"} Number solarmanpv_stationList_xxx_batterySoc "SOLARMANPV - stationList - xxx - batterySoc" { channel="http:url:solarmanpv_station:stationList_xxx_batterySoc"} String solarmanpv_stationList_xxx_networkStatus "SOLARMANPV - stationList - xxx - networkStatus" { channel="http:url:solarmanpv_station:stationList_xxx_networkStatus"} Number solarmanpv_stationList_xxx_generationPower "SOLARMANPV - stationList - xxx - generationPower" { channel="http:url:solarmanpv_station:stationList_xxx_generationPower"} DateTime solarmanpv_stationList_xxx_lastUpdateTime "SOLARMANPV - stationList - xxx - lastUpdateTime" { channel="http:url:solarmanpv_station:stationList_xxx_lastUpdateTime"} // // https://globalapi.solarmanpv.com/station/v1.0/realTime?language=en // // Gesamte Ăbersicht Number solarmanpv_realTime_generationTotal "SOLARMANPV - stationList - generationTotal" // xxx - Bosswerk BW-HY 3600 - Haus SĂŒd String solarmanpv_realTime_xxx_code "SOLARMANPV - stationList - xxx - code" { channel="http:url:solarmanpv_realTime:realTime_xxx_code"} String solarmanpv_realTime_xxx_msg "SOLARMANPV - stationList - xxx - msg" { channel="http:url:solarmanpv_realTime:realTime_xxx_msg"} String solarmanpv_realTime_xxx_success "SOLARMANPV - stationList - xxx - success" { channel="http:url:solarmanpv_realTime:realTime_xxx_success"} String solarmanpv_realTime_xxx_requestId "SOLARMANPV - stationList - xxx - requestId" { channel="http:url:solarmanpv_realTime:realTime_xxx_requestId"} Number solarmanpv_realTime_xxx_generationPower "SOLARMANPV - stationList - xxx - generationPower" { channel="http:url:solarmanpv_realTime:realTime_xxx_generationPower"} Number solarmanpv_realTime_xxx_usePower "SOLARMANPV - stationList - xxx - usePower" { channel="http:url:solarmanpv_realTime:realTime_xxx_usePower"} Number solarmanpv_realTime_xxx_purchasePower "SOLARMANPV - stationList - xxx - purchasePower" { channel="http:url:solarmanpv_realTime:realTime_xxx_purchasePower"} Number solarmanpv_realTime_xxx_batteryPower "SOLARMANPV - stationList - xxx - batteryPower" { channel="http:url:solarmanpv_realTime:realTime_xxx_batteryPower"} Number solarmanpv_realTime_xxx_batterySoc "SOLARMANPV - stationList - xxx - batterySoc" { channel="http:url:solarmanpv_realTime:realTime_xxx_batterySoc"} Number solarmanpv_realTime_xxx_generationTotal "SOLARMANPV - stationList - xxx - generationTotal" { channel="http:url:solarmanpv_realTime:realTime_xxx_generationTotal"} DateTime solarmanpv_realTime_xxx_lastUpdateTime "SOLARMANPV - stationList - xxx - lastUpdateTime" { channel="http:url:solarmanpv_realTime:realTime_xxx_lastUpdateTime"}
Rules
Nun kann man in der Regeldatei SOLARMAN.rules seine entsprechenden Regeln parametrieren:
rule "SOLARMANPV generation total" when Item solarmanpv_realTime_xxx_generationTotal received update then logInfo("INFO", "SOLARMAN.rules - Gesamtproduktion Start: " + solarmanpv_realTime_xxx_generationTotal.state) solarmanpv_realTime_generationTotal.postUpdate((solarmanpv_realTime_xxx_generationTotal.state as Number) + (solarmanpv_realTime_xxx_generationTotal.state as Number) + (solarmanpv_realTime_xxx_generationTotal.state as Number) + (solarmanpv_realTime_xxx_generationTotal.state as Number)) logInfo("INFO", "SOLARMAN.rules - Gesamtproduktion (Total kWh): " + solarmanpv_realTime_generationTotal.state) end // 08.02.2024 - Wenn Item mit Refresh Token von SOLARMAN PV geĂ€ndert wird, Regel ausfĂŒhren rule "SOLARMANPV refresh token" when Item solarmanpv_account_config received update then //logInfo("INFO", "SOLARMAN.rules - JSON: " + solarmanpv_account_config) val access_token = transform("JSONPATH", "$.access_token", solarmanpv_account_config.state.toString) val expires_in = transform("JSONPATH", "$.expires_in", solarmanpv_account_config.state.toString) val double expiresMinutes = Double.parseDouble(expires_in.toString) / 60 val double expiresHours = Double.parseDouble(expiresMinutes.toString) / 60 val double expiresDays = Double.parseDouble(expiresHours.toString) / 24 // TEST NOCH OFFEN - expires_in muss DOUBLE sein //var test = 0; //test = Math.floor(expires_in) //minutes = Math.floor(dur / 60); //hours = Math.floor(minutes / 60); //days = Math.floor(hours / 24); //logInfo("INFO", "SOLARMAN.rules - Access Token: " + access_token) logInfo("INFO", "SOLARMAN.rules - Expires In (Sekunden): " + expires_in) logInfo("INFO", "SOLARMAN.rules - Expires In (Tage): " + expiresDays) val telegramAction = getActions("telegram","telegram:telegramBot:HA_Bot") telegramAction.sendTelegram(Long::parseLong(TELEGRAM_CHANNEL_SMARTHOME_ADMIN.label), "SOLARMAN.rules - Expires In (Tage): " + expiresDays) end
In dieser Regel summiere ich verschiedene Werte meiner Wechselrichter in einer GesamtĂŒbersicht.
AuĂerdem schreibe ich eine Nachricht in eine Telegram-Gruppe, wenn der Token von SOLARMANPV nach ca. 90 Tagen ablĂ€uft.
Haus.sitemap
Text label="PV" icon="sun" { Frame label="GesamtĂŒbersicht" { Text item=solarmanpv_realTime_generationTotal label="Gesamtproduktion (kWh) [%s]" icon="energy" } Frame label="PV-Anlagen & Wechselrichter" { Text label="Bosswerk BW-HY 3600 - Haus SĂŒd" icon="settings" { Frame label="Stationsdaten" { Text item=solarmanpv_stationList_xxx_name label="Name der Anlage [%s]" icon="" Text item=solarmanpv_stationList_xxx_installedCapacity label="Installierte KapazitĂ€t (kWp) [%s]" icon="" Text item=solarmanpv_stationList_xxx_batterySoc label="Batterie [%s]" icon="" Text item=solarmanpv_stationList_xxx_networkStatus label="Status [%s]" icon="" Text item=solarmanpv_stationList_xxx_generationPower label="Produktion [%s]" icon="" Text item=solarmanpv_stationList_xxx_lastUpdateTime label="Aktualisiert [%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS]" icon="" } Frame label="Echtzeitdaten" { Text item=solarmanpv_realTime_xxx_generationPower label="Produktion [%s]" icon="" Text item=solarmanpv_realTime_xxx_usePower label="Verbrauch [%s]" icon="" Text item=solarmanpv_realTime_xxx_purchasePower label="Netz [%s]" icon="" Text item=solarmanpv_realTime_xxx_batteryPower label="Batterie [%s]" icon="" Text item=solarmanpv_realTime_xxx_batterySoc label="Batteriestand [%s]" icon="" Text item=solarmanpv_realTime_xxx_generationTotal label="Gesamtproduktion [%s]" icon="" Text item=solarmanpv_realTime_xxx_lastUpdateTime label="Aktualisiert [%1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS]" icon="" } Frame label="Gesamtproduktion" { Switch item=Chart_Zeitraum_D_W_M_Y label="" mappings=[0="Tag", 1="Woche", 2="Monat", 3="Jahr"] Chart item=solarmanpv_realTime_xxx_generationTotal service="rrd4j" period=D refresh=15000 visibility=[Chart_Zeitraum_D_W_M_Y==0, Chart_Zeitraum_D_W_M_Y=="Uninitialized"] Chart item=solarmanpv_realTime_xxx_generationTotal service="rrd4j" period=W refresh=15000 visibility=[Chart_Zeitraum_D_W_M_Y==1] Chart item=solarmanpv_realTime_xxx_generationTotal service="rrd4j" period=M refresh=15000 visibility=[Chart_Zeitraum_D_W_M_Y==2] Chart item=solarmanpv_realTime_xxx_generationTotal service="rrd4j" period=Y refresh=15000 visibility=[Chart_Zeitraum_D_W_M_Y==3] } } } }
Fazit
Die Integration von SOLARMAN in openHAB war fĂŒr mich etwas komplizierter. Es gab kein fertiges Binding, das ich verwenden konnte, d.h. ich musste zu einer manuellen Integration der Webservices per HTTP-Binding ĂŒbergehen.
In der frĂŒhen Phase der Integration gab es dann noch das Problem, dass der Token von SOLARMAN leider nicht mit dem Binding funktionierte. Das wurde dann aber in Zusammenarbeit mit dem sehr aktiven Entwickler gelöst und in den nĂ€chsten Versionen behoben.
Momentan habe ich noch das Problem, dass der Token alle 90 Tage ablĂ€uft. Ich kann den Token zwar auslesen, aber ich habe noch keine Möglichkeit gefunden, den Token dann auch entsprechend automatisiert in den Items von openHAB zu hinterlegen (dafĂŒr schicke ich mir grob vor dem Ablaufdatum eine Nachricht und passe es noch manuell an).
Die technische Integration von SOLARMAN in openHAB ist mit den aktuellen Versionen inzwischen recht einfach möglich. Ein einfaches Beispielszenario ist schnell eingerichtet. Die Integration lÀuft nun seit einiger Zeit sehr stabil und ich bin sehr zufrieden damit.
Hinterlasse einen Kommentar
An der Diskussion beteiligen?Hinterlasse uns deinen Kommentar!