Add sort list service to Shopping List (#90671)

pull/90633/head
Michael 2023-04-03 21:34:44 +02:00 committed by GitHub
parent 8c621699af
commit d4d77d9395
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 10 deletions

View File

@ -18,6 +18,8 @@ from homeassistant.helpers.typing import ConfigType
from homeassistant.util.json import JsonArrayType, load_json_array
from .const import (
ATTR_REVERSE,
DEFAULT_REVERSE,
DOMAIN,
EVENT_SHOPPING_LIST_UPDATED,
SERVICE_ADD_ITEM,
@ -27,6 +29,7 @@ from .const import (
SERVICE_INCOMPLETE_ALL,
SERVICE_INCOMPLETE_ITEM,
SERVICE_REMOVE_ITEM,
SERVICE_SORT,
)
ATTR_COMPLETE = "complete"
@ -38,6 +41,9 @@ PERSISTENCE = ".shopping_list.json"
SERVICE_ITEM_SCHEMA = vol.Schema({vol.Required(ATTR_NAME): cv.string})
SERVICE_LIST_SCHEMA = vol.Schema({})
SERVICE_SORT_SCHEMA = vol.Schema(
{vol.Optional(ATTR_REVERSE, default=DEFAULT_REVERSE): bool}
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
@ -111,6 +117,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
"""Clear all completed items from the list."""
await data.async_clear_completed()
async def sort_list_service(call: ServiceCall) -> None:
"""Sort all items by name."""
await data.async_sort(call.data[ATTR_REVERSE])
data = hass.data[DOMAIN] = ShoppingData(hass)
await data.async_load()
@ -147,6 +157,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
clear_completed_items_service,
schema=SERVICE_LIST_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_SORT,
sort_list_service,
schema=SERVICE_SORT_SCHEMA,
)
hass.http.register_view(ShoppingListView)
hass.http.register_view(CreateShoppingListItemView)
@ -277,6 +293,16 @@ class ShoppingData:
context=context,
)
async def async_sort(self, reverse=False, context=None):
"""Sort items by name."""
self.items = sorted(self.items, key=lambda item: item["name"], reverse=reverse)
self.hass.async_add_executor_job(self.save)
self.hass.bus.async_fire(
EVENT_SHOPPING_LIST_UPDATED,
{"action": "sorted"},
context=context,
)
async def async_load(self) -> None:
"""Load items."""

View File

@ -2,6 +2,10 @@
DOMAIN = "shopping_list"
EVENT_SHOPPING_LIST_UPDATED = "shopping_list_updated"
ATTR_REVERSE = "reverse"
DEFAULT_REVERSE = False
SERVICE_ADD_ITEM = "add_item"
SERVICE_REMOVE_ITEM = "remove_item"
SERVICE_COMPLETE_ITEM = "complete_item"
@ -9,3 +13,4 @@ SERVICE_INCOMPLETE_ITEM = "incomplete_item"
SERVICE_COMPLETE_ALL = "complete_all"
SERVICE_INCOMPLETE_ALL = "incomplete_all"
SERVICE_CLEAR_COMPLETED_ITEMS = "clear_completed_items"
SERVICE_SORT = "sort"

View File

@ -56,3 +56,14 @@ incomplete_all:
clear_completed_items:
name: Clear completed items
description: Clear completed items from the shopping list.
sort:
name: Sort all items
description: Sort all items by name in the shopping list.
fields:
reverse:
name: Sort reverse
description: Whether to sort in reverse (descending) order.
default: false
selector:
boolean:

View File

@ -5,12 +5,14 @@ import pytest
from homeassistant.components.shopping_list import NoMatchingShoppingListItem
from homeassistant.components.shopping_list.const import (
ATTR_REVERSE,
DOMAIN,
EVENT_SHOPPING_LIST_UPDATED,
SERVICE_ADD_ITEM,
SERVICE_CLEAR_COMPLETED_ITEMS,
SERVICE_COMPLETE_ITEM,
SERVICE_REMOVE_ITEM,
SERVICE_SORT,
)
from homeassistant.components.websocket_api.const import (
ERR_INVALID_FORMAT,
@ -657,8 +659,6 @@ async def test_add_item_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_NAME: "beer"},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
@ -672,15 +672,12 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_NAME: "beer"},
blocking=True,
)
await hass.async_block_till_done()
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_ITEM,
{ATTR_NAME: "cheese"},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 2
assert len(events) == 2
@ -690,8 +687,6 @@ async def test_remove_item_service(hass: HomeAssistant, sl_setup) -> None:
{ATTR_NAME: "beer"},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 1
assert hass.data[DOMAIN].items[0]["name"] == "cheese"
assert len(events) == 3
@ -706,7 +701,6 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
{ATTR_NAME: "beer"},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
@ -717,7 +711,6 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
{ATTR_NAME: "beer"},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 1
assert len(events) == 1
@ -728,6 +721,44 @@ async def test_clear_completed_items_service(hass: HomeAssistant, sl_setup) -> N
{},
blocking=True,
)
await hass.async_block_till_done()
assert len(hass.data[DOMAIN].items) == 0
assert len(events) == 1
async def test_sort_list_service(hass: HomeAssistant, sl_setup) -> None:
"""Test sort_all service."""
for name in ("zzz", "ddd", "aaa"):
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_ITEM,
{ATTR_NAME: name},
blocking=True,
)
# sort ascending
events = async_capture_events(hass, EVENT_SHOPPING_LIST_UPDATED)
await hass.services.async_call(
DOMAIN,
SERVICE_SORT,
{ATTR_REVERSE: False},
blocking=True,
)
assert hass.data[DOMAIN].items[0][ATTR_NAME] == "aaa"
assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd"
assert hass.data[DOMAIN].items[2][ATTR_NAME] == "zzz"
assert len(events) == 1
# sort descending
await hass.services.async_call(
DOMAIN,
SERVICE_SORT,
{ATTR_REVERSE: True},
blocking=True,
)
assert hass.data[DOMAIN].items[0][ATTR_NAME] == "zzz"
assert hass.data[DOMAIN].items[1][ATTR_NAME] == "ddd"
assert hass.data[DOMAIN].items[2][ATTR_NAME] == "aaa"
assert len(events) == 2