mirror of
https://github.com/kuhyx/wake-alarm.git
synced 2026-11-10 18:07:05 +01:00
On 2026-09-19 the phone was powered off after the bathroom tag and nothing on the PC noticed: every finished session was published and nothing read it. Now wake-alarm distils the phone's morning into an HMAC-signed ~/.local/state/wake_alarm/morning_session.json (wake-alarm-session.timer, 07:00-11:00 every 2 min) that screen-locker and leetcode-guard read to defer their locks -- provisionally until min(start + 90 min, 09:30) while the session is live, until 11:00 once it completed inside that cutoff, and not at all for a session finished later (the phone switched on at noon completes the legs then; that must un-lock nothing). The phone publishes the session at start as well as at finish (current_session, cleared with the finish stamp), so the PC can tell "started, not finished" from "never started". A required 1-5 energy tap joins the weigh-in and ships as latest_energy with a PC reader mirroring _weight (the shared half is _field_reader). Also fixes the vmbox harness, red since the 2026-09-11 home-tidy: the guest tree lives at ~/wake-alarm, and the INSTALL-OK grep matched vm's echo of the command line. Takes Flutter 3.47.5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tp54ewdXRLaimUuT8LgYyn
68 lines
2.1 KiB
Python
68 lines
2.1 KiB
Python
"""The phone's newest morning weigh-in, read from the synced alarm record.
|
|
|
|
The morning session (``phone_app``, ``DOCS-morning-session.md``) publishes
|
|
``latest_weight_kg`` -- ``{"date": "yyyy-mm-dd", "kg": 72.4}`` -- on the same
|
|
``alarm.json`` the alarm time rides on. This module is the PC-side reader,
|
|
built for diet-guard's protein target: it imports this lazily, so the two
|
|
packages stay independently installable.
|
|
|
|
Firebase only. The GitHub mirror has no per-field stamps and is on its way
|
|
out; a weigh-in that reached only the mirror is simply not seen yet.
|
|
|
|
python -m wake_alarm._weight # prints {"date": ..., "kg": ...} or nothing
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import json
|
|
import sys
|
|
|
|
from wake_alarm._field_reader import latest_field, newest_field
|
|
|
|
WEIGHT_FIELD = "latest_weight_kg"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Weight:
|
|
"""One weigh-in: the local date it was taken and the kilograms typed."""
|
|
|
|
date: str
|
|
kg: float
|
|
device_id: str
|
|
|
|
|
|
def _parse_weight(value: object, device_id: str) -> Weight | None:
|
|
"""The weigh-in out of one device's ``latest_weight_kg`` value, or None."""
|
|
if not isinstance(value, dict):
|
|
return None
|
|
date, kg = value.get("date"), value.get("kg")
|
|
if not isinstance(date, str) or isinstance(kg, bool):
|
|
return None
|
|
if not isinstance(kg, (int, float)):
|
|
return None
|
|
return Weight(date=date, kg=float(kg), device_id=device_id)
|
|
|
|
|
|
def newest_weight(published: list[tuple[str, str]]) -> Weight | None:
|
|
"""The most recently stamped weigh-in across every device's log."""
|
|
return newest_field(published, WEIGHT_FIELD, _parse_weight)
|
|
|
|
|
|
def latest_weight() -> Weight | None:
|
|
"""The phone's newest weigh-in from Firebase, or None when unreadable."""
|
|
return latest_field(WEIGHT_FIELD, _parse_weight, what="weigh-in")
|
|
|
|
|
|
def main() -> int:
|
|
"""Print the newest weigh-in as JSON; exit 1 when there is none."""
|
|
weight = latest_weight()
|
|
if weight is None:
|
|
return 1
|
|
sys.stdout.write(json.dumps({"date": weight.date, "kg": weight.kg}) + "\n")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|