Add more shopping list services (#45591)
Co-authored-by: Franck Nijhof <git@frenck.dev>pull/46976/head
parent
d96249e39c
commit
23b2953773
|
@ -15,17 +15,21 @@ from homeassistant.util.json import load_json, save_json
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
ATTR_NAME = "name"
|
ATTR_NAME = "name"
|
||||||
|
ATTR_COMPLETE = "complete"
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA)
|
CONFIG_SCHEMA = vol.Schema({DOMAIN: {}}, extra=vol.ALLOW_EXTRA)
|
||||||
EVENT = "shopping_list_updated"
|
EVENT = "shopping_list_updated"
|
||||||
ITEM_UPDATE_SCHEMA = vol.Schema({"complete": bool, ATTR_NAME: str})
|
ITEM_UPDATE_SCHEMA = vol.Schema({ATTR_COMPLETE: bool, ATTR_NAME: str})
|
||||||
PERSISTENCE = ".shopping_list.json"
|
PERSISTENCE = ".shopping_list.json"
|
||||||
|
|
||||||
SERVICE_ADD_ITEM = "add_item"
|
SERVICE_ADD_ITEM = "add_item"
|
||||||
SERVICE_COMPLETE_ITEM = "complete_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_ITEM_SCHEMA = vol.Schema({vol.Required(ATTR_NAME): vol.Any(None, cv.string)})
|
||||||
|
SERVICE_LIST_SCHEMA = vol.Schema({})
|
||||||
|
|
||||||
WS_TYPE_SHOPPING_LIST_ITEMS = "shopping_list/items"
|
WS_TYPE_SHOPPING_LIST_ITEMS = "shopping_list/items"
|
||||||
WS_TYPE_SHOPPING_LIST_ADD_ITEM = "shopping_list/items/add"
|
WS_TYPE_SHOPPING_LIST_ADD_ITEM = "shopping_list/items/add"
|
||||||
|
@ -92,6 +96,27 @@ async def async_setup_entry(hass, config_entry):
|
||||||
else:
|
else:
|
||||||
await data.async_update(item["id"], {"name": name, "complete": True})
|
await data.async_update(item["id"], {"name": name, "complete": True})
|
||||||
|
|
||||||
|
async def incomplete_item_service(call):
|
||||||
|
"""Mark the item provided via `name` as incomplete."""
|
||||||
|
data = hass.data[DOMAIN]
|
||||||
|
name = call.data.get(ATTR_NAME)
|
||||||
|
if name is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
item = [item for item in data.items if item["name"] == name][0]
|
||||||
|
except IndexError:
|
||||||
|
_LOGGER.error("Restoring of item failed: %s cannot be found", name)
|
||||||
|
else:
|
||||||
|
await data.async_update(item["id"], {"name": name, "complete": False})
|
||||||
|
|
||||||
|
async def complete_all_service(call):
|
||||||
|
"""Mark all items in the list as complete."""
|
||||||
|
await data.async_update_list({"complete": True})
|
||||||
|
|
||||||
|
async def incomplete_all_service(call):
|
||||||
|
"""Mark all items in the list as incomplete."""
|
||||||
|
await data.async_update_list({"complete": False})
|
||||||
|
|
||||||
data = hass.data[DOMAIN] = ShoppingData(hass)
|
data = hass.data[DOMAIN] = ShoppingData(hass)
|
||||||
await data.async_load()
|
await data.async_load()
|
||||||
|
|
||||||
|
@ -101,6 +126,24 @@ async def async_setup_entry(hass, config_entry):
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN, SERVICE_COMPLETE_ITEM, complete_item_service, schema=SERVICE_ITEM_SCHEMA
|
DOMAIN, SERVICE_COMPLETE_ITEM, complete_item_service, schema=SERVICE_ITEM_SCHEMA
|
||||||
)
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_INCOMPLETE_ITEM,
|
||||||
|
incomplete_item_service,
|
||||||
|
schema=SERVICE_ITEM_SCHEMA,
|
||||||
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_COMPLETE_ALL,
|
||||||
|
complete_all_service,
|
||||||
|
schema=SERVICE_LIST_SCHEMA,
|
||||||
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_INCOMPLETE_ALL,
|
||||||
|
incomplete_all_service,
|
||||||
|
schema=SERVICE_LIST_SCHEMA,
|
||||||
|
)
|
||||||
|
|
||||||
hass.http.register_view(ShoppingListView)
|
hass.http.register_view(ShoppingListView)
|
||||||
hass.http.register_view(CreateShoppingListItemView)
|
hass.http.register_view(CreateShoppingListItemView)
|
||||||
|
@ -165,6 +208,13 @@ class ShoppingData:
|
||||||
self.items = [itm for itm in self.items if not itm["complete"]]
|
self.items = [itm for itm in self.items if not itm["complete"]]
|
||||||
await self.hass.async_add_executor_job(self.save)
|
await self.hass.async_add_executor_job(self.save)
|
||||||
|
|
||||||
|
async def async_update_list(self, info):
|
||||||
|
"""Update all items in the list."""
|
||||||
|
for item in self.items:
|
||||||
|
item.update(info)
|
||||||
|
await self.hass.async_add_executor_job(self.save)
|
||||||
|
return self.items
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_reorder(self, item_ids):
|
def async_reorder(self, item_ids):
|
||||||
"""Reorder items."""
|
"""Reorder items."""
|
||||||
|
|
|
@ -21,3 +21,16 @@ complete_item:
|
||||||
example: Beer
|
example: Beer
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
|
||||||
|
incomplete_item:
|
||||||
|
description: Marks an item as incomplete in the shopping list.
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
description: The name of the item to mark as incomplete.
|
||||||
|
example: Beer
|
||||||
|
|
||||||
|
complete_all:
|
||||||
|
description: Marks all items as completed in the shopping list. It does not remove the items.
|
||||||
|
|
||||||
|
incomplete_all:
|
||||||
|
description: Marks all items as incomplete in the shopping list.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Test shopping list component."""
|
"""Test shopping list component."""
|
||||||
|
|
||||||
|
from homeassistant.components.shopping_list.const import DOMAIN
|
||||||
from homeassistant.components.websocket_api.const import (
|
from homeassistant.components.websocket_api.const import (
|
||||||
ERR_INVALID_FORMAT,
|
ERR_INVALID_FORMAT,
|
||||||
ERR_NOT_FOUND,
|
ERR_NOT_FOUND,
|
||||||
|
@ -19,6 +20,39 @@ async def test_add_item(hass, sl_setup):
|
||||||
assert response.speech["plain"]["speech"] == "I've added beer to your shopping list"
|
assert response.speech["plain"]["speech"] == "I've added beer to your shopping list"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_list(hass, sl_setup):
|
||||||
|
"""Test updating all list items."""
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "beer"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
await intent.async_handle(
|
||||||
|
hass, "test", "HassShoppingListAddItem", {"item": {"value": "cheese"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update a single attribute, other attributes shouldn't change
|
||||||
|
await hass.data[DOMAIN].async_update_list({"complete": True})
|
||||||
|
|
||||||
|
beer = hass.data[DOMAIN].items[0]
|
||||||
|
assert beer["name"] == "beer"
|
||||||
|
assert beer["complete"] is True
|
||||||
|
|
||||||
|
cheese = hass.data[DOMAIN].items[1]
|
||||||
|
assert cheese["name"] == "cheese"
|
||||||
|
assert cheese["complete"] is True
|
||||||
|
|
||||||
|
# Update multiple attributes
|
||||||
|
await hass.data[DOMAIN].async_update_list({"name": "dupe", "complete": False})
|
||||||
|
|
||||||
|
beer = hass.data[DOMAIN].items[0]
|
||||||
|
assert beer["name"] == "dupe"
|
||||||
|
assert beer["complete"] is False
|
||||||
|
|
||||||
|
cheese = hass.data[DOMAIN].items[1]
|
||||||
|
assert cheese["name"] == "dupe"
|
||||||
|
assert cheese["complete"] is False
|
||||||
|
|
||||||
|
|
||||||
async def test_recent_items_intent(hass, sl_setup):
|
async def test_recent_items_intent(hass, sl_setup):
|
||||||
"""Test recent items."""
|
"""Test recent items."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue