|
@@ -17,6 +17,7 @@ from .const import (
|
|
|
CONF_ROOM_NAME,
|
|
CONF_ROOM_NAME,
|
|
|
CONF_SYNC_MODE,
|
|
CONF_SYNC_MODE,
|
|
|
DOMAIN,
|
|
DOMAIN,
|
|
|
|
|
+ SLOT_ACTIVE,
|
|
|
STORE_CURRENT,
|
|
STORE_CURRENT,
|
|
|
STORE_NEXT,
|
|
STORE_NEXT,
|
|
|
SYNC_MODE_API,
|
|
SYNC_MODE_API,
|
|
@@ -43,13 +44,17 @@ async def async_setup_entry(
|
|
|
|
|
|
|
|
entities: list[SensorEntity] = [
|
|
entities: list[SensorEntity] = [
|
|
|
SyncSourceSensor(entry, room_name, room_state_device),
|
|
SyncSourceSensor(entry, room_name, room_state_device),
|
|
|
- ActiveLastUpdatedSensor(coordinator, entry, room_state_device),
|
|
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
if entry.options.get(CONF_SYNC_MODE) == SYNC_MODE_API:
|
|
if entry.options.get(CONF_SYNC_MODE) == SYNC_MODE_API:
|
|
|
entities.append(ApiHealthSensor(coordinator, entry, room_state_device))
|
|
entities.append(ApiHealthSensor(coordinator, entry, room_state_device))
|
|
|
|
|
|
|
|
|
|
+ # Active is listed first: it's the live/effective record (overridable) that in-room,
|
|
|
|
|
+ # customer-facing consumers (signage, door displays) should read. Current/Next are
|
|
|
|
|
+ # pure backend mirrors — never masked by an override — for employee-facing views that
|
|
|
|
|
+ # want to see the raw upcoming feed regardless of any in-progress override.
|
|
|
for slot, device_name in (
|
|
for slot, device_name in (
|
|
|
|
|
+ (SLOT_ACTIVE, f"{room_name} Active"),
|
|
|
(STORE_CURRENT, f"{room_name} Current"),
|
|
(STORE_CURRENT, f"{room_name} Current"),
|
|
|
(STORE_NEXT, f"{room_name} Next"),
|
|
(STORE_NEXT, f"{room_name} Next"),
|
|
|
):
|
|
):
|
|
@@ -123,6 +128,8 @@ class _ReservationSensor(CoordinatorEntity[RoomCalCoordinator], SensorEntity):
|
|
|
|
|
|
|
|
def _reservation(self) -> ReservationData:
|
|
def _reservation(self) -> ReservationData:
|
|
|
data: CoordinatorData = self.coordinator.data
|
|
data: CoordinatorData = self.coordinator.data
|
|
|
|
|
+ if self._slot == SLOT_ACTIVE:
|
|
|
|
|
+ return data.active
|
|
|
return data.current if self._slot == STORE_CURRENT else data.next
|
|
return data.current if self._slot == STORE_CURRENT else data.next
|
|
|
|
|
|
|
|
|
|
|
|
@@ -264,53 +271,20 @@ class LastUpdatedSensor(CoordinatorEntity[RoomCalCoordinator], SensorEntity):
|
|
|
@property
|
|
@property
|
|
|
def native_value(self) -> datetime | None:
|
|
def native_value(self) -> datetime | None:
|
|
|
data: CoordinatorData = self.coordinator.data
|
|
data: CoordinatorData = self.coordinator.data
|
|
|
- return (
|
|
|
|
|
- data.current_last_updated
|
|
|
|
|
- if self._slot == STORE_CURRENT
|
|
|
|
|
- else data.next_last_updated
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ if self._slot == SLOT_ACTIVE:
|
|
|
|
|
+ return data.active_last_updated
|
|
|
|
|
+ return data.current_last_updated if self._slot == STORE_CURRENT else data.next_last_updated
|
|
|
|
|
|
|
|
@property
|
|
@property
|
|
|
def extra_state_attributes(self) -> dict:
|
|
def extra_state_attributes(self) -> dict:
|
|
|
data: CoordinatorData = self.coordinator.data
|
|
data: CoordinatorData = self.coordinator.data
|
|
|
- refresh = (
|
|
|
|
|
- data.current_last_refresh
|
|
|
|
|
- if self._slot == STORE_CURRENT
|
|
|
|
|
- else data.next_last_refresh
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ if self._slot == SLOT_ACTIVE:
|
|
|
|
|
+ refresh = data.active_last_refresh
|
|
|
|
|
+ else:
|
|
|
|
|
+ refresh = data.current_last_refresh if self._slot == STORE_CURRENT else data.next_last_refresh
|
|
|
return {"last_refresh": refresh}
|
|
return {"last_refresh": refresh}
|
|
|
|
|
|
|
|
|
|
|
|
|
-class ActiveLastUpdatedSensor(CoordinatorEntity[RoomCalCoordinator], SensorEntity):
|
|
|
|
|
- """Last time the live/active record changed — distinct from the Current device's
|
|
|
|
|
- LastUpdatedSensor, which only tracks the backend-mirror feed and never reflects
|
|
|
|
|
- override/patch activity."""
|
|
|
|
|
-
|
|
|
|
|
- _attr_has_entity_name = True
|
|
|
|
|
- _attr_translation_key = "active_last_updated"
|
|
|
|
|
- _attr_device_class = SensorDeviceClass.TIMESTAMP
|
|
|
|
|
- _attr_icon = "mdi:clock-edit-outline"
|
|
|
|
|
- _attr_entity_category = EntityCategory.DIAGNOSTIC
|
|
|
|
|
-
|
|
|
|
|
- def __init__(
|
|
|
|
|
- self,
|
|
|
|
|
- coordinator: RoomCalCoordinator,
|
|
|
|
|
- entry: ConfigEntry,
|
|
|
|
|
- device_info: DeviceInfo,
|
|
|
|
|
- ) -> None:
|
|
|
|
|
- super().__init__(coordinator)
|
|
|
|
|
- self._attr_unique_id = f"{entry.entry_id}_active_last_updated"
|
|
|
|
|
- self._attr_device_info = device_info
|
|
|
|
|
-
|
|
|
|
|
- @property
|
|
|
|
|
- def native_value(self) -> datetime | None:
|
|
|
|
|
- return self.coordinator.data.active_last_updated
|
|
|
|
|
-
|
|
|
|
|
- @property
|
|
|
|
|
- def extra_state_attributes(self) -> dict:
|
|
|
|
|
- return {"last_refresh": self.coordinator.data.active_last_refresh}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
class ApiHealthSensor(CoordinatorEntity[RoomCalCoordinator], SensorEntity):
|
|
class ApiHealthSensor(CoordinatorEntity[RoomCalCoordinator], SensorEntity):
|
|
|
_attr_has_entity_name = True
|
|
_attr_has_entity_name = True
|
|
|
_attr_translation_key = "api_health"
|
|
_attr_translation_key = "api_health"
|