mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 08:17:08 +00:00
Alexa: context + log events (#16023)
This commit is contained in:
parent
1f0d113688
commit
18d19fde0b
@ -53,6 +53,7 @@ CONF_DISPLAY_CATEGORIES = 'display_categories'
|
|||||||
|
|
||||||
HANDLERS = Registry()
|
HANDLERS = Registry()
|
||||||
ENTITY_ADAPTERS = Registry()
|
ENTITY_ADAPTERS = Registry()
|
||||||
|
EVENT_ALEXA_SMART_HOME = 'alexa_smart_home'
|
||||||
|
|
||||||
|
|
||||||
class _DisplayCategory:
|
class _DisplayCategory:
|
||||||
@ -703,24 +704,47 @@ class SmartHomeView(http.HomeAssistantView):
|
|||||||
return b'' if response is None else self.json(response)
|
return b'' if response is None else self.json(response)
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def async_handle_message(hass, config, request, context=None):
|
||||||
def async_handle_message(hass, config, message):
|
|
||||||
"""Handle incoming API messages."""
|
"""Handle incoming API messages."""
|
||||||
assert message[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3'
|
assert request[API_DIRECTIVE][API_HEADER]['payloadVersion'] == '3'
|
||||||
|
|
||||||
|
if context is None:
|
||||||
|
context = ha.Context()
|
||||||
|
|
||||||
# Read head data
|
# Read head data
|
||||||
message = message[API_DIRECTIVE]
|
request = request[API_DIRECTIVE]
|
||||||
namespace = message[API_HEADER]['namespace']
|
namespace = request[API_HEADER]['namespace']
|
||||||
name = message[API_HEADER]['name']
|
name = request[API_HEADER]['name']
|
||||||
|
|
||||||
# Do we support this API request?
|
# Do we support this API request?
|
||||||
funct_ref = HANDLERS.get((namespace, name))
|
funct_ref = HANDLERS.get((namespace, name))
|
||||||
if not funct_ref:
|
if funct_ref:
|
||||||
|
response = await funct_ref(hass, config, request, context)
|
||||||
|
else:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Unsupported API request %s/%s", namespace, name)
|
"Unsupported API request %s/%s", namespace, name)
|
||||||
return api_error(message)
|
response = api_error(request)
|
||||||
|
|
||||||
return (yield from funct_ref(hass, config, message))
|
request_info = {
|
||||||
|
'namespace': namespace,
|
||||||
|
'name': name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if API_ENDPOINT in request and 'endpointId' in request[API_ENDPOINT]:
|
||||||
|
request_info['entity_id'] = \
|
||||||
|
request[API_ENDPOINT]['endpointId'].replace('#', '.')
|
||||||
|
|
||||||
|
response_header = response[API_EVENT][API_HEADER]
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_ALEXA_SMART_HOME, {
|
||||||
|
'request': request_info,
|
||||||
|
'response': {
|
||||||
|
'namespace': response_header['namespace'],
|
||||||
|
'name': response_header['name'],
|
||||||
|
}
|
||||||
|
}, context=context)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def api_message(request,
|
def api_message(request,
|
||||||
@ -784,8 +808,7 @@ def api_error(request,
|
|||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.Discovery', 'Discover'))
|
@HANDLERS.register(('Alexa.Discovery', 'Discover'))
|
||||||
@asyncio.coroutine
|
async def async_api_discovery(hass, config, request, context):
|
||||||
def async_api_discovery(hass, config, request):
|
|
||||||
"""Create a API formatted discovery response.
|
"""Create a API formatted discovery response.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
@ -827,8 +850,7 @@ def async_api_discovery(hass, config, request):
|
|||||||
|
|
||||||
def extract_entity(funct):
|
def extract_entity(funct):
|
||||||
"""Decorate for extract entity object from request."""
|
"""Decorate for extract entity object from request."""
|
||||||
@asyncio.coroutine
|
async def async_api_entity_wrapper(hass, config, request, context):
|
||||||
def async_api_entity_wrapper(hass, config, request):
|
|
||||||
"""Process a turn on request."""
|
"""Process a turn on request."""
|
||||||
entity_id = request[API_ENDPOINT]['endpointId'].replace('#', '.')
|
entity_id = request[API_ENDPOINT]['endpointId'].replace('#', '.')
|
||||||
|
|
||||||
@ -839,15 +861,14 @@ def extract_entity(funct):
|
|||||||
request[API_HEADER]['name'], entity_id)
|
request[API_HEADER]['name'], entity_id)
|
||||||
return api_error(request, error_type='NO_SUCH_ENDPOINT')
|
return api_error(request, error_type='NO_SUCH_ENDPOINT')
|
||||||
|
|
||||||
return (yield from funct(hass, config, request, entity))
|
return await funct(hass, config, request, context, entity)
|
||||||
|
|
||||||
return async_api_entity_wrapper
|
return async_api_entity_wrapper
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PowerController', 'TurnOn'))
|
@HANDLERS.register(('Alexa.PowerController', 'TurnOn'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_turn_on(hass, config, request, context, entity):
|
||||||
def async_api_turn_on(hass, config, request, entity):
|
|
||||||
"""Process a turn on request."""
|
"""Process a turn on request."""
|
||||||
domain = entity.domain
|
domain = entity.domain
|
||||||
if entity.domain == group.DOMAIN:
|
if entity.domain == group.DOMAIN:
|
||||||
@ -857,17 +878,16 @@ def async_api_turn_on(hass, config, request, entity):
|
|||||||
if entity.domain == cover.DOMAIN:
|
if entity.domain == cover.DOMAIN:
|
||||||
service = cover.SERVICE_OPEN_COVER
|
service = cover.SERVICE_OPEN_COVER
|
||||||
|
|
||||||
yield from hass.services.async_call(domain, service, {
|
await hass.services.async_call(domain, service, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PowerController', 'TurnOff'))
|
@HANDLERS.register(('Alexa.PowerController', 'TurnOff'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_turn_off(hass, config, request, context, entity):
|
||||||
def async_api_turn_off(hass, config, request, entity):
|
|
||||||
"""Process a turn off request."""
|
"""Process a turn off request."""
|
||||||
domain = entity.domain
|
domain = entity.domain
|
||||||
if entity.domain == group.DOMAIN:
|
if entity.domain == group.DOMAIN:
|
||||||
@ -877,32 +897,30 @@ def async_api_turn_off(hass, config, request, entity):
|
|||||||
if entity.domain == cover.DOMAIN:
|
if entity.domain == cover.DOMAIN:
|
||||||
service = cover.SERVICE_CLOSE_COVER
|
service = cover.SERVICE_CLOSE_COVER
|
||||||
|
|
||||||
yield from hass.services.async_call(domain, service, {
|
await hass.services.async_call(domain, service, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.BrightnessController', 'SetBrightness'))
|
@HANDLERS.register(('Alexa.BrightnessController', 'SetBrightness'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_brightness(hass, config, request, context, entity):
|
||||||
def async_api_set_brightness(hass, config, request, entity):
|
|
||||||
"""Process a set brightness request."""
|
"""Process a set brightness request."""
|
||||||
brightness = int(request[API_PAYLOAD]['brightness'])
|
brightness = int(request[API_PAYLOAD]['brightness'])
|
||||||
|
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_BRIGHTNESS_PCT: brightness,
|
light.ATTR_BRIGHTNESS_PCT: brightness,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.BrightnessController', 'AdjustBrightness'))
|
@HANDLERS.register(('Alexa.BrightnessController', 'AdjustBrightness'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_adjust_brightness(hass, config, request, context, entity):
|
||||||
def async_api_adjust_brightness(hass, config, request, entity):
|
|
||||||
"""Process an adjust brightness request."""
|
"""Process an adjust brightness request."""
|
||||||
brightness_delta = int(request[API_PAYLOAD]['brightnessDelta'])
|
brightness_delta = int(request[API_PAYLOAD]['brightnessDelta'])
|
||||||
|
|
||||||
@ -915,18 +933,17 @@ def async_api_adjust_brightness(hass, config, request, entity):
|
|||||||
|
|
||||||
# set brightness
|
# set brightness
|
||||||
brightness = max(0, brightness_delta + current)
|
brightness = max(0, brightness_delta + current)
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_BRIGHTNESS_PCT: brightness,
|
light.ATTR_BRIGHTNESS_PCT: brightness,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.ColorController', 'SetColor'))
|
@HANDLERS.register(('Alexa.ColorController', 'SetColor'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_color(hass, config, request, context, entity):
|
||||||
def async_api_set_color(hass, config, request, entity):
|
|
||||||
"""Process a set color request."""
|
"""Process a set color request."""
|
||||||
rgb = color_util.color_hsb_to_RGB(
|
rgb = color_util.color_hsb_to_RGB(
|
||||||
float(request[API_PAYLOAD]['color']['hue']),
|
float(request[API_PAYLOAD]['color']['hue']),
|
||||||
@ -934,25 +951,25 @@ def async_api_set_color(hass, config, request, entity):
|
|||||||
float(request[API_PAYLOAD]['color']['brightness'])
|
float(request[API_PAYLOAD]['color']['brightness'])
|
||||||
)
|
)
|
||||||
|
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_RGB_COLOR: rgb,
|
light.ATTR_RGB_COLOR: rgb,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.ColorTemperatureController', 'SetColorTemperature'))
|
@HANDLERS.register(('Alexa.ColorTemperatureController', 'SetColorTemperature'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_color_temperature(hass, config, request, context,
|
||||||
def async_api_set_color_temperature(hass, config, request, entity):
|
entity):
|
||||||
"""Process a set color temperature request."""
|
"""Process a set color temperature request."""
|
||||||
kelvin = int(request[API_PAYLOAD]['colorTemperatureInKelvin'])
|
kelvin = int(request[API_PAYLOAD]['colorTemperatureInKelvin'])
|
||||||
|
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_KELVIN: kelvin,
|
light.ATTR_KELVIN: kelvin,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
@ -960,17 +977,17 @@ def async_api_set_color_temperature(hass, config, request, entity):
|
|||||||
@HANDLERS.register(
|
@HANDLERS.register(
|
||||||
('Alexa.ColorTemperatureController', 'DecreaseColorTemperature'))
|
('Alexa.ColorTemperatureController', 'DecreaseColorTemperature'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_decrease_color_temp(hass, config, request, context,
|
||||||
def async_api_decrease_color_temp(hass, config, request, entity):
|
entity):
|
||||||
"""Process a decrease color temperature request."""
|
"""Process a decrease color temperature request."""
|
||||||
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
|
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
|
||||||
max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS))
|
max_mireds = int(entity.attributes.get(light.ATTR_MAX_MIREDS))
|
||||||
|
|
||||||
value = min(max_mireds, current + 50)
|
value = min(max_mireds, current + 50)
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_COLOR_TEMP: value,
|
light.ATTR_COLOR_TEMP: value,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
@ -978,31 +995,30 @@ def async_api_decrease_color_temp(hass, config, request, entity):
|
|||||||
@HANDLERS.register(
|
@HANDLERS.register(
|
||||||
('Alexa.ColorTemperatureController', 'IncreaseColorTemperature'))
|
('Alexa.ColorTemperatureController', 'IncreaseColorTemperature'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_increase_color_temp(hass, config, request, context,
|
||||||
def async_api_increase_color_temp(hass, config, request, entity):
|
entity):
|
||||||
"""Process an increase color temperature request."""
|
"""Process an increase color temperature request."""
|
||||||
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
|
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
|
||||||
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
|
min_mireds = int(entity.attributes.get(light.ATTR_MIN_MIREDS))
|
||||||
|
|
||||||
value = max(min_mireds, current - 50)
|
value = max(min_mireds, current - 50)
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(entity.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id,
|
ATTR_ENTITY_ID: entity.entity_id,
|
||||||
light.ATTR_COLOR_TEMP: value,
|
light.ATTR_COLOR_TEMP: value,
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.SceneController', 'Activate'))
|
@HANDLERS.register(('Alexa.SceneController', 'Activate'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_activate(hass, config, request, context, entity):
|
||||||
def async_api_activate(hass, config, request, entity):
|
|
||||||
"""Process an activate request."""
|
"""Process an activate request."""
|
||||||
domain = entity.domain
|
domain = entity.domain
|
||||||
|
|
||||||
yield from hass.services.async_call(domain, SERVICE_TURN_ON, {
|
await hass.services.async_call(domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'cause': {'type': _Cause.VOICE_INTERACTION},
|
'cause': {'type': _Cause.VOICE_INTERACTION},
|
||||||
@ -1019,14 +1035,13 @@ def async_api_activate(hass, config, request, entity):
|
|||||||
|
|
||||||
@HANDLERS.register(('Alexa.SceneController', 'Deactivate'))
|
@HANDLERS.register(('Alexa.SceneController', 'Deactivate'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_deactivate(hass, config, request, context, entity):
|
||||||
def async_api_deactivate(hass, config, request, entity):
|
|
||||||
"""Process a deactivate request."""
|
"""Process a deactivate request."""
|
||||||
domain = entity.domain
|
domain = entity.domain
|
||||||
|
|
||||||
yield from hass.services.async_call(domain, SERVICE_TURN_OFF, {
|
await hass.services.async_call(domain, SERVICE_TURN_OFF, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'cause': {'type': _Cause.VOICE_INTERACTION},
|
'cause': {'type': _Cause.VOICE_INTERACTION},
|
||||||
@ -1043,8 +1058,7 @@ def async_api_deactivate(hass, config, request, entity):
|
|||||||
|
|
||||||
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
|
@HANDLERS.register(('Alexa.PercentageController', 'SetPercentage'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_percentage(hass, config, request, context, entity):
|
||||||
def async_api_set_percentage(hass, config, request, entity):
|
|
||||||
"""Process a set percentage request."""
|
"""Process a set percentage request."""
|
||||||
percentage = int(request[API_PAYLOAD]['percentage'])
|
percentage = int(request[API_PAYLOAD]['percentage'])
|
||||||
service = None
|
service = None
|
||||||
@ -1066,16 +1080,15 @@ def async_api_set_percentage(hass, config, request, entity):
|
|||||||
service = SERVICE_SET_COVER_POSITION
|
service = SERVICE_SET_COVER_POSITION
|
||||||
data[cover.ATTR_POSITION] = percentage
|
data[cover.ATTR_POSITION] = percentage
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, service, data, blocking=False)
|
entity.domain, service, data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage'))
|
@HANDLERS.register(('Alexa.PercentageController', 'AdjustPercentage'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_adjust_percentage(hass, config, request, context, entity):
|
||||||
def async_api_adjust_percentage(hass, config, request, entity):
|
|
||||||
"""Process an adjust percentage request."""
|
"""Process an adjust percentage request."""
|
||||||
percentage_delta = int(request[API_PAYLOAD]['percentageDelta'])
|
percentage_delta = int(request[API_PAYLOAD]['percentageDelta'])
|
||||||
service = None
|
service = None
|
||||||
@ -1114,20 +1127,19 @@ def async_api_adjust_percentage(hass, config, request, entity):
|
|||||||
|
|
||||||
data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
|
data[cover.ATTR_POSITION] = max(0, percentage_delta + current)
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, service, data, blocking=False)
|
entity.domain, service, data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.LockController', 'Lock'))
|
@HANDLERS.register(('Alexa.LockController', 'Lock'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_lock(hass, config, request, context, entity):
|
||||||
def async_api_lock(hass, config, request, entity):
|
|
||||||
"""Process a lock request."""
|
"""Process a lock request."""
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_LOCK, {
|
await hass.services.async_call(entity.domain, SERVICE_LOCK, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
# Alexa expects a lockState in the response, we don't know the actual
|
# Alexa expects a lockState in the response, we don't know the actual
|
||||||
# lockState at this point but assume it is locked. It is reported
|
# lockState at this point but assume it is locked. It is reported
|
||||||
@ -1144,20 +1156,18 @@ def async_api_lock(hass, config, request, entity):
|
|||||||
# Not supported by Alexa yet
|
# Not supported by Alexa yet
|
||||||
@HANDLERS.register(('Alexa.LockController', 'Unlock'))
|
@HANDLERS.register(('Alexa.LockController', 'Unlock'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_unlock(hass, config, request, context, entity):
|
||||||
def async_api_unlock(hass, config, request, entity):
|
|
||||||
"""Process an unlock request."""
|
"""Process an unlock request."""
|
||||||
yield from hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
|
await hass.services.async_call(entity.domain, SERVICE_UNLOCK, {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}, blocking=False)
|
}, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.Speaker', 'SetVolume'))
|
@HANDLERS.register(('Alexa.Speaker', 'SetVolume'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_volume(hass, config, request, context, entity):
|
||||||
def async_api_set_volume(hass, config, request, entity):
|
|
||||||
"""Process a set volume request."""
|
"""Process a set volume request."""
|
||||||
volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2)
|
volume = round(float(request[API_PAYLOAD]['volume'] / 100), 2)
|
||||||
|
|
||||||
@ -1166,17 +1176,16 @@ def async_api_set_volume(hass, config, request, entity):
|
|||||||
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_VOLUME_SET,
|
entity.domain, SERVICE_VOLUME_SET,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.InputController', 'SelectInput'))
|
@HANDLERS.register(('Alexa.InputController', 'SelectInput'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_select_input(hass, config, request, context, entity):
|
||||||
def async_api_select_input(hass, config, request, entity):
|
|
||||||
"""Process a set input request."""
|
"""Process a set input request."""
|
||||||
media_input = request[API_PAYLOAD]['input']
|
media_input = request[API_PAYLOAD]['input']
|
||||||
|
|
||||||
@ -1200,17 +1209,16 @@ def async_api_select_input(hass, config, request, entity):
|
|||||||
media_player.ATTR_INPUT_SOURCE: media_input,
|
media_player.ATTR_INPUT_SOURCE: media_input,
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, media_player.SERVICE_SELECT_SOURCE,
|
entity.domain, media_player.SERVICE_SELECT_SOURCE,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
|
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_adjust_volume(hass, config, request, context, entity):
|
||||||
def async_api_adjust_volume(hass, config, request, entity):
|
|
||||||
"""Process an adjust volume request."""
|
"""Process an adjust volume request."""
|
||||||
volume_delta = int(request[API_PAYLOAD]['volume'])
|
volume_delta = int(request[API_PAYLOAD]['volume'])
|
||||||
|
|
||||||
@ -1229,17 +1237,16 @@ def async_api_adjust_volume(hass, config, request, entity):
|
|||||||
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
media_player.ATTR_MEDIA_VOLUME_LEVEL: volume,
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, media_player.SERVICE_VOLUME_SET,
|
entity.domain, media_player.SERVICE_VOLUME_SET,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.StepSpeaker', 'AdjustVolume'))
|
@HANDLERS.register(('Alexa.StepSpeaker', 'AdjustVolume'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_adjust_volume_step(hass, config, request, context, entity):
|
||||||
def async_api_adjust_volume_step(hass, config, request, entity):
|
|
||||||
"""Process an adjust volume step request."""
|
"""Process an adjust volume step request."""
|
||||||
# media_player volume up/down service does not support specifying steps
|
# media_player volume up/down service does not support specifying steps
|
||||||
# each component handles it differently e.g. via config.
|
# each component handles it differently e.g. via config.
|
||||||
@ -1252,13 +1259,13 @@ def async_api_adjust_volume_step(hass, config, request, entity):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if volume_step > 0:
|
if volume_step > 0:
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, media_player.SERVICE_VOLUME_UP,
|
entity.domain, media_player.SERVICE_VOLUME_UP,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
elif volume_step < 0:
|
elif volume_step < 0:
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, media_player.SERVICE_VOLUME_DOWN,
|
entity.domain, media_player.SERVICE_VOLUME_DOWN,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
@ -1266,8 +1273,7 @@ def async_api_adjust_volume_step(hass, config, request, entity):
|
|||||||
@HANDLERS.register(('Alexa.StepSpeaker', 'SetMute'))
|
@HANDLERS.register(('Alexa.StepSpeaker', 'SetMute'))
|
||||||
@HANDLERS.register(('Alexa.Speaker', 'SetMute'))
|
@HANDLERS.register(('Alexa.Speaker', 'SetMute'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_set_mute(hass, config, request, context, entity):
|
||||||
def async_api_set_mute(hass, config, request, entity):
|
|
||||||
"""Process a set mute request."""
|
"""Process a set mute request."""
|
||||||
mute = bool(request[API_PAYLOAD]['mute'])
|
mute = bool(request[API_PAYLOAD]['mute'])
|
||||||
|
|
||||||
@ -1276,89 +1282,84 @@ def async_api_set_mute(hass, config, request, entity):
|
|||||||
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
|
media_player.ATTR_MEDIA_VOLUME_MUTED: mute,
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, media_player.SERVICE_VOLUME_MUTE,
|
entity.domain, media_player.SERVICE_VOLUME_MUTE,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PlaybackController', 'Play'))
|
@HANDLERS.register(('Alexa.PlaybackController', 'Play'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_play(hass, config, request, context, entity):
|
||||||
def async_api_play(hass, config, request, entity):
|
|
||||||
"""Process a play request."""
|
"""Process a play request."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_MEDIA_PLAY,
|
entity.domain, SERVICE_MEDIA_PLAY,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PlaybackController', 'Pause'))
|
@HANDLERS.register(('Alexa.PlaybackController', 'Pause'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_pause(hass, config, request, context, entity):
|
||||||
def async_api_pause(hass, config, request, entity):
|
|
||||||
"""Process a pause request."""
|
"""Process a pause request."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_MEDIA_PAUSE,
|
entity.domain, SERVICE_MEDIA_PAUSE,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PlaybackController', 'Stop'))
|
@HANDLERS.register(('Alexa.PlaybackController', 'Stop'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_stop(hass, config, request, context, entity):
|
||||||
def async_api_stop(hass, config, request, entity):
|
|
||||||
"""Process a stop request."""
|
"""Process a stop request."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_MEDIA_STOP,
|
entity.domain, SERVICE_MEDIA_STOP,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PlaybackController', 'Next'))
|
@HANDLERS.register(('Alexa.PlaybackController', 'Next'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_next(hass, config, request, context, entity):
|
||||||
def async_api_next(hass, config, request, entity):
|
|
||||||
"""Process a next request."""
|
"""Process a next request."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_MEDIA_NEXT_TRACK,
|
entity.domain, SERVICE_MEDIA_NEXT_TRACK,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.PlaybackController', 'Previous'))
|
@HANDLERS.register(('Alexa.PlaybackController', 'Previous'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_previous(hass, config, request, context, entity):
|
||||||
def async_api_previous(hass, config, request, entity):
|
|
||||||
"""Process a previous request."""
|
"""Process a previous request."""
|
||||||
data = {
|
data = {
|
||||||
ATTR_ENTITY_ID: entity.entity_id
|
ATTR_ENTITY_ID: entity.entity_id
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, SERVICE_MEDIA_PREVIOUS_TRACK,
|
entity.domain, SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||||
data, blocking=False)
|
data, blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
@ -1405,7 +1406,7 @@ def temperature_from_object(temp_obj, to_unit, interval=False):
|
|||||||
|
|
||||||
@HANDLERS.register(('Alexa.ThermostatController', 'SetTargetTemperature'))
|
@HANDLERS.register(('Alexa.ThermostatController', 'SetTargetTemperature'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
async def async_api_set_target_temp(hass, config, request, entity):
|
async def async_api_set_target_temp(hass, config, request, context, entity):
|
||||||
"""Process a set target temperature request."""
|
"""Process a set target temperature request."""
|
||||||
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
|
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
|
||||||
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
|
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
|
||||||
@ -1439,14 +1440,15 @@ async def async_api_set_target_temp(hass, config, request, entity):
|
|||||||
data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high
|
data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False)
|
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False,
|
||||||
|
context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.ThermostatController', 'AdjustTargetTemperature'))
|
@HANDLERS.register(('Alexa.ThermostatController', 'AdjustTargetTemperature'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
async def async_api_adjust_target_temp(hass, config, request, entity):
|
async def async_api_adjust_target_temp(hass, config, request, context, entity):
|
||||||
"""Process an adjust target temperature request."""
|
"""Process an adjust target temperature request."""
|
||||||
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
|
unit = entity.attributes[CONF_UNIT_OF_MEASUREMENT]
|
||||||
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
|
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
|
||||||
@ -1466,14 +1468,16 @@ async def async_api_adjust_target_temp(hass, config, request, entity):
|
|||||||
}
|
}
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False)
|
entity.domain, climate.SERVICE_SET_TEMPERATURE, data, blocking=False,
|
||||||
|
context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa.ThermostatController', 'SetThermostatMode'))
|
@HANDLERS.register(('Alexa.ThermostatController', 'SetThermostatMode'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
async def async_api_set_thermostat_mode(hass, config, request, entity):
|
async def async_api_set_thermostat_mode(hass, config, request, context,
|
||||||
|
entity):
|
||||||
"""Process a set thermostat mode request."""
|
"""Process a set thermostat mode request."""
|
||||||
mode = request[API_PAYLOAD]['thermostatMode']
|
mode = request[API_PAYLOAD]['thermostatMode']
|
||||||
mode = mode if isinstance(mode, str) else mode['value']
|
mode = mode if isinstance(mode, str) else mode['value']
|
||||||
@ -1499,15 +1503,14 @@ async def async_api_set_thermostat_mode(hass, config, request, entity):
|
|||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
entity.domain, climate.SERVICE_SET_OPERATION_MODE, data,
|
entity.domain, climate.SERVICE_SET_OPERATION_MODE, data,
|
||||||
blocking=False)
|
blocking=False, context=context)
|
||||||
|
|
||||||
return api_message(request)
|
return api_message(request)
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register(('Alexa', 'ReportState'))
|
@HANDLERS.register(('Alexa', 'ReportState'))
|
||||||
@extract_entity
|
@extract_entity
|
||||||
@asyncio.coroutine
|
async def async_api_reportstate(hass, config, request, context, entity):
|
||||||
def async_api_reportstate(hass, config, request, entity):
|
|
||||||
"""Process a ReportState request."""
|
"""Process a ReportState request."""
|
||||||
alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity)
|
alexa_entity = ENTITY_ADAPTERS[entity.domain](config, entity)
|
||||||
properties = []
|
properties = []
|
||||||
|
@ -5,6 +5,7 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.core import Context, callback
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
TEMP_FAHRENHEIT, STATE_LOCKED, STATE_UNLOCKED,
|
TEMP_FAHRENHEIT, STATE_LOCKED, STATE_UNLOCKED,
|
||||||
STATE_UNKNOWN)
|
STATE_UNKNOWN)
|
||||||
@ -18,6 +19,17 @@ from tests.common import async_mock_service
|
|||||||
DEFAULT_CONFIG = smart_home.Config(should_expose=lambda entity_id: True)
|
DEFAULT_CONFIG = smart_home.Config(should_expose=lambda entity_id: True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def events(hass):
|
||||||
|
"""Fixture that catches alexa events."""
|
||||||
|
events = []
|
||||||
|
hass.bus.async_listen(
|
||||||
|
smart_home.EVENT_ALEXA_SMART_HOME,
|
||||||
|
callback(lambda e: events.append(e))
|
||||||
|
)
|
||||||
|
yield events
|
||||||
|
|
||||||
|
|
||||||
def get_new_request(namespace, name, endpoint=None):
|
def get_new_request(namespace, name, endpoint=None):
|
||||||
"""Generate a new API message."""
|
"""Generate a new API message."""
|
||||||
raw_msg = {
|
raw_msg = {
|
||||||
@ -145,7 +157,7 @@ def assert_endpoint_capabilities(endpoint, *interfaces):
|
|||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_switch(hass):
|
def test_switch(hass, events):
|
||||||
"""Test switch discovery."""
|
"""Test switch discovery."""
|
||||||
device = ('switch.test', 'on', {'friendly_name': "Test switch"})
|
device = ('switch.test', 'on', {'friendly_name': "Test switch"})
|
||||||
appliance = yield from discovery_test(device, hass)
|
appliance = yield from discovery_test(device, hass)
|
||||||
@ -963,23 +975,26 @@ def assert_request_calls_service(
|
|||||||
response_type='Response',
|
response_type='Response',
|
||||||
payload=None):
|
payload=None):
|
||||||
"""Assert an API request calls a hass service."""
|
"""Assert an API request calls a hass service."""
|
||||||
|
context = Context()
|
||||||
request = get_new_request(namespace, name, endpoint)
|
request = get_new_request(namespace, name, endpoint)
|
||||||
if payload:
|
if payload:
|
||||||
request['directive']['payload'] = payload
|
request['directive']['payload'] = payload
|
||||||
|
|
||||||
domain, service_name = service.split('.')
|
domain, service_name = service.split('.')
|
||||||
call = async_mock_service(hass, domain, service_name)
|
calls = async_mock_service(hass, domain, service_name)
|
||||||
|
|
||||||
msg = yield from smart_home.async_handle_message(
|
msg = yield from smart_home.async_handle_message(
|
||||||
hass, DEFAULT_CONFIG, request)
|
hass, DEFAULT_CONFIG, request, context)
|
||||||
yield from hass.async_block_till_done()
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(call) == 1
|
assert len(calls) == 1
|
||||||
|
call = calls[0]
|
||||||
assert 'event' in msg
|
assert 'event' in msg
|
||||||
assert call[0].data['entity_id'] == endpoint.replace('#', '.')
|
assert call.data['entity_id'] == endpoint.replace('#', '.')
|
||||||
assert msg['event']['header']['name'] == response_type
|
assert msg['event']['header']['name'] == response_type
|
||||||
|
assert call.context == context
|
||||||
|
|
||||||
return call[0], msg
|
return call, msg
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
@ -1372,3 +1387,53 @@ def test_api_select_input(hass, domain, payload, source_list, idx):
|
|||||||
hass,
|
hass,
|
||||||
payload={'input': payload})
|
payload={'input': payload})
|
||||||
assert call.data['source'] == source_list[idx]
|
assert call.data['source'] == source_list[idx]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_logging_request(hass, events):
|
||||||
|
"""Test that we log requests."""
|
||||||
|
context = Context()
|
||||||
|
request = get_new_request('Alexa.Discovery', 'Discover')
|
||||||
|
await smart_home.async_handle_message(
|
||||||
|
hass, DEFAULT_CONFIG, request, context)
|
||||||
|
|
||||||
|
# To trigger event listener
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
event = events[0]
|
||||||
|
|
||||||
|
assert event.data['request'] == {
|
||||||
|
'namespace': 'Alexa.Discovery',
|
||||||
|
'name': 'Discover',
|
||||||
|
}
|
||||||
|
assert event.data['response'] == {
|
||||||
|
'namespace': 'Alexa.Discovery',
|
||||||
|
'name': 'Discover.Response'
|
||||||
|
}
|
||||||
|
assert event.context == context
|
||||||
|
|
||||||
|
|
||||||
|
async def test_logging_request_with_entity(hass, events):
|
||||||
|
"""Test that we log requests."""
|
||||||
|
context = Context()
|
||||||
|
request = get_new_request('Alexa.PowerController', 'TurnOn', 'switch#xy')
|
||||||
|
await smart_home.async_handle_message(
|
||||||
|
hass, DEFAULT_CONFIG, request, context)
|
||||||
|
|
||||||
|
# To trigger event listener
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
event = events[0]
|
||||||
|
|
||||||
|
assert event.data['request'] == {
|
||||||
|
'namespace': 'Alexa.PowerController',
|
||||||
|
'name': 'TurnOn',
|
||||||
|
'entity_id': 'switch.xy'
|
||||||
|
}
|
||||||
|
# Entity doesn't exist
|
||||||
|
assert event.data['response'] == {
|
||||||
|
'namespace': 'Alexa',
|
||||||
|
'name': 'ErrorResponse'
|
||||||
|
}
|
||||||
|
assert event.context == context
|
||||||
|
Loading…
x
Reference in New Issue
Block a user