a raspberry pi alarm clock you actually want to wake up to
Set a wake-up time via a dark-mode web UI served on port 3141. At the scheduled moment, the Pi fades volume in over 15 minutes and starts playing — either a Spotify playlist via the Spotify Connect API, or internet radio via MPD/MPC. If Spotify's device goes missing, it falls back to radio automatically. A crontab entry fires a curl to the local server, so the alarm persists across reboots. Optional numpad controls let you physically play/pause, skip stations, and set volume without ever touching a screen.
HTTP API with ~20 routes. Manages alarm state, Spotify OAuth, volume control, radio playback, and cron scheduling. Global state for alarm_time, alarm_active, and currently_playing.
server.py — 650 linesDark Jinja2 template with time picker, alarm toggle, volume/balance sliders, and playback buttons.
templates/index.htmlpython-crontab writes a job tagged "SPOTIFY ALARM" that curls /radioalarm or /spotialarm at wake time.
python-crontab libpynput listener maps USB numpad keys to play, pause, volume, and station switching.
keyboard_input.pyOAuth Authorization Code flow. Picks a random track from a playlist via spotipy, pushes playback to a hardcoded device ID.
server.py → spotify_request()Subprocess calls to mpc for play/stop/next/prev. Stations added via mpc add. NTS Live configured by default.
Makefile → radio-stationsamixer controls with stereo balance. Threaded fade-in/fade-out over configurable duration (default 15 min).
server.py → fade_volume_in()Fires a "wakeup_alarm_triggered" webhook on alarm start — for smart lights, etc.
server.py → homeassistant_triggerWebhook()Spotify credentials, device ID, playable URI, and error preferences in config.ini.
config_reader.py + config.iniAll routes live in server.py. The web UI talks to these over XHR. The crontab alarm fires via curl.
The codebase is intentionally small — one main file, one template, one config. That means changes land fast and you can hold the whole thing in your head.
Replace virtualenv + requirements.txt with uv and a pyproject.toml. Update Makefile targets accordingly.
easyThe systemd service and Makefile assume ~/Developer/spotify-alarm-clock. Make this configurable or auto-detect.
easyCurrently one alarm at a time. The crontab layer can hold many — the UI and state management need to catch up.
mediumAlarm state is global Python variables. A reboot or crash loses the in-memory state. Consider SQLite or a simple JSON file.
mediumThe fade-in thread and play command can race — volume might "flash" loud before the fade starts. The 3s sleep is a workaround, not a fix.
medium650 lines doing routing, Spotify OAuth, volume math, cron management, and radio control. Good candidate for splitting into modules.
hard