Laden ohne die Hausbatterie zu leeren

Robeck
Beiträge: 106
Registriert: Sa Jan 23, 2021 11:12 am
Has thanked: 12 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Robeck »

Gero hat geschrieben: Di Dez 17, 2024 9:11 am
Robeck hat geschrieben: Mo Dez 16, 2024 9:04 pm Bei mir mit E3DC ist nichts zu sehen, oder ich finde es einfach nicht.

Aber vielleicht findet sich ja jemand, der das für die openWB umsetzen mag.
Wir haben doch jetzt Weihnachten, da such doch mit Sicherheit einer von Euch eine Herausforderung ;) :mrgreen:
Ich kann sowas einfach nicht
15,6 kwp PV, E3DC Speicher mit 18kw, Tesla Model 3 und eine openWB Pro+
Basti
Beiträge: 94
Registriert: Di Feb 21, 2023 3:28 pm
Has thanked: 1 time
Been thanked: 11 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Basti »

So dank der neuen docu müsste es für solaredge jetzt funktionieren . Testen kann ich es selbst erst nach Weihnachten , auf eigene Gefahr 😜.

Code: Alles auswählen

bat.py ersetzen in Solaredge

Code: Alles auswählen

der Controll Mode 4 muss gesetzt werden , ich habe die Funktion auf dem Script jetzt entfernt , weil ich der Meinung bin das openwb keine Konfiguration ändern sollte am Fremdsystem , der Modus 4 Remote 0xE004 muss durch den User aktiv gesetzt werden 

Code: Alles auswählen

#!/usr/bin/env python3
"""
Dieses Skript ermöglicht die optionale Steuerung von SolarEdge-Batterien über Modbus. 
Für eine Fernsteuerung der Batterie muss der Wert des Registers StorageConf_CtrlMode (0xE004) auf "4" (Remote) gesetzt sein. 
Die Steuerung erfolgt über das Setzen von Leistungsbegrenzungen, Abfrage des Ladezustands (SOC) und der Batterieleistung.
"""

import logging
from typing import Optional, Dict, Union

from pymodbus.payload import BinaryPayloadDecoder, BinaryPayloadBuilder
from pymodbus.constants import Endian
from dataclass_utils import dataclass_from_dict
from modules.common import modbus
from modules.common.abstract_device import AbstractBat
from modules.common.component_state import BatState
from modules.common.component_type import ComponentDescriptor
from modules.common.fault_state import ComponentInfo, FaultState
from modules.common.simcount import SimCounter
from modules.common.store import get_bat_value_store
from modules.devices.solaredge.solaredge.config import SolaredgeBatSetup

# Initialisiere Logger für die Debug- und Fehlerausgabe
log = logging.getLogger(__name__)


class SolaredgeConfig:
    """
    Konfigurationsklasse für SolarEdge-Registeradressen. 
    Diese Klasse zentralisiert Adressen, um Änderungen einfach umzusetzen.
    """

    def __init__(self):
        # Register für Leistungsbegrenzung
        self.power_limit_register = 0xE010  # Maximal erlaubte Lade-/Entladeleistung
        # Register für den Ladezustand der Batterie (State of Charge - SOC)
        self.soc_register = 0xE184  # SOC in Prozent
        # Register für aktuelle Batterieleistung
        self.power_register = 0xE174  # Momentanleistung der Batterie


class SolaredgeBat(AbstractBat):
    """
    Klasse zur Steuerung eines SolarEdge-Speichersystems.
    Ermöglicht Modbus-Kommunikation zum Lesen und Schreiben von Parametern.
    """

    def __init__(self,
                 device_id: int,
                 component_config: Union[Dict, SolaredgeBatSetup],
                 tcp_client: modbus.ModbusTcpClient_,
                 config: SolaredgeConfig) -> None:
        """
        Initialisiert die SolarEdge-Batterie-Klasse.

        :param device_id: Eindeutige ID des Geräts für die Kommunikation.
        :param component_config: Konfigurationseinstellungen für die Batterie.
        :param tcp_client: Modbus-TCP-Client für die Kommunikation.
        :param config: Instanz der Konfigurationsklasse mit Registeradressen.
        """
        self.__device_id = device_id  # Geräte-ID
        self.component_config = dataclass_from_dict(SolaredgeBatSetup, component_config)
        self.__tcp_client = tcp_client  # Modbus-TCP-Client für Lese-/Schreiboperationen
        self.config = config  # SolarEdge-Registerkonfiguration
        self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher")
        self.store = get_bat_value_store(self.component_config.id)  # Interner Zustandsspeicher
        self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))

    def set_power_limit(self, power_limit: Optional[int]) -> None:
        """
        Setzt die Leistungsbegrenzung für das Laden/Entladen.

        :param power_limit: Die gewünschte Begrenzung in Watt.
                            - Ein positiver Wert aktiviert die Steuerung.
                            - None oder -1 setzt die Begrenzung zurück (keine Limitierung).
        """
        unit = self.component_config.configuration.modbus_id
        register = self.config.power_limit_register

        try:
            if power_limit is None:
                power_limit = -1
                log.debug("Keine Leistungsbegrenzung angegeben. Begrenzung wird deaktiviert (-1).")

            # Versuch, den Wert direkt als INT32 zu schreiben
            try:
                current_limit = self.__tcp_client.read_holding_registers(register, ModbusDataType.INT_32, unit=unit)
                if current_limit == power_limit:
                    log.debug(f"Aktuelle Leistungsbegrenzung ({current_limit} W) entspricht dem Sollwert ({power_limit} W). Kein Schreibvorgang erforderlich.")
                    return
                self.__tcp_client.write_registers(register, power_limit, ModbusDataType.INT_32, unit=unit)
                log.info(f"Leistungsbegrenzung erfolgreich auf {power_limit} W gesetzt.")
            except Exception as e:
                log.warning(f"Direkte INT32-Verarbeitung fehlgeschlagen: {e}. Wechsel zu manueller Dekodierung.")

                # Fallback auf manuelle Zerlegung und Schreiben
                builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little)
                builder.add_32bit_int(power_limit)
                registers = builder.to_registers()
                self.__tcp_client.write_registers(register, registers, unit=unit)
                log.info(f"Leistungsbegrenzung erfolgreich (Fallback) auf {power_limit} W gesetzt.")
        except Exception as e:
            log.error(f"Fehler beim Setzen der Leistungsbegrenzung: {e}")
            raise

    def get_power_limit(self) -> Optional[int]:
        """
        Liest die aktuell eingestellte Leistungsbegrenzung.

        :return: Der aktuelle Wert der Leistungsbegrenzung in Watt (INT32).
        """
        unit = self.component_config.configuration.modbus_id
        register = self.config.power_limit_register

        try:
            # Direkt INT32 lesen
            try:
                current_limit = self.__tcp_client.read_holding_registers(register, ModbusDataType.INT_32, unit=unit)
                log.debug(f"Aktuelle Leistungsbegrenzung aus Register {register}: {current_limit} W")
                return current_limit
            except Exception as e:
                log.warning(f"Direkte INT32-Verarbeitung fehlgeschlagen: {e}. Wechsel zu manueller Dekodierung.")

                # Fallback auf manuelle Dekodierung
                response = self.__tcp_client.read_holding_registers(register, count=2, unit=unit)
                if response.isError():
                    raise Exception(f"Fehler beim Lesen der Leistungsbegrenzung aus Register {register}.")
                decoder = BinaryPayloadDecoder.fromRegisters(
                    response.registers, byteorder=Endian.Little, wordorder=Endian.Little
                )
                current_limit = decoder.decode_32bit_int()
                log.debug(f"Aktuelle Leistungsbegrenzung (Fallback) aus Register {register}: {current_limit} W")
                return current_limit
        except Exception as e:
            log.error(f"Fehler beim Lesen der Leistungsbegrenzung: {e}")
            raise


# Beschreibung der Konfigurationskomponente
component_descriptor = ComponentDescriptor(configuration_factory=SolaredgeBatSetup)
Zuletzt geändert von Basti am So Dez 22, 2024 2:25 pm, insgesamt 8-mal geändert.
Montair
Beiträge: 208
Registriert: Mi Feb 17, 2021 11:09 am
Has thanked: 1 time
Been thanked: 4 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Montair »

Bei E3DC müsste das eigentlich recht einfach zu übernehmen sein, E3DC-Control kann das ja auch schon mWn. Vielleicht kann sich ein Entwickler mal mit Eberhard @eba-M via github kurzschließen?

Ich bin leider auch nicht für programmiertechnisches zu gebrauchen. Er hat ja schon mit E3DC-Control ein Steuerprogramm des E3DC auf die Beine gestellt, was auch die WB Steuerung übernimmt, und er ist sowieso dabei für seine Software eine openWB Übernahme mit aufzunehmen (meine seine Tochter hat eine openWB). Wenn er das von der einen Seite macht, kann man das sicherlich auch von der anderen Seite übernehmen.

https://github.com/Eba-M/E3DC-Control
PV: 29,99kWp an E3DC S10 Pro 45,5kWh, div. Hoymiles ModulWR (HMT-2250, HMT-1800, HM-1500, HM-800), DTU Pro-S/Pro. Wärmepumpe.

openWB Standalone, 2 x openWB Series2 standard+, alle Software 2.x
Gero
Beiträge: 3650
Registriert: Sa Feb 20, 2021 9:55 am
Has thanked: 10 times
Been thanked: 99 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Gero »

Die Herausforderung sind die unterschiedlichen Programmiersprachen. E3dc-Control basiert auf der Beispielimplementierung von e3dc und die ist in C. Das muss man kompilieren, die openWB ist in Skriptsprachen implementiert, die ohne kompilieren laufen.

Einer der Gründe, warum das in C ist, ist vielleicht die mitgelieferte AES-Bibliothek, die für die verschlüsselte Kommunikation mit dem S10 benötigt wird. Und das ist das, was ich mit erhöhtem Implementierungsaufwand meine. Für eine nahtlose Integration in die openWB müsste nicht nur die Ansteuerung an sich, sondern auch noch der AES-Part portiert werden. Oder man findet eine passende in Python o.ä. Oder man bei C, muss dafür dann aber die Installationsrroutine für das e3dc-Modul um eine Kompilier-Routine erweitern.

Wesentlich einfacher wäre eine REST-API, da kommt die Verschlüsselung über https und muss nicht separat implementiert werden.
openWB-series2, openWB-Buchse, E3/DC S10pro+19.5kWh, 30kWp Ost-Süd, Model 3 und Ion
Basti
Beiträge: 94
Registriert: Di Feb 21, 2023 3:28 pm
Has thanked: 1 time
Been thanked: 11 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Basti »

So kleine Anpassung , es muss zuerst der Control Mode gesetzt werden . Erst dann können die Werte gesetzt werden. Ich habe das angepasst , das Schreiben erfolgt jetzt via float32 und little endian . Habe Kommentare hinzugefügt damit der Code besser lesbar ist und evtl. Leute helfen können .Adressen ausgelagert für die Übersicht .


Habt Nachsicht , ich mach das gerade alles am Handy 🙈
Benutzeravatar
mrinas
Beiträge: 2278
Registriert: Mi Jan 29, 2020 10:12 pm
Has thanked: 36 times
Been thanked: 34 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von mrinas »

Ich hätte auch eine Steuerung für SMA hybrid als PR fertig, mir fehlt aktuell lediglich die Sonne um das Testen zu können. Batterie ist zuletzt chronisch leer.
15,2kWp SMA (SB4000TL-21, SB3.0, STP6.0-SE + BYD HVS, EnergyMeter), openWB Standard+, openWB Pro, Smart #1 (ersetzt den e2008), Tesla Model Y LR.
Robeck
Beiträge: 106
Registriert: Sa Jan 23, 2021 11:12 am
Has thanked: 12 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Robeck »

Ihr müsst E3DC machen, das ist der hot shit :mrgreen:

Spass beiseite, ich muss euch bewundern, für mich ist das chinesisch.....
15,6 kwp PV, E3DC Speicher mit 18kw, Tesla Model 3 und eine openWB Pro+
Basti
Beiträge: 94
Registriert: Di Feb 21, 2023 3:28 pm
Has thanked: 1 time
Been thanked: 11 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Basti »

Naja theoretisch kannst du über Menü S10 auf sunspec umstellen und dann sollte auch das solaredge / sunspec Script funktionieren . Aber mit E3DC habe ich mich lange nicht mehr beschäftigt .

Ansonsten Python-e3dc über rscp
Gero
Beiträge: 3650
Registriert: Sa Feb 20, 2021 9:55 am
Has thanked: 10 times
Been thanked: 99 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von Gero »

Basti hat geschrieben: So Dez 22, 2024 8:44 am Naja theoretisch kannst du über Menü S10 auf sunspec umstellen und dann sollte auch das solaredge / sunspec Script funktionieren .
Nur lesend. Schreiben geht nur per rscp.
openWB-series2, openWB-Buchse, E3/DC S10pro+19.5kWh, 30kWp Ost-Süd, Model 3 und Ion
hominidae
Beiträge: 1452
Registriert: Di Sep 03, 2019 4:13 pm
Has thanked: 23 times
Been thanked: 21 times

Re: Laden ohne die Hausbatterie zu leeren

Beitrag von hominidae »

mrinas hat geschrieben: Sa Dez 21, 2024 7:06 pm Ich hätte auch eine Steuerung für SMA hybrid als PR fertig, mir fehlt aktuell lediglich die Sonne um das Testen zu können. Batterie ist zuletzt chronisch leer.
Vielleicht Off-Topic, aber bei SMA und sunspec muss man wohl aufpassen, welche Register beschrieben werden.
Im victron forum - da das ESS nun auch SMA -WR dynamisch regeln kann - ist das Thema aufgetaucht.
Wer die falschen Register zu oft nutzt, kann den Flash-Speicher des WR schrotten oder erreichen, das der WR sich tot stellt.

Ausserdem muss man evtl. aufpassen, ob der SMA Homemanager mit verbaut ist oder nicht, wenn man den WR ansprechen will.

Einige Infos, hier: https://community.victronenergy.com/t/v ... ry/14183/1
Antworten