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

Alexa improvements (#10632)

* Initial scene support

* Initial fan support

* ordering

* Initial lock support

* Scenes cant be deactivated; Correct the scene display category

* Initial input_boolean support

* Support customization of Alexa discovered entities

* Initial media player support

* Add input_boolean to tests

* Add play/pause/stop/next/previous to media player

* Add missing functions and pylint

* Set manufacturerName to Home Assistant since the value is displayed in app

* Add scene test

* Add fan tests

* Add lock test

* Fix volume logic

* Add volume tests

* settup -> setup

* Remove unused variable

* Set required scene description as per docs

* Allow setting scene category (ACTIVITY_TRIGGER/SCENE_TRIGGER)

* Add alert, automation and group support/tests

* Change display categories to match docs

* simplify down the display category props into a single prop which can be used on any entity

* Fix tests to expect proper display categories

* Add cover support

* sort things

* Use generic homeassistant domain for turn on/off
This commit is contained in:
Robbie Trencheny
2017-11-17 09:14:22 -08:00
committed by Paulus Schoutsen
parent f43092c563
commit 2b60fca08d
2 changed files with 853 additions and 27 deletions

View File

@@ -99,7 +99,7 @@ def test_discovery_request(hass):
"""Test alexa discovery request."""
request = get_new_request('Alexa.Discovery', 'Discover')
# settup test devices
# setup test devices
hass.states.async_set(
'switch.test', 'on', {'friendly_name': "Test switch"})
@@ -117,12 +117,52 @@ def test_discovery_request(hass):
hass.states.async_set(
'script.test', 'off', {'friendly_name': "Test script"})
hass.states.async_set(
'input_boolean.test', 'off', {'friendly_name': "Test input boolean"})
hass.states.async_set(
'scene.test', 'off', {'friendly_name': "Test scene"})
hass.states.async_set(
'fan.test_1', 'off', {'friendly_name': "Test fan 1"})
hass.states.async_set(
'fan.test_2', 'off', {
'friendly_name': "Test fan 2", 'supported_features': 1,
'speed_list': ['low', 'medium', 'high']
})
hass.states.async_set(
'lock.test', 'off', {'friendly_name': "Test lock"})
hass.states.async_set(
'media_player.test', 'off', {
'friendly_name': "Test media player",
'supported_features': 20925,
'volume_level': 1
})
hass.states.async_set(
'alert.test', 'off', {'friendly_name': "Test alert"})
hass.states.async_set(
'automation.test', 'off', {'friendly_name': "Test automation"})
hass.states.async_set(
'group.test', 'off', {'friendly_name': "Test group"})
hass.states.async_set(
'cover.test', 'off', {
'friendly_name': "Test cover", 'supported_features': 255,
'position': 85
})
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(msg['payload']['endpoints']) == 5
assert len(msg['payload']['endpoints']) == 15
assert msg['header']['name'] == 'Discover.Response'
assert msg['header']['namespace'] == 'Alexa.Discovery'
@@ -174,13 +214,108 @@ def test_discovery_request(hass):
continue
if appliance['endpointId'] == 'script#test':
assert appliance['displayCategories'][0] == "SWITCH"
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test script"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'input_boolean#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test input boolean"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'scene#test':
assert appliance['displayCategories'][0] == "ACTIVITY_TRIGGER"
assert appliance['friendlyName'] == "Test scene"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.SceneController'
continue
if appliance['endpointId'] == 'fan#test_1':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test fan 1"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'fan#test_2':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test fan 2"
assert len(appliance['capabilities']) == 2
caps = set()
for feature in appliance['capabilities']:
caps.add(feature['interface'])
assert 'Alexa.PercentageController' in caps
assert 'Alexa.PowerController' in caps
continue
if appliance['endpointId'] == 'lock#test':
assert appliance['displayCategories'][0] == "SMARTLOCK"
assert appliance['friendlyName'] == "Test lock"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.LockController'
continue
if appliance['endpointId'] == 'media_player#test':
assert appliance['displayCategories'][0] == "TV"
assert appliance['friendlyName'] == "Test media player"
assert len(appliance['capabilities']) == 3
caps = set()
for feature in appliance['capabilities']:
caps.add(feature['interface'])
assert 'Alexa.PowerController' in caps
assert 'Alexa.Speaker' in caps
assert 'Alexa.PlaybackController' in caps
continue
if appliance['endpointId'] == 'alert#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test alert"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'automation#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test automation"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'group#test':
assert appliance['displayCategories'][0] == "OTHER"
assert appliance['friendlyName'] == "Test group"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'cover#test':
assert appliance['displayCategories'][0] == "DOOR"
assert appliance['friendlyName'] == "Test cover"
assert len(appliance['capabilities']) == 2
caps = set()
for feature in appliance['capabilities']:
caps.add(feature['interface'])
assert 'Alexa.PercentageController' in caps
assert 'Alexa.PowerController' in caps
continue
raise AssertionError("Unknown appliance!")
@@ -217,19 +352,21 @@ def test_api_function_not_implemented(hass):
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['light', 'switch', 'script'])
@pytest.mark.parametrize("domain", ['alert', 'automation', 'group',
'input_boolean', 'light', 'script',
'switch'])
def test_api_turn_on(hass, domain):
"""Test api turn on process."""
request = get_new_request(
'Alexa.PowerController', 'TurnOn', '{}#test'.format(domain))
# settup test devices
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'turn_on')
call = async_mock_service(hass, 'homeassistant', 'turn_on')
msg = yield from smart_home.async_handle_message(hass, request)
@@ -242,19 +379,21 @@ def test_api_turn_on(hass, domain):
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['light', 'switch', 'script'])
@pytest.mark.parametrize("domain", ['alert', 'automation', 'group',
'input_boolean', 'light', 'script',
'switch'])
def test_api_turn_off(hass, domain):
"""Test api turn on process."""
request = get_new_request(
'Alexa.PowerController', 'TurnOff', '{}#test'.format(domain))
# settup test devices
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'on', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'turn_off')
call = async_mock_service(hass, 'homeassistant', 'turn_off')
msg = yield from smart_home.async_handle_message(hass, request)
@@ -275,7 +414,7 @@ def test_api_set_brightness(hass):
# add payload
request['directive']['payload']['brightness'] = '50'
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {'friendly_name': "Test light"})
@@ -303,7 +442,7 @@ def test_api_adjust_brightness(hass, result, adjust):
# add payload
request['directive']['payload']['brightnessDelta'] = adjust
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'brightness': '77'
@@ -335,7 +474,7 @@ def test_api_set_color_rgb(hass):
'brightness': '0.342',
}
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light",
@@ -368,7 +507,7 @@ def test_api_set_color_xy(hass):
'brightness': '0.342',
}
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light",
@@ -399,7 +538,7 @@ def test_api_set_color_temperature(hass):
# add payload
request['directive']['payload']['colorTemperatureInKelvin'] = '7500'
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {'friendly_name': "Test light"})
@@ -424,7 +563,7 @@ def test_api_decrease_color_temp(hass, result, initial):
'Alexa.ColorTemperatureController', 'DecreaseColorTemperature',
'light#test')
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'color_temp': initial,
@@ -452,7 +591,7 @@ def test_api_increase_color_temp(hass, result, initial):
'Alexa.ColorTemperatureController', 'IncreaseColorTemperature',
'light#test')
# settup test devices
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'color_temp': initial,
@@ -470,3 +609,378 @@ def test_api_increase_color_temp(hass, result, initial):
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['color_temp'] == result
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['scene'])
def test_api_activate(hass, domain):
"""Test api activate process."""
request = get_new_request(
'Alexa.SceneController', 'Activate', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'turn_on')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_percentage_fan(hass):
"""Test api set percentage for fan process."""
request = get_new_request(
'Alexa.PercentageController', 'SetPercentage', 'fan#test_2')
# add payload
request['directive']['payload']['percentage'] = '50'
# setup test devices
hass.states.async_set(
'fan.test_2', 'off', {'friendly_name': "Test fan"})
call_fan = async_mock_service(hass, 'fan', 'set_speed')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_fan) == 1
assert call_fan[0].data['entity_id'] == 'fan.test_2'
assert call_fan[0].data['speed'] == 'medium'
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_percentage_cover(hass):
"""Test api set percentage for cover process."""
request = get_new_request(
'Alexa.PercentageController', 'SetPercentage', 'cover#test')
# add payload
request['directive']['payload']['percentage'] = '50'
# setup test devices
hass.states.async_set(
'cover.test', 'closed', {
'friendly_name': "Test cover"
})
call_cover = async_mock_service(hass, 'cover', 'set_cover_position')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_cover) == 1
assert call_cover[0].data['entity_id'] == 'cover.test'
assert call_cover[0].data['position'] == 50
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"result,adjust", [('high', '-5'), ('off', '5'), ('low', '-80')])
def test_api_adjust_percentage_fan(hass, result, adjust):
"""Test api adjust percentage for fan process."""
request = get_new_request(
'Alexa.PercentageController', 'AdjustPercentage', 'fan#test_2')
# add payload
request['directive']['payload']['percentageDelta'] = adjust
# setup test devices
hass.states.async_set(
'fan.test_2', 'on', {
'friendly_name': "Test fan 2", 'speed': 'high'
})
call_fan = async_mock_service(hass, 'fan', 'set_speed')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_fan) == 1
assert call_fan[0].data['entity_id'] == 'fan.test_2'
assert call_fan[0].data['speed'] == result
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"result,adjust", [(25, '-5'), (35, '5'), (0, '-80')])
def test_api_adjust_percentage_cover(hass, result, adjust):
"""Test api adjust percentage for cover process."""
request = get_new_request(
'Alexa.PercentageController', 'AdjustPercentage', 'cover#test')
# add payload
request['directive']['payload']['percentageDelta'] = adjust
# setup test devices
hass.states.async_set(
'cover.test', 'closed', {
'friendly_name': "Test cover",
'position': 30
})
call_cover = async_mock_service(hass, 'cover', 'set_cover_position')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_cover) == 1
assert call_cover[0].data['entity_id'] == 'cover.test'
assert call_cover[0].data['position'] == result
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['lock'])
def test_api_lock(hass, domain):
"""Test api lock process."""
request = get_new_request(
'Alexa.LockController', 'Lock', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'lock')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_play(hass, domain):
"""Test api play process."""
request = get_new_request(
'Alexa.PlaybackController', 'Play', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'media_play')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_pause(hass, domain):
"""Test api pause process."""
request = get_new_request(
'Alexa.PlaybackController', 'Pause', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'media_pause')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_stop(hass, domain):
"""Test api stop process."""
request = get_new_request(
'Alexa.PlaybackController', 'Stop', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'media_stop')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_next(hass, domain):
"""Test api next process."""
request = get_new_request(
'Alexa.PlaybackController', 'Next', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'media_next_track')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_previous(hass, domain):
"""Test api previous process."""
request = get_new_request(
'Alexa.PlaybackController', 'Previous', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'media_previous_track')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_volume(hass):
"""Test api set volume process."""
request = get_new_request(
'Alexa.Speaker', 'SetVolume', 'media_player#test')
# add payload
request['directive']['payload']['volume'] = 50
# setup test devices
hass.states.async_set(
'media_player.test', 'off', {
'friendly_name': "Test media player", 'volume_level': 0
})
call_media_player = async_mock_service(hass, 'media_player', 'volume_set')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_media_player) == 1
assert call_media_player[0].data['entity_id'] == 'media_player.test'
assert call_media_player[0].data['volume_level'] == 0.5
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"result,adjust", [(0.7, '-5'), (0.8, '5'), (0, '-80')])
def test_api_adjust_volume(hass, result, adjust):
"""Test api adjust volume process."""
request = get_new_request(
'Alexa.Speaker', 'AdjustVolume', 'media_player#test')
# add payload
request['directive']['payload']['volume'] = adjust
# setup test devices
hass.states.async_set(
'media_player.test', 'off', {
'friendly_name': "Test media player", 'volume_level': 0.75
})
call_media_player = async_mock_service(hass, 'media_player', 'volume_set')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call_media_player) == 1
assert call_media_player[0].data['entity_id'] == 'media_player.test'
assert call_media_player[0].data['volume_level'] == result
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['media_player'])
def test_api_mute(hass, domain):
"""Test api mute process."""
request = get_new_request(
'Alexa.Speaker', 'SetMute', '{}#test'.format(domain))
request['directive']['payload']['mute'] = True
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call = async_mock_service(hass, domain, 'volume_mute')
msg = yield from smart_home.async_handle_message(hass, request)
assert 'event' in msg
msg = msg['event']
assert len(call) == 1
assert call[0].data['entity_id'] == '{}.test'.format(domain)
assert msg['header']['name'] == 'Response'