From 4d9ff1338459e10530eaabe109d3118774f1160f Mon Sep 17 00:00:00 2001 From: jan iversen Date: Thu, 8 Oct 2020 23:52:41 +0200 Subject: [PATCH] Fix bool registers in modbus integration (#41506) When a register is of type bool modbus returns a whole byte, but ONLY the lowest bit determine true/false. --- .../components/modbus/binary_sensor.py | 2 +- homeassistant/components/modbus/cover.py | 2 +- homeassistant/components/modbus/switch.py | 2 +- .../modbus/test_modbus_binary_sensor.py | 21 +++++++ tests/components/modbus/test_modbus_switch.py | 58 +++++++++---------- 5 files changed, 52 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/modbus/binary_sensor.py b/homeassistant/components/modbus/binary_sensor.py index ce66b7aecdb..8d80a2a45d0 100644 --- a/homeassistant/components/modbus/binary_sensor.py +++ b/homeassistant/components/modbus/binary_sensor.py @@ -122,5 +122,5 @@ class ModbusBinarySensor(BinarySensorEntity): self._available = False return - self._value = result.bits[0] + self._value = result.bits[0] & 1 self._available = True diff --git a/homeassistant/components/modbus/cover.py b/homeassistant/components/modbus/cover.py index a7c9c301ac5..e6040daefac 100644 --- a/homeassistant/components/modbus/cover.py +++ b/homeassistant/components/modbus/cover.py @@ -228,7 +228,7 @@ class ModbusCover(CoverEntity, RestoreEntity): self._available = False return - value = bool(result.bits[0]) + value = bool(result.bits[0] & 1) self._available = True return value diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index fa5b42807b0..5b79d71489e 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -174,7 +174,7 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity): # bits[0] select the lowest bit in result, # is_on for a binary_sensor is true if the bit are 1 # The other bits are not considered. - return bool(result.bits[0]) + return bool(result.bits[0] & 1) def _write_coil(self, coil, value): """Write coil using the Modbus hub slave.""" diff --git a/tests/components/modbus/test_modbus_binary_sensor.py b/tests/components/modbus/test_modbus_binary_sensor.py index cb59104458e..53021b57849 100644 --- a/tests/components/modbus/test_modbus_binary_sensor.py +++ b/tests/components/modbus/test_modbus_binary_sensor.py @@ -29,6 +29,13 @@ _LOGGER = logging.getLogger(__name__) [0xFF], STATE_ON, ), + ( + { + CONF_INPUT_TYPE: CALL_TYPE_COIL, + }, + [0x01], + STATE_ON, + ), ( { CONF_INPUT_TYPE: CALL_TYPE_COIL, @@ -36,6 +43,20 @@ _LOGGER = logging.getLogger(__name__) [0x00], STATE_OFF, ), + ( + { + CONF_INPUT_TYPE: CALL_TYPE_COIL, + }, + [0x80], + STATE_OFF, + ), + ( + { + CONF_INPUT_TYPE: CALL_TYPE_COIL, + }, + [0xFE], + STATE_OFF, + ), ( { CONF_INPUT_TYPE: CALL_TYPE_DISCRETE, diff --git a/tests/components/modbus/test_modbus_switch.py b/tests/components/modbus/test_modbus_switch.py index 9f7b6b600cd..1f8ebe91a28 100644 --- a/tests/components/modbus/test_modbus_switch.py +++ b/tests/components/modbus/test_modbus_switch.py @@ -13,34 +13,6 @@ from .conftest import run_base_read_test, setup_base_test _LOGGER = logging.getLogger(__name__) -async def run_sensor_test(hass, use_mock_hub, value, expected): - """Run test for given config.""" - switch_name = "modbus_test_switch" - scan_interval = 5 - entity_id, now, device = await setup_base_test( - switch_name, - hass, - use_mock_hub, - { - CONF_COILS: [ - {CONF_NAME: switch_name, CALL_TYPE_COIL: 1234, CONF_SLAVE: 1}, - ] - }, - SWITCH_DOMAIN, - scan_interval, - ) - - await run_base_read_test( - entity_id, - hass, - use_mock_hub, - CALL_TYPE_COIL, - value, - expected, - now + timedelta(seconds=scan_interval + 1), - ) - - @pytest.mark.parametrize( "regs,expected", [ @@ -48,6 +20,14 @@ async def run_sensor_test(hass, use_mock_hub, value, expected): [0x00], STATE_OFF, ), + ( + [0x80], + STATE_OFF, + ), + ( + [0xFE], + STATE_OFF, + ), ( [0xFF], STATE_ON, @@ -59,10 +39,28 @@ async def run_sensor_test(hass, use_mock_hub, value, expected): ], ) async def test_coil_switch(hass, mock_hub, regs, expected): - """Test reading of switch coil.""" - await run_sensor_test( + """Run test for given config.""" + switch_name = "modbus_test_switch" + scan_interval = 5 + entity_id, now, device = await setup_base_test( + switch_name, hass, mock_hub, + { + CONF_COILS: [ + {CONF_NAME: switch_name, CALL_TYPE_COIL: 1234, CONF_SLAVE: 1}, + ] + }, + SWITCH_DOMAIN, + scan_interval, + ) + + await run_base_read_test( + entity_id, + hass, + mock_hub, + CALL_TYPE_COIL, regs, expected, + now + timedelta(seconds=scan_interval + 1), )