wake-alarm/wake_alarm
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
..
tests Phase 2a: retry card-profile activation so the HDMI sink can appear 2026-08-26 23:02:52 +02:00
__init__.py Add tests and fix pre-commit issues across all projects 2026-04-12 20:45:24 +02:00
_alarm_display.py feat: split oversized modules for 500-line limit, fix kasa coverage gap 2026-06-14 07:19:37 +02:00
_alarm_payload.py Discover alarm device directories instead of reading a fixed phone path 2026-08-09 17:06:43 +02:00
_alarm_view.py Size the alarm's type in pixels, and gate every screen on fitting 768px 2026-08-03 16:04:27 +02:00
_alarm.py Size the alarm's type in pixels, and gate every screen on fitting 768px 2026-08-03 16:04:27 +02:00
_audio.py Play alarm audio on every sink, not one hardcoded output 2026-07-11 18:12:39 +02:00
_challenges.py Fire alarm daily, harden dismissal challenge; ignore wake_state.json 2026-07-10 23:14:40 +02:00
_constants.py Phase 2a: retry card-profile activation so the HDMI sink can appear 2026-08-26 23:02:52 +02:00
_correlation.py Phase 1: structured JSONL logging with cross-process dedupe 2026-08-26 23:00:08 +02:00
_escape_flow.py Size the alarm's type in pixels, and gate every screen on fitting 768px 2026-08-03 16:04:27 +02:00
_log_dedupe.py Phase 1: structured JSONL logging with cross-process dedupe 2026-08-26 23:00:08 +02:00
_log_sink.py Phase 1: structured JSONL logging with cross-process dedupe 2026-08-26 23:00:08 +02:00
_logging_setup.py Phase 1: structured JSONL logging with cross-process dedupe 2026-08-26 23:00:08 +02:00
_reactivate.py Phase 2a: retry card-profile activation so the HDMI sink can appear 2026-08-26 23:02:52 +02:00
_sinks.py Replace sleep-and-hope audio waits with per-cycle presence checks 2026-07-11 18:51:03 +02:00
_smart_plug.py Extract wake_alarm from testsAndMisc as a standalone repo 2026-06-22 12:31:40 +02:00
_state.py Extract wake_alarm from testsAndMisc as a standalone repo 2026-06-22 12:31:40 +02:00
_sync.py Discover alarm device directories instead of reading a fixed phone path 2026-08-09 17:06:43 +02:00
_trigger.py Phase 1: structured JSONL logging with cross-process dedupe 2026-08-26 23:00:08 +02:00