Add action exceptions to Alexa Devices (#147546)

This commit is contained in:
Simone Chemelli 2025-06-26 20:59:02 +03:00 committed by Franck Nijhof
parent 6b2aaf3fdb
commit 03f9caf3eb
No known key found for this signature in database
GPG Key ID: AB33ADACE7101952
6 changed files with 109 additions and 1 deletions

View File

@ -15,6 +15,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity from .entity import AmazonEntity
from .utils import alexa_api_call
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 1
@ -70,6 +71,7 @@ class AmazonNotifyEntity(AmazonEntity, NotifyEntity):
entity_description: AmazonNotifyEntityDescription entity_description: AmazonNotifyEntityDescription
@alexa_api_call
async def async_send_message( async def async_send_message(
self, message: str, title: str | None = None, **kwargs: Any self, message: str, title: str | None = None, **kwargs: Any
) -> None: ) -> None:

View File

@ -26,7 +26,7 @@ rules:
unique-config-entry: done unique-config-entry: done
# Silver # Silver
action-exceptions: todo action-exceptions: done
config-entry-unloading: done config-entry-unloading: done
docs-configuration-parameters: todo docs-configuration-parameters: todo
docs-installation-parameters: todo docs-installation-parameters: todo

View File

@ -70,5 +70,13 @@
"name": "Do not disturb" "name": "Do not disturb"
} }
} }
},
"exceptions": {
"cannot_connect": {
"message": "Error connecting: {error}"
},
"cannot_retrieve_data": {
"message": "Error retrieving data: {error}"
}
} }
} }

View File

@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry from .coordinator import AmazonConfigEntry
from .entity import AmazonEntity from .entity import AmazonEntity
from .utils import alexa_api_call
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 1
@ -60,6 +61,7 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity):
entity_description: AmazonSwitchEntityDescription entity_description: AmazonSwitchEntityDescription
@alexa_api_call
async def _switch_set_state(self, state: bool) -> None: async def _switch_set_state(self, state: bool) -> None:
"""Set desired switch state.""" """Set desired switch state."""
method = getattr(self.coordinator.api, self.entity_description.method) method = getattr(self.coordinator.api, self.entity_description.method)

View File

@ -0,0 +1,40 @@
"""Utils for Alexa Devices."""
from collections.abc import Awaitable, Callable, Coroutine
from functools import wraps
from typing import Any, Concatenate
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData
from homeassistant.exceptions import HomeAssistantError
from .const import DOMAIN
from .entity import AmazonEntity
def alexa_api_call[_T: AmazonEntity, **_P](
func: Callable[Concatenate[_T, _P], Awaitable[None]],
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]:
"""Catch Alexa API call exceptions."""
@wraps(func)
async def cmd_wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
"""Wrap all command methods."""
try:
await func(self, *args, **kwargs)
except CannotConnect as err:
self.coordinator.last_update_success = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except CannotRetrieveData as err:
self.coordinator.last_update_success = False
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_retrieve_data",
translation_placeholders={"error": repr(err)},
) from err
return cmd_wrapper

View File

@ -0,0 +1,56 @@
"""Tests for Alexa Devices utils."""
from unittest.mock import AsyncMock
from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData
import pytest
from homeassistant.components.alexa_devices.const import DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SERVICE_TURN_ON
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import setup_integration
from tests.common import MockConfigEntry
ENTITY_ID = "switch.echo_test_do_not_disturb"
@pytest.mark.parametrize(
("side_effect", "key", "error"),
[
(CannotConnect, "cannot_connect", "CannotConnect()"),
(CannotRetrieveData, "cannot_retrieve_data", "CannotRetrieveData()"),
],
)
async def test_alexa_api_call_exceptions(
hass: HomeAssistant,
mock_amazon_devices_client: AsyncMock,
mock_config_entry: MockConfigEntry,
side_effect: Exception,
key: str,
error: str,
) -> None:
"""Test alexa_api_call decorator for exceptions."""
await setup_integration(hass, mock_config_entry)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_OFF
mock_amazon_devices_client.set_do_not_disturb.side_effect = side_effect
# Call API
with pytest.raises(HomeAssistantError) as exc_info:
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
assert exc_info.value.translation_domain == DOMAIN
assert exc_info.value.translation_key == key
assert exc_info.value.translation_placeholders == {"error": error}