Go to file
Krzysztof kuhy Rudnicki eeb567ccce Phase 2a: retry card-profile activation so the HDMI sink can appear
Root cause of the silent-alarm-on-HDMI bug, measured on the host rather than
inferred:

    $ pactl list cards | grep 'alsa_card.pci-0000_01_00.1' -A1
    Active Profile: off
    $ pactl list short sinks
    61  alsa_output.pci-0000_0d_00.6.analog-stereo   IDLE

While that card's profile is "off" the G27Q's HDMI sink does not exist, so
_list_real_sinks() cannot return it. _play_on_all_sinks re-enumerates sinks
every beep cycle -- its docstring says it picks up late outputs "without
re-running activation" -- but that can never find a sink whose card is off.
There is nothing to enumerate.

Activation runs exactly once, at startup. At wake time the monitor has not
finished its HDMI handshake, the profile reads "available: no",
_parse_profile_line drops it, and the card is never revisited. Silent alarm.

_reactivate re-runs activation on a decaying schedule (3,5,7,10,15,20,30s)
on a daemon thread, logging the full enumerated sink list every pass so the
next failure is diagnosable from the JSONL log instead of guessed at.

Verified end to end on this machine:
  BEFORE: ['alsa_output.pci-0000_0d_00.6.analog-stereo']
  AFTER : ['alsa_output.pci-0000_01_00.1.hdmi-stereo',
           'alsa_output.pci-0000_0d_00.6.analog-stereo']
and a 1 kHz tone plays through the HDMI sink (paplay exit 0). Note the
constant ALARM_AUDIO_SINK was correct all along; only the activation was
failing.

Wiring into WakeAlarm lands with the _alarm.py split, since touching that
478-line file trips the 250-line gate.

399 tests, 100% branch coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EckWmyfetYq3Mi8MAnUudh
2026-08-26 23:02:52 +02:00
.github/workflows Adopt the four markdown namespaces and gate them 2026-08-24 15:53:51 +02:00
docs/explanations Add change explanation: phone-driven-alarm-timer 2026-07-20 09:17:12 +02:00
phone_app Phase 0: fix dependency pin mismatch, stale docs, add speaker research 2026-08-26 22:53:01 +02:00
prompts Add the removal marker to every TODO file 2026-08-24 15:56:24 +02:00
scripts Adopt the four markdown namespaces and gate them 2026-08-24 15:53:51 +02:00
wake_alarm Phase 2a: retry card-profile activation so the HDMI sink can appear 2026-08-26 23:02:52 +02:00
.gitignore Add CI-mirror pre-push gate + requirements-sync check 2026-07-11 17:51:16 +02:00
.pre-commit-config.yaml Phase 0: fix dependency pin mismatch, stale docs, add speaker research 2026-08-26 22:53:01 +02:00
AGENTS.md Phase 0: fix dependency pin mismatch, stale docs, add speaker research 2026-08-26 22:53:01 +02:00
CLAUDE.md Phase 0: fix dependency pin mismatch, stale docs, add speaker research 2026-08-26 22:53:01 +02:00
DOCS-speaker-recommendation.md Phase 0: fix dependency pin mismatch, stale docs, add speaker research 2026-08-26 22:53:01 +02:00
install.sh Read the phone's alarm from Firebase, keeping GitHub as the mirror 2026-08-05 09:09:22 +02:00
pyproject.toml Bump gatelock to v0.4.1: keep the dismiss box focused after a monitor blip 2026-08-12 22:17:31 +02:00
README.md Fire the alarm at the phone's time via a per-minute timer 2026-07-19 20:21:17 +02:00
requirements.txt Bump gatelock to v0.4.1: keep the dismiss box focused after a monitor blip 2026-08-12 22:17:31 +02:00
shutdown-wrapper.sh Read the phone's alarm from Firebase, keeping GitHub as the mirror 2026-08-05 09:09:22 +02:00
sleep-hook.sh Extract wake_alarm from testsAndMisc as a standalone repo 2026-06-22 12:31:40 +02:00
TODO-file-length-250.md Add the removal marker to every TODO file 2026-08-24 15:56:24 +02:00
wake-alarm-fans.sh Extract wake_alarm from testsAndMisc as a standalone repo 2026-06-22 12:31:40 +02:00
wake-alarm-trigger.service Fire the alarm at the phone's time via a per-minute timer 2026-07-19 20:21:17 +02:00
wake-alarm-trigger.timer Fire the alarm at the phone's time via a per-minute timer 2026-07-19 20:21:17 +02:00
wake-alarm.service Fire the alarm at the phone's time via a per-minute timer 2026-07-19 20:21:17 +02:00

wake_alarm

A phone-driven wake alarm for an always-on PC. The alarm time is whatever the companion phone app last synced (via the shared crdt_sync transport over a private GitHub repo); a per-minute systemd user timer fires at exactly that time, showing a fullscreen, challenge-dismissed alarm with fan ramp and optional TP-Link Tapo P110 smart-plug control.

Change the alarm on the phone at any hour and the PC follows within ~5 minutes. If the PC does happen to be powered down, a shutdown wrapper hibernates with rtcwake set to the same synced time, as a fallback.

Install

bash install.sh

This installs the package + dependencies into system Python's user site-packages (the systemd units run /usr/bin/python directly, not a venv — see CLAUDE.md), installs and enables the per-minute trigger timer, the systemd-sleep resume hook, the shutdown wrapper that hibernates with rtcwake as a fallback, the fan-ramp script, and (optionally) python-kasa for smart-plug control.

Reading the phone's alarm needs a GitHub token with repo scope, at ~/.config/wake_alarm/sync.json as {"token": "..."}. If that file is absent, a sibling app's ~/.config/{diet_guard,screen_locker}/sync_token is used instead, so one token covers every app syncing through kuhyx/syncs.

Usage

python -m wake_alarm._alarm --demo     # test the alarm window (safe, closeable)
python -m wake_alarm._sync --show      # what alarm time the PC resolves
python -m wake_alarm._trigger --dry-run  # what the next tick would do
journalctl --user -fu wake-alarm-trigger.service  # watch it live

The alarm fires automatically once installed; no manual invocation is needed in normal operation.

Development

python -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/pre-commit install && .venv/bin/pre-commit install --hook-type pre-push
.venv/bin/python -m pytest wake_alarm/tests/ --cov=wake_alarm --cov-branch --cov-fail-under=100

See CLAUDE.md for scheduling details, the hibernate/rtcwake mechanism, the cross-repo workout_log.json read, and production deployment gotchas.