Send only a single event per incoming Google command (#49449)

pull/49494/head
Paulus Schoutsen 2021-04-20 09:21:52 -07:00 committed by GitHub
parent 410f0e3604
commit 7e7267f822
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 133 deletions

View File

@ -1,8 +1,7 @@
"""Describe logbook events."""
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import callback
from .const import DOMAIN, EVENT_COMMAND_RECEIVED
from .const import DOMAIN, EVENT_COMMAND_RECEIVED, SOURCE_CLOUD
COMMON_COMMAND_PREFIX = "action.devices.commands."
@ -14,16 +13,18 @@ def async_describe_events(hass, async_describe_event):
@callback
def async_describe_logbook_event(event):
"""Describe a logbook event."""
entity_id = event.data[ATTR_ENTITY_ID]
state = hass.states.get(entity_id)
name = state.name if state else entity_id
commands = []
command = event.data["execution"]["command"]
for command_payload in event.data["execution"]:
command = command_payload["command"]
if command.startswith(COMMON_COMMAND_PREFIX):
command = command[len(COMMON_COMMAND_PREFIX) :]
commands.append(command)
message = f"sent command {command} for {name} (via {event.data['source']})"
message = f"sent command {', '.join(commands)}"
if event.data["source"] != SOURCE_CLOUD:
message += f" (via {event.data['source']})"
return {"name": "Google Assistant", "message": message, "entity_id": entity_id}
return {"name": "Google Assistant", "message": message}
async_describe_event(DOMAIN, EVENT_COMMAND_RECEIVED, async_describe_logbook_event)

View File

@ -116,21 +116,23 @@ async def async_devices_query(hass, data, payload):
https://developers.google.com/assistant/smarthome/develop/process-intents#QUERY
"""
devices = {}
for device in payload.get("devices", []):
devid = device["id"]
state = hass.states.get(devid)
payload_devices = payload.get("devices", [])
hass.bus.async_fire(
EVENT_QUERY_RECEIVED,
{
"request_id": data.request_id,
ATTR_ENTITY_ID: devid,
ATTR_ENTITY_ID: [device["id"] for device in payload_devices],
"source": data.source,
},
context=data.context,
)
devices = {}
for device in payload_devices:
devid = device["id"]
state = hass.states.get(devid)
if not state:
# If we can't find a state, the device is offline
devices[devid] = {"online": False}
@ -175,20 +177,20 @@ async def handle_devices_execute(hass, data, payload):
results = {}
for command in payload["commands"]:
for device, execution in product(command["devices"], command["execution"]):
entity_id = device["id"]
hass.bus.async_fire(
EVENT_COMMAND_RECEIVED,
{
"request_id": data.request_id,
ATTR_ENTITY_ID: entity_id,
"execution": execution,
ATTR_ENTITY_ID: [device["id"] for device in command["devices"]],
"execution": command["execution"],
"source": data.source,
},
context=data.context,
)
for device, execution in product(command["devices"], command["execution"]):
entity_id = device["id"]
# Happens if error occurred. Skip entity for further processing
if entity_id in results:
continue

View File

@ -32,11 +32,13 @@ async def test_humanify_command_received(hass):
EVENT_COMMAND_RECEIVED,
{
"request_id": "abcd",
ATTR_ENTITY_ID: "light.kitchen",
"execution": {
ATTR_ENTITY_ID: ["light.kitchen"],
"execution": [
{
"command": "action.devices.commands.OnOff",
"params": {"on": True},
},
}
],
"source": SOURCE_LOCAL,
},
),
@ -44,11 +46,13 @@ async def test_humanify_command_received(hass):
EVENT_COMMAND_RECEIVED,
{
"request_id": "abcd",
ATTR_ENTITY_ID: "light.non_existing",
"execution": {
ATTR_ENTITY_ID: ["light.non_existing"],
"execution": [
{
"command": "action.devices.commands.OnOff",
"params": {"on": False},
},
}
],
"source": SOURCE_CLOUD,
},
),
@ -63,10 +67,8 @@ async def test_humanify_command_received(hass):
assert event1["name"] == "Google Assistant"
assert event1["domain"] == DOMAIN
assert event1["message"] == "sent command OnOff for The Kitchen Lights (via local)"
assert event1["entity_id"] == "light.kitchen"
assert event1["message"] == "sent command OnOff (via local)"
assert event2["name"] == "Google Assistant"
assert event2["domain"] == DOMAIN
assert event2["message"] == "sent command OnOff for light.non_existing (via cloud)"
assert event2["entity_id"] == "light.non_existing"
assert event2["message"] == "sent command OnOff"

View File

@ -353,29 +353,16 @@ async def test_query_message(hass):
await hass.async_block_till_done()
assert len(events) == 4
assert len(events) == 1
assert events[0].event_type == EVENT_QUERY_RECEIVED
assert events[0].data == {
"request_id": REQ_ID,
"entity_id": "light.demo_light",
"source": "cloud",
}
assert events[1].event_type == EVENT_QUERY_RECEIVED
assert events[1].data == {
"request_id": REQ_ID,
"entity_id": "light.another_light",
"source": "cloud",
}
assert events[2].event_type == EVENT_QUERY_RECEIVED
assert events[2].data == {
"request_id": REQ_ID,
"entity_id": "light.color_temp_light",
"source": "cloud",
}
assert events[3].event_type == EVENT_QUERY_RECEIVED
assert events[3].data == {
"request_id": REQ_ID,
"entity_id": "light.non_existing",
"entity_id": [
"light.demo_light",
"light.another_light",
"light.color_temp_light",
"light.non_existing",
],
"source": "cloud",
}
@ -467,65 +454,25 @@ async def test_execute(hass):
},
}
assert len(events) == 6
assert len(events) == 1
assert events[0].event_type == EVENT_COMMAND_RECEIVED
assert events[0].data == {
"request_id": REQ_ID,
"entity_id": "light.non_existing",
"execution": {
"entity_id": [
"light.non_existing",
"light.ceiling_lights",
"light.kitchen_lights",
],
"execution": [
{
"command": "action.devices.commands.OnOff",
"params": {"on": True},
},
"source": "cloud",
}
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},
},
"source": "cloud",
}
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},
},
"source": "cloud",
}
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},
},
"source": "cloud",
}
assert events[4].event_type == EVENT_COMMAND_RECEIVED
assert events[4].data == {
"request_id": REQ_ID,
"entity_id": "light.kitchen_lights",
"execution": {
"command": "action.devices.commands.OnOff",
"params": {"on": True},
},
"source": "cloud",
}
assert events[5].event_type == EVENT_COMMAND_RECEIVED
assert events[5].data == {
"request_id": REQ_ID,
"entity_id": "light.kitchen_lights",
"execution": {
{
"command": "action.devices.commands.BrightnessAbsolute",
"params": {"brightness": 20},
},
],
"source": "cloud",
}
@ -543,9 +490,8 @@ async def test_execute(hass):
"service": "turn_on",
"service_data": {"brightness_pct": 20, "entity_id": "light.ceiling_lights"},
}
assert service_events[0].context == events[2].context
assert service_events[1].context == events[2].context
assert service_events[1].context == events[3].context
assert service_events[0].context == events[0].context
assert service_events[1].context == events[0].context
assert service_events[2].data == {
"domain": "light",
"service": "turn_on",
@ -556,9 +502,8 @@ async def test_execute(hass):
"service": "turn_on",
"service_data": {"brightness_pct": 20, "entity_id": "light.kitchen_lights"},
}
assert service_events[2].context == events[4].context
assert service_events[3].context == events[4].context
assert service_events[3].context == events[5].context
assert service_events[2].context == events[0].context
assert service_events[3].context == events[0].context
async def test_raising_error_trait(hass):
@ -618,11 +563,13 @@ async def test_raising_error_trait(hass):
assert events[0].event_type == EVENT_COMMAND_RECEIVED
assert events[0].data == {
"request_id": REQ_ID,
"entity_id": "climate.bla",
"execution": {
"entity_id": ["climate.bla"],
"execution": [
{
"command": "action.devices.commands.ThermostatTemperatureSetpoint",
"params": {"thermostatTemperatureSetpoint": 10},
},
}
],
"source": "cloud",
}