mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 10:17:51 +00:00
Add SecurityPanelController for alarm_control_panel to alexa (#27081)
* Implemented Alexa.SecurityPanelController Interface for alarm_control_panel https://developer.amazon.com/docs/device-apis/alexa-securitypanelcontroller.html * Implemented Tests for Alexa.SecurityPanelController Interface for alarm_control_panel * Added additional AuthorizationRequired error handling * Removed optional exitDelayInSeconds * Updating elif to if to please pylint * Adding self to code owners. * Adding self to code owners. * Added AlexaEndpointHealth Interface to alarm_control_panel entities. * Added additional entity tests. * Code reformatted with Black. * Updated alexa alarm_control_panel tests for more coverage. * Updated alexa alarm_control_panel tests for more coverage. Fixed Test. * Adding self to code owners.
This commit is contained in:
parent
f169e84d21
commit
9a5c1fbaed
@ -17,7 +17,7 @@ homeassistant/components/adguard/* @frenck
|
|||||||
homeassistant/components/airly/* @bieniu
|
homeassistant/components/airly/* @bieniu
|
||||||
homeassistant/components/airvisual/* @bachya
|
homeassistant/components/airvisual/* @bachya
|
||||||
homeassistant/components/alarm_control_panel/* @colinodell
|
homeassistant/components/alarm_control_panel/* @colinodell
|
||||||
homeassistant/components/alexa/* @home-assistant/cloud
|
homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy
|
||||||
homeassistant/components/alpha_vantage/* @fabaff
|
homeassistant/components/alpha_vantage/* @fabaff
|
||||||
homeassistant/components/amazon_polly/* @robbiet480
|
homeassistant/components/amazon_polly/* @robbiet480
|
||||||
homeassistant/components/ambiclimate/* @danielhiversen
|
homeassistant/components/ambiclimate/* @danielhiversen
|
||||||
|
@ -5,6 +5,10 @@ from homeassistant.const import (
|
|||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
|
STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
STATE_ALARM_ARMED_HOME,
|
||||||
|
STATE_ALARM_ARMED_NIGHT,
|
||||||
STATE_LOCKED,
|
STATE_LOCKED,
|
||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
@ -13,6 +17,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
import homeassistant.components.climate.const as climate
|
import homeassistant.components.climate.const as climate
|
||||||
|
from homeassistant.components.alarm_control_panel import ATTR_CODE_FORMAT, FORMAT_NUMBER
|
||||||
from homeassistant.components import light, fan, cover
|
from homeassistant.components import light, fan, cover
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
@ -79,6 +84,11 @@ class AlexaCapibility:
|
|||||||
"""Applicable only to scenes."""
|
"""Applicable only to scenes."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def configuration():
|
||||||
|
"""Applicable only to security control panel."""
|
||||||
|
return []
|
||||||
|
|
||||||
def serialize_discovery(self):
|
def serialize_discovery(self):
|
||||||
"""Serialize according to the Discovery API."""
|
"""Serialize according to the Discovery API."""
|
||||||
result = {
|
result = {
|
||||||
@ -96,6 +106,11 @@ class AlexaCapibility:
|
|||||||
supports_deactivation = self.supports_deactivation()
|
supports_deactivation = self.supports_deactivation()
|
||||||
if supports_deactivation is not None:
|
if supports_deactivation is not None:
|
||||||
result["supportsDeactivation"] = supports_deactivation
|
result["supportsDeactivation"] = supports_deactivation
|
||||||
|
|
||||||
|
configuration = self.configuration()
|
||||||
|
if configuration:
|
||||||
|
result["configuration"] = configuration
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def serialize_properties(self):
|
def serialize_properties(self):
|
||||||
@ -649,3 +664,55 @@ class AlexaPowerLevelController(AlexaCapibility):
|
|||||||
return PERCENTAGE_FAN_MAP.get(speed, None)
|
return PERCENTAGE_FAN_MAP.get(speed, None)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class AlexaSecurityPanelController(AlexaCapibility):
|
||||||
|
"""Implements Alexa.SecurityPanelController.
|
||||||
|
|
||||||
|
https://developer.amazon.com/docs/device-apis/alexa-securitypanelcontroller.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hass, entity):
|
||||||
|
"""Initialize the entity."""
|
||||||
|
super().__init__(entity)
|
||||||
|
self.hass = hass
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
"""Return the Alexa API name of this interface."""
|
||||||
|
return "Alexa.SecurityPanelController"
|
||||||
|
|
||||||
|
def properties_supported(self):
|
||||||
|
"""Return what properties this entity supports."""
|
||||||
|
return [{"name": "armState"}]
|
||||||
|
|
||||||
|
def properties_proactively_reported(self):
|
||||||
|
"""Return True if properties asynchronously reported."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def properties_retrievable(self):
|
||||||
|
"""Return True if properties can be retrieved."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_property(self, name):
|
||||||
|
"""Read and return a property."""
|
||||||
|
if name != "armState":
|
||||||
|
raise UnsupportedProperty(name)
|
||||||
|
|
||||||
|
arm_state = self.entity.state
|
||||||
|
if arm_state == STATE_ALARM_ARMED_HOME:
|
||||||
|
return "ARMED_STAY"
|
||||||
|
if arm_state == STATE_ALARM_ARMED_AWAY:
|
||||||
|
return "ARMED_AWAY"
|
||||||
|
if arm_state == STATE_ALARM_ARMED_NIGHT:
|
||||||
|
return "ARMED_NIGHT"
|
||||||
|
if arm_state == STATE_ALARM_ARMED_CUSTOM_BYPASS:
|
||||||
|
return "ARMED_STAY"
|
||||||
|
return "DISARMED"
|
||||||
|
|
||||||
|
def configuration(self):
|
||||||
|
"""Return supported authorization types."""
|
||||||
|
code_format = self.entity.attributes.get(ATTR_CODE_FORMAT)
|
||||||
|
|
||||||
|
if code_format == FORMAT_NUMBER:
|
||||||
|
return {"supportedAuthorizationTypes": [{"type": "FOUR_DIGIT_PIN"}]}
|
||||||
|
return []
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.util.decorator import Registry
|
from homeassistant.util.decorator import Registry
|
||||||
from homeassistant.components.climate import const as climate
|
from homeassistant.components.climate import const as climate
|
||||||
from homeassistant.components import (
|
from homeassistant.components import (
|
||||||
|
alarm_control_panel,
|
||||||
alert,
|
alert,
|
||||||
automation,
|
automation,
|
||||||
binary_sensor,
|
binary_sensor,
|
||||||
@ -45,6 +46,7 @@ from .capabilities import (
|
|||||||
AlexaPowerController,
|
AlexaPowerController,
|
||||||
AlexaPowerLevelController,
|
AlexaPowerLevelController,
|
||||||
AlexaSceneController,
|
AlexaSceneController,
|
||||||
|
AlexaSecurityPanelController,
|
||||||
AlexaSpeaker,
|
AlexaSpeaker,
|
||||||
AlexaStepSpeaker,
|
AlexaStepSpeaker,
|
||||||
AlexaTemperatureSensor,
|
AlexaTemperatureSensor,
|
||||||
@ -487,3 +489,18 @@ class BinarySensorCapabilities(AlexaEntity):
|
|||||||
return self.TYPE_CONTACT
|
return self.TYPE_CONTACT
|
||||||
if attrs.get(ATTR_DEVICE_CLASS) == "motion":
|
if attrs.get(ATTR_DEVICE_CLASS) == "motion":
|
||||||
return self.TYPE_MOTION
|
return self.TYPE_MOTION
|
||||||
|
|
||||||
|
|
||||||
|
@ENTITY_ADAPTERS.register(alarm_control_panel.DOMAIN)
|
||||||
|
class AlarmControlPanelCapabilities(AlexaEntity):
|
||||||
|
"""Class to represent Alarm capabilities."""
|
||||||
|
|
||||||
|
def default_display_categories(self):
|
||||||
|
"""Return the display categories for this entity."""
|
||||||
|
return [DisplayCategory.SECURITY_PANEL]
|
||||||
|
|
||||||
|
def interfaces(self):
|
||||||
|
"""Yield the supported interfaces."""
|
||||||
|
if not self.entity.attributes.get("code_arm_required"):
|
||||||
|
yield AlexaSecurityPanelController(self.hass, self.entity)
|
||||||
|
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||||
|
@ -83,3 +83,17 @@ class AlexaBridgeUnreachableError(AlexaError):
|
|||||||
|
|
||||||
namespace = "Alexa"
|
namespace = "Alexa"
|
||||||
error_type = "BRIDGE_UNREACHABLE"
|
error_type = "BRIDGE_UNREACHABLE"
|
||||||
|
|
||||||
|
|
||||||
|
class AlexaSecurityPanelUnauthorizedError(AlexaError):
|
||||||
|
"""Class to represent SecurityPanelController Unauthorized errors."""
|
||||||
|
|
||||||
|
namespace = "Alexa.SecurityPanelController"
|
||||||
|
error_type = "UNAUTHORIZED"
|
||||||
|
|
||||||
|
|
||||||
|
class AlexaSecurityPanelAuthorizationRequired(AlexaError):
|
||||||
|
"""Class to represent SecurityPanelController AuthorizationRequired errors."""
|
||||||
|
|
||||||
|
namespace = "Alexa.SecurityPanelController"
|
||||||
|
error_type = "AUTHORIZATION_REQUIRED"
|
||||||
|
@ -9,6 +9,11 @@ from homeassistant.const import (
|
|||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_SUPPORTED_FEATURES,
|
ATTR_SUPPORTED_FEATURES,
|
||||||
ATTR_TEMPERATURE,
|
ATTR_TEMPERATURE,
|
||||||
|
STATE_ALARM_DISARMED,
|
||||||
|
SERVICE_ALARM_ARM_AWAY,
|
||||||
|
SERVICE_ALARM_ARM_HOME,
|
||||||
|
SERVICE_ALARM_ARM_NIGHT,
|
||||||
|
SERVICE_ALARM_DISARM,
|
||||||
SERVICE_LOCK,
|
SERVICE_LOCK,
|
||||||
SERVICE_MEDIA_NEXT_TRACK,
|
SERVICE_MEDIA_NEXT_TRACK,
|
||||||
SERVICE_MEDIA_PAUSE,
|
SERVICE_MEDIA_PAUSE,
|
||||||
@ -35,6 +40,8 @@ from .const import API_TEMP_UNITS, API_THERMOSTAT_MODES, API_THERMOSTAT_PRESETS,
|
|||||||
from .entities import async_get_entities
|
from .entities import async_get_entities
|
||||||
from .errors import (
|
from .errors import (
|
||||||
AlexaInvalidValueError,
|
AlexaInvalidValueError,
|
||||||
|
AlexaSecurityPanelAuthorizationRequired,
|
||||||
|
AlexaSecurityPanelUnauthorizedError,
|
||||||
AlexaTempRangeError,
|
AlexaTempRangeError,
|
||||||
AlexaUnsupportedThermostatModeError,
|
AlexaUnsupportedThermostatModeError,
|
||||||
)
|
)
|
||||||
@ -849,3 +856,71 @@ async def async_api_adjust_power_level(hass, config, directive, context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return directive.response()
|
return directive.response()
|
||||||
|
|
||||||
|
|
||||||
|
@HANDLERS.register(("Alexa.SecurityPanelController", "Arm"))
|
||||||
|
async def async_api_arm(hass, config, directive, context):
|
||||||
|
"""Process a Security Panel Arm request."""
|
||||||
|
entity = directive.entity
|
||||||
|
service = None
|
||||||
|
arm_state = directive.payload["armState"]
|
||||||
|
data = {ATTR_ENTITY_ID: entity.entity_id}
|
||||||
|
|
||||||
|
if entity.state != STATE_ALARM_DISARMED:
|
||||||
|
msg = "You must disarm the system before you can set the requested arm state."
|
||||||
|
raise AlexaSecurityPanelAuthorizationRequired(msg)
|
||||||
|
|
||||||
|
if arm_state == "ARMED_AWAY":
|
||||||
|
service = SERVICE_ALARM_ARM_AWAY
|
||||||
|
if arm_state == "ARMED_STAY":
|
||||||
|
service = SERVICE_ALARM_ARM_HOME
|
||||||
|
if arm_state == "ARMED_NIGHT":
|
||||||
|
service = SERVICE_ALARM_ARM_NIGHT
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
entity.domain, service, data, blocking=False, context=context
|
||||||
|
)
|
||||||
|
|
||||||
|
response = directive.response(
|
||||||
|
name="Arm.Response", namespace="Alexa.SecurityPanelController"
|
||||||
|
)
|
||||||
|
|
||||||
|
response.add_context_property(
|
||||||
|
{
|
||||||
|
"name": "armState",
|
||||||
|
"namespace": "Alexa.SecurityPanelController",
|
||||||
|
"value": arm_state,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@HANDLERS.register(("Alexa.SecurityPanelController", "Disarm"))
|
||||||
|
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}
|
||||||
|
|
||||||
|
payload = directive.payload
|
||||||
|
if "authorization" in payload:
|
||||||
|
value = payload["authorization"]["value"]
|
||||||
|
if payload["authorization"]["type"] == "FOUR_DIGIT_PIN":
|
||||||
|
data["code"] = value
|
||||||
|
|
||||||
|
if not await hass.services.async_call(
|
||||||
|
entity.domain, SERVICE_ALARM_DISARM, data, blocking=True, context=context
|
||||||
|
):
|
||||||
|
msg = "Invalid Code"
|
||||||
|
raise AlexaSecurityPanelUnauthorizedError(msg)
|
||||||
|
|
||||||
|
response = directive.response()
|
||||||
|
response.add_context_property(
|
||||||
|
{
|
||||||
|
"name": "armState",
|
||||||
|
"namespace": "Alexa.SecurityPanelController",
|
||||||
|
"value": "DISARMED",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
@ -4,5 +4,8 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/alexa",
|
"documentation": "https://www.home-assistant.io/integrations/alexa",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"dependencies": ["http"],
|
"dependencies": ["http"],
|
||||||
"codeowners": ["@home-assistant/cloud"]
|
"codeowners": [
|
||||||
|
"@home-assistant/cloud",
|
||||||
|
"@ochlocracy"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@ from homeassistant.const import (
|
|||||||
STATE_UNLOCKED,
|
STATE_UNLOCKED,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
|
STATE_ALARM_DISARMED,
|
||||||
|
STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
STATE_ALARM_ARMED_HOME,
|
||||||
|
STATE_ALARM_ARMED_NIGHT,
|
||||||
)
|
)
|
||||||
from homeassistant.components.climate import const as climate
|
from homeassistant.components.climate import const as climate
|
||||||
from homeassistant.components.alexa import smart_home
|
from homeassistant.components.alexa import smart_home
|
||||||
@ -527,3 +532,33 @@ async def test_temperature_sensor_climate(hass):
|
|||||||
properties.assert_equal(
|
properties.assert_equal(
|
||||||
"Alexa.TemperatureSensor", "temperature", {"value": 34.0, "scale": "CELSIUS"}
|
"Alexa.TemperatureSensor", "temperature", {"value": 34.0, "scale": "CELSIUS"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_report_alarm_control_panel_state(hass):
|
||||||
|
"""Test SecurityPanelController implements armState property."""
|
||||||
|
hass.states.async_set("alarm_control_panel.armed_away", STATE_ALARM_ARMED_AWAY, {})
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.armed_custom_bypass", STATE_ALARM_ARMED_CUSTOM_BYPASS, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set("alarm_control_panel.armed_home", STATE_ALARM_ARMED_HOME, {})
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.armed_night", STATE_ALARM_ARMED_NIGHT, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set("alarm_control_panel.disarmed", STATE_ALARM_DISARMED, {})
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel.armed_away")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_AWAY")
|
||||||
|
|
||||||
|
properties = await reported_properties(
|
||||||
|
hass, "alarm_control_panel.armed_custom_bypass"
|
||||||
|
)
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_STAY")
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel.armed_home")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_STAY")
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel.armed_night")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_NIGHT")
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel.disarmed")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED")
|
||||||
|
@ -1306,3 +1306,127 @@ async def test_endpoint_bad_health(hass):
|
|||||||
properties.assert_equal(
|
properties.assert_equal(
|
||||||
"Alexa.EndpointHealth", "connectivity", {"value": "UNREACHABLE"}
|
"Alexa.EndpointHealth", "connectivity", {"value": "UNREACHABLE"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alarm_control_panel_disarmed(hass):
|
||||||
|
"""Test alarm_control_panel discovery."""
|
||||||
|
device = (
|
||||||
|
"alarm_control_panel.test_1",
|
||||||
|
"disarmed",
|
||||||
|
{
|
||||||
|
"friendly_name": "Test Alarm Control Panel 1",
|
||||||
|
"code_arm_required": False,
|
||||||
|
"code_format": "number",
|
||||||
|
"code": "1234",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
appliance = await discovery_test(device, hass)
|
||||||
|
|
||||||
|
assert appliance["endpointId"] == "alarm_control_panel#test_1"
|
||||||
|
assert appliance["displayCategories"][0] == "SECURITY_PANEL"
|
||||||
|
assert appliance["friendlyName"] == "Test Alarm Control Panel 1"
|
||||||
|
capabilities = assert_endpoint_capabilities(
|
||||||
|
appliance, "Alexa.SecurityPanelController", "Alexa.EndpointHealth"
|
||||||
|
)
|
||||||
|
security_panel_capability = get_capability(
|
||||||
|
capabilities, "Alexa.SecurityPanelController"
|
||||||
|
)
|
||||||
|
assert security_panel_capability is not None
|
||||||
|
configuration = security_panel_capability["configuration"]
|
||||||
|
assert {"type": "FOUR_DIGIT_PIN"} in configuration["supportedAuthorizationTypes"]
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel#test_1")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED")
|
||||||
|
|
||||||
|
call, msg = await assert_request_calls_service(
|
||||||
|
"Alexa.SecurityPanelController",
|
||||||
|
"Arm",
|
||||||
|
"alarm_control_panel#test_1",
|
||||||
|
"alarm_control_panel.alarm_arm_home",
|
||||||
|
hass,
|
||||||
|
response_type="Arm.Response",
|
||||||
|
payload={"armState": "ARMED_STAY"},
|
||||||
|
)
|
||||||
|
properties = ReportedProperties(msg["context"]["properties"])
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_STAY")
|
||||||
|
|
||||||
|
call, msg = await assert_request_calls_service(
|
||||||
|
"Alexa.SecurityPanelController",
|
||||||
|
"Arm",
|
||||||
|
"alarm_control_panel#test_1",
|
||||||
|
"alarm_control_panel.alarm_arm_away",
|
||||||
|
hass,
|
||||||
|
response_type="Arm.Response",
|
||||||
|
payload={"armState": "ARMED_AWAY"},
|
||||||
|
)
|
||||||
|
properties = ReportedProperties(msg["context"]["properties"])
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_AWAY")
|
||||||
|
|
||||||
|
call, msg = await assert_request_calls_service(
|
||||||
|
"Alexa.SecurityPanelController",
|
||||||
|
"Arm",
|
||||||
|
"alarm_control_panel#test_1",
|
||||||
|
"alarm_control_panel.alarm_arm_night",
|
||||||
|
hass,
|
||||||
|
response_type="Arm.Response",
|
||||||
|
payload={"armState": "ARMED_NIGHT"},
|
||||||
|
)
|
||||||
|
properties = ReportedProperties(msg["context"]["properties"])
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_NIGHT")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alarm_control_panel_armed(hass):
|
||||||
|
"""Test alarm_control_panel discovery."""
|
||||||
|
device = (
|
||||||
|
"alarm_control_panel.test_2",
|
||||||
|
"armed_away",
|
||||||
|
{
|
||||||
|
"friendly_name": "Test Alarm Control Panel 2",
|
||||||
|
"code_arm_required": False,
|
||||||
|
"code_format": "FORMAT_NUMBER",
|
||||||
|
"code": "1234",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
appliance = await discovery_test(device, hass)
|
||||||
|
|
||||||
|
assert appliance["endpointId"] == "alarm_control_panel#test_2"
|
||||||
|
assert appliance["displayCategories"][0] == "SECURITY_PANEL"
|
||||||
|
assert appliance["friendlyName"] == "Test Alarm Control Panel 2"
|
||||||
|
assert_endpoint_capabilities(
|
||||||
|
appliance, "Alexa.SecurityPanelController", "Alexa.EndpointHealth"
|
||||||
|
)
|
||||||
|
|
||||||
|
properties = await reported_properties(hass, "alarm_control_panel#test_2")
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_AWAY")
|
||||||
|
|
||||||
|
call, msg = await assert_request_calls_service(
|
||||||
|
"Alexa.SecurityPanelController",
|
||||||
|
"Disarm",
|
||||||
|
"alarm_control_panel#test_2",
|
||||||
|
"alarm_control_panel.alarm_disarm",
|
||||||
|
hass,
|
||||||
|
payload={"authorization": {"type": "FOUR_DIGIT_PIN", "value": "1234"}},
|
||||||
|
)
|
||||||
|
assert call.data["code"] == "1234"
|
||||||
|
properties = ReportedProperties(msg["context"]["properties"])
|
||||||
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED")
|
||||||
|
|
||||||
|
msg = await assert_request_fails(
|
||||||
|
"Alexa.SecurityPanelController",
|
||||||
|
"Arm",
|
||||||
|
"alarm_control_panel#test_2",
|
||||||
|
"alarm_control_panel.alarm_arm_home",
|
||||||
|
hass,
|
||||||
|
payload={"armState": "ARMED_STAY"},
|
||||||
|
)
|
||||||
|
assert msg["event"]["payload"]["type"] == "AUTHORIZATION_REQUIRED"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_alarm_control_panel_code_arm_required(hass):
|
||||||
|
"""Test alarm_control_panel with code_arm_required discovery."""
|
||||||
|
device = (
|
||||||
|
"alarm_control_panel.test_3",
|
||||||
|
"disarmed",
|
||||||
|
{"friendly_name": "Test Alarm Control Panel 3", "code_arm_required": True},
|
||||||
|
)
|
||||||
|
await discovery_test(device, hass, expected_endpoints=0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user