Add HassListAddItem intent (#103716)

* Add HassListAddItem intent

* Add missing list test
This commit is contained in:
Michael Hansen 2023-11-14 12:00:30 -06:00 committed by GitHub
parent 2cb4435cf0
commit be8507f870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 0 deletions

View 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

View File

@ -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"}},
)