A BJJ round timer for the BUSY Bar. Runs a repeating roll/rest cycle on the bar's front LED display and its speaker:
- READY countdown (default 10s) before each roll, ticking each second (higher-pitched for the last three)
- Roll countdown (default 5:00) in big green digits filling the display, opening with the buzzer
- Warning double-beep with the clock turning yellow at 1:00 remaining
- Loud triple-blast buzzer when the round ends
- Rest countdown (default 1:00) in red
The bar's own controls work during any countdown:
- START button — starts the timer (it launches waiting for START), then pauses/resumes as pressed. Paused shows a frozen white clock.
- Scroll wheel — adds or subtracts time (10s per detent,
-sto change) from whatever countdown is currently on screen. Scrolling to zero skips it.
The clock is drawn as extra_large text (10px — the largest font on the
72x16 display; the bar's native countdown element is only 5px and ignores
font) and updated once per second.
Requires Python 3.10+ and a BUSY Bar reachable over USB or Wi-Fi.
pip install busylibOr just use uv, which reads the script's inline metadata:
uv run bjj_timer.pyOver USB the bar answers at 10.0.4.20 with no token needed (the default).
Over Wi-Fi, use the bar's LAN address and pass its access-key PIN with
--token if one is set.
# Defaults: 5 min rounds, 1 min breaks, warning at 1:00 left, until Ctrl-C
python3 bjj_timer.py
# 6-minute rounds, 90-second breaks, stop after 8 rounds
python3 bjj_timer.py -r 6 -b 1.5 -n 8
# Competition pace: 7 min rounds, warning at 30 seconds
python3 bjj_timer.py -r 7 -w 30
# Over Wi-Fi with an access key
python3 bjj_timer.py --host 192.168.1.20 --token 1234| Flag | Default | Meaning |
|---|---|---|
-r, --round-minutes |
5 |
Round length in minutes |
-b, --break-minutes |
1 |
Break length in minutes (0 = none) |
-n, --rounds |
0 |
Number of rounds (0 = until Ctrl-C) |
-w, --warning-seconds |
60 |
Warning beep this long before round end (0 = off) |
-g, --ready-seconds |
10 |
READY countdown before each roll (0 = off) |
-s, --step-seconds |
10 |
Time added/removed per scroll-wheel detent |
--autostart |
off | Begin immediately instead of waiting for START |
--host |
10.0.4.20 |
Bar address (default is the USB address) |
--token |
— | Access-key PIN (Wi-Fi only, if set) |
--volume |
100 |
Speaker volume 0–100 |
- Beeps are synthesized in pure Python as raw PCM (s16le mono 44.1 kHz — the
format the firmware expects) and uploaded once at startup via
POST /api/assets/upload; no ffmpeg required. - Every phase redraws a big
textclock each second viaPOST /api/display/draw, so pause and wheel adjustments take effect instantly. - Button and scroll-wheel events arrive on the
/api/status/wsprotobuf stream (START = button event, wheel = encoder deltas), read by a background thread that reconnects on stream errors. - Sounds fire via
POST /api/audio/playreferencing the uploaded assets. - If the bar has an active BUSY session (work timer / on-call card), the firmware refuses app draws no matter the requested priority, so the script saves the session, stops it while the timer runs, and restores it on exit (including Ctrl-C).
Built on busylib against the BUSY Bar HTTP API.
See DEVELOPMENT.md for firmware quirks, API links, and the always-test-on-the-device rule before making changes.