Haltet euch BITTE an die Forumsregeln, jeder der hier Mitglied ist, hat bei der Registrierung den Regeln zugestimmt. Wer sich nicht daran hält, wird aus dem Forum entfernt.
Deye Hybrid Angebote Deye Zubehör Produkte
Mir fehlen noch Akku Daten und alle Statistiken um die App komplett zu ersetzen. Weiß nicht wie ich die vorhandenen Daten einmal pro Tag wegschreibe und variabel Darstelle.
Die Akku Details bekomme hatte ich mir zum testen per Solarassistant geholt. Dann aber in einem anderen Forum einen Entwurf eines Python Scripts zum Auslesen des BMS gefunden und das mit Hilfe meines Sohns für mich angepasst. Das läuft derzeit auf einem extra PI und sendet seine Daten per MQTT an ioBroker. Ergebnis sieht dann so aus:
Das ganze für quasi für jeden einzelnen Battery Pack.
In iobroker gibt es ja unzählige Möglichkeiten, wie auch bei HA, um die Daten zu speichern und zu Visualisieren. Ich bin halt ziemlich lange bei ioBroker dabei und erst wegen der Apple Geräte vor ca. 1 Jahr zusätzlich zu HA gekommen. Mir fällt es leider mangels Zeit schwer mich richtig tief in HA einzuarbeiten, zusätzlich sehe ich aktuell keinen Grund von ioBroker komplett nach HA zu wechseln.
Mit Hausmitteln wäre das History und Flot zum Darstellen von Diagrammen etc. Eventuell gibt es auch schon weiterführende Adapter aber da ich das mit Influx und Grafana mache bin ich diesbezüglich nicht so aktuell.
Gibt auch ne Menge Scripte usw. und Tabellen zu erstellen und/oder die Sachen zusätzlich z.B. noch in ein Excel zu speichern.
2x SMA PV mit Gesamt 21,7kWP
Netzparallel ohne PV Module Deye SUN-12K-SG04LP3-EU mit Felicity 20,48kWH Speicher
Eine Alternative wäre die Lösung von der Solaranzeige, die auch die Daten auf einen vServer parallel übertragen kann. Hier gibt es dazu weitere Informationen
.
------------------------------------
Ulrich
Administrator des Open Source Projektes "Solaranzeige"
Die Solaranzeige kann auch einige Deye Geräte über MODBUS steuern.
@funkboje Bin ichauch schon drüber gestolpert. Liefert die Solaranzeige die einzelnen Akku werte wenn man mehrere hat, das konnte ich irgendwie nirgendwo rauslesen.
2x SMA PV mit Gesamt 21,7kWP
Netzparallel ohne PV Module Deye SUN-12K-SG04LP3-EU mit Felicity 20,48kWH Speicher
Wenn der BMS in der Liste ist, der Geräte, die ausgelesen werden können, bzw. ein baugleiches Gerät ist, dann ja.
------------------------------------
Ulrich
Administrator des Open Source Projektes "Solaranzeige"
Die Solaranzeige kann auch einige Deye Geräte über MODBUS steuern.
@funkboje von welcher Liste sprichst du bzw. Wo finde ich die denn?
2x SMA PV mit Gesamt 21,7kWP
Netzparallel ohne PV Module Deye SUN-12K-SG04LP3-EU mit Felicity 20,48kWH Speicher
@epo_deluxe ich benutze auch nur noch influx. der recorder von homeassi ist grottig. dann brauch ich auch nicht in engem raster loggen wenn der nur alle paar muniten dauerhaft speichert und nach wochen die daten verliert
@funkboje von welcher Liste sprichst du bzw. Wo finde ich die denn?
Die Liste findest du hier:
https://solaranzeige.de/phpBB3/viewtopic.php?t=1069
------------------------------------
Ulrich
Administrator des Open Source Projektes "Solaranzeige"
Die Solaranzeige kann auch einige Deye Geräte über MODBUS steuern.
@funkboje danke.
gibt es die Liste auch ohne vorherige Registrierung?
Macht ja wenig Sinn wenn ich mich extra anmelde um dann festzustellen das meine Batterien da nicht aufgeführt sind.
2x SMA PV mit Gesamt 21,7kWP
Netzparallel ohne PV Module Deye SUN-12K-SG04LP3-EU mit Felicity 20,48kWH Speicher
Ja, das gibt es auch hier:
https://solaranzeige.de/phpBB3/viewtopic.php?t=170
Sorry, darauf hätte ich achten müssen.
------------------------------------
Ulrich
Administrator des Open Source Projektes "Solaranzeige"
Die Solaranzeige kann auch einige Deye Geräte über MODBUS steuern.
Hallo, ist genau das was ich suche. Wenn alle Daten inkl. BMS ausgelesen werden können ist dies richtig klasse. Für mein Projekt benötige ich nur den "SOC". Hintergrund ist, dass ich eine Wärmepumpe im April 2025 eingebaut bekomme. Diese ist "SG-Ready" und somit kann bei bei Nutzung einer PV die Energie im Pfufferspeicher eingelagert werden. Ich habe dies bisher so im Probetrieb aufgebaut:
>Deye Sun 12 + 40 kW Abbus sind so eingestellt: Erzeugung in den Haushalt > Überschuss in den Akkus > wenn SOC 100% dann bisher Verkauf
>Die SG-Ready Schnittstelle wird mit einem Node-Red-Script und einem Shelly 3 Pro (220V) gesteuert. Das Script ist so aufgebaut, das es über MQTT den SOC abfrgen soll. Dann ist bis 71% SOC die Schnittstelle auf "Boost Mode" (Kontaktstellung: beide Kontakte geschlossen) geschalten. Ab 51 - 70% wird auf Comfort Mode (Kontakt eins offen, Kontakt 2 geschlossen). Bei SOC 20% (Deye-Einstellung) bis 50% wird der "Normal Mode" aktiviert. Also beide Kontakte sind geöffnet.
Wäre ein zur Verfügungstellung des Scriptes möglich?
Danke.
Herbert
@jimibondi Hi,
sorry war wegen Dienstreise und Gartenumbau bisschen offline.
Hier das Script wie es derzeit bei mir läuft:
import logging import signal import sys import paho.mqtt.client as mqtt import datetime import time import os.path from FelicityBMSManager import FelicityBMSManager # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') #DataItem = namedtuple("DataItem", ["key", "command", "data", "lastdata", "isdirty"]) class DataItem: def __init__(self, command : str, clientid : int): self.command = command self.clientid = clientid self.data = None self.lastdata = None class MQTTBroker: def __init__(self, name: str, address : str, topic : str): self.name = name self.address = address self.topic = topic self.client = None ioBroker_broker = MQTTBroker('ioBroker', '192.168.178.18', 'felicity/battery') def on_connect(client, userdata, rc, *extra_params): print('ioBroker: Connected with result code ' + str(rc)) client.subscribe(ioBroker_broker.topic + '/set/#') #client.publish(settings.MQTT_TOPIC + '', get_gpio_status(), 1) ioBroker_broker.client = mqtt.Client() isrunning = True def _handle_sigterm(sig, frame): logging.info("SIGTERM received. Exiting...") isrunning = False ioBroker_broker.client.loop_stop() exit() def getFormattedData(command, clientid): ws = FelicityBMSManager(command=command, clientid=clientid) data = ws.get_data() #if websocket_data != last_websocket_data: formatted_data = {} for inverter_key in data: inverter_item = data[inverter_key] value = inverter_item.value #if value == '--': # value = '0' formatted_data[inverter_key] = {"name": inverter_item.name, "value": value, "unit": inverter_item.unit} #print(inverter_key, inverter_item.desc, value, inverter_item.unit) return formatted_data def sendData(broker, topic_key, data): broker.client.publish(broker.topic + topic_key, data) #print(broker.name, broker.address, broker.topic + topic_key, data) starttime = datetime.datetime.min resettime = datetime.datetime.min try: signal.signal(signal.SIGTERM, _handle_sigterm) ioBroker_broker.client.on_connect = on_connect #ioBroker_broker.client.on_message = on_message ioBroker_broker.client.username_pw_set("mqtt","ioBroker") ioBroker_broker.client.connect(ioBroker_broker.address, 1889, 60) ioBroker_broker.client.loop_start() starttime = datetime.datetime.min battery_information_list = [ DataItem(command="GetBMSBatteryInformation", clientid = 1), DataItem(command="GetBMSBatteryInformation", clientid = 2), DataItem(command="GetBMSBatteryInformation", clientid = 3), DataItem(command="GetBMSBatteryInformation", clientid = 4) ] cell_information_list = [DataItem(command="GetBMSCellInformation", clientid = battery_information.clientid) for battery_information in battery_information_list] while isrunning: if datetime.datetime.now() > starttime + datetime.timedelta(seconds = 10): #org 10 try: isDirtyDefault = False if datetime.datetime.now() > resettime + datetime.timedelta(seconds = 60): #org 60 isDirtyDefault = True resettime = datetime.datetime.now() for battery_index, battery_information in enumerate(battery_information_list): battery_information.lastdata = battery_information.data battery_information.data = getFormattedData(command = battery_information.command, clientid = battery_information.clientid) time.sleep(1) if isDirtyDefault or battery_information.lastdata != battery_information.data: battery_voltage = battery_information.data["BatteryVoltage"]["value"] battery_current = battery_information.data["BatteryCurrent"]["value"] battery_power = battery_voltage * battery_current * -1 # positiv Ladung, negativ Entladung battery_soc = battery_information.data["SOC"]["value"] sendData(ioBroker_broker, '/' + str(battery_information.clientid) + '/battery_voltage', battery_voltage) sendData(ioBroker_broker, '/' + str(battery_information.clientid) + '/battery_current', battery_current) sendData(ioBroker_broker, '/' + str(battery_information.clientid) + '/battery_power', round(battery_power, 1)) sendData(ioBroker_broker, '/' + str(battery_information.clientid) + '/soc', battery_soc) cell_information = cell_information_list[battery_index] cell_information.lastdata = cell_information.data cell_information.data = getFormattedData(command = cell_information.command, clientid = cell_information.clientid) time.sleep(1) if isDirtyDefault or cell_information.lastdata != cell_information.data: for cell_index in range(1, 17): cell_voltage = cell_information.data["Cell" + str(cell_index) + " Voltage"]["value"] sendData(ioBroker_broker, '/' + str(battery_information.clientid) + '/cell_' + str(cell_index) + '_voltage', cell_voltage) except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] logging.exception("Caught exception") print(exc_type, fname, exc_tb.tb_lineno) pass starttime = datetime.datetime.now() time.sleep(1) #except KeyboardInterrupt: # GPIO.cleanup() # dmx_stop() finally: print("cleanup")
Und hier die verwendete Library nochmal:
import asyncio import serial import time import crcmod import argparse from terminaltables import AsciiTable # type: ignore from collections import namedtuple InverterItem = namedtuple("InverterItem", ["name", "desc", "value", "unit"]) class FelicityBMSManager: """ FelicityBMSManager""" def __init__(self, command: str, clientid : int = 1, device: str = "/dev/ttyUSB0", baudrate: int = 9600): #def __init__(self, command: str, value: int = None, device: str = "socket://192.168.0.205:4196", baudrate: int = 9600, inverterport: str = "/dev/ttyRS232", inverterbaud: int = 2400): clientid_hex = "%0.2x" % int(clientid) #print(command, clientid, clientid_hex, device, baudrate) self.command: str = command self.clientid_hex = clientid_hex self.device: str = device self.baudrate: int = baudrate async def get_data_async(self) -> dict[str, InverterItem]: data: dict[str, InverterItem] = {} #data["test"] = InverterItem( # name="t1", # desc="desc1", # value="value1", # unit="unit1") device = "" baud = 0 no_crc_command = None command_result = None if self.command == "GetBMSVersion": device = self.device baud = self.baudrate no_crc_command = bytes.fromhex(self.clientid_hex + " 03 F8 0B 00 01") # Battery version command_result = self.send_command(device, baud, no_crc_command); elif self.command == "GetBMSCellInformation": device = self.device baud = self.baudrate no_crc_command = bytes.fromhex(self.clientid_hex + " 03 13 2A 00 14") # Cell voltage and temperature command_result = self.send_command(device, baud, no_crc_command); elif self.command == "GetBMSBatteryInformation": device = self.device baud = self.baudrate no_crc_command = bytes.fromhex(self.clientid_hex + " 03 13 02 00 0A") # Battery information command_result = self.send_command(device, baud, no_crc_command); else: print("Invalid command") if command_result is not None: if self.command == "GetBMSCellInformation": #datacount = int(len(command_result) / 2) #print(datacount) #for i in range(datacount): # cell_key = str(i + 1) # value = command_result[i * 2:i * 2 + 2] # print(value, value.hex()); # data["Cell" + cell_key + " Voltage"] = InverterItem( # name="Cell" + cell_key + " Voltage", # desc="Cell" + cell_key + " Voltage", # value=round(int.from_bytes(value, byteorder='big') * 1, 4), # unit="V") cellcount = 16 for i in range (0, cellcount): cell_key = str(i + 1) value = command_result[i * 2:i * 2 + 2] #print(i, value.hex()) data["Cell" + cell_key + " Voltage"] = InverterItem( name="Cell" + cell_key + " Voltage", desc="Cell" + cell_key + " Voltage", value=round(int.from_bytes(value, byteorder='big') * 0.001, 4), unit="V") for i in range (0, 4): cell_key = str(i + 1) value = command_result[(cellcount + i) * 2:(cellcount + i) * 2 + 2] #value = value * 0.001 data["Sensor" + cell_key + " Temperature"] = InverterItem( name="Sensor" + cell_key + " Temperature", desc="Sensor" + cell_key + " Temperature", value=round(int.from_bytes(value, byteorder='big') * 1, 4), unit="°C") elif self.command == "GetBMSVersion": version = command_result[0:2] #print(type(version), version.hex(), int.from_bytes(version, byteorder='big')) data["BMSVersion"] = InverterItem( name="BMSVersion", desc="BMSVersion", value=int.from_bytes(command_result[0:2], byteorder='big'), unit="") elif self.command == "GetBMSBatteryInformation": #soc = command_result[18:20] #print(type(soc), soc.hex(), int.from_bytes(soc, byteorder='big')) data["BatteryVoltage"] = InverterItem( name="BatteryVoltage", desc="BatteryVoltage", value=round(int.from_bytes(command_result[8:10], byteorder='big') * 0.01, 4), unit="V") signedCurrent = int.from_bytes(command_result[10:12], byteorder='big') #print(signedCurrent) signedCurrent -= (signedCurrent & 0x8000) << 1 #print(signedCurrent) data["BatteryCurrent"] = InverterItem( name="BatteryCurrent", desc="BatteryCurrent", value=round(signedCurrent * 0.1, 2), unit="A") data["SOC"] = InverterItem( name="SOC", desc="Ladezustand", value=int.from_bytes(command_result[18:20], byteorder='big'), unit="%") return data def send_command(self, serial_port, serial_baud, no_crc_command): crc = self.modbusCrc(no_crc_command) ba = crc.to_bytes(2, byteorder='little') full_command = no_crc_command + ba #print("full command", full_command.hex()) try: with serial.serial_for_url(serial_port, serial_baud) as s: # while True: # response_line = s.read_until(b"\r") # #dataline = s.readline(); # print(response_line) #print("Executing command via serialio...") s.timeout = 1 s.write_timeout = 1 s.flushInput() s.flushOutput() s.write(full_command) #s.write(full_command+'\r') time.sleep(0.1) # give serial port time to receive the data # response_line = s.read_until(b"\r") # print("serial response was: %s", response_line) # bytes = bytearray(response_line) # print(bytes.hex()) resp = s.read(3) #print(resp.hex()) datalength = resp[2] resp = s.read(datalength) #print(type(resp), resp.hex()) # hex_val = resp.hex() # byte_val = bytes([int(hex_val[i : i + 2], 16) for i in range(0, len(hex_val), 2)]) # print(byte_val) return resp #soc = resp[18:20] #print(type(soc), soc.hex(), int.from_bytes(soc, byteorder='big')) # return response_line except Exception as e: print(f"Serial read error: {e}") print("Command execution failed") def modbusCrc(self, msg:str) -> int: crc = 0xFFFF for n in range(len(msg)): crc ^= msg[n] for i in range(8): if crc & 1: crc >>= 1 crc ^= 0xA001 else: crc >>= 1 return crc def get_data(self) -> dict[str, InverterItem]: return asyncio.run(self.get_data_async()) def main(): """Command line interface to the Felicity BMS""" parser = argparse.ArgumentParser( description="Retrieve data from Felicity Inverter and BMS" ) parser.add_argument("command", help="Command") parser.add_argument("-c", "--clientid", default=1, help="ClientId (DIP Settings)") parser.add_argument("-d", "--device", default="/dev/ttyUSB0", help="Device (e.g. /dev/ttyUSB0 or socket://ipadress:port") parser.add_argument("-b", "--baudrate", default="9600", help="baudrate") #parser.add_argument( # "--details", action="store_true", help="show more details" #) #parser.add_argument('--version', action='version', version=version) args: dict[str, InverterItem] = parser.parse_args() data: list[InverterItem] = FelicityBMSManager(args.command, args.clientid, args.device, args.baudrate).get_data() #if args.details: # table: list[list[str]] = [["Item", "Value", "ID"]] + [ # [item.desc, f"{item.value} {item.unit}", id] # for id, item in data.items() # ] #else: table = [["Name", "Description", "Value"]] + [ [item.name, item.desc, f"{item.value} {item.unit}"] for item in data.values() ] print(AsciiTable(table).table) if __name__ == "__main__":
2x SMA PV mit Gesamt 21,7kWP
Netzparallel ohne PV Module Deye SUN-12K-SG04LP3-EU mit Felicity 20,48kWH Speicher