Trigger Alexa routines from toggles and buttons (#67889)

pull/69720/head
Mike Degatano 2022-06-29 07:56:02 -04:00 committed by GitHub
parent 4bdec1589d
commit 9392f59913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 9 deletions

View File

@ -4,9 +4,11 @@ from __future__ import annotations
import logging
from homeassistant.components import (
button,
cover,
fan,
image_processing,
input_button,
input_number,
light,
timer,
@ -1891,7 +1893,10 @@ class AlexaEventDetectionSensor(AlexaCapability):
if self.entity.domain == image_processing.DOMAIN:
if int(state):
human_presence = "DETECTED"
elif state == STATE_ON:
elif state == STATE_ON or self.entity.domain in [
input_button.DOMAIN,
button.DOMAIN,
]:
human_presence = "DETECTED"
return {"value": human_presence}
@ -1903,7 +1908,8 @@ class AlexaEventDetectionSensor(AlexaCapability):
"detectionModes": {
"humanPresence": {
"featureAvailability": "ENABLED",
"supportsNotDetected": True,
"supportsNotDetected": self.entity.domain
not in [input_button.DOMAIN, button.DOMAIN],
}
},
}

View File

@ -382,7 +382,6 @@ def async_get_entities(hass, config) -> list[AlexaEntity]:
@ENTITY_ADAPTERS.register(alert.DOMAIN)
@ENTITY_ADAPTERS.register(automation.DOMAIN)
@ENTITY_ADAPTERS.register(group.DOMAIN)
@ENTITY_ADAPTERS.register(input_boolean.DOMAIN)
class GenericCapabilities(AlexaEntity):
"""A generic, on/off device.
@ -405,12 +404,16 @@ class GenericCapabilities(AlexaEntity):
]
@ENTITY_ADAPTERS.register(input_boolean.DOMAIN)
@ENTITY_ADAPTERS.register(switch.DOMAIN)
class SwitchCapabilities(AlexaEntity):
"""Class to represent Switch capabilities."""
def default_display_categories(self):
"""Return the display categories for this entity."""
if self.entity.domain == input_boolean.DOMAIN:
return [DisplayCategory.OTHER]
device_class = self.entity.attributes.get(ATTR_DEVICE_CLASS)
if device_class == switch.SwitchDeviceClass.OUTLET:
return [DisplayCategory.SMARTPLUG]
@ -421,6 +424,7 @@ class SwitchCapabilities(AlexaEntity):
"""Yield the supported interfaces."""
return [
AlexaPowerController(self.entity),
AlexaContactSensor(self.hass, self.entity),
AlexaEndpointHealth(self.hass, self.entity),
Alexa(self.hass),
]
@ -439,6 +443,8 @@ class ButtonCapabilities(AlexaEntity):
"""Yield the supported interfaces."""
return [
AlexaSceneController(self.entity, supports_deactivation=False),
AlexaEventDetectionSensor(self.hass, self.entity),
AlexaEndpointHealth(self.hass, self.entity),
Alexa(self.hass),
]

View File

@ -846,6 +846,57 @@ async def test_report_image_processing(hass):
)
@pytest.mark.parametrize("domain", ["button", "input_button"])
async def test_report_button_pressed(hass, domain):
"""Test button presses report human presence detection events to trigger routines."""
hass.states.async_set(
f"{domain}.test_button", "now", {"friendly_name": "Test button"}
)
properties = await reported_properties(hass, f"{domain}#test_button")
properties.assert_equal(
"Alexa.EventDetectionSensor",
"humanPresenceDetectionState",
{"value": "DETECTED"},
)
@pytest.mark.parametrize("domain", ["switch", "input_boolean"])
async def test_toggle_entities_report_contact_events(hass, domain):
"""Test toggles and switches report contact sensor events to trigger routines."""
hass.states.async_set(
f"{domain}.test_toggle", "on", {"friendly_name": "Test toggle"}
)
properties = await reported_properties(hass, f"{domain}#test_toggle")
properties.assert_equal(
"Alexa.PowerController",
"powerState",
"ON",
)
properties.assert_equal(
"Alexa.ContactSensor",
"detectionState",
"DETECTED",
)
hass.states.async_set(
f"{domain}.test_toggle", "off", {"friendly_name": "Test toggle"}
)
properties = await reported_properties(hass, f"{domain}#test_toggle")
properties.assert_equal(
"Alexa.PowerController",
"powerState",
"OFF",
)
properties.assert_equal(
"Alexa.ContactSensor",
"detectionState",
"NOT_DETECTED",
)
async def test_get_property_blowup(hass, caplog):
"""Test we handle a property blowing up."""
hass.states.async_set(

View File

@ -182,8 +182,12 @@ async def test_switch(hass, events):
assert appliance["endpointId"] == "switch#test"
assert appliance["displayCategories"][0] == "SWITCH"
assert appliance["friendlyName"] == "Test switch"
assert_endpoint_capabilities(
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
capabilities = assert_endpoint_capabilities(
appliance,
"Alexa.PowerController",
"Alexa.ContactSensor",
"Alexa.EndpointHealth",
"Alexa",
)
await assert_power_controller_works(
@ -192,6 +196,14 @@ async def test_switch(hass, events):
properties = await reported_properties(hass, "switch#test")
properties.assert_equal("Alexa.PowerController", "powerState", "ON")
properties.assert_equal("Alexa.ContactSensor", "detectionState", "DETECTED")
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
contact_sensor_capability = get_capability(capabilities, "Alexa.ContactSensor")
assert contact_sensor_capability is not None
properties = contact_sensor_capability["properties"]
assert properties["retrievable"] is True
assert {"name": "detectionState"} in properties["supported"]
async def test_outlet(hass, events):
@ -207,7 +219,11 @@ async def test_outlet(hass, events):
assert appliance["displayCategories"][0] == "SMARTPLUG"
assert appliance["friendlyName"] == "Test switch"
assert_endpoint_capabilities(
appliance, "Alexa", "Alexa.PowerController", "Alexa.EndpointHealth"
appliance,
"Alexa",
"Alexa.PowerController",
"Alexa.EndpointHealth",
"Alexa.ContactSensor",
)
@ -335,8 +351,12 @@ async def test_input_boolean(hass):
assert appliance["endpointId"] == "input_boolean#test"
assert appliance["displayCategories"][0] == "OTHER"
assert appliance["friendlyName"] == "Test input boolean"
assert_endpoint_capabilities(
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
capabilities = assert_endpoint_capabilities(
appliance,
"Alexa.PowerController",
"Alexa.ContactSensor",
"Alexa.EndpointHealth",
"Alexa",
)
await assert_power_controller_works(
@ -347,6 +367,17 @@ async def test_input_boolean(hass):
"2022-04-19T07:53:05Z",
)
properties = await reported_properties(hass, "input_boolean#test")
properties.assert_equal("Alexa.PowerController", "powerState", "OFF")
properties.assert_equal("Alexa.ContactSensor", "detectionState", "NOT_DETECTED")
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
contact_sensor_capability = get_capability(capabilities, "Alexa.ContactSensor")
assert contact_sensor_capability is not None
properties = contact_sensor_capability["properties"]
assert properties["retrievable"] is True
assert {"name": "detectionState"} in properties["supported"]
@freeze_time("2022-04-19 07:53:05")
async def test_scene(hass):
@ -4003,7 +4034,11 @@ async def test_button(hass, domain):
assert appliance["friendlyName"] == "Ring Doorbell"
capabilities = assert_endpoint_capabilities(
appliance, "Alexa.SceneController", "Alexa"
appliance,
"Alexa.SceneController",
"Alexa.EventDetectionSensor",
"Alexa.EndpointHealth",
"Alexa",
)
scene_capability = get_capability(capabilities, "Alexa.SceneController")
assert scene_capability["supportsDeactivation"] is False
@ -4016,6 +4051,21 @@ async def test_button(hass, domain):
"2022-04-19T07:53:05Z",
)
event_detection_capability = get_capability(
capabilities, "Alexa.EventDetectionSensor"
)
assert event_detection_capability is not None
properties = event_detection_capability["properties"]
assert properties["proactivelyReported"] is True
assert not properties["retrievable"]
assert {"name": "humanPresenceDetectionState"} in properties["supported"]
assert (
event_detection_capability["configuration"]["detectionModes"]["humanPresence"][
"supportsNotDetected"
]
is False
)
async def test_api_message_sets_authorized(hass):
"""Test an incoming API messages sets the authorized flag."""