diff --git a/homeassistant/components/alexa/errors.py b/homeassistant/components/alexa/errors.py index f4c50a24267..0ce00f1fe48 100644 --- a/homeassistant/components/alexa/errors.py +++ b/homeassistant/components/alexa/errors.py @@ -1,6 +1,8 @@ """Alexa related errors.""" from __future__ import annotations +from typing import Literal + from homeassistant.exceptions import HomeAssistantError from .const import API_TEMP_UNITS @@ -58,6 +60,30 @@ class AlexaInvalidValueError(AlexaError): error_type = "INVALID_VALUE" +class AlexaInteralError(AlexaError): + """Class to represent internal errors.""" + + namespace = "Alexa" + error_type = "INTERNAL_ERROR" + + +class AlexaNotSupportedInCurrentMode(AlexaError): + """The device is not in the correct mode to support this command.""" + + namespace = "Alexa" + error_type = "NOT_SUPPORTED_IN_CURRENT_MODE" + + def __init__( + self, + endpoint_id: str, + current_mode: Literal["COLOR", "ASLEEP", "NOT_PROVISIONED", "OTHER"], + ) -> None: + """Initialize invalid endpoint error.""" + msg = f"Not supported while in {current_mode} mode" + AlexaError.__init__(self, msg, {"currentDeviceMode": current_mode}) + self.endpoint_id = endpoint_id + + class AlexaUnsupportedThermostatModeError(AlexaError): """Class to represent UnsupportedThermostatMode errors.""" diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index c0b0782f62e..f3f669de3b3 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -212,20 +212,14 @@ async def async_api_adjust_brightness(hass, config, directive, context): entity = directive.entity brightness_delta = int(directive.payload["brightnessDelta"]) - # read current state - try: - current = math.floor( - int(entity.attributes.get(light.ATTR_BRIGHTNESS)) / 255 * 100 - ) - except ZeroDivisionError: - current = 0 - # set brightness - brightness = max(0, brightness_delta + current) await hass.services.async_call( entity.domain, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_BRIGHTNESS_PCT: brightness}, + { + ATTR_ENTITY_ID: entity.entity_id, + light.ATTR_BRIGHTNESS_STEP_PCT: brightness_delta, + }, blocking=False, context=context, ) diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index 7d144619bc9..24229507877 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -48,8 +48,18 @@ async def async_handle_message(hass, config, request, context=None, enabled=True response = directive.error() except AlexaError as err: response = directive.error( - error_type=err.error_type, error_message=err.error_message + error_type=err.error_type, + error_message=err.error_message, + payload=err.payload, ) + except Exception: # pylint: disable=broad-except + _LOGGER.exception( + "Uncaught exception processing Alexa %s/%s request (%s)", + directive.namespace, + directive.name, + directive.entity_id or "-", + ) + response = directive.error(error_message="Unknown error") request_info = {"namespace": directive.namespace, "name": directive.name} diff --git a/tests/components/alexa/__init__.py b/tests/components/alexa/__init__.py index 1d8289b5ec0..053100d2e00 100644 --- a/tests/components/alexa/__init__.py +++ b/tests/components/alexa/__init__.py @@ -194,7 +194,7 @@ async def assert_scene_controller_works( assert re.search(pattern, response["event"]["payload"]["timestamp"]) -async def reported_properties(hass, endpoint): +async def reported_properties(hass, endpoint, return_full_response=False): """Use ReportState to get properties and return them. The result is a ReportedProperties instance, which has methods to make @@ -203,6 +203,8 @@ async def reported_properties(hass, endpoint): request = get_new_request("Alexa", "ReportState", endpoint) msg = await smart_home.async_handle_message(hass, get_default_config(), request) await hass.async_block_till_done() + if return_full_response: + return msg return ReportedProperties(msg["context"]["properties"]) diff --git a/tests/components/alexa/test_capabilities.py b/tests/components/alexa/test_capabilities.py index 566917d7c39..8a9a40e3217 100644 --- a/tests/components/alexa/test_capabilities.py +++ b/tests/components/alexa/test_capabilities.py @@ -4,7 +4,6 @@ from unittest.mock import patch import pytest from homeassistant.components.alexa import smart_home -from homeassistant.components.alexa.errors import UnsupportedProperty from homeassistant.components.climate import const as climate from homeassistant.components.lock import STATE_JAMMED, STATE_LOCKING, STATE_UNLOCKING from homeassistant.components.media_player.const import ( @@ -39,8 +38,8 @@ from . import ( from tests.common import async_mock_service -@pytest.mark.parametrize("result,adjust", [(25, "-5"), (35, "5"), (0, "-80")]) -async def test_api_adjust_brightness(hass, result, adjust): +@pytest.mark.parametrize("adjust", ["-5", "5", "-80"]) +async def test_api_adjust_brightness(hass, adjust): """Test api adjust brightness process.""" request = get_new_request( "Alexa.BrightnessController", "AdjustBrightness", "light#test" @@ -64,7 +63,7 @@ async def test_api_adjust_brightness(hass, result, adjust): assert len(call_light) == 1 assert call_light[0].data["entity_id"] == "light.test" - assert call_light[0].data["brightness_pct"] == result + assert call_light[0].data["brightness_step_pct"] == int(adjust) assert msg["header"]["name"] == "Response" @@ -677,16 +676,9 @@ async def test_report_climate_state(hass): ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, }, ) - with pytest.raises(UnsupportedProperty): - properties = await reported_properties(hass, "climate.unsupported") - properties.assert_not_has_property( - "Alexa.ThermostatController", "thermostatMode" - ) - properties.assert_equal( - "Alexa.TemperatureSensor", - "temperature", - {"value": 34.0, "scale": "CELSIUS"}, - ) + msg = await reported_properties(hass, "climate.unsupported", True) + assert msg["event"]["header"]["name"] == "ErrorResponse" + assert msg["event"]["payload"]["type"] == "INTERNAL_ERROR" async def test_temperature_sensor_sensor(hass):