NEWS
Qcells / Solax Pv Anlage live Datenabfrage via Curl
-
Hallo,
alle die eine Solax bzw Qcells Pv Anlage haben kennen bestimmt das Problem, dass die Daten welche man über die Cloud bekommt nur alle 5 Minuten aktualisiert werden. Das ist für meine Zwecke jedoch zu ungenau. Also kurz gesucht und siehe da es gibt eine Lösung welche wesentlich mehr Daten in einem wesentlich kürzeren Zeitintervall ausgibt:curl -d "optType=ReadRealTimeData&pwd=<XXX Passwort XXX>" http://<XXX IP Adresse XXX>
Als Folge kommt dann dieses tolle Zahlen Chaos (wahrscheinlich in json) raus:
{"sn":"XXXXXXXX","ver":"3.008.10","type":14,"Data":[2342,2297,2364,55,54,54,1269,1229,1268,3766,4949,3130,21,91,1067,2852,4996,5000,4998,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1341,0,0,0,0,42780,0,0,4262,0,0,1,49,2425,256,7685,6672,5893,100,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0,11156,0,385,4,0,0,2833,0,3235,0,31,100,12045,0,471,0,0,0,44032,0,16875,0,2755,0,220,0,0,0,0,0,0,0,0,0,1,99,1,32,122,256,4672,3200,0,300,242,218,34,33,24,2132,782,15934,15934,15934,15934,0,0,3358,3321,44242,9,20564,12339,18753,12599,18737,12356,14132,20564,12339,18754,12345,18742,12358,12599,20564,12339,18754,12345,18742,12870,14132,20564,12339,18754,12345,18742,12870,12338,20564,12339,18754,12345,18742,12358,13110,4097,4098,1281,259,0,42780,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Information":[XXXXXX,XXX,"XXXXXXXXXXX",8,1.30,0.00,1.28,1.09,0.00,1]}
(XXX ersetzt hier nur Zahlen, welche ich nicht veröffentlichen möchte)
Auf Github hat jemand die Zahlen dann mit diesem Skript auseinander gepuzzelt (welcher Wert bedeutet was)
import voluptuous as vol
import aiohttp
from solax.inverter import InverterPost
from solax.units import Total, Units
from solax.utils import (
div10,
div100,
twoway_div10,
to_signed,
pv_energy,
twoway_div100,
total_energy,
discharge_energy,
charge_energy,
feedin_energy,
consumption,
)class QVOLTHYBG33P(InverterPost):
"""
QCells
Q.VOLT HYB-G3-3P
"""class Processors: """ Postprocessors used only in the QVOLTHYBG33P inverter sensor_map. """ @staticmethod def inverter_modes(value, *_args, **_kwargs): return { 0: "Waiting", 1: "Checking", 2: "Normal", 3: "Off", 4: "Permanent Fault", 5: "Updating", 6: "EPS Check", 7: "EPS Mode", 8: "Self Test", 9: "Idle", 10: "Standby", }.get(value, f"unmapped value '{value}'") @staticmethod def battery_modes(value, *_args, **_kwargs): return { 0: "Self Use Mode", 1: "Force Time Use", 2: "Back Up Mode", 3: "Feed-in Priority", }.get(value, f"unmapped value '{value}'") def __init__(self, host, port, pwd=""): super().__init__(host, port, pwd) self.manufacturer = "Qcells" _schema = vol.Schema( { vol.Required("type"): vol.All(int, 14), vol.Required("sn"): str, vol.Required("ver"): str, vol.Required("Data"): vol.Schema( vol.All( [vol.Coerce(float)], vol.Length(min=200, max=200), ) ), vol.Required("Information"): vol.Schema( vol.All(vol.Length(min=10, max=10)) ), }, extra=vol.REMOVE_EXTRA, ) @classmethod def response_decoder(cls): return { "Network Voltage Phase 1": (0, Units.V, div10), "Network Voltage Phase 2": (1, Units.V, div10), "Network Voltage Phase 3": (2, Units.V, div10), "Output Current Phase 1": (3, Units.A, twoway_div10), "Output Current Phase 2": (4, Units.A, twoway_div10), "Output Current Phase 3": (5, Units.A, twoway_div10), "Power Now Phase 1": (6, Units.W, to_signed), "Power Now Phase 2": (7, Units.W, to_signed), "Power Now Phase 3": (8, Units.W, to_signed), "AC Power": (9, Units.W, to_signed), "PV1 Voltage": (10, Units.V, div10), "PV2 Voltage": (11, Units.V, div10), "PV1 Current": (12, Units.A, div10), "PV2 Current": (13, Units.A, div10), "PV1 Power": (14, Units.W), "PV2 Power": (15, Units.W), "Grid Frequency Phase 1": (16, Units.HZ, div100), "Grid Frequency Phase 2": (17, Units.HZ, div100), "Grid Frequency Phase 3": (18, Units.HZ, div100), "Inverter Operation mode": (19, Units.NONE, cls.Processors.inverter_modes), # 20 - 32: always 0 # 33: always 1 # instead of to_signed this is actually 34 - 35, # because 35 = if 34>32767: 0 else: 65535 "Exported Power": (34, Units.W, to_signed), # 35: if 34>32767: 0 else: 65535 # 36 - 38 : always 0 "Battery Voltage": (39, Units.V, div100), "Battery Current": (40, Units.A, twoway_div100), "Battery Power": (41, Units.W, to_signed), # 42: div10, almost identical to [39] # 43: twoway_div10, almost the same as "40" (battery current) # 44: twoway_div100, almost the same as "41" (battery power), # 45: always 1 # 46: follows PV Output, idles around 44, peaks at 52, "Power Now": (47, Units.W, to_signed), # 48: always 256 # 49,50: [49] + [50] * 15160 some increasing counter # 51: always 5634 # 52: always 100 # 53: always 0 # 54: follows PV Output, idles around 35, peaks at 54, # 55-67: always 0 "Total Energy": (68, Total(Units.KWH), total_energy), "Total Energy Resets": (69), # 70: div10, today's energy including battery usage # 71-73: 0 "Total Battery Discharge Energy": (74, Total(Units.KWH), discharge_energy), "Total Battery Discharge Energy Resets": (75), "Total Battery Charge Energy": (76, Total(Units.KWH), charge_energy), "Total Battery Charge Energy Resets": (77), "Today's Battery Discharge Energy": (78, Units.KWH, div10), "Today's Battery Charge Energy": (79, Units.KWH, div10), "Total PV Energy": (80, Total(Units.KWH), pv_energy), "Total PV Energy Resets": (81), "Today's Energy": (82, Units.KWH, div10), # 83-85: always 0 "Total Feed-in Energy": (86, Total(Units.KWH), feedin_energy), "Total Feed-in Energy Resets": (87), "Total Consumption": (88, Total(Units.KWH), consumption), "Total Consumption Resets": (89), "Today's Feed-in Energy": (90, Units.KWH, div100), # 91: always 0 "Today's Consumption": (92, Units.KWH, div100), # 93-101: always 0 # 102: always 1 "Battery Remaining Capacity": (103, Units.PERCENT), # 104: always 1 "Battery Temperature": (105, Units.C), "Battery Remaining Energy": (106, Units.KWH, div10), # 107: always 256 or 0 # 108: always 3504 # 109: always 2400 # 110: around rise to 300 if battery not full, 0 if battery is full # 112, 113: range [250,350]; looks like 113 + offset = 112, # peaks if battery is full # 114, 115: something around 33; Some temperature?! # 116: increases slowly [2,5] # 117-121: 1620 773 12850 12850 12850 # 122-124: always 0 # 125,126: some curve, look very similar to "42"(Battery Power), # with offset around 15 # 127,128 resetting counter /1000, around battery charge + discharge # 164,165,166 some curves "Battery Operation mode": (168, Units.NONE, cls.Processors.battery_modes), # 169: div100 same as [39] # 170-199: always 0 } @classmethod async def make_request(cls, host, port=80, pwd="", headers=None): url = f"http://{host}:{port}/" data = f"optType=ReadRealTimeData&pwd={pwd}" async with aiohttp.ClientSession() as session: async with session.post(url, headers=headers, data=data) as req: resp = await req.read() return cls.handle_response(resp)
Nun ist meine Idee die Werte aus der Curl Abfrage einfach in ioBroker Datenpunkte einzutragen, leider kenne ich mich mit der Materie noch zu wenig aus und wollte daher mal fragen ob eventuell hier jemand eine Idee hat.
Viele Grüße
Jan -
@jamakr4 schau dir axios mal an..damit kannst du den curl absetzen und die daten empfangen..
dann musst du den json zerlegen.. -
Ich arbeite mit dem Solax Adapter und kann damit die Daten lokal z. B. im 10-Sekundentakt abfragen. Die cloud benötige ich gar nicht.
-
@meister-mopper Danke
-
@jamakr4
siehe hier:
Solax Photovoltaik Portal in ioBrokerhttps://forum.iobroker.net/topic/13152/solax-photovoltaik-portal-in-iobroker/10
evtl. nützt es dir was.