mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Google Assistant: Create and pass context to service calls (#21551)
* Google Assistant: Create and pass context to service calls * Refactor request data into separate object and pass to execute.
This commit is contained in:
parent
fc1ee9be43
commit
d1038ea79f
@ -236,7 +236,6 @@ class Cloud:
|
|||||||
self._gactions_config = ga_h.Config(
|
self._gactions_config = ga_h.Config(
|
||||||
should_expose=should_expose,
|
should_expose=should_expose,
|
||||||
allow_unlock=self.prefs.google_allow_unlock,
|
allow_unlock=self.prefs.google_allow_unlock,
|
||||||
agent_user_id=self.claims['cognito:username'],
|
|
||||||
entity_config=conf.get(CONF_ENTITY_CONFIG),
|
entity_config=conf.get(CONF_ENTITY_CONFIG),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -334,7 +334,9 @@ def async_handle_google_actions(hass, cloud, payload):
|
|||||||
return ga.turned_off_response(payload)
|
return ga.turned_off_response(payload)
|
||||||
|
|
||||||
result = yield from ga.async_handle_message(
|
result = yield from ga.async_handle_message(
|
||||||
hass, cloud.gactions_config, payload)
|
hass, cloud.gactions_config,
|
||||||
|
cloud.claims['cognito:username'],
|
||||||
|
payload)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""Helper classes for Google Assistant integration."""
|
"""Helper classes for Google Assistant integration."""
|
||||||
|
from homeassistant.core import Context
|
||||||
|
|
||||||
|
|
||||||
class SmartHomeError(Exception):
|
class SmartHomeError(Exception):
|
||||||
@ -16,10 +17,19 @@ class SmartHomeError(Exception):
|
|||||||
class Config:
|
class Config:
|
||||||
"""Hold the configuration for Google Assistant."""
|
"""Hold the configuration for Google Assistant."""
|
||||||
|
|
||||||
def __init__(self, should_expose, allow_unlock, agent_user_id,
|
def __init__(self, should_expose, allow_unlock,
|
||||||
entity_config=None):
|
entity_config=None):
|
||||||
"""Initialize the configuration."""
|
"""Initialize the configuration."""
|
||||||
self.should_expose = should_expose
|
self.should_expose = should_expose
|
||||||
self.agent_user_id = agent_user_id
|
|
||||||
self.entity_config = entity_config or {}
|
self.entity_config = entity_config or {}
|
||||||
self.allow_unlock = allow_unlock
|
self.allow_unlock = allow_unlock
|
||||||
|
|
||||||
|
|
||||||
|
class RequestData:
|
||||||
|
"""Hold data associated with a particular request."""
|
||||||
|
|
||||||
|
def __init__(self, config, user_id, request_id):
|
||||||
|
"""Initialize the request data."""
|
||||||
|
self.config = config
|
||||||
|
self.request_id = request_id
|
||||||
|
self.context = Context(user_id=user_id)
|
||||||
|
@ -71,17 +71,16 @@ class GoogleAssistantView(HomeAssistantView):
|
|||||||
|
|
||||||
def __init__(self, is_exposed, entity_config, allow_unlock):
|
def __init__(self, is_exposed, entity_config, allow_unlock):
|
||||||
"""Initialize the Google Assistant request handler."""
|
"""Initialize the Google Assistant request handler."""
|
||||||
self.is_exposed = is_exposed
|
self.config = Config(is_exposed,
|
||||||
self.entity_config = entity_config
|
allow_unlock,
|
||||||
self.allow_unlock = allow_unlock
|
entity_config)
|
||||||
|
|
||||||
async def post(self, request: Request) -> Response:
|
async def post(self, request: Request) -> Response:
|
||||||
"""Handle Google Assistant requests."""
|
"""Handle Google Assistant requests."""
|
||||||
message = await request.json() # type: dict
|
message = await request.json() # type: dict
|
||||||
config = Config(self.is_exposed,
|
|
||||||
self.allow_unlock,
|
|
||||||
request['hass_user'].id,
|
|
||||||
self.entity_config)
|
|
||||||
result = await async_handle_message(
|
result = await async_handle_message(
|
||||||
request.app['hass'], config, message)
|
request.app['hass'],
|
||||||
|
self.config,
|
||||||
|
request['hass_user'].id,
|
||||||
|
message)
|
||||||
return self.json(result)
|
return self.json(result)
|
||||||
|
@ -36,7 +36,7 @@ from .const import (
|
|||||||
ERR_UNKNOWN_ERROR,
|
ERR_UNKNOWN_ERROR,
|
||||||
EVENT_COMMAND_RECEIVED, EVENT_SYNC_RECEIVED, EVENT_QUERY_RECEIVED
|
EVENT_COMMAND_RECEIVED, EVENT_SYNC_RECEIVED, EVENT_QUERY_RECEIVED
|
||||||
)
|
)
|
||||||
from .helpers import SmartHomeError
|
from .helpers import SmartHomeError, RequestData
|
||||||
|
|
||||||
HANDLERS = Registry()
|
HANDLERS = Registry()
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -87,7 +87,8 @@ class _GoogleEntity:
|
|||||||
domain = state.domain
|
domain = state.domain
|
||||||
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||||
|
|
||||||
return [Trait(self.hass, state, self.config) for Trait in trait.TRAITS
|
return [Trait(self.hass, state, self.config)
|
||||||
|
for Trait in trait.TRAITS
|
||||||
if Trait.supported(domain, features)]
|
if Trait.supported(domain, features)]
|
||||||
|
|
||||||
async def sync_serialize(self):
|
async def sync_serialize(self):
|
||||||
@ -178,7 +179,7 @@ class _GoogleEntity:
|
|||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a command.
|
"""Execute a command.
|
||||||
|
|
||||||
https://developers.google.com/actions/smarthome/create-app#actiondevicesexecute
|
https://developers.google.com/actions/smarthome/create-app#actiondevicesexecute
|
||||||
@ -186,7 +187,7 @@ class _GoogleEntity:
|
|||||||
executed = False
|
executed = False
|
||||||
for trt in self.traits():
|
for trt in self.traits():
|
||||||
if trt.can_execute(command, params):
|
if trt.can_execute(command, params):
|
||||||
await trt.execute(command, params)
|
await trt.execute(command, data, params)
|
||||||
executed = True
|
executed = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -202,9 +203,13 @@ class _GoogleEntity:
|
|||||||
self.state = self.hass.states.get(self.entity_id)
|
self.state = self.hass.states.get(self.entity_id)
|
||||||
|
|
||||||
|
|
||||||
async def async_handle_message(hass, config, message):
|
async def async_handle_message(hass, config, user_id, message):
|
||||||
"""Handle incoming API messages."""
|
"""Handle incoming API messages."""
|
||||||
response = await _process(hass, config, message)
|
request_id = message.get('requestId') # type: str
|
||||||
|
|
||||||
|
data = RequestData(config, user_id, request_id)
|
||||||
|
|
||||||
|
response = await _process(hass, data, message)
|
||||||
|
|
||||||
if response and 'errorCode' in response['payload']:
|
if response and 'errorCode' in response['payload']:
|
||||||
_LOGGER.error('Error handling message %s: %s',
|
_LOGGER.error('Error handling message %s: %s',
|
||||||
@ -213,14 +218,13 @@ async def async_handle_message(hass, config, message):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
async def _process(hass, config, message):
|
async def _process(hass, data, message):
|
||||||
"""Process a message."""
|
"""Process a message."""
|
||||||
request_id = message.get('requestId') # type: str
|
|
||||||
inputs = message.get('inputs') # type: list
|
inputs = message.get('inputs') # type: list
|
||||||
|
|
||||||
if len(inputs) != 1:
|
if len(inputs) != 1:
|
||||||
return {
|
return {
|
||||||
'requestId': request_id,
|
'requestId': data.request_id,
|
||||||
'payload': {'errorCode': ERR_PROTOCOL_ERROR}
|
'payload': {'errorCode': ERR_PROTOCOL_ERROR}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,49 +232,49 @@ async def _process(hass, config, message):
|
|||||||
|
|
||||||
if handler is None:
|
if handler is None:
|
||||||
return {
|
return {
|
||||||
'requestId': request_id,
|
'requestId': data.request_id,
|
||||||
'payload': {'errorCode': ERR_PROTOCOL_ERROR}
|
'payload': {'errorCode': ERR_PROTOCOL_ERROR}
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await handler(hass, config, request_id,
|
result = await handler(hass, data, inputs[0].get('payload'))
|
||||||
inputs[0].get('payload'))
|
|
||||||
except SmartHomeError as err:
|
except SmartHomeError as err:
|
||||||
return {
|
return {
|
||||||
'requestId': request_id,
|
'requestId': data.request_id,
|
||||||
'payload': {'errorCode': err.code}
|
'payload': {'errorCode': err.code}
|
||||||
}
|
}
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception('Unexpected error')
|
_LOGGER.exception('Unexpected error')
|
||||||
return {
|
return {
|
||||||
'requestId': request_id,
|
'requestId': data.request_id,
|
||||||
'payload': {'errorCode': ERR_UNKNOWN_ERROR}
|
'payload': {'errorCode': ERR_UNKNOWN_ERROR}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result is None:
|
if result is None:
|
||||||
return None
|
return None
|
||||||
return {'requestId': request_id, 'payload': result}
|
return {'requestId': data.request_id, 'payload': result}
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register('action.devices.SYNC')
|
@HANDLERS.register('action.devices.SYNC')
|
||||||
async def async_devices_sync(hass, config, request_id, payload):
|
async def async_devices_sync(hass, data, payload):
|
||||||
"""Handle action.devices.SYNC request.
|
"""Handle action.devices.SYNC request.
|
||||||
|
|
||||||
https://developers.google.com/actions/smarthome/create-app#actiondevicessync
|
https://developers.google.com/actions/smarthome/create-app#actiondevicessync
|
||||||
"""
|
"""
|
||||||
hass.bus.async_fire(EVENT_SYNC_RECEIVED, {
|
hass.bus.async_fire(
|
||||||
'request_id': request_id
|
EVENT_SYNC_RECEIVED,
|
||||||
})
|
{'request_id': data.request_id},
|
||||||
|
context=data.context)
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
for state in hass.states.async_all():
|
for state in hass.states.async_all():
|
||||||
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not config.should_expose(state):
|
if not data.config.should_expose(state):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entity = _GoogleEntity(hass, config, state)
|
entity = _GoogleEntity(hass, data.config, state)
|
||||||
serialized = await entity.sync_serialize()
|
serialized = await entity.sync_serialize()
|
||||||
|
|
||||||
if serialized is None:
|
if serialized is None:
|
||||||
@ -280,7 +284,7 @@ async def async_devices_sync(hass, config, request_id, payload):
|
|||||||
devices.append(serialized)
|
devices.append(serialized)
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'agentUserId': config.agent_user_id,
|
'agentUserId': data.context.user_id,
|
||||||
'devices': devices,
|
'devices': devices,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +292,7 @@ async def async_devices_sync(hass, config, request_id, payload):
|
|||||||
|
|
||||||
|
|
||||||
@HANDLERS.register('action.devices.QUERY')
|
@HANDLERS.register('action.devices.QUERY')
|
||||||
async def async_devices_query(hass, config, request_id, payload):
|
async def async_devices_query(hass, data, payload):
|
||||||
"""Handle action.devices.QUERY request.
|
"""Handle action.devices.QUERY request.
|
||||||
|
|
||||||
https://developers.google.com/actions/smarthome/create-app#actiondevicesquery
|
https://developers.google.com/actions/smarthome/create-app#actiondevicesquery
|
||||||
@ -298,23 +302,27 @@ async def async_devices_query(hass, config, request_id, payload):
|
|||||||
devid = device['id']
|
devid = device['id']
|
||||||
state = hass.states.get(devid)
|
state = hass.states.get(devid)
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_QUERY_RECEIVED, {
|
hass.bus.async_fire(
|
||||||
'request_id': request_id,
|
EVENT_QUERY_RECEIVED,
|
||||||
ATTR_ENTITY_ID: devid,
|
{
|
||||||
})
|
'request_id': data.request_id,
|
||||||
|
ATTR_ENTITY_ID: devid,
|
||||||
|
},
|
||||||
|
context=data.context)
|
||||||
|
|
||||||
if not state:
|
if not state:
|
||||||
# If we can't find a state, the device is offline
|
# If we can't find a state, the device is offline
|
||||||
devices[devid] = {'online': False}
|
devices[devid] = {'online': False}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
devices[devid] = _GoogleEntity(hass, config, state).query_serialize()
|
entity = _GoogleEntity(hass, data.config, state)
|
||||||
|
devices[devid] = entity.query_serialize()
|
||||||
|
|
||||||
return {'devices': devices}
|
return {'devices': devices}
|
||||||
|
|
||||||
|
|
||||||
@HANDLERS.register('action.devices.EXECUTE')
|
@HANDLERS.register('action.devices.EXECUTE')
|
||||||
async def handle_devices_execute(hass, config, request_id, payload):
|
async def handle_devices_execute(hass, data, payload):
|
||||||
"""Handle action.devices.EXECUTE request.
|
"""Handle action.devices.EXECUTE request.
|
||||||
|
|
||||||
https://developers.google.com/actions/smarthome/create-app#actiondevicesexecute
|
https://developers.google.com/actions/smarthome/create-app#actiondevicesexecute
|
||||||
@ -327,11 +335,14 @@ async def handle_devices_execute(hass, config, request_id, payload):
|
|||||||
command['execution']):
|
command['execution']):
|
||||||
entity_id = device['id']
|
entity_id = device['id']
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_COMMAND_RECEIVED, {
|
hass.bus.async_fire(
|
||||||
'request_id': request_id,
|
EVENT_COMMAND_RECEIVED,
|
||||||
ATTR_ENTITY_ID: entity_id,
|
{
|
||||||
'execution': execution
|
'request_id': data.request_id,
|
||||||
})
|
ATTR_ENTITY_ID: entity_id,
|
||||||
|
'execution': execution
|
||||||
|
},
|
||||||
|
context=data.context)
|
||||||
|
|
||||||
# Happens if error occurred. Skip entity for further processing
|
# Happens if error occurred. Skip entity for further processing
|
||||||
if entity_id in results:
|
if entity_id in results:
|
||||||
@ -348,10 +359,11 @@ async def handle_devices_execute(hass, config, request_id, payload):
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entities[entity_id] = _GoogleEntity(hass, config, state)
|
entities[entity_id] = _GoogleEntity(hass, data.config, state)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await entities[entity_id].execute(execution['command'],
|
await entities[entity_id].execute(execution['command'],
|
||||||
|
data,
|
||||||
execution.get('params', {}))
|
execution.get('params', {}))
|
||||||
except SmartHomeError as err:
|
except SmartHomeError as err:
|
||||||
results[entity_id] = {
|
results[entity_id] = {
|
||||||
@ -378,7 +390,7 @@ async def handle_devices_execute(hass, config, request_id, payload):
|
|||||||
|
|
||||||
|
|
||||||
@HANDLERS.register('action.devices.DISCONNECT')
|
@HANDLERS.register('action.devices.DISCONNECT')
|
||||||
async def async_devices_disconnect(hass, config, request_id, payload):
|
async def async_devices_disconnect(hass, data, payload):
|
||||||
"""Handle action.devices.DISCONNECT request.
|
"""Handle action.devices.DISCONNECT request.
|
||||||
|
|
||||||
https://developers.google.com/actions/smarthome/create#actiondevicesdisconnect
|
https://developers.google.com/actions/smarthome/create#actiondevicesdisconnect
|
||||||
|
@ -102,7 +102,7 @@ class _Trait:
|
|||||||
"""Test if command can be executed."""
|
"""Test if command can be executed."""
|
||||||
return command in self.commands
|
return command in self.commands
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a trait command."""
|
"""Execute a trait command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ class BrightnessTrait(_Trait):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a brightness command."""
|
"""Execute a brightness command."""
|
||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
|
|
||||||
@ -168,20 +168,20 @@ class BrightnessTrait(_Trait):
|
|||||||
light.DOMAIN, light.SERVICE_TURN_ON, {
|
light.DOMAIN, light.SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
light.ATTR_BRIGHTNESS_PCT: params['brightness']
|
light.ATTR_BRIGHTNESS_PCT: params['brightness']
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
elif domain == cover.DOMAIN:
|
elif domain == cover.DOMAIN:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, {
|
cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
cover.ATTR_POSITION: params['brightness']
|
cover.ATTR_POSITION: params['brightness']
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
elif domain == media_player.DOMAIN:
|
elif domain == media_player.DOMAIN:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
media_player.DOMAIN, media_player.SERVICE_VOLUME_SET, {
|
media_player.DOMAIN, media_player.SERVICE_VOLUME_SET, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
media_player.ATTR_MEDIA_VOLUME_LEVEL:
|
media_player.ATTR_MEDIA_VOLUME_LEVEL:
|
||||||
params['brightness'] / 100
|
params['brightness'] / 100
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -221,7 +221,7 @@ class OnOffTrait(_Trait):
|
|||||||
return {'on': self.state.state != cover.STATE_CLOSED}
|
return {'on': self.state.state != cover.STATE_CLOSED}
|
||||||
return {'on': self.state.state != STATE_OFF}
|
return {'on': self.state.state != STATE_OFF}
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute an OnOff command."""
|
"""Execute an OnOff command."""
|
||||||
domain = self.state.domain
|
domain = self.state.domain
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ class OnOffTrait(_Trait):
|
|||||||
|
|
||||||
await self.hass.services.async_call(service_domain, service, {
|
await self.hass.services.async_call(service_domain, service, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -288,7 +288,7 @@ class ColorSpectrumTrait(_Trait):
|
|||||||
return (command in self.commands and
|
return (command in self.commands and
|
||||||
'spectrumRGB' in params.get('color', {}))
|
'spectrumRGB' in params.get('color', {}))
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a color spectrum command."""
|
"""Execute a color spectrum command."""
|
||||||
# Convert integer to hex format and left pad with 0's till length 6
|
# Convert integer to hex format and left pad with 0's till length 6
|
||||||
hex_value = "{0:06x}".format(params['color']['spectrumRGB'])
|
hex_value = "{0:06x}".format(params['color']['spectrumRGB'])
|
||||||
@ -298,7 +298,7 @@ class ColorSpectrumTrait(_Trait):
|
|||||||
await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, {
|
await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
light.ATTR_HS_COLOR: color
|
light.ATTR_HS_COLOR: color
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -355,7 +355,7 @@ class ColorTemperatureTrait(_Trait):
|
|||||||
return (command in self.commands and
|
return (command in self.commands and
|
||||||
'temperature' in params.get('color', {}))
|
'temperature' in params.get('color', {}))
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a color temperature command."""
|
"""Execute a color temperature command."""
|
||||||
temp = color_util.color_temperature_kelvin_to_mired(
|
temp = color_util.color_temperature_kelvin_to_mired(
|
||||||
params['color']['temperature'])
|
params['color']['temperature'])
|
||||||
@ -371,7 +371,7 @@ class ColorTemperatureTrait(_Trait):
|
|||||||
await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, {
|
await self.hass.services.async_call(light.DOMAIN, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
light.ATTR_COLOR_TEMP: temp,
|
light.ATTR_COLOR_TEMP: temp,
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -400,13 +400,14 @@ class SceneTrait(_Trait):
|
|||||||
"""Return scene query attributes."""
|
"""Return scene query attributes."""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a scene command."""
|
"""Execute a scene command."""
|
||||||
# Don't block for scripts as they can be slow.
|
# Don't block for scripts as they can be slow.
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, SERVICE_TURN_ON, {
|
self.state.domain, SERVICE_TURN_ON, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=self.state.domain != script.DOMAIN)
|
}, blocking=self.state.domain != script.DOMAIN,
|
||||||
|
context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -434,12 +435,12 @@ class DockTrait(_Trait):
|
|||||||
"""Return dock query attributes."""
|
"""Return dock query attributes."""
|
||||||
return {'isDocked': self.state.state == vacuum.STATE_DOCKED}
|
return {'isDocked': self.state.state == vacuum.STATE_DOCKED}
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a dock command."""
|
"""Execute a dock command."""
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, vacuum.SERVICE_RETURN_TO_BASE, {
|
self.state.domain, vacuum.SERVICE_RETURN_TO_BASE, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -473,30 +474,30 @@ class StartStopTrait(_Trait):
|
|||||||
'isPaused': self.state.state == vacuum.STATE_PAUSED,
|
'isPaused': self.state.state == vacuum.STATE_PAUSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a StartStop command."""
|
"""Execute a StartStop command."""
|
||||||
if command == COMMAND_STARTSTOP:
|
if command == COMMAND_STARTSTOP:
|
||||||
if params['start']:
|
if params['start']:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, vacuum.SERVICE_START, {
|
self.state.domain, vacuum.SERVICE_START, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
else:
|
else:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, vacuum.SERVICE_STOP, {
|
self.state.domain, vacuum.SERVICE_STOP, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
elif command == COMMAND_PAUSEUNPAUSE:
|
elif command == COMMAND_PAUSEUNPAUSE:
|
||||||
if params['pause']:
|
if params['pause']:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, vacuum.SERVICE_PAUSE, {
|
self.state.domain, vacuum.SERVICE_PAUSE, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
else:
|
else:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
self.state.domain, vacuum.SERVICE_START, {
|
self.state.domain, vacuum.SERVICE_START, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -584,7 +585,7 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute a temperature point or mode command."""
|
"""Execute a temperature point or mode command."""
|
||||||
# All sent in temperatures are always in Celsius
|
# All sent in temperatures are always in Celsius
|
||||||
unit = self.hass.config.units.temperature_unit
|
unit = self.hass.config.units.temperature_unit
|
||||||
@ -608,7 +609,7 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, {
|
climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
ATTR_TEMPERATURE: temp
|
ATTR_TEMPERATURE: temp
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
elif command == COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE:
|
elif command == COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE:
|
||||||
temp_high = temp_util.convert(
|
temp_high = temp_util.convert(
|
||||||
@ -640,7 +641,7 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
climate.ATTR_TARGET_TEMP_HIGH: temp_high,
|
climate.ATTR_TARGET_TEMP_HIGH: temp_high,
|
||||||
climate.ATTR_TARGET_TEMP_LOW: temp_low,
|
climate.ATTR_TARGET_TEMP_LOW: temp_low,
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
elif command == COMMAND_THERMOSTAT_SET_MODE:
|
elif command == COMMAND_THERMOSTAT_SET_MODE:
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
@ -648,7 +649,7 @@ class TemperatureSettingTrait(_Trait):
|
|||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
climate.ATTR_OPERATION_MODE:
|
climate.ATTR_OPERATION_MODE:
|
||||||
self.google_to_hass[params['thermostatMode']],
|
self.google_to_hass[params['thermostatMode']],
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -681,7 +682,7 @@ class LockUnlockTrait(_Trait):
|
|||||||
allowed_unlock = not params['lock'] and self.config.allow_unlock
|
allowed_unlock = not params['lock'] and self.config.allow_unlock
|
||||||
return params['lock'] or allowed_unlock
|
return params['lock'] or allowed_unlock
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute an LockUnlock command."""
|
"""Execute an LockUnlock command."""
|
||||||
if params['lock']:
|
if params['lock']:
|
||||||
service = lock.SERVICE_LOCK
|
service = lock.SERVICE_LOCK
|
||||||
@ -690,7 +691,7 @@ class LockUnlockTrait(_Trait):
|
|||||||
|
|
||||||
await self.hass.services.async_call(lock.DOMAIN, service, {
|
await self.hass.services.async_call(lock.DOMAIN, service, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id
|
ATTR_ENTITY_ID: self.state.entity_id
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -760,13 +761,13 @@ class FanSpeedTrait(_Trait):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute an SetFanSpeed command."""
|
"""Execute an SetFanSpeed command."""
|
||||||
await self.hass.services.async_call(
|
await self.hass.services.async_call(
|
||||||
fan.DOMAIN, fan.SERVICE_SET_SPEED, {
|
fan.DOMAIN, fan.SERVICE_SET_SPEED, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
fan.ATTR_SPEED: params['fanSpeed']
|
fan.ATTR_SPEED: params['fanSpeed']
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
|
||||||
|
|
||||||
@register_trait
|
@register_trait
|
||||||
@ -934,7 +935,7 @@ class ModesTrait(_Trait):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def execute(self, command, params):
|
async def execute(self, command, data, params):
|
||||||
"""Execute an SetModes command."""
|
"""Execute an SetModes command."""
|
||||||
settings = params.get('updateModeSettings')
|
settings = params.get('updateModeSettings')
|
||||||
requested_source = settings.get(
|
requested_source = settings.get(
|
||||||
@ -951,4 +952,4 @@ class ModesTrait(_Trait):
|
|||||||
media_player.SERVICE_SELECT_SOURCE, {
|
media_player.SERVICE_SELECT_SOURCE, {
|
||||||
ATTR_ENTITY_ID: self.state.entity_id,
|
ATTR_ENTITY_ID: self.state.entity_id,
|
||||||
media_player.ATTR_INPUT_SOURCE: source
|
media_player.ATTR_INPUT_SOURCE: source
|
||||||
}, blocking=True)
|
}, blocking=True, context=data.context)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Test Google Smart Home."""
|
"""Test Google Smart Home."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State, EVENT_CALL_SERVICE
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS)
|
ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, TEMP_CELSIUS)
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -19,8 +19,7 @@ from tests.common import (mock_device_registry, mock_registry,
|
|||||||
|
|
||||||
BASIC_CONFIG = helpers.Config(
|
BASIC_CONFIG = helpers.Config(
|
||||||
should_expose=lambda state: True,
|
should_expose=lambda state: True,
|
||||||
allow_unlock=False,
|
allow_unlock=False
|
||||||
agent_user_id='test-agent',
|
|
||||||
)
|
)
|
||||||
REQ_ID = 'ff36a3cc-ec34-11e6-b1a0-64510650abcf'
|
REQ_ID = 'ff36a3cc-ec34-11e6-b1a0-64510650abcf'
|
||||||
|
|
||||||
@ -56,7 +55,6 @@ async def test_sync_message(hass):
|
|||||||
config = helpers.Config(
|
config = helpers.Config(
|
||||||
should_expose=lambda state: state.entity_id != 'light.not_expose',
|
should_expose=lambda state: state.entity_id != 'light.not_expose',
|
||||||
allow_unlock=False,
|
allow_unlock=False,
|
||||||
agent_user_id='test-agent',
|
|
||||||
entity_config={
|
entity_config={
|
||||||
'light.demo_light': {
|
'light.demo_light': {
|
||||||
const.CONF_ROOM_HINT: 'Living Room',
|
const.CONF_ROOM_HINT: 'Living Room',
|
||||||
@ -68,12 +66,14 @@ async def test_sync_message(hass):
|
|||||||
events = []
|
events = []
|
||||||
hass.bus.async_listen(EVENT_SYNC_RECEIVED, events.append)
|
hass.bus.async_listen(EVENT_SYNC_RECEIVED, events.append)
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, config, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, config, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.SYNC"
|
"requestId": REQ_ID,
|
||||||
}]
|
"inputs": [{
|
||||||
})
|
"intent": "action.devices.SYNC"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
'requestId': REQ_ID,
|
'requestId': REQ_ID,
|
||||||
@ -114,6 +114,7 @@ async def test_sync_message(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
async def test_sync_in_area(hass, registries):
|
async def test_sync_in_area(hass, registries):
|
||||||
"""Test a sync message where room hint comes from area."""
|
"""Test a sync message where room hint comes from area."""
|
||||||
area = registries.area.async_create("Living Room")
|
area = registries.area.async_create("Living Room")
|
||||||
@ -142,19 +143,20 @@ async def test_sync_in_area(hass, registries):
|
|||||||
config = helpers.Config(
|
config = helpers.Config(
|
||||||
should_expose=lambda _: True,
|
should_expose=lambda _: True,
|
||||||
allow_unlock=False,
|
allow_unlock=False,
|
||||||
agent_user_id='test-agent',
|
|
||||||
entity_config={}
|
entity_config={}
|
||||||
)
|
)
|
||||||
|
|
||||||
events = []
|
events = []
|
||||||
hass.bus.async_listen(EVENT_SYNC_RECEIVED, events.append)
|
hass.bus.async_listen(EVENT_SYNC_RECEIVED, events.append)
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, config, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, config, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.SYNC"
|
"requestId": REQ_ID,
|
||||||
}]
|
"inputs": [{
|
||||||
})
|
"intent": "action.devices.SYNC"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
'requestId': REQ_ID,
|
'requestId': REQ_ID,
|
||||||
@ -216,21 +218,23 @@ async def test_query_message(hass):
|
|||||||
events = []
|
events = []
|
||||||
hass.bus.async_listen(EVENT_QUERY_RECEIVED, events.append)
|
hass.bus.async_listen(EVENT_QUERY_RECEIVED, events.append)
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, BASIC_CONFIG, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.QUERY",
|
"requestId": REQ_ID,
|
||||||
"payload": {
|
"inputs": [{
|
||||||
"devices": [{
|
"intent": "action.devices.QUERY",
|
||||||
"id": "light.demo_light",
|
"payload": {
|
||||||
}, {
|
"devices": [{
|
||||||
"id": "light.another_light",
|
"id": "light.demo_light",
|
||||||
}, {
|
}, {
|
||||||
"id": "light.non_existing",
|
"id": "light.another_light",
|
||||||
}]
|
}, {
|
||||||
}
|
"id": "light.non_existing",
|
||||||
}]
|
}]
|
||||||
})
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
'requestId': REQ_ID,
|
'requestId': REQ_ID,
|
||||||
@ -280,39 +284,44 @@ async def test_execute(hass):
|
|||||||
'light': {'platform': 'demo'}
|
'light': {'platform': 'demo'}
|
||||||
})
|
})
|
||||||
|
|
||||||
events = []
|
|
||||||
hass.bus.async_listen(EVENT_COMMAND_RECEIVED, events.append)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
'light', 'turn_off', {'entity_id': 'light.ceiling_lights'},
|
'light', 'turn_off', {'entity_id': 'light.ceiling_lights'},
|
||||||
blocking=True)
|
blocking=True)
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
events = []
|
||||||
"requestId": REQ_ID,
|
hass.bus.async_listen(EVENT_COMMAND_RECEIVED, events.append)
|
||||||
"inputs": [{
|
|
||||||
"intent": "action.devices.EXECUTE",
|
service_events = []
|
||||||
"payload": {
|
hass.bus.async_listen(EVENT_CALL_SERVICE, service_events.append)
|
||||||
"commands": [{
|
|
||||||
"devices": [
|
result = await sh.async_handle_message(
|
||||||
{"id": "light.non_existing"},
|
hass, BASIC_CONFIG, None,
|
||||||
{"id": "light.ceiling_lights"},
|
{
|
||||||
],
|
"requestId": REQ_ID,
|
||||||
"execution": [{
|
"inputs": [{
|
||||||
"command": "action.devices.commands.OnOff",
|
"intent": "action.devices.EXECUTE",
|
||||||
"params": {
|
"payload": {
|
||||||
"on": True
|
"commands": [{
|
||||||
}
|
"devices": [
|
||||||
}, {
|
{"id": "light.non_existing"},
|
||||||
"command":
|
{"id": "light.ceiling_lights"},
|
||||||
"action.devices.commands.BrightnessAbsolute",
|
],
|
||||||
"params": {
|
"execution": [{
|
||||||
"brightness": 20
|
"command": "action.devices.commands.OnOff",
|
||||||
}
|
"params": {
|
||||||
|
"on": True
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"command":
|
||||||
|
"action.devices.commands.BrightnessAbsolute",
|
||||||
|
"params": {
|
||||||
|
"brightness": 20
|
||||||
|
}
|
||||||
|
}]
|
||||||
}]
|
}]
|
||||||
}]
|
}
|
||||||
}
|
}]
|
||||||
}]
|
})
|
||||||
})
|
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
"requestId": REQ_ID,
|
"requestId": REQ_ID,
|
||||||
@ -383,6 +392,24 @@ async def test_execute(hass):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert len(service_events) == 2
|
||||||
|
assert service_events[0].data == {
|
||||||
|
'domain': 'light',
|
||||||
|
'service': 'turn_on',
|
||||||
|
'service_data': {'entity_id': 'light.ceiling_lights'}
|
||||||
|
}
|
||||||
|
assert service_events[0].context == events[2].context
|
||||||
|
assert service_events[1].data == {
|
||||||
|
'domain': 'light',
|
||||||
|
'service': 'turn_on',
|
||||||
|
'service_data': {
|
||||||
|
'brightness_pct': 20,
|
||||||
|
'entity_id': 'light.ceiling_lights'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert service_events[1].context == events[2].context
|
||||||
|
assert service_events[1].context == events[3].context
|
||||||
|
|
||||||
|
|
||||||
async def test_raising_error_trait(hass):
|
async def test_raising_error_trait(hass):
|
||||||
"""Test raising an error while executing a trait command."""
|
"""Test raising an error while executing a trait command."""
|
||||||
@ -397,26 +424,28 @@ async def test_raising_error_trait(hass):
|
|||||||
hass.bus.async_listen(EVENT_COMMAND_RECEIVED, events.append)
|
hass.bus.async_listen(EVENT_COMMAND_RECEIVED, events.append)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, BASIC_CONFIG, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.EXECUTE",
|
"requestId": REQ_ID,
|
||||||
"payload": {
|
"inputs": [{
|
||||||
"commands": [{
|
"intent": "action.devices.EXECUTE",
|
||||||
"devices": [
|
"payload": {
|
||||||
{"id": "climate.bla"},
|
"commands": [{
|
||||||
],
|
"devices": [
|
||||||
"execution": [{
|
{"id": "climate.bla"},
|
||||||
"command": "action.devices.commands."
|
],
|
||||||
"ThermostatTemperatureSetpoint",
|
"execution": [{
|
||||||
"params": {
|
"command": "action.devices.commands."
|
||||||
"thermostatTemperatureSetpoint": 10
|
"ThermostatTemperatureSetpoint",
|
||||||
}
|
"params": {
|
||||||
|
"thermostatTemperatureSetpoint": 10
|
||||||
|
}
|
||||||
|
}]
|
||||||
}]
|
}]
|
||||||
}]
|
}
|
||||||
}
|
}]
|
||||||
}]
|
})
|
||||||
})
|
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
"requestId": REQ_ID,
|
"requestId": REQ_ID,
|
||||||
@ -446,6 +475,7 @@ async def test_raising_error_trait(hass):
|
|||||||
async def test_serialize_input_boolean(hass):
|
async def test_serialize_input_boolean(hass):
|
||||||
"""Test serializing an input boolean entity."""
|
"""Test serializing an input boolean entity."""
|
||||||
state = State('input_boolean.bla', 'on')
|
state = State('input_boolean.bla', 'on')
|
||||||
|
# pylint: disable=protected-access
|
||||||
entity = sh._GoogleEntity(hass, BASIC_CONFIG, state)
|
entity = sh._GoogleEntity(hass, BASIC_CONFIG, state)
|
||||||
result = await entity.sync_serialize()
|
result = await entity.sync_serialize()
|
||||||
assert result == {
|
assert result == {
|
||||||
@ -466,15 +496,17 @@ async def test_unavailable_state_doesnt_sync(hass):
|
|||||||
)
|
)
|
||||||
light.hass = hass
|
light.hass = hass
|
||||||
light.entity_id = 'light.demo_light'
|
light.entity_id = 'light.demo_light'
|
||||||
light._available = False
|
light._available = False # pylint: disable=protected-access
|
||||||
await light.async_update_ha_state()
|
await light.async_update_ha_state()
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, BASIC_CONFIG, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.SYNC"
|
"requestId": REQ_ID,
|
||||||
}]
|
"inputs": [{
|
||||||
})
|
"intent": "action.devices.SYNC"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
'requestId': REQ_ID,
|
'requestId': REQ_ID,
|
||||||
@ -495,12 +527,14 @@ async def test_empty_name_doesnt_sync(hass):
|
|||||||
light.entity_id = 'light.demo_light'
|
light.entity_id = 'light.demo_light'
|
||||||
await light.async_update_ha_state()
|
await light.async_update_ha_state()
|
||||||
|
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
result = await sh.async_handle_message(
|
||||||
"requestId": REQ_ID,
|
hass, BASIC_CONFIG, 'test-agent',
|
||||||
"inputs": [{
|
{
|
||||||
"intent": "action.devices.SYNC"
|
"requestId": REQ_ID,
|
||||||
}]
|
"inputs": [{
|
||||||
})
|
"intent": "action.devices.SYNC"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
assert result == {
|
assert result == {
|
||||||
'requestId': REQ_ID,
|
'requestId': REQ_ID,
|
||||||
@ -513,11 +547,13 @@ async def test_empty_name_doesnt_sync(hass):
|
|||||||
|
|
||||||
async def test_query_disconnect(hass):
|
async def test_query_disconnect(hass):
|
||||||
"""Test a disconnect message."""
|
"""Test a disconnect message."""
|
||||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
result = await sh.async_handle_message(
|
||||||
'inputs': [
|
hass, BASIC_CONFIG, 'test-agent',
|
||||||
{'intent': 'action.devices.DISCONNECT'}
|
{
|
||||||
],
|
'inputs': [
|
||||||
'requestId': REQ_ID
|
{'intent': 'action.devices.DISCONNECT'}
|
||||||
})
|
],
|
||||||
|
'requestId': REQ_ID
|
||||||
|
})
|
||||||
|
|
||||||
assert result is None
|
assert result is None
|
||||||
|
@ -19,19 +19,25 @@ from homeassistant.components.google_assistant import trait, helpers, const
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
STATE_ON, STATE_OFF, ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||||
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE)
|
TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE)
|
||||||
from homeassistant.core import State, DOMAIN as HA_DOMAIN
|
from homeassistant.core import State, DOMAIN as HA_DOMAIN, EVENT_CALL_SERVICE
|
||||||
from homeassistant.util import color
|
from homeassistant.util import color
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
|
|
||||||
BASIC_CONFIG = helpers.Config(
|
BASIC_CONFIG = helpers.Config(
|
||||||
should_expose=lambda state: True,
|
should_expose=lambda state: True,
|
||||||
allow_unlock=False,
|
allow_unlock=False
|
||||||
agent_user_id='test-agent',
|
)
|
||||||
|
|
||||||
|
REQ_ID = 'ff36a3cc-ec34-11e6-b1a0-64510650abcf'
|
||||||
|
|
||||||
|
BASIC_DATA = helpers.RequestData(
|
||||||
|
BASIC_CONFIG,
|
||||||
|
'test-agent',
|
||||||
|
REQ_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
UNSAFE_CONFIG = helpers.Config(
|
UNSAFE_CONFIG = helpers.Config(
|
||||||
should_expose=lambda state: True,
|
should_expose=lambda state: True,
|
||||||
agent_user_id='test-agent',
|
|
||||||
allow_unlock=True,
|
allow_unlock=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,16 +57,28 @@ async def test_brightness_light(hass):
|
|||||||
'brightness': 95
|
'brightness': 95
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events = []
|
||||||
|
hass.bus.async_listen(EVENT_CALL_SERVICE, events.append)
|
||||||
|
|
||||||
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON)
|
||||||
await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, {
|
await trt.execute(
|
||||||
'brightness': 50
|
trait.COMMAND_BRIGHTNESS_ABSOLUTE, BASIC_DATA,
|
||||||
})
|
{'brightness': 50})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'light.bla',
|
ATTR_ENTITY_ID: 'light.bla',
|
||||||
light.ATTR_BRIGHTNESS_PCT: 50
|
light.ATTR_BRIGHTNESS_PCT: 50
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
assert events[0].data == {
|
||||||
|
'domain': 'light',
|
||||||
|
'service': 'turn_on',
|
||||||
|
'service_data': {'brightness_pct': 50, 'entity_id': 'light.bla'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_brightness_cover(hass):
|
async def test_brightness_cover(hass):
|
||||||
"""Test brightness trait support for cover domain."""
|
"""Test brightness trait support for cover domain."""
|
||||||
@ -79,9 +97,9 @@ async def test_brightness_cover(hass):
|
|||||||
|
|
||||||
calls = async_mock_service(
|
calls = async_mock_service(
|
||||||
hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION)
|
hass, cover.DOMAIN, cover.SERVICE_SET_COVER_POSITION)
|
||||||
await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, {
|
await trt.execute(
|
||||||
'brightness': 50
|
trait.COMMAND_BRIGHTNESS_ABSOLUTE, BASIC_DATA,
|
||||||
})
|
{'brightness': 50})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'cover.bla',
|
ATTR_ENTITY_ID: 'cover.bla',
|
||||||
@ -107,9 +125,9 @@ async def test_brightness_media_player(hass):
|
|||||||
|
|
||||||
calls = async_mock_service(
|
calls = async_mock_service(
|
||||||
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET)
|
hass, media_player.DOMAIN, media_player.SERVICE_VOLUME_SET)
|
||||||
await trt.execute(trait.COMMAND_BRIGHTNESS_ABSOLUTE, {
|
await trt.execute(
|
||||||
'brightness': 60
|
trait.COMMAND_BRIGHTNESS_ABSOLUTE, BASIC_DATA,
|
||||||
})
|
{'brightness': 60})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'media_player.bla',
|
ATTR_ENTITY_ID: 'media_player.bla',
|
||||||
@ -137,18 +155,18 @@ async def test_onoff_group(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'group.bla',
|
ATTR_ENTITY_ID: 'group.bla',
|
||||||
}
|
}
|
||||||
|
|
||||||
off_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_OFF)
|
off_calls = async_mock_service(hass, HA_DOMAIN, SERVICE_TURN_OFF)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'group.bla',
|
ATTR_ENTITY_ID: 'group.bla',
|
||||||
@ -176,9 +194,9 @@ async def test_onoff_input_boolean(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, input_boolean.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, input_boolean.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'input_boolean.bla',
|
ATTR_ENTITY_ID: 'input_boolean.bla',
|
||||||
@ -186,9 +204,9 @@ async def test_onoff_input_boolean(hass):
|
|||||||
|
|
||||||
off_calls = async_mock_service(hass, input_boolean.DOMAIN,
|
off_calls = async_mock_service(hass, input_boolean.DOMAIN,
|
||||||
SERVICE_TURN_OFF)
|
SERVICE_TURN_OFF)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'input_boolean.bla',
|
ATTR_ENTITY_ID: 'input_boolean.bla',
|
||||||
@ -216,18 +234,18 @@ async def test_onoff_switch(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'switch.bla',
|
ATTR_ENTITY_ID: 'switch.bla',
|
||||||
}
|
}
|
||||||
|
|
||||||
off_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_OFF)
|
off_calls = async_mock_service(hass, switch.DOMAIN, SERVICE_TURN_OFF)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'switch.bla',
|
ATTR_ENTITY_ID: 'switch.bla',
|
||||||
@ -252,18 +270,18 @@ async def test_onoff_fan(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'fan.bla',
|
ATTR_ENTITY_ID: 'fan.bla',
|
||||||
}
|
}
|
||||||
|
|
||||||
off_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_OFF)
|
off_calls = async_mock_service(hass, fan.DOMAIN, SERVICE_TURN_OFF)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'fan.bla',
|
ATTR_ENTITY_ID: 'fan.bla',
|
||||||
@ -290,18 +308,18 @@ async def test_onoff_light(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'light.bla',
|
ATTR_ENTITY_ID: 'light.bla',
|
||||||
}
|
}
|
||||||
|
|
||||||
off_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_OFF)
|
off_calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_OFF)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'light.bla',
|
ATTR_ENTITY_ID: 'light.bla',
|
||||||
@ -329,9 +347,9 @@ async def test_onoff_cover(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER)
|
on_calls = async_mock_service(hass, cover.DOMAIN, cover.SERVICE_OPEN_COVER)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'cover.bla',
|
ATTR_ENTITY_ID: 'cover.bla',
|
||||||
@ -339,9 +357,9 @@ async def test_onoff_cover(hass):
|
|||||||
|
|
||||||
off_calls = async_mock_service(hass, cover.DOMAIN,
|
off_calls = async_mock_service(hass, cover.DOMAIN,
|
||||||
cover.SERVICE_CLOSE_COVER)
|
cover.SERVICE_CLOSE_COVER)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'cover.bla',
|
ATTR_ENTITY_ID: 'cover.bla',
|
||||||
@ -369,9 +387,9 @@ async def test_onoff_media_player(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, media_player.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, media_player.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'media_player.bla',
|
ATTR_ENTITY_ID: 'media_player.bla',
|
||||||
@ -380,9 +398,9 @@ async def test_onoff_media_player(hass):
|
|||||||
off_calls = async_mock_service(hass, media_player.DOMAIN,
|
off_calls = async_mock_service(hass, media_player.DOMAIN,
|
||||||
SERVICE_TURN_OFF)
|
SERVICE_TURN_OFF)
|
||||||
|
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'media_player.bla',
|
ATTR_ENTITY_ID: 'media_player.bla',
|
||||||
@ -410,9 +428,9 @@ async def test_onoff_climate(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_calls = async_mock_service(hass, climate.DOMAIN, SERVICE_TURN_ON)
|
on_calls = async_mock_service(hass, climate.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': True
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': True})
|
||||||
assert len(on_calls) == 1
|
assert len(on_calls) == 1
|
||||||
assert on_calls[0].data == {
|
assert on_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'climate.bla',
|
ATTR_ENTITY_ID: 'climate.bla',
|
||||||
@ -421,9 +439,9 @@ async def test_onoff_climate(hass):
|
|||||||
off_calls = async_mock_service(hass, climate.DOMAIN,
|
off_calls = async_mock_service(hass, climate.DOMAIN,
|
||||||
SERVICE_TURN_OFF)
|
SERVICE_TURN_OFF)
|
||||||
|
|
||||||
await trt_on.execute(trait.COMMAND_ONOFF, {
|
await trt_on.execute(
|
||||||
'on': False
|
trait.COMMAND_ONOFF, BASIC_DATA,
|
||||||
})
|
{'on': False})
|
||||||
assert len(off_calls) == 1
|
assert len(off_calls) == 1
|
||||||
assert off_calls[0].data == {
|
assert off_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'climate.bla',
|
ATTR_ENTITY_ID: 'climate.bla',
|
||||||
@ -445,7 +463,8 @@ async def test_dock_vacuum(hass):
|
|||||||
|
|
||||||
calls = async_mock_service(hass, vacuum.DOMAIN,
|
calls = async_mock_service(hass, vacuum.DOMAIN,
|
||||||
vacuum.SERVICE_RETURN_TO_BASE)
|
vacuum.SERVICE_RETURN_TO_BASE)
|
||||||
await trt.execute(trait.COMMAND_DOCK, {})
|
await trt.execute(
|
||||||
|
trait.COMMAND_DOCK, BASIC_DATA, {})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'vacuum.bla',
|
ATTR_ENTITY_ID: 'vacuum.bla',
|
||||||
@ -469,7 +488,7 @@ async def test_startstop_vacuum(hass):
|
|||||||
|
|
||||||
start_calls = async_mock_service(hass, vacuum.DOMAIN,
|
start_calls = async_mock_service(hass, vacuum.DOMAIN,
|
||||||
vacuum.SERVICE_START)
|
vacuum.SERVICE_START)
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, {'start': True})
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {'start': True})
|
||||||
assert len(start_calls) == 1
|
assert len(start_calls) == 1
|
||||||
assert start_calls[0].data == {
|
assert start_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'vacuum.bla',
|
ATTR_ENTITY_ID: 'vacuum.bla',
|
||||||
@ -477,7 +496,7 @@ async def test_startstop_vacuum(hass):
|
|||||||
|
|
||||||
stop_calls = async_mock_service(hass, vacuum.DOMAIN,
|
stop_calls = async_mock_service(hass, vacuum.DOMAIN,
|
||||||
vacuum.SERVICE_STOP)
|
vacuum.SERVICE_STOP)
|
||||||
await trt.execute(trait.COMMAND_STARTSTOP, {'start': False})
|
await trt.execute(trait.COMMAND_STARTSTOP, BASIC_DATA, {'start': False})
|
||||||
assert len(stop_calls) == 1
|
assert len(stop_calls) == 1
|
||||||
assert stop_calls[0].data == {
|
assert stop_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'vacuum.bla',
|
ATTR_ENTITY_ID: 'vacuum.bla',
|
||||||
@ -485,7 +504,7 @@ async def test_startstop_vacuum(hass):
|
|||||||
|
|
||||||
pause_calls = async_mock_service(hass, vacuum.DOMAIN,
|
pause_calls = async_mock_service(hass, vacuum.DOMAIN,
|
||||||
vacuum.SERVICE_PAUSE)
|
vacuum.SERVICE_PAUSE)
|
||||||
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, {'pause': True})
|
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, BASIC_DATA, {'pause': True})
|
||||||
assert len(pause_calls) == 1
|
assert len(pause_calls) == 1
|
||||||
assert pause_calls[0].data == {
|
assert pause_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'vacuum.bla',
|
ATTR_ENTITY_ID: 'vacuum.bla',
|
||||||
@ -493,7 +512,7 @@ async def test_startstop_vacuum(hass):
|
|||||||
|
|
||||||
unpause_calls = async_mock_service(hass, vacuum.DOMAIN,
|
unpause_calls = async_mock_service(hass, vacuum.DOMAIN,
|
||||||
vacuum.SERVICE_START)
|
vacuum.SERVICE_START)
|
||||||
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, {'pause': False})
|
await trt.execute(trait.COMMAND_PAUSEUNPAUSE, BASIC_DATA, {'pause': False})
|
||||||
assert len(unpause_calls) == 1
|
assert len(unpause_calls) == 1
|
||||||
assert unpause_calls[0].data == {
|
assert unpause_calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'vacuum.bla',
|
ATTR_ENTITY_ID: 'vacuum.bla',
|
||||||
@ -532,7 +551,7 @@ async def test_color_spectrum_light(hass):
|
|||||||
})
|
})
|
||||||
|
|
||||||
calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, {
|
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, BASIC_DATA, {
|
||||||
'color': {
|
'color': {
|
||||||
'spectrumRGB': 1052927
|
'spectrumRGB': 1052927
|
||||||
}
|
}
|
||||||
@ -581,14 +600,14 @@ async def test_color_temperature_light(hass):
|
|||||||
calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
calls = async_mock_service(hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
|
||||||
with pytest.raises(helpers.SmartHomeError) as err:
|
with pytest.raises(helpers.SmartHomeError) as err:
|
||||||
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, {
|
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, BASIC_DATA, {
|
||||||
'color': {
|
'color': {
|
||||||
'temperature': 5555
|
'temperature': 5555
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE
|
assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE
|
||||||
|
|
||||||
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, {
|
await trt.execute(trait.COMMAND_COLOR_ABSOLUTE, BASIC_DATA, {
|
||||||
'color': {
|
'color': {
|
||||||
'temperature': 2857
|
'temperature': 2857
|
||||||
}
|
}
|
||||||
@ -626,7 +645,7 @@ async def test_scene_scene(hass):
|
|||||||
assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
||||||
|
|
||||||
calls = async_mock_service(hass, scene.DOMAIN, SERVICE_TURN_ON)
|
calls = async_mock_service(hass, scene.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt.execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
await trt.execute(trait.COMMAND_ACTIVATE_SCENE, BASIC_DATA, {})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'scene.bla',
|
ATTR_ENTITY_ID: 'scene.bla',
|
||||||
@ -643,7 +662,7 @@ async def test_scene_script(hass):
|
|||||||
assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
assert trt.can_execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
||||||
|
|
||||||
calls = async_mock_service(hass, script.DOMAIN, SERVICE_TURN_ON)
|
calls = async_mock_service(hass, script.DOMAIN, SERVICE_TURN_ON)
|
||||||
await trt.execute(trait.COMMAND_ACTIVATE_SCENE, {})
|
await trt.execute(trait.COMMAND_ACTIVATE_SCENE, BASIC_DATA, {})
|
||||||
|
|
||||||
# We don't wait till script execution is done.
|
# We don't wait till script execution is done.
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -695,10 +714,11 @@ async def test_temperature_setting_climate_range(hass):
|
|||||||
|
|
||||||
calls = async_mock_service(
|
calls = async_mock_service(
|
||||||
hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE)
|
hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE)
|
||||||
await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, {
|
await trt.execute(
|
||||||
'thermostatTemperatureSetpointHigh': 25,
|
trait.COMMAND_THERMOSTAT_TEMPERATURE_SET_RANGE, BASIC_DATA, {
|
||||||
'thermostatTemperatureSetpointLow': 20,
|
'thermostatTemperatureSetpointHigh': 25,
|
||||||
})
|
'thermostatTemperatureSetpointLow': 20,
|
||||||
|
})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'climate.bla',
|
ATTR_ENTITY_ID: 'climate.bla',
|
||||||
@ -708,7 +728,7 @@ async def test_temperature_setting_climate_range(hass):
|
|||||||
|
|
||||||
calls = async_mock_service(
|
calls = async_mock_service(
|
||||||
hass, climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE)
|
hass, climate.DOMAIN, climate.SERVICE_SET_OPERATION_MODE)
|
||||||
await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, {
|
await trt.execute(trait.COMMAND_THERMOSTAT_SET_MODE, BASIC_DATA, {
|
||||||
'thermostatMode': 'heatcool',
|
'thermostatMode': 'heatcool',
|
||||||
})
|
})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
@ -718,9 +738,9 @@ async def test_temperature_setting_climate_range(hass):
|
|||||||
}
|
}
|
||||||
|
|
||||||
with pytest.raises(helpers.SmartHomeError) as err:
|
with pytest.raises(helpers.SmartHomeError) as err:
|
||||||
await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {
|
await trt.execute(
|
||||||
'thermostatTemperatureSetpoint': -100,
|
trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, BASIC_DATA,
|
||||||
})
|
{'thermostatTemperatureSetpoint': -100})
|
||||||
assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE
|
assert err.value.code == const.ERR_VALUE_OUT_OF_RANGE
|
||||||
hass.config.units.temperature_unit = TEMP_CELSIUS
|
hass.config.units.temperature_unit = TEMP_CELSIUS
|
||||||
|
|
||||||
@ -762,13 +782,13 @@ async def test_temperature_setting_climate_setpoint(hass):
|
|||||||
hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE)
|
hass, climate.DOMAIN, climate.SERVICE_SET_TEMPERATURE)
|
||||||
|
|
||||||
with pytest.raises(helpers.SmartHomeError):
|
with pytest.raises(helpers.SmartHomeError):
|
||||||
await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {
|
await trt.execute(
|
||||||
'thermostatTemperatureSetpoint': -100,
|
trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, BASIC_DATA,
|
||||||
})
|
{'thermostatTemperatureSetpoint': -100})
|
||||||
|
|
||||||
await trt.execute(trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, {
|
await trt.execute(
|
||||||
'thermostatTemperatureSetpoint': 19,
|
trait.COMMAND_THERMOSTAT_TEMPERATURE_SETPOINT, BASIC_DATA,
|
||||||
})
|
{'thermostatTemperatureSetpoint': 19})
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
ATTR_ENTITY_ID: 'climate.bla',
|
ATTR_ENTITY_ID: 'climate.bla',
|
||||||
@ -793,7 +813,7 @@ async def test_lock_unlock_lock(hass):
|
|||||||
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': True})
|
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': True})
|
||||||
|
|
||||||
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_LOCK)
|
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_LOCK)
|
||||||
await trt.execute(trait.COMMAND_LOCKUNLOCK, {'lock': True})
|
await trt.execute(trait.COMMAND_LOCKUNLOCK, BASIC_DATA, {'lock': True})
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
@ -830,7 +850,7 @@ async def test_lock_unlock_unlock(hass):
|
|||||||
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': False})
|
assert trt.can_execute(trait.COMMAND_LOCKUNLOCK, {'lock': False})
|
||||||
|
|
||||||
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_UNLOCK)
|
calls = async_mock_service(hass, lock.DOMAIN, lock.SERVICE_UNLOCK)
|
||||||
await trt.execute(trait.COMMAND_LOCKUNLOCK, {'lock': False})
|
await trt.execute(trait.COMMAND_LOCKUNLOCK, BASIC_DATA, {'lock': False})
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
@ -910,7 +930,8 @@ async def test_fan_speed(hass):
|
|||||||
trait.COMMAND_FANSPEED, params={'fanSpeed': 'medium'})
|
trait.COMMAND_FANSPEED, params={'fanSpeed': 'medium'})
|
||||||
|
|
||||||
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_SPEED)
|
calls = async_mock_service(hass, fan.DOMAIN, fan.SERVICE_SET_SPEED)
|
||||||
await trt.execute(trait.COMMAND_FANSPEED, params={'fanSpeed': 'medium'})
|
await trt.execute(
|
||||||
|
trait.COMMAND_FANSPEED, BASIC_DATA, {'fanSpeed': 'medium'})
|
||||||
|
|
||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data == {
|
assert calls[0].data == {
|
||||||
@ -995,7 +1016,7 @@ async def test_modes(hass):
|
|||||||
calls = async_mock_service(
|
calls = async_mock_service(
|
||||||
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE)
|
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE)
|
||||||
await trt.execute(
|
await trt.execute(
|
||||||
trait.COMMAND_MODES, params={
|
trait.COMMAND_MODES, BASIC_DATA, {
|
||||||
'updateModeSettings': {
|
'updateModeSettings': {
|
||||||
trt.HA_TO_GOOGLE.get(media_player.ATTR_INPUT_SOURCE): 'media'
|
trt.HA_TO_GOOGLE.get(media_player.ATTR_INPUT_SOURCE): 'media'
|
||||||
}})
|
}})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user