1 """Todo platform for Mealie."""
3 from __future__
import annotations
5 from aiomealie
import MealieError, MutateShoppingItem, ShoppingItem, ShoppingList
12 TodoListEntityFeature,
19 from .const
import DOMAIN
20 from .coordinator
import MealieConfigEntry, MealieShoppingListCoordinator
21 from .entity
import MealieEntity
24 False: TodoItemStatus.NEEDS_ACTION,
25 True: TodoItemStatus.COMPLETED,
27 TODO_STATUS_MAP_INV = {v: k
for k, v
in TODO_STATUS_MAP.items()}
31 """Convert Mealie shopping list items into a TodoItem."""
36 status=TODO_STATUS_MAP.get(
38 TodoItemStatus.NEEDS_ACTION,
47 entry: MealieConfigEntry,
48 async_add_entities: AddEntitiesCallback,
50 """Set up the todo platform for entity."""
51 coordinator = entry.runtime_data.shoppinglist_coordinator
53 added_lists: set[str] = set()
55 assert entry.unique_id
is not None
57 def _async_delete_entities(lists: set[str]) ->
None:
58 """Delete entities for removed shopping lists."""
59 entity_registry = er.async_get(hass)
61 entity_id = entity_registry.async_get_entity_id(
62 TODO_DOMAIN, DOMAIN, f
"{entry.unique_id}_{list_id}"
65 entity_registry.async_remove(entity_id)
67 def _async_entity_listener() -> None:
68 """Handle additions/deletions of shopping lists."""
69 received_lists = set(coordinator.data)
70 new_lists = received_lists - added_lists
71 removed_lists = added_lists - received_lists
75 for shopping_list_id
in new_lists
77 added_lists.update(new_lists)
79 _async_delete_entities(removed_lists)
81 coordinator.async_add_listener(_async_entity_listener)
82 _async_entity_listener()
86 """A todo list entity."""
88 _attr_supported_features = (
89 TodoListEntityFeature.CREATE_TODO_ITEM
90 | TodoListEntityFeature.UPDATE_TODO_ITEM
91 | TodoListEntityFeature.DELETE_TODO_ITEM
92 | TodoListEntityFeature.MOVE_TODO_ITEM
95 _attr_translation_key =
"shopping_list"
97 coordinator: MealieShoppingListCoordinator
100 self, coordinator: MealieShoppingListCoordinator, shopping_list_id: str
102 """Create the todo entity."""
103 super().
__init__(coordinator, shopping_list_id)
109 """Get the shopping list."""
110 return self.coordinator.data[self.
_shopping_list_id_shopping_list_id].shopping_list
114 """Get the shopping items for this list."""
119 """Get the current set of To-do items."""
123 """Add an item to the list."""
128 new_shopping_item = MutateShoppingItem(
130 note=item.summary.strip()
if item.summary
else item.summary,
134 await self.coordinator.client.add_shopping_item(new_shopping_item)
135 except MealieError
as exception:
137 translation_domain=DOMAIN,
138 translation_key=
"add_item_error",
139 translation_placeholders={
147 """Update an item on the list."""
150 for items
in list_items:
151 if items.item_id == item.uid:
152 position = items.position
155 list_item: ShoppingItem |
None = next(
156 (x
for x
in list_items
if x.item_id == item.uid),
None
161 translation_domain=DOMAIN,
162 translation_key=
"item_not_found_error",
163 translation_placeholders={
"shopping_list_item": item.uid
or ""},
166 udpdate_shopping_item = MutateShoppingItem(
167 item_id=list_item.item_id,
168 list_id=list_item.list_id,
170 display=list_item.display,
171 checked=item.status == TodoItemStatus.COMPLETED,
172 position=list_item.position,
173 is_food=list_item.is_food,
174 disable_amount=list_item.disable_amount,
175 quantity=list_item.quantity,
176 label_id=list_item.label_id,
177 food_id=list_item.food_id,
178 unit_id=list_item.unit_id,
181 stripped_item_summary = item.summary.strip()
if item.summary
else item.summary
183 if list_item.display.strip() != stripped_item_summary:
184 udpdate_shopping_item.note = stripped_item_summary
185 udpdate_shopping_item.position = position
186 udpdate_shopping_item.is_food =
False
187 udpdate_shopping_item.food_id =
None
188 udpdate_shopping_item.quantity = 0.0
189 udpdate_shopping_item.checked = item.status == TodoItemStatus.COMPLETED
192 await self.coordinator.client.update_shopping_item(
193 list_item.item_id, udpdate_shopping_item
195 except MealieError
as exception:
197 translation_domain=DOMAIN,
198 translation_key=
"update_item_error",
199 translation_placeholders={
207 """Delete items from the list."""
210 await self.coordinator.client.delete_shopping_item(uid)
211 except MealieError
as exception:
213 translation_domain=DOMAIN,
214 translation_key=
"delete_item_error",
215 translation_placeholders={
223 self, uid: str, previous_uid: str |
None =
None
225 """Re-order an item on the list."""
226 if uid == previous_uid:
228 list_items: list[ShoppingItem] = self.
shopping_itemsshopping_items
230 item_idx = {itm.item_id: idx
for idx, itm
in enumerate(list_items)}
231 if uid
not in item_idx:
233 translation_domain=DOMAIN,
234 translation_key=
"item_not_found_error",
235 translation_placeholders={
"shopping_list_item": uid},
237 if previous_uid
and previous_uid
not in item_idx:
239 translation_domain=DOMAIN,
240 translation_key=
"item_not_found_error",
241 translation_placeholders={
"shopping_list_item": previous_uid},
243 dst_idx = item_idx[previous_uid] + 1
if previous_uid
else 0
244 src_idx = item_idx[uid]
245 src_item = list_items.pop(src_idx)
246 if dst_idx > src_idx:
248 list_items.insert(dst_idx, src_item)
250 for position, item
in enumerate(list_items):
251 mutate_shopping_item = MutateShoppingItem()
252 mutate_shopping_item.list_id = item.list_id
253 mutate_shopping_item.item_id = item.item_id
254 mutate_shopping_item.position = position
255 mutate_shopping_item.is_food = item.is_food
256 mutate_shopping_item.quantity = item.quantity
257 mutate_shopping_item.label_id = item.label_id
258 mutate_shopping_item.note = item.note
259 mutate_shopping_item.checked = item.checked
262 mutate_shopping_item.food_id = item.food_id
263 mutate_shopping_item.unit_id = item.unit_id
265 await self.coordinator.client.update_shopping_item(
266 mutate_shopping_item.item_id, mutate_shopping_item
273 """Return False if shopping list no longer available."""
274 return super().available
and self.
_shopping_list_id_shopping_list_id
in self.coordinator.data
TodoItem _convert_api_item(ShoppingItem item)
None async_setup_entry(HomeAssistant hass, MealieConfigEntry entry, AddEntitiesCallback async_add_entities)