Send only a single event per incoming Google command (#49449)
parent
410f0e3604
commit
7e7267f822
|
@ -1,8 +1,7 @@
|
||||||
"""Describe logbook events."""
|
"""Describe logbook events."""
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
|
||||||
from homeassistant.core import callback
|
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."
|
COMMON_COMMAND_PREFIX = "action.devices.commands."
|
||||||
|
|
||||||
|
@ -14,16 +13,18 @@ def async_describe_events(hass, async_describe_event):
|
||||||
@callback
|
@callback
|
||||||
def async_describe_logbook_event(event):
|
def async_describe_logbook_event(event):
|
||||||
"""Describe a logbook event."""
|
"""Describe a logbook event."""
|
||||||
entity_id = event.data[ATTR_ENTITY_ID]
|
commands = []
|
||||||
state = hass.states.get(entity_id)
|
|
||||||
name = state.name if state else entity_id
|
|
||||||
|
|
||||||
command = event.data["execution"]["command"]
|
for command_payload in event.data["execution"]:
|
||||||
if command.startswith(COMMON_COMMAND_PREFIX):
|
command = command_payload["command"]
|
||||||
command = command[len(COMMON_COMMAND_PREFIX) :]
|
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)
|
async_describe_event(DOMAIN, EVENT_COMMAND_RECEIVED, async_describe_logbook_event)
|
||||||
|
|
|
@ -116,21 +116,23 @@ async def async_devices_query(hass, data, payload):
|
||||||
|
|
||||||
https://developers.google.com/assistant/smarthome/develop/process-intents#QUERY
|
https://developers.google.com/assistant/smarthome/develop/process-intents#QUERY
|
||||||
"""
|
"""
|
||||||
|
payload_devices = payload.get("devices", [])
|
||||||
|
|
||||||
|
hass.bus.async_fire(
|
||||||
|
EVENT_QUERY_RECEIVED,
|
||||||
|
{
|
||||||
|
"request_id": data.request_id,
|
||||||
|
ATTR_ENTITY_ID: [device["id"] for device in payload_devices],
|
||||||
|
"source": data.source,
|
||||||
|
},
|
||||||
|
context=data.context,
|
||||||
|
)
|
||||||
|
|
||||||
devices = {}
|
devices = {}
|
||||||
for device in payload.get("devices", []):
|
for device in payload_devices:
|
||||||
devid = device["id"]
|
devid = device["id"]
|
||||||
state = hass.states.get(devid)
|
state = hass.states.get(devid)
|
||||||
|
|
||||||
hass.bus.async_fire(
|
|
||||||
EVENT_QUERY_RECEIVED,
|
|
||||||
{
|
|
||||||
"request_id": data.request_id,
|
|
||||||
ATTR_ENTITY_ID: devid,
|
|
||||||
"source": data.source,
|
|
||||||
},
|
|
||||||
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}
|
||||||
|
@ -175,20 +177,20 @@ async def handle_devices_execute(hass, data, payload):
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
for command in payload["commands"]:
|
for command in payload["commands"]:
|
||||||
|
hass.bus.async_fire(
|
||||||
|
EVENT_COMMAND_RECEIVED,
|
||||||
|
{
|
||||||
|
"request_id": data.request_id,
|
||||||
|
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"]):
|
for device, execution in product(command["devices"], command["execution"]):
|
||||||
entity_id = device["id"]
|
entity_id = device["id"]
|
||||||
|
|
||||||
hass.bus.async_fire(
|
|
||||||
EVENT_COMMAND_RECEIVED,
|
|
||||||
{
|
|
||||||
"request_id": data.request_id,
|
|
||||||
ATTR_ENTITY_ID: entity_id,
|
|
||||||
"execution": execution,
|
|
||||||
"source": data.source,
|
|
||||||
},
|
|
||||||
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:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -32,11 +32,13 @@ async def test_humanify_command_received(hass):
|
||||||
EVENT_COMMAND_RECEIVED,
|
EVENT_COMMAND_RECEIVED,
|
||||||
{
|
{
|
||||||
"request_id": "abcd",
|
"request_id": "abcd",
|
||||||
ATTR_ENTITY_ID: "light.kitchen",
|
ATTR_ENTITY_ID: ["light.kitchen"],
|
||||||
"execution": {
|
"execution": [
|
||||||
"command": "action.devices.commands.OnOff",
|
{
|
||||||
"params": {"on": True},
|
"command": "action.devices.commands.OnOff",
|
||||||
},
|
"params": {"on": True},
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": SOURCE_LOCAL,
|
"source": SOURCE_LOCAL,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -44,11 +46,13 @@ async def test_humanify_command_received(hass):
|
||||||
EVENT_COMMAND_RECEIVED,
|
EVENT_COMMAND_RECEIVED,
|
||||||
{
|
{
|
||||||
"request_id": "abcd",
|
"request_id": "abcd",
|
||||||
ATTR_ENTITY_ID: "light.non_existing",
|
ATTR_ENTITY_ID: ["light.non_existing"],
|
||||||
"execution": {
|
"execution": [
|
||||||
"command": "action.devices.commands.OnOff",
|
{
|
||||||
"params": {"on": False},
|
"command": "action.devices.commands.OnOff",
|
||||||
},
|
"params": {"on": False},
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": SOURCE_CLOUD,
|
"source": SOURCE_CLOUD,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -63,10 +67,8 @@ async def test_humanify_command_received(hass):
|
||||||
|
|
||||||
assert event1["name"] == "Google Assistant"
|
assert event1["name"] == "Google Assistant"
|
||||||
assert event1["domain"] == DOMAIN
|
assert event1["domain"] == DOMAIN
|
||||||
assert event1["message"] == "sent command OnOff for The Kitchen Lights (via local)"
|
assert event1["message"] == "sent command OnOff (via local)"
|
||||||
assert event1["entity_id"] == "light.kitchen"
|
|
||||||
|
|
||||||
assert event2["name"] == "Google Assistant"
|
assert event2["name"] == "Google Assistant"
|
||||||
assert event2["domain"] == DOMAIN
|
assert event2["domain"] == DOMAIN
|
||||||
assert event2["message"] == "sent command OnOff for light.non_existing (via cloud)"
|
assert event2["message"] == "sent command OnOff"
|
||||||
assert event2["entity_id"] == "light.non_existing"
|
|
||||||
|
|
|
@ -353,29 +353,16 @@ async def test_query_message(hass):
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
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].event_type == EVENT_QUERY_RECEIVED
|
||||||
assert events[0].data == {
|
assert events[0].data == {
|
||||||
"request_id": REQ_ID,
|
"request_id": REQ_ID,
|
||||||
"entity_id": "light.demo_light",
|
"entity_id": [
|
||||||
"source": "cloud",
|
"light.demo_light",
|
||||||
}
|
"light.another_light",
|
||||||
assert events[1].event_type == EVENT_QUERY_RECEIVED
|
"light.color_temp_light",
|
||||||
assert events[1].data == {
|
"light.non_existing",
|
||||||
"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",
|
|
||||||
"source": "cloud",
|
"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].event_type == EVENT_COMMAND_RECEIVED
|
||||||
assert events[0].data == {
|
assert events[0].data == {
|
||||||
"request_id": REQ_ID,
|
"request_id": REQ_ID,
|
||||||
"entity_id": "light.non_existing",
|
"entity_id": [
|
||||||
"execution": {
|
"light.non_existing",
|
||||||
"command": "action.devices.commands.OnOff",
|
"light.ceiling_lights",
|
||||||
"params": {"on": True},
|
"light.kitchen_lights",
|
||||||
},
|
],
|
||||||
"source": "cloud",
|
"execution": [
|
||||||
}
|
{
|
||||||
assert events[1].event_type == EVENT_COMMAND_RECEIVED
|
"command": "action.devices.commands.OnOff",
|
||||||
assert events[1].data == {
|
"params": {"on": True},
|
||||||
"request_id": REQ_ID,
|
},
|
||||||
"entity_id": "light.non_existing",
|
{
|
||||||
"execution": {
|
"command": "action.devices.commands.BrightnessAbsolute",
|
||||||
"command": "action.devices.commands.BrightnessAbsolute",
|
"params": {"brightness": 20},
|
||||||
"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",
|
"source": "cloud",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,9 +490,8 @@ async def test_execute(hass):
|
||||||
"service": "turn_on",
|
"service": "turn_on",
|
||||||
"service_data": {"brightness_pct": 20, "entity_id": "light.ceiling_lights"},
|
"service_data": {"brightness_pct": 20, "entity_id": "light.ceiling_lights"},
|
||||||
}
|
}
|
||||||
assert service_events[0].context == events[2].context
|
assert service_events[0].context == events[0].context
|
||||||
assert service_events[1].context == events[2].context
|
assert service_events[1].context == events[0].context
|
||||||
assert service_events[1].context == events[3].context
|
|
||||||
assert service_events[2].data == {
|
assert service_events[2].data == {
|
||||||
"domain": "light",
|
"domain": "light",
|
||||||
"service": "turn_on",
|
"service": "turn_on",
|
||||||
|
@ -556,9 +502,8 @@ async def test_execute(hass):
|
||||||
"service": "turn_on",
|
"service": "turn_on",
|
||||||
"service_data": {"brightness_pct": 20, "entity_id": "light.kitchen_lights"},
|
"service_data": {"brightness_pct": 20, "entity_id": "light.kitchen_lights"},
|
||||||
}
|
}
|
||||||
assert service_events[2].context == events[4].context
|
assert service_events[2].context == events[0].context
|
||||||
assert service_events[3].context == events[4].context
|
assert service_events[3].context == events[0].context
|
||||||
assert service_events[3].context == events[5].context
|
|
||||||
|
|
||||||
|
|
||||||
async def test_raising_error_trait(hass):
|
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].event_type == EVENT_COMMAND_RECEIVED
|
||||||
assert events[0].data == {
|
assert events[0].data == {
|
||||||
"request_id": REQ_ID,
|
"request_id": REQ_ID,
|
||||||
"entity_id": "climate.bla",
|
"entity_id": ["climate.bla"],
|
||||||
"execution": {
|
"execution": [
|
||||||
"command": "action.devices.commands.ThermostatTemperatureSetpoint",
|
{
|
||||||
"params": {"thermostatTemperatureSetpoint": 10},
|
"command": "action.devices.commands.ThermostatTemperatureSetpoint",
|
||||||
},
|
"params": {"thermostatTemperatureSetpoint": 10},
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": "cloud",
|
"source": "cloud",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue