Add due date and description to CalDAV To-do (#104656)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
parent
1522118453
commit
af2f8699b7
2 changed files with 142 additions and 24 deletions
|
@ -2,10 +2,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from datetime import date, datetime, timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import cast
|
||||
from typing import Any, cast
|
||||
|
||||
import caldav
|
||||
from caldav.lib.error import DAVError, NotFoundError
|
||||
|
@ -21,6 +21,7 @@ from homeassistant.config_entries import ConfigEntry
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .api import async_get_calendars, get_attr_value
|
||||
from .const import DOMAIN
|
||||
|
@ -71,6 +72,12 @@ def _todo_item(resource: caldav.CalendarObjectResource) -> TodoItem | None:
|
|||
or (summary := get_attr_value(todo, "summary")) is None
|
||||
):
|
||||
return None
|
||||
due: date | datetime | None = None
|
||||
if due_value := get_attr_value(todo, "due"):
|
||||
if isinstance(due_value, datetime):
|
||||
due = dt_util.as_local(due_value)
|
||||
elif isinstance(due_value, date):
|
||||
due = due_value
|
||||
return TodoItem(
|
||||
uid=uid,
|
||||
summary=summary,
|
||||
|
@ -78,9 +85,28 @@ def _todo_item(resource: caldav.CalendarObjectResource) -> TodoItem | None:
|
|||
get_attr_value(todo, "status") or "",
|
||||
TodoItemStatus.NEEDS_ACTION,
|
||||
),
|
||||
due=due,
|
||||
description=get_attr_value(todo, "description"),
|
||||
)
|
||||
|
||||
|
||||
def _to_ics_fields(item: TodoItem) -> dict[str, Any]:
|
||||
"""Convert a TodoItem to the set of add or update arguments."""
|
||||
item_data: dict[str, Any] = {}
|
||||
if summary := item.summary:
|
||||
item_data["summary"] = summary
|
||||
if status := item.status:
|
||||
item_data["status"] = TODO_STATUS_MAP_INV.get(status, "NEEDS-ACTION")
|
||||
if due := item.due:
|
||||
if isinstance(due, datetime):
|
||||
item_data["due"] = dt_util.as_utc(due).strftime("%Y%m%dT%H%M%SZ")
|
||||
else:
|
||||
item_data["due"] = due.strftime("%Y%m%d")
|
||||
if description := item.description:
|
||||
item_data["description"] = description
|
||||
return item_data
|
||||
|
||||
|
||||
class WebDavTodoListEntity(TodoListEntity):
|
||||
"""CalDAV To-do list entity."""
|
||||
|
||||
|
@ -89,6 +115,9 @@ class WebDavTodoListEntity(TodoListEntity):
|
|||
TodoListEntityFeature.CREATE_TODO_ITEM
|
||||
| TodoListEntityFeature.UPDATE_TODO_ITEM
|
||||
| TodoListEntityFeature.DELETE_TODO_ITEM
|
||||
| TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
|
||||
| TodoListEntityFeature.SET_DUE_DATETIME_ON_ITEM
|
||||
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
|
||||
)
|
||||
|
||||
def __init__(self, calendar: caldav.Calendar, config_entry_id: str) -> None:
|
||||
|
@ -116,13 +145,7 @@ class WebDavTodoListEntity(TodoListEntity):
|
|||
"""Add an item to the To-do list."""
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(
|
||||
self._calendar.save_todo,
|
||||
summary=item.summary,
|
||||
status=TODO_STATUS_MAP_INV.get(
|
||||
item.status or TodoItemStatus.NEEDS_ACTION, "NEEDS-ACTION"
|
||||
),
|
||||
),
|
||||
partial(self._calendar.save_todo, **_to_ics_fields(item)),
|
||||
)
|
||||
except (requests.ConnectionError, DAVError) as err:
|
||||
raise HomeAssistantError(f"CalDAV save error: {err}") from err
|
||||
|
@ -139,10 +162,7 @@ class WebDavTodoListEntity(TodoListEntity):
|
|||
except (requests.ConnectionError, DAVError) as err:
|
||||
raise HomeAssistantError(f"CalDAV lookup error: {err}") from err
|
||||
vtodo = todo.icalendar_component # type: ignore[attr-defined]
|
||||
if item.summary:
|
||||
vtodo["summary"] = item.summary
|
||||
if item.status:
|
||||
vtodo["status"] = TODO_STATUS_MAP_INV.get(item.status, "NEEDS-ACTION")
|
||||
vtodo.update(**_to_ics_fields(item))
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue