mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 02:07:54 +00:00
Add entity matching to intent_script (#120973)
This commit is contained in:
parent
254aa8c9ea
commit
407e4f6ca2
@ -144,6 +144,12 @@ class _IntentCardData(TypedDict):
|
|||||||
class ScriptIntentHandler(intent.IntentHandler):
|
class ScriptIntentHandler(intent.IntentHandler):
|
||||||
"""Respond to an intent with a script."""
|
"""Respond to an intent with a script."""
|
||||||
|
|
||||||
|
slot_schema = {
|
||||||
|
vol.Any("name", "area", "floor"): cv.string,
|
||||||
|
vol.Optional("domain"): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
vol.Optional("device_class"): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, intent_type: str, config: ConfigType) -> None:
|
def __init__(self, intent_type: str, config: ConfigType) -> None:
|
||||||
"""Initialize the script intent handler."""
|
"""Initialize the script intent handler."""
|
||||||
self.intent_type = intent_type
|
self.intent_type = intent_type
|
||||||
@ -158,8 +164,10 @@ class ScriptIntentHandler(intent.IntentHandler):
|
|||||||
card: _IntentCardData | None = self.config.get(CONF_CARD)
|
card: _IntentCardData | None = self.config.get(CONF_CARD)
|
||||||
action: script.Script | None = self.config.get(CONF_ACTION)
|
action: script.Script | None = self.config.get(CONF_ACTION)
|
||||||
is_async_action: bool = self.config[CONF_ASYNC_ACTION]
|
is_async_action: bool = self.config[CONF_ASYNC_ACTION]
|
||||||
|
hass: HomeAssistant = intent_obj.hass
|
||||||
|
intent_slots = self.async_validate_slots(intent_obj.slots)
|
||||||
slots: dict[str, Any] = {
|
slots: dict[str, Any] = {
|
||||||
key: value["value"] for key, value in intent_obj.slots.items()
|
key: value["value"] for key, value in intent_slots.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
@ -172,6 +180,51 @@ class ScriptIntentHandler(intent.IntentHandler):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
entity_name = slots.get("name")
|
||||||
|
area_name = slots.get("area")
|
||||||
|
floor_name = slots.get("floor")
|
||||||
|
|
||||||
|
# Optional domain/device class filters.
|
||||||
|
# Convert to sets for speed.
|
||||||
|
domains: set[str] | None = None
|
||||||
|
device_classes: set[str] | None = None
|
||||||
|
|
||||||
|
if "domain" in slots:
|
||||||
|
domains = set(slots["domain"])
|
||||||
|
|
||||||
|
if "device_class" in slots:
|
||||||
|
device_classes = set(slots["device_class"])
|
||||||
|
|
||||||
|
match_constraints = intent.MatchTargetsConstraints(
|
||||||
|
name=entity_name,
|
||||||
|
area_name=area_name,
|
||||||
|
floor_name=floor_name,
|
||||||
|
domains=domains,
|
||||||
|
device_classes=device_classes,
|
||||||
|
assistant=intent_obj.assistant,
|
||||||
|
)
|
||||||
|
|
||||||
|
if match_constraints.has_constraints:
|
||||||
|
match_result = intent.async_match_targets(hass, match_constraints)
|
||||||
|
if match_result.is_match:
|
||||||
|
targets = {}
|
||||||
|
|
||||||
|
if match_result.states:
|
||||||
|
targets["entities"] = [
|
||||||
|
state.entity_id for state in match_result.states
|
||||||
|
]
|
||||||
|
|
||||||
|
if match_result.areas:
|
||||||
|
targets["areas"] = [area.id for area in match_result.areas]
|
||||||
|
|
||||||
|
if match_result.floors:
|
||||||
|
targets["floors"] = [
|
||||||
|
floor.floor_id for floor in match_result.floors
|
||||||
|
]
|
||||||
|
|
||||||
|
if targets:
|
||||||
|
slots["targets"] = targets
|
||||||
|
|
||||||
if action is not None:
|
if action is not None:
|
||||||
if is_async_action:
|
if is_async_action:
|
||||||
intent_obj.hass.async_create_task(
|
intent_obj.hass.async_create_task(
|
||||||
|
@ -6,7 +6,12 @@ from homeassistant import config as hass_config
|
|||||||
from homeassistant.components.intent_script import DOMAIN
|
from homeassistant.components.intent_script import DOMAIN
|
||||||
from homeassistant.const import SERVICE_RELOAD
|
from homeassistant.const import SERVICE_RELOAD
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import intent
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
entity_registry as er,
|
||||||
|
floor_registry as fr,
|
||||||
|
intent,
|
||||||
|
)
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import async_mock_service, get_fixture_path
|
from tests.common import async_mock_service, get_fixture_path
|
||||||
@ -197,6 +202,84 @@ async def test_intent_script_falsy_reprompt(hass: HomeAssistant) -> None:
|
|||||||
assert response.card["simple"]["content"] == "Content for Paulus"
|
assert response.card["simple"]["content"] == "Content for Paulus"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_script_targets(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
area_registry: ar.AreaRegistry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test intent scripts work."""
|
||||||
|
calls = async_mock_service(hass, "test", "service")
|
||||||
|
|
||||||
|
await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"intent_script",
|
||||||
|
{
|
||||||
|
"intent_script": {
|
||||||
|
"Targets": {
|
||||||
|
"description": "Intent to control a test service.",
|
||||||
|
"action": {
|
||||||
|
"service": "test.service",
|
||||||
|
"data_template": {
|
||||||
|
"targets": "{{ targets if targets is defined }}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"speech": {
|
||||||
|
"text": "{{ targets.entities[0] if targets is defined }}"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
floor_1 = floor_registry.async_create("first floor")
|
||||||
|
kitchen = area_registry.async_get_or_create("kitchen")
|
||||||
|
area_registry.async_update(kitchen.id, floor_id=floor_1.floor_id)
|
||||||
|
entity_registry.async_get_or_create(
|
||||||
|
"light", "demo", "1234", suggested_object_id="kitchen"
|
||||||
|
)
|
||||||
|
entity_registry.async_update_entity("light.kitchen", area_id=kitchen.id)
|
||||||
|
hass.states.async_set("light.kitchen", "off")
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
"Targets",
|
||||||
|
{"name": {"value": "kitchen"}, "domain": {"value": "light"}},
|
||||||
|
)
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["targets"] == {"entities": ["light.kitchen"]}
|
||||||
|
assert response.speech["plain"]["speech"] == "light.kitchen"
|
||||||
|
calls.clear()
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
"Targets",
|
||||||
|
{
|
||||||
|
"area": {"value": "kitchen"},
|
||||||
|
"floor": {"value": "first floor"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["targets"] == {
|
||||||
|
"entities": ["light.kitchen"],
|
||||||
|
"areas": ["kitchen"],
|
||||||
|
"floors": ["first_floor"],
|
||||||
|
}
|
||||||
|
calls.clear()
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
"Targets",
|
||||||
|
{"device_class": {"value": "door"}},
|
||||||
|
)
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["targets"] == ""
|
||||||
|
calls.clear()
|
||||||
|
|
||||||
|
|
||||||
async def test_reload(hass: HomeAssistant) -> None:
|
async def test_reload(hass: HomeAssistant) -> None:
|
||||||
"""Verify we can reload intent config."""
|
"""Verify we can reload intent config."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user