From a23f4dac6207d6872a6584fb3ef879b7fd72c493 Mon Sep 17 00:00:00 2001 From: Meow Date: Wed, 25 Aug 2021 12:26:37 +0200 Subject: [PATCH] Add service to clear completed shoppinglist items (#55032) --- .../components/shopping_list/__init__.py | 25 +++++-- .../components/shopping_list/const.py | 7 ++ .../components/shopping_list/services.yaml | 4 + tests/components/shopping_list/test_init.py | 75 ++++++++++++++++++- tests/components/shopping_list/test_intent.py | 9 +++ 5 files changed, 112 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/shopping_list/__init__.py b/homeassistant/components/shopping_list/__init__.py index 841865cd759..49b4d8a5d91 100644 --- a/homeassistant/components/shopping_list/__init__.py +++ b/homeassistant/components/shopping_list/__init__.py @@ -12,7 +12,15 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from homeassistant.util.json import load_json, save_json -from .const import DOMAIN +from .const import ( + DOMAIN, + SERVICE_ADD_ITEM, + SERVICE_CLEAR_COMPLETED_ITEMS, + SERVICE_COMPLETE_ALL, + SERVICE_COMPLETE_ITEM, + SERVICE_INCOMPLETE_ALL, + SERVICE_INCOMPLETE_ITEM, +) ATTR_COMPLETE = "complete" @@ -22,11 +30,6 @@ EVENT = "shopping_list_updated" ITEM_UPDATE_SCHEMA = vol.Schema({ATTR_COMPLETE: bool, ATTR_NAME: str}) PERSISTENCE = ".shopping_list.json" -SERVICE_ADD_ITEM = "add_item" -SERVICE_COMPLETE_ITEM = "complete_item" -SERVICE_INCOMPLETE_ITEM = "incomplete_item" -SERVICE_COMPLETE_ALL = "complete_all" -SERVICE_INCOMPLETE_ALL = "incomplete_all" SERVICE_ITEM_SCHEMA = vol.Schema({vol.Required(ATTR_NAME): vol.Any(None, cv.string)}) SERVICE_LIST_SCHEMA = vol.Schema({}) @@ -116,6 +119,10 @@ async def async_setup_entry(hass, config_entry): """Mark all items in the list as incomplete.""" await data.async_update_list({"complete": False}) + async def clear_completed_items_service(call): + """Clear all completed items from the list.""" + await data.async_clear_completed() + data = hass.data[DOMAIN] = ShoppingData(hass) await data.async_load() @@ -143,6 +150,12 @@ async def async_setup_entry(hass, config_entry): incomplete_all_service, schema=SERVICE_LIST_SCHEMA, ) + hass.services.async_register( + DOMAIN, + SERVICE_CLEAR_COMPLETED_ITEMS, + clear_completed_items_service, + schema=SERVICE_LIST_SCHEMA, + ) hass.http.register_view(ShoppingListView) hass.http.register_view(CreateShoppingListItemView) diff --git a/homeassistant/components/shopping_list/const.py b/homeassistant/components/shopping_list/const.py index 4878d317780..2969fc8f86d 100644 --- a/homeassistant/components/shopping_list/const.py +++ b/homeassistant/components/shopping_list/const.py @@ -1,2 +1,9 @@ """All constants related to the shopping list component.""" DOMAIN = "shopping_list" + +SERVICE_ADD_ITEM = "add_item" +SERVICE_COMPLETE_ITEM = "complete_item" +SERVICE_INCOMPLETE_ITEM = "incomplete_item" +SERVICE_COMPLETE_ALL = "complete_all" +SERVICE_INCOMPLETE_ALL = "incomplete_all" +SERVICE_CLEAR_COMPLETED_ITEMS = "clear_completed_items" diff --git a/homeassistant/components/shopping_list/services.yaml b/homeassistant/components/shopping_list/services.yaml index 7bf209550d7..0af388cfcb1 100644 --- a/homeassistant/components/shopping_list/services.yaml +++ b/homeassistant/components/shopping_list/services.yaml @@ -40,3 +40,7 @@ complete_all: incomplete_all: name: Incomplete all description: Marks all items as incomplete in the shopping list. + +clear_completed_items: + name: Clear completed items + description: Clear completed items from the shopping list. diff --git a/tests/components/shopping_list/test_init.py b/tests/components/shopping_list/test_init.py index 48482787f4d..65fddec894e 100644 --- a/tests/components/shopping_list/test_init.py +++ b/tests/components/shopping_list/test_init.py @@ -1,12 +1,17 @@ """Test shopping list component.""" -from homeassistant.components.shopping_list.const import DOMAIN +from homeassistant.components.shopping_list.const import ( + DOMAIN, + SERVICE_ADD_ITEM, + SERVICE_CLEAR_COMPLETED_ITEMS, + SERVICE_COMPLETE_ITEM, +) from homeassistant.components.websocket_api.const import ( ERR_INVALID_FORMAT, ERR_NOT_FOUND, TYPE_RESULT, ) -from homeassistant.const import HTTP_NOT_FOUND +from homeassistant.const import ATTR_NAME, HTTP_NOT_FOUND from homeassistant.helpers import intent @@ -53,6 +58,29 @@ async def test_update_list(hass, sl_setup): assert cheese["complete"] is False +async def test_clear_completed_items(hass, sl_setup): + """Test clear completed list items.""" + await intent.async_handle( + hass, + "test", + "HassShoppingListAddItem", + {"item": {"value": "beer"}}, + ) + + await intent.async_handle( + hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}} + ) + + assert len(hass.data[DOMAIN].items) == 2 + + # Update a single attribute, other attributes shouldn't change + await hass.data[DOMAIN].async_update_list({"complete": True}) + + await hass.data[DOMAIN].async_clear_completed() + + assert len(hass.data[DOMAIN].items) == 0 + + async def test_recent_items_intent(hass, sl_setup): """Test recent items.""" @@ -471,3 +499,46 @@ async def test_ws_reorder_items_failure(hass, hass_ws_client, sl_setup): msg = await client.receive_json() assert msg["success"] is False assert msg["error"]["code"] == ERR_INVALID_FORMAT + + +async def test_add_item_service(hass, sl_setup): + """Test adding shopping_list item service.""" + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_ITEM, + {ATTR_NAME: "beer"}, + blocking=True, + ) + await hass.async_block_till_done() + + assert len(hass.data[DOMAIN].items) == 1 + + +async def test_clear_completed_items_service(hass, sl_setup): + """Test clearing completed shopping_list items service.""" + await hass.services.async_call( + DOMAIN, + SERVICE_ADD_ITEM, + {ATTR_NAME: "beer"}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(hass.data[DOMAIN].items) == 1 + + await hass.services.async_call( + DOMAIN, + SERVICE_COMPLETE_ITEM, + {ATTR_NAME: "beer"}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(hass.data[DOMAIN].items) == 1 + + await hass.services.async_call( + DOMAIN, + SERVICE_CLEAR_COMPLETED_ITEMS, + {}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(hass.data[DOMAIN].items) == 0 diff --git a/tests/components/shopping_list/test_intent.py b/tests/components/shopping_list/test_intent.py index d0bcb1d837c..a03353e510e 100644 --- a/tests/components/shopping_list/test_intent.py +++ b/tests/components/shopping_list/test_intent.py @@ -20,3 +20,12 @@ async def test_recent_items_intent(hass, sl_setup): response.speech["plain"]["speech"] == "These are the top 3 items on your shopping list: soda, wine, beer" ) + + +async def test_recent_items_intent_no_items(hass, sl_setup): + """Test recent items.""" + response = await intent.async_handle(hass, "test", "HassShoppingListLastItems") + + assert ( + response.speech["plain"]["speech"] == "There are no items on your shopping list" + )