Publish the API's effective timezone and tolerate an older snapshot payload
TZ-002: with no zone stored the API reads its clock in the host's local time, but the panel fell back to Home Assistant's zone and the admin app to the browser's, so dates could disagree near midnight. The API now names the zone it actually uses (api/host_zone.py: the stored zone, else the host's own IANA zone from TZ, /etc/localtime or /etc/timezone) and publishes it as the panel snapshot's `timezone` and the admin settings' read-only `effective_timezone`. Both cards and the admin app format in that one zone; Home Assistant's or the browser's zone applies only when none is published. TZ-003: snapshot_from_api_payload reads `timezone` with a "" default, so an integration upgraded ahead of its API keeps refreshing against the pre-change payload. Task 7e173881-fb99-426f-a2f0-2678439ffc39 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -111,8 +111,9 @@ def snapshot_from_api_payload(payload: dict[str, Any]) -> NestQuestSnapshot:
|
||||
through verbatim;
|
||||
- ``present`` and ``next_present`` pass through verbatim (the API
|
||||
resolved them through the same presence engine server-side);
|
||||
- ``timezone`` (the stored application timezone, ``""`` when unset)
|
||||
passes through verbatim for the cards' date and clock;
|
||||
- ``timezone`` (the API's effective application timezone) passes
|
||||
through verbatim for the cards' date and clock; an API older than
|
||||
the field omits it, which reads as ``""`` (the cards' fallback);
|
||||
- the instance list is rebuilt IN ORDER, so the panel payload the
|
||||
sensors emit matches the route's order byte for byte.
|
||||
|
||||
@@ -163,7 +164,7 @@ def snapshot_from_api_payload(payload: dict[str, Any]) -> NestQuestSnapshot:
|
||||
today_iso=payload["today_iso"],
|
||||
children=tuple(children),
|
||||
cycle_day=payload["cycle_day"],
|
||||
timezone=payload["timezone"],
|
||||
timezone=payload.get("timezone", ""),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -97,8 +97,9 @@ class NestQuestSnapshot:
|
||||
children: tuple[ChildDaySnapshot, ...]
|
||||
cycle_day: int
|
||||
#: The application timezone the admin stored (``""`` when unset —
|
||||
#: the host's local time). The panel cards format their visible
|
||||
#: date and clock in it; it never re-resolves the day itself.
|
||||
#: the host's local time; the API publishes its effective zone in
|
||||
#: its place, api/host_zone.py). The panel cards format their
|
||||
#: visible date and clock in it; it never re-resolves the day itself.
|
||||
timezone: str = ""
|
||||
|
||||
|
||||
|
||||
@@ -441,8 +441,9 @@ class NestQuestHouseholdDueTodaySensor(_NestQuestHouseholdSensor):
|
||||
for child in data.children
|
||||
},
|
||||
"child_roster": _child_roster(data.children),
|
||||
# The stored application timezone ("" when unset): the
|
||||
# panel cards format their visible date and clock in it.
|
||||
# The API's effective application timezone ("" only when
|
||||
# it could not name one): the panel cards format their
|
||||
# visible date and clock in it.
|
||||
"timezone": data.timezone,
|
||||
}
|
||||
|
||||
|
||||
@@ -1243,10 +1243,12 @@ const Te = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Ee = "M50 3 44.5 2
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
/** The zone the visible date and clock render in: the application
|
||||
* timezone the admin stored (published on the household rollup by
|
||||
* custom_components/nestquest/sensor.py — ``timezone``). Only when
|
||||
* none is stored does Home Assistant's zone (or the browser's) apply. */
|
||||
/** The zone the visible date and clock render in: the API's effective
|
||||
* application timezone — the stored zone, or the API host's when none
|
||||
* is stored (published on the household rollup by
|
||||
* custom_components/nestquest/sensor.py — ``timezone``). Only when the
|
||||
* rollup carries none does Home Assistant's zone (or the browser's)
|
||||
* apply. */
|
||||
_timeZone() {
|
||||
const e = this._state("sensor.nestquest_household_quests_due_today")?.attributes?.timezone;
|
||||
return typeof e == "string" && e.trim() ? e.trim() : kt(this.hass);
|
||||
@@ -3732,10 +3734,12 @@ const Ue = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", yn = "polygon(50%
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
/** The zone the visible date and clock render in: the application
|
||||
* timezone the admin stored (published on the household rollup by
|
||||
* custom_components/nestquest/sensor.py — ``timezone``). Only when
|
||||
* none is stored does Home Assistant's zone (or the browser's) apply. */
|
||||
/** The zone the visible date and clock render in: the API's effective
|
||||
* application timezone — the stored zone, or the API host's when none
|
||||
* is stored (published on the household rollup by
|
||||
* custom_components/nestquest/sensor.py — ``timezone``). Only when the
|
||||
* rollup carries none does Home Assistant's zone (or the browser's)
|
||||
* apply. */
|
||||
_timeZone() {
|
||||
const e = this._state("sensor.nestquest_household_quests_due_today")?.attributes?.timezone;
|
||||
return typeof e == "string" && e.trim() ? e.trim() : rn(this.hass);
|
||||
|
||||
Reference in New Issue
Block a user