1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 12:59:34 +00:00

Report scripts and groups as scenes to Alexa (#11900)

* Send Alexa Smart Home responses to debug log

* Report scripts and groups as scenes to Alexa

The Alexa API docs have a couple display categories that sound relevant
to scenes or scripts:

    ACTIVITY_TRIGGER: Describes a combination of devices set to a
    specific state, when the state change must occur in a specific
    order.  For example, a “watch Neflix” scene might require the: 1. TV
    to be powered on & 2. Input set to HDMI1.

    SCENE_TRIGGER: Describes a combination of devices set to a specific
    state, when the order of the state change is not important. For
    example a bedtime scene might include turning off lights and
    lowering the thermostat, but the order is unimportant.

Additionally, Alexa has a notion of scenes that support deactivation.
This is a natural fit for groups, and scripts with delays which can be
cancelled.

https://developer.amazon.com/docs/device-apis/alexa-discovery.html#display-categories

The mechanism to map entities to the Alexa Discovery response is
refactored since extending the data structures in MAPPING_COMPONENT to
implement supportsDeactivation would have added complication to what I
already found to be a confusing construct.
This commit is contained in:
Phil Frost
2018-01-26 05:06:57 +00:00
committed by Paulus Schoutsen
parent 3aa3130d05
commit 920f9f132b
2 changed files with 382 additions and 86 deletions

View File

@@ -122,6 +122,9 @@ def test_discovery_request(hass):
hass.states.async_set(
'script.test', 'off', {'friendly_name': "Test script"})
hass.states.async_set(
'script.test_2', 'off', {'friendly_name': "Test script 2",
'can_cancel': True})
hass.states.async_set(
'input_boolean.test', 'off', {'friendly_name': "Test input boolean"})
@@ -169,7 +172,7 @@ def test_discovery_request(hass):
assert 'event' in msg
msg = msg['event']
assert len(msg['payload']['endpoints']) == 15
assert len(msg['payload']['endpoints']) == 16
assert msg['header']['name'] == 'Discover.Response'
assert msg['header']['namespace'] == 'Alexa.Discovery'
@@ -221,11 +224,18 @@ def test_discovery_request(hass):
continue
if appliance['endpointId'] == 'script#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['displayCategories'][0] == "ACTIVITY_TRIGGER"
assert appliance['friendlyName'] == "Test script"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
capability = appliance['capabilities'][-1]
assert capability['interface'] == 'Alexa.SceneController'
assert not capability['supportsDeactivation']
continue
if appliance['endpointId'] == 'script#test_2':
assert len(appliance['capabilities']) == 1
capability = appliance['capabilities'][-1]
assert capability['supportsDeactivation']
continue
if appliance['endpointId'] == 'input_boolean#test':
@@ -237,7 +247,7 @@ def test_discovery_request(hass):
continue
if appliance['endpointId'] == 'scene#test':
assert appliance['displayCategories'][0] == "ACTIVITY_TRIGGER"
assert appliance['displayCategories'][0] == "SCENE_TRIGGER"
assert appliance['friendlyName'] == "Test scene"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
@@ -303,11 +313,12 @@ def test_discovery_request(hass):
continue
if appliance['endpointId'] == 'group#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['displayCategories'][0] == "SCENE_TRIGGER"
assert appliance['friendlyName'] == "Test group"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
capability = appliance['capabilities'][-1]
assert capability['interface'] == 'Alexa.SceneController'
assert capability['supportsDeactivation'] is True
continue
if appliance['endpointId'] == 'cover#test':
@@ -425,8 +436,8 @@ def test_api_function_not_implemented(hass):
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['alert', 'automation', 'cover', 'group',
'input_boolean', 'light', 'script',
@pytest.mark.parametrize("domain", ['alert', 'automation', 'cover',
'input_boolean', 'light',
'switch'])
def test_api_turn_on(hass, domain):
"""Test api turn on process."""
@@ -441,9 +452,6 @@ def test_api_turn_on(hass, domain):
call_domain = domain
if domain == 'group':
call_domain = 'homeassistant'
if domain == 'cover':
call = async_mock_service(hass, call_domain, 'open_cover')
else:
@@ -719,7 +727,7 @@ def test_api_increase_color_temp(hass, result, initial):
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['scene'])
@pytest.mark.parametrize("domain", ['scene', 'group', 'script'])
def test_api_activate(hass, domain):
"""Test api activate process."""
request = get_new_request(
@@ -731,7 +739,12 @@ def test_api_activate(hass, domain):
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'turn_on')
if domain == 'group':
call_domain = 'homeassistant'
else:
call_domain = domain
call = async_mock_service(hass, call_domain, 'turn_on')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
@@ -747,6 +760,40 @@ def test_api_activate(hass, domain):
assert 'timestamp' in msg['payload']
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['group', 'script'])
def test_api_deactivate(hass, domain):
"""Test api deactivate process."""
request = get_new_request(
'Alexa.SceneController', 'Deactivate', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
if domain == 'group':
call_domain = 'homeassistant'
else:
call_domain = domain
call = async_mock_service(hass, call_domain, 'turn_off')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'DeactivationStarted'
assert msg['payload']['cause']['type'] == 'VOICE_INTERACTION'
assert 'timestamp' in msg['payload']
@asyncio.coroutine
def test_api_set_percentage_fan(hass):
"""Test api set percentage for fan process."""
@@ -1160,6 +1207,23 @@ def test_entity_config(hass):
'Alexa.PowerController'
@asyncio.coroutine
def test_unsupported_domain(hass):
"""Discovery ignores entities of unknown domains."""
request = get_new_request('Alexa.Discovery', 'Discover')
hass.states.async_set(
'woz.boop', 'on', {'friendly_name': "Boop Woz"})
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
assert 'event' in msg
msg = msg['event']
assert len(msg['payload']['endpoints']) == 0
@asyncio.coroutine
def do_http_discovery(config, hass, test_client):
"""Submit a request to the Smart Home HTTP API."""