From 72b0c4b7bcb1383f0d195df6f69abd9bde199038 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 28 Aug 2026 19:19:06 +0200 Subject: [PATCH] Honor the Modbus verify states for coils (#180490) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- homeassistant/components/modbus/entity.py | 31 ++++++++-------- tests/components/modbus/test_switch.py | 44 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/modbus/entity.py b/homeassistant/components/modbus/entity.py index 182251d0ce14..df7c3288230f 100644 --- a/homeassistant/components/modbus/entity.py +++ b/homeassistant/components/modbus/entity.py @@ -374,20 +374,21 @@ class ModbusToggleEntity(ModbusBaseEntity, ToggleEntity, RestoreEntity): self._attr_available = True if self._verify_type in (CALL_TYPE_COIL, CALL_TYPE_DISCRETE): - self._attr_is_on = bool(result.bits[0] & 1) + value = int(result.bits[0] & 1) else: value = int(result.registers[0]) - if value in self._state_on: - self._attr_is_on = True - elif value in self._state_off: - self._attr_is_on = False - elif value is not None: - LOGGER.error( - ( - "Unexpected response from modbus device slave %s register %s," - " got 0x%2x" - ), - self._device_address, - self._verify_address, - value, - ) + + if value in self._state_on: + self._attr_is_on = True + elif value in self._state_off: + self._attr_is_on = False + elif value is not None: + LOGGER.error( + ( + "Unexpected response from modbus device slave %s register %s," + " got 0x%2x" + ), + self._device_address, + self._verify_address, + value, + ) diff --git a/tests/components/modbus/test_switch.py b/tests/components/modbus/test_switch.py index f9763e80307a..3175bb58cf98 100644 --- a/tests/components/modbus/test_switch.py +++ b/tests/components/modbus/test_switch.py @@ -478,6 +478,50 @@ async def test_service_switch_update(hass: HomeAssistant, mock_modbus_ha) -> Non assert hass.states.get(ENTITY_ID).state == STATE_ON +@pytest.mark.parametrize( + "do_config", + [ + { + CONF_SWITCHES: [ + { + CONF_NAME: TEST_ENTITY_NAME, + CONF_ADDRESS: 1234, + CONF_WRITE_TYPE: CALL_TYPE_COIL, + CONF_COMMAND_ON: 0, + CONF_COMMAND_OFF: 1, + CONF_VERIFY: { + CONF_STATE_ON: [0], + CONF_STATE_OFF: [1], + }, + } + ] + }, + ], +) +@pytest.mark.parametrize( + ("coil_value", "expected_state"), + [ + (0x00, STATE_ON), + (0x01, STATE_OFF), + ], +) +async def test_switch_verify_coil_inverted( + hass: HomeAssistant, + mock_modbus_ha: mock.AsyncMock, + coil_value: int, + expected_state: str, +) -> None: + """Run test for an inverted coil switch, where state_on is 0.""" + mock_modbus_ha.read_coils.return_value = ReadResult([coil_value]) + await hass.services.async_call( + HOMEASSISTANT_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: ENTITY_ID}, + blocking=True, + ) + assert hass.states.get(ENTITY_ID).state == expected_state + + @pytest.mark.parametrize( "do_config", [