|
@@ -15,6 +15,7 @@ from homeassistant.util import dt as dt_util
|
|
|
from .backends import BackendAuthError, BackendConnectError, CalDavBackend, RoomCalApiBackend
|
|
from .backends import BackendAuthError, BackendConnectError, CalDavBackend, RoomCalApiBackend
|
|
|
from .const import (
|
|
from .const import (
|
|
|
CONF_REFRESH_INTERVAL,
|
|
CONF_REFRESH_INTERVAL,
|
|
|
|
|
+ CONF_RESERVATION_DEFAULTS,
|
|
|
CONF_SYNC_MODE,
|
|
CONF_SYNC_MODE,
|
|
|
DEFAULT_REFRESH_INTERVAL,
|
|
DEFAULT_REFRESH_INTERVAL,
|
|
|
DOMAIN,
|
|
DOMAIN,
|
|
@@ -24,6 +25,7 @@ from .const import (
|
|
|
SYNC_MODE_CALDAV,
|
|
SYNC_MODE_CALDAV,
|
|
|
)
|
|
)
|
|
|
from .models import ApiStatusData, ReservationData
|
|
from .models import ApiStatusData, ReservationData
|
|
|
|
|
+from dataclasses import fields as dataclass_fields
|
|
|
from .store import ReservationStore
|
|
from .store import ReservationStore
|
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -105,8 +107,8 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
self._current_last_refresh = now
|
|
self._current_last_refresh = now
|
|
|
self._next_last_refresh = now
|
|
self._next_last_refresh = now
|
|
|
|
|
|
|
|
- new_current = self.store.get(STORE_CURRENT) or ReservationData()
|
|
|
|
|
- new_next = self.store.get(STORE_NEXT) or ReservationData()
|
|
|
|
|
|
|
+ new_current = self.store.get(STORE_CURRENT) or self._empty_reservation()
|
|
|
|
|
+ new_next = self.store.get(STORE_NEXT) or self._empty_reservation()
|
|
|
|
|
|
|
|
self._reschedule_mode_sync(new_current, new_next)
|
|
self._reschedule_mode_sync(new_current, new_next)
|
|
|
|
|
|
|
@@ -176,8 +178,8 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
stored reservation data implies a different state than what was restored, then
|
|
stored reservation data implies a different state than what was restored, then
|
|
|
commits a single HA state write via GuestModeSwitch.commit_state().
|
|
commits a single HA state write via GuestModeSwitch.commit_state().
|
|
|
"""
|
|
"""
|
|
|
- current = self.store.get(STORE_CURRENT) or ReservationData()
|
|
|
|
|
- nxt = self.store.get(STORE_NEXT) or ReservationData()
|
|
|
|
|
|
|
+ current = self.store.get(STORE_CURRENT) or self._empty_reservation()
|
|
|
|
|
+ nxt = self.store.get(STORE_NEXT) or self._empty_reservation()
|
|
|
data_changed = False
|
|
data_changed = False
|
|
|
|
|
|
|
|
if self._is_mode_sync_on():
|
|
if self._is_mode_sync_on():
|
|
@@ -193,7 +195,7 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
if state == "elapsed":
|
|
if state == "elapsed":
|
|
|
await self.store.async_set(STORE_CURRENT, None)
|
|
await self.store.async_set(STORE_CURRENT, None)
|
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
|
- current = ReservationData()
|
|
|
|
|
|
|
+ current = self._empty_reservation()
|
|
|
if self._guest_mode_switch is not None:
|
|
if self._guest_mode_switch is not None:
|
|
|
self._guest_mode_switch._state = False
|
|
self._guest_mode_switch._state = False
|
|
|
data_changed = True
|
|
data_changed = True
|
|
@@ -202,8 +204,8 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
self._guest_mode_switch._state = True
|
|
self._guest_mode_switch._state = True
|
|
|
data_changed = True
|
|
data_changed = True
|
|
|
|
|
|
|
|
- new_current = self.store.get(STORE_CURRENT) or ReservationData()
|
|
|
|
|
- new_next = self.store.get(STORE_NEXT) or ReservationData()
|
|
|
|
|
|
|
+ new_current = self.store.get(STORE_CURRENT) or self._empty_reservation()
|
|
|
|
|
+ new_next = self.store.get(STORE_NEXT) or self._empty_reservation()
|
|
|
self._reschedule_mode_sync(new_current, new_next)
|
|
self._reschedule_mode_sync(new_current, new_next)
|
|
|
|
|
|
|
|
if data_changed:
|
|
if data_changed:
|
|
@@ -241,8 +243,8 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
await self.store.async_set(STORE_CURRENT, None)
|
|
await self.store.async_set(STORE_CURRENT, None)
|
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
|
self.async_set_updated_data(CoordinatorData(
|
|
self.async_set_updated_data(CoordinatorData(
|
|
|
- current=ReservationData(),
|
|
|
|
|
- next=self.store.get(STORE_NEXT) or ReservationData(),
|
|
|
|
|
|
|
+ current=self._empty_reservation(),
|
|
|
|
|
+ next=self.store.get(STORE_NEXT) or self._empty_reservation(),
|
|
|
current_last_refresh=self._current_last_refresh,
|
|
current_last_refresh=self._current_last_refresh,
|
|
|
next_last_refresh=self._next_last_refresh,
|
|
next_last_refresh=self._next_last_refresh,
|
|
|
))
|
|
))
|
|
@@ -251,10 +253,10 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
|
|
|
|
|
async def async_promote_next_to_current(self) -> None:
|
|
async def async_promote_next_to_current(self) -> None:
|
|
|
"""Copy the Next slot into Current and notify listeners. Next is left unchanged."""
|
|
"""Copy the Next slot into Current and notify listeners. Next is left unchanged."""
|
|
|
- next_data = self.store.get(STORE_NEXT) or ReservationData()
|
|
|
|
|
|
|
+ next_data = self.store.get(STORE_NEXT) or self._empty_reservation()
|
|
|
await self.store.async_set(STORE_CURRENT, next_data)
|
|
await self.store.async_set(STORE_CURRENT, next_data)
|
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
self._current_last_refresh = dt_util.utcnow()
|
|
|
- new_next = self.store.get(STORE_NEXT) or ReservationData()
|
|
|
|
|
|
|
+ new_next = self.store.get(STORE_NEXT) or self._empty_reservation()
|
|
|
self.async_set_updated_data(CoordinatorData(
|
|
self.async_set_updated_data(CoordinatorData(
|
|
|
current=next_data,
|
|
current=next_data,
|
|
|
next=new_next,
|
|
next=new_next,
|
|
@@ -270,7 +272,7 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
|
|
|
|
|
async def async_apply_update(self, target: str, partial: dict[str, Any]) -> None:
|
|
async def async_apply_update(self, target: str, partial: dict[str, Any]) -> None:
|
|
|
"""Apply a thin update (service call) to current or next."""
|
|
"""Apply a thin update (service call) to current or next."""
|
|
|
- stored = self.store.get(target) or ReservationData()
|
|
|
|
|
|
|
+ stored = self.store.get(target) or self._empty_reservation()
|
|
|
updated = stored.apply_partial(partial)
|
|
updated = stored.apply_partial(partial)
|
|
|
await self.store.async_set(target, updated)
|
|
await self.store.async_set(target, updated)
|
|
|
|
|
|
|
@@ -281,8 +283,8 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
self._next_last_refresh = now
|
|
self._next_last_refresh = now
|
|
|
|
|
|
|
|
self.async_set_updated_data(CoordinatorData(
|
|
self.async_set_updated_data(CoordinatorData(
|
|
|
- current=self.store.get(STORE_CURRENT) or ReservationData(),
|
|
|
|
|
- next=self.store.get(STORE_NEXT) or ReservationData(),
|
|
|
|
|
|
|
+ current=self.store.get(STORE_CURRENT) or self._empty_reservation(),
|
|
|
|
|
+ next=self.store.get(STORE_NEXT) or self._empty_reservation(),
|
|
|
current_last_refresh=self._current_last_refresh,
|
|
current_last_refresh=self._current_last_refresh,
|
|
|
next_last_refresh=self._next_last_refresh,
|
|
next_last_refresh=self._next_last_refresh,
|
|
|
))
|
|
))
|
|
@@ -291,3 +293,11 @@ class RoomCalCoordinator(DataUpdateCoordinator[CoordinatorData]):
|
|
|
for cancel in self._mode_sync_handles:
|
|
for cancel in self._mode_sync_handles:
|
|
|
cancel()
|
|
cancel()
|
|
|
self._mode_sync_handles.clear()
|
|
self._mode_sync_handles.clear()
|
|
|
|
|
+
|
|
|
|
|
+ def _empty_reservation(self) -> ReservationData:
|
|
|
|
|
+ """Return a ReservationData with configured defaults applied (for empty slots)."""
|
|
|
|
|
+ defaults = self.entry.options.get(CONF_RESERVATION_DEFAULTS, {})
|
|
|
|
|
+ if not defaults:
|
|
|
|
|
+ return ReservationData()
|
|
|
|
|
+ raw = {f.name: defaults.get(f.name) for f in dataclass_fields(ReservationData)}
|
|
|
|
|
+ return ReservationData.from_dict(raw)
|