From ce38d8542fd5415b24a149ef29ee1e8e473b5b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Costa?= Date: Sat, 18 Nov 2023 22:17:05 +0000 Subject: [PATCH] Update Idasen Desk to fulfill Silver requirements (#102979) * Update Idasen Desk to fulfill Silver requirements * Add tests --- .../components/idasen_desk/__init__.py | 6 ++++ homeassistant/components/idasen_desk/cover.py | 25 +++++++++++--- .../components/idasen_desk/manifest.json | 1 + tests/components/idasen_desk/test_cover.py | 33 +++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/idasen_desk/__init__.py b/homeassistant/components/idasen_desk/__init__.py index 0a17ebec96c..564406d423e 100644 --- a/homeassistant/components/idasen_desk/__init__.py +++ b/homeassistant/components/idasen_desk/__init__.py @@ -44,6 +44,7 @@ class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): super().__init__(hass, logger, name=name) self._address = address self._expected_connected = False + self._connection_lost = False self.desk = Desk(self.async_set_updated_data) @@ -63,6 +64,7 @@ class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): """Disconnect from desk.""" _LOGGER.debug("Disconnecting from %s", self._address) self._expected_connected = False + self._connection_lost = False await self.desk.disconnect() @callback @@ -71,7 +73,11 @@ class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): if self._expected_connected: if not self.desk.is_connected: _LOGGER.debug("Desk disconnected. Reconnecting") + self._connection_lost = True self.hass.async_create_task(self.async_connect()) + elif self._connection_lost: + _LOGGER.info("Reconnected to desk") + self._connection_lost = False elif self.desk.is_connected: _LOGGER.warning("Desk is connected but should not be. Disconnecting") self.hass.async_create_task(self.desk.disconnect()) diff --git a/homeassistant/components/idasen_desk/cover.py b/homeassistant/components/idasen_desk/cover.py index 3148616d182..1daebe52420 100644 --- a/homeassistant/components/idasen_desk/cover.py +++ b/homeassistant/components/idasen_desk/cover.py @@ -3,6 +3,8 @@ from __future__ import annotations from typing import Any +from bleak.exc import BleakError + from homeassistant.components.cover import ( ATTR_POSITION, CoverDeviceClass, @@ -12,6 +14,7 @@ from homeassistant.components.cover import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_NAME from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -71,19 +74,33 @@ class IdasenDeskCover(CoordinatorEntity[IdasenDeskCoordinator], CoverEntity): async def async_close_cover(self, **kwargs: Any) -> None: """Close the cover.""" - await self._desk.move_down() + try: + await self._desk.move_down() + except BleakError as err: + raise HomeAssistantError("Failed to move down: Bluetooth error") from err async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" - await self._desk.move_up() + try: + await self._desk.move_up() + except BleakError as err: + raise HomeAssistantError("Failed to move up: Bluetooth error") from err async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" - await self._desk.stop() + try: + await self._desk.stop() + except BleakError as err: + raise HomeAssistantError("Failed to stop moving: Bluetooth error") from err async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover shutter to a specific position.""" - await self._desk.move_to(int(kwargs[ATTR_POSITION])) + try: + await self._desk.move_to(int(kwargs[ATTR_POSITION])) + except BleakError as err: + raise HomeAssistantError( + "Failed to move to specified position: Bluetooth error" + ) from err @callback def _handle_coordinator_update(self, *args: Any) -> None: diff --git a/homeassistant/components/idasen_desk/manifest.json b/homeassistant/components/idasen_desk/manifest.json index ed941f4f87d..9681b2136e1 100644 --- a/homeassistant/components/idasen_desk/manifest.json +++ b/homeassistant/components/idasen_desk/manifest.json @@ -11,5 +11,6 @@ "dependencies": ["bluetooth_adapters"], "documentation": "https://www.home-assistant.io/integrations/idasen_desk", "iot_class": "local_push", + "quality_scale": "silver", "requirements": ["idasen-ha==2.3"] } diff --git a/tests/components/idasen_desk/test_cover.py b/tests/components/idasen_desk/test_cover.py index a9c74be7081..4c8bf7806e0 100644 --- a/tests/components/idasen_desk/test_cover.py +++ b/tests/components/idasen_desk/test_cover.py @@ -2,6 +2,7 @@ from typing import Any from unittest.mock import MagicMock +from bleak.exc import BleakError import pytest from homeassistant.components.cover import ( @@ -19,6 +20,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from . import init_integration @@ -80,3 +82,34 @@ async def test_cover_services( assert state assert state.state == expected_state assert state.attributes[ATTR_CURRENT_POSITION] == expected_position + + +@pytest.mark.parametrize( + ("service", "service_data", "mock_method_name"), + [ + (SERVICE_SET_COVER_POSITION, {ATTR_POSITION: 100}, "move_to"), + (SERVICE_OPEN_COVER, {}, "move_up"), + (SERVICE_CLOSE_COVER, {}, "move_down"), + (SERVICE_STOP_COVER, {}, "stop"), + ], +) +async def test_cover_services_exception( + hass: HomeAssistant, + mock_desk_api: MagicMock, + service: str, + service_data: dict[str, Any], + mock_method_name: str, +) -> None: + """Test cover services exception handling.""" + entity_id = "cover.test" + await init_integration(hass) + fail_call = getattr(mock_desk_api, mock_method_name) + fail_call.side_effect = BleakError() + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + COVER_DOMAIN, + service, + {"entity_id": entity_id, **service_data}, + blocking=True, + ) + await hass.async_block_till_done()