Skip to content

Commit

Permalink
add solarmax bat
Browse files Browse the repository at this point in the history
  • Loading branch information
LKuemmel committed Jul 24, 2023
1 parent 8258d14 commit bfea4e2
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 62 deletions.
20 changes: 19 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@


class UpdateConfig:
DATASTORE_VERSION = 16
DATASTORE_VERSION = 17
valid_topic = [
"^openWB/bat/config/configured$",
"^openWB/bat/set/charging_power_left$",
Expand Down Expand Up @@ -778,3 +778,21 @@ def upgrade_datastore_15(self) -> None:
except Exception:
log.exception(f"Logfile {file} konnte nicht konvertiert werden.")
Pub().pub("openWB/system/datastore_version", 16)

def upgrade_datastore_16(self) -> None:
for topic_device, payload in self.all_received_topics.items():
if re.search("openWB/system/device/[0-9]+/config", topic_device) is not None:
payload_device = decode_payload(payload)
index = get_index(topic_device)
if payload_device["type"] == "solarmax":
for topic_component, payload_component in self.all_received_topics.items():
if re.search(f"^openWB/system/device/{index}/component/[0-9]+/config$",
topic_component) is not None:
payload_inverter = decode_payload(payload_component)
if payload_inverter["type"] == "inverter":
payload_inverter["configuration"]["modbus_id"] = payload_device["configuration"][
"modbus_id"]
payload_device["configuration"].pop("modbus_id")
Pub().pub(topic_device.replace("openWB/", "openWB/set/"), payload_device)
Pub().pub(topic_component.replace("openWB/", "openWB/set/"), payload_inverter)
Pub().pub("openWB/system/datastore_version", 17)
34 changes: 34 additions & 0 deletions packages/modules/devices/solarmax/bat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
from dataclass_utils import dataclass_from_dict
from modules.common.component_state import BatState
from modules.common.component_type import ComponentDescriptor
from modules.common.fault_state import ComponentInfo
from modules.common.modbus import ModbusDataType, ModbusTcpClient_
from modules.common.simcount import SimCounter
from modules.common.store import get_bat_value_store
from modules.devices.solarmax.config import SolarmaxBatSetup


class SolarmaxBat:
def __init__(self, device_id: int, component_config: SolarmaxBatSetup) -> None:
self.__device_id = device_id
self.component_config = dataclass_from_dict(SolarmaxBatSetup, component_config)
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher")
self.store = get_bat_value_store(self.component_config.id)
self.component_info = ComponentInfo.from_component_config(self.component_config)

def update(self, client: ModbusTcpClient_) -> None:
power = client.read_holding_registers(114, ModbusDataType.INT_32, unit=1)
soc = client.read_holding_registers(122, ModbusDataType.INT_16, unit=1)
imported, exported = self.sim_counter.sim_count(power)

bat_state = BatState(
power=power,
soc=soc,
imported=imported,
exported=exported
)
self.store.set(bat_state)


component_descriptor = ComponentDescriptor(configuration_factory=SolarmaxBatSetup)
19 changes: 16 additions & 3 deletions packages/modules/devices/solarmax/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@


class SolarmaxConfiguration:
def __init__(self, ip_address: Optional[str] = None, modbus_id: int = 1):
def __init__(self, ip_address: Optional[str] = None):
self.ip_address = ip_address
self.modbus_id = modbus_id


class Solarmax:
Expand All @@ -21,11 +20,25 @@ def __init__(self,
self.configuration = configuration or SolarmaxConfiguration()


class SolarmaxInverterConfiguration:
class SolarmaxBatConfiguration:
def __init__(self):
pass


class SolarmaxBatSetup(ComponentSetup[SolarmaxBatConfiguration]):
def __init__(self,
name: str = "Solarmax MAX.STORAGE / MAX.STORAGE Ultimate Speicher",
type: str = "bat",
id: int = 0,
configuration: SolarmaxBatConfiguration = None) -> None:
super().__init__(name, type, id, configuration or SolarmaxBatConfiguration())


class SolarmaxInverterConfiguration:
def __init__(self, modbus_id: int = 1):
self.modbus_id = modbus_id


class SolarmaxInverterSetup(ComponentSetup[SolarmaxInverterConfiguration]):
def __init__(self,
name: str = "Solarmax Wechselrichter",
Expand Down
79 changes: 31 additions & 48 deletions packages/modules/devices/solarmax/device.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,44 @@
#!/usr/bin/env python3
import logging
from typing import Dict, Optional, List, Union
from typing import Iterable, Optional, List, Union

from dataclass_utils import dataclass_from_dict
from helpermodules.cli import run_using_positional_cli_args
from modules.common import modbus
from modules.common.abstract_device import AbstractDevice, DeviceDescriptor
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater
from modules.devices.solarmax import inverter
from modules.devices.solarmax.config import Solarmax, SolarmaxConfiguration
from modules.devices.solarmax.bat import SolarmaxBat
from modules.devices.solarmax.config import Solarmax, SolarmaxBatSetup, SolarmaxConfiguration, SolarmaxInverterSetup

log = logging.getLogger(__name__)


class Device(AbstractDevice):
COMPONENT_TYPE_TO_CLASS = {
"inverter": inverter.SolarmaxInverter
}

def __init__(self, device_config: Union[Dict, Solarmax]) -> None:
self.components = {}
try:
self.device_config = dataclass_from_dict(Solarmax, device_config)
self.client = modbus.ModbusTcpClient_(self.device_config.configuration.ip_address, 502)
except Exception:
log.exception("Fehler im Modul "+self.device_config.name)

def add_component(self, component_config: dict) -> None:
if isinstance(component_config, Dict):
component_type = component_config["type"]
else:
component_type = component_config.type
component_config = dataclass_from_dict(COMPONENT_TYPE_TO_MODULE[
component_type].component_descriptor.configuration_factory, component_config)
if component_type in self.COMPONENT_TYPE_TO_CLASS:
self.components["component"+str(component_config.id)] = (self.COMPONENT_TYPE_TO_CLASS[component_type](
self.device_config.id,
component_config, self.client,
self.device_config.configuration.modbus_id))
else:
raise Exception(
"illegal component type " + component_type + ". Allowed values: " +
','.join(self.COMPONENT_TYPE_TO_CLASS.keys())
)

def update(self) -> None:
log.debug("Start device reading " + str(self.components))
if self.components:
for component in self.components:
# Auch wenn bei einer Komponente ein Fehler auftritt, sollen alle anderen noch ausgelesen werden.
with SingleComponentUpdateContext(self.components[component].component_info):
self.components[component].update()
else:
log.warning(
self.device_config.name +
": Es konnten keine Werte gelesen werden, da noch keine oder zu viele Komponenten konfiguriert wurden."
)
def create_device(device_config: Solarmax):
def create_bat_component(component_config: SolarmaxBatSetup):
return SolarmaxBat(device_config.id, component_config)

def create_inverter_component(component_config: SolarmaxInverterSetup):
return inverter.SolarmaxInverter(device_config.id, component_config)

def update_components(components: Iterable[Union[SolarmaxBat, inverter.SolarmaxInverter]]):
with client as c:
for component in components:
with SingleComponentUpdateContext(component.component_info):
component.update(c)

try:
client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, 502)
except Exception:
log.exception("Fehler in create_device")
return ConfigurableDevice(
device_config=device_config,
component_factory=ComponentFactoryByType(
bat=create_bat_component,
inverter=create_inverter_component,
),
component_updater=MultiComponentUpdater(update_components)
)


COMPONENT_TYPE_TO_MODULE = {
Expand All @@ -64,7 +47,7 @@ def update(self) -> None:


def read_legacy(component_type: str, ip_address: str, num: Optional[int] = None) -> None:
dev = Device(Solarmax(configuration=SolarmaxConfiguration(ip_address=ip_address)))
dev = create_device(Solarmax(configuration=SolarmaxConfiguration(ip_address=ip_address)))
if component_type in COMPONENT_TYPE_TO_MODULE:
component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory()
else:
Expand Down
15 changes: 5 additions & 10 deletions packages/modules/devices/solarmax/inverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
from typing import Dict, Union

from dataclass_utils import dataclass_from_dict
from modules.common import modbus
from modules.common.component_state import InverterState
from modules.common.component_type import ComponentDescriptor
from modules.common.fault_state import ComponentInfo
from modules.common.modbus import ModbusDataType
from modules.common.modbus import ModbusDataType, ModbusTcpClient_
from modules.common.simcount import SimCounter
from modules.common.store import get_inverter_value_store
from modules.devices.solarmax.config import SolarmaxInverterSetup
Expand All @@ -15,20 +14,16 @@
class SolarmaxInverter:
def __init__(self,
device_id: int,
component_config: Union[Dict, SolarmaxInverterSetup],
tcp_client: modbus.ModbusTcpClient_,
modbus_id: int) -> None:
component_config: Union[Dict, SolarmaxInverterSetup]) -> None:
self.__device_id = device_id
self.__modbus_id = modbus_id
self.component_config = dataclass_from_dict(SolarmaxInverterSetup, component_config)
self.__tcp_client = tcp_client
self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv")
self.store = get_inverter_value_store(self.component_config.id)
self.component_info = ComponentInfo.from_component_config(self.component_config)

def update(self) -> None:
with self.__tcp_client:
power = self.__tcp_client.read_holding_registers(4151, ModbusDataType.UINT_32, unit=self.__modbus_id) * -1
def update(self, client: ModbusTcpClient_) -> None:
power = client.read_holding_registers(4151, ModbusDataType.UINT_32,
unit=self.component_config.configuration.modbus_id) * -1
power = power / 10
_, exported = self.sim_counter.sim_count(power)
inverter_state = InverterState(
Expand Down

0 comments on commit bfea4e2

Please sign in to comment.