mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Update caldav to use an DataUpdateCoordinator for fetching data (#102089)
This commit is contained in:
parent
a4c31f63bf
commit
4599b788b4
@ -24,12 +24,16 @@ from homeassistant.const import (
|
|||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
CONF_VERIFY_SSL,
|
CONF_VERIFY_SSL,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import generate_entity_id
|
from homeassistant.helpers.entity import generate_entity_id
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import Throttle, dt as dt_util
|
from homeassistant.helpers.update_coordinator import (
|
||||||
|
CoordinatorEntity,
|
||||||
|
DataUpdateCoordinator,
|
||||||
|
)
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -103,16 +107,14 @@ def setup_platform(
|
|||||||
name = cust_calendar[CONF_NAME]
|
name = cust_calendar[CONF_NAME]
|
||||||
device_id = f"{cust_calendar[CONF_CALENDAR]} {cust_calendar[CONF_NAME]}"
|
device_id = f"{cust_calendar[CONF_CALENDAR]} {cust_calendar[CONF_NAME]}"
|
||||||
entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
|
entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
|
||||||
calendar_devices.append(
|
coordinator = CalDavUpdateCoordinator(
|
||||||
WebDavCalendarEntity(
|
hass,
|
||||||
name=name,
|
|
||||||
calendar=calendar,
|
calendar=calendar,
|
||||||
entity_id=entity_id,
|
|
||||||
days=days,
|
days=days,
|
||||||
all_day=True,
|
include_all_day=True,
|
||||||
search=cust_calendar[CONF_SEARCH],
|
search=cust_calendar[CONF_SEARCH],
|
||||||
)
|
)
|
||||||
)
|
calendar_devices.append(WebDavCalendarEntity(name, entity_id, coordinator))
|
||||||
|
|
||||||
# Create a default calendar if there was no custom one for all calendars
|
# Create a default calendar if there was no custom one for all calendars
|
||||||
# that support events.
|
# that support events.
|
||||||
@ -130,24 +132,26 @@ def setup_platform(
|
|||||||
name = calendar.name
|
name = calendar.name
|
||||||
device_id = calendar.name
|
device_id = calendar.name
|
||||||
entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
|
entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id, hass=hass)
|
||||||
calendar_devices.append(
|
coordinator = CalDavUpdateCoordinator(
|
||||||
WebDavCalendarEntity(name, calendar, entity_id, days)
|
hass,
|
||||||
|
calendar=calendar,
|
||||||
|
days=days,
|
||||||
|
include_all_day=False,
|
||||||
|
search=None,
|
||||||
)
|
)
|
||||||
|
calendar_devices.append(WebDavCalendarEntity(name, entity_id, coordinator))
|
||||||
|
|
||||||
add_entities(calendar_devices, True)
|
add_entities(calendar_devices, True)
|
||||||
|
|
||||||
|
|
||||||
class WebDavCalendarEntity(CalendarEntity):
|
class WebDavCalendarEntity(
|
||||||
|
CoordinatorEntity["CalDavUpdateCoordinator"], CalendarEntity
|
||||||
|
):
|
||||||
"""A device for getting the next Task from a WebDav Calendar."""
|
"""A device for getting the next Task from a WebDav Calendar."""
|
||||||
|
|
||||||
def __init__(self, name, calendar, entity_id, days, all_day=False, search=None):
|
def __init__(self, name, entity_id, coordinator):
|
||||||
"""Create the WebDav Calendar Event Device."""
|
"""Create the WebDav Calendar Event Device."""
|
||||||
self.data = WebDavCalendarData(
|
super().__init__(coordinator)
|
||||||
calendar=calendar,
|
|
||||||
days=days,
|
|
||||||
include_all_day=all_day,
|
|
||||||
search=search,
|
|
||||||
)
|
|
||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
self._event: CalendarEvent | None = None
|
self._event: CalendarEvent | None = None
|
||||||
self._attr_name = name
|
self._attr_name = name
|
||||||
@ -161,31 +165,42 @@ class WebDavCalendarEntity(CalendarEntity):
|
|||||||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
|
||||||
) -> list[CalendarEvent]:
|
) -> list[CalendarEvent]:
|
||||||
"""Get all events in a specific time frame."""
|
"""Get all events in a specific time frame."""
|
||||||
return await self.data.async_get_events(hass, start_date, end_date)
|
return await self.coordinator.async_get_events(hass, start_date, end_date)
|
||||||
|
|
||||||
def update(self) -> None:
|
@callback
|
||||||
|
def _handle_coordinator_update(self) -> None:
|
||||||
"""Update event data."""
|
"""Update event data."""
|
||||||
self.data.update()
|
self._event = self.coordinator.data
|
||||||
self._event = self.data.event
|
|
||||||
self._attr_extra_state_attributes = {
|
self._attr_extra_state_attributes = {
|
||||||
"offset_reached": is_offset_reached(
|
"offset_reached": is_offset_reached(
|
||||||
self._event.start_datetime_local, self.data.offset
|
self._event.start_datetime_local, self.coordinator.offset
|
||||||
)
|
)
|
||||||
if self._event
|
if self._event
|
||||||
else False
|
else False
|
||||||
}
|
}
|
||||||
|
super()._handle_coordinator_update()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""When entity is added to hass update state from existing coordinator data."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self._handle_coordinator_update()
|
||||||
|
|
||||||
|
|
||||||
class WebDavCalendarData:
|
class CalDavUpdateCoordinator(DataUpdateCoordinator[CalendarEvent | None]):
|
||||||
"""Class to utilize the calendar dav client object to get next event."""
|
"""Class to utilize the calendar dav client object to get next event."""
|
||||||
|
|
||||||
def __init__(self, calendar, days, include_all_day, search):
|
def __init__(self, hass, calendar, days, include_all_day, search):
|
||||||
"""Set up how we are going to search the WebDav calendar."""
|
"""Set up how we are going to search the WebDav calendar."""
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name=f"CalDAV {calendar.name}",
|
||||||
|
update_interval=MIN_TIME_BETWEEN_UPDATES,
|
||||||
|
)
|
||||||
self.calendar = calendar
|
self.calendar = calendar
|
||||||
self.days = days
|
self.days = days
|
||||||
self.include_all_day = include_all_day
|
self.include_all_day = include_all_day
|
||||||
self.search = search
|
self.search = search
|
||||||
self.event = None
|
|
||||||
self.offset = None
|
self.offset = None
|
||||||
|
|
||||||
async def async_get_events(
|
async def async_get_events(
|
||||||
@ -222,19 +237,21 @@ class WebDavCalendarData:
|
|||||||
|
|
||||||
return event_list
|
return event_list
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
async def _async_update_data(self) -> CalendarEvent | None:
|
||||||
def update(self):
|
|
||||||
"""Get the latest data."""
|
"""Get the latest data."""
|
||||||
start_of_today = dt_util.start_of_local_day()
|
start_of_today = dt_util.start_of_local_day()
|
||||||
start_of_tomorrow = dt_util.start_of_local_day() + timedelta(days=self.days)
|
start_of_tomorrow = dt_util.start_of_local_day() + timedelta(days=self.days)
|
||||||
|
|
||||||
# We have to retrieve the results for the whole day as the server
|
# We have to retrieve the results for the whole day as the server
|
||||||
# won't return events that have already started
|
# won't return events that have already started
|
||||||
results = self.calendar.search(
|
results = await self.hass.async_add_executor_job(
|
||||||
|
partial(
|
||||||
|
self.calendar.search,
|
||||||
start=start_of_today,
|
start=start_of_today,
|
||||||
end=start_of_tomorrow,
|
end=start_of_tomorrow,
|
||||||
event=True,
|
event=True,
|
||||||
expand=True,
|
expand=True,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create new events for each recurrence of an event that happens today.
|
# Create new events for each recurrence of an event that happens today.
|
||||||
@ -247,12 +264,15 @@ class WebDavCalendarData:
|
|||||||
continue
|
continue
|
||||||
vevent = event.instance.vevent
|
vevent = event.instance.vevent
|
||||||
for start_dt in vevent.getrruleset() or []:
|
for start_dt in vevent.getrruleset() or []:
|
||||||
_start_of_today = start_of_today
|
_start_of_today: date | datetime
|
||||||
_start_of_tomorrow = start_of_tomorrow
|
_start_of_tomorrow: datetime | date
|
||||||
if self.is_all_day(vevent):
|
if self.is_all_day(vevent):
|
||||||
start_dt = start_dt.date()
|
start_dt = start_dt.date()
|
||||||
_start_of_today = _start_of_today.date()
|
_start_of_today = start_of_today.date()
|
||||||
_start_of_tomorrow = _start_of_tomorrow.date()
|
_start_of_tomorrow = start_of_tomorrow.date()
|
||||||
|
else:
|
||||||
|
_start_of_today = start_of_today
|
||||||
|
_start_of_tomorrow = start_of_tomorrow
|
||||||
if _start_of_today <= start_dt < _start_of_tomorrow:
|
if _start_of_today <= start_dt < _start_of_tomorrow:
|
||||||
new_event = event.copy()
|
new_event = event.copy()
|
||||||
new_vevent = new_event.instance.vevent
|
new_vevent = new_event.instance.vevent
|
||||||
@ -293,21 +313,21 @@ class WebDavCalendarData:
|
|||||||
len(vevents),
|
len(vevents),
|
||||||
self.calendar.name,
|
self.calendar.name,
|
||||||
)
|
)
|
||||||
self.event = None
|
self.offset = None
|
||||||
return
|
return None
|
||||||
|
|
||||||
# Populate the entity attributes with the event values
|
# Populate the entity attributes with the event values
|
||||||
(summary, offset) = extract_offset(
|
(summary, offset) = extract_offset(
|
||||||
self.get_attr_value(vevent, "summary") or "", OFFSET
|
self.get_attr_value(vevent, "summary") or "", OFFSET
|
||||||
)
|
)
|
||||||
self.event = CalendarEvent(
|
self.offset = offset
|
||||||
|
return CalendarEvent(
|
||||||
summary=summary,
|
summary=summary,
|
||||||
start=self.to_local(vevent.dtstart.value),
|
start=self.to_local(vevent.dtstart.value),
|
||||||
end=self.to_local(self.get_end_date(vevent)),
|
end=self.to_local(self.get_end_date(vevent)),
|
||||||
location=self.get_attr_value(vevent, "location"),
|
location=self.get_attr_value(vevent, "location"),
|
||||||
description=self.get_attr_value(vevent, "description"),
|
description=self.get_attr_value(vevent, "description"),
|
||||||
)
|
)
|
||||||
self.offset = offset
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_matching(vevent, search):
|
def is_matching(vevent, search):
|
||||||
@ -333,15 +353,15 @@ class WebDavCalendarData:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def is_over(vevent):
|
def is_over(vevent):
|
||||||
"""Return if the event is over."""
|
"""Return if the event is over."""
|
||||||
return dt_util.now() >= WebDavCalendarData.to_datetime(
|
return dt_util.now() >= CalDavUpdateCoordinator.to_datetime(
|
||||||
WebDavCalendarData.get_end_date(vevent)
|
CalDavUpdateCoordinator.get_end_date(vevent)
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_datetime(obj):
|
def to_datetime(obj):
|
||||||
"""Return a datetime."""
|
"""Return a datetime."""
|
||||||
if isinstance(obj, datetime):
|
if isinstance(obj, datetime):
|
||||||
return WebDavCalendarData.to_local(obj)
|
return CalDavUpdateCoordinator.to_local(obj)
|
||||||
return datetime.combine(obj, time.min).replace(tzinfo=dt_util.DEFAULT_TIME_ZONE)
|
return datetime.combine(obj, time.min).replace(tzinfo=dt_util.DEFAULT_TIME_ZONE)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
Loading…
x
Reference in New Issue
Block a user