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:
@@ -39,6 +39,7 @@ from .coordinator import (
|
||||
NestQuestCoordinator,
|
||||
QuestInstanceView,
|
||||
)
|
||||
from .core.snapshot import instance_payload
|
||||
|
||||
#: Home Assistant's state machine refuses states longer than 255
|
||||
#: characters (its hard recorder/state limit). These sensors' states
|
||||
@@ -63,40 +64,6 @@ def _state_within_limit(state: Any) -> Any:
|
||||
return state
|
||||
|
||||
|
||||
def _instance_payload(
|
||||
instances: Iterable[QuestInstanceView], *, include_missed: bool
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Shape instance dicts per ENTITIES-AND-SERVICES.md §1.
|
||||
|
||||
``state`` maps the coordinator's derived ``done`` to the panel's
|
||||
``completed`` spelling; ``missed`` instances are omitted unless
|
||||
``include_missed`` (the admin payload, D-009, keeps them with
|
||||
their ``missed`` state).
|
||||
"""
|
||||
payload: list[dict[str, Any]] = []
|
||||
for view in instances:
|
||||
if view.state == "missed" and not include_missed:
|
||||
continue
|
||||
payload.append(
|
||||
{
|
||||
"id": view.instance_id,
|
||||
"definition_id": view.definition_id,
|
||||
"child_id": view.child_id,
|
||||
"title": view.title,
|
||||
"icon": view.icon,
|
||||
"window": view.window,
|
||||
"due_time": view.due_time,
|
||||
"state": (
|
||||
"completed" if view.state == "done" else view.state
|
||||
),
|
||||
"overdue": view.overdue,
|
||||
"completed_at": view.completed_at,
|
||||
"on_time": view.was_on_time,
|
||||
}
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
#: State reported by the next-quest sensor when the child owes nothing
|
||||
#: else today. A documented sentinel, never an empty string (an empty
|
||||
#: state reads as "sensor went unavailable" in HA).
|
||||
@@ -215,10 +182,10 @@ class NestQuestQuestsDueTodaySensor(_NestQuestChildDaySensor):
|
||||
if child is None:
|
||||
return None
|
||||
return {
|
||||
"instances": _instance_payload(
|
||||
"instances": instance_payload(
|
||||
child.instances, include_missed=False
|
||||
),
|
||||
"admin_instances": _instance_payload(
|
||||
"admin_instances": instance_payload(
|
||||
child.instances, include_missed=True
|
||||
),
|
||||
"child_id": child.child_id,
|
||||
|
||||
Reference in New Issue
Block a user