diff --git a/packages/modules/devices/thermia/__init__ .py b/packages/modules/devices/thermia/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/thermia/thermia/__init__.py b/packages/modules/devices/thermia/thermia/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/thermia/thermia/config.py b/packages/modules/devices/thermia/thermia/config.py new file mode 100644 index 0000000000..e597e10826 --- /dev/null +++ b/packages/modules/devices/thermia/thermia/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 ThermiaConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502, modbus_id: int = 1): + self.ip_address = ip_address + self.port = port + self.modbus_id = modbus_id + + +@auto_str +class Thermia: + def __init__(self, + name: str = "Thermia", + type: str = "thermia", + id: int = 0, + configuration: ThermiaConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or ThermiaConfiguration() + + +@auto_str +class ThermiaCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class ThermiaCounterSetup(ComponentSetup[ThermiaCounterConfiguration]): + def __init__(self, + name: str = "Thermia Zähler", + type: str = "counter", + id: int = 0, + configuration: ThermiaCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ThermiaCounterConfiguration()) diff --git a/packages/modules/devices/thermia/thermia/counter.py b/packages/modules/devices/thermia/thermia/counter.py new file mode 100644 index 0000000000..20d39d773e --- /dev/null +++ b/packages/modules/devices/thermia/thermia/counter.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +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.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.thermia.thermia.config import ThermiaCounterSetup +from pymodbus.constants import Endian + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + modbus_id: int + + +class ThermiaCounter(AbstractCounter): + def __init__(self, component_config: ThermiaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + with self.client: + voltages = [val / 100 for val in self.client.read_input_registers( + 72, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + powers = [val / 1 for val in self.client.read_input_registers( + 78, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + power = sum(powers) + currents = [(val / 100) for val in self.client.read_input_registers( + 69, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + imported = self.client.read_input_registers( + 83, ModbusDataType.INT_32, wordorder=Endian.Little, + unit=self.modbus_id) * 100 + exported = 0 + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ThermiaCounterSetup) diff --git a/packages/modules/devices/thermia/thermia/device.py b/packages/modules/devices/thermia/thermia/device.py new file mode 100644 index 0000000000..0f85723c84 --- /dev/null +++ b/packages/modules/devices/thermia/thermia/device.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.thermia.thermia.config import Thermia, ThermiaCounterSetup +from modules.devices.thermia.thermia.counter import ThermiaCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Thermia): + client = None + + def create_counter_component(component_config: ThermiaCounterSetup): + nonlocal client + return ThermiaCounter(component_config, device_id=device_config.id, + client=client, modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[Union[ThermiaCounter]]): + with client: + for component in components: + 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=Thermia) diff --git a/packages/modules/devices/thermia/vendor.py b/packages/modules/devices/thermia/vendor.py new file mode 100644 index 0000000000..911c58a237 --- /dev/null +++ b/packages/modules/devices/thermia/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 = "Thermia" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor)