From 8de23b955953bc1789a39a444639bc994179d047 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 16 Apr 2025 13:49:37 +0200 Subject: [PATCH] Raise on failed switching in devolo Home Network (#143072) --- .../components/devolo_home_network/switch.py | 22 +++- tests/components/devolo_home_network/mock.py | 2 + .../devolo_home_network/test_switch.py | 113 ++++++++---------- 3 files changed, 69 insertions(+), 68 deletions(-) diff --git a/homeassistant/components/devolo_home_network/switch.py b/homeassistant/components/devolo_home_network/switch.py index 0271270fa09..b57305a7a77 100644 --- a/homeassistant/components/devolo_home_network/switch.py +++ b/homeassistant/components/devolo_home_network/switch.py @@ -114,9 +114,14 @@ class DevoloSwitchEntity[_DataT: _DataType]( translation_key="password_protected", translation_placeholders={"title": self.entry.title}, ) from ex - except DeviceUnavailable: - pass # The coordinator will handle this - await self.coordinator.async_request_refresh() + except DeviceUnavailable as ex: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="no_response", + translation_placeholders={"title": self.entry.title}, + ) from ex + finally: + await self.coordinator.async_request_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the entity off.""" @@ -129,6 +134,11 @@ class DevoloSwitchEntity[_DataT: _DataType]( translation_key="password_protected", translation_placeholders={"title": self.entry.title}, ) from ex - except DeviceUnavailable: - pass # The coordinator will handle this - await self.coordinator.async_request_refresh() + except DeviceUnavailable as ex: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="no_response", + translation_placeholders={"title": self.entry.title}, + ) from ex + finally: + await self.coordinator.async_request_refresh() diff --git a/tests/components/devolo_home_network/mock.py b/tests/components/devolo_home_network/mock.py index 82bf3e5ad76..d0dc89a988b 100644 --- a/tests/components/devolo_home_network/mock.py +++ b/tests/components/devolo_home_network/mock.py @@ -64,6 +64,7 @@ class MockDevice(Device): return_value=FIRMWARE_UPDATE_AVAILABLE ) self.device.async_get_led_setting = AsyncMock(return_value=False) + self.device.async_set_led_setting = AsyncMock(return_value=True) self.device.async_restart = AsyncMock(return_value=True) self.device.async_uptime = AsyncMock(return_value=UPTIME) self.device.async_start_wps = AsyncMock(return_value=True) @@ -71,6 +72,7 @@ class MockDevice(Device): return_value=CONNECTED_STATIONS ) self.device.async_get_wifi_guest_access = AsyncMock(return_value=GUEST_WIFI) + self.device.async_set_wifi_guest_access = AsyncMock(return_value=True) self.device.async_get_wifi_neighbor_access_points = AsyncMock( return_value=NEIGHBOR_ACCESS_POINTS ) diff --git a/tests/components/devolo_home_network/test_switch.py b/tests/components/devolo_home_network/test_switch.py index b96697dc9cc..7a342780877 100644 --- a/tests/components/devolo_home_network/test_switch.py +++ b/tests/components/devolo_home_network/test_switch.py @@ -1,7 +1,7 @@ """Tests for the devolo Home Network switch.""" from datetime import timedelta -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock from devolo_plc_api.device_api import WifiGuestAccessGet from devolo_plc_api.exceptions.device import DevicePasswordProtected, DeviceUnavailable @@ -16,6 +16,7 @@ from homeassistant.components.devolo_home_network.const import ( from homeassistant.components.switch import DOMAIN as PLATFORM from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, @@ -106,18 +107,15 @@ async def test_update_enable_guest_wifi( mock_device.device.async_get_wifi_guest_access.return_value = WifiGuestAccessGet( enabled=False ) - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_wifi_guest_access", - new=AsyncMock(), - ) as turn_off: - await hass.services.async_call( - PLATFORM, SERVICE_TURN_OFF, {"entity_id": state_key}, blocking=True - ) + await hass.services.async_call( + PLATFORM, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: state_key}, blocking=True + ) - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_OFF - turn_off.assert_called_once_with(False) + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_OFF + mock_device.device.async_set_wifi_guest_access.assert_called_once_with(False) + mock_device.device.async_set_wifi_guest_access.reset_mock() freezer.tick(REQUEST_REFRESH_DEFAULT_COOLDOWN) async_fire_time_changed(hass) @@ -127,18 +125,15 @@ async def test_update_enable_guest_wifi( mock_device.device.async_get_wifi_guest_access.return_value = WifiGuestAccessGet( enabled=True ) - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_wifi_guest_access", - new=AsyncMock(), - ) as turn_on: - await hass.services.async_call( - PLATFORM, SERVICE_TURN_ON, {"entity_id": state_key}, blocking=True - ) + await hass.services.async_call( + PLATFORM, SERVICE_TURN_ON, {ATTR_ENTITY_ID: state_key}, blocking=True + ) - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_ON - turn_on.assert_called_once_with(True) + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_ON + mock_device.device.async_set_wifi_guest_access.assert_called_once_with(True) + mock_device.device.async_set_wifi_guest_access.reset_mock() freezer.tick(REQUEST_REFRESH_DEFAULT_COOLDOWN) async_fire_time_changed(hass) @@ -146,17 +141,17 @@ async def test_update_enable_guest_wifi( # Device unavailable mock_device.device.async_get_wifi_guest_access.side_effect = DeviceUnavailable() - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_wifi_guest_access", - side_effect=DeviceUnavailable, + mock_device.device.async_set_wifi_guest_access.side_effect = DeviceUnavailable() + + with pytest.raises( + HomeAssistantError, match=f"Device {entry.title} did not respond" ): await hass.services.async_call( - PLATFORM, SERVICE_TURN_ON, {"entity_id": state_key}, blocking=True + PLATFORM, SERVICE_TURN_ON, {ATTR_ENTITY_ID: state_key}, blocking=True ) - - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_UNAVAILABLE + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_UNAVAILABLE await hass.config_entries.async_unload(entry.entry_id) @@ -191,18 +186,15 @@ async def test_update_enable_leds( # Switch off mock_device.device.async_get_led_setting.return_value = False - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_led_setting", - new=AsyncMock(), - ) as turn_off: - await hass.services.async_call( - PLATFORM, SERVICE_TURN_OFF, {"entity_id": state_key}, blocking=True - ) + await hass.services.async_call( + PLATFORM, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: state_key}, blocking=True + ) - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_OFF - turn_off.assert_called_once_with(False) + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_OFF + mock_device.device.async_set_led_setting.assert_called_once_with(False) + mock_device.device.async_set_led_setting.reset_mock() freezer.tick(REQUEST_REFRESH_DEFAULT_COOLDOWN) async_fire_time_changed(hass) @@ -210,18 +202,15 @@ async def test_update_enable_leds( # Switch on mock_device.device.async_get_led_setting.return_value = True - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_led_setting", - new=AsyncMock(), - ) as turn_on: - await hass.services.async_call( - PLATFORM, SERVICE_TURN_ON, {"entity_id": state_key}, blocking=True - ) + await hass.services.async_call( + PLATFORM, SERVICE_TURN_ON, {ATTR_ENTITY_ID: state_key}, blocking=True + ) - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_ON - turn_on.assert_called_once_with(True) + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_ON + mock_device.device.async_set_led_setting.assert_called_once_with(True) + mock_device.device.async_set_led_setting.reset_mock() freezer.tick(REQUEST_REFRESH_DEFAULT_COOLDOWN) async_fire_time_changed(hass) @@ -229,17 +218,17 @@ async def test_update_enable_leds( # Device unavailable mock_device.device.async_get_led_setting.side_effect = DeviceUnavailable() - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_set_led_setting", - side_effect=DeviceUnavailable, + mock_device.device.async_set_led_setting.side_effect = DeviceUnavailable() + + with pytest.raises( + HomeAssistantError, match=f"Device {entry.title} did not respond" ): await hass.services.async_call( - PLATFORM, SERVICE_TURN_OFF, {"entity_id": state_key}, blocking=True + PLATFORM, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: state_key}, blocking=True ) - - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_UNAVAILABLE + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_UNAVAILABLE await hass.config_entries.async_unload(entry.entry_id) @@ -308,7 +297,7 @@ async def test_auth_failed( with pytest.raises(HomeAssistantError): await hass.services.async_call( - PLATFORM, SERVICE_TURN_ON, {"entity_id": state_key}, blocking=True + PLATFORM, SERVICE_TURN_ON, {ATTR_ENTITY_ID: state_key}, blocking=True ) await hass.async_block_till_done()