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