README
¶
deckmaster
An application to control your Elgato Stream Deck on Linux
Features
- Multiple pages & navigation between decks
- Buttons (icons & text)
- Background images
- Brightness control
- Supports different actions for short & long presses
- Supports all Stream Deck models, including:
- Stream Deck+ and + XL dials and touch screen
- Stream Deck Studio dials with LED rings
- Stream Deck Neo touch keys and info screen
- Stream Deck Pedal
- Comes with a collection of widgets:
- Buttons
- Time (with formatting)
- CPU/Mem usage
- Weather
- Command output
- Recently used windows (X11-only)
- Lets you trigger several actions:
- Run commands
- Emulate a key-press
- Paste to clipboard
- Trigger a dbus call
Supported Devices
| Device | Keys | Other controls |
|---|---|---|
| Stream Deck Original, MK.2, Module 15 Keys (all revisions) | 5×3 | |
| Stream Deck Mini, Module 6 Keys (all revisions) | 3×2 | |
| Stream Deck XL, Module 32 Keys (all revisions) | 8×4 | |
| Stream Deck+ | 4×2 | 4 dials, touch screen |
| Stream Deck + XL* | 9×4 | 6 dials, touch screen |
| Stream Deck Neo* | 4×2 | 2 touch keys with LEDs, info screen |
| Stream Deck Studio* | 16×2 (144×112 pixels) | 2 dials with LED rings |
| Stream Deck Pedal* | 3 pedals |
* Support for these models follows the reference implementations of their protocol, but hasn't been tested on hardware yet. Please report how it works!
Installation
Packages
From source
Make sure you have a working Go environment (Go 1.26 or higher is required). See the install instructions.
To install deckmaster, simply run:
git clone https://github.com/nix-er/deckmaster.git
cd deckmaster
go build
Or install it directly with:
go install github.com/nix-er/deckmaster@latest
Building requires cgo (a C compiler) for USB access.
Nix
The repository is a Nix flake. To run deckmaster without installing it:
nix run github:nix-er/deckmaster -- -deck main.deck
To add it to a NixOS configuration, add the flake as an input and import its
module. It installs deckmaster and the udev rules described below and, when
deck is set, runs deckmaster as a systemd user service in your graphical
session:
{
inputs.deckmaster.url = "github:nix-er/deckmaster";
# in your NixOS configuration:
imports = [ inputs.deckmaster.nixosModules.default ];
programs.deckmaster = {
enable = true;
deck = "~/.config/deckmaster/main.deck"; # optional
brightness = 50;
};
}
If you use home-manager, you can run the service from there instead with
inputs.deckmaster.homeManagerModules.default and services.deckmaster, which
takes the same options. Keep programs.deckmaster.enable in your NixOS
configuration for the udev rules. Both modules also accept keyboard, device,
sleep and extraArgs.
iconPacks adds icon packs to the service, so deck files can use
icons by name. The flake packages Simple Icons, which Nix fetches from the pack's
release at build time:
programs.deckmaster.iconPacks = [ inputs.deckmaster.packages.${pkgs.system}.simple-icons ];
overlays.default adds pkgs.deckmaster, if you'd rather use the package directly.
nix develop opens a shell with Go, a C compiler, golangci-lint and goreleaser.
System Setup
On Linux you need to set up some udev rules to be able to access the device as
a regular user. Copy udev/70-streamdeck.rules to
/etc/udev/rules.d/ (on NixOS, use services.udev.packages as shown above):
sudo cp udev/70-streamdeck.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
The rules use TAG+="uaccess", which gives the user logged in on the local
seat access to the device, so no extra group membership is needed. The file name
must sort before 73-seat-late.rules, or uaccess has no effect. Unplug and
re-plug the device, and you should be good to go.
Emulating key presses needs no extra setup: deckmaster talks to your desktop
session directly (see Emulate key-presses). Only the
optional -keyboard uinput backend needs write access to /dev/uinput; see
the commented rule at the end of the file.
Starting deckmaster automatically
On NixOS, set deck in the Nix module instead.
If you want deckmaster to be started automatically upon device plugin, you can
use systemd path activation, adding streamdeck.path and streamdeck.service
files to $HOME/.config/systemd/user.
streamdeck.path contents:
[Unit]
Description="Stream Deck Device Path"
[Path]
# /dev/streamdeck is created by the optional SYMLINK rule at the end of
# udev/70-streamdeck.rules
PathExists=/dev/streamdeck
Unit=streamdeck.service
[Install]
WantedBy=default.target
streamdeck.service contents:
[Unit]
Description=Deckmaster Service
[Service]
# adjust the path to deckmaster and .deck file to suit your needs
ExecStart=/usr/local/bin/deckmaster --deck path-to/some.deck
Restart=on-failure
ExecReload=kill -HUP $MAINPID
[Install]
WantedBy=default.target
Then enable and start the streamdeck.path unit:
systemctl --user enable streamdeck.path
systemctl --user start streamdeck.path
Usage
Start deckmaster with the initial deck configuration you want to load:
deckmaster -deck deck/main.deck
You can control the brightness, in percent:
deckmaster -brightness 50
Control a specific streamdeck:
deckmaster -device [serial number]
Set a sleep timeout after which the screen gets turned off:
deckmaster -sleep 10m
Choose how key presses get emulated (auto, wayland, x11, uinput or
none, see Emulate key-presses):
deckmaster -keyboard wayland
Configuration
You can find a few example configurations in the decks directory. Edit them to your needs!
Widgets
Any widget is build up the following way:
[[keys]]
index = 0
index needs to be present in every widget and describes the position of the
widget on the streamdeck. index is 0-indexed and counted from top to bottom
and left to right.
Update interval for widgets
Optionally, you can configure an update interval for each widget:
[keys.widget]
id = "button"
interval = 500 # optional
The attribute interval defines the time in ms between two consecutive
updates of a widget.
Button
A simple button that can display an image and/or a label.
[keys.widget]
id = "button"
[keys.widget.config]
icon = "/some/image.png" # optional
label = "My Button" # optional
fontsize = 10.0 # optional
color = "#fefefe" # optional
flatten = true # optional
icon is either a PNG or SVG file, or the name of an icon from an
icon pack, such as icon = "firefox". If flatten is true
all opaque pixels of the icon will have the color color.
An icon without a file extension, like firefox or simple-icons/firefox, is
looked up by name, in icon packs that live in these directories:
- the directories in
$DECKMASTER_ICON_PATH(colon-separated) ~/.local/share/deckmaster/icons($XDG_DATA_HOME/deckmaster/icons)deckmaster/iconsin each of$XDG_DATA_DIRS, e.g./usr/share/deckmaster/icons
Each directory in there is a pack, keeping its .svg or .png icons either
directly inside or in an icons subdirectory. That makes unpacked Stream Deck
icon packs work as they are. simple-icons/firefox only looks in the pack
called simple-icons, while firefox takes the first match from any pack.
For example, to install Simple Icons,
3000+ brand logos in their brand colors, download the .streamDeckIconPack
file from its releases
and unzip it:
mkdir -p ~/.local/share/deckmaster/icons/simple-icons
unzip com.mackenly.simpleiconsstreamdeck.streamDeckIconPack -d /tmp/simple-icons
mv /tmp/simple-icons/*.sdIconPack/* ~/.local/share/deckmaster/icons/simple-icons/
On NixOS, use the module's iconPacks option instead (see Nix). Some
logos are black, which is invisible on a black key. Give them a color with
color = "#ffffff" and flatten = true.
Recent Window (requires X11)
Displays the icon of a recently used window/application. Pressing the button activates the window.
[keys.widget]
id = "recentWindow"
[keys.widget.config]
window = 1
showTitle = true # optional
If showTitle is true, the title of the window will be displayed below the
window icon.
Time
A flexible widget that can display the current time or date.
[keys.widget]
id = "time"
[keys.widget.config]
format = "%H;%i;%s"
font = "bold;regular;thin" # optional
color = "#fefefe" # optional
layout = "0x0+72x24;0x24+72x24;0x48+72x24" # optional
With layout custom layouts can be definded in the format [posX]x[posY]+[width]x[height].
Values for format are:
| % | gets replaced with |
|---|---|
| %Y | A full numeric representation of a year, 4 digits |
| %y | A two digit representation of a year |
| %F | A full textual representation of a month, such as January or March |
| %M | A short textual representation of a month, three letters |
| %m | Numeric representation of a month, with leading zeros |
| %l | A full textual representation of the day of the week |
| %D | A textual representation of a day, three letters |
| %d | Day of the month, 2 digits with leading zeros |
| %h | 12-hour format of an hour with leading zeros |
| %H | 24-hour format of an hour with leading zeros |
| %i | Minutes with leading zeros |
| %s | Seconds with leading zeros |
| %a | Lowercase Ante meridiem and Post meridiem |
| %t | Timezone abbreviation |
Top
This widget shows the current CPU or memory utilization as a bar graph.
[keys.widget]
id = "top"
[keys.widget.config]
mode = "cpu"
color = "#fefefe" # optional
fillColor = "#d497de" # optional
There are two values for mode: cpu and memory.
Command
A widget that displays the output of commands.
[keys.widget]
id = "command"
[keys.widget.config]
command = "echo 'Files:'; ls -a ~ | wc -l"
font = "regular;bold" # optional
color = "#fefefe" # optional
layout = "0x0+72x20;0x20+72x52" # optional
Weather
A widget that displays the weather condition and temperature.
[keys.widget]
id = "weather"
[keys.widget.config]
location = "MyCity" # optional
unit = "celsius" # optional
color = "#fefefe" # optional
flatten = true # optional
theme = "openmoji" # optional
The supported location types can be found here. The unit
has to be either celsius or fahrenheit. If flatten is true all opaque
pixels of the condition icon will have the color color. In case theme is set
corresponding PNG or SVG icons with correct names need to be placed in
~/.local/share/deckmaster/themes/[theme]. The default icons with their
respective names can be found here.
Actions
You can hook up any key with several actions. A regular keypress will trigger
the widget's configured keys.action, while holding the key will trigger
keys.action_hold.
Switch deck
[keys.action]
deck = "relative/path/to/another.deck"
Run a command
[keys.action]
exec = "some_command --with-parameters"
Emulate key-presses
[keys.action]
keycode = "Leftctrl-C"
Emulate a series of key-presses with delay in between:
[keys.action]
keycode = "Leftctrl-X+500 / Leftctrl-V / Num1"
A list of available keycodes can be found here: keycodes
Keycodes name physical keys on a US keyboard, whatever layout you use.
By default (-keyboard auto), deckmaster sends key presses through your desktop
session, so they only reach the apps in it:
- On Wayland, it uses the virtual keyboard protocol
(
zwp_virtual_keyboard_manager_v1), which niri, Hyprland, Sway, river and other wlroots compositors support. deckmaster connects toWAYLAND_DISPLAY; if that isn't set, e.g. in a systemd user service, it uses the firstwayland-Nsocket in$XDG_RUNTIME_DIR. - On X11, it uses the XTEST extension.
Some compositors, such as niri, don't apply their own keybindings to virtual keyboard input; key presses go straight to the focused window. To trigger a compositor action (volume, overview, workspaces), run it directly instead of emulating its shortcut:
[keys.action]
exec = "niri msg action show-hotkey-overlay"
-keyboard uinput creates a kernel-level virtual keyboard instead. It works
everywhere, including on the text console and the lock screen, but it needs
write access to /dev/uinput, and every program you run then gets that same
access. Only use it if the other backends don't work for you.
Paste to clipboard
[keys.action]
paste = "some text"
This copies the text to the clipboard and presses ctrl-v. It needs wl-copy
(from wl-clipboard) on Wayland, or xclip or xsel on X11; the Nix package
brings its own.
Most terminals, such as Ghostty, paste with ctrl-shift-v instead. Set
paste_keys to the keys to press, written like a keycode:
[keys.action]
paste = "some text"
paste_keys = "Leftctrl-Leftshift-V"
Trigger a dbus call
[keys.action.dbus]
object = "object"
path = "path"
method = "method"
value = "value" # optional
Without a value the method is called without arguments. A plain value is
passed as a string; prefix it with a type like dbus-send does to pass
something else, e.g. int32:4, uint32:4, int64:4, uint64:4,
int16:4, uint16:4, byte:4, double:0.5, boolean:true,
objpath:/org/example or string:int32:4 (the string int32:4).
Device actions
Increase the brightness. If no value is specified, it will be increased by 10%:
[keys.action]
device = "brightness+5"
Decrease the brightness. If no value is specified, it will be decreased by 10%:
[keys.action]
device = "brightness-5"
Set the brightness to a specific value between 0 and 100:
[keys.action]
device = "brightness=50"
Put the device into sleep mode, blanking the screen until the next key gets pressed:
[keys.action]
device = "sleep"
Background Image
You can configure each deck to display an individual wallpaper behind its widgets:
background = "/some/image.png"
Re-using another deck's configuration
If you specify a parent inside a deck's configuration, it will inherit all
of the parent's settings that are not overwritten by the deck's own settings.
This even works recursively:
parent = "another.deck"
Dials, swipe actions and the info screen widget are inherited the same way.
Dials and touch screen
The Stream Deck+ (four dials) and + XL (six dials) have a touch screen segment
above each dial. A dial and its segment are configured together in a [[dials]] entry,
with index 0 being the leftmost dial. All settings are optional:
[[dials]]
index = 0
# widget shown on the touch screen segment above the dial
[dials.widget]
id = "button"
[dials.widget.config]
icon = "/some/volume.png"
label = "Volume"
# pressing the dial (action_hold for long presses)
[dials.action]
keycode = "Mute"
# turning the dial counterclockwise / clockwise
[dials.action_left]
keycode = "VolumeDown"
[dials.action_right]
keycode = "VolumeUp"
# tapping the segment (action_touch_hold for long presses)
[dials.action_touch]
exec = "pavucontrol"
All widgets and actions work on dials. Buttons, time and command widgets
use the whole segment (a button shows its icon left of its label); the others
are drawn as a square in the middle of their segment. The touch screen can't
show fine detail, so large icons and short labels work best, e.g. a single
line format = "%H:%i" for a clock. A fast turn sends several steps at once;
the turn action then runs once per step (at most 10 times), except for deck
switches.
Tapping a segment without an action_touch triggers the widget's own action
(e.g. activating a window for recentWindow).
Swiping across the touch screen can trigger an action, e.g. to switch decks:
[swipe]
[swipe.left]
deck = "next.deck"
[swipe.right]
deck = "previous.deck"
See decks/plus.deck for a complete example.
Stream Deck Studio dial LEDs
The Stream Deck Studio's two dials have no screen, but an LED ring and a knob
LED. Configure them in the [[dials]] entry, next to the dial's actions. The
ring can show a value from 0 to 100, printed by a command:
[[dials]]
index = 0
ring_color = "#00c060"
knob_color = "#ffffff"
# optional: light only part of the ring, e.g. to show the volume
ring_value = "pamixer --get-volume"
ring_interval = 1000 # milliseconds, optional
The value command runs every ring_interval milliseconds (every second by
default), and right after the dial is turned or pressed. LEDs without a color
are turned off. See decks/studio.deck for a complete
example.
Stream Deck Neo touch keys and info screen
The Stream Deck Neo's two touch keys have no display, but an LED. They are
keys 8 and 9, configured like the other keys with a color instead of a
widget:
[[keys]]
index = 8
color = "#ff8000"
[keys.action]
keycode = "VolumeDown"
Any widget can be shown on the info screen between them (248×58 pixels):
[info.widget]
id = "time"
[info.widget.config]
format = "%H:%i"
See decks/neo.deck for a complete example.
Stream Deck Pedal
The pedals are keys 0 to 2 (left to right). As they have no display, only their actions are used.
More Decks!
- deckmaster-emojis, an Emoji keyboard deck
- deckmaster-helldivers2, a deck for calling down Helldivers 2 game stratagems
Made your own useful decks? Submit a pull request!
Feedback
Got some feedback or suggestions? Please open an issue.
deckmaster is a fork of muesli/deckmaster by Christian Muehlhaeuser.
Documentation
¶
Overview ¶
Deckmaster is an application to control Elgato Stream Deck devices on Linux.