mirror of
https://github.com/home-assistant/core.git
synced 2025-07-04 03:47:09 +00:00
Add complete intent function for shopping list component (#128565)
* add intent * add tests * raise IntentHandleError * add check for non completed * Prefer completing non complete items * cleanup * cleanup tests * rename test Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com> * remove duplicated test * update test * complete all items * fix event * remove type def * return speech slots --------- Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
This commit is contained in:
parent
4fcebf18dc
commit
c20ad5fde1
@ -92,13 +92,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||||||
"""Mark the first item with matching `name` as completed."""
|
"""Mark the first item with matching `name` as completed."""
|
||||||
data = hass.data[DOMAIN]
|
data = hass.data[DOMAIN]
|
||||||
name = call.data[ATTR_NAME]
|
name = call.data[ATTR_NAME]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = [item for item in data.items if item["name"] == name][0]
|
await data.async_complete(name)
|
||||||
except IndexError:
|
except NoMatchingShoppingListItem:
|
||||||
_LOGGER.error("Updating of item failed: %s cannot be found", name)
|
_LOGGER.error("Completing of item failed: %s cannot be found", name)
|
||||||
else:
|
|
||||||
await data.async_update(item["id"], {"name": name, "complete": True})
|
|
||||||
|
|
||||||
async def incomplete_item_service(call: ServiceCall) -> None:
|
async def incomplete_item_service(call: ServiceCall) -> None:
|
||||||
"""Mark the first item with matching `name` as incomplete."""
|
"""Mark the first item with matching `name` as incomplete."""
|
||||||
@ -258,6 +255,30 @@ class ShoppingData:
|
|||||||
)
|
)
|
||||||
return removed
|
return removed
|
||||||
|
|
||||||
|
async def async_complete(
|
||||||
|
self, name: str, context: Context | None = None
|
||||||
|
) -> list[dict[str, JsonValueType]]:
|
||||||
|
"""Mark all shopping list items with the given name as complete."""
|
||||||
|
complete_items = [
|
||||||
|
item for item in self.items if item["name"] == name and not item["complete"]
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(complete_items) == 0:
|
||||||
|
raise NoMatchingShoppingListItem
|
||||||
|
|
||||||
|
for item in complete_items:
|
||||||
|
_LOGGER.debug("Completing %s", item)
|
||||||
|
item["complete"] = True
|
||||||
|
await self.hass.async_add_executor_job(self.save)
|
||||||
|
self._async_notify()
|
||||||
|
for item in complete_items:
|
||||||
|
self.hass.bus.async_fire(
|
||||||
|
EVENT_SHOPPING_LIST_UPDATED,
|
||||||
|
{"action": "complete", "item": item},
|
||||||
|
context=context,
|
||||||
|
)
|
||||||
|
return complete_items
|
||||||
|
|
||||||
async def async_update(
|
async def async_update(
|
||||||
self, item_id: str | None, info: dict[str, Any], context: Context | None = None
|
self, item_id: str | None, info: dict[str, Any], context: Context | None = None
|
||||||
) -> dict[str, JsonValueType]:
|
) -> dict[str, JsonValueType]:
|
||||||
|
@ -5,15 +5,17 @@ from __future__ import annotations
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import config_validation as cv, intent
|
from homeassistant.helpers import config_validation as cv, intent
|
||||||
|
|
||||||
from . import DOMAIN, EVENT_SHOPPING_LIST_UPDATED
|
from . import DOMAIN, EVENT_SHOPPING_LIST_UPDATED, NoMatchingShoppingListItem
|
||||||
|
|
||||||
INTENT_ADD_ITEM = "HassShoppingListAddItem"
|
INTENT_ADD_ITEM = "HassShoppingListAddItem"
|
||||||
|
INTENT_COMPLETE_ITEM = "HassShoppingListCompleteItem"
|
||||||
INTENT_LAST_ITEMS = "HassShoppingListLastItems"
|
INTENT_LAST_ITEMS = "HassShoppingListLastItems"
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_intents(hass: HomeAssistant) -> None:
|
async def async_setup_intents(hass: HomeAssistant) -> None:
|
||||||
"""Set up the Shopping List intents."""
|
"""Set up the Shopping List intents."""
|
||||||
intent.async_register(hass, AddItemIntent())
|
intent.async_register(hass, AddItemIntent())
|
||||||
|
intent.async_register(hass, CompleteItemIntent())
|
||||||
intent.async_register(hass, ListTopItemsIntent())
|
intent.async_register(hass, ListTopItemsIntent())
|
||||||
|
|
||||||
|
|
||||||
@ -36,6 +38,33 @@ class AddItemIntent(intent.IntentHandler):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class CompleteItemIntent(intent.IntentHandler):
|
||||||
|
"""Handle CompleteItem intents."""
|
||||||
|
|
||||||
|
intent_type = INTENT_COMPLETE_ITEM
|
||||||
|
description = "Marks an item as completed on the shopping list"
|
||||||
|
slot_schema = {"item": cv.string}
|
||||||
|
platforms = {DOMAIN}
|
||||||
|
|
||||||
|
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||||
|
"""Handle the intent."""
|
||||||
|
slots = self.async_validate_slots(intent_obj.slots)
|
||||||
|
item = slots["item"]["value"].strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
complete_items = await intent_obj.hass.data[DOMAIN].async_complete(item)
|
||||||
|
except NoMatchingShoppingListItem:
|
||||||
|
complete_items = []
|
||||||
|
|
||||||
|
intent_obj.hass.bus.async_fire(EVENT_SHOPPING_LIST_UPDATED)
|
||||||
|
|
||||||
|
response = intent_obj.create_response()
|
||||||
|
response.async_set_speech_slots({"completed_items": complete_items})
|
||||||
|
response.response_type = intent.IntentResponseType.ACTION_DONE
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ListTopItemsIntent(intent.IntentHandler):
|
class ListTopItemsIntent(intent.IntentHandler):
|
||||||
"""Handle AddItem intents."""
|
"""Handle AddItem intents."""
|
||||||
|
|
||||||
@ -47,7 +76,7 @@ class ListTopItemsIntent(intent.IntentHandler):
|
|||||||
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||||
"""Handle the intent."""
|
"""Handle the intent."""
|
||||||
items = intent_obj.hass.data[DOMAIN].items[-5:]
|
items = intent_obj.hass.data[DOMAIN].items[-5:]
|
||||||
response = intent_obj.create_response()
|
response: intent.IntentResponse = intent_obj.create_response()
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
response.async_set_speech("There are no items on your shopping list")
|
response.async_set_speech("There are no items on your shopping list")
|
||||||
|
@ -4,6 +4,52 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers import intent
|
from homeassistant.helpers import intent
|
||||||
|
|
||||||
|
|
||||||
|
async def test_complete_item_intent(hass: HomeAssistant, sl_setup) -> None:
|
||||||
|
"""Test complete item."""
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "soda"}}
|
||||||
|
)
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "wine"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListCompleteItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
completed_items = response.speech_slots.get("completed_items")
|
||||||
|
assert len(completed_items) == 2
|
||||||
|
assert completed_items[0]["name"] == "beer"
|
||||||
|
assert hass.data["shopping_list"].items[1]["complete"]
|
||||||
|
assert hass.data["shopping_list"].items[2]["complete"]
|
||||||
|
|
||||||
|
# Complete again
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListCompleteItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert response.speech_slots.get("completed_items") == []
|
||||||
|
assert hass.data["shopping_list"].items[1]["complete"]
|
||||||
|
assert hass.data["shopping_list"].items[2]["complete"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_complete_item_intent_not_found(hass: HomeAssistant, sl_setup) -> None:
|
||||||
|
"""Test completing a missing item."""
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListCompleteItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert response.speech_slots.get("completed_items") == []
|
||||||
|
|
||||||
|
|
||||||
async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None:
|
async def test_recent_items_intent(hass: HomeAssistant, sl_setup) -> None:
|
||||||
"""Test recent items."""
|
"""Test recent items."""
|
||||||
await intent.async_handle(
|
await intent.async_handle(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user