Live ride wait times for UK theme parks (Alton Towers, Blackpool Pleasure Beach, Chessington, Flamingo Land, Legoland Windsor, Paultons Park, Thorpe Park, and any UK park Queue-Times adds), from Queue-Times.com, with weather from Open-Meteo.
One small binary, three parts you can run in any mix:
| Part | What it is |
|---|---|
led |
The main display. Drawn in the terminal for now; this is what will drive a real LED matrix. |
api |
A JSON API for the data and the settings. |
web |
A browser display that looks like an RGB LED matrix. Includes the api. |
coaster-queue # led only (the default)
coaster-queue -r api # headless API
coaster-queue -r led,api # display plus API on one device
coaster-queue -r web # API and web display (same as `serve`)
coaster-queue -r all
All parts share one data store and one poller, and the poller only fetches the parks somebody is looking at, so running more parts costs almost nothing extra. Idle, the API uses under 5 MB of memory, two threads and no CPU between polls; connections to the data sources are kept alive, and the server's per-connection threads are small and capped.
Parks. Name them on the command line (coaster-queue chess legoland; a unique start of the name is
enough), or use parks in the config. --list-parks shows the choices.
Ride groups. Restrict the display to some kinds of ride with -S thrill,family (or show in the
config). Built-in groups: thrill, family, kids, water, transport, dark-rides, scare and
extras (--list-groups). They match rides by name and by the land queue-times files them under, so new
parks pick up most of them automatically; the lists are curated by hand and will be imperfect, so
override anything that's wrong.
Custom groups and ride lists are defined in the config file, and are the same thing:
[groups.my-favourites]
rides = ["Nemesis*", "The Smiler", "Stealth"] # a custom list of rides
[groups.big-day-out]
include = ["thrill", "transport"] # combine other groups
parks = ["alton-towers"] # ...for just this parkPatterns ignore case and may use *. A group can also match lands. Use one by adding its name to
show. A group with a built-in's name replaces it.
Settings are layered, each overriding the last: built-in defaults, the config file, command-line flags, then the API while running (changes through the API last until the program restarts).
The config file is TOML, found at --config FILE, ./coaster-queue.toml,
~/.config/coaster-queue/config.toml or /etc/coaster-queue/config.toml. See
coaster-queue.example.toml for every option. Unknown keys and invalid
values are errors, so typos are caught at startup. --print-config shows what's in effect.
| Setting | Meaning |
|---|---|
parks |
park slugs to show |
show |
ride groups to restrict to (empty: every ride) |
dwell |
seconds each page stays up |
show_closed |
include closed rides |
interval |
seconds between polls |
brightness, theme, gradients, weather, feels_like, clock24, flip, shine, cols |
display options (used by the web display now, and by the LED matrix later) |
On the command line, -s KEY=VALUE sets any of them (-s dwell=10 -s theme=amber), and -S, -a
and -i are shorthands for show, show_closed and interval. Server options are -b/--bind,
-p/--port, --web-dir and --api-token. Run coaster-queue --help for everything.
| Endpoint | |
|---|---|
GET /api/queues |
current data for the configured parks. ?parks=a,b picks parks (all for every park), ?show=thrill,family picks groups (show= for every ride) |
GET /api/parks |
every park available: id and name |
GET /api/groups |
every ride group: id, label, builtin |
GET /api/settings |
the current settings |
PATCH /api/settings |
change settings with a JSON object, e.g. {"dwell": 12, "show": ["thrill"]}. All or nothing; errors come back as {"error": "..."} with a 400 |
If api_token is set (config or --api-token), changes need Authorization: Bearer <token>; reads stay
open. Without a token anyone who can reach the port can change settings, so leave the server on
127.0.0.1 or set a token before listening on other addresses.
The web part serves a display that looks like an RGB LED matrix: text resolves like a split-flap board,
long names scroll, wait bars glide, arrows show which way a wait last moved, and the footer shows the
current weather for the park on screen (it scrolls if it's too long for the space, and feels_like
turns the "feels like" temperature off), and the overall page number (e.g. 4/9, counting every park) stays
in the corner. Press s (or the Settings link under the board) for a settings
menu drawn on the matrix, p to choose parks and r to choose ride groups.
The page starts from the server's settings. Changes made in a browser are remembered in that browser (and
in the URL, so a tuned board can be bookmarked), and only for settings that viewer changed; the rest keep
following the server. URL parameters: parks, show, park, theme, dwell, brightness, cols,
gradients, weather, feels_like, clock24, flip, shine, closed, where park=all and show=all mean
"everything". Add ?demo for made-up queues when the parks are shut.
| Key | Action |
|---|---|
space |
pause paging |
← → |
previous / next page (or tap the left third / the rest of the screen) |
↑ ↓ |
brightness |
s p r |
settings menu, choose parks, choose ride groups |
t g c d w |
theme, gradients, closed rides, dot density, weather |
1–9 0 |
show just that chosen park / all chosen parks |
f h |
fullscreen, help |
Shows each chosen park in a panel, and takes turns when more are chosen than fit on screen. It redraws
only when data, settings or the clock change, and always shows which screen of parks it's on. Keys: q quit, r refresh now, a show or hide closed
rides.
cargo test # all tests (no network needed)
cargo clippy # lints
cargo run -- -r web --web-dir web # serve the web display from disk, so edits show on refresh
How it's laid out:
Rust (src/) |
|
|---|---|
main.rs, lib.rs |
main.rs starts the parts that were asked for; everything else is in the library, so tests/ can use it |
config.rs |
command line and config file |
settings.rs |
the settings and their validation (shared by file, flags and API) |
groups.rs, groups.toml |
ride groups and the built-in categories |
store.rs |
the shared data store and the single poller |
queue.rs |
clients for queue-times.com and Open-Meteo |
led.rs |
the main display (terminal for now) |
web.rs |
the HTTP server: API and web display files |
Tests are kept out of the code they test, and are only compiled by cargo test, never into the
release binary:
| Tests | |
|---|---|
src/<module>/tests.rs |
unit tests, one file per module (e.g. src/store/tests.rs), pulled in by #[cfg(test)] mod tests;. Being inside the module they can test its private functions |
tests/api.rs |
the HTTP server end to end, on a real socket |
tests/cli.rs |
the real binary: exit codes, output, how flags and the config file combine |
tests/upstream.rs |
decoding queue-times and Open-Meteo responses, from the saved copies in tests/fixtures/ |
When a bug is fixed, add a test for it to whichever of these is closest, with a comment saying what went wrong.
Web display (web/) |
|
|---|---|
app.js |
entry point and draw loop |
data.js, demo.js |
the feed from the API, and made-up data for ?demo |
settings.js, controls.js |
settings (parse, save, step) and keyboard/touch controls |
board.js, overlays.js, weather.js |
what's drawn: the board, the menu/picker/help screens, the weather line |
led.js (WebGL matrix), font.js, fx.js (effects and themes), color.js, layout.js, util.js |
rendering and shared helpers |
Wait times are provided by Queue-Times.com and weather by Open-Meteo.com (CC BY 4.0); keep both links on the web display.
GNU General Public License v3.0 or later; see LICENSE.