From 8ed4ce64c3890420c36832223a228f209a9a7254 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sat, 26 Nov 2022 11:11:50 +0100 Subject: [PATCH] Solve modbus binary slave problem (#82338) * Solve modbus binary slave problem. --- .../components/modbus/binary_sensor.py | 15 +- tests/components/modbus/test_binary_sensor.py | 180 ++++++++---------- 2 files changed, 82 insertions(+), 113 deletions(-) diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index ed2f6fa0227..06d7b1b6a11 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -60,7 +60,7 @@ class ModbusBinarySensor(BasePlatform, RestoreEntity, BinarySensorEntity): """Initialize the Modbus binary sensor.""" self._count = slave_count + 1 self._coordinator: DataUpdateCoordinator[Any] | None = None - self._result = None + self._result: list = [] super().__init__(hub, entry) async def async_setup_slaves( @@ -106,15 +106,15 @@ class ModbusBinarySensor(BasePlatform, RestoreEntity, BinarySensorEntity): return self._lazy_errors = self._lazy_error_count self._attr_available = False - self._result = None + self._result = [] else: self._lazy_errors = self._lazy_error_count self._attr_available = True - self._result = result if self._input_type in (CALL_TYPE_COIL, CALL_TYPE_DISCRETE): - self._attr_is_on = bool(result.bits[0] & 1) + self._result = result.bits else: - self._attr_is_on = bool(result.registers[0] & 1) + self._result = result.registers + self._attr_is_on = bool(self._result[0] & 1) self.async_write_ha_state() if self._coordinator: @@ -132,8 +132,7 @@ class SlaveSensor(CoordinatorEntity, RestoreEntity, BinarySensorEntity): self._attr_name = f"{entry[CONF_NAME]} {idx}" self._attr_device_class = entry.get(CONF_DEVICE_CLASS) self._attr_available = False - self._result_inx = int(idx / 8) - self._result_bit = 2 ** (idx % 8) + self._result_inx = idx super().__init__(coordinator) async def async_added_to_hass(self) -> None: @@ -148,5 +147,5 @@ class SlaveSensor(CoordinatorEntity, RestoreEntity, BinarySensorEntity): """Handle updated data from the coordinator.""" result = self.coordinator.data if result: - self._attr_is_on = result.bits[self._result_inx] & self._result_bit + self._attr_is_on = bool(result[self._result_inx] & 1) super()._handle_coordinator_update() diff --git a/tests/components/modbus/test_binary_sensor.py b/tests/components/modbus/test_binary_sensor.py index cdd019c73f3..611f558cf1f 100644 --- a/tests/components/modbus/test_binary_sensor.py +++ b/tests/components/modbus/test_binary_sensor.py @@ -117,35 +117,50 @@ async def test_config_binary_sensor(hass, mock_modbus): "register_words,do_exception,expected", [ ( - [0xFF], + [True] * 8, False, STATE_ON, ), ( - [0x01], + [False] * 8, + False, + STATE_OFF, + ), + ( + [False] + [True] * 7, + False, + STATE_OFF, + ), + ( + [True] + [False] * 7, False, STATE_ON, ), ( - [0x00], - False, - STATE_OFF, - ), - ( - [0x80], - False, - STATE_OFF, - ), - ( - [0xFE], - False, - STATE_OFF, - ), - ( - [0x00], + [False] * 8, True, STATE_UNAVAILABLE, ), + ( + [1] * 8, + False, + STATE_ON, + ), + ( + [2] * 8, + False, + STATE_OFF, + ), + ( + [4] + [1] * 7, + False, + STATE_OFF, + ), + ( + [1] + [8] * 7, + False, + STATE_ON, + ), ], ) async def test_all_binary_sensor(hass, expected, mock_do_cycle): @@ -173,7 +188,7 @@ async def test_all_binary_sensor(hass, expected, mock_do_cycle): "register_words,do_exception,start_expect,end_expect", [ ( - [0x00], + [False * 16], True, STATE_UNKNOWN, STATE_UNAVAILABLE, @@ -289,6 +304,34 @@ async def test_config_slave_binary_sensor(hass, mock_modbus): { CONF_NAME: TEST_ENTITY_NAME, CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_COIL, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_DISCRETE, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_HOLDING, + } + ] + }, + { + CONF_BINARY_SENSORS: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 51, + CONF_INPUT_TYPE: CALL_TYPE_REGISTER_INPUT, } ] }, @@ -299,106 +342,33 @@ async def test_config_slave_binary_sensor(hass, mock_modbus): [ ( {CONF_SLAVE_COUNT: 1}, - [0x01], - STATE_ON, - [ - STATE_OFF, - ], + [False] * 8, + STATE_OFF, + [STATE_OFF], ), ( {CONF_SLAVE_COUNT: 1}, - [0x02], - STATE_OFF, - [ - STATE_ON, - ], + [True] + [False] * 7, + STATE_ON, + [STATE_OFF], ), ( {CONF_SLAVE_COUNT: 1}, - [0x04], + [False, True] + [False] * 6, STATE_OFF, - [ - STATE_OFF, - ], + [STATE_ON], ), ( {CONF_SLAVE_COUNT: 7}, - [0x01], + [True, False] * 4, STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - ], + [STATE_OFF, STATE_ON] * 3 + [STATE_OFF], ), ( - {CONF_SLAVE_COUNT: 7}, - [0x82], - STATE_OFF, - [ - STATE_ON, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x01, 0x00], + {CONF_SLAVE_COUNT: 31}, + [True, False] * 16, STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x01, 0x01], - STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - STATE_OFF, - STATE_OFF, - ], - ), - ( - {CONF_SLAVE_COUNT: 10}, - [0x81, 0x01], - STATE_ON, - [ - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_OFF, - STATE_ON, - STATE_ON, - STATE_OFF, - STATE_OFF, - ], + [STATE_OFF, STATE_ON] * 15 + [STATE_OFF], ), ], )