|
|
||
Es gibt genau eine lokale Lösung die für pre-SetApp Wechselrichter funktioniert wenn man den Traffic mitschneidet und den Key per RS485 aus dem WR WR [Wechselrichter] ausliest: https://github.com/jbuehl/solaredge Alles andere geht nur noch via Webscraping (deine Links). Es spricht aber auch nichts dagegen die Daten via python snippet abzuholen (per cron oder telegraf), in eine InfluxDB zu schreiben und von dort holt Nodered die Daten wieder ab. Laienbedienbar ist das nicht, stimmt, aber ein RPI findet sich gleich zum basteln. |
||
|
||
Für wirklich wichtige Daten kann man den Solaredge Wechselrichter via Modbus TCP auslesen. Solaredge hat "Sunspec" implementiert https://www.solaredge.com/sites/default/files/sunspec-implementation-technical-note.pdf - hier der link zu der Sunspec alliance: https://sunspec.org/ Leider unterstütz Sunspec keine Optimiererdaten, diese müssen wie @wiwi schon sagte über das Portal abgefragt werden (sollten aber auch nicht sooo kritisch sein) Ich verfolge bei meinen Auswertungen auch den Ansatz alles offline abzufragen, aber solche zusätzlichen aber nicht essenziellen infos finde ich ok wenn man diese aus dem Internet zieht. |
||
|
||
Hallo zusammen Danke für eure Antworten. Die lokale Lösung habe ich mir angeschaut und da ist mir zuviel Aufwand dabei, auch weil Keys ausgelesen werden müssen und weil keiner weiss, ob das nach dem nächsten Update noch läuft. Mir würde die Lösung über Webscraping reichen. Leider reichen meine Programmierkenntnisse nicht aus, um das Python Snippet zum Laufen zu bringen. Die Daten sollen final sowieso in eine influxDB, NoderRed habe ich nur vorgeschlagen, weil ich nicht wusste, ob sich schon jemand eine Node gebastelt hat... Hat von euch schon mal jemand eine Lösung zum Laufen gebracht? SG Wolferl |
||
|
||
|
||
nodes für solaredge gibt es ja, hast hier mal gesucht? https://flows.nodered.org/node/node-red-contrib-solaredge-power https://flows.nodered.org/search?term=solaredge |
||
|
||
Hallo Fani Danke für die Links. Ja, die habe ich schon gefunden. Leider geben diese Nodes keine Optimizer Daten aus. Wie gesagt, NodeRed wäre für mich erst mal nicht so wichtig. Ich hätte die Daten einfach gerne in InfluxDB, NodeRed kann ein Vehikel dazu sein. Wenn es eine Lösung gibt (die ich mit meinen beschränkten Programmierkenntnissen implementieren kann) die direkt in Influx schreibt, passt das auch... SG Wolferl |
||
|
||
Mit Google Suche "solaredge api optimizer data" findet man einige Seiten. z.B. https://github.com/ProudElm/solaredgeoptimizers Das Problem ist, das die Optimizer-Daten (außer Inventory) nicht mit SolarEdge API ausgelesen werden, sondern von der WebSite geholt werden. Ich verwende derzeit ein Image von solaranzeige.de, wo aber die Daten nur mit Modbus TCP ausgelesen werden. Wenn ich mehr Zeit habe, möchte mir aber Optimizer-Daten zusätzlich holen. Aber ich habe leider noch keine perfekte Lösung gesehen. |
||
|
||
Das Problem ist, per Modbus TCP werden die Optimierer Daten von SE nicht zur Verfügung gestellt, obwohl es möglich wäre. Aktuelle SetApp-WR kommunizieren SSL-verschlüsselt mit SE, da bleibt de-facto nur Webscraping. Bei den alten WR WR [Wechselrichter] mit Display (pre-SetApp) funktioniert die Entschlüsselung über den per Modbus RTU extrahierten Key und das mitschneiden (oder umleiten) des Datenverkehrs. |
||
|
||
Für mich wäre Webscraping mehr als ok, perfekte Lösung gibt es wahrscheinlich keine da SE das nicht unterstützt... wenn sich jemand mit besseren Programmierkenntnissen als ich mit dem Snippet von https://gist.github.com/mihailescu2m/27d5dd22656a4d64a0c755da99b7b162 beschäftigt wird es vielleicht was. Schön wäre, wenn man das in eine function node in Nodered integrieren könnte... Ich habs probiert, auch in Python, bin aber nicht weiter gekommen |
||
|
||
Mit https://gist.github.com/cooldil/0b2c5ee22befbbfcdefd06c9cf2b7a98 kommen da schon Daten (hab den Fork genommen weil der vor drei Monaten zumindest aktiv war). Die Variablen im Python Code sind halt mit deinen Login Daten und der Site ID zu ergänzen ansonsten kommt der cookie Error. Eine laufende InfluxDB wäre auch von Vorteil, dann füllt man auch die IP und DB etc. aus. Ansonsten kann man auch die letzten zwei Zeilen ("conn ...") auskommentieren und vorher ein print(df.to_markdown()) einfügen dann landen die Daten erstmal im Terminal. |
||
|
||
Hab mir jetzt mal Visual Studio mit Python installiert und der Fork, den du vorgeschlagen hast, bringt wirklich Daten. Hatte das vorher schon mal mit dem Original Snippet gemacht, da war ein Return() statt einem Exit() in Zeile 31 und dauernd eine Fehlermeldung. Muss mich da jetzt ein bisschen einlesen, wäre aber froh, wenn ich dir danach noch ein oder zwei Fragen stellen dürfte. Du scheinst in Python ja gut daheim zu sein. Im Moment bekomme ich noch eine Fehlermeldung 401 wenn ich in den Bucket schreiben möchte, Benutzername und Passwort ist eingetragen der Zeile mit dem DataFrameClient. Hast du da auch noch eine Idee? Danke schon mal für deine Hilfe bis jetzt... Wolferl |
||
|
||
Der Fehler 401 deutet auf Probleme mit der Authentifizierung bei der InfluxDB hin. Schwer zu sagen aus der Ferne wo es da zwickt. 🙂 |
||
|
||
Danke, das hab ich mir gedacht. Hab dann mal nachgelesen. Ich glaub, mein Problem ist, dass die Funktion DataFrameClient zwar User und PW akzeptiert, aber keinen Token... habs noch nicht rausgefunden... |
||
|
||
Hallo wiwi Ich hab es jetzt hinbekommen, dass die Daten in die Datenbank geschrieben werden. mit DataFrameClient() gings nicht, ich hab das jetzt mit InfluxDBClient gemacht. Bleiben noch zwei Probleme: • Ich habe 30 Optimierer, die Abfrage bringt aber nur 22 Stk • Im Ergebnis der Abfrage wird nicht die Seriennummer der Optimierer ausgegeben, sondern scheinbar eine ID. Hast du eine Ahnung, ob es da irgendwo eine Lookup Table gibt, die Seriennummer des Optimierers und ID korreliert? Das Script kommt im nächsten Post, habs nicht geschafft das als Datei hochzuladen... Danke Wolferl |
||
|
||
import requests, pickle from datetime import datetime import json, pytz import pandas as pd from influxdb import DataFrameClient from influxdb_client import InfluxDBClient, Point, Dialect from influxdb_client.client.write_api import SYNCHRONOUS login_url = "https://monitoring.solaredge.com/solaredge-apigw/api/login" panels_url = "https://monitoring.solaredge.com/solaredge-web/p/playbackData" SOLAREDGE_USER = "x@gmail.com" # web username SOLAREDGE_PASS = "xxx" # web password SOLAREDGE_SITE_ID = "123" # site id DAILY_DATA = "4" WEEKLY_DATA = "5" MONTHLY_DATA = "6" YEARLY_DATA = "7" ALL_DATA = "0" INFLUXDB_IP = "192.168.1.10:8086" INFLUXDB_TOKEN = "lkjhlkjhlkjhlkjhlj" INFLUXDB_ORG = 'hjkjhjk' INFLUXDB_BUCKET = 'test' session = requests.session() try: # Make sure the cookie file exists with open('solaredge.cookies', 'rb') as f: f.close() except IOError: # Create the cookie file session.post(login_url, headers = {"Content-Type": "application/x-www-form-urlencoded"}, data={"j_username": SOLAREDGE_USER, "j_password": SOLAREDGE_PASS}) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit": DAILY_DATA}) with open('solaredge.cookies', 'wb') as f: pickle.dump(session.cookies, f) f.close() with open('solaredge.cookies', 'rb') as f: session.cookies.update(pickle.load(f)) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit":DAILY_DATA}) if panels.status_code != 200: session.post(login_url, headers = {"Content-Type": "application/x-www-form-urlencoded"}, data={"j_username": SOLAREDGE_USER, "j_password": SOLAREDGE_PASS}) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit": DAILY_DATA}) if panels.status_code != 200: exit() with open('solaredge.cookies', 'wb') as f: pickle.dump(s.cookies, f) response = panels.content.decode("utf-8").replace('\'', '"').replace('Array', '').replace('key', '"key"').replace('value', '"value"') response = response.replace('timeUnit', '"timeUnit"').replace('fieldData', '"fieldData"').replace('reportersData', '"reportersData"') response = json.loads(response) data = {} for date_str in response["reportersData"].keys(): date = datetime.strptime(date_str, '%a %b %d %H:%M:%S GMT %Y') date = pytz.timezone('Europe/Berlin').localize(date).astimezone(pytz.utc) for sid in response["reportersData"][date_str].keys(): for entry in response["reportersData"][date_str][sid]: if entry["key"] not in data.keys(): data[entry["key"]] = {} data[entry["key"]][date] = float(entry["value"].replace(",", "")) df = pd.DataFrame(data) client=InfluxDBClient(url=INFLUXDB_IP, token = INFLUXDB_TOKEN, org=INFLUXDB_ORG) write_api = client.write_api(write_options=SYNCHRONOUS) #print(df.to_string()) write_api.write(bucket=INFLUXDB_BUCKET, org=INFLUXDB_ORG, record=df, data_frame_measurement_name = "optimizer") |
||
|
||
Leider nein, das musst du vermutlich anhand der Daten im Monitoringportal zuordnen. |
||
|
||
Hallo wiwi Ich habe inzwischen mit dem Author des scripts Kontakt aufgenommen. Das mit den Daten im Monitoring Portal ist leider richtig. So wie es aussieht, muss man das aber nur einmal machen. Ich bin relativ nah dran, sobald ich das habe werde ich das hier posten, vielleicht kann es ja ein anderer SE Besitzer brauchen. SG Wolferl |
||
|
||
Guten Morgen Nochmal ein update zu meinem Setup. Es funktioniert jetzt alles wie gewünscht. Das überarbeitete Python Script kommt dann im nächsten Post: • Das Python script läuft bei mir auf einer Synology DS216+II und wird per taskplaner alle 15min gestartet • Damit werden die Daten in einen temporären InfluxDB bucket geschrieben • Aus diesem Bucket holt ein InfluxDB Task alle 15min die letzten Daten und schreibt sie in den endgültigen bucket. Bei diesem Task werden dann noch drei Tags dazugefügt - Seriennummer (aus dem Python script kommt nur eine ID) - logische Nummer des Panels aus dem Portal - Typ der Daten: Inverter, String oder Panel Damit habe ich jetzt die Daten in einem InfluxDB bucket und kann sie so auswerten, wie ich möchte. Ausserdem sind sie da dauerhaft gespeichert. Frohe Weihnachten Wolferl |
||
|
||
# script zum Importieren von Optimiererdaten aus dem SE Portal # wird alle 15min mittels Taskplaner auf der DS216 ausgeführt, SE aktualisiert die Daten im selben Rythmus # Original Script von https://gist.github.com/cooldil/0b2c5ee22befbbfcdefd06c9cf2b7a98 # Nach dem Auslesen werden die Daten in eine InfluxDB geschrieben, Dort werden die Daten mittels Task weiterverarbeitet import requests, pickle from datetime import datetime import json, pytz import pandas as pd from influxdb_client import InfluxDBClient, Point, Dialect from influxdb_client.client.write_api import SYNCHRONOUS login_url = "https://monitoring.solaredge.com/solaredge-apigw/api/login" panels_url = "https://monitoring.solaredge.com/solaredge-web/p/playbackData" SOLAREDGE_USER = "XXX" # web username SOLAREDGE_PASS = "XXX" # web password SOLAREDGE_SITE_ID = "XXX" # site id DATA_RANGE = "4" # 4 is daily, 5 is weekly ALL_DATA = "0" INFLUXDB_IP = "192.168.1.10:8086" INFLUXDB_TOKEN = "XX" INFLUXDB_ORG = 'XX' INFLUXDB_BUCKET = 'SE_Temp' INFLUXDB_MEASUREMENT = "SE-web" session = requests.session() try: # Make sure the cookie file exists with open('solaredge.cookies', 'rb') as f: f.close() except IOError: # Create the cookie file session.post(login_url, headers = {"Content-Type": "application/x-www-form-urlencoded"}, data={"j_username": SOLAREDGE_USER, "j_password": SOLAREDGE_PASS}) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit": DATA_RANGE}) with open('solaredge.cookies', 'wb') as f: pickle.dump(session.cookies, f) f.close() with open('solaredge.cookies', 'rb') as f: session.cookies.update(pickle.load(f)) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit":DATA_RANGE}) if panels.status_code != 200: session.post(login_url, headers = {"Content-Type": "application/x-www-form-urlencoded"}, data={"j_username": SOLAREDGE_USER, "j_password": SOLAREDGE_PASS}) panels = session.post(panels_url, headers = {"Content-Type": "application/x-www-form-urlencoded", "X-CSRF-TOKEN": session.cookies["CSRF-TOKEN"]}, data={"fieldId": SOLAREDGE_SITE_ID, "timeUnit": DATA_RANGE}) if panels.status_code != 200: exit() with open('solaredge.cookies', 'wb') as f: pickle.dump(session.cookies, f) response = panels.content.decode("utf-8").replace('\'', '"').replace('Array', '').replace('key', '"key"').replace('value', '"value"') response = response.replace('timeUnit', '"timeUnit"').replace('fieldData', '"fieldData"').replace('reportersData', '"reportersData"') response = json.loads(response) data = {} for date_str in response["reportersData"].keys(): date = datetime.strptime(date_str, '%a %b %d %H:%M:%S GMT %Y') date = pytz.timezone('Europe/Berlin').localize(date).astimezone(pytz.utc) for sid in response["reportersData"][date_str].keys(): for entry in response["reportersData"][date_str][sid]: if entry["key"] not in data.keys(): data[entry["key"]] = {} data[entry["key"]][date] = float(entry["value"].replace(",", "")) df = pd.DataFrame(data) client=InfluxDBClient(url=INFLUXDB_IP, token = INFLUXDB_TOKEN, org=INFLUXDB_ORG) write_api = client.write_api(write_options=SYNCHRONOUS) #print(df.to_string()) write_api.write(bucket=INFLUXDB_BUCKET, org=INFLUXDB_ORG, record=df, data_frame_measurement_name =INFLUXDB_MEASUREMENT ) |
Beitrag schreiben oder Werbung ausblenden?
Einloggen
Kostenlos registrieren [Mehr Infos]