Add shopping lists to Mealie integration (#121534)
* Add mealie shopping lists & tests * Add shopping_lists init failure * Fix coordinator name * Fixes * Add available, fix merge * Fixes * Fixes * Add todo failure tests * Fix testspull/121619/head
parent
5b25c24539
commit
898803abe9
|
@ -13,10 +13,15 @@ from homeassistant.helpers.device_registry import DeviceEntryType
|
|||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MealieConfigEntry, MealieCoordinator
|
||||
from .coordinator import (
|
||||
MealieConfigEntry,
|
||||
MealieData,
|
||||
MealieMealplanCoordinator,
|
||||
MealieShoppingListCoordinator,
|
||||
)
|
||||
from .services import setup_services
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CALENDAR]
|
||||
PLATFORMS: list[Platform] = [Platform.CALENDAR, Platform.TODO]
|
||||
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
|
||||
|
@ -50,11 +55,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: MealieConfigEntry) -> bo
|
|||
sw_version=about.version,
|
||||
)
|
||||
|
||||
coordinator = MealieCoordinator(hass, client)
|
||||
mealplan_coordinator = MealieMealplanCoordinator(hass, client)
|
||||
shoppinglist_coordinator = MealieShoppingListCoordinator(hass, client)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
await mealplan_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = coordinator
|
||||
await shoppinglist_coordinator.async_get_shopping_lists()
|
||||
await shoppinglist_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
entry.runtime_data = MealieData(
|
||||
client, mealplan_coordinator, shoppinglist_coordinator
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from homeassistant.components.calendar import CalendarEntity, CalendarEvent
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .coordinator import MealieConfigEntry, MealieCoordinator
|
||||
from .coordinator import MealieConfigEntry, MealieMealplanCoordinator
|
||||
from .entity import MealieEntity
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ async def async_setup_entry(
|
|||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the calendar platform for entity."""
|
||||
coordinator = entry.runtime_data
|
||||
coordinator = entry.runtime_data.mealplan_coordinator
|
||||
|
||||
async_add_entities(
|
||||
MealieMealplanCalendarEntity(coordinator, entry_type)
|
||||
|
@ -47,7 +47,7 @@ class MealieMealplanCalendarEntity(MealieEntity, CalendarEntity):
|
|||
"""A calendar entity."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator: MealieCoordinator, entry_type: MealplanEntryType
|
||||
self, coordinator: MealieMealplanCoordinator, entry_type: MealplanEntryType
|
||||
) -> None:
|
||||
"""Create the Calendar entity."""
|
||||
super().__init__(coordinator, entry_type.name.lower())
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
from aiomealie import (
|
||||
|
@ -10,6 +11,8 @@ from aiomealie import (
|
|||
MealieConnectionError,
|
||||
Mealplan,
|
||||
MealplanEntryType,
|
||||
ShoppingItem,
|
||||
ShoppingList,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -22,18 +25,53 @@ from .const import LOGGER
|
|||
|
||||
WEEK = timedelta(days=7)
|
||||
|
||||
type MealieConfigEntry = ConfigEntry[MealieCoordinator]
|
||||
|
||||
@dataclass
|
||||
class MealieData:
|
||||
"""Mealie data type."""
|
||||
|
||||
client: MealieClient
|
||||
mealplan_coordinator: MealieMealplanCoordinator
|
||||
shoppinglist_coordinator: MealieShoppingListCoordinator
|
||||
|
||||
|
||||
class MealieCoordinator(DataUpdateCoordinator[dict[MealplanEntryType, list[Mealplan]]]):
|
||||
"""Class to manage fetching Mealie data."""
|
||||
type MealieConfigEntry = ConfigEntry[MealieData]
|
||||
|
||||
|
||||
class MealieDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
||||
"""Base coordinator."""
|
||||
|
||||
config_entry: MealieConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
name: str,
|
||||
client: MealieClient,
|
||||
update_interval: timedelta,
|
||||
) -> None:
|
||||
"""Initialize the Withings data coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
LOGGER,
|
||||
name=name,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
self.client = client
|
||||
|
||||
|
||||
class MealieMealplanCoordinator(
|
||||
MealieDataUpdateCoordinator[dict[MealplanEntryType, list[Mealplan]]]
|
||||
):
|
||||
"""Class to manage fetching Mealie data."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, client: MealieClient) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
hass, logger=LOGGER, name="Mealie", update_interval=timedelta(hours=1)
|
||||
hass,
|
||||
name="MealieMealplan",
|
||||
client=client,
|
||||
update_interval=timedelta(hours=1),
|
||||
)
|
||||
self.client = client
|
||||
|
||||
|
@ -56,3 +94,51 @@ class MealieCoordinator(DataUpdateCoordinator[dict[MealplanEntryType, list[Mealp
|
|||
for meal in data:
|
||||
res[meal.entry_type].append(meal)
|
||||
return res
|
||||
|
||||
|
||||
class MealieShoppingListCoordinator(
|
||||
MealieDataUpdateCoordinator[dict[str, list[ShoppingItem]]]
|
||||
):
|
||||
"""Class to manage fetching Mealie Shopping list data."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, client: MealieClient) -> None:
|
||||
"""Initialize coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
name="MealieShoppingLists",
|
||||
client=client,
|
||||
update_interval=timedelta(minutes=5),
|
||||
)
|
||||
self.shopping_lists: list[ShoppingList]
|
||||
|
||||
async def async_get_shopping_lists(self) -> list[ShoppingList]:
|
||||
"""Return shopping lists."""
|
||||
try:
|
||||
self.shopping_lists = (await self.client.get_shopping_lists()).items
|
||||
except MealieAuthenticationError as error:
|
||||
raise ConfigEntryError("Authentication failed") from error
|
||||
except MealieConnectionError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
return self.shopping_lists
|
||||
|
||||
async def _async_update_data(
|
||||
self,
|
||||
) -> dict[str, list[ShoppingItem]]:
|
||||
shopping_list_items: dict[str, list[ShoppingItem]] = {}
|
||||
|
||||
try:
|
||||
for shopping_list in self.shopping_lists:
|
||||
shopping_list_id = shopping_list.list_id
|
||||
|
||||
shopping_items = (
|
||||
await self.client.get_shopping_items(shopping_list_id)
|
||||
).items
|
||||
|
||||
shopping_list_items[shopping_list_id] = shopping_items
|
||||
|
||||
except MealieAuthenticationError as error:
|
||||
raise ConfigEntryError("Authentication failed") from error
|
||||
except MealieConnectionError as error:
|
||||
raise UpdateFailed(error) from error
|
||||
|
||||
return shopping_list_items
|
||||
|
|
|
@ -4,15 +4,15 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MealieCoordinator
|
||||
from .coordinator import MealieDataUpdateCoordinator
|
||||
|
||||
|
||||
class MealieEntity(CoordinatorEntity[MealieCoordinator]):
|
||||
class MealieEntity(CoordinatorEntity[MealieDataUpdateCoordinator]):
|
||||
"""Defines a base Mealie entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: MealieCoordinator, key: str) -> None:
|
||||
def __init__(self, coordinator: MealieDataUpdateCoordinator, key: str) -> None:
|
||||
"""Initialize Mealie entity."""
|
||||
super().__init__(coordinator)
|
||||
unique_id = coordinator.config_entry.unique_id
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"entity": {
|
||||
"todo": {
|
||||
"shopping_list": {
|
||||
"default": "mdi:basket"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"get_mealplan": "mdi:food",
|
||||
"get_recipe": "mdi:map"
|
||||
|
|
|
@ -51,6 +51,18 @@
|
|||
},
|
||||
"recipe_not_found": {
|
||||
"message": "Recipe with ID or slug `{recipe_id}` not found."
|
||||
},
|
||||
"add_item_error": {
|
||||
"message": "An error occurred adding an item to {shopping_list_name}."
|
||||
},
|
||||
"update_item_error": {
|
||||
"message": "An error occurred updating an item in {shopping_list_name}."
|
||||
},
|
||||
"delete_item_error": {
|
||||
"message": "An error occurred deleting an item in {shopping_list_name}."
|
||||
},
|
||||
"item_not_found_error": {
|
||||
"message": "Item {shopping_list_item} not found."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
"""Todo platform for Mealie."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from aiomealie import MealieError, MutateShoppingItem, ShoppingItem, ShoppingList
|
||||
|
||||
from homeassistant.components.todo import (
|
||||
TodoItem,
|
||||
TodoItemStatus,
|
||||
TodoListEntity,
|
||||
TodoListEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MealieConfigEntry, MealieShoppingListCoordinator
|
||||
from .entity import MealieEntity
|
||||
|
||||
TODO_STATUS_MAP = {
|
||||
False: TodoItemStatus.NEEDS_ACTION,
|
||||
True: TodoItemStatus.COMPLETED,
|
||||
}
|
||||
TODO_STATUS_MAP_INV = {v: k for k, v in TODO_STATUS_MAP.items()}
|
||||
|
||||
|
||||
def _convert_api_item(item: ShoppingItem) -> TodoItem:
|
||||
"""Convert Mealie shopping list items into a TodoItem."""
|
||||
|
||||
return TodoItem(
|
||||
summary=item.display,
|
||||
uid=item.item_id,
|
||||
status=TODO_STATUS_MAP.get(
|
||||
item.checked,
|
||||
TodoItemStatus.NEEDS_ACTION,
|
||||
),
|
||||
due=None,
|
||||
description=None,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: MealieConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the todo platform for entity."""
|
||||
coordinator = entry.runtime_data.shoppinglist_coordinator
|
||||
|
||||
async_add_entities(
|
||||
MealieShoppingListTodoListEntity(coordinator, shopping_list)
|
||||
for shopping_list in coordinator.shopping_lists
|
||||
)
|
||||
|
||||
|
||||
class MealieShoppingListTodoListEntity(MealieEntity, TodoListEntity):
|
||||
"""A todo list entity."""
|
||||
|
||||
_attr_supported_features = (
|
||||
TodoListEntityFeature.CREATE_TODO_ITEM
|
||||
| TodoListEntityFeature.UPDATE_TODO_ITEM
|
||||
| TodoListEntityFeature.DELETE_TODO_ITEM
|
||||
| TodoListEntityFeature.MOVE_TODO_ITEM
|
||||
)
|
||||
|
||||
_attr_translation_key = "shopping_list"
|
||||
|
||||
coordinator: MealieShoppingListCoordinator
|
||||
|
||||
def __init__(
|
||||
self, coordinator: MealieShoppingListCoordinator, shopping_list: ShoppingList
|
||||
) -> None:
|
||||
"""Create the todo entity."""
|
||||
super().__init__(coordinator, shopping_list.list_id)
|
||||
self._shopping_list = shopping_list
|
||||
self._attr_name = shopping_list.name
|
||||
|
||||
@property
|
||||
def shopping_items(self) -> list[ShoppingItem]:
|
||||
"""Get the shopping items for this list."""
|
||||
return self.coordinator.data[self._shopping_list.list_id]
|
||||
|
||||
@property
|
||||
def todo_items(self) -> list[TodoItem] | None:
|
||||
"""Get the current set of To-do items."""
|
||||
return [_convert_api_item(item) for item in self.shopping_items]
|
||||
|
||||
async def async_create_todo_item(self, item: TodoItem) -> None:
|
||||
"""Add an item to the list."""
|
||||
position = 0
|
||||
if len(self.shopping_items) > 0:
|
||||
position = self.shopping_items[-1].position + 1
|
||||
|
||||
new_shopping_item = MutateShoppingItem(
|
||||
list_id=self._shopping_list.list_id,
|
||||
note=item.summary.strip() if item.summary else item.summary,
|
||||
position=position,
|
||||
)
|
||||
try:
|
||||
await self.coordinator.client.add_shopping_item(new_shopping_item)
|
||||
except MealieError as exception:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="add_item_error",
|
||||
translation_placeholders={
|
||||
"shopping_list_name": self._shopping_list.name
|
||||
},
|
||||
) from exception
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_update_todo_item(self, item: TodoItem) -> None:
|
||||
"""Update an item on the list."""
|
||||
list_items = self.shopping_items
|
||||
|
||||
for items in list_items:
|
||||
if items.item_id == item.uid:
|
||||
position = items.position
|
||||
break
|
||||
|
||||
list_item: ShoppingItem | None = next(
|
||||
(x for x in list_items if x.item_id == item.uid), None
|
||||
)
|
||||
|
||||
if not list_item:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="item_not_found_error",
|
||||
translation_placeholders={"shopping_list_item": item.uid or ""},
|
||||
)
|
||||
|
||||
udpdate_shopping_item = MutateShoppingItem(
|
||||
item_id=list_item.item_id,
|
||||
list_id=list_item.list_id,
|
||||
note=list_item.note,
|
||||
display=list_item.display,
|
||||
checked=item.status == TodoItemStatus.COMPLETED,
|
||||
position=list_item.position,
|
||||
is_food=list_item.is_food,
|
||||
disable_amount=list_item.disable_amount,
|
||||
quantity=list_item.quantity,
|
||||
label_id=list_item.label_id,
|
||||
food_id=list_item.food_id,
|
||||
unit_id=list_item.unit_id,
|
||||
)
|
||||
|
||||
stripped_item_summary = item.summary.strip() if item.summary else item.summary
|
||||
|
||||
if list_item.display.strip() != stripped_item_summary:
|
||||
udpdate_shopping_item.note = stripped_item_summary
|
||||
udpdate_shopping_item.position = position
|
||||
udpdate_shopping_item.is_food = False
|
||||
udpdate_shopping_item.food_id = None
|
||||
udpdate_shopping_item.quantity = 0.0
|
||||
udpdate_shopping_item.checked = item.status == TodoItemStatus.COMPLETED
|
||||
|
||||
try:
|
||||
await self.coordinator.client.update_shopping_item(
|
||||
list_item.item_id, udpdate_shopping_item
|
||||
)
|
||||
except MealieError as exception:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="update_item_error",
|
||||
translation_placeholders={
|
||||
"shopping_list_name": self._shopping_list.name
|
||||
},
|
||||
) from exception
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_delete_todo_items(self, uids: list[str]) -> None:
|
||||
"""Delete items from the list."""
|
||||
try:
|
||||
for uid in uids:
|
||||
await self.coordinator.client.delete_shopping_item(uid)
|
||||
except MealieError as exception:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="delete_item_error",
|
||||
translation_placeholders={
|
||||
"shopping_list_name": self._shopping_list.name
|
||||
},
|
||||
) from exception
|
||||
finally:
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_move_todo_item(
|
||||
self, uid: str, previous_uid: str | None = None
|
||||
) -> None:
|
||||
"""Re-order an item on the list."""
|
||||
if uid == previous_uid:
|
||||
return
|
||||
list_items: list[ShoppingItem] = self.shopping_items
|
||||
|
||||
item_idx = {itm.item_id: idx for idx, itm in enumerate(list_items)}
|
||||
if uid not in item_idx:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="item_not_found_error",
|
||||
translation_placeholders={"shopping_list_item": uid},
|
||||
)
|
||||
if previous_uid and previous_uid not in item_idx:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="item_not_found_error",
|
||||
translation_placeholders={"shopping_list_item": previous_uid},
|
||||
)
|
||||
dst_idx = item_idx[previous_uid] + 1 if previous_uid else 0
|
||||
src_idx = item_idx[uid]
|
||||
src_item = list_items.pop(src_idx)
|
||||
if dst_idx > src_idx:
|
||||
dst_idx -= 1
|
||||
list_items.insert(dst_idx, src_item)
|
||||
|
||||
for position, item in enumerate(list_items):
|
||||
mutate_shopping_item = MutateShoppingItem()
|
||||
mutate_shopping_item.list_id = item.list_id
|
||||
mutate_shopping_item.item_id = item.item_id
|
||||
mutate_shopping_item.position = position
|
||||
mutate_shopping_item.is_food = item.is_food
|
||||
mutate_shopping_item.quantity = item.quantity
|
||||
mutate_shopping_item.label_id = item.label_id
|
||||
mutate_shopping_item.note = item.note
|
||||
mutate_shopping_item.checked = item.checked
|
||||
|
||||
if item.is_food:
|
||||
mutate_shopping_item.food_id = item.food_id
|
||||
mutate_shopping_item.unit_id = item.unit_id
|
||||
|
||||
await self.coordinator.client.update_shopping_item(
|
||||
mutate_shopping_item.item_id, mutate_shopping_item
|
||||
)
|
||||
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return False if shopping list no longer available."""
|
||||
return (
|
||||
super().available and self._shopping_list.list_id in self.coordinator.data
|
||||
)
|
|
@ -3,7 +3,15 @@
|
|||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
from aiomealie import About, Mealplan, MealplanResponse, Recipe, UserInfo
|
||||
from aiomealie import (
|
||||
About,
|
||||
Mealplan,
|
||||
MealplanResponse,
|
||||
Recipe,
|
||||
ShoppingItemsResponse,
|
||||
ShoppingListsResponse,
|
||||
UserInfo,
|
||||
)
|
||||
from mashumaro.codecs.orjson import ORJSONDecoder
|
||||
import pytest
|
||||
|
||||
|
@ -13,6 +21,9 @@ from homeassistant.const import CONF_API_TOKEN, CONF_HOST
|
|||
from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.components.smhi.common import AsyncMock
|
||||
|
||||
SHOPPING_LIST_ID = "list-id-1"
|
||||
SHOPPING_ITEM_NOTE = "Shopping Item 1"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
|
@ -50,6 +61,12 @@ def mock_mealie_client() -> Generator[AsyncMock]:
|
|||
client.get_about.return_value = About.from_json(
|
||||
load_fixture("about.json", DOMAIN)
|
||||
)
|
||||
client.get_shopping_lists.return_value = ShoppingListsResponse.from_json(
|
||||
load_fixture("get_shopping_lists.json", DOMAIN)
|
||||
)
|
||||
client.get_shopping_items.return_value = ShoppingItemsResponse.from_json(
|
||||
load_fixture("get_shopping_items.json", DOMAIN)
|
||||
)
|
||||
client.get_recipe.return_value = Recipe.from_json(
|
||||
load_fixture("get_recipe.json", DOMAIN)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"page": 1,
|
||||
"per_page": 1000,
|
||||
"total": 3,
|
||||
"total_pages": 1,
|
||||
"items": [
|
||||
{
|
||||
"quantity": 2.0,
|
||||
"unit": null,
|
||||
"food": null,
|
||||
"note": "Apples",
|
||||
"isFood": false,
|
||||
"disableAmount": true,
|
||||
"display": "2 Apples",
|
||||
"shoppingListId": "9ce096fe-ded2-4077-877d-78ba450ab13e",
|
||||
"checked": false,
|
||||
"position": 0,
|
||||
"foodId": null,
|
||||
"labelId": null,
|
||||
"unitId": null,
|
||||
"extras": {},
|
||||
"id": "f45430f7-3edf-45a9-a50f-73bb375090be",
|
||||
"label": null,
|
||||
"recipeReferences": [],
|
||||
"createdAt": "2024-06-25T10:45:03.362623",
|
||||
"updateAt": "2024-06-25T11:57:22.412650"
|
||||
},
|
||||
{
|
||||
"quantity": 1.0,
|
||||
"unit": {
|
||||
"id": "7bf539d4-fc78-48bc-b48e-c35ccccec34a",
|
||||
"name": "can",
|
||||
"pluralName": null,
|
||||
"description": "",
|
||||
"extras": {},
|
||||
"fraction": true,
|
||||
"abbreviation": "",
|
||||
"pluralAbbreviation": "",
|
||||
"useAbbreviation": false,
|
||||
"aliases": [],
|
||||
"createdAt": "2024-05-14T14:45:02.464122",
|
||||
"updateAt": "2024-05-14T14:45:02.464124"
|
||||
},
|
||||
"food": {
|
||||
"id": "09322430-d24c-4b1a-abb6-22b6ed3a88f5",
|
||||
"name": "acorn squash",
|
||||
"pluralName": null,
|
||||
"description": "",
|
||||
"extras": {},
|
||||
"labelId": null,
|
||||
"aliases": [],
|
||||
"label": null,
|
||||
"createdAt": "2024-05-14T14:45:04.454134",
|
||||
"updateAt": "2024-05-14T14:45:04.454141"
|
||||
},
|
||||
"note": "",
|
||||
"isFood": true,
|
||||
"disableAmount": false,
|
||||
"display": "1 can acorn squash",
|
||||
"shoppingListId": "9ce096fe-ded2-4077-877d-78ba450ab13e",
|
||||
"checked": false,
|
||||
"position": 1,
|
||||
"foodId": "09322430-d24c-4b1a-abb6-22b6ed3a88f5",
|
||||
"labelId": null,
|
||||
"unitId": "7bf539d4-fc78-48bc-b48e-c35ccccec34a",
|
||||
"extras": {},
|
||||
"id": "84d8fd74-8eb0-402e-84b6-71f251bfb7cc",
|
||||
"label": null,
|
||||
"recipeReferences": [],
|
||||
"createdAt": "2024-06-25T10:45:14.547922",
|
||||
"updateAt": "2024-06-25T10:45:14.547925"
|
||||
},
|
||||
{
|
||||
"quantity": 0.0,
|
||||
"unit": null,
|
||||
"food": {
|
||||
"id": "96801494-4e26-4148-849a-8155deb76327",
|
||||
"name": "aubergine",
|
||||
"pluralName": null,
|
||||
"description": "",
|
||||
"extras": {},
|
||||
"labelId": null,
|
||||
"aliases": [],
|
||||
"label": null,
|
||||
"createdAt": "2024-05-14T14:45:03.868792",
|
||||
"updateAt": "2024-05-14T14:45:03.868794"
|
||||
},
|
||||
"note": "",
|
||||
"isFood": true,
|
||||
"disableAmount": false,
|
||||
"display": "aubergine",
|
||||
"shoppingListId": "9ce096fe-ded2-4077-877d-78ba450ab13e",
|
||||
"checked": false,
|
||||
"position": 2,
|
||||
"foodId": "96801494-4e26-4148-849a-8155deb76327",
|
||||
"labelId": null,
|
||||
"unitId": null,
|
||||
"extras": {},
|
||||
"id": "69913b9a-7c75-4935-abec-297cf7483f88",
|
||||
"label": null,
|
||||
"recipeReferences": [],
|
||||
"createdAt": "2024-06-25T11:56:59.656699",
|
||||
"updateAt": "2024-06-25T11:56:59.656701"
|
||||
}
|
||||
],
|
||||
"next": null,
|
||||
"previous": null
|
||||
}
|
|
@ -0,0 +1,838 @@
|
|||
{
|
||||
"page": 1,
|
||||
"per_page": 50,
|
||||
"total": 3,
|
||||
"total_pages": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Supermarket",
|
||||
"extras": {},
|
||||
"createdAt": "2024-06-17T11:01:54.267314",
|
||||
"updateAt": "2024-06-22T10:22:13.555389",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"userId": "90b03954-00e1-46de-9520-f0305022b84f",
|
||||
"id": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"recipeReferences": [],
|
||||
"labelSettings": [
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "0f63545a-606a-47ea-a784-452d45de6158",
|
||||
"position": 0,
|
||||
"id": "ad5f48b0-5b26-4c2d-a2aa-79b0beae1e42",
|
||||
"label": {
|
||||
"name": "Alcohol",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0f63545a-606a-47ea-a784-452d45de6158"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "0c2d6111-9837-4319-acb5-490a32979993",
|
||||
"position": 1,
|
||||
"id": "c9b8289a-6693-4bec-9841-d7d08c3b240b",
|
||||
"label": {
|
||||
"name": "Baked Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0c2d6111-9837-4319-acb5-490a32979993"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "3922802c-8e8c-47d4-9c68-e60b0a1338b6",
|
||||
"position": 2,
|
||||
"id": "9be06f8a-6c23-476b-a8cc-334884bcdd40",
|
||||
"label": {
|
||||
"name": "Beverages",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3922802c-8e8c-47d4-9c68-e60b0a1338b6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "4111bfff-d834-4e8c-88ed-5eff761e06db",
|
||||
"position": 3,
|
||||
"id": "47bc36ae-1ee4-40be-ad68-ad8662c26cae",
|
||||
"label": {
|
||||
"name": "Canned Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "4111bfff-d834-4e8c-88ed-5eff761e06db"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "24fa2836-25e8-44af-b497-ad0d428a7f78",
|
||||
"position": 4,
|
||||
"id": "ad41f42c-08c3-49ef-8b96-dc1740ec95b6",
|
||||
"label": {
|
||||
"name": "Condiments",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "24fa2836-25e8-44af-b497-ad0d428a7f78"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "20a735de-c75b-4fdb-abaf-b8d71ef192f8",
|
||||
"position": 5,
|
||||
"id": "5514842f-8c05-4003-a42d-7a5a70d80148",
|
||||
"label": {
|
||||
"name": "Confectionary",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "20a735de-c75b-4fdb-abaf-b8d71ef192f8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "71178428-70aa-4491-b5b4-b8d93e7b04cf",
|
||||
"position": 6,
|
||||
"id": "0465a139-6571-4599-836b-a562afc95536",
|
||||
"label": {
|
||||
"name": "Dairy Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "71178428-70aa-4491-b5b4-b8d93e7b04cf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "c58ed864-b5bf-4aac-88a1-007833c706c7",
|
||||
"position": 7,
|
||||
"id": "8d85fe1b-ec4d-49d0-aecc-15f9dbc66fd0",
|
||||
"label": {
|
||||
"name": "Frozen Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c58ed864-b5bf-4aac-88a1-007833c706c7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "f398f1a4-ce53-42df-95d4-8a3403bb6a38",
|
||||
"position": 8,
|
||||
"id": "b6980720-bd88-4703-a115-50c0b915f607",
|
||||
"label": {
|
||||
"name": "Fruits",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "f398f1a4-ce53-42df-95d4-8a3403bb6a38"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "fd936065-3d53-4844-99df-9332f1bf0c8a",
|
||||
"position": 9,
|
||||
"id": "5d69d13c-5d7f-45af-9ecc-045ca914f7ca",
|
||||
"label": {
|
||||
"name": "Grains",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "fd936065-3d53-4844-99df-9332f1bf0c8a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf",
|
||||
"position": 10,
|
||||
"id": "a5e65ce7-3588-412b-a118-2fe1a2ca0104",
|
||||
"label": {
|
||||
"name": "Health Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b",
|
||||
"position": 11,
|
||||
"id": "9890d86a-98e9-4599-8daf-82d341ef1e8d",
|
||||
"label": {
|
||||
"name": "Household",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "cf136576-1929-4fc9-a3da-34c49ff58920",
|
||||
"position": 12,
|
||||
"id": "18fc0f39-3e45-412f-afa7-7eb779f7bfdf",
|
||||
"label": {
|
||||
"name": "Meat",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf136576-1929-4fc9-a3da-34c49ff58920"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa",
|
||||
"position": 13,
|
||||
"id": "4cd55de7-7c2e-4078-8c61-87d40b33ebda",
|
||||
"label": {
|
||||
"name": "Meat Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "2a035661-fd5d-462c-8eb0-6b78af982e0c",
|
||||
"position": 14,
|
||||
"id": "21c55b4a-c1b1-44c0-962e-040bbfa5e148",
|
||||
"label": {
|
||||
"name": "Other",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "2a035661-fd5d-462c-8eb0-6b78af982e0c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "af147838-d114-4a92-bd0f-08f05f59bbe5",
|
||||
"position": 15,
|
||||
"id": "b295a6be-1437-4415-92bb-4eee21d3195d",
|
||||
"label": {
|
||||
"name": "Produce",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "af147838-d114-4a92-bd0f-08f05f59bbe5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "cf7672b8-036a-45a4-8323-6a167d2731be",
|
||||
"position": 16,
|
||||
"id": "d3ae533f-c1a8-4f08-8a0f-a88914b2c84b",
|
||||
"label": {
|
||||
"name": "Regular",
|
||||
"color": "#2E7D32FF",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf7672b8-036a-45a4-8323-6a167d2731be"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18",
|
||||
"position": 17,
|
||||
"id": "572dbf60-4308-499e-ad7c-d806462ee501",
|
||||
"label": {
|
||||
"name": "Seafood",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "1c59a263-227a-4f43-a450-d53ca1485b36",
|
||||
"position": 18,
|
||||
"id": "5321b4d8-3aba-4a64-95b2-03ac533dda32",
|
||||
"label": {
|
||||
"name": "Snacks",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "1c59a263-227a-4f43-a450-d53ca1485b36"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "189099a9-0033-4783-804a-ec6805e7d557",
|
||||
"position": 19,
|
||||
"id": "98aebebf-27fe-4834-b3d3-0e45201a182f",
|
||||
"label": {
|
||||
"name": "Spices",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "189099a9-0033-4783-804a-ec6805e7d557"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "c28efdde-5993-4044-b824-f111f3a118ef",
|
||||
"position": 20,
|
||||
"id": "3e3aa706-3008-4280-b332-a7d2c31cf683",
|
||||
"label": {
|
||||
"name": "Sweets",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c28efdde-5993-4044-b824-f111f3a118ef"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "27edbaab-2ec6-441f-8490-0283ea77585f",
|
||||
"labelId": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c",
|
||||
"position": 21,
|
||||
"id": "48f109ca-c57a-4828-98ab-a2db1e6514c6",
|
||||
"label": {
|
||||
"name": "Vegetables",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Special groceries",
|
||||
"extras": {},
|
||||
"createdAt": "2024-06-07T07:17:05.479808",
|
||||
"updateAt": "2024-06-12T08:44:58.831239",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"userId": "90b03954-00e1-46de-9520-f0305022b84f",
|
||||
"id": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"recipeReferences": [],
|
||||
"labelSettings": [
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "0f63545a-606a-47ea-a784-452d45de6158",
|
||||
"position": 0,
|
||||
"id": "1a5dc45b-e6ae-4db2-bd2f-fa3c07efedeb",
|
||||
"label": {
|
||||
"name": "Alcohol",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0f63545a-606a-47ea-a784-452d45de6158"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "0c2d6111-9837-4319-acb5-490a32979993",
|
||||
"position": 1,
|
||||
"id": "d1594c9d-f1b6-4160-a4eb-0686499a40ea",
|
||||
"label": {
|
||||
"name": "Baked Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0c2d6111-9837-4319-acb5-490a32979993"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "3922802c-8e8c-47d4-9c68-e60b0a1338b6",
|
||||
"position": 2,
|
||||
"id": "077106d0-5c85-493c-ae6b-dea06002c824",
|
||||
"label": {
|
||||
"name": "Beverages",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3922802c-8e8c-47d4-9c68-e60b0a1338b6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "4111bfff-d834-4e8c-88ed-5eff761e06db",
|
||||
"position": 3,
|
||||
"id": "bf66b7e8-3758-4f9e-9e13-c7b9ff564889",
|
||||
"label": {
|
||||
"name": "Canned Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "4111bfff-d834-4e8c-88ed-5eff761e06db"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "24fa2836-25e8-44af-b497-ad0d428a7f78",
|
||||
"position": 4,
|
||||
"id": "bb34f741-10b4-490a-a512-67bbd374427c",
|
||||
"label": {
|
||||
"name": "Condiments",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "24fa2836-25e8-44af-b497-ad0d428a7f78"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "20a735de-c75b-4fdb-abaf-b8d71ef192f8",
|
||||
"position": 5,
|
||||
"id": "d88b23a5-e397-4cf2-b527-d8982ecf89e0",
|
||||
"label": {
|
||||
"name": "Confectionary",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "20a735de-c75b-4fdb-abaf-b8d71ef192f8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "71178428-70aa-4491-b5b4-b8d93e7b04cf",
|
||||
"position": 6,
|
||||
"id": "82d44804-5bef-4cc3-9d1f-0d8e879783c0",
|
||||
"label": {
|
||||
"name": "Dairy Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "71178428-70aa-4491-b5b4-b8d93e7b04cf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "c58ed864-b5bf-4aac-88a1-007833c706c7",
|
||||
"position": 7,
|
||||
"id": "0ae70dde-7403-408f-a6c6-c19b8c0f6a4d",
|
||||
"label": {
|
||||
"name": "Frozen Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c58ed864-b5bf-4aac-88a1-007833c706c7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "f398f1a4-ce53-42df-95d4-8a3403bb6a38",
|
||||
"position": 8,
|
||||
"id": "7667a581-8d63-4785-a013-8e164994dfc4",
|
||||
"label": {
|
||||
"name": "Fruits",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "f398f1a4-ce53-42df-95d4-8a3403bb6a38"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "fd936065-3d53-4844-99df-9332f1bf0c8a",
|
||||
"position": 9,
|
||||
"id": "749c8cbd-c4e5-4879-bce1-40c3b62ada71",
|
||||
"label": {
|
||||
"name": "Grains",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "fd936065-3d53-4844-99df-9332f1bf0c8a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf",
|
||||
"position": 10,
|
||||
"id": "e7979797-7679-47be-b14f-5fdcfe1c987d",
|
||||
"label": {
|
||||
"name": "Health Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b",
|
||||
"position": 11,
|
||||
"id": "1a9b6d19-d8b5-41a0-8e75-548c36fc0b1b",
|
||||
"label": {
|
||||
"name": "Household",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "cf136576-1929-4fc9-a3da-34c49ff58920",
|
||||
"position": 12,
|
||||
"id": "0df24ff7-1767-46a1-9841-97f816079580",
|
||||
"label": {
|
||||
"name": "Meat",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf136576-1929-4fc9-a3da-34c49ff58920"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa",
|
||||
"position": 13,
|
||||
"id": "761b5985-9f49-450b-a33c-5b85366501da",
|
||||
"label": {
|
||||
"name": "Meat Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "2a035661-fd5d-462c-8eb0-6b78af982e0c",
|
||||
"position": 14,
|
||||
"id": "cd993b6c-2c06-40b3-8fe2-8f9613d29b8e",
|
||||
"label": {
|
||||
"name": "Other",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "2a035661-fd5d-462c-8eb0-6b78af982e0c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "af147838-d114-4a92-bd0f-08f05f59bbe5",
|
||||
"position": 15,
|
||||
"id": "9c9f8e0d-a9e8-4503-ad98-ee7039ec6eec",
|
||||
"label": {
|
||||
"name": "Produce",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "af147838-d114-4a92-bd0f-08f05f59bbe5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "cf7672b8-036a-45a4-8323-6a167d2731be",
|
||||
"position": 16,
|
||||
"id": "f2a1fa92-1ee3-47b5-9d5f-1ac21e0d6bf3",
|
||||
"label": {
|
||||
"name": "Regular",
|
||||
"color": "#2E7D32FF",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf7672b8-036a-45a4-8323-6a167d2731be"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18",
|
||||
"position": 17,
|
||||
"id": "bf2eb5db-bf88-44bc-a83f-7c69c38fc03f",
|
||||
"label": {
|
||||
"name": "Seafood",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "1c59a263-227a-4f43-a450-d53ca1485b36",
|
||||
"position": 18,
|
||||
"id": "14f5ca34-fcec-4847-8ee7-71b29488dc5b",
|
||||
"label": {
|
||||
"name": "Snacks",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "1c59a263-227a-4f43-a450-d53ca1485b36"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "189099a9-0033-4783-804a-ec6805e7d557",
|
||||
"position": 19,
|
||||
"id": "197f3d41-27a6-4782-a78d-60ea582108c8",
|
||||
"label": {
|
||||
"name": "Spices",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "189099a9-0033-4783-804a-ec6805e7d557"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "c28efdde-5993-4044-b824-f111f3a118ef",
|
||||
"position": 20,
|
||||
"id": "b5021331-2004-4570-a2bb-c6f364787bcc",
|
||||
"label": {
|
||||
"name": "Sweets",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c28efdde-5993-4044-b824-f111f3a118ef"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "f8438635-8211-4be8-80d0-0aa42e37a5f2",
|
||||
"labelId": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c",
|
||||
"position": 21,
|
||||
"id": "98e9ecff-d650-4717-96fe-d7744258bf43",
|
||||
"label": {
|
||||
"name": "Vegetables",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Freezer",
|
||||
"extras": {},
|
||||
"createdAt": "2024-06-05T09:49:00.404632",
|
||||
"updateAt": "2024-06-23T08:21:51.764793",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"userId": "90b03954-00e1-46de-9520-f0305022b84f",
|
||||
"id": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"recipeReferences": [],
|
||||
"labelSettings": [
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "0f63545a-606a-47ea-a784-452d45de6158",
|
||||
"position": 0,
|
||||
"id": "666b5b98-dcf6-4121-a5a6-2782f06f5f7e",
|
||||
"label": {
|
||||
"name": "Alcohol",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0f63545a-606a-47ea-a784-452d45de6158"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "0c2d6111-9837-4319-acb5-490a32979993",
|
||||
"position": 1,
|
||||
"id": "6d25fc7e-33d2-459c-ba14-7e0aaf30a522",
|
||||
"label": {
|
||||
"name": "Baked Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "0c2d6111-9837-4319-acb5-490a32979993"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "3922802c-8e8c-47d4-9c68-e60b0a1338b6",
|
||||
"position": 2,
|
||||
"id": "56402a4e-c94e-4480-9f68-87370dbda209",
|
||||
"label": {
|
||||
"name": "Beverages",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3922802c-8e8c-47d4-9c68-e60b0a1338b6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "4111bfff-d834-4e8c-88ed-5eff761e06db",
|
||||
"position": 3,
|
||||
"id": "743e9e2b-a13a-4d80-b203-431d1c23f691",
|
||||
"label": {
|
||||
"name": "Canned Goods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "4111bfff-d834-4e8c-88ed-5eff761e06db"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "24fa2836-25e8-44af-b497-ad0d428a7f78",
|
||||
"position": 4,
|
||||
"id": "93b46c6e-0542-4adf-ad9d-8942b47dd9e3",
|
||||
"label": {
|
||||
"name": "Condiments",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "24fa2836-25e8-44af-b497-ad0d428a7f78"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "20a735de-c75b-4fdb-abaf-b8d71ef192f8",
|
||||
"position": 5,
|
||||
"id": "8c6f20ff-a5e3-4c64-a1ff-aa07bbdd455a",
|
||||
"label": {
|
||||
"name": "Confectionary",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "20a735de-c75b-4fdb-abaf-b8d71ef192f8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "71178428-70aa-4491-b5b4-b8d93e7b04cf",
|
||||
"position": 6,
|
||||
"id": "02995d80-108f-4949-bd58-d04d670b388d",
|
||||
"label": {
|
||||
"name": "Dairy Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "71178428-70aa-4491-b5b4-b8d93e7b04cf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "c58ed864-b5bf-4aac-88a1-007833c706c7",
|
||||
"position": 7,
|
||||
"id": "b20c178c-e719-4159-b199-91a6dd25dcd3",
|
||||
"label": {
|
||||
"name": "Frozen Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c58ed864-b5bf-4aac-88a1-007833c706c7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "f398f1a4-ce53-42df-95d4-8a3403bb6a38",
|
||||
"position": 8,
|
||||
"id": "5ff12e47-9b84-46d2-aabf-da4165a68f65",
|
||||
"label": {
|
||||
"name": "Fruits",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "f398f1a4-ce53-42df-95d4-8a3403bb6a38"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "fd936065-3d53-4844-99df-9332f1bf0c8a",
|
||||
"position": 9,
|
||||
"id": "e0ec7da9-c0b8-4d78-a5b8-591c99d87370",
|
||||
"label": {
|
||||
"name": "Grains",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "fd936065-3d53-4844-99df-9332f1bf0c8a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf",
|
||||
"position": 10,
|
||||
"id": "3dc2d2e7-274e-40ec-8ba1-09ce1820b29b",
|
||||
"label": {
|
||||
"name": "Health Foods",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "db7b685a-4aeb-4ebd-9b64-0c14827d9eaf"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b",
|
||||
"position": 11,
|
||||
"id": "e30fa937-4bb1-4ff9-b163-2da67e2749ca",
|
||||
"label": {
|
||||
"name": "Household",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "28bbdab4-7eab-4fb2-b0e1-b0f2c10e489b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "cf136576-1929-4fc9-a3da-34c49ff58920",
|
||||
"position": 12,
|
||||
"id": "ecd715af-fafe-4d32-a376-538e476bf215",
|
||||
"label": {
|
||||
"name": "Meat",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf136576-1929-4fc9-a3da-34c49ff58920"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa",
|
||||
"position": 13,
|
||||
"id": "5ded867c-473f-456d-b0a0-83cae279df71",
|
||||
"label": {
|
||||
"name": "Meat Products",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "5b7d69d0-4d9f-48f9-96f1-8cb843227baa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "2a035661-fd5d-462c-8eb0-6b78af982e0c",
|
||||
"position": 14,
|
||||
"id": "eb88d477-cd50-4b84-a1bb-5adc077d38e5",
|
||||
"label": {
|
||||
"name": "Other",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "2a035661-fd5d-462c-8eb0-6b78af982e0c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "af147838-d114-4a92-bd0f-08f05f59bbe5",
|
||||
"position": 15,
|
||||
"id": "ab7e96e3-f8d5-4e4e-91ee-b966bd980cf0",
|
||||
"label": {
|
||||
"name": "Produce",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "af147838-d114-4a92-bd0f-08f05f59bbe5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "cf7672b8-036a-45a4-8323-6a167d2731be",
|
||||
"position": 16,
|
||||
"id": "3fcf5e5a-f8e2-4174-be79-2496a1cb505a",
|
||||
"label": {
|
||||
"name": "Regular",
|
||||
"color": "#2E7D32FF",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "cf7672b8-036a-45a4-8323-6a167d2731be"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18",
|
||||
"position": 17,
|
||||
"id": "e768c9e7-c568-44d1-a263-081d93fd1298",
|
||||
"label": {
|
||||
"name": "Seafood",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "bbcfaf8b-02e6-4c3d-98a6-6863b36bef18"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "1c59a263-227a-4f43-a450-d53ca1485b36",
|
||||
"position": 18,
|
||||
"id": "f8a78147-c6d1-4a86-b159-5f178ae72089",
|
||||
"label": {
|
||||
"name": "Snacks",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "1c59a263-227a-4f43-a450-d53ca1485b36"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "189099a9-0033-4783-804a-ec6805e7d557",
|
||||
"position": 19,
|
||||
"id": "23253f2f-bc71-4ecf-837c-d1697738b505",
|
||||
"label": {
|
||||
"name": "Spices",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "189099a9-0033-4783-804a-ec6805e7d557"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "c28efdde-5993-4044-b824-f111f3a118ef",
|
||||
"position": 20,
|
||||
"id": "706d656b-3755-46f7-8c12-c9196730baf2",
|
||||
"label": {
|
||||
"name": "Sweets",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "c28efdde-5993-4044-b824-f111f3a118ef"
|
||||
}
|
||||
},
|
||||
{
|
||||
"shoppingListId": "e9d78ff2-4b23-4b77-a3a8-464827100b46",
|
||||
"labelId": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c",
|
||||
"position": 21,
|
||||
"id": "d9d60d8d-f2de-4636-864f-d7262e24ead3",
|
||||
"label": {
|
||||
"name": "Vegetables",
|
||||
"color": "#E0E0E0",
|
||||
"groupId": "9ed7c880-3e85-4955-9318-1443d6e080fe",
|
||||
"id": "3f151d15-27f9-41c7-9dfc-2ae1024b1c7c"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"next": null,
|
||||
"previous": null
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
# serializer version: 1
|
||||
# name: test_entities[todo.mealie_freezer-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'todo',
|
||||
'entity_category': None,
|
||||
'entity_id': 'todo.mealie_freezer',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Freezer',
|
||||
'platform': 'mealie',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
'translation_key': 'shopping_list',
|
||||
'unique_id': 'bf1c62fe-4941-4332-9886-e54e88dbdba0_e9d78ff2-4b23-4b77-a3a8-464827100b46',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[todo.mealie_freezer-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mealie Freezer',
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'todo.mealie_freezer',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[todo.mealie_special_groceries-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'todo',
|
||||
'entity_category': None,
|
||||
'entity_id': 'todo.mealie_special_groceries',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Special groceries',
|
||||
'platform': 'mealie',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
'translation_key': 'shopping_list',
|
||||
'unique_id': 'bf1c62fe-4941-4332-9886-e54e88dbdba0_f8438635-8211-4be8-80d0-0aa42e37a5f2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[todo.mealie_special_groceries-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mealie Special groceries',
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'todo.mealie_special_groceries',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[todo.mealie_supermarket-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'todo',
|
||||
'entity_category': None,
|
||||
'entity_id': 'todo.mealie_supermarket',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Supermarket',
|
||||
'platform': 'mealie',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
'translation_key': 'shopping_list',
|
||||
'unique_id': 'bf1c62fe-4941-4332-9886-e54e88dbdba0_27edbaab-2ec6-441f-8490-0283ea77585f',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[todo.mealie_supermarket-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mealie Supermarket',
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'todo.mealie_supermarket',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
||||
# name: test_get_todo_list_items
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mealie Supermarket',
|
||||
'supported_features': <TodoListEntityFeature: 15>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'todo.mealie_supermarket',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
from datetime import date
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import AsyncMock
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
|
@ -40,7 +41,8 @@ async def test_entities(
|
|||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the API returns the calendar."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
with patch("homeassistant.components.mealie.PLATFORMS", [Platform.CALENDAR]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ async def test_load_unload_entry(
|
|||
(MealieAuthenticationError, ConfigEntryState.SETUP_ERROR),
|
||||
],
|
||||
)
|
||||
async def test_initialization_failure(
|
||||
async def test_mealplan_initialization_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
|
@ -68,3 +68,47 @@ async def test_initialization_failure(
|
|||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_config_entry.state is state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exc", "state"),
|
||||
[
|
||||
(MealieConnectionError, ConfigEntryState.SETUP_RETRY),
|
||||
(MealieAuthenticationError, ConfigEntryState.SETUP_ERROR),
|
||||
],
|
||||
)
|
||||
async def test_shoppingitems_initialization_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
exc: Exception,
|
||||
state: ConfigEntryState,
|
||||
) -> None:
|
||||
"""Test initialization failure."""
|
||||
mock_mealie_client.get_shopping_items.side_effect = exc
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_config_entry.state is state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exc", "state"),
|
||||
[
|
||||
(MealieConnectionError, ConfigEntryState.SETUP_ERROR),
|
||||
(MealieAuthenticationError, ConfigEntryState.SETUP_ERROR),
|
||||
],
|
||||
)
|
||||
async def test_shoppinglists_initialization_failure(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
exc: Exception,
|
||||
state: ConfigEntryState,
|
||||
) -> None:
|
||||
"""Test initialization failure."""
|
||||
mock_mealie_client.get_shopping_lists.side_effect = exc
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_config_entry.state is state
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
"""Tests for the Mealie todo."""
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from aiomealie.exceptions import MealieError
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.todo import DOMAIN as TODO_DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
async def test_entities(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test todo entities."""
|
||||
with patch("homeassistant.components.mealie.PLATFORMS", [Platform.TODO]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_add_todo_list_item(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for adding a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"add_item",
|
||||
{"item": "Soda"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_mealie_client.add_shopping_item.assert_called_once()
|
||||
|
||||
|
||||
async def test_add_todo_list_item_error(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for failing to add a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
mock_mealie_client.add_shopping_item.side_effect = MealieError
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"add_item",
|
||||
{"item": "Soda"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_todo_list_item(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for updating a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"update_item",
|
||||
{"item": "aubergine", "rename": "Eggplant", "status": "completed"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_mealie_client.update_shopping_item.assert_called_once()
|
||||
|
||||
|
||||
async def test_update_todo_list_item_error(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for failing to update a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
mock_mealie_client.update_shopping_item.side_effect = MealieError
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"update_item",
|
||||
{"item": "aubergine", "rename": "Eggplant", "status": "completed"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_delete_todo_list_item(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for deleting a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"remove_item",
|
||||
{"item": "aubergine"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_mealie_client.delete_shopping_item.assert_called_once()
|
||||
|
||||
|
||||
async def test_delete_todo_list_item_error(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test for failing to delete a To-do Item."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
mock_mealie_client.delete_shopping_item = AsyncMock()
|
||||
mock_mealie_client.delete_shopping_item.side_effect = MealieError
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
TODO_DOMAIN,
|
||||
"remove_item",
|
||||
{"item": "aubergine"},
|
||||
target={ATTR_ENTITY_ID: "todo.mealie_supermarket"},
|
||||
blocking=True,
|
||||
)
|
Loading…
Reference in New Issue