hass-core/tests/components/alexa/test_smart_home.py

1486 lines
46 KiB
Python
Raw Normal View History

"""Test for smart home alexa support."""
import asyncio
import json
from uuid import uuid4
import pytest
from homeassistant.const import (
TEMP_FAHRENHEIT, CONF_UNIT_OF_MEASUREMENT, STATE_LOCKED, STATE_UNLOCKED,
STATE_UNKNOWN, STATE_ON, STATE_OFF)
from homeassistant.setup import async_setup_component
from homeassistant.components import alexa, light
from homeassistant.components.alexa import smart_home
from homeassistant.helpers import entityfilter
from tests.common import async_mock_service
DEFAULT_CONFIG = smart_home.Config(should_expose=lambda entity_id: True)
def get_new_request(namespace, name, endpoint=None):
"""Generate a new API message."""
raw_msg = {
'directive': {
'header': {
'namespace': namespace,
'name': name,
'messageId': str(uuid4()),
'correlationToken': str(uuid4()),
'payloadVersion': '3',
},
'endpoint': {
'scope': {
'type': 'BearerToken',
'token': str(uuid4()),
},
'endpointId': endpoint,
},
'payload': {},
}
}
if not endpoint:
raw_msg['directive'].pop('endpoint')
return raw_msg
def test_create_api_message_defaults():
"""Create a API message response of a request with defaults."""
request = get_new_request('Alexa.PowerController', 'TurnOn', 'switch#xy')
request = request['directive']
msg = smart_home.api_message(request, payload={'test': 3})
assert 'event' in msg
msg = msg['event']
assert msg['header']['messageId'] is not None
assert msg['header']['messageId'] != request['header']['messageId']
assert msg['header']['correlationToken'] == \
request['header']['correlationToken']
assert msg['header']['name'] == 'Response'
assert msg['header']['namespace'] == 'Alexa'
assert msg['header']['payloadVersion'] == '3'
assert 'test' in msg['payload']
assert msg['payload']['test'] == 3
assert msg['endpoint'] == request['endpoint']
def test_create_api_message_special():
"""Create a API message response of a request with non defaults."""
request = get_new_request('Alexa.PowerController', 'TurnOn')
request = request['directive']
request['header'].pop('correlationToken')
msg = smart_home.api_message(request, 'testName', 'testNameSpace')
assert 'event' in msg
msg = msg['event']
assert msg['header']['messageId'] is not None
assert msg['header']['messageId'] != request['header']['messageId']
assert 'correlationToken' not in msg['header']
assert msg['header']['name'] == 'testName'
assert msg['header']['namespace'] == 'testNameSpace'
assert msg['header']['payloadVersion'] == '3'
assert msg['payload'] == {}
assert 'endpoint' not in msg
@asyncio.coroutine
def test_wrong_version(hass):
"""Test with wrong version."""
msg = get_new_request('Alexa.PowerController', 'TurnOn')
msg['directive']['header']['payloadVersion'] = '2'
with pytest.raises(AssertionError):
yield from smart_home.async_handle_message(hass, DEFAULT_CONFIG, msg)
@asyncio.coroutine
def test_discovery_request(hass):
"""Test alexa discovery request."""
request = get_new_request('Alexa.Discovery', 'Discover')
# setup test devices
hass.states.async_set(
'switch.test', 'on', {'friendly_name': "Test switch"})
hass.states.async_set(
'light.test_1', 'on', {'friendly_name': "Test light 1"})
hass.states.async_set(
'light.test_2', 'on', {
'friendly_name': "Test light 2", 'supported_features': 1
})
hass.states.async_set(
'light.test_3', 'on', {
'friendly_name': "Test light 3", 'supported_features': 19
})
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"})
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
})
hass.states.async_set(
'sensor.test_temp', '59', {
'friendly_name': "Test Temp Sensor",
'unit_of_measurement': TEMP_FAHRENHEIT,
})
# This sensor measures a quantity not applicable to Alexa, and should not
# be discovered.
hass.states.async_set(
'sensor.test_sickness', '0.1', {
'friendly_name': "Test Space Sickness Sensor",
'unit_of_measurement': 'garn',
})
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
assert 'event' in msg
msg = msg['event']
assert msg['header']['name'] == 'Discover.Response'
assert msg['header']['namespace'] == 'Alexa.Discovery'
endpoint_ids = set(
appliance['endpointId']
for appliance in msg['payload']['endpoints'])
assert endpoint_ids == {
'switch#test',
'light#test_1',
'light#test_2',
'light#test_3',
'script#test',
'script#test_2',
'input_boolean#test',
'scene#test',
'fan#test_1',
'fan#test_2',
'lock#test',
'media_player#test',
'alert#test',
'automation#test',
'group#test',
'cover#test',
'sensor#test_temp',
}
for appliance in msg['payload']['endpoints']:
if appliance['endpointId'] == 'switch#test':
assert appliance['displayCategories'][0] == "SWITCH"
assert appliance['friendlyName'] == "Test switch"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'light#test_1':
assert appliance['displayCategories'][0] == "LIGHT"
assert appliance['friendlyName'] == "Test light 1"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.PowerController'
continue
if appliance['endpointId'] == 'light#test_2':
assert appliance['displayCategories'][0] == "LIGHT"
assert appliance['friendlyName'] == "Test light 2"
assert len(appliance['capabilities']) == 2
caps = set()
for feature in appliance['capabilities']:
caps.add(feature['interface'])
assert 'Alexa.BrightnessController' in caps
assert 'Alexa.PowerController' in caps
continue
if appliance['endpointId'] == 'light#test_3':
assert appliance['displayCategories'][0] == "LIGHT"
assert appliance['friendlyName'] == "Test light 3"
assert len(appliance['capabilities']) == 4
caps = set()
for feature in appliance['capabilities']:
caps.add(feature['interface'])
assert 'Alexa.BrightnessController' in caps
assert 'Alexa.PowerController' in caps
assert 'Alexa.ColorController' in caps
assert 'Alexa.ColorTemperatureController' in caps
continue
if appliance['endpointId'] == 'script#test':
assert appliance['displayCategories'][0] == "ACTIVITY_TRIGGER"
assert appliance['friendlyName'] == "Test script"
assert len(appliance['capabilities']) == 1
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':
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] == "SCENE_TRIGGER"
assert appliance['friendlyName'] == "Test scene"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'Alexa.SceneController'
capability = appliance['capabilities'][-1]
assert not capability['supportsDeactivation']
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] == "SCENE_TRIGGER"
assert appliance['friendlyName'] == "Test group"
assert len(appliance['capabilities']) == 1
capability = appliance['capabilities'][-1]
assert capability['interface'] == 'Alexa.SceneController'
assert capability['supportsDeactivation']
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
if appliance['endpointId'] == 'sensor#test_temp':
assert appliance['displayCategories'][0] == 'TEMPERATURE_SENSOR'
assert appliance['friendlyName'] == 'Test Temp Sensor'
assert len(appliance['capabilities']) == 1
capability = appliance['capabilities'][0]
assert capability['interface'] == 'Alexa.TemperatureSensor'
properties = capability['properties']
assert properties['retrievable'] is True
assert {'name': 'temperature'} in properties['supported']
continue
raise AssertionError("Unknown appliance!")
@asyncio.coroutine
def test_exclude_filters(hass):
"""Test exclusion filters."""
request = get_new_request('Alexa.Discovery', 'Discover')
# setup test devices
hass.states.async_set(
'switch.test', 'on', {'friendly_name': "Test switch"})
hass.states.async_set(
'script.deny', 'off', {'friendly_name': "Blocked script"})
hass.states.async_set(
'cover.deny', 'off', {'friendly_name': "Blocked cover"})
config = smart_home.Config(should_expose=entityfilter.generate_filter(
include_domains=[],
include_entities=[],
exclude_domains=['script'],
exclude_entities=['cover.deny'],
))
msg = yield from smart_home.async_handle_message(hass, config, request)
yield from hass.async_block_till_done()
msg = msg['event']
assert len(msg['payload']['endpoints']) == 1
@asyncio.coroutine
def test_include_filters(hass):
"""Test inclusion filters."""
request = get_new_request('Alexa.Discovery', 'Discover')
# setup test devices
hass.states.async_set(
'switch.deny', 'on', {'friendly_name': "Blocked switch"})
hass.states.async_set(
'script.deny', 'off', {'friendly_name': "Blocked script"})
hass.states.async_set(
'automation.allow', 'off', {'friendly_name': "Allowed automation"})
hass.states.async_set(
'group.allow', 'off', {'friendly_name': "Allowed group"})
config = smart_home.Config(should_expose=entityfilter.generate_filter(
include_domains=['automation', 'group'],
include_entities=['script.deny'],
exclude_domains=[],
exclude_entities=[],
))
msg = yield from smart_home.async_handle_message(hass, config, request)
yield from hass.async_block_till_done()
msg = msg['event']
assert len(msg['payload']['endpoints']) == 3
@asyncio.coroutine
def test_api_entity_not_exists(hass):
"""Test api turn on process without entity."""
request = get_new_request('Alexa.PowerController', 'TurnOn', 'switch#test')
call_switch = async_mock_service(hass, 'switch', 'turn_on')
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_switch) == 0
assert msg['header']['name'] == 'ErrorResponse'
assert msg['header']['namespace'] == 'Alexa'
assert msg['payload']['type'] == 'NO_SUCH_ENDPOINT'
@asyncio.coroutine
def test_api_function_not_implemented(hass):
"""Test api call that is not implemented to us."""
request = get_new_request('Alexa.HAHAAH', 'Sweet')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
assert 'event' in msg
msg = msg['event']
assert msg['header']['name'] == 'ErrorResponse'
assert msg['header']['namespace'] == 'Alexa'
assert msg['payload']['type'] == 'INTERNAL_ERROR'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['alert', 'automation', 'cover',
'input_boolean', 'light',
'switch'])
def test_api_turn_on(hass, domain):
"""Test api turn on process."""
request = get_new_request(
'Alexa.PowerController', 'TurnOn', '{}#test'.format(domain))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'off', {
'friendly_name': "Test {}".format(domain)
})
call_domain = domain
if domain == 'cover':
call = async_mock_service(hass, call_domain, 'open_cover')
else:
call = async_mock_service(hass, call_domain, 'turn_on')
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'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("domain", ['alert', 'automation', 'cover', '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))
# setup test devices
hass.states.async_set(
'{}.test'.format(domain), 'on', {
'friendly_name': "Test {}".format(domain)
})
call_domain = domain
if domain == 'group':
call_domain = 'homeassistant'
if domain == 'cover':
call = async_mock_service(hass, call_domain, 'close_cover')
else:
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'] == 'Response'
@asyncio.coroutine
def test_api_set_brightness(hass):
"""Test api set brightness process."""
request = get_new_request(
'Alexa.BrightnessController', 'SetBrightness', 'light#test')
# add payload
request['directive']['payload']['brightness'] = '50'
# setup test devices
hass.states.async_set(
'light.test', 'off', {'friendly_name': "Test light"})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['brightness_pct'] == 50
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"result,adjust", [(25, '-5'), (35, '5'), (0, '-80')])
def test_api_adjust_brightness(hass, result, adjust):
"""Test api adjust brightness process."""
request = get_new_request(
'Alexa.BrightnessController', 'AdjustBrightness', 'light#test')
# add payload
request['directive']['payload']['brightnessDelta'] = adjust
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'brightness': '77'
})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['brightness_pct'] == result
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_color_rgb(hass):
"""Test api set color process."""
request = get_new_request(
'Alexa.ColorController', 'SetColor', 'light#test')
# add payload
request['directive']['payload']['color'] = {
'hue': '120',
'saturation': '0.612',
'brightness': '0.342',
}
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light",
'supported_features': 16,
})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['rgb_color'] == (33, 87, 33)
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_color_xy(hass):
"""Test api set color process."""
request = get_new_request(
'Alexa.ColorController', 'SetColor', 'light#test')
# add payload
request['directive']['payload']['color'] = {
'hue': '120',
'saturation': '0.612',
'brightness': '0.342',
}
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light",
'supported_features': 64,
})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['xy_color'] == (0.23, 0.585)
assert call_light[0].data['brightness'] == 18
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
def test_api_set_color_temperature(hass):
"""Test api set color temperature process."""
request = get_new_request(
'Alexa.ColorTemperatureController', 'SetColorTemperature',
'light#test')
# add payload
request['directive']['payload']['colorTemperatureInKelvin'] = '7500'
# setup test devices
hass.states.async_set(
'light.test', 'off', {'friendly_name': "Test light"})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
assert call_light[0].data['entity_id'] == 'light.test'
assert call_light[0].data['kelvin'] == 7500
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize("result,initial", [(383, '333'), (500, '500')])
def test_api_decrease_color_temp(hass, result, initial):
"""Test api decrease color temp process."""
request = get_new_request(
'Alexa.ColorTemperatureController', 'DecreaseColorTemperature',
'light#test')
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'color_temp': initial,
'max_mireds': 500,
})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
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("result,initial", [(283, '333'), (142, '142')])
def test_api_increase_color_temp(hass, result, initial):
"""Test api increase color temp process."""
request = get_new_request(
'Alexa.ColorTemperatureController', 'IncreaseColorTemperature',
'light#test')
# setup test devices
hass.states.async_set(
'light.test', 'off', {
'friendly_name': "Test light", 'color_temp': initial,
'min_mireds': 142,
})
call_light = async_mock_service(hass, 'light', 'turn_on')
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_light) == 1
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', 'group', 'script'])
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)
})
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)
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'] == 'ActivationStarted'
assert msg['payload']['cause']['type'] == 'VOICE_INTERACTION'
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."""
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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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, 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'] == '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, 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'] == '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, 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'] == '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, 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'] == '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, 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'] == '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, 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'] == '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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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(
"domain,payload,source_list,idx", [
('media_player', 'GAME CONSOLE', ['tv', 'game console'], 1),
('media_player', 'SATELLITE TV', ['satellite-tv', 'game console'], 0),
('media_player', 'SATELLITE TV', ['satellite_tv', 'game console'], 0),
('media_player', 'BAD DEVICE', ['satellite_tv', 'game console'], None),
]
)
def test_api_select_input(hass, domain, payload, source_list, idx):
"""Test api set input process."""
request = get_new_request(
'Alexa.InputController', 'SelectInput', 'media_player#test')
# add payload
request['directive']['payload']['input'] = payload
# setup test devices
hass.states.async_set(
'media_player.test', 'off', {
'friendly_name': "Test media player",
'source': 'unknown',
'source_list': source_list,
})
call_media_player = async_mock_service(hass, domain, 'select_source')
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']
# test where no source matches
if idx is None:
assert len(call_media_player) == 0
assert msg['header']['name'] == 'ErrorResponse'
return
assert len(call_media_player) == 1
assert call_media_player[0].data['entity_id'] == 'media_player.test'
assert call_media_player[0].data['source'] == source_list[idx]
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, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
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, 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'] == 'Response'
@asyncio.coroutine
def test_api_report_temperature(hass):
"""Test API ReportState response for a temperature sensor."""
request = get_new_request('Alexa', 'ReportState', 'sensor#test')
# setup test devices
hass.states.async_set(
'sensor.test', '42', {
'friendly_name': 'test sensor',
CONF_UNIT_OF_MEASUREMENT: TEMP_FAHRENHEIT,
})
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
header = msg['event']['header']
assert header['namespace'] == 'Alexa'
assert header['name'] == 'StateReport'
properties = msg['context']['properties']
assert len(properties) == 1
prop = properties[0]
assert prop['namespace'] == 'Alexa.TemperatureSensor'
assert prop['name'] == 'temperature'
assert prop['value'] == {'value': 42.0, 'scale': 'FAHRENHEIT'}
@asyncio.coroutine
def test_report_lock_state(hass):
"""Test LockController implements lockState property."""
hass.states.async_set(
'lock.locked', STATE_LOCKED, {})
hass.states.async_set(
'lock.unlocked', STATE_UNLOCKED, {})
hass.states.async_set(
'lock.unknown', STATE_UNKNOWN, {})
request = get_new_request('Alexa', 'ReportState', 'lock#locked')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
properties = msg['context']['properties']
assert len(properties) == 1
prop = properties[0]
assert prop['namespace'] == 'Alexa.LockController'
assert prop['name'] == 'lockState'
assert prop['value'] == 'LOCKED'
request = get_new_request('Alexa', 'ReportState', 'lock#unlocked')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
properties = msg['context']['properties']
prop = properties[0]
assert prop['value'] == 'UNLOCKED'
request = get_new_request('Alexa', 'ReportState', 'lock#unknown')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
properties = msg['context']['properties']
prop = properties[0]
assert prop['value'] == 'JAMMED'
@asyncio.coroutine
def test_report_power_state(hass):
"""Test PowerController implements powerState property."""
hass.states.async_set(
'switch.on', STATE_ON, {})
hass.states.async_set(
'switch.off', STATE_OFF, {})
request = get_new_request('Alexa', 'ReportState', 'switch#on')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
properties = msg['context']['properties']
assert len(properties) == 1
prop = properties[0]
assert prop['namespace'] == 'Alexa.PowerController'
assert prop['name'] == 'powerState'
assert prop['value'] == 'ON'
request = get_new_request('Alexa', 'ReportState', 'switch#off')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
@asyncio.coroutine
def test_report_brightness(hass):
"""Test BrightnessController implements brightness property."""
hass.states.async_set(
'light.test', STATE_ON, {
'brightness': 128,
'supported_features': light.SUPPORT_BRIGHTNESS,
}
)
request = get_new_request('Alexa', 'ReportState', 'light.test')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
for prop in msg['context']['properties']:
if (
prop['namespace'] == 'Alexa.BrightnessController'
and prop['name'] == 'brightness'
):
assert prop['value'] == 50
break
else:
assert False, 'no brightness property present'
@asyncio.coroutine
def test_entity_config(hass):
"""Test that we can configure things via entity config."""
request = get_new_request('Alexa.Discovery', 'Discover')
hass.states.async_set(
'light.test_1', 'on', {'friendly_name': "Test light 1"})
config = smart_home.Config(
should_expose=lambda entity_id: True,
entity_config={
'light.test_1': {
'name': 'Config name',
'display_categories': 'SWITCH',
'description': 'Config description'
}
}
)
msg = yield from smart_home.async_handle_message(
hass, config, request)
assert 'event' in msg
msg = msg['event']
assert len(msg['payload']['endpoints']) == 1
appliance = msg['payload']['endpoints'][0]
assert appliance['endpointId'] == 'light#test_1'
assert appliance['displayCategories'][0] == "SWITCH"
assert appliance['friendlyName'] == "Config name"
assert appliance['description'] == "Config description"
assert len(appliance['capabilities']) == 1
assert appliance['capabilities'][-1]['interface'] == \
'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."""
yield from async_setup_component(hass, alexa.DOMAIN, config)
http_client = yield from test_client(hass.http.app)
request = get_new_request('Alexa.Discovery', 'Discover')
response = yield from http_client.post(
smart_home.SMART_HOME_HTTP_ENDPOINT,
data=json.dumps(request),
headers={'content-type': 'application/json'})
return response
@asyncio.coroutine
def test_http_api(hass, test_client):
"""With `smart_home:` HTTP API is exposed."""
config = {
'alexa': {
'smart_home': None
}
}
response = yield from do_http_discovery(config, hass, test_client)
response_data = yield from response.json()
# Here we're testing just the HTTP view glue -- details of discovery are
# covered in other tests.
assert response_data['event']['header']['name'] == 'Discover.Response'
@asyncio.coroutine
def test_http_api_disabled(hass, test_client):
"""Without `smart_home:`, the HTTP API is disabled."""
config = {
'alexa': {}
}
response = yield from do_http_discovery(config, hass, test_client)
assert response.status == 404