Home Assistant Unofficial Reference 2024.12.1
todo.py
Go to the documentation of this file.
1 """Todo platform for the Bring! integration."""
2 
3 from __future__ import annotations
4 
5 from typing import TYPE_CHECKING
6 import uuid
7 
8 from bring_api import (
9  BringItem,
10  BringItemOperation,
11  BringNotificationType,
12  BringRequestException,
13 )
14 import voluptuous as vol
15 
17  TodoItem,
18  TodoItemStatus,
19  TodoListEntity,
20  TodoListEntityFeature,
21 )
22 from homeassistant.core import HomeAssistant
23 from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
24 from homeassistant.helpers import config_validation as cv, entity_platform
25 from homeassistant.helpers.entity_platform import AddEntitiesCallback
26 
27 from . import BringConfigEntry
28 from .const import (
29  ATTR_ITEM_NAME,
30  ATTR_NOTIFICATION_TYPE,
31  DOMAIN,
32  SERVICE_PUSH_NOTIFICATION,
33 )
34 from .coordinator import BringData, BringDataUpdateCoordinator
35 from .entity import BringBaseEntity
36 
37 
39  hass: HomeAssistant,
40  config_entry: BringConfigEntry,
41  async_add_entities: AddEntitiesCallback,
42 ) -> None:
43  """Set up the sensor from a config entry created in the integrations UI."""
44  coordinator = config_entry.runtime_data
45 
48  coordinator,
49  bring_list=bring_list,
50  )
51  for bring_list in coordinator.data.values()
52  )
53 
54  platform = entity_platform.async_get_current_platform()
55 
56  platform.async_register_entity_service(
57  SERVICE_PUSH_NOTIFICATION,
58  {
59  vol.Required(ATTR_NOTIFICATION_TYPE): vol.All(
60  vol.Upper, cv.enum(BringNotificationType)
61  ),
62  vol.Optional(ATTR_ITEM_NAME): cv.string,
63  },
64  "async_send_message",
65  )
66 
67 
69  """A To-do List representation of the Bring! Shopping List."""
70 
71  _attr_translation_key = "shopping_list"
72  _attr_name = None
73  _attr_supported_features = (
74  TodoListEntityFeature.CREATE_TODO_ITEM
75  | TodoListEntityFeature.UPDATE_TODO_ITEM
76  | TodoListEntityFeature.DELETE_TODO_ITEM
77  | TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
78  )
79 
80  def __init__(
81  self, coordinator: BringDataUpdateCoordinator, bring_list: BringData
82  ) -> None:
83  """Initialize the entity."""
84  super().__init__(coordinator, bring_list)
85  self._attr_unique_id_attr_unique_id = f"{coordinator.config_entry.unique_id}_{self._list_uuid}"
86 
87  @property
88  def todo_items(self) -> list[TodoItem]:
89  """Return the todo items."""
90  return [
91  *(
92  TodoItem(
93  uid=item["uuid"],
94  summary=item["itemId"],
95  description=item["specification"] or "",
96  status=TodoItemStatus.NEEDS_ACTION,
97  )
98  for item in self.bring_listbring_list["purchase"]
99  ),
100  *(
101  TodoItem(
102  uid=item["uuid"],
103  summary=item["itemId"],
104  description=item["specification"] or "",
105  status=TodoItemStatus.COMPLETED,
106  )
107  for item in self.bring_listbring_list["recently"]
108  ),
109  ]
110 
111  @property
112  def bring_list(self) -> BringData:
113  """Return the bring list."""
114  return self.coordinator.data[self._list_uuid_list_uuid]
115 
116  async def async_create_todo_item(self, item: TodoItem) -> None:
117  """Add an item to the To-do list."""
118  try:
119  await self.coordinator.bring.save_item(
120  self.bring_listbring_list["listUuid"],
121  item.summary or "",
122  item.description or "",
123  str(uuid.uuid4()),
124  )
125  except BringRequestException as e:
126  raise HomeAssistantError(
127  translation_domain=DOMAIN,
128  translation_key="todo_save_item_failed",
129  translation_placeholders={"name": item.summary or ""},
130  ) from e
131 
132  await self.coordinator.async_refresh()
133 
134  async def async_update_todo_item(self, item: TodoItem) -> None:
135  """Update an item to the To-do list.
136 
137  Bring has an internal 'recent' list which we want to use instead of a todo list
138  status, therefore completed todo list items are matched to the recent list and
139  pending items to the purchase list.
140 
141  This results in following behaviour:
142 
143  - Completed items will move to the "completed" section in home assistant todo
144  list and get moved to the recently list in bring
145  - Bring shows some odd behaviour when renaming items. This is because Bring
146  did not have unique identifiers for items in the past and this is still
147  a relic from it. Therefore the name is not to be changed! Should a name
148  be changed anyway, the item will be deleted and a new item will be created
149  instead and no update for this item is performed and on the next cloud pull
150  update, it will get cleared and replaced seamlessly.
151  """
152 
153  bring_list = self.bring_listbring_list
154 
155  bring_purchase_item = next(
156  (i for i in bring_list["purchase"] if i["uuid"] == item.uid),
157  None,
158  )
159 
160  bring_recently_item = next(
161  (i for i in bring_list["recently"] if i["uuid"] == item.uid),
162  None,
163  )
164 
165  current_item = bring_purchase_item or bring_recently_item
166 
167  if TYPE_CHECKING:
168  assert item.uid
169  assert current_item
170 
171  if item.summary == current_item["itemId"]:
172  try:
173  await self.coordinator.bring.batch_update_list(
174  bring_list["listUuid"],
175  BringItem(
176  itemId=item.summary or "",
177  spec=item.description or "",
178  uuid=item.uid,
179  ),
180  BringItemOperation.ADD
181  if item.status == TodoItemStatus.NEEDS_ACTION
182  else BringItemOperation.COMPLETE,
183  )
184  except BringRequestException as e:
185  raise HomeAssistantError(
186  translation_domain=DOMAIN,
187  translation_key="todo_update_item_failed",
188  translation_placeholders={"name": item.summary or ""},
189  ) from e
190  else:
191  try:
192  await self.coordinator.bring.batch_update_list(
193  bring_list["listUuid"],
194  [
195  BringItem(
196  itemId=current_item["itemId"],
197  spec=item.description or "",
198  uuid=item.uid,
199  operation=BringItemOperation.REMOVE,
200  ),
201  BringItem(
202  itemId=item.summary or "",
203  spec=item.description or "",
204  uuid=str(uuid.uuid4()),
205  operation=BringItemOperation.ADD
206  if item.status == TodoItemStatus.NEEDS_ACTION
207  else BringItemOperation.COMPLETE,
208  ),
209  ],
210  )
211 
212  except BringRequestException as e:
213  raise HomeAssistantError(
214  translation_domain=DOMAIN,
215  translation_key="todo_rename_item_failed",
216  translation_placeholders={"name": item.summary or ""},
217  ) from e
218 
219  await self.coordinator.async_refresh()
220 
221  async def async_delete_todo_items(self, uids: list[str]) -> None:
222  """Delete an item from the To-do list."""
223 
224  try:
225  await self.coordinator.bring.batch_update_list(
226  self.bring_listbring_list["listUuid"],
227  [
228  BringItem(
229  itemId=uid,
230  spec="",
231  uuid=uid,
232  )
233  for uid in uids
234  ],
235  BringItemOperation.REMOVE,
236  )
237  except BringRequestException as e:
238  raise HomeAssistantError(
239  translation_domain=DOMAIN,
240  translation_key="todo_delete_item_failed",
241  translation_placeholders={"count": str(len(uids))},
242  ) from e
243 
244  await self.coordinator.async_refresh()
245 
247  self,
248  message: BringNotificationType,
249  item: str | None = None,
250  ) -> None:
251  """Send a push notification to members of a shared bring list."""
252 
253  try:
254  await self.coordinator.bring.notify(self._list_uuid_list_uuid, message, item or None)
255  except BringRequestException as e:
256  raise HomeAssistantError(
257  translation_domain=DOMAIN,
258  translation_key="notify_request_failed",
259  ) from e
260  except ValueError as e:
262  translation_domain=DOMAIN,
263  translation_key="notify_missing_argument_item",
264  translation_placeholders={
265  "service": f"{DOMAIN}.{SERVICE_PUSH_NOTIFICATION}",
266  },
267  ) from e
None async_update_todo_item(self, TodoItem item)
Definition: todo.py:134
None async_delete_todo_items(self, list[str] uids)
Definition: todo.py:221
None __init__(self, BringDataUpdateCoordinator coordinator, BringData bring_list)
Definition: todo.py:82
None async_send_message(self, BringNotificationType message, str|None item=None)
Definition: todo.py:250
None async_create_todo_item(self, TodoItem item)
Definition: todo.py:116
None async_setup_entry(HomeAssistant hass, BringConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: todo.py:42