diff --git a/homeassistant/components/slide_local/button.py b/homeassistant/components/slide_local/button.py index 9c28588111674..795cd4f1c2e2a 100644 --- a/homeassistant/components/slide_local/button.py +++ b/homeassistant/components/slide_local/button.py @@ -2,16 +2,25 @@ from __future__ import annotations +from goslideapi.goslideapi import ( + AuthenticationFailed, + ClientConnectionError, + ClientTimeoutError, + DigestAuthCalcError, +) + from homeassistant.components.button import ButtonEntity from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import SlideConfigEntry +from .const import DOMAIN from .coordinator import SlideCoordinator from .entity import SlideEntity -PARALLEL_UPDATES = 0 +PARALLEL_UPDATES = 1 async def async_setup_entry( @@ -39,4 +48,15 @@ def __init__(self, coordinator: SlideCoordinator) -> None: async def async_press(self) -> None: """Send out a calibrate command.""" - await self.coordinator.slide.slide_calibrate(self.coordinator.host) + try: + await self.coordinator.slide.slide_calibrate(self.coordinator.host) + except ( + ClientConnectionError, + AuthenticationFailed, + ClientTimeoutError, + DigestAuthCalcError, + ) as ex: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="calibration_error", + ) from ex diff --git a/homeassistant/components/slide_local/quality_scale.yaml b/homeassistant/components/slide_local/quality_scale.yaml index 887b90b6b1135..4833f19e2b20f 100644 --- a/homeassistant/components/slide_local/quality_scale.yaml +++ b/homeassistant/components/slide_local/quality_scale.yaml @@ -25,9 +25,7 @@ rules: config-entry-unloading: done log-when-unavailable: done entity-unavailable: done - action-exceptions: - status: exempt - comment: No custom action. + action-exceptions: done reauthentication-flow: todo parallel-updates: done test-coverage: todo diff --git a/homeassistant/components/slide_local/strings.json b/homeassistant/components/slide_local/strings.json index 24c03d2ff964a..6aeda9f92fd4d 100644 --- a/homeassistant/components/slide_local/strings.json +++ b/homeassistant/components/slide_local/strings.json @@ -54,6 +54,12 @@ } }, "exceptions": { + "calibration_error": { + "message": "Error while sending the calibration request to the device." + }, + "touchgo_error": { + "message": "Error while sending the request setting Touch&Go to {state} to the device." + }, "update_error": { "message": "Error while updating data from the API." } diff --git a/homeassistant/components/slide_local/switch.py b/homeassistant/components/slide_local/switch.py index 6d357864c4899..f1c33f9a76f7c 100644 --- a/homeassistant/components/slide_local/switch.py +++ b/homeassistant/components/slide_local/switch.py @@ -4,16 +4,25 @@ from typing import Any +from goslideapi.goslideapi import ( + AuthenticationFailed, + ClientConnectionError, + ClientTimeoutError, + DigestAuthCalcError, +) + from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import SlideConfigEntry +from .const import DOMAIN from .coordinator import SlideCoordinator from .entity import SlideEntity -PARALLEL_UPDATES = 0 +PARALLEL_UPDATES = 1 async def async_setup_entry( @@ -47,10 +56,38 @@ def is_on(self) -> bool: async def async_turn_off(self, **kwargs: Any) -> None: """Turn off touchgo.""" - await self.coordinator.slide.slide_set_touchgo(self.coordinator.host, False) + try: + await self.coordinator.slide.slide_set_touchgo(self.coordinator.host, False) + except ( + ClientConnectionError, + AuthenticationFailed, + ClientTimeoutError, + DigestAuthCalcError, + ) as ex: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="touchgo_error", + translation_placeholders={ + "state": "off", + }, + ) from ex await self.coordinator.async_request_refresh() async def async_turn_on(self, **kwargs: Any) -> None: """Turn on touchgo.""" - await self.coordinator.slide.slide_set_touchgo(self.coordinator.host, True) + try: + await self.coordinator.slide.slide_set_touchgo(self.coordinator.host, True) + except ( + ClientConnectionError, + AuthenticationFailed, + ClientTimeoutError, + DigestAuthCalcError, + ) as ex: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="touchgo_error", + translation_placeholders={ + "state": "on", + }, + ) from ex await self.coordinator.async_request_refresh() diff --git a/tests/components/slide_local/test_button.py b/tests/components/slide_local/test_button.py index 646c8fd7ef3b2..c232affbb99e0 100644 --- a/tests/components/slide_local/test_button.py +++ b/tests/components/slide_local/test_button.py @@ -2,11 +2,19 @@ from unittest.mock import AsyncMock +from goslideapi.goslideapi import ( + AuthenticationFailed, + ClientConnectionError, + ClientTimeoutError, + DigestAuthCalcError, +) +import pytest from syrupy import SnapshotAssertion from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import setup_platform @@ -44,3 +52,37 @@ async def test_pressing_button( blocking=True, ) mock_slide_api.slide_calibrate.assert_called_once() + + +@pytest.mark.parametrize( + ("exception"), + [ + ClientConnectionError, + ClientTimeoutError, + AuthenticationFailed, + DigestAuthCalcError, + ], +) +async def test_pressing_button_exception( + hass: HomeAssistant, + exception: Exception, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test pressing button.""" + await setup_platform(hass, mock_config_entry, [Platform.BUTTON]) + + mock_slide_api.slide_calibrate.side_effect = exception + + with pytest.raises( + HomeAssistantError, + match="Error while sending the calibration request to the device", + ): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + { + ATTR_ENTITY_ID: "button.slide_bedroom_calibrate", + }, + blocking=True, + ) diff --git a/tests/components/slide_local/test_switch.py b/tests/components/slide_local/test_switch.py index 0ac9820ca10c0..9d0d8274aa56d 100644 --- a/tests/components/slide_local/test_switch.py +++ b/tests/components/slide_local/test_switch.py @@ -2,6 +2,12 @@ from unittest.mock import AsyncMock +from goslideapi.goslideapi import ( + AuthenticationFailed, + ClientConnectionError, + ClientTimeoutError, + DigestAuthCalcError, +) import pytest from syrupy import SnapshotAssertion @@ -13,6 +19,7 @@ ) from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import setup_platform @@ -59,3 +66,38 @@ async def test_services( blocking=True, ) mock_slide_api.slide_set_touchgo.assert_called_once() + + +@pytest.mark.parametrize( + ("exception", "service"), + [ + (ClientConnectionError, SERVICE_TURN_OFF), + (ClientTimeoutError, SERVICE_TURN_ON), + (AuthenticationFailed, SERVICE_TURN_OFF), + (DigestAuthCalcError, SERVICE_TURN_ON), + ], +) +async def test_service_exception( + hass: HomeAssistant, + exception: Exception, + service: str, + mock_slide_api: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test pressing button.""" + await setup_platform(hass, mock_config_entry, [Platform.SWITCH]) + + mock_slide_api.slide_set_touchgo.side_effect = exception + + with pytest.raises( + HomeAssistantError, + match=f"Error while sending the request setting Touch&Go to {service[5:]} to the device", + ): + await hass.services.async_call( + SWITCH_DOMAIN, + service, + { + ATTR_ENTITY_ID: "switch.slide_bedroom_touchgo", + }, + blocking=True, + )