From ea9641f9808e0ab789aa3067831d3a699efdfd49 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Fri, 16 Apr 2021 22:33:58 +0200 Subject: [PATCH] Apply Precision/Scale/Offset to struct in modbus sensor (#48544) The single values in struct are corrected with presicion, scale and offset, just as it is done with single values. --- homeassistant/components/modbus/sensor.py | 14 ++++++- tests/components/modbus/test_modbus_sensor.py | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index 21069d86427..dcc68b52db8 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -319,7 +319,19 @@ class ModbusRegisterSensor(RestoreEntity, SensorEntity): # 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)) + # Apply scale and precision to floats and ints + v_result = [] + for entry in val: + v_temp = self._scale * entry + 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(v_temp, int) and self._precision == 0: + v_result.append(str(v_temp)) + else: + v_result.append(f"{float(v_temp):.{self._precision}f}") + self._value = ",".join(map(str, v_result)) else: val = val[0] diff --git a/tests/components/modbus/test_modbus_sensor.py b/tests/components/modbus/test_modbus_sensor.py index ce9889d8aaa..b81cc9c4c1e 100644 --- a/tests/components/modbus/test_modbus_sensor.py +++ b/tests/components/modbus/test_modbus_sensor.py @@ -12,6 +12,7 @@ from homeassistant.components.modbus.const import ( CONF_REGISTERS, CONF_REVERSE_ORDER, CONF_SCALE, + DATA_TYPE_CUSTOM, DATA_TYPE_FLOAT, DATA_TYPE_INT, DATA_TYPE_STRING, @@ -26,6 +27,7 @@ from homeassistant.const import ( CONF_OFFSET, CONF_SENSORS, CONF_SLAVE, + CONF_STRUCTURE, ) from .conftest import base_config_test, base_test @@ -338,6 +340,7 @@ async def test_config_sensor(hass, do_discovery, do_config): ) async def test_all_sensor(hass, cfg, regs, expected): """Run test for sensor.""" + sensor_name = "modbus_test_sensor" state = await base_test( hass, @@ -352,3 +355,41 @@ async def test_all_sensor(hass, cfg, regs, expected): scan_interval=5, ) assert state == expected + + +async def test_struct_sensor(hass): + """Run test for sensor struct.""" + + sensor_name = "modbus_test_sensor" + # floats: 7.931250095367432, 10.600000381469727, + # 1.000879611487865e-28, 10.566553115844727 + expected = "7.93,10.60,0.00,10.57" + state = await base_test( + hass, + { + CONF_NAME: sensor_name, + CONF_REGISTER: 1234, + CONF_COUNT: 8, + CONF_PRECISION: 2, + CONF_DATA_TYPE: DATA_TYPE_CUSTOM, + CONF_STRUCTURE: ">4f", + }, + sensor_name, + SENSOR_DOMAIN, + CONF_SENSORS, + CONF_REGISTERS, + [ + 0x40FD, + 0xCCCD, + 0x4129, + 0x999A, + 0x10FD, + 0xC0CD, + 0x4129, + 0x109A, + ], + expected, + method_discovery=False, + scan_interval=5, + ) + assert state == expected