1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-24 12:59:34 +00:00

Add new intents for cover, valve, vacuum, and media player (#110757)

* Add valve to HassTurnOn/Off

* Add set position for valves

* Add set position to covers

* Add HassTurnOn/Off for vacuums

* Add media player intents

* Split out vacuum intents

* Address comments

* Extra test
This commit is contained in:
Michael Hansen
2024-02-19 21:28:42 -06:00
committed by GitHub
parent 015f9cdb35
commit ec4bd9a421
11 changed files with 510 additions and 28 deletions

View File

@@ -7,6 +7,7 @@ from collections.abc import Collection, Coroutine, Iterable
import dataclasses
from dataclasses import dataclass
from enum import Enum
from functools import cached_property
import logging
from typing import Any, TypeVar
@@ -33,6 +34,7 @@ INTENT_TURN_ON = "HassTurnOn"
INTENT_TOGGLE = "HassToggle"
INTENT_GET_STATE = "HassGetState"
INTENT_NEVERMIND = "HassNevermind"
INTENT_SET_POSITION = "HassSetPosition"
SLOT_SCHEMA = vol.Schema({}, extra=vol.ALLOW_EXTRA)
@@ -347,7 +349,6 @@ class IntentHandler:
intent_type: str | None = None
slot_schema: vol.Schema | None = None
_slot_schema: vol.Schema | None = None
platforms: Iterable[str] | None = []
@callback
@@ -361,17 +362,20 @@ class IntentHandler:
if self.slot_schema is None:
return slots
if self._slot_schema is None:
self._slot_schema = vol.Schema(
{
key: SLOT_SCHEMA.extend({"value": validator})
for key, validator in self.slot_schema.items()
},
extra=vol.ALLOW_EXTRA,
)
return self._slot_schema(slots) # type: ignore[no-any-return]
@cached_property
def _slot_schema(self) -> vol.Schema:
"""Create validation schema for slots."""
assert self.slot_schema is not None
return vol.Schema(
{
key: SLOT_SCHEMA.extend({"value": validator})
for key, validator in self.slot_schema.items()
},
extra=vol.ALLOW_EXTRA,
)
async def async_handle(self, intent_obj: Intent) -> IntentResponse:
"""Handle the intent."""
raise NotImplementedError()
@@ -398,13 +402,44 @@ class ServiceIntentHandler(IntentHandler):
service_timeout: float = 0.2
def __init__(
self, intent_type: str, domain: str, service: str, speech: str | None = None
self,
intent_type: str,
domain: str,
service: str,
speech: str | None = None,
extra_slots: dict[str, vol.Schema] | None = None,
) -> None:
"""Create Service Intent Handler."""
self.intent_type = intent_type
self.domain = domain
self.service = service
self.speech = speech
self.extra_slots = extra_slots
@cached_property
def _slot_schema(self) -> vol.Schema:
"""Create validation schema for slots (with extra required slots)."""
if self.slot_schema is None:
raise ValueError("Slot schema is not defined")
if self.extra_slots:
slot_schema = {
**self.slot_schema,
**{
vol.Required(key): schema
for key, schema in self.extra_slots.items()
},
}
else:
slot_schema = self.slot_schema
return vol.Schema(
{
key: SLOT_SCHEMA.extend({"value": validator})
for key, validator in slot_schema.items()
},
extra=vol.ALLOW_EXTRA,
)
async def async_handle(self, intent_obj: Intent) -> IntentResponse:
"""Handle the hass intent."""
@@ -467,6 +502,9 @@ class ServiceIntentHandler(IntentHandler):
area=area_name or area_id,
)
# Update intent slots to include any transformations done by the schemas
intent_obj.slots = slots
response = await self.async_handle_states(intent_obj, states, area)
# Make the matched states available in the response
@@ -539,12 +577,19 @@ class ServiceIntentHandler(IntentHandler):
async def async_call_service(self, intent_obj: Intent, state: State) -> None:
"""Call service on entity."""
hass = intent_obj.hass
service_data: dict[str, Any] = {ATTR_ENTITY_ID: state.entity_id}
if self.extra_slots:
service_data.update(
{key: intent_obj.slots[key]["value"] for key in self.extra_slots}
)
await self._run_then_background(
hass.async_create_task(
hass.services.async_call(
self.domain,
self.service,
{ATTR_ENTITY_ID: state.entity_id},
service_data,
context=intent_obj.context,
blocking=True,
),