core/homeassistant/components/conversation/trigger.py

90 lines
2.7 KiB
Python

"""Offer sentence based automation rules."""
from __future__ import annotations
from typing import Any
from hassil.recognize import PUNCTUATION, RecognizeResult
import voluptuous as vol
from homeassistant.const import CONF_COMMAND, CONF_PLATFORM
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType
from . import HOME_ASSISTANT_AGENT, _get_agent_manager
from .const import DOMAIN
from .default_agent import DefaultAgent
def has_no_punctuation(value: list[str]) -> list[str]:
"""Validate result does not contain punctuation."""
for sentence in value:
if PUNCTUATION.search(sentence):
raise vol.Invalid("sentence should not contain punctuation")
return value
TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_PLATFORM): DOMAIN,
vol.Required(CONF_COMMAND): vol.All(
cv.ensure_list, [cv.string], has_no_punctuation
),
}
)
async def async_attach_trigger(
hass: HomeAssistant,
config: ConfigType,
action: TriggerActionType,
trigger_info: TriggerInfo,
) -> CALLBACK_TYPE:
"""Listen for events based on configuration."""
trigger_data = trigger_info["trigger_data"]
sentences = config.get(CONF_COMMAND, [])
job = HassJob(action)
@callback
async def call_action(sentence: str, result: RecognizeResult) -> str | None:
"""Call action with right context."""
# Add slot values as extra trigger data
details = {
entity_name: {
"name": entity_name,
"text": entity.text.strip(), # remove whitespace
"value": entity.value.strip()
if isinstance(entity.value, str)
else entity.value,
}
for entity_name, entity in result.entities.items()
}
trigger_input: dict[str, Any] = { # Satisfy type checker
**trigger_data,
"platform": DOMAIN,
"sentence": sentence,
"details": details,
"slots": { # direct access to values
entity_name: entity["value"] for entity_name, entity in details.items()
},
}
# Wait for the automation to complete
if future := hass.async_run_hass_job(
job,
{"trigger": trigger_input},
):
await future
return "Done"
default_agent = await _get_agent_manager(hass).async_get_agent(HOME_ASSISTANT_AGENT)
assert isinstance(default_agent, DefaultAgent)
return default_agent.register_trigger(sentences, call_action)