mirror of
https://github.com/kuhyx/wake-alarm.git
synced 2026-11-11 13:07:32 +01:00
95be57b575
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 03392e7ea5 |
Discover alarm device directories instead of reading a fixed phone path
The PC read one hardcoded path, wake-alarm-sync/devices/phone/alarm.json, and never parsed the HLC. The phone's device id is migrating from the fixed constant "phone" to a per-install uuid (a role constant collides as soon as a second install takes the same role, and a reinstall inherits the previous install's CRDT identity). A reader pinned to the old path would have stopped seeing the alarm the moment the phone migrated, fallen through to the cache and then to FALLBACK_ALARM_HOUR -- i.e. woken at the wrong time, silently. So the PC half ships first, and this commit is that half plus the phone change it makes safe: - _fetch_firebase_alarm() lists SYNC_DEVICES_PREFIX, reads every device's alarm.json, and picks the newest by the hour field's HLC rather than by listing order. device_id breaks an exact tie so two devices stamped in the same millisecond don't alternate between ticks; a device whose payload doesn't parse is skipped rather than allowed to win with a bad value. - SYNC_ALARM_PATH stays as a last-resort read for a backend that lists nothing, so an un-migrated install keeps working. The GitHub mirror still reads the single fixed path. - The phone persists a uuid under SharedPreferences 'crdt.nodeId' (the key todo and home_inventory already use) and pushes under it. deviceId is now an instance field, injectable so tests don't touch real prefs. Parsing and ranking moved to _alarm_payload.py: _sync.py was over the repo's 500-line cap, and the wire format deserves testing without network or disk -- a misparse here doesn't raise, it just resolves to the wrong wake-up time. Verified against the live RTDB, not just mocks: seeded a uuid-named device directory and confirmed the reader discovered it, read it and ranked it -- losing to the phone with an old stamp, winning with a new one (03:15 vs the real 07:45). Test device deleted; --show returns 07:45 and the cache is intact. 354 tests, 100% branch coverage. Phone app: 10 tests, analyzer clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZH89WEqAjuJT8WZdqc7AP |
|||
| 66bc3a9e55 |
Read the phone's alarm from Firebase, keeping GitHub as the mirror
The seventh and last app on the GitHub-repo transport. fetch_remote_alarm() is now `_fetch_firebase_alarm() or _fetch_github_alarm()`, so retiring the mirror later is deleting one branch and three constants. Two things made this look harder than it was. The first was the standing "_sync.py is stdlib-only" rule: crdt_sync pulls in requests, and the service runs /usr/bin/python rather than a venv. But the package already depends on gatelock and python-kasa, and _smart_plug.py already has the pattern for exactly this -- a module-scope import inside try/except ImportError. So the Firebase import is guarded the same way: a dependency that failed to install degrades the read to GitHub instead of killing the per-minute trigger. The GitHub path stays stdlib-only, which is what makes that fallback trustworthy. The second was the shutdown wrapper, which used to do its own authenticated GitHub read. It could not have worked against Firebase -- an ID token lasts about an hour and a shell script at shutdown time has no safe way to refresh one. It now reads the local alarm cache the trigger already keeps fresh (deployed to /usr/local/bin/shutdown), so the backend is invisible to it. install.sh also now refuses to run under sudo, which silently skipped its later steps. Verified against the live database, not just in tests: the phone's alarm is already published at wake-alarm-sync/devices/phone/alarm.json, and Firebase and GitHub both resolve it to 07:45. `_sync --show --no-cache` reports "07:45 (remote)" and the trigger dry-run behaves unchanged. 338 tests at 100% branch coverage, no new lint suppressions. The backend tests moved to test_sync_backends.py to keep both files under the 500-line cap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017FH32BgGQbQLDstBhR26Xa |
|||
| 87d2629e88 |
Fire the alarm at the phone's time via a per-minute timer
The PC is no longer shut down at night, which broke the scheduling model outright. The wake time used to be decided once, at hibernate, and baked into the RTC by shutdown-wrapper.sh. With no shutdown there is no resume, so sleep-hook.sh never ran and nothing fired the alarm at all. Worse, wake-alarm.service was WantedBy=graphical-session.target and enabled by install.sh, while _should_run_alarm() gates on day / dismissed / workout-logged but has no *time* gate. On an always-on machine that target is reached on every login, so the alarm fired at login time on any alarm day — which is every day. Replace both with a systemd user timer ticking once a minute: - _sync.py reads the alarm the phone pushed to kuhyx/syncs, mirroring shutdown-wrapper.sh's jq parse so the two readers cannot drift. Caches each read, then degrades fresh cache -> fetch -> stale cache -> FALLBACK_ALARM_HOUR, so a network blip can never cost a wake-up. stdlib-only: the units run /usr/bin/python, so a dependency here is a production install-path hazard. - _trigger.py fires morning-routine.service (never wake-alarm.service — that skips the workout lock) when local time is inside [alarm, alarm + TRIGGER_CATCHUP_MINUTES). The catch-up window covers a suspended or just-booted machine; starting an already-active unit is a no-op, so an alarm on screen needs no extra dedupe state. - wake-alarm.service loses its [Install]; install.sh disables it. A timer rather than a resident scheduler: each tick is a fresh process, so there is no daemon to die silently and no wall-clock jump to handle across suspend/resume. Ticks are per-minute for exact-minute firing, but only hit the network when the cache is older than 5 minutes — ~288 GitHub reads/day, and phone changes land within ~5 minutes. Degraded reads log at INFO, not WARNING: at one tick a minute a persistent condition would be 1440 identical journal lines a day. The one WARNING kept is _trigger firing on a non-phone alarm time. Token resolution gains a fallback to the sibling apps' repo-scoped sync_token, in both readers, so one PAT covers every app on kuhyx/syncs. Note this surfaced that wake-alarm-sync/ does not exist in kuhyx/syncs at all: the phone has never completed a push since the crdt_sync migration, so the hibernate path had been silently using the +8h fallback. Fixing that is a phone-side action (reconnect GitHub with repo scope, then one "Set Alarm & Sync"). Alarms edited directly in the Clock app still do not reach the sync — that needs an ACTION_NEXT_ALARM_CLOCK_CHANGED receiver. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |