From fd921d6f28c564b35e254e8c1e2fcc6f986441ec Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 24 Nov 2024 07:39:06 +0100 Subject: [PATCH 1/2] Fixes for SC2 with wrong byte-decoding --- custom_components/solvis_control/coordinator.py | 15 +++++++++++---- custom_components/solvis_control/number.py | 13 +++++++++---- custom_components/solvis_control/select.py | 13 +++++++++---- custom_components/solvis_control/sensor.py | 12 ++++++++---- custom_components/solvis_control/switch.py | 13 +++++++++---- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/custom_components/solvis_control/coordinator.py b/custom_components/solvis_control/coordinator.py index 396715e..9ba6448 100644 --- a/custom_components/solvis_control/coordinator.py +++ b/custom_components/solvis_control/coordinator.py @@ -1,5 +1,6 @@ """Solvis Modbus Data Coordinator""" +import struct from datetime import timedelta import logging @@ -71,7 +72,6 @@ async def _async_update_data(self): if entity_entry and entity_entry.disabled: _LOGGER.debug(f"Skipping disabled entity: {entity_id}") continue - try: if register.register == 1: result = await self.modbus.read_input_registers( @@ -94,9 +94,16 @@ async def _async_update_data(self): f"Value from previous register before modification: {register_value}" ) value = round(register_value * register.multiplier, 2) - parsed_data[register.name] = ( - abs(value) if register.absolute_value else value - ) + try: + value = round( + decoder.decode_16bit_int() * register.multiplier, 2 + ) + except struct.error: + parsed_data[register.name] = -300 + else: + parsed_data[register.name] = ( + abs(value) if register.absolute_value else value + ) # if "version" in register.name: # parsed_data[register.name] = ".".join( # parsed_data[register.name].split(0, 2) diff --git a/custom_components/solvis_control/number.py b/custom_components/solvis_control/number.py index 2f2d69d..8666245 100644 --- a/custom_components/solvis_control/number.py +++ b/custom_components/solvis_control/number.py @@ -151,16 +151,21 @@ def _handle_coordinator_update(self) -> None: response_data = self.coordinator.data.get(self._response_key) if response_data is None: - _LOGGER.warning("No data available for (%s)", self._response_key) + _LOGGER.warning(f"No data available for {self._response_key}") self._attr_available = False return # Validate the data type received from the coordinator if not isinstance(response_data, (int, float, complex, Decimal)): _LOGGER.warning( - "Invalid response data type from coordinator. %s has type %s", - response_data, - type(response_data), + f"Invalid response data type from coordinator. {response_data} has type {type(response_data)}" + ) + self._attr_available = False + return + + if response_data == -300: + _LOGGER.warning( + f"The coordinator failed to fetch data for entity: {self._response_key}" ) self._attr_available = False return diff --git a/custom_components/solvis_control/select.py b/custom_components/solvis_control/select.py index a8593ed..ecddfbe 100644 --- a/custom_components/solvis_control/select.py +++ b/custom_components/solvis_control/select.py @@ -129,16 +129,21 @@ def _handle_coordinator_update(self) -> None: response_data = self.coordinator.data.get(self._response_key) if response_data is None: - _LOGGER.warning("No data available for (%s)", self._response_key) + _LOGGER.warning(f"No data available for {self._response_key}") self._attr_available = False return # Validate the data type received from the coordinator if not isinstance(response_data, (int, float, complex, Decimal)): _LOGGER.warning( - "Invalid response data type from coordinator. %s has type %s", - response_data, - type(response_data), + f"Invalid response data type from coordinator. {response_data} has type {type(response_data)}" + ) + self._attr_available = False + return + + if response_data == -300: + _LOGGER.warning( + f"The coordinator failed to fetch data for entity: {self._response_key}" ) self._attr_available = False return diff --git a/custom_components/solvis_control/sensor.py b/custom_components/solvis_control/sensor.py index 13935be..10a6fbf 100644 --- a/custom_components/solvis_control/sensor.py +++ b/custom_components/solvis_control/sensor.py @@ -134,20 +134,24 @@ def _handle_coordinator_update(self) -> None: response_data = self.coordinator.data.get(self._response_key) if response_data is None: - _LOGGER.warning("No data available for (%s)", self._response_key) + _LOGGER.warning(f"No data available for {self._response_key}") self._attr_available = False return # Validate the data type received from the coordinator if not isinstance(response_data, (int, float, complex, Decimal)): _LOGGER.warning( - "Invalid response data type from coordinator. %s has type %s", - response_data, - type(response_data), + f"Invalid response data type from coordinator. {response_data} has type {type(response_data)}" ) self._attr_available = False return + if response_data == -300: + _LOGGER.warning( + f"The coordinator failed to fetch data for entity: {self._response_key}" + ) + self._attr_available = False + return self._attr_available = True self._attr_native_value = response_data # Update the sensor value self.async_write_ha_state() diff --git a/custom_components/solvis_control/switch.py b/custom_components/solvis_control/switch.py index 0dae73a..6ba1201 100644 --- a/custom_components/solvis_control/switch.py +++ b/custom_components/solvis_control/switch.py @@ -127,16 +127,21 @@ def _handle_coordinator_update(self) -> None: response_data = self.coordinator.data.get(self._response_key) if response_data is None: - _LOGGER.warning("No data available for (%s)", self._response_key) + _LOGGER.warning(f"No data available for {self._response_key}") self._attr_available = False return # Validate the data type received from the coordinator if not isinstance(response_data, (int, float, complex, Decimal)): _LOGGER.warning( - "Invalid response data type from coordinator. %s has type %s", - response_data, - type(response_data), + f"Invalid response data type from coordinator. {response_data} has type {type(response_data)}" + ) + self._attr_available = False + return + + if response_data == -300: + _LOGGER.warning( + f"The coordinator failed to fetch data for entity: {self._response_key}" ) self._attr_available = False return From a8078eafadc294d53894a5ea159bded952187712 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 24 Nov 2024 07:57:50 +0100 Subject: [PATCH 2/2] Added version processing --- custom_components/solvis_control/const.py | 5 +++++ .../solvis_control/manifest.json | 2 +- custom_components/solvis_control/number.py | 7 ++++++- custom_components/solvis_control/select.py | 9 ++++++++- custom_components/solvis_control/sensor.py | 20 ++++++++++++++++++- custom_components/solvis_control/switch.py | 3 +++ 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/custom_components/solvis_control/const.py b/custom_components/solvis_control/const.py index 7be12fc..9f12758 100644 --- a/custom_components/solvis_control/const.py +++ b/custom_components/solvis_control/const.py @@ -45,6 +45,9 @@ class ModbusFieldConfig: # Possibilities: # sensor (0), select (1), number (2), switch (3) input_type: int = 0 + # Option to further process data + # 0: no processing, 1: version string split + data_processing: int = 0 PORT = 502 @@ -760,6 +763,7 @@ class ModbusFieldConfig: state_class=None, multiplier=1, entity_category="diagnostic", + data_processing=1, ), ModbusFieldConfig( # VersionNBG name="version_nbg", @@ -769,6 +773,7 @@ class ModbusFieldConfig: state_class=None, multiplier=1, entity_category="diagnostic", + data_processing=1, ), ModbusFieldConfig( name="digin_error", diff --git a/custom_components/solvis_control/manifest.json b/custom_components/solvis_control/manifest.json index 14b45b2..68cac1d 100644 --- a/custom_components/solvis_control/manifest.json +++ b/custom_components/solvis_control/manifest.json @@ -9,5 +9,5 @@ "iot_class": "local_polling", "issue_tracker": "https://github.com/LarsK1/hass_solvis_control/issues", "requirements": ["pymodbus"], - "version": "1.1.4" + "version": "1.1.5" } diff --git a/custom_components/solvis_control/number.py b/custom_components/solvis_control/number.py index 8666245..27c1f13 100644 --- a/custom_components/solvis_control/number.py +++ b/custom_components/solvis_control/number.py @@ -86,6 +86,7 @@ async def async_setup_entry( register.step_size, register.address, register.multiplier, + register.data_processing, ) ) @@ -109,6 +110,7 @@ def __init__( step_size: int | None = None, modbus_address: int = None, multiplier: float = 1, + data_processing: int = 0, ): """Initialize the Solvis number entity.""" super().__init__(coordinator) @@ -135,6 +137,7 @@ def __init__( if range_data: self.native_min_value = range_data[0] self.native_max_value = range_data[1] + self.data_processing = data_processing @callback def _handle_coordinator_update(self) -> None: @@ -171,7 +174,9 @@ def _handle_coordinator_update(self) -> None: return self._attr_available = True - self._attr_native_value = response_data # Update the number value + match self.data_processing: + case _: + self._attr_native_value = response_data # Update the number value self.async_write_ha_state() async def async_set_native_value(self, value: float) -> None: diff --git a/custom_components/solvis_control/select.py b/custom_components/solvis_control/select.py index ecddfbe..4bca1f1 100644 --- a/custom_components/solvis_control/select.py +++ b/custom_components/solvis_control/select.py @@ -80,6 +80,7 @@ async def async_setup_entry( register.enabled_by_default, register.options, # These are the options for the select entity register.address, + register.data_processing, ) ) @@ -98,6 +99,7 @@ def __init__( enabled_by_default: bool = True, options: tuple = None, # Renamed for clarity modbus_address: int = None, + data_processing: int = None, ): """Initialize the Solvis select entity.""" super().__init__(coordinator) @@ -113,6 +115,7 @@ def __init__( self.translation_key = name self._attr_current_option = None self._attr_options = options # Set the options for the select entity + self.data_processing = data_processing @callback def _handle_coordinator_update(self) -> None: @@ -149,7 +152,11 @@ def _handle_coordinator_update(self) -> None: return self._attr_available = True - self._attr_current_option = str(response_data) # Update the selected option + match self.data_processing: + case _: + self._attr_current_option = str( + response_data + ) # Update the selected option self.async_write_ha_state() async def async_select_option(self, option: str) -> None: diff --git a/custom_components/solvis_control/sensor.py b/custom_components/solvis_control/sensor.py index 10a6fbf..8c5b738 100644 --- a/custom_components/solvis_control/sensor.py +++ b/custom_components/solvis_control/sensor.py @@ -80,6 +80,7 @@ async def async_setup_entry( register.state_class, register.entity_category, register.enabled_by_default, + register.data_processing, ) ) @@ -100,6 +101,7 @@ def __init__( state_class: str | None = None, entity_category: str | None = None, enabled_by_default: bool = True, + data_processing: int = 0, ): """Initialize the Solvis sensor.""" super().__init__(coordinator) @@ -117,6 +119,7 @@ def __init__( self._attr_has_entity_name = True self.unique_id = f"{re.sub('^[A-Za-z0-9_-]*$', '', name)}_{name}" self.translation_key = name + self.data_processing = data_processing @callback def _handle_coordinator_update(self) -> None: @@ -153,5 +156,20 @@ def _handle_coordinator_update(self) -> None: self._attr_available = False return self._attr_available = True - self._attr_native_value = response_data # Update the sensor value + match self.data_processing: + case 1: # Version + if len(str(response_data)) == 5: + response_data = str(response_data) + self._attr_native_value = ( + f"{response_data[0]}.{response_data[1-2]}.{response_data[3-4]}" + ) + if self._address == 32770: + self.device_info.sw_version = self._attr_native_value + elif self._address == 32771: + self.device_info.hw_version = self._attr_native_value + else: + _LOGGER.warning("Couldn't process version string to Version.") + self._attr_native_value = response_data + case _: + self._attr_native_value = response_data # Update the sensor value self.async_write_ha_state() diff --git a/custom_components/solvis_control/switch.py b/custom_components/solvis_control/switch.py index 6ba1201..eb2e473 100644 --- a/custom_components/solvis_control/switch.py +++ b/custom_components/solvis_control/switch.py @@ -79,6 +79,7 @@ async def async_setup_entry( register.name, register.enabled_by_default, register.address, + register.data_processing, ) ) @@ -96,6 +97,7 @@ def __init__( name: str, enabled_by_default: bool = True, modbus_address: int = None, + data_processing: int = 0, ): """Initialize the Solvis switch.""" super().__init__(coordinator) @@ -110,6 +112,7 @@ def __init__( self.unique_id = f"{re.sub('^[A-Za-z0-9_-]*$', '', name)}_{name}" self.translation_key = name self._attr_current_option = None + self.data_processing = data_processing @callback def _handle_coordinator_update(self) -> None: