From d518db1c95f8b43b719e794cb4f0c0164916993b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Z=C3=A1hradn=C3=ADk?= Date: Wed, 2 Dec 2020 21:16:30 +0100 Subject: [PATCH] Improve custom datatype parsing in Modbus sensor and climate (#42354) --- .coveragerc | 1 + homeassistant/components/modbus/climate.py | 10 +++++- homeassistant/components/modbus/sensor.py | 36 ++++++++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/.coveragerc b/.coveragerc index e5ee7b3dc92..76d48720110 100644 --- a/.coveragerc +++ b/.coveragerc @@ -547,6 +547,7 @@ omit = homeassistant/components/modbus/climate.py homeassistant/components/modbus/cover.py homeassistant/components/modbus/switch.py + homeassistant/components/modbus/sensor.py homeassistant/components/modem_callerid/sensor.py homeassistant/components/motion_blinds/__init__.py homeassistant/components/motion_blinds/const.py diff --git a/homeassistant/components/modbus/climate.py b/homeassistant/components/modbus/climate.py index d57d0f761c6..b09a38f082e 100644 --- a/homeassistant/components/modbus/climate.py +++ b/homeassistant/components/modbus/climate.py @@ -255,7 +255,15 @@ class ModbusThermostat(ClimateEntity): byte_string = b"".join( [x.to_bytes(2, byteorder="big") for x in result.registers] ) - val = struct.unpack(self._structure, byte_string)[0] + val = struct.unpack(self._structure, byte_string) + if len(val) != 1 or not isinstance(val[0], (float, int)): + _LOGGER.error( + "Unable to parse result as a single int or float value; adjust your configuration. Result: %s", + str(val), + ) + return -1 + + val = val[0] register_value = format( (self._scale * val) + self._offset, f".{self._precision}f" ) diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index bd6b5e349c3..656e5e2986d 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -250,16 +250,32 @@ class ModbusRegisterSensor(RestoreEntity): registers.reverse() byte_string = b"".join([x.to_bytes(2, byteorder="big") for x in registers]) - if self._data_type != DATA_TYPE_STRING: - val = struct.unpack(self._structure, byte_string)[0] - val = self._scale * val + self._offset - if isinstance(val, int): - self._value = str(val) - if self._precision > 0: - self._value += "." + "0" * self._precision - else: - self._value = f"{val:.{self._precision}f}" - else: + if self._data_type == DATA_TYPE_STRING: self._value = byte_string.decode() + else: + val = struct.unpack(self._structure, byte_string) + + # Issue: https://github.com/home-assistant/core/issues/41944 + # If unpack() returns a tuple greater than 1, don't try to process the value. + # Instead, return the values of unpack(...) separated by commas. + if len(val) > 1: + self._value = ",".join(map(str, val)) + else: + val = val[0] + + # Apply scale and precision to floats and ints + if isinstance(val, (float, int)): + val = self._scale * val + self._offset + + # We could convert int to float, and the code would still work; however + # we lose some precision, and unit tests will fail. Therefore, we do + # the conversion only when it's absolutely necessary. + if isinstance(val, int) and self._precision == 0: + self._value = str(val) + else: + self._value = f"{float(val):.{self._precision}f}" + else: + # Don't process remaining datatypes (bytes and booleans) + self._value = str(val) self._available = True