mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
parent
9b3a3fc1ac
commit
b87eb9d79e
@ -22,6 +22,8 @@ from .const import (
|
||||
CONF_EXPOSE, CONF_ALIASES, CONF_ROOM_HINT, CONF_ALLOW_UNLOCK,
|
||||
DEFAULT_ALLOW_UNLOCK
|
||||
)
|
||||
from .const import EVENT_COMMAND_RECEIVED, EVENT_SYNC_RECEIVED # noqa: F401
|
||||
from .const import EVENT_QUERY_RECEIVED # noqa: F401
|
||||
from .http import async_register_http
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -42,3 +42,8 @@ ERR_NOT_SUPPORTED = "notSupported"
|
||||
ERR_PROTOCOL_ERROR = 'protocolError'
|
||||
ERR_UNKNOWN_ERROR = 'unknownError'
|
||||
ERR_FUNCTION_NOT_SUPPORTED = 'functionNotSupported'
|
||||
|
||||
# Event types
|
||||
EVENT_COMMAND_RECEIVED = 'google_assistant_command_received'
|
||||
EVENT_QUERY_RECEIVED = 'google_assistant_query_received'
|
||||
EVENT_SYNC_RECEIVED = 'google_assistant_sync_received'
|
||||
|
@ -8,7 +8,7 @@ from homeassistant.util.decorator import Registry
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import (
|
||||
CLOUD_NEVER_EXPOSED_ENTITIES, CONF_NAME, STATE_UNAVAILABLE,
|
||||
ATTR_SUPPORTED_FEATURES
|
||||
ATTR_SUPPORTED_FEATURES, ATTR_ENTITY_ID,
|
||||
)
|
||||
from homeassistant.components import (
|
||||
climate,
|
||||
@ -32,7 +32,8 @@ from .const import (
|
||||
TYPE_THERMOSTAT, TYPE_FAN,
|
||||
CONF_ALIASES, CONF_ROOM_HINT,
|
||||
ERR_FUNCTION_NOT_SUPPORTED, ERR_PROTOCOL_ERROR, ERR_DEVICE_OFFLINE,
|
||||
ERR_UNKNOWN_ERROR
|
||||
ERR_UNKNOWN_ERROR,
|
||||
EVENT_COMMAND_RECEIVED, EVENT_SYNC_RECEIVED, EVENT_QUERY_RECEIVED
|
||||
)
|
||||
from .helpers import SmartHomeError
|
||||
|
||||
@ -214,7 +215,8 @@ async def _process(hass, config, message):
|
||||
}
|
||||
|
||||
try:
|
||||
result = await handler(hass, config, inputs[0].get('payload'))
|
||||
result = await handler(hass, config, request_id,
|
||||
inputs[0].get('payload'))
|
||||
except SmartHomeError as err:
|
||||
return {
|
||||
'requestId': request_id,
|
||||
@ -233,11 +235,15 @@ async def _process(hass, config, message):
|
||||
|
||||
|
||||
@HANDLERS.register('action.devices.SYNC')
|
||||
async def async_devices_sync(hass, config, payload):
|
||||
async def async_devices_sync(hass, config, request_id, 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
|
||||
})
|
||||
|
||||
devices = []
|
||||
for state in hass.states.async_all():
|
||||
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
|
||||
@ -255,14 +261,16 @@ async def async_devices_sync(hass, config, payload):
|
||||
|
||||
devices.append(serialized)
|
||||
|
||||
return {
|
||||
response = {
|
||||
'agentUserId': config.agent_user_id,
|
||||
'devices': devices,
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@HANDLERS.register('action.devices.QUERY')
|
||||
async def async_devices_query(hass, config, payload):
|
||||
async def async_devices_query(hass, config, request_id, payload):
|
||||
"""Handle action.devices.QUERY request.
|
||||
|
||||
https://developers.google.com/actions/smarthome/create-app#actiondevicesquery
|
||||
@ -272,6 +280,11 @@ async def async_devices_query(hass, config, payload):
|
||||
devid = device['id']
|
||||
state = hass.states.get(devid)
|
||||
|
||||
hass.bus.async_fire(EVENT_QUERY_RECEIVED, {
|
||||
'request_id': request_id,
|
||||
ATTR_ENTITY_ID: devid,
|
||||
})
|
||||
|
||||
if not state:
|
||||
# If we can't find a state, the device is offline
|
||||
devices[devid] = {'online': False}
|
||||
@ -283,7 +296,7 @@ async def async_devices_query(hass, config, payload):
|
||||
|
||||
|
||||
@HANDLERS.register('action.devices.EXECUTE')
|
||||
async def handle_devices_execute(hass, config, payload):
|
||||
async def handle_devices_execute(hass, config, request_id, payload):
|
||||
"""Handle action.devices.EXECUTE request.
|
||||
|
||||
https://developers.google.com/actions/smarthome/create-app#actiondevicesexecute
|
||||
@ -296,6 +309,12 @@ async def handle_devices_execute(hass, config, payload):
|
||||
command['execution']):
|
||||
entity_id = device['id']
|
||||
|
||||
hass.bus.async_fire(EVENT_COMMAND_RECEIVED, {
|
||||
'request_id': request_id,
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
'execution': execution
|
||||
})
|
||||
|
||||
# Happens if error occurred. Skip entity for further processing
|
||||
if entity_id in results:
|
||||
continue
|
||||
@ -341,7 +360,7 @@ async def handle_devices_execute(hass, config, payload):
|
||||
|
||||
|
||||
@HANDLERS.register('action.devices.DISCONNECT')
|
||||
async def async_devices_disconnect(hass, config, payload):
|
||||
async def async_devices_disconnect(hass, config, request_id, payload):
|
||||
"""Handle action.devices.DISCONNECT request.
|
||||
|
||||
https://developers.google.com/actions/smarthome/create#actiondevicesdisconnect
|
||||
|
@ -7,7 +7,8 @@ from homeassistant.components.climate.const import (
|
||||
ATTR_MIN_TEMP, ATTR_MAX_TEMP, STATE_HEAT, SUPPORT_OPERATION_MODE
|
||||
)
|
||||
from homeassistant.components.google_assistant import (
|
||||
const, trait, helpers, smart_home as sh)
|
||||
const, trait, helpers, smart_home as sh,
|
||||
EVENT_COMMAND_RECEIVED, EVENT_QUERY_RECEIVED, EVENT_SYNC_RECEIVED)
|
||||
from homeassistant.components.light.demo import DemoLight
|
||||
|
||||
|
||||
@ -48,6 +49,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, {
|
||||
"requestId": REQ_ID,
|
||||
"inputs": [{
|
||||
@ -85,6 +89,13 @@ async def test_sync_message(hass):
|
||||
}]
|
||||
}
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(events) == 1
|
||||
assert events[0].event_type == EVENT_SYNC_RECEIVED
|
||||
assert events[0].data == {
|
||||
'request_id': REQ_ID,
|
||||
}
|
||||
|
||||
|
||||
async def test_query_message(hass):
|
||||
@ -109,6 +120,9 @@ async def test_query_message(hass):
|
||||
light2.entity_id = 'light.another_light'
|
||||
await light2.async_update_ha_state()
|
||||
|
||||
events = []
|
||||
hass.bus.async_listen(EVENT_QUERY_RECEIVED, events.append)
|
||||
|
||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
||||
"requestId": REQ_ID,
|
||||
"inputs": [{
|
||||
@ -149,12 +163,33 @@ async def test_query_message(hass):
|
||||
}
|
||||
}
|
||||
|
||||
assert len(events) == 3
|
||||
assert events[0].event_type == EVENT_QUERY_RECEIVED
|
||||
assert events[0].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.demo_light'
|
||||
}
|
||||
assert events[1].event_type == EVENT_QUERY_RECEIVED
|
||||
assert events[1].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.another_light'
|
||||
}
|
||||
assert events[2].event_type == EVENT_QUERY_RECEIVED
|
||||
assert events[2].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.non_existing'
|
||||
}
|
||||
|
||||
|
||||
async def test_execute(hass):
|
||||
"""Test an execute command."""
|
||||
await async_setup_component(hass, 'light', {
|
||||
'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)
|
||||
@ -209,6 +244,52 @@ async def test_execute(hass):
|
||||
}
|
||||
}
|
||||
|
||||
assert len(events) == 4
|
||||
assert events[0].event_type == EVENT_COMMAND_RECEIVED
|
||||
assert events[0].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.non_existing',
|
||||
'execution': {
|
||||
'command': 'action.devices.commands.OnOff',
|
||||
'params': {
|
||||
'on': True
|
||||
}
|
||||
}
|
||||
}
|
||||
assert events[1].event_type == EVENT_COMMAND_RECEIVED
|
||||
assert events[1].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.non_existing',
|
||||
'execution': {
|
||||
'command': 'action.devices.commands.BrightnessAbsolute',
|
||||
'params': {
|
||||
'brightness': 20
|
||||
}
|
||||
}
|
||||
}
|
||||
assert events[2].event_type == EVENT_COMMAND_RECEIVED
|
||||
assert events[2].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.ceiling_lights',
|
||||
'execution': {
|
||||
'command': 'action.devices.commands.OnOff',
|
||||
'params': {
|
||||
'on': True
|
||||
}
|
||||
}
|
||||
}
|
||||
assert events[3].event_type == EVENT_COMMAND_RECEIVED
|
||||
assert events[3].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'light.ceiling_lights',
|
||||
'execution': {
|
||||
'command': 'action.devices.commands.BrightnessAbsolute',
|
||||
'params': {
|
||||
'brightness': 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def test_raising_error_trait(hass):
|
||||
"""Test raising an error while executing a trait command."""
|
||||
@ -218,6 +299,11 @@ async def test_raising_error_trait(hass):
|
||||
ATTR_SUPPORTED_FEATURES: SUPPORT_OPERATION_MODE,
|
||||
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS,
|
||||
})
|
||||
|
||||
events = []
|
||||
hass.bus.async_listen(EVENT_COMMAND_RECEIVED, events.append)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await sh.async_handle_message(hass, BASIC_CONFIG, {
|
||||
"requestId": REQ_ID,
|
||||
"inputs": [{
|
||||
@ -250,6 +336,19 @@ async def test_raising_error_trait(hass):
|
||||
}
|
||||
}
|
||||
|
||||
assert len(events) == 1
|
||||
assert events[0].event_type == EVENT_COMMAND_RECEIVED
|
||||
assert events[0].data == {
|
||||
'request_id': REQ_ID,
|
||||
'entity_id': 'climate.bla',
|
||||
'execution': {
|
||||
'command': 'action.devices.commands.ThermostatTemperatureSetpoint',
|
||||
'params': {
|
||||
'thermostatTemperatureSetpoint': 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_serialize_input_boolean():
|
||||
"""Test serializing an input boolean entity."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user