mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add HassListAddItem intent (#103716)
* Add HassListAddItem intent * Add missing list test
This commit is contained in:
parent
2cb4435cf0
commit
be8507f870
54
homeassistant/components/todo/intent.py
Normal file
54
homeassistant/components/todo/intent.py
Normal file
@ -0,0 +1,54 @@
|
||||
"""Intents for the todo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import intent
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
from . import DOMAIN, TodoItem, TodoListEntity
|
||||
|
||||
INTENT_LIST_ADD_ITEM = "HassListAddItem"
|
||||
|
||||
|
||||
async def async_setup_intents(hass: HomeAssistant) -> None:
|
||||
"""Set up the todo intents."""
|
||||
intent.async_register(hass, ListAddItemIntent())
|
||||
|
||||
|
||||
class ListAddItemIntent(intent.IntentHandler):
|
||||
"""Handle ListAddItem intents."""
|
||||
|
||||
intent_type = INTENT_LIST_ADD_ITEM
|
||||
slot_schema = {"item": cv.string, "name": cv.string}
|
||||
|
||||
async def async_handle(self, intent_obj: intent.Intent):
|
||||
"""Handle the intent."""
|
||||
hass = intent_obj.hass
|
||||
|
||||
slots = self.async_validate_slots(intent_obj.slots)
|
||||
item = slots["item"]["value"]
|
||||
list_name = slots["name"]["value"]
|
||||
|
||||
component: EntityComponent[TodoListEntity] = hass.data[DOMAIN]
|
||||
target_list: TodoListEntity | None = None
|
||||
|
||||
# Find matching list
|
||||
for list_state in intent.async_match_states(
|
||||
hass, name=list_name, domains=[DOMAIN]
|
||||
):
|
||||
target_list = component.get_entity(list_state.entity_id)
|
||||
if target_list is not None:
|
||||
break
|
||||
|
||||
if target_list is None:
|
||||
raise intent.IntentHandleError(f"No to-do list: {list_name}")
|
||||
|
||||
assert target_list is not None
|
||||
|
||||
# Add to list
|
||||
await target_list.async_create_todo_item(TodoItem(item))
|
||||
|
||||
response = intent_obj.create_response()
|
||||
response.response_type = intent.IntentResponseType.ACTION_DONE
|
||||
return response
|
@ -13,11 +13,13 @@ from homeassistant.components.todo import (
|
||||
TodoItemStatus,
|
||||
TodoListEntity,
|
||||
TodoListEntityFeature,
|
||||
intent as todo_intent,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState, ConfigFlow
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import intent
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from tests.common import (
|
||||
@ -37,6 +39,18 @@ class MockFlow(ConfigFlow):
|
||||
"""Test flow."""
|
||||
|
||||
|
||||
class MockTodoListEntity(TodoListEntity):
|
||||
"""Test todo list entity."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize entity."""
|
||||
self.items: list[TodoItem] = []
|
||||
|
||||
async def async_create_todo_item(self, item: TodoItem) -> None:
|
||||
"""Add an item to the To-do list."""
|
||||
self.items.append(item)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||
"""Mock config flow."""
|
||||
@ -737,3 +751,70 @@ async def test_move_item_unsupported(
|
||||
resp = await client.receive_json()
|
||||
assert resp.get("id") == 1
|
||||
assert resp.get("error", {}).get("code") == "not_supported"
|
||||
|
||||
|
||||
async def test_add_item_intent(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test adding items to lists using an intent."""
|
||||
await todo_intent.async_setup_intents(hass)
|
||||
|
||||
entity1 = MockTodoListEntity()
|
||||
entity1._attr_name = "List 1"
|
||||
entity1.entity_id = "todo.list_1"
|
||||
|
||||
entity2 = MockTodoListEntity()
|
||||
entity2._attr_name = "List 2"
|
||||
entity2.entity_id = "todo.list_2"
|
||||
|
||||
await create_mock_platform(hass, [entity1, entity2])
|
||||
|
||||
# Add to first list
|
||||
response = await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
todo_intent.INTENT_LIST_ADD_ITEM,
|
||||
{"item": {"value": "beer"}, "name": {"value": "list 1"}},
|
||||
)
|
||||
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
|
||||
assert len(entity1.items) == 1
|
||||
assert len(entity2.items) == 0
|
||||
assert entity1.items[0].summary == "beer"
|
||||
entity1.items.clear()
|
||||
|
||||
# Add to second list
|
||||
response = await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
todo_intent.INTENT_LIST_ADD_ITEM,
|
||||
{"item": {"value": "cheese"}, "name": {"value": "List 2"}},
|
||||
)
|
||||
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
|
||||
assert len(entity1.items) == 0
|
||||
assert len(entity2.items) == 1
|
||||
assert entity2.items[0].summary == "cheese"
|
||||
|
||||
# List name is case insensitive
|
||||
response = await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
todo_intent.INTENT_LIST_ADD_ITEM,
|
||||
{"item": {"value": "wine"}, "name": {"value": "lIST 2"}},
|
||||
)
|
||||
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||
|
||||
assert len(entity1.items) == 0
|
||||
assert len(entity2.items) == 2
|
||||
assert entity2.items[1].summary == "wine"
|
||||
|
||||
# Missing list
|
||||
with pytest.raises(intent.IntentHandleError):
|
||||
await intent.async_handle(
|
||||
hass,
|
||||
"test",
|
||||
todo_intent.INTENT_LIST_ADD_ITEM,
|
||||
{"item": {"value": "wine"}, "name": {"value": "This list does not exist"}},
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user