A unique Raspberry Pi-based alarm clock that makes sure you are fully awake before it turns off!
Instead of simply pressing a button to stop the alarm, this clock requires you to solve a short memory and attention puzzle. When the alarm rings, you press the button, and the built-in LED will blink a random number of times (between 1 and 7). You then have to press the button exactly that many times to confirm. Get it right, and the music stops. Get it wrong, and you'll have to try again!
## Hardware Requirements
***Raspberry Pi 4 Model B** (2018)
***33mm Illuminated Arcade Button** (e.g., from bastelgarage.ch)
***External Speakers** (connected via 3.5mm jack or a USB Soundcard for better audio quality)
Please refer to the [arcade-button-wiring.md](arcade-button-wiring.md) file for detailed instructions on how to connect the arcade button and its LED to the Raspberry Pi's GPIO pins.
`--style` selects the alarm behaviour and defaults to `blink` (so existing cron entries keep working unchanged). Each style owns its own ringing tone: `blink` plays a music file on loop (resolved from `--music-file`, then the `MUSIC_FILE` env var, then the default `Laid Back - Sunshine Reggae.mp3`); `simple` ignores the music file and beeps.
* **Keep the API key secret.** Treat it like a password: store it only in `.env`, rotate it periodically, and generate a strong key with `python3 -c "import secrets; print(secrets.token_urlsafe(32))"`.
* **The API currently does not implement rate limiting.** The safest deployment is to expose it only on your local network or through Tailscale. If you expose it to the internet, place it behind a reverse proxy (e.g., nginx, Caddy, or Traefik) that handles TLS and brute-force protection.
* If you need built-in rate limiting, consider adding `slowapi` or a similar ASGI middleware later.
*(Note: `command` is generated automatically by the API and is **not** accepted as an input parameter on `setAlarm`, to prevent command injection into the system crontab.)*
Each alarm has a **style** that decides how you turn it off. Set it per alarm via the `style` field of `setAlarm` (or the `style` argument of `startRinging`). Available styles:
| `simple` | A repeating, ordinary-alarm-clock **beep** (no music file needed). | Press the button once. The LED stays solid on so the button is findable in the dark. **Default for new alarms.** |
| `blink` | A music file on an endless loop (from `--music-file` / `MUSIC_FILE` / the default track). | The memory-and-attention puzzle described in [How the Puzzle Works](#how-the-puzzle-works). |
**The style owns its ringing tone.** The runner hands each style an audio capability (`play_music(path)` and `play_beep()`); the style decides which to use and when. This is the seam for future styles that handle their own tone.
Adding a style is a plugin-style drop-in: add a class under `styles/` implementing the `AlarmStyle` contract (`__init__(set_led, sound, music_file)` + `start()` + `update(now, is_pressed) -> bool`) and register one line in `styles/__init__.py`.
**Backward compatibility:** existing cron entries created before this feature have no `--style` flag and keep running the `blink` puzzle, so an upgrade never silently changes an alarm. To switch an existing alarm to `simple`, re-save it with `setAlarm(id: ..., cronExpression: ..., style: "simple")`.
**Audio issues when running via Cron (ALSA: Couldn't open audio device: Unknown error 524):**
If the script runs perfectly from the terminal but crashes when triggered by cron, it might be trying to use an invalid audio output (like a disconnected HDMI port) because background jobs don't have the same environment variables as interactive sessions.
To force the system to use the 3.5mm headphone jack (Audio Jack), you can create a `~/.asoundrc` file for the `pi` user with the following content:
```
pcm.!default {
type asym
playback.pcm {
type plug
slave.pcm "hw:2,0"
}
}
ctl.!default {
type hw
card 2
}
```
*(Note: Change `hw:2,0` and `card 2` to match your actual headphone jack card index, which you can find by running `aplay -l`).*