Move panel payload shaping into core; pin the snapshot clock

PR #148 review (CHANGES_REQUIRED) follow-up:

- Move the panel payload dict shaper (_instance_payload) from the
  HA-importing sensor.py into core/snapshot.py as instance_payload, and
  have sensor.py import it from core. The API panel route now shares ONE
  shaping implementation with the HA sensor layer (done -> completed,
  on_time passthrough, missed omitted unless include_missed).
- build_snapshot takes the HA-local now explicitly instead of
  reading datetime.now(time_zone) internally, so date and time come from
  a single clock read and the overdue check is pinned in tests. The
  coordinator passes the same now it already resolved; time_zone is
  dropped (now is timezone-aware).
- Strengthen tests/test_core_snapshot.py with explicit literal expected
  values instead of self-equality: an AWAY child (2-week absent-week
  schedule + a presence override => next_present), a due-timed quest
  exercised as overdue under a pinned 12:00 now, a completed quest
  (done -> completed, on_time True), cycle_day == 2, and a constructed
  missed view proving omission from the panel payload and retention in
  the admin payload.
- Docstring cleanups: state plainly that settings is currently
  unused (no 'wired' implication); correct the coordinator docstring
  (settings are passed in, not resolved off the entry); trim the
  coordinator __all__ to what other modules actually import.
- Derive the seed/materialize date from coordinator._local_now() so the
  seed date and snapshot date cannot diverge near midnight; drop the
  redundant bo/ghost/list_children leftovers from the prior seed.
This commit is contained in:
2026-09-21 17:37:16 -04:00
parent 6741a1a210
commit c18246d58f
4 changed files with 317 additions and 190 deletions
+6 -9
View File
@@ -14,10 +14,11 @@ layers.
The snapshot assembly itself lives in the Home-Assistant-free
:mod:`custom_components.nestquest.core.snapshot` module
(:func:`~core.snapshot.build_snapshot`); this coordinator is now a thin
HA wrapper that resolves the local timezone and settings off the config
entry and delegates to that builder. The API service's panel snapshot
route shares the same builder, so both consumers render ONE snapshot
shape.
HA wrapper that resolves the local timezone and reads the settings handed
into its constructor, then delegates to that builder, passing the SAME
HA-local ``now`` it resolved in one clock read. The API service's panel
snapshot route shares the same builder, so both consumers render ONE
snapshot shape.
Presence is resolved through the SAME engine the materializer uses
(:class:`~.presence.PresenceEngine`): schedules and overrides are read
@@ -53,7 +54,6 @@ from .core.snapshot import (
ChildDaySnapshot,
NestQuestSnapshot,
QuestInstanceView,
_cycle_day,
build_snapshot,
)
@@ -62,8 +62,6 @@ __all__ = [
"NestQuestCoordinator",
"NestQuestSnapshot",
"QuestInstanceView",
"_cycle_day",
"build_snapshot",
]
@@ -120,7 +118,6 @@ class NestQuestCoordinator(DataUpdateCoordinator):
async def _async_update_data(self) -> NestQuestSnapshot:
now = self._local_now()
today = now.date()
time_zone = ZoneInfo(self.hass.config.time_zone)
return await build_snapshot(
self.database, self.settings, today, time_zone
self.database, self.settings, today, now
)