diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/__init__.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/config.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/config.py new file mode 100644 index 0000000000..b05c3b6ac0 --- /dev/null +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/config.py @@ -0,0 +1,38 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class ZCS3PConfiguration: + def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: int = 502): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + + +class ZCS3P: + def __init__(self, + name: str = "Azzurro - ZCS 3PH 12KTL", + type: str = "azzurro_zcs_3p", + id: int = 0, + configuration: ZCS3PConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or ZCS3PConfiguration() + + +class ZCSPvInverterConfiguration: + def __init__(self): + pass + + +class ZCSPvInverterSetup(ComponentSetup[ZCSPvInverterConfiguration]): + def __init__(self, + name: str = "ZCS Azzurro Wechselrichter", + type: str = "pv_inverter", + id: int = 0, + configuration: ZCSPvInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ZCSPvInverterConfiguration()) diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/device.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/device.py new file mode 100644 index 0000000000..a276877b47 --- /dev/null +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/device.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.azzurro_zcs.azzurro_zcs_3p.config import ZCS3P, ZCSPvInverterSetup +from modules.devices.azzurro_zcs.azzurro_zcs_3p.pv_inverter import ZCSPvInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: ZCS3P): + client = None + + def create_pv_inverter_component(component_config: ZCSPvInverterSetup): + nonlocal client + return ZCSPvInverter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + client=client) + + def update_components(components: Iterable[ZCSPvInverter]): + nonlocal client + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + pv_inverter=create_pv_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=ZCS3P) diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/pv_inverter.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/pv_inverter.py new file mode 100644 index 0000000000..8b878fac4b --- /dev/null +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs_3p/pv_inverter.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +import logging +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_inverter_value_store +from modules.devices.azzurro_zcs.azzurro_zcs_3p.config import ZCSPvInverterSetup + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + modbus_id: int + + +class ZCSPvInverter(AbstractInverter): + def __init__(self, component_config: ZCSPvInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + power = exported = 0 + currents = None + try: + power = self.client.read_holding_registers(0x0485, ModbusDataType.INT_16, unit=self.__modbus_id)*10 + exported = self.client.read_holding_registers(0x0684, ModbusDataType.UINT_32, unit=self.__modbus_id)*10 + currents = [ + self.client.read_holding_registers(0x48E, ModbusDataType.INT_16, unit=self.__modbus_id)*0.01, + self.client.read_holding_registers(0x499, ModbusDataType.INT_16, unit=self.__modbus_id)*0.01, + self.client.read_holding_registers(0x4A4, ModbusDataType.INT_16, unit=self.__modbus_id)*0.01 + ] + except Exception: + log.debug("Modbus could not be read.") + + inverter_state = InverterState( + currents=currents, + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ZCSPvInverterSetup) diff --git a/packages/modules/devices/chint/__init__ .py b/packages/modules/devices/chint/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/chint/chint/__init__.py b/packages/modules/devices/chint/chint/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/chint/chint/config.py b/packages/modules/devices/chint/chint/config.py new file mode 100644 index 0000000000..377124ab62 --- /dev/null +++ b/packages/modules/devices/chint/chint/config.py @@ -0,0 +1,42 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class CHINTConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 8899): + self.ip_address = ip_address + self.port = port + + +@auto_str +class CHINT: + def __init__(self, + name: str = "CHINT", + type: str = "chint", + id: int = 0, + configuration: CHINTConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or CHINTConfiguration() + + +@auto_str +class CHINTCounterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class CHINTCounterSetup(ComponentSetup[CHINTCounterConfiguration]): + def __init__(self, + name: str = "CHINT DTSU666 Zähler", + type: str = "counter", + id: int = 0, + configuration: CHINTCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or CHINTCounterConfiguration()) diff --git a/packages/modules/devices/chint/chint/counter.py b/packages/modules/devices/chint/chint/counter.py new file mode 100644 index 0000000000..8b0189d453 --- /dev/null +++ b/packages/modules/devices/chint/chint/counter.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +import logging +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_counter_value_store +from modules.devices.chint.chint.config import CHINTCounterSetup + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + +class CHINTCounter(AbstractCounter): + def __init__(self, component_config: CHINTCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.__modbus_id = self.component_config.configuration.modbus_id + + def update(self): + powers = voltages = currents = power_factors = None + imported_ep = exported_ep = power = frequency = 0 + irat = self.client.read_holding_registers(0x0006, ModbusDataType.INT_16, unit=self.__modbus_id) + urat = self.client.read_holding_registers(0x0007, ModbusDataType.INT_16, unit=self.__modbus_id) + power_ratio = urat*0.1*irat*0.1 + + frequency = self.client.read_holding_registers(0x2044, ModbusDataType.FLOAT_32, unit=self.__modbus_id)/100 + power = self.client.read_holding_registers(0x2012, + ModbusDataType.FLOAT_32, unit=self.__modbus_id) * power_ratio + powers = [self.client.read_holding_registers(reg, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * power_ratio + for reg in [0x2014, 0x2016, 0x2018]] + voltage_ratio = urat*0.1*0.1 + voltages = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * voltage_ratio + for reg in [0x2006, 0x2008, 0x200A]] + current_ratio = irat*0.001 + currents = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * current_ratio + for reg in [0x200C, 0x200E, 0x2010]] + power_factors = [self.client.read_holding_registers(reg, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * 0.001 + for reg in [0x202C, 0x202E, 0x2030]] + ep_ratio = irat * urat * 100 + imported_ep = self.client.read_holding_registers(0x401E, + ModbusDataType.FLOAT_32, unit=self.__modbus_id) * ep_ratio + exported_ep = self.client.read_holding_registers(0x4028, + ModbusDataType.FLOAT_32, unit=self.__modbus_id) * ep_ratio + + counter_state = CounterState( + currents=currents, + imported=imported_ep, + exported=exported_ep, + power=power, + frequency=frequency, + power_factors=power_factors, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=CHINTCounterSetup) diff --git a/packages/modules/devices/chint/chint/device.py b/packages/modules/devices/chint/chint/device.py new file mode 100644 index 0000000000..ff7b982add --- /dev/null +++ b/packages/modules/devices/chint/chint/device.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.chint.chint.config import CHINT, CHINTCounterSetup +from modules.devices.chint.chint.counter import CHINTCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: CHINT): + client = None + + def create_counter_component(component_config: CHINTCounterSetup): + nonlocal client + return CHINTCounter(component_config=component_config, client=client) + + def update_components(components: Iterable[Union[CHINTCounter]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=CHINT) diff --git a/packages/modules/devices/chint/vendor.py b/packages/modules/devices/chint/vendor.py new file mode 100644 index 0000000000..fa72f537e2 --- /dev/null +++ b/packages/modules/devices/chint/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "CHINT" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor)