1 """Todo platform for the Habitica integration."""
3 from __future__
import annotations
6 from enum
import StrEnum
7 from typing
import TYPE_CHECKING
9 from aiohttp
import ClientResponseError
16 TodoListEntityFeature,
24 from .const
import ASSETS_URL, DOMAIN
25 from .coordinator
import HabiticaDataUpdateCoordinator
26 from .entity
import HabiticaBase
27 from .types
import HabiticaConfigEntry, HabiticaTaskType
28 from .util
import next_due_date
34 """Habitica Entities."""
44 config_entry: HabiticaConfigEntry,
45 async_add_entities: AddEntitiesCallback,
47 """Set up the sensor from a config entry created in the integrations UI."""
48 coordinator = config_entry.runtime_data
59 """Representation of Habitica task lists."""
63 coordinator: HabiticaDataUpdateCoordinator,
65 """Initialize HabiticaTodoListEntity."""
70 """Delete Habitica tasks."""
71 if len(uids) > 1
and self.
entity_descriptionentity_description.key
is HabiticaTodoList.TODOS:
73 await self.coordinator.api.tasks.clearCompletedTodos.post()
74 except ClientResponseError
as e:
76 translation_domain=DOMAIN,
77 translation_key=
"delete_completed_todos_failed",
82 await self.coordinator.api.tasks[task_id].
delete()
83 except ClientResponseError
as e:
85 translation_domain=DOMAIN,
86 translation_key=f
"delete_{self.entity_description.key}_failed",
92 self, uid: str, previous_uid: str |
None =
None
94 """Move an item in the To-do list."""
101 next(item
for item
in self.
todo_itemstodo_items
if item.uid == previous_uid)
109 await self.coordinator.api.tasks[uid].move.to[
str(pos)].
post()
111 except ClientResponseError
as e:
113 translation_domain=DOMAIN,
114 translation_key=f
"move_{self.entity_description.key}_item_failed",
115 translation_placeholders={
"pos":
str(pos)},
119 tasks = self.coordinator.data.tasks
121 tasks.index(next(task
for task
in tasks
if task[
"id"] == previous_uid))
126 old_pos = tasks.index(next(task
for task
in tasks
if task[
"id"] == uid))
127 tasks.insert(new_pos, tasks.pop(old_pos))
131 """Update a Habitica todo."""
132 refresh_required =
False
134 (task
for task
in (self.
todo_itemstodo_items
or [])
if task.uid == item.uid),
144 and item.due
is not None
146 date = item.due.isoformat()
151 item.summary != current_item.summary
152 or item.description != current_item.description
153 or item.due != current_item.due
156 await self.coordinator.api.tasks[item.uid].put(
158 notes=item.description
or "",
161 refresh_required =
True
162 except ClientResponseError
as e:
164 translation_domain=DOMAIN,
165 translation_key=f
"update_{self.entity_description.key}_item_failed",
166 translation_placeholders={
"name": item.summary
or ""},
172 current_item.status
is TodoItemStatus.NEEDS_ACTION
173 and item.status == TodoItemStatus.COMPLETED
176 await self.coordinator.api.tasks[item.uid].score[
"up"].
post()
178 refresh_required =
True
180 current_item.status
is TodoItemStatus.COMPLETED
181 and item.status == TodoItemStatus.NEEDS_ACTION
184 await self.coordinator.api.tasks[item.uid].score[
"down"].
post()
186 refresh_required =
True
190 except ClientResponseError
as e:
192 translation_domain=DOMAIN,
193 translation_key=f
"score_{self.entity_description.key}_item_failed",
194 translation_placeholders={
"name": item.summary
or ""},
197 if score_result
and (drop := score_result.get(
"_tmp", {}).
get(
"drop",
False)):
199 f
"![{drop["key
"]}]({ASSETS_URL}Pet_{drop["type
"]}_{drop["key
"]}.png)\n"
202 persistent_notification.async_create(
203 self.
hasshasshass, message=msg, title=
"Habitica"
210 """List of Habitica todos."""
212 _attr_supported_features = (
213 TodoListEntityFeature.CREATE_TODO_ITEM
214 | TodoListEntityFeature.DELETE_TODO_ITEM
215 | TodoListEntityFeature.UPDATE_TODO_ITEM
216 | TodoListEntityFeature.MOVE_TODO_ITEM
217 | TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
218 | TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
221 key=HabiticaTodoList.TODOS,
222 translation_key=HabiticaTodoList.TODOS,
227 """Return the todo items."""
233 summary=task[
"text"],
234 description=task[
"notes"],
237 datetime.datetime.fromisoformat(task[
"date"])
243 TodoItemStatus.NEEDS_ACTION
244 if not task[
"completed"]
245 else TodoItemStatus.COMPLETED
248 for task
in self.coordinator.data.tasks
249 if task[
"type"] == HabiticaTaskType.TODO
254 """Create a Habitica todo."""
257 await self.coordinator.api.tasks.user.post(
259 type=HabiticaTaskType.TODO,
260 notes=item.description,
261 date=item.due.isoformat()
if item.due
else None,
263 except ClientResponseError
as e:
265 translation_domain=DOMAIN,
266 translation_key=f
"create_{self.entity_description.key}_item_failed",
267 translation_placeholders={
"name": item.summary
or ""},
274 """List of Habitica dailies."""
276 _attr_supported_features = (
277 TodoListEntityFeature.UPDATE_TODO_ITEM
278 | TodoListEntityFeature.MOVE_TODO_ITEM
279 | TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
280 | TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
283 key=HabiticaTodoList.DAILIES,
284 translation_key=HabiticaTodoList.DAILIES,
289 """Return the dailies.
291 dailies don't have a date, but we still can show the next due date,
292 which is a calculated value based on recurrence of the task.
293 If a task is a yesterdaily, the due date is the last time
294 a new day has been started. This allows to check off dailies from yesterday,
295 that have been completed but forgotten to mark as completed before resetting the dailies.
296 Changes of the date input field in Home Assistant will be ignored.
299 last_cron = self.coordinator.data.user[
"lastCron"]
305 summary=task[
"text"],
306 description=task[
"notes"],
309 TodoItemStatus.COMPLETED
311 else TodoItemStatus.NEEDS_ACTION
314 for task
in self.coordinator.data.tasks
315 if task[
"type"] == HabiticaTaskType.DAILY
None __init__(self, HabiticaDataUpdateCoordinator coordinator)
None async_delete_todo_items(self, list[str] uids)
None async_move_todo_item(self, str uid, str|None previous_uid=None)
None async_update_todo_item(self, TodoItem item)
list[TodoItem] todo_items(self)
None async_create_todo_item(self, TodoItem item)
list[TodoItem] todo_items(self)
list[TodoItem]|None todo_items(self)
None async_request_refresh(self)
web.Response post(self, web.Request request, str config_key)
web.Response get(self, web.Request request, str config_key)
web.Response delete(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, HabiticaConfigEntry config_entry, AddEntitiesCallback async_add_entities)
datetime.date|None next_due_date(dict[str, Any] task, str last_cron)