deckmaster

command module
v0.14.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 54 Imported by: 0

README

deckmaster

Latest Release Software License Build Status Go Version Nix Flake

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.

Instead of a deck file, decks takes the deck files as Nix, keyed by name without .deck. They're written to one directory, so they can refer to each other by file name, and deckmaster starts with main:

programs.deckmaster.decks = {
  main.keys = [
    { index = 0; widget.id = "clock"; }
    {
      index = 1;
      widget = { id = "button"; config.label = "Media"; };
      action.deck = "media.deck";
    }
  ];
  media = { parent = "main.deck"; keys = [ ]; };
};

Relative icon paths resolve in that directory too, so use icon packs or absolute paths (icon = "${./icons/mute.svg}").

To use several Stream Decks at once, add one entry to instances per device. Each one runs as its own deckmaster-<name> user service, takes the same options as the top level and needs the device's serial number, which deckmaster -device x lists:

programs.deckmaster.instances = {
  plus = {
    device = "A00WA0000000";
    decks.main = { ... };
  };
  xl = {
    device = "A00NA0000000";
    deck = "~/.config/deckmaster/xl.deck";
  };
};

iconPacks adds icon packs to the service, so deck files can use icons by name. Instances use the top-level packs unless they set their own. 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.

Icon packs

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:

  1. the directories in $DECKMASTER_ICON_PATH (colon-separated)
  2. ~/.local/share/deckmaster/icons ($XDG_DATA_HOME/deckmaster/icons)
  3. deckmaster/icons in 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 to WAYLAND_DISPLAY; if that isn't set, e.g. in a systemd user service, it uses the first wayland-N socket 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!

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL