From f3442b20b714352407e4cbfd9846606b2262430b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:26:42 +0200 Subject: [PATCH] Fix stop service in modbus (#181915) --- homeassistant/components/modbus/const.py | 2 +- homeassistant/components/modbus/entity.py | 6 ++- homeassistant/components/modbus/services.py | 2 +- tests/components/modbus/test_services.py | 48 +++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/modbus/const.py b/homeassistant/components/modbus/const.py index 18829e4cebe7..8e0c0e0c882f 100644 --- a/homeassistant/components/modbus/const.py +++ b/homeassistant/components/modbus/const.py @@ -155,7 +155,7 @@ SERVICE_WRITE_REGISTER = "write_register" SERVICE_STOP = "stop" # dispatcher signals -SIGNAL_STOP_ENTITY = "modbus.stop" +SIGNAL_STOP_ENTITY = "modbus.stop_{}" # integration names DEFAULT_HUB = "modbus_hub" diff --git a/homeassistant/components/modbus/entity.py b/homeassistant/components/modbus/entity.py index df7c3288230f..5e6934099a11 100644 --- a/homeassistant/components/modbus/entity.py +++ b/homeassistant/components/modbus/entity.py @@ -149,7 +149,11 @@ class ModbusBaseEntity(Entity): ) ) self.async_on_remove( - async_dispatcher_connect(self.hass, SIGNAL_STOP_ENTITY, self.async_disable) + async_dispatcher_connect( + self.hass, + SIGNAL_STOP_ENTITY.format(self._hub.name), + self.async_disable, + ) ) diff --git a/homeassistant/components/modbus/services.py b/homeassistant/components/modbus/services.py index ff49255dbdea..834707d48c8c 100644 --- a/homeassistant/components/modbus/services.py +++ b/homeassistant/components/modbus/services.py @@ -101,7 +101,7 @@ async def _async_stop_hub(service: ServiceCall) -> None: """Stop Modbus hub.""" hass = service.hass hub = _get_hubs(hass)[service.data[ATTR_HUB]] - async_dispatcher_send(hass, SIGNAL_STOP_ENTITY) + async_dispatcher_send(hass, SIGNAL_STOP_ENTITY.format(hub.name)) await hub.async_close() diff --git a/tests/components/modbus/test_services.py b/tests/components/modbus/test_services.py index 52a96de3e78e..14ea2ce635fb 100644 --- a/tests/components/modbus/test_services.py +++ b/tests/components/modbus/test_services.py @@ -28,6 +28,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError +from homeassistant.helpers.entity_platform import async_get_platforms from homeassistant.setup import async_setup_component from tests.common import get_fixture_path @@ -162,3 +163,50 @@ async def test_service_after_failed_reload_raises( await hass.services.async_call(DOMAIN, service, data, blocking=True) assert err.value.translation_key == "not_loaded" + + +TEST_HUB_A = "hub_a" +TEST_HUB_B = "hub_b" + + +def _two_hub_config() -> dict: + """Return a config with two hubs, each exposing one sensor.""" + return { + DOMAIN: [ + { + CONF_NAME: name, + CONF_TYPE: "tcp", + CONF_HOST: "modbusHost", + CONF_PORT: port, + CONF_SENSORS: [{CONF_NAME: f"sensor_{name}", CONF_ADDRESS: 1}], + } + for name, port in ((TEST_HUB_A, 5501), (TEST_HUB_B, 5502)) + ] + } + + +@pytest.mark.usefixtures("mock_pymodbus") +async def test_stop_only_disables_the_selected_hub(hass: HomeAssistant) -> None: + """Test stopping one hub leaves the other hub's entities alone.""" + assert await async_setup_component(hass, DOMAIN, _two_hub_config()) + await hass.async_block_till_done() + + # async_disable only flips availability, it does not write the state, so the + # entity objects rather than the state machine show the effect. + entities = { + entity.entity_id: entity + for platform in async_get_platforms(hass, DOMAIN) + for entity in platform.entities.values() + } + entity_a = entities[f"sensor.sensor_{TEST_HUB_A}"] + entity_b = entities[f"sensor.sensor_{TEST_HUB_B}"] + assert entity_a.available + assert entity_b.available + + await hass.services.async_call( + DOMAIN, SERVICE_STOP, {ATTR_HUB: TEST_HUB_A}, blocking=True + ) + await hass.async_block_till_done() + + assert not entity_a.available + assert entity_b.available