gatherBYDData.py fragt die BMU der HVM unter 192.168.16.254:8080 ab (BE-Connect-Protokoll, nach ioBroker.bydhvs): Ladestand und SOH laut BMU, 128 Zellspannungen, 64 Temperaturen, Spreizung, Ausgleich, Fehlerbits, Gesamtzaehler. Das Netzwerkmodul startet alle ~102 s neu und bedient nur die ersten Verbindungen danach - eigene Klopf-Schleife, ein Satz etwa alle 100 s. Werte unter solarManager/byd/#, Historie in byd und byd_zellen. tbatt kommt jetzt aus der BMU statt fest 0. mqttClient.publish legt ein einzelnes Dataclass-Objekt in rtData als Untertopics ab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
3.7 KiB
Python
93 lines
3.7 KiB
Python
import sys
|
|
import dataclasses
|
|
import logging
|
|
import json
|
|
sys.path.append("./")
|
|
import paho.mqtt.client as mqtt
|
|
logging.config.fileConfig('./logging.ini')
|
|
_LOGGER = logging.getLogger()
|
|
|
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
def default(self, o):
|
|
if dataclasses.is_dataclass(o):
|
|
return dataclasses.asdict(o)
|
|
return super().default(o)
|
|
|
|
unacked_publish = set()
|
|
|
|
|
|
mqttClient=mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
|
#mqttClient.on_publish = on_publish
|
|
mqttClient.user_data_set(unacked_publish)
|
|
|
|
logging.basicConfig(
|
|
format="%(asctime)s %(message)s",
|
|
level=logging.WARN,
|
|
)
|
|
|
|
def publish(dict,topic="solarManager"):
|
|
if dataclasses.is_dataclass(dict):
|
|
for field in dataclasses.fields(dict):
|
|
key = field.name
|
|
value = getattr(dict, field.name)
|
|
if hasattr(value, '__len__') and (not isinstance(value, str)):
|
|
if dataclasses.is_dataclass(value): #make subtopics of dataclasses
|
|
publish(value,topic+"/"+key)
|
|
else:
|
|
#try: #make subtopics of dataclass arrays
|
|
if len(value):
|
|
if dataclasses.is_dataclass(value[0]):
|
|
i=0
|
|
for d in value:
|
|
publish(d,topic+"/"+key+str(i))
|
|
i=i+1
|
|
#except:
|
|
else:
|
|
mqttClient.publish(topic+"/"+key,json.dumps(value, cls=EnhancedJSONEncoder))
|
|
else: #output empty array anyway to keep data consistent
|
|
mqttClient.publish(topic+"/"+key,json.dumps(value, cls=EnhancedJSONEncoder))
|
|
else:
|
|
try:
|
|
flt = float(value)
|
|
mqttClient.publish(topic+"/"+key,round(flt,2))
|
|
except ValueError:
|
|
mqttClient.publish(topic+"/"+key,value)
|
|
else:
|
|
for key, value in dict.items():
|
|
# Ein einzelnes Dataclass-Objekt (z.B. rtData["byd"]) hat keine
|
|
# Laenge und fiel frueher in den Zahlenzweig - "value is no number".
|
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
publish(value,topic+"/"+key)
|
|
elif hasattr(value, '__len__') and (not isinstance(value, str)):
|
|
if dataclasses.is_dataclass(value): #make subtopics of dataclasses
|
|
publish(value,topic+"/"+key)
|
|
else:
|
|
#try: #make subtopics of dataclass arrays
|
|
if len(value):
|
|
if dataclasses.is_dataclass(value[0]):
|
|
i=0
|
|
for d in value:
|
|
publish(d,topic+"/"+key+str(i))
|
|
i=i+1
|
|
#except:
|
|
else:
|
|
mqttClient.publish(topic+"/"+key,json.dumps(value, cls=EnhancedJSONEncoder))
|
|
else: #output empty array anyway to keep data consistent
|
|
mqttClient.publish(topic+"/"+key,json.dumps(value, cls=EnhancedJSONEncoder))
|
|
else:
|
|
try:
|
|
flt = float(value)
|
|
mqttClient.publish(topic+"/"+key,round(flt,2))
|
|
except ValueError:
|
|
mqttClient.publish(topic+"/"+key,value)
|
|
except:
|
|
_LOGGER.error(f"value is no number")
|
|
|
|
#print(message)
|
|
|
|
def startMqttClient():
|
|
mqttClient.connect("localhost", 1883, 60)
|
|
mqttClient.loop_start()
|
|
#await asyncio.Future() # run forever
|
|
def closeMqttClient():
|
|
mqttClient.disconnect() |