Config-driven controller for the Elgato Stream Deck+. One TOML file drives the 8 buttons, 4 dials, and the LCD touch strip. Written in Rust; runs as a single binary in the foreground, as a daemon, or inside a terminal UI.
The fun part is the LCD strip: Sonos album art, an animated clock, live weather with condition videos, camera feeds. The buttons do the boring-but-useful part.
- Buttons — emoji or text labels, PNG icons, colors, live RTSP streams, Home Assistant camera snapshots
- Dials — separate actions for press, turn right, and turn left (e.g. Sonos volume on a knob, like nature intended)
- LCD strip — four segments, each showing one of: Sonos album art, clock (with time-of-day videos), Home Assistant weather (with condition videos), static text, a live stream, or an HA camera
- Actions —
shell,hotkey,http,mqtt,launch,multi,toggle,page,display(live command output on a button),ha(Home Assistant service calls),sonos - Pages — named groups of buttons; switch between them from a button
- Web UI — config editor and live device view (default port 2389)
- TUI — the same, but ratatui
- Hot reload — edit the config file, the deck updates; invalid configs are rejected and the last good one keeps running
- A Stream Deck+ (the model with the LCD strip and knobs)
- Rust toolchain to build
ffmpegon PATH — only needed for video animations and live streams- Developed and daily-driven on macOS
git clone https://github.com/2389-research/decker.git
cd decker
cargo build --release -p decker
./target/release/decker setup # interactive setup wizard
./target/release/decker run --web # foreground + web UI on :2389Other subcommands:
decker daemon --port 2389 # headless
decker tui # terminal UI
decker validate # check the config, exit
decker list-devices # enumerate connected Stream Decks
decker brightness 75
decker demo # fill the deck with red/green toggles
decker set 3 --label "Hi" --action-type shell --action-value "say hi"Default location is your platform config directory — on macOS
~/Library/Application Support/decker/config.toml, on Linux
~/.config/decker/config.toml. Override with --config <path>.
[device]
brightness = 75
serial = "auto"
[sonos]
speaker_ips = ["192.168.1.50"]
[homeassistant]
url = "https://your-instance.ui.nabu.casa"
token_env = "HA_TOKEN" # read from the environment, never stored here
[pages.main]
label = "Main"
[[pages.main.buttons]]
position = 0
label = "🔒"
action = { type = "shell", command = "pmset displaysleepnow" }
[[pages.main.buttons]]
position = 1
label = "Media >"
action = { type = "page", target = "media" }
[[pages.main.encoders]]
position = 0
label = "Vol"
action_turn = { type = "sonos", room = "Living Room", command = "volume_up" }
action_turn_left = { type = "sonos", room = "Living Room", command = "volume_down" }
action_press = { type = "sonos", room = "Living Room", command = "play_pause" }
[[pages.main.lcd_segments]]
position = 0
type = "sonos_art"
room = "Living Room"
[[pages.main.lcd_segments]]
position = 1
type = "clock"
format = "%-I:%M"
[pages.media]
label = "Media"
[[pages.media.buttons]]
position = 0
label = "< Back"
action = { type = "page", target = "main" }Icon paths resolve relative to the config file's directory. MQTT credentials
and the Home Assistant token come from environment variables (password_env,
token_env) so the config file stays safe to share.
decker/— the library and CLI binary: engine, executor, renderer, web UI, TUI, device traitdecker-app/— a Tauri 2 shell that wraps the web UI in a macOS app (work in progress)
MIT — see LICENSE. Bundled fonts (JetBrains Mono, Noto Emoji) are
licensed under the SIL Open Font License 1.1 — see decker/fonts/OFL-*.txt.