diff --git a/homeassistant/components/alexa/capabilities.py b/homeassistant/components/alexa/capabilities.py index 080a8c39147..8b93b911fc4 100644 --- a/homeassistant/components/alexa/capabilities.py +++ b/homeassistant/components/alexa/capabilities.py @@ -10,6 +10,11 @@ from homeassistant.components import ( vacuum, ) from homeassistant.components.alarm_control_panel import ATTR_CODE_FORMAT, FORMAT_NUMBER +from homeassistant.components.alarm_control_panel.const import ( + SUPPORT_ALARM_ARM_AWAY, + SUPPORT_ALARM_ARM_HOME, + SUPPORT_ALARM_ARM_NIGHT, +) import homeassistant.components.climate.const as climate import homeassistant.components.media_player.const as media_player from homeassistant.const import ( @@ -1082,10 +1087,23 @@ class AlexaSecurityPanelController(AlexaCapability): def configuration(self): """Return configuration object with supported authorization types.""" code_format = self.entity.attributes.get(ATTR_CODE_FORMAT) + supported = self.entity.attributes[ATTR_SUPPORTED_FEATURES] + configuration = {} + + supported_arm_states = [{"value": "DISARMED"}] + if supported & SUPPORT_ALARM_ARM_AWAY: + supported_arm_states.append({"value": "ARMED_AWAY"}) + if supported & SUPPORT_ALARM_ARM_HOME: + supported_arm_states.append({"value": "ARMED_STAY"}) + if supported & SUPPORT_ALARM_ARM_NIGHT: + supported_arm_states.append({"value": "ARMED_NIGHT"}) + + configuration["supportedArmStates"] = supported_arm_states if code_format == FORMAT_NUMBER: - return {"supportedAuthorizationTypes": [{"type": "FOUR_DIGIT_PIN"}]} - return None + configuration["supportedAuthorizationTypes"] = [{"type": "FOUR_DIGIT_PIN"}] + + return configuration class AlexaModeController(AlexaCapability): diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index 8bd52b1e40b..f67e2e259d0 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -908,8 +908,11 @@ async def async_api_arm(hass, config, directive, context): entity.domain, service, data, blocking=False, context=context ) + # return 0 until alarm integration supports an exit delay + payload = {"exitDelayInSeconds": 0} + response = directive.response( - name="Arm.Response", namespace="Alexa.SecurityPanelController" + name="Arm.Response", namespace="Alexa.SecurityPanelController", payload=payload ) response.add_context_property( @@ -928,6 +931,12 @@ async def async_api_disarm(hass, config, directive, context): """Process a Security Panel Disarm request.""" entity = directive.entity data = {ATTR_ENTITY_ID: entity.entity_id} + response = directive.response() + + # Per Alexa Documentation: If you receive a Disarm directive, and the system is already disarmed, + # respond with a success response, not an error response. + if entity.state == STATE_ALARM_DISARMED: + return response payload = directive.payload if "authorization" in payload: @@ -941,7 +950,6 @@ async def async_api_disarm(hass, config, directive, context): msg = "Invalid Code" raise AlexaSecurityPanelUnauthorizedError(msg) - response = directive.response() response.add_context_property( { "name": "armState", diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index 161f69287d4..1510474aa6e 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -2353,6 +2353,7 @@ async def test_alarm_control_panel_disarmed(hass): "code_arm_required": False, "code_format": "number", "code": "1234", + "supported_features": 31, }, ) appliance = await discovery_test(device, hass) @@ -2369,6 +2370,10 @@ async def test_alarm_control_panel_disarmed(hass): assert security_panel_capability is not None configuration = security_panel_capability["configuration"] assert {"type": "FOUR_DIGIT_PIN"} in configuration["supportedAuthorizationTypes"] + assert {"value": "DISARMED"} in configuration["supportedArmStates"] + assert {"value": "ARMED_STAY"} in configuration["supportedArmStates"] + assert {"value": "ARMED_AWAY"} in configuration["supportedArmStates"] + assert {"value": "ARMED_NIGHT"} in configuration["supportedArmStates"] properties = await reported_properties(hass, "alarm_control_panel#test_1") properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED") @@ -2420,6 +2425,7 @@ async def test_alarm_control_panel_armed(hass): "code_arm_required": False, "code_format": "FORMAT_NUMBER", "code": "1234", + "supported_features": 3, }, ) appliance = await discovery_test(device, hass) @@ -2458,11 +2464,15 @@ async def test_alarm_control_panel_armed(hass): async def test_alarm_control_panel_code_arm_required(hass): - """Test alarm_control_panel with code_arm_required discovery.""" + """Test alarm_control_panel with code_arm_required not in discovery.""" device = ( "alarm_control_panel.test_3", "disarmed", - {"friendly_name": "Test Alarm Control Panel 3", "code_arm_required": True}, + { + "friendly_name": "Test Alarm Control Panel 3", + "code_arm_required": True, + "supported_features": 3, + }, ) await discovery_test(device, hass, expected_endpoints=0)