Skip to content

Commit

Permalink
Trigger Alexa routines from toggless and buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
mdegat01 committed Mar 9, 2022
1 parent 6af9c89 commit 54d244c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 9 deletions.
10 changes: 8 additions & 2 deletions homeassistant/components/alexa/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import logging

from homeassistant.components import (
button,
cover,
fan,
image_processing,
input_button,
input_number,
light,
timer,
Expand Down Expand Up @@ -1905,7 +1907,10 @@ def get_property(self, name):
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}
Expand All @@ -1917,7 +1922,8 @@ def configuration(self):
"detectionModes": {
"humanPresence": {
"featureAvailability": "ENABLED",
"supportsNotDetected": True,
"supportsNotDetected": self.entity.domain
not in [input_button.DOMAIN, button.DOMAIN],
}
},
}
Expand Down
8 changes: 7 additions & 1 deletion homeassistant/components/alexa/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -405,12 +404,16 @@ def interfaces(self):
]


@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]
Expand All @@ -421,6 +424,7 @@ def interfaces(self):
"""Yield the supported interfaces."""
return [
AlexaPowerController(self.entity),
AlexaContactSensor(self.hass, self.entity),
AlexaEndpointHealth(self.hass, self.entity),
Alexa(self.hass),
]
Expand All @@ -439,6 +443,8 @@ def interfaces(self):
"""Yield the supported interfaces."""
return [
AlexaSceneController(self.entity, supports_deactivation=False),
AlexaEventDetectionSensor(self.hass, self.entity),
AlexaEndpointHealth(self.hass, self.entity),
Alexa(self.hass),
]

Expand Down
51 changes: 51 additions & 0 deletions tests/components/alexa/test_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
62 changes: 56 additions & 6 deletions tests/components/alexa/test_smart_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,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(
Expand All @@ -190,6 +194,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):
Expand All @@ -205,7 +217,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",
)


Expand Down Expand Up @@ -330,14 +346,29 @@ 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(
"input_boolean#test", "input_boolean.turn_on", "input_boolean.turn_off", hass
)

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"]


async def test_scene(hass):
"""Test scene discovery."""
Expand Down Expand Up @@ -3967,7 +3998,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
Expand All @@ -3976,6 +4011,21 @@ async def test_button(hass, domain):
f"{domain}#ring_doorbell", f"{domain}.press", False, hass
)

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."""
Expand Down

0 comments on commit 54d244c

Please sign in to comment.