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:
parent
bf8e2bd77e
commit
e449ceeeff
2 changed files with 853 additions and 27 deletions
|
@ -4,9 +4,16 @@ import logging
|
|||
import math
|
||||
from uuid import uuid4
|
||||
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
||||
from homeassistant.components import switch, light, script
|
||||
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, SERVICE_LOCK,
|
||||
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
|
||||
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
||||
SERVICE_UNLOCK, SERVICE_VOLUME_SET)
|
||||
from homeassistant.components import (
|
||||
alert, automation, cover, fan, group, input_boolean, light, lock,
|
||||
media_player, scene, script, switch)
|
||||
import homeassistant.util.color as color_util
|
||||
from homeassistant.util.decorator import Registry
|
||||
|
||||
|
@ -14,15 +21,32 @@ HANDLERS = Registry()
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
API_DIRECTIVE = 'directive'
|
||||
API_ENDPOINT = 'endpoint'
|
||||
API_EVENT = 'event'
|
||||
API_HEADER = 'header'
|
||||
API_PAYLOAD = 'payload'
|
||||
API_ENDPOINT = 'endpoint'
|
||||
|
||||
ATTR_ALEXA_DESCRIPTION = 'alexa_description'
|
||||
ATTR_ALEXA_DISPLAY_CATEGORIES = 'alexa_display_categories'
|
||||
ATTR_ALEXA_HIDDEN = 'alexa_hidden'
|
||||
ATTR_ALEXA_NAME = 'alexa_name'
|
||||
|
||||
|
||||
MAPPING_COMPONENT = {
|
||||
script.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
|
||||
switch.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
|
||||
alert.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
|
||||
automation.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
|
||||
cover.DOMAIN: [
|
||||
'DOOR', ('Alexa.PowerController',), {
|
||||
cover.SUPPORT_SET_POSITION: 'Alexa.PercentageController',
|
||||
}
|
||||
],
|
||||
fan.DOMAIN: [
|
||||
'OTHER', ('Alexa.PowerController',), {
|
||||
fan.SUPPORT_SET_SPEED: 'Alexa.PercentageController',
|
||||
}
|
||||
],
|
||||
group.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
|
||||
input_boolean.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
|
||||
light.DOMAIN: [
|
||||
'LIGHT', ('Alexa.PowerController',), {
|
||||
light.SUPPORT_BRIGHTNESS: 'Alexa.BrightnessController',
|
||||
|
@ -31,6 +55,20 @@ MAPPING_COMPONENT = {
|
|||
light.SUPPORT_COLOR_TEMP: 'Alexa.ColorTemperatureController',
|
||||
}
|
||||
],
|
||||
lock.DOMAIN: ['SMARTLOCK', ('Alexa.LockController',), None],
|
||||
media_player.DOMAIN: [
|
||||
'TV', ('Alexa.PowerController',), {
|
||||
media_player.SUPPORT_VOLUME_SET: 'Alexa.Speaker',
|
||||
media_player.SUPPORT_PLAY: 'Alexa.PlaybackController',
|
||||
media_player.SUPPORT_PAUSE: 'Alexa.PlaybackController',
|
||||
media_player.SUPPORT_STOP: 'Alexa.PlaybackController',
|
||||
media_player.SUPPORT_NEXT_TRACK: 'Alexa.PlaybackController',
|
||||
media_player.SUPPORT_PREVIOUS_TRACK: 'Alexa.PlaybackController',
|
||||
}
|
||||
],
|
||||
scene.DOMAIN: ['ACTIVITY_TRIGGER', ('Alexa.SceneController',), None],
|
||||
script.DOMAIN: ['OTHER', ('Alexa.PowerController',), None],
|
||||
switch.DOMAIN: ['SWITCH', ('Alexa.PowerController',), None],
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,18 +146,33 @@ def async_api_discovery(hass, request):
|
|||
discovery_endpoints = []
|
||||
|
||||
for entity in hass.states.async_all():
|
||||
if entity.attributes.get(ATTR_ALEXA_HIDDEN, False):
|
||||
continue
|
||||
|
||||
class_data = MAPPING_COMPONENT.get(entity.domain)
|
||||
|
||||
if not class_data:
|
||||
continue
|
||||
|
||||
friendly_name = entity.attributes.get(ATTR_ALEXA_NAME, entity.name)
|
||||
description = entity.attributes.get(ATTR_ALEXA_DESCRIPTION,
|
||||
entity.entity_id)
|
||||
|
||||
# Required description as per Amazon Scene docs
|
||||
if entity.domain == scene.DOMAIN:
|
||||
scene_fmt = '%s (Scene connected via Home Assistant)'
|
||||
description = scene_fmt.format(description)
|
||||
|
||||
cat_key = ATTR_ALEXA_DISPLAY_CATEGORIES
|
||||
display_categories = entity.attributes.get(cat_key, class_data[0])
|
||||
|
||||
endpoint = {
|
||||
'displayCategories': [class_data[0]],
|
||||
'displayCategories': [display_categories],
|
||||
'additionalApplianceDetails': {},
|
||||
'endpointId': entity.entity_id.replace('.', '#'),
|
||||
'friendlyName': entity.name,
|
||||
'description': '',
|
||||
'manufacturerName': 'Unknown',
|
||||
'friendlyName': friendly_name,
|
||||
'description': description,
|
||||
'manufacturerName': 'Home Assistant',
|
||||
}
|
||||
actions = set()
|
||||
|
||||
|
@ -175,7 +228,7 @@ def extract_entity(funct):
|
|||
@asyncio.coroutine
|
||||
def async_api_turn_on(hass, request, entity):
|
||||
"""Process a turn on request."""
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||
yield from hass.services.async_call(ha.DOMAIN, SERVICE_TURN_ON, {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}, blocking=True)
|
||||
|
||||
|
@ -187,7 +240,7 @@ def async_api_turn_on(hass, request, entity):
|
|||
@asyncio.coroutine
|
||||
def async_api_turn_off(hass, request, entity):
|
||||
"""Process a turn off request."""
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_OFF, {
|
||||
yield from hass.services.async_call(ha.DOMAIN, SERVICE_TURN_OFF, {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}, blocking=True)
|
||||
|
||||
|
@ -310,3 +363,262 @@ def async_api_increase_color_temp(hass, request, entity):
|
|||
}, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.SceneController', 'Activate'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_activate(hass, request, entity):
|
||||
"""Process a activate request."""
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_set_percentage(hass, request, entity):
|
||||
"""Process a set percentage request."""
|
||||
percentage = int(request[API_PAYLOAD]['percentage'])
|
||||
service = None
|
||||
data = {ATTR_ENTITY_ID: entity.entity_id}
|
||||
|
||||
if entity.domain == fan.DOMAIN:
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
speed = "off"
|
||||
|
||||
if percentage <= 33:
|
||||
speed = "low"
|
||||
elif percentage <= 66:
|
||||
speed = "medium"
|
||||
elif percentage <= 100:
|
||||
speed = "high"
|
||||
data[fan.ATTR_SPEED] = speed
|
||||
|
||||
elif entity.domain == cover.DOMAIN:
|
||||
service = SERVICE_SET_COVER_POSITION
|
||||
data[cover.ATTR_POSITION] = percentage
|
||||
|
||||
yield from hass.services.async_call(entity.domain, service,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_adjust_percentage(hass, request, entity):
|
||||
"""Process a adjust percentage request."""
|
||||
percentage_delta = int(request[API_PAYLOAD]['percentageDelta'])
|
||||
service = None
|
||||
data = {ATTR_ENTITY_ID: entity.entity_id}
|
||||
|
||||
if entity.domain == fan.DOMAIN:
|
||||
service = fan.SERVICE_SET_SPEED
|
||||
speed = entity.attributes.get(fan.ATTR_SPEED)
|
||||
|
||||
if speed == "off":
|
||||
current = 0
|
||||
elif speed == "low":
|
||||
current = 33
|
||||
elif speed == "medium":
|
||||
current = 66
|
||||
elif speed == "high":
|
||||
current = 100
|
||||
|
||||
# set percentage
|
||||
percentage = max(0, percentage_delta + current)
|
||||
speed = "off"
|
||||
|
||||
if percentage <= 33:
|
||||
speed = "low"
|
||||
elif percentage <= 66:
|
||||
speed = "medium"
|
||||
elif percentage <= 100:
|
||||
speed = "high"
|
||||
|
||||
data[fan.ATTR_SPEED] = speed
|
||||
|
||||
elif entity.domain == cover.DOMAIN:
|
||||
service = SERVICE_SET_COVER_POSITION
|
||||
|
||||
current = entity.attributes.get(cover.ATTR_POSITION)
|
||||
|
||||
data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
|
||||
|
||||
yield from hass.services.async_call(entity.domain, service,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.LockController', 'Lock'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_lock(hass, request, entity):
|
||||
"""Process a lock request."""
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_LOCK, {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
# Not supported by Alexa yet
|
||||
@HANDLERS.register(('Alexa.LockController', 'Unlock'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_unlock(hass, request, entity):
|
||||
"""Process a unlock request."""
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.Speaker', 'SetVolume'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_set_volume(hass, request, entity):
|
||||
"""Process a set volume request."""
|
||||
volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2)
|
||||
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_VOLUME_SET,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_adjust_volume(hass, request, entity):
|
||||
"""Process a adjust volume request."""
|
||||
volume_delta = int(request[API_PAYLOAD]['volume'])
|
||||
|
||||
current_level = entity.attributes.get(media_player.ATTR_MEDIA_VOLUME_LEVEL)
|
||||
|
||||
# read current state
|
||||
try:
|
||||
current = math.floor(int(current_level * 100))
|
||||
except ZeroDivisionError:
|
||||
current = 0
|
||||
|
||||
volume = float(max(0, volume_delta + current) / 100)
|
||||
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain,
|
||||
media_player.SERVICE_VOLUME_SET,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.Speaker', 'SetMute'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_set_mute(hass, request, entity):
|
||||
"""Process a set mute request."""
|
||||
mute = bool(request[API_PAYLOAD]['mute'])
|
||||
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain,
|
||||
media_player.SERVICE_VOLUME_MUTE,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PlaybackController', 'Play'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_play(hass, request, entity):
|
||||
"""Process a play request."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_MEDIA_PLAY,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PlaybackController', 'Pause'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_pause(hass, request, entity):
|
||||
"""Process a pause request."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_MEDIA_PAUSE,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PlaybackController', 'Stop'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_stop(hass, request, entity):
|
||||
"""Process a stop request."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain, SERVICE_MEDIA_STOP,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PlaybackController', 'Next'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_next(hass, request, entity):
|
||||
"""Process a next request."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
||||
|
||||
@HANDLERS.register(('Alexa.PlaybackController', 'Previous'))
|
||||
@extract_entity
|
||||
@asyncio.coroutine
|
||||
def async_api_previous(hass, request, entity):
|
||||
"""Process a previous request."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id
|
||||
}
|
||||
|
||||
yield from hass.services.async_call(entity.domain,
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||
data, blocking=True)
|
||||
|
||||
return api_message(request)
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue