From 4c628dad6d6382c9bcbd7c35ed94b69e8ed80305 Mon Sep 17 00:00:00 2001 From: laverman Date: Sat, 26 Aug 2023 16:49:32 +0200 Subject: [PATCH] rename pellets to biomass and fix pylint findings --- .github/workflows/ci.yaml | 2 +- Makefile | 8 +- example.py | 15 +- pyproject.toml | 5 +- pysolarfocus/__init__.py | 33 +-- pysolarfocus/component_factory.py | 43 ++-- pysolarfocus/components/base/component.py | 205 +++++++++--------- pysolarfocus/components/base/data_value.py | 94 ++++---- pysolarfocus/components/base/enums.py | 12 +- pysolarfocus/components/base/part.py | 10 +- .../components/base/performance_calculator.py | 24 +- .../components/base/register_slice.py | 9 +- pysolarfocus/components/biomass_boiler.py | 37 ++++ pysolarfocus/components/boiler.py | 23 +- pysolarfocus/components/buffer.py | 36 +-- pysolarfocus/components/fresh_water_module.py | 5 +- pysolarfocus/components/heat_pump.py | 45 ++-- pysolarfocus/components/heating_circuit.py | 47 ++-- pysolarfocus/components/pellets_boiler.py | 35 --- pysolarfocus/components/photovoltaic.py | 22 +- pysolarfocus/components/solar.py | 29 +-- pysolarfocus/modbus_wrapper.py | 18 +- tests/test_component.py | 41 ++-- 23 files changed, 417 insertions(+), 381 deletions(-) create mode 100644 pysolarfocus/components/biomass_boiler.py delete mode 100644 pysolarfocus/components/pellets_boiler.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 625cb82..be7a2dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest ] - python-version: [ "3.9", "3.10", "3.11" ] + python-version: [ "3.11" ] steps: - name: Check out ${{ github.sha }} from repository ${{ github.repository }} uses: actions/checkout@v3 diff --git a/Makefile b/Makefile index 69c0a0d..69289b2 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ check: check-pylint check-black check-pylint: - @poetry run pylint pysolarfocus/*.py + @poetry run pylint pysolarfocus/* check-black: - @poetry run black --check pysolarfocus/*.py + @poetry run black --check pysolarfocus/* codefix: - @poetry run isort pysolarfocus/*.py - @poetry run black pysolarfocus/*.py + @poetry run isort pysolarfocus/* + @poetry run black pysolarfocus/* test: @poetry run pytest diff --git a/example.py b/example.py index 9627482..8cff07d 100644 --- a/example.py +++ b/example.py @@ -1,10 +1,9 @@ -from pysolarfocus import SolarfocusAPI,Systems,ApiVersions +from pysolarfocus import SolarfocusAPI, Systems, ApiVersions # Create the Solarfocus API client solarfocus = SolarfocusAPI( - ip="solarfocus", # adapt IP-Address - system=Systems.Vampair, # change to Systems.Therminator - api_version=ApiVersions.V_23_020) # select Solarfocus version + ip="solarfocus", system=Systems.VAMPAIR, api_version=ApiVersions.V_23_020 # adapt IP-Address # change to Systems.Therminator +) # select Solarfocus version solarfocus.connect() # Fetch the values @@ -18,12 +17,12 @@ print("\n") print(solarfocus.buffers[0]) print("\n") -if solarfocus.system is Systems.Therminator: - print(solarfocus.pelletsboiler) -if solarfocus.system is Systems.Vampair: +if solarfocus.system is Systems.THERMINATOR: + print(solarfocus.biomassboiler) +if solarfocus.system is Systems.VAMPAIR: print(solarfocus.heatpump) print("\n") print(solarfocus.photovoltaic) print("\n") print(solarfocus.solar) -print("\n") \ No newline at end of file +print("\n") diff --git a/pyproject.toml b/pyproject.toml index e445b8b..eadaefa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pysolarfocus" -version = "3.7.0.1" +version = "4.0.0" description = "Unofficial, local Solarfocus client" authors = ["Jeroen Laverman "] license = "Apache-2.0" @@ -77,6 +77,9 @@ disable = [ "too-many-boolean-expressions", "unused-argument", "wrong-import-order", + "missing-class-docstring", + "missing-function-docstring", + "logging-fstring-interpolation" ] enable = [ #"useless-suppression", # temporarily every now and then to clean them up diff --git a/pysolarfocus/__init__.py b/pysolarfocus/__init__.py index bef42af..f2b2cf7 100644 --- a/pysolarfocus/__init__.py +++ b/pysolarfocus/__init__.py @@ -15,9 +15,9 @@ class Systems(str, Enum): Supported systems by this library """ - Vampair = "Vampair" - Therminator = "Therminator" - Ecotop = "Ecotop" + VAMPAIR = "Vampair" + THERMINATOR = "Therminator" + ECOTOP = "Ecotop" class ApiVersions(str, Enum): @@ -31,6 +31,7 @@ class ApiVersions(str, Enum): V_23_020 = "23.020" def greater_or_equal(self, api_version) -> bool: + """Compare given version with own version.""" return version.parse(self.value) >= version.parse(api_version) @@ -57,7 +58,7 @@ def __init__( buffer_count: int = 1, boiler_count: int = 1, fresh_water_module_count: int = 1, - system: Systems = Systems.Vampair, + system: Systems = Systems.VAMPAIR, port: int = PORT, slave_id: int = SLAVE_ID, api_version: ApiVersions = ApiVersions.V_21_140, @@ -84,7 +85,7 @@ def __init__( # Single components self.heatpump = self.__factory.heatpump(system) self.photovoltaic = self.__factory.photovoltaic(system) - self.pelletsboiler = self.__factory.pelletsboiler(system, api_version) + self.biomassboiler = self.__factory.pelletsboiler(system, api_version) self.solar = self.__factory.solar(system) def connect(self): @@ -104,7 +105,7 @@ def update(self) -> bool: and self.update_boiler() and self.update_heatpump() and self.update_photovoltaic() - and self.update_pelletsboiler() + and self.update_biomassboiler() and self.update_solar() and self.update_fresh_water_modules() ): @@ -148,19 +149,19 @@ def update_photovoltaic(self) -> bool: """Read values from Heating System""" return self.photovoltaic.update() - def update_pelletsboiler(self) -> bool: - """Read values from Pellets boiler""" - return self.pelletsboiler.update() + def update_biomassboiler(self) -> bool: + """Read values from biomass boiler""" + return self.biomassboiler.update() def update_solar(self) -> bool: """Read values from Solar""" return self.solar.update() def __repr__(self) -> str: - s = ["-" * 50] - s.append(f"{self.__class__.__name__}, v{__version__}") - s.append("-" * 50) - s.append(f"+ System: {self.system.name}") - s.append(f"+ Version: {self._api_version}") - s.append("-" * 50) - return "\n".join(s) + message = ["-" * 50] + message.append(f"{self.__class__.__name__}, v{__version__}") + message.append("-" * 50) + message.append(f"+ System: {self.system.value}") + message.append(f"+ Version: {self._api_version.value}") + message.append("-" * 50) + return "\n".join(message) diff --git a/pysolarfocus/component_factory.py b/pysolarfocus/component_factory.py index a21b8dd..08a71eb 100644 --- a/pysolarfocus/component_factory.py +++ b/pysolarfocus/component_factory.py @@ -1,12 +1,13 @@ +"""Solarfocus component factory""" from . import ApiVersions, Systems -from .components.boiler import * -from .components.buffer import * -from .components.fresh_water_module import * -from .components.heat_pump import * -from .components.heating_circuit import * -from .components.pellets_boiler import * -from .components.photovoltaic import * -from .components.solar import * +from .components.boiler import Boiler +from .components.buffer import Buffer, TherminatorBuffer +from .components.fresh_water_module import FreshWaterModule +from .components.heat_pump import HeatPump +from .components.heating_circuit import HeatingCircuit, TherminatorHeatingCircuit +from .components.biomass_boiler import BiomassBoiler +from .components.photovoltaic import Photovoltaic +from .components.solar import Solar from .modbus_wrapper import ModbusConnector @@ -20,10 +21,10 @@ def heating_circuit(self, system: Systems, count: int) -> list[HeatingCircuit]: heating_circuits = [] for i in range(count): input, holding = input_addresses[i], holding_addresses[i] - if system == Systems.Therminator or system == Systems.Ecotop: - heating_circuit = TherminatorHeatingCircuit(input, holding)._initialize(self.__modbus_connector) + if system in [Systems.THERMINATOR, Systems.ECOTOP]: + heating_circuit = TherminatorHeatingCircuit(input, holding).initialize(self.__modbus_connector) else: - heating_circuit = HeatingCircuit(input, holding)._initialize(self.__modbus_connector) + heating_circuit = HeatingCircuit(input, holding).initialize(self.__modbus_connector) heating_circuits.append(heating_circuit) return heating_circuits @@ -33,7 +34,7 @@ def boiler(self, system: Systems, count: int) -> list[Boiler]: boilers = [] for i in range(count): input, holding = input_addresses[i], holding_addresses[i] - boilers.append(Boiler(input, holding)._initialize(self.__modbus_connector)) + boilers.append(Boiler(input, holding).initialize(self.__modbus_connector)) return boilers def buffer(self, system: Systems, count: int, api_version: ApiVersions) -> list[Buffer]: @@ -48,10 +49,10 @@ def buffer(self, system: Systems, count: int, api_version: ApiVersions) -> list[ holding = holding_addresses[i] else: holding = -1 - if system == Systems.Therminator or system == Systems.Ecotop: - buffer = TherminatorBuffer(input)._initialize(self.__modbus_connector) + if system in [Systems.THERMINATOR, Systems.ECOTOP]: + buffer = TherminatorBuffer(input).initialize(self.__modbus_connector) else: - buffer = Buffer(input, holding, api_version=api_version)._initialize(self.__modbus_connector) + buffer = Buffer(input, holding, api_version=api_version).initialize(self.__modbus_connector) buffers.append(buffer) return buffers @@ -60,17 +61,17 @@ def fresh_water_modules(self, system: Systems, count: int) -> list[FreshWaterMod fresh_water_modules = [] for i in range(count): input = input_addresses[i] - fresh_water_modules.append(FreshWaterModule(input)._initialize(self.__modbus_connector)) + fresh_water_modules.append(FreshWaterModule(input).initialize(self.__modbus_connector)) return fresh_water_modules def heatpump(self, system: Systems) -> HeatPump: - return HeatPump()._initialize(self.__modbus_connector) + return HeatPump().initialize(self.__modbus_connector) def photovoltaic(self, system: Systems) -> Photovoltaic: - return Photovoltaic()._initialize(self.__modbus_connector) + return Photovoltaic().initialize(self.__modbus_connector) - def pelletsboiler(self, system: Systems, api_version: ApiVersions) -> PelletsBoiler: - return PelletsBoiler(api_version=api_version, system=system)._initialize(self.__modbus_connector) + def pelletsboiler(self, system: Systems, api_version: ApiVersions) -> BiomassBoiler: + return BiomassBoiler(api_version=api_version, system=system).initialize(self.__modbus_connector) def solar(self, system: Systems) -> Solar: - return Solar()._initialize(self.__modbus_connector) + return Solar().initialize(self.__modbus_connector) diff --git a/pysolarfocus/components/base/component.py b/pysolarfocus/components/base/component.py index c1d328b..2511186 100644 --- a/pysolarfocus/components/base/component.py +++ b/pysolarfocus/components/base/component.py @@ -1,6 +1,6 @@ +"""Solarfocus abstract component""" import logging - from ...modbus_wrapper import ModbusConnector from .data_value import DataValue from .enums import DataTypes, RegisterTypes @@ -8,8 +8,8 @@ from .register_slice import RegisterSlice -class Component(object): - def __init__(self,input_address:int,holding_address:int=-1)->None: +class Component: + def __init__(self, input_address: int, holding_address: int = -1) -> None: self.input_address = input_address self.input_count = 0 self.holding_address = holding_address @@ -19,191 +19,192 @@ def __init__(self,input_address:int,holding_address:int=-1)->None: self.__holding_values = None self.__performance_calculators = None self.__modbus = None - - def _initialize(self,modbus:ModbusConnector): + + def initialize(self, modbus: ModbusConnector): """ Initializes the absolute addresses of the DataValues and the count of the Component """ - + self.__modbus = modbus - - for _,value in self.__get_data_values(): - - if value.register_type == RegisterTypes.Input: - value._absolut_address = self.input_address + + for _, value in self.__get_data_values(): + + if value.register_type == RegisterTypes.INPUT: + value.absolut_address = self.input_address else: - value._absolut_address = self.holding_address + value.absolut_address = self.holding_address # Holding registers can write to the heating system - value._modbus = modbus - - #Dynamically calcuate how many registers have to be read - if len(self.__get_input_values()) > 0: - _,last_input_value = self.__get_input_values()[-1] - self.input_count = last_input_value.address+last_input_value.count - + value.modbus = modbus + + # Dynamically calcuate how many registers have to be read + if len(self.__get_input_values()) > 0: + _, last_input_value = self.__get_input_values()[-1] + self.input_count = last_input_value.address + last_input_value.count + if len(self.__get_holding_values()) > 0: - _,last_holding_value = self.__get_holding_values()[-1] - self.holding_count = last_holding_value.address+last_holding_value.count - - #Calculate the address slices we need to read - #This is necessary because the modbus protocol can block the read/write of some registers - #=> if one of these registers is between the start and end address of the read/write we need to skip it - self.__input_slices = Component._calculate_ranges([v for (n,v) in self.__get_input_values()]) - self.__holding_slices = Component._calculate_ranges([v for (n,v) in self.__get_holding_values()]) - + _, last_holding_value = self.__get_holding_values()[-1] + self.holding_count = last_holding_value.address + last_holding_value.count + + # Calculate the address slices we need to read + # This is necessary because the modbus protocol can block the read/write of some registers + # => if one of these registers is between the start and end address of the read/write we need to skip it + self.__input_slices = Component._calculate_ranges([v for (n, v) in self.__get_input_values()]) + self.__holding_slices = Component._calculate_ranges([v for (n, v) in self.__get_holding_values()]) + # Initialize has_performance_calculators self.__get_performance_calculators() - + return self - + @property - def holding_slices(self)->list[RegisterSlice]: + def holding_slices(self) -> list[RegisterSlice]: """ Returns the address slices of the holding registers """ return self.__holding_slices - + @property - def input_slices(self)->list[RegisterSlice]: + def input_slices(self) -> list[RegisterSlice]: """ Returns the address slices of the input registers """ return self.__input_slices - - @staticmethod - def _calculate_ranges(datavalues:list[DataValue])->list[RegisterSlice]: + + @staticmethod + def _calculate_ranges(datavalues: list[DataValue]) -> list[RegisterSlice]: slices = [] if len(datavalues) < 1: return slices - current_slice = RegisterSlice(datavalues[0].get_absolute_address(),datavalues[0].address,datavalues[-1].address+datavalues[-1].count) - for i,datavalue in enumerate(datavalues[:-1]): - if datavalue.address+datavalue.count != datavalues[i+1].address: - #A gap was found => define the range from the start of the current slice to the end of the current address+count - current_slice.count = (datavalue.address+datavalue.count)-current_slice.relative_address + current_slice = RegisterSlice(datavalues[0].get_absolute_address(), datavalues[0].address, datavalues[-1].address + datavalues[-1].count) + for i, datavalue in enumerate(datavalues[:-1]): + if datavalue.address + datavalue.count != datavalues[i + 1].address: + # A gap was found => define the range from the start of the current slice to the end of the current address+count + current_slice.count = (datavalue.address + datavalue.count) - current_slice.relative_address slices.append(current_slice) - #the new count is the distance between the next address and the last address - new_count = (datavalues[-1].address+datavalues[-1].count)-datavalues[i+1].address - current_slice = RegisterSlice(datavalues[i+1].get_absolute_address(),datavalues[i+1].address,new_count) + # the new count is the distance between the next address and the last address + new_count = (datavalues[-1].address + datavalues[-1].count) - datavalues[i + 1].address + current_slice = RegisterSlice(datavalues[i + 1].get_absolute_address(), datavalues[i + 1].address, new_count) slices.append(current_slice) return slices + @property - def has_input_address(self)->bool: + def has_input_address(self) -> bool: return self.input_address >= 0 and self.input_count > 0 - + @property - def has_holding_address(self)->bool: + def has_holding_address(self) -> bool: return self.holding_address >= 0 and self.holding_count > 0 - + @property - def has_performance_calculators(self)->bool: + def has_performance_calculators(self) -> bool: return len(self.__get_performance_calculators()) > 0 - - def __get_data_values(self)->list[tuple[str,DataValue]]: + + def __get_data_values(self) -> list[tuple[str, DataValue]]: """ Returns all DataValues of this Component """ if self.__data_values is None: - self.__data_values = [(k,v) for k,v in self.__dict__.items() if isinstance(v,DataValue)] + self.__data_values = [(k, v) for k, v in self.__dict__.items() if isinstance(v, DataValue)] return self.__data_values - - def __get_input_values(self)->list[tuple[str,DataValue]]: + + def __get_input_values(self) -> list[tuple[str, DataValue]]: """ Get sorted list of all DataValues with RegisterType Input """ if self.__input_values is None: - self.__input_values = [(k,v) for k,v in self.__get_data_values() if v.register_type == RegisterTypes.Input] - self.__input_values = sorted(self.__input_values,key=lambda item: item[1].address) + self.__input_values = [(k, v) for k, v in self.__get_data_values() if v.register_type == RegisterTypes.INPUT] + self.__input_values = sorted(self.__input_values, key=lambda item: item[1].address) return self.__input_values - - def __get_holding_values(self)->list[tuple[str,DataValue]]: + + def __get_holding_values(self) -> list[tuple[str, DataValue]]: """ Get sorted list of all DataValues with RegisterType Holding """ if self.__holding_values is None: - self.__holding_values = [(k,v) for k,v in self.__get_data_values() if v.register_type == RegisterTypes.Holding] - self.__holding_values = sorted(self.__holding_values,key=lambda item: item[1].address) + self.__holding_values = [(k, v) for k, v in self.__get_data_values() if v.register_type == RegisterTypes.HOLDING] + self.__holding_values = sorted(self.__holding_values, key=lambda item: item[1].address) return self.__holding_values - - def __get_performance_calculators(self)->list[tuple[str,PerformanceCalculator]]: + + def __get_performance_calculators(self) -> list[tuple[str, PerformanceCalculator]]: """ Returns all PerformanceCalculators of this Component """ if self.__performance_calculators is None: - self.__performance_calculators = [(k,v) for k,v in self.__dict__.items() if isinstance(v,PerformanceCalculator)] + self.__performance_calculators = [(k, v) for k, v in self.__dict__.items() if isinstance(v, PerformanceCalculator)] return self.__performance_calculators - + @staticmethod - def __unsigned_to_signed(n:int, byte_count:int)->int: - return int.from_bytes( - n.to_bytes(byte_count, "little", signed=False), "little", signed=True - ) - - def update(self)->bool: + def __unsigned_to_signed(number: int, byte_count: int) -> int: + return int.from_bytes(number.to_bytes(byte_count, "little", signed=False), "little", signed=True) + + def update(self) -> bool: """ Retrieve current values from the heating system """ - failed=False + failed = False if self.has_input_address: - read_success, registers = self.__modbus.read_input_registers(self.input_slices,self.input_count) + read_success, registers = self.__modbus.read_input_registers(self.input_slices, self.input_count) parsing_success = False if read_success: - parsing_success = self._parse(registers, RegisterTypes.Input) and read_success + parsing_success = self._parse(registers, RegisterTypes.INPUT) and read_success failed = not parsing_success and read_success or failed if failed: logging.error(f"Failed to read input registers of {self.__class__.__name__}") - + if self.has_holding_address: - read_success, registers = self.__modbus.read_holding_registers(self.holding_slices,self.holding_count) + read_success, registers = self.__modbus.read_holding_registers(self.holding_slices, self.holding_count) parsing_success = False if read_success: - parsing_success = self._parse(registers, RegisterTypes.Holding) and read_success + parsing_success = self._parse(registers, RegisterTypes.HOLDING) and read_success failed = not (parsing_success and read_success) or failed if failed: logging.error(f"Failed to read holding registers of {self.__class__.__name__}") return not failed - - def _parse(self,data:list[int],type:RegisterTypes)->bool: + + def _parse(self, data: list[int], type: RegisterTypes) -> bool: """ Dynamically assignes the values to the DataValues of this Component """ - if len(data) != (self.input_count if type == RegisterTypes.Input else self.holding_count): - logging.error(f"Data length does not match the expected length of {self.input_count if type == RegisterTypes.Input else self.holding_count} for {self.__class__.__name__}") + if len(data) != (self.input_count if type == RegisterTypes.INPUT else self.holding_count): + logging.error( + f"Data length does not match the expected length of {self.input_count if type == RegisterTypes.INPUT else self.holding_count} for {self.__class__.__name__}" + ) return False - + encountered_error = False - for name,value in self.__get_input_values() if type == RegisterTypes.Input else self.__get_holding_values(): + for name, value in self.__get_input_values() if type == RegisterTypes.INPUT else self.__get_holding_values(): try: # Multi-register values (UINT32, INT32) if value.count == 2: - _value = (data[value.address] << 16) + data[value.address+ 1] + _value = (data[value.address] << 16) + data[value.address + 1] else: _value = data[value.address] - + # Datatype - if value.type == DataTypes.INT: + if value.data_type == DataTypes.INT: _value = Component.__unsigned_to_signed(_value, value.count * 2) - - #Store - value.value = _value + + # Store + value.value = _value except: logging.exception(f"Error while parsing {name} of {self.__class__.__name__}") encountered_error = True return not encountered_error - + def __repr__(self) -> str: - s = ["="*12] - s.append(f"{self.__class__.__name__}") - s.append("="*12) + message = ["=" * 12] + message.append(f"{self.__class__.__name__}") + message.append("=" * 12) if self.has_input_address: - s.append("---Input:") - for name,value in self.__get_input_values(): - s.append(f"{name}| raw:{value.value} scaled:{value.scaled_value}") + message.append("---Input:") + for name, value in self.__get_input_values(): + message.append(f"{name}| raw:{value.value} scaled:{value.scaled_value}") if self.has_holding_address: - s.append("---Holding:") - for name,value in self.__get_holding_values(): - s.append(f"{name} | raw:{value.value} scaled:{value.scaled_value}") + message.append("---Holding:") + for name, value in self.__get_holding_values(): + message.append(f"{name} | raw:{value.value} scaled:{value.scaled_value}") if self.has_performance_calculators: - s.append("---Calculations:") - for name,value in self.__get_performance_calculators(): - s.append(f"{name} | raw:{value.value} scaled:{value.scaled_value}") - return "\n".join(s) + message.append("---Calculations:") + for name, value in self.__get_performance_calculators(): + message.append(f"{name} | raw:{value.value} scaled:{value.scaled_value}") + return "\n".join(message) diff --git a/pysolarfocus/components/base/data_value.py b/pysolarfocus/components/base/data_value.py index 78adbb0..6b35325 100644 --- a/pysolarfocus/components/base/data_value.py +++ b/pysolarfocus/components/base/data_value.py @@ -1,87 +1,87 @@ -import logging - +"""Solarfocus data value""" import logging -from .part import Part from ...modbus_wrapper import ModbusConnector from .enums import DataTypes, RegisterTypes +from .part import Part + class DataValue(Part): """ Abstraction of a certain relative address in the modbus register """ - type:DataTypes - address:int - count:int - value:int - multiplier:float - _absolut_address:int - _modbus:ModbusConnector - register_type:RegisterTypes - def __init__(self,address:int,count:int=1,default_value:int=0,multiplier:float=None,type:DataTypes=DataTypes.INT,register_type:RegisterTypes=RegisterTypes.Input) -> None: + + data_type: DataTypes + address: int + count: int + value: int + multiplier: float + absolut_address: int + modbus: ModbusConnector + register_type: RegisterTypes + + def __init__( + self, address: int, count: int = 1, default_value: int = 0, multiplier: float = None, data_type: DataTypes = DataTypes.INT, register_type: RegisterTypes = RegisterTypes.INPUT + ) -> None: self.address = address self.count = count self.value = default_value self.multiplier = multiplier - self.type = type + self.data_type = data_type self.register_type = register_type - #These are set by the parent component - self._absolut_address = None - self._modbus = None - - def get_absolute_address(self)->int: + # These are set by the parent component + self.absolut_address = None + self.modbus = None + + def get_absolute_address(self) -> int: """ Returns the absolute address of the register """ - if not self._absolut_address: + if not self.absolut_address: raise Exception("Absolute address not set!") - return self._absolut_address + self.address - + return self.absolut_address + self.address + @property - def has_scaler(self)->bool: - return self.multiplier is not None - + def has_scaler(self) -> bool: + return self.multiplier is not None + @property - def scaled_value(self)->float: + def scaled_value(self) -> float: """ Scaled value of this register """ if self.has_scaler: - #Input registers are scaled differently than holding registers - if self.register_type == RegisterTypes.Input: + # Input registers are scaled differently than holding registers + if self.register_type == RegisterTypes.INPUT: return self.value * self.multiplier - else: - return self.value / self.multiplier - else: - return self.value - - def reverse_scale(self,value:float)->float: + return self.value / self.multiplier + return self.value + + def reverse_scale(self, value: float) -> float: """ Applies the scaler in the reverse direction """ if self.has_scaler: - #Input registers are scaled differently than holding registers - if self.register_type == RegisterTypes.Input: + # Input registers are scaled differently than holding registers + if self.register_type == RegisterTypes.INPUT: return value / self.multiplier - else: - return value * self.multiplier - else: - return value - - def set_unscaled_value(self,value:float)->None: + return value * self.multiplier + return value + + def set_unscaled_value(self, value: float) -> None: """ Applies the reverse scaler to the value and sets the value """ self.value = self.reverse_scale(value) - - def commit(self)->bool: + + def commit(self) -> bool: """ Writes the current value to the heating system """ - if self._modbus is None: - #Modbus is never set for input registers + if self.modbus is None: + # Modbus is never set for input registers return False - + logging.debug(f"Writing to server: Scaled Value={self.scaled_value}, Raw Value={int(self.value)}, Address={self.get_absolute_address()}") - return self._modbus.write_register(int(self.value),self.get_absolute_address()) + return self.modbus.write_register(int(self.value), self.get_absolute_address()) diff --git a/pysolarfocus/components/base/enums.py b/pysolarfocus/components/base/enums.py index 2ca5cd0..92d182e 100644 --- a/pysolarfocus/components/base/enums.py +++ b/pysolarfocus/components/base/enums.py @@ -1,9 +1,13 @@ +"""Solarfocus base enums""" + from enum import Enum + class RegisterTypes(str, Enum): - Holding = "Holding" - Input = "Input" - + HOLDING = "Holding" + INPUT = "Input" + + class DataTypes(int, Enum): INT = 1 - UINT = 2 \ No newline at end of file + UINT = 2 diff --git a/pysolarfocus/components/base/part.py b/pysolarfocus/components/base/part.py index 9035c4b..49995d5 100644 --- a/pysolarfocus/components/base/part.py +++ b/pysolarfocus/components/base/part.py @@ -1,12 +1,14 @@ +"""Solarfocus abstract part""" + from abc import abstractmethod -class Part(object): +class Part: """ Abstraction of a metric of the heating system """ - + @property @abstractmethod - def scaled_value(self)->float: - pass \ No newline at end of file + def scaled_value(self) -> float: + pass diff --git a/pysolarfocus/components/base/performance_calculator.py b/pysolarfocus/components/base/performance_calculator.py index 53096d3..06aaf8a 100644 --- a/pysolarfocus/components/base/performance_calculator.py +++ b/pysolarfocus/components/base/performance_calculator.py @@ -1,31 +1,33 @@ +"""Solarfocus performance calculator""" + from .part import Part + class PerformanceCalculator(Part): """ Performing performance calculations """ - - nominator:Part - denominator:Part - value:int = None - - def __init__(self, nominator:Part, denominator:Part) -> None: + + nominator: Part + denominator: Part + value: int = None + + def __init__(self, nominator: Part, denominator: Part) -> None: self.nominator = nominator self.denominator = denominator - + @property - def value(self)->float: + def value(self) -> float: """ Return value of the calculation """ if self.denominator.scaled_value: return self.nominator.scaled_value / self.denominator.scaled_value return 0.0 - + @property - def scaled_value(self)->float: + def scaled_value(self) -> float: """ To provide a uniform interface for integrating, a scaled value property is offered """ return self.value - \ No newline at end of file diff --git a/pysolarfocus/components/base/register_slice.py b/pysolarfocus/components/base/register_slice.py index 9820fc7..cd82973 100644 --- a/pysolarfocus/components/base/register_slice.py +++ b/pysolarfocus/components/base/register_slice.py @@ -1,7 +1,10 @@ +"""Solarfocus register""" + from dataclasses import dataclass + @dataclass() class RegisterSlice: - absolute_address:int - relative_address:int - count:int + absolute_address: int + relative_address: int + count: int diff --git a/pysolarfocus/components/biomass_boiler.py b/pysolarfocus/components/biomass_boiler.py new file mode 100644 index 0000000..e02019c --- /dev/null +++ b/pysolarfocus/components/biomass_boiler.py @@ -0,0 +1,37 @@ +"""Solarfocus pelletsboiler component""" + +from .. import ApiVersions, Systems +from .base.component import Component +from .base.data_value import DataValue +from .base.enums import DataTypes, RegisterTypes + + +class BiomassBoiler(Component): + def __init__(self, input_address=2400, holding_address=-1, api_version: ApiVersions = ApiVersions.V_23_010, system=Systems.ECOTOP) -> None: + + if api_version.greater_or_equal(ApiVersions.V_22_090.value): + holding_address = 33400 + + super().__init__(input_address, holding_address) + self.temperature = DataValue(address=0, multiplier=0.1) + self.status = DataValue(address=1, data_type=DataTypes.UINT) + self.message_number = DataValue(address=4) + self.door_contact = DataValue(address=5) + self.cleaning = DataValue(address=6) + self.ash_container = DataValue(address=7) + self.outdoor_temperature = DataValue(address=8, multiplier=0.1) + self.boiler_operating_mode = DataValue(address=9) + self.octoplus_buffer_temperature_bottom = DataValue(address=10, multiplier=0.1) + self.octoplus_buffer_temperature_top = DataValue(address=11, multiplier=0.1) + self.log_wood = DataValue(address=12, data_type=DataTypes.UINT) + + if api_version.greater_or_equal(ApiVersions.V_22_090.value) and system is not Systems.ECOTOP: + self.sweep_function_start_stop = DataValue(address=10, register_type=RegisterTypes.HOLDING) + self.sweep_function_extend = DataValue(address=11, register_type=RegisterTypes.HOLDING) + + if api_version.greater_or_equal(ApiVersions.V_23_010.value): + self.pellet_usage_last_fill = DataValue(address=14, count=2, multiplier=0.1) + self.pellet_usage_total = DataValue(address=16, count=2, multiplier=0.1) + self.heat_energy_total = DataValue(address=18, count=2, multiplier=0.1) + + self.pellet_usage_reset = DataValue(address=12, register_type=RegisterTypes.HOLDING) diff --git a/pysolarfocus/components/boiler.py b/pysolarfocus/components/boiler.py index 90bdd70..f7f7b91 100644 --- a/pysolarfocus/components/boiler.py +++ b/pysolarfocus/components/boiler.py @@ -1,15 +1,18 @@ +"""Solarfocus boiler component""" + from .base.component import Component -from .base.enums import DataTypes,RegisterTypes from .base.data_value import DataValue +from .base.enums import DataTypes, RegisterTypes + class Boiler(Component): - def __init__(self,input_address:int=500, holding_address:int=32000) -> None: + def __init__(self, input_address: int = 500, holding_address: int = 32000) -> None: super().__init__(input_address=input_address, holding_address=holding_address) - self.temperature = DataValue(address=0,multiplier=0.1) - self.state = DataValue(address=1,type=DataTypes.UINT) - self.mode = DataValue(address=2,type=DataTypes.UINT) - - self.target_temperature = DataValue(address=0,multiplier=10,register_type=RegisterTypes.Holding) - self.single_charge = DataValue(address=1,register_type=RegisterTypes.Holding) - self.holding_mode= DataValue(address=2,register_type=RegisterTypes.Holding) - self.circulation = DataValue(address=3,register_type=RegisterTypes.Holding) \ No newline at end of file + self.temperature = DataValue(address=0, multiplier=0.1) + self.state = DataValue(address=1, data_type=DataTypes.UINT) + self.mode = DataValue(address=2, data_type=DataTypes.UINT) + + self.target_temperature = DataValue(address=0, multiplier=10, register_type=RegisterTypes.HOLDING) + self.single_charge = DataValue(address=1, register_type=RegisterTypes.HOLDING) + self.holding_mode = DataValue(address=2, register_type=RegisterTypes.HOLDING) + self.circulation = DataValue(address=3, register_type=RegisterTypes.HOLDING) diff --git a/pysolarfocus/components/buffer.py b/pysolarfocus/components/buffer.py index 79ab723..867fb6d 100644 --- a/pysolarfocus/components/buffer.py +++ b/pysolarfocus/components/buffer.py @@ -1,30 +1,32 @@ +"""Solarfocus buffer component""" + +from .. import ApiVersions from .base.component import Component -from .base.enums import DataTypes, RegisterTypes from .base.data_value import DataValue -from .. import ApiVersions +from .base.enums import DataTypes, RegisterTypes + class Buffer(Component): - def __init__(self,input_address:int=1900, holding_address=-1, api_version:ApiVersions=ApiVersions.V_21_140) -> None: + def __init__(self, input_address: int = 1900, holding_address=-1, api_version: ApiVersions = ApiVersions.V_21_140) -> None: super().__init__(input_address, holding_address) - self.top_temperature = DataValue(address=0,multiplier=0.1) - self.bottom_temperature = DataValue(address=1,multiplier=0.1) + self.top_temperature = DataValue(address=0, multiplier=0.1) + self.bottom_temperature = DataValue(address=1, multiplier=0.1) self.pump = DataValue(address=2) - self.state = DataValue(address=3,type=DataTypes.UINT) - self.mode = DataValue(address=4,type=DataTypes.UINT) + self.state = DataValue(address=3, data_type=DataTypes.UINT) + self.mode = DataValue(address=4, data_type=DataTypes.UINT) if api_version.greater_or_equal(ApiVersions.V_22_090.value): - self.external_top_temperature_x44 = DataValue(address=0,multiplier=10,register_type=RegisterTypes.Holding) - self.external_middle_temperature_x36 = DataValue(address=1,multiplier=10,register_type=RegisterTypes.Holding) - self.external_bottom_temperature_x35 = DataValue(address=2,multiplier=10,register_type=RegisterTypes.Holding) - - + self.external_top_temperature_x44 = DataValue(address=0, multiplier=10, register_type=RegisterTypes.HOLDING) + self.external_middle_temperature_x36 = DataValue(address=1, multiplier=10, register_type=RegisterTypes.HOLDING) + self.external_bottom_temperature_x35 = DataValue(address=2, multiplier=10, register_type=RegisterTypes.HOLDING) + + class TherminatorBuffer(Buffer): - def __init__(self,address:int=1900) -> None: + def __init__(self, address: int = 1900) -> None: super().__init__(input_address=address) - self.x35_temperature = DataValue(address=2,multiplier=0.1) + self.x35_temperature = DataValue(address=2, multiplier=0.1) self.pump = DataValue(address=3) - self.state = DataValue(address=4,type=DataTypes.UINT) - self.mode = DataValue(address=5,type=DataTypes.UINT) - \ No newline at end of file + self.state = DataValue(address=4, data_type=DataTypes.UINT) + self.mode = DataValue(address=5, data_type=DataTypes.UINT) diff --git a/pysolarfocus/components/fresh_water_module.py b/pysolarfocus/components/fresh_water_module.py index f03b5f6..da48124 100644 --- a/pysolarfocus/components/fresh_water_module.py +++ b/pysolarfocus/components/fresh_water_module.py @@ -1,9 +1,10 @@ -from .base.enums import RegisterTypes +"""Solarfocus fresh water module component""" + from .base.component import Component from .base.data_value import DataValue + class FreshWaterModule(Component): def __init__(self, input_address=700) -> None: super().__init__(input_address) self.state = DataValue(address=0) - \ No newline at end of file diff --git a/pysolarfocus/components/heat_pump.py b/pysolarfocus/components/heat_pump.py index c827715..e16ce6e 100644 --- a/pysolarfocus/components/heat_pump.py +++ b/pysolarfocus/components/heat_pump.py @@ -1,37 +1,40 @@ +"""Solarfocus heat pump component""" from pysolarfocus.components.base.performance_calculator import PerformanceCalculator + from .base.component import Component -from .base.enums import DataTypes,RegisterTypes from .base.data_value import DataValue +from .base.enums import DataTypes, RegisterTypes + class HeatPump(Component): def __init__(self) -> None: - super().__init__(input_address=2300,holding_address=33404) - self.supply_temperature = DataValue(address=0,multiplier=0.1) - self.return_temperature = DataValue(address=1,multiplier=0.1) + super().__init__(input_address=2300, holding_address=33404) + self.supply_temperature = DataValue(address=0, multiplier=0.1) + self.return_temperature = DataValue(address=1, multiplier=0.1) self.flow_rate = DataValue(address=2) self.compressor_speed = DataValue(address=3) - self.evu_lock_active = DataValue(address=4,type=DataTypes.UINT) - self.defrost_active = DataValue(address=5,type=DataTypes.UINT) - self.boiler_charge = DataValue(address=6, type=DataTypes.UINT) - self.thermal_energy_total = DataValue(address=7,count=2,multiplier=0.001) - self.thermal_energy_drinking_water = DataValue(address=9,count=2,multiplier=0.001) - self.thermal_energy_heating = DataValue(address=11,count=2,multiplier=0.001) - self.electrical_energy_total = DataValue(address=13,count=2,multiplier=0.001) - self.electrical_energy_drinking_water = DataValue(address=15,count=2,multiplier=0.001) - self.electrical_energy_heating = DataValue(address=17,count=2,multiplier=0.001) + self.evu_lock_active = DataValue(address=4, data_type=DataTypes.UINT) + self.defrost_active = DataValue(address=5, data_type=DataTypes.UINT) + self.boiler_charge = DataValue(address=6, data_type=DataTypes.UINT) + self.thermal_energy_total = DataValue(address=7, count=2, multiplier=0.001) + self.thermal_energy_drinking_water = DataValue(address=9, count=2, multiplier=0.001) + self.thermal_energy_heating = DataValue(address=11, count=2, multiplier=0.001) + self.electrical_energy_total = DataValue(address=13, count=2, multiplier=0.001) + self.electrical_energy_drinking_water = DataValue(address=15, count=2, multiplier=0.001) + self.electrical_energy_heating = DataValue(address=17, count=2, multiplier=0.001) self.electrical_power = DataValue(address=19) self.thermal_power_cooling = DataValue(address=20) self.thermal_power_heating = DataValue(address=21) - self.thermal_energy_cooling = DataValue(address=22,count=2,multiplier=0.001,type=DataTypes.UINT) - self.electrical_energy_cooling = DataValue(address=24,count=2,multiplier=0.001,type=DataTypes.UINT) - self.vampair_state = DataValue(address=26,type=DataTypes.UINT) - - self.evu_lock = DataValue(address=0,register_type=RegisterTypes.Holding) - self.smart_grid = DataValue(address=1,register_type=RegisterTypes.Holding) - self.outdoor_temperature_external = DataValue(address=2,multiplier=10,register_type=RegisterTypes.Holding) + self.thermal_energy_cooling = DataValue(address=22, count=2, multiplier=0.001, data_type=DataTypes.UINT) + self.electrical_energy_cooling = DataValue(address=24, count=2, multiplier=0.001, data_type=DataTypes.UINT) + self.vampair_state = DataValue(address=26, data_type=DataTypes.UINT) + + self.evu_lock = DataValue(address=0, register_type=RegisterTypes.HOLDING) + self.smart_grid = DataValue(address=1, register_type=RegisterTypes.HOLDING) + self.outdoor_temperature_external = DataValue(address=2, multiplier=10, register_type=RegisterTypes.HOLDING) self.cop_heating = PerformanceCalculator(self.thermal_power_heating, self.electrical_power) self.cop_cooling = PerformanceCalculator(self.thermal_power_cooling, self.electrical_power) self.performance_overall = PerformanceCalculator(self.thermal_energy_total, self.electrical_energy_total) self.performance_overall_heating = PerformanceCalculator(self.thermal_energy_heating, self.electrical_energy_heating) - self.performance_overall_drinking_water = PerformanceCalculator(self.thermal_energy_drinking_water, self.electrical_energy_drinking_water) \ No newline at end of file + self.performance_overall_drinking_water = PerformanceCalculator(self.thermal_energy_drinking_water, self.electrical_energy_drinking_water) diff --git a/pysolarfocus/components/heating_circuit.py b/pysolarfocus/components/heating_circuit.py index 74a9a3d..743d258 100644 --- a/pysolarfocus/components/heating_circuit.py +++ b/pysolarfocus/components/heating_circuit.py @@ -1,29 +1,32 @@ +"""Solarfocus heating circuit component""" from .base.component import Component -from .base.enums import DataTypes,RegisterTypes from .base.data_value import DataValue +from .base.enums import DataTypes, RegisterTypes + class HeatingCircuit(Component): - def __init__(self,input_address=1100,holding_address=32600) -> None: - super().__init__(input_address=input_address,holding_address=holding_address) - self.supply_temperature = DataValue(address=0,multiplier=0.1) - self.room_temperature = DataValue(address=1,multiplier=0.1) - self.humidity = DataValue(address=2,multiplier=0.1) - self.limit_thermostat = DataValue(address=3,type=DataTypes.UINT) - self.circulator_pump = DataValue(address=4,type=DataTypes.UINT) - self.mixer_valve = DataValue(address=5,type=DataTypes.UINT) - self.state = DataValue(address=6,type=DataTypes.UINT) - - self.target_supply_temperature = DataValue(address=0,multiplier=10,register_type=RegisterTypes.Holding) - self.cooling = DataValue(address=2,register_type=RegisterTypes.Holding) - self.mode = DataValue(address=3,register_type=RegisterTypes.Holding) - self.target_room_temperatur = DataValue(address=5,multiplier=10,register_type=RegisterTypes.Holding) - self.indoor_temperatur_external = DataValue(address=6,multiplier=10,register_type=RegisterTypes.Holding) - self.indoor_humidity_external = DataValue(address=7,register_type=RegisterTypes.Holding) - + def __init__(self, input_address=1100, holding_address=32600) -> None: + super().__init__(input_address=input_address, holding_address=holding_address) + self.supply_temperature = DataValue(address=0, multiplier=0.1) + self.room_temperature = DataValue(address=1, multiplier=0.1) + self.humidity = DataValue(address=2, multiplier=0.1) + self.limit_thermostat = DataValue(address=3, data_type=DataTypes.UINT) + self.circulator_pump = DataValue(address=4, data_type=DataTypes.UINT) + self.mixer_valve = DataValue(address=5, data_type=DataTypes.UINT) + self.state = DataValue(address=6, data_type=DataTypes.UINT) + + self.target_supply_temperature = DataValue(address=0, multiplier=10, register_type=RegisterTypes.HOLDING) + self.cooling = DataValue(address=2, register_type=RegisterTypes.HOLDING) + self.mode = DataValue(address=3, register_type=RegisterTypes.HOLDING) + self.target_room_temperatur = DataValue(address=5, multiplier=10, register_type=RegisterTypes.HOLDING) + self.indoor_temperatur_external = DataValue(address=6, multiplier=10, register_type=RegisterTypes.HOLDING) + self.indoor_humidity_external = DataValue(address=7, register_type=RegisterTypes.HOLDING) + + class TherminatorHeatingCircuit(HeatingCircuit): def __init__(self, input_address=1100, holding_address=32600) -> None: super().__init__(input_address, holding_address) - # No idea, why this is offset by 1 ... - self.circulator_pump = DataValue(address=5,type=DataTypes.UINT) - self.mixer_valve = DataValue(address=6,type=DataTypes.UINT) - self.state = DataValue(address=7,type=DataTypes.UINT) \ No newline at end of file + # No idea, why this is offset by 1 ... + self.circulator_pump = DataValue(address=5, data_type=DataTypes.UINT) + self.mixer_valve = DataValue(address=6, data_type=DataTypes.UINT) + self.state = DataValue(address=7, data_type=DataTypes.UINT) diff --git a/pysolarfocus/components/pellets_boiler.py b/pysolarfocus/components/pellets_boiler.py deleted file mode 100644 index 9d196a5..0000000 --- a/pysolarfocus/components/pellets_boiler.py +++ /dev/null @@ -1,35 +0,0 @@ -from .base.component import Component -from .base.enums import DataTypes, RegisterTypes -from .base.data_value import DataValue -from .. import ApiVersions -from .. import Systems - -class PelletsBoiler(Component): - def __init__(self,input_address=2400,holding_address=-1,api_version:ApiVersions=ApiVersions.V_23_010,system=Systems.Ecotop) -> None: - - if api_version.greater_or_equal(ApiVersions.V_22_090.value): - holding_address=33400 - - super().__init__(input_address, holding_address) - self.temperature = DataValue(address=0,multiplier=0.1) - self.status = DataValue(address=1,type=DataTypes.UINT) - self.message_number = DataValue(address=4) - self.door_contact = DataValue(address=5) - self.cleaning = DataValue(address=6) - self.ash_container = DataValue(address=7) - self.outdoor_temperature = DataValue(address=8,multiplier=0.1) - self.boiler_operating_mode = DataValue(address=9) - self.octoplus_buffer_temperature_bottom = DataValue(address=10,multiplier=0.1) - self.octoplus_buffer_temperature_top = DataValue(address=11,multiplier=0.1) - self.log_wood = DataValue(address=12,type=DataTypes.UINT) - - if api_version.greater_or_equal(ApiVersions.V_22_090.value) and system is not Systems.Ecotop: - self.sweep_function_start_stop = DataValue(address=10,register_type=RegisterTypes.Holding) - self.sweep_function_extend = DataValue(address=11,register_type=RegisterTypes.Holding) - - if api_version.greater_or_equal(ApiVersions.V_23_010.value): - self.pellet_usage_last_fill = DataValue(address=14,count=2,multiplier=0.1) - self.pellet_usage_total = DataValue(address=16,count=2,multiplier=0.1) - self.heat_energy_total = DataValue(address=18,count=2,multiplier=0.1) - - self.pellet_usage_reset = DataValue(address=12,register_type=RegisterTypes.Holding) diff --git a/pysolarfocus/components/photovoltaic.py b/pysolarfocus/components/photovoltaic.py index e20ddbf..649e0e5 100644 --- a/pysolarfocus/components/photovoltaic.py +++ b/pysolarfocus/components/photovoltaic.py @@ -1,16 +1,18 @@ +"""Solarfocus photovoltaic component""" from .base.component import Component -from .base.enums import RegisterTypes from .base.data_value import DataValue +from .base.enums import RegisterTypes + class Photovoltaic(Component): def __init__(self) -> None: super().__init__(input_address=2500, holding_address=33407) - self.power = DataValue(address=0,count=2) - self.house_consumption = DataValue(address=2,count=2) - self.heatpump_consumption = DataValue(address=4,count=2) - self.grid_import = DataValue(address=6,count=2) - self.grid_export = DataValue(address=8,count=2) - - self.smart_meter = DataValue(address=0,register_type=RegisterTypes.Holding) - self.photovoltaic = DataValue(address=1,register_type=RegisterTypes.Holding) - self.grid_im_export = DataValue(address=2,register_type=RegisterTypes.Holding) \ No newline at end of file + self.power = DataValue(address=0, count=2) + self.house_consumption = DataValue(address=2, count=2) + self.heatpump_consumption = DataValue(address=4, count=2) + self.grid_import = DataValue(address=6, count=2) + self.grid_export = DataValue(address=8, count=2) + + self.smart_meter = DataValue(address=0, register_type=RegisterTypes.HOLDING) + self.photovoltaic = DataValue(address=1, register_type=RegisterTypes.HOLDING) + self.grid_im_export = DataValue(address=2, register_type=RegisterTypes.HOLDING) diff --git a/pysolarfocus/components/solar.py b/pysolarfocus/components/solar.py index 802f684..4bdc38d 100644 --- a/pysolarfocus/components/solar.py +++ b/pysolarfocus/components/solar.py @@ -1,20 +1,21 @@ +"""Solarfocus solar component""" from .base.component import Component -from .base.enums import DataTypes from .base.data_value import DataValue +from .base.enums import DataTypes + class Solar(Component): def __init__(self) -> None: super().__init__(input_address=2100) - self.collector_temperature_1 = DataValue(address=0,multiplier=0.1) - self.collector_temperature_2 = DataValue(address=1,multiplier=0.1) - self.collector_supply_temperature = DataValue(address=2,multiplier=0.1) - self.collector_return_temperature = DataValue(address=3,multiplier=0.1) - self.flow_heat_meter = DataValue(address=4,multiplier=0.1) - self.curent_power = DataValue(address=5,multiplier=0.1) - self.curent_yield_heat_meter = DataValue(address=6,count=2) - self.today_yield = DataValue(address=8,count=2) - self.buffer_sensor_1 = DataValue(address=10,multiplier=0.1) - self.buffer_sensor_2 = DataValue(address=11,multiplier=0.1) - self.buffer_sensor_3 = DataValue(address=12,multiplier=0.1) - self.state = DataValue(address=13,type=DataTypes.UINT) - \ No newline at end of file + self.collector_temperature_1 = DataValue(address=0, multiplier=0.1) + self.collector_temperature_2 = DataValue(address=1, multiplier=0.1) + self.collector_supply_temperature = DataValue(address=2, multiplier=0.1) + self.collector_return_temperature = DataValue(address=3, multiplier=0.1) + self.flow_heat_meter = DataValue(address=4, multiplier=0.1) + self.curent_power = DataValue(address=5, multiplier=0.1) + self.curent_yield_heat_meter = DataValue(address=6, count=2) + self.today_yield = DataValue(address=8, count=2) + self.buffer_sensor_1 = DataValue(address=10, multiplier=0.1) + self.buffer_sensor_2 = DataValue(address=11, multiplier=0.1) + self.buffer_sensor_3 = DataValue(address=12, multiplier=0.1) + self.state = DataValue(address=13, data_type=DataTypes.UINT) diff --git a/pysolarfocus/modbus_wrapper.py b/pysolarfocus/modbus_wrapper.py index 34cb059..f208670 100644 --- a/pysolarfocus/modbus_wrapper.py +++ b/pysolarfocus/modbus_wrapper.py @@ -1,3 +1,5 @@ + +"""Solarfocus modbus wrapper""" import logging try: @@ -39,13 +41,13 @@ def read_input_registers(self, slices: list[RegisterSlice], count: int, check_co return False, None try: combined_result = [None] * count - for registerSlice in slices: - result = self.client.read_input_registers(address=registerSlice.absolute_address, count=registerSlice.count, **self.__slave_args) + for register_slice in slices: + result = self.client.read_input_registers(address=register_slice.absolute_address, count=register_slice.count, **self.__slave_args) if result.isError(): - logging.error(f"Modbus read error at address={registerSlice.absolute_address}, count={registerSlice.count}: {result}") + logging.error(f"Modbus read error at address={register_slice.absolute_address}, count={register_slice.count}: {result}") return False, None slice_data = result.registers - combined_result[registerSlice.relative_address : registerSlice.relative_address + registerSlice.count] = slice_data + combined_result[register_slice.relative_address : register_slice.relative_address + register_slice.count] = slice_data return True, combined_result except Exception: logging.exception(f"Exception while reading input registers for address: '{slices[0].absolute_address}'!") @@ -58,13 +60,13 @@ def read_holding_registers(self, slices: list[RegisterSlice], count: int, check_ return False, None try: combined_result = [None] * count - for registerSlice in slices: - result = self.client.read_holding_registers(address=registerSlice.absolute_address, count=registerSlice.count, **self.__slave_args) + for register_slice in slices: + result = self.client.read_holding_registers(address=register_slice.absolute_address, count=register_slice.count, **self.__slave_args) if result.isError(): - logging.error(f"Modbus read error at address={registerSlice.absolute_address}: {result}") + logging.error(f"Modbus read error at address={register_slice.absolute_address}: {result}") return False, None slice_data = result.registers - combined_result[registerSlice.relative_address : registerSlice.relative_address + registerSlice.count] = slice_data + combined_result[register_slice.relative_address : register_slice.relative_address + register_slice.count] = slice_data return True, combined_result except Exception: logging.exception(f"Exception while reading holding registers for address: '{slices[0].absolute_address}'!") diff --git a/tests/test_component.py b/tests/test_component.py index 24287d3..f62696d 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -1,9 +1,11 @@ from pysolarfocus.components.base.component import Component from pysolarfocus.components.base.data_value import DataValue -def _assign_absolute_addresses(data_values:list[DataValue], start_address): + +def _assign_absolute_addresses(data_values: list[DataValue], start_address): for data_value in data_values: - data_value._absolut_address = start_address + data_value.absolut_address = start_address + def test_calculate_ranges_handles_simple_case(): absolute_address = 500 @@ -20,10 +22,10 @@ def test_calculate_ranges_handles_simple_case(): assert slices[0].absolute_address == absolute_address assert slices[0].relative_address == 0 assert slices[0].count == 5 - - + + def test_calculate_ranges_handles_skipped_values(): - #Tests if 503 is skipped + # Tests if 503 is skipped absolute_address = 500 data_values = [ DataValue(address=0), @@ -41,8 +43,8 @@ def test_calculate_ranges_handles_skipped_values(): assert slices[1].absolute_address == 504 assert slices[1].relative_address == 4 assert slices[1].count == 2 - - + + def test_calculate_ranges_handles_multiregister_values(): absolute_address = 500 data_values = [ @@ -56,13 +58,14 @@ def test_calculate_ranges_handles_multiregister_values(): assert slices[0].absolute_address == absolute_address assert slices[0].relative_address == 0 assert slices[0].count == 6 - + + def test_calculate_ranges_handles_multiregister_and_skipped_values(): absolute_address = 500 data_values = [ DataValue(address=0, count=2), DataValue(address=2, count=3), - DataValue(address=20,count=10), + DataValue(address=20, count=10), ] _assign_absolute_addresses(data_values, absolute_address) slices = Component._calculate_ranges(data_values) @@ -70,13 +73,13 @@ def test_calculate_ranges_handles_multiregister_and_skipped_values(): assert slices[0].absolute_address == absolute_address assert slices[0].relative_address == 0 assert slices[0].count == 5 - assert slices[1].absolute_address == absolute_address+20 + assert slices[1].absolute_address == absolute_address + 20 assert slices[1].relative_address == 20 assert slices[1].count == 10 - - + + def test_calculate_ranges_handles_multiple_skipped_values(): - #Test the Heating Circuit configuration + # Test the Heating Circuit configuration absolute_address = 32600 data_values = [ DataValue(address=0), @@ -89,17 +92,15 @@ def test_calculate_ranges_handles_multiple_skipped_values(): _assign_absolute_addresses(data_values, absolute_address) slices = Component._calculate_ranges(data_values) assert len(slices) == 3 - + assert slices[0].absolute_address == absolute_address assert slices[0].relative_address == 0 assert slices[0].count == 1 - - assert slices[1].absolute_address == absolute_address+2 + + assert slices[1].absolute_address == absolute_address + 2 assert slices[1].relative_address == 2 assert slices[1].count == 2 - - assert slices[2].absolute_address == absolute_address+5 + + assert slices[2].absolute_address == absolute_address + 5 assert slices[2].relative_address == 5 assert slices[2].count == 3 - - \ No newline at end of file