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:
committed by
Paulus Schoutsen
parent
3aa3130d05
commit
920f9f132b
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user