An unofficial Omarchy plugin for controlling studio hardware: lights, stream deck and CamLink.
Note
I am not a programmer. I used Claude Code and Codex to build this for myself. The code and docs are fully generated by AI. Excuse my ignorance to etiquette or common practice here. Any feedback and contributions welcome!
Legend: ✓ yes, ✗ no, ? plausible but unconfirmed.
I could only test hardware that I own.
Note
If you own hardware that is not supported, please feel free to test, add it, and submit a PR.
Layout comes from the elgato-streamdeck crate, so every model it recognises is
laid out correctly with no change here. All are USB vendor 0fd9.
| Device | Grid | USB ID | Supported | Tested | What the plugin does with it |
|---|---|---|---|---|---|
| Stream Deck MK.2 | 5x3 | 0080 |
✓ | ✓ | Key grid, pages, multi-key editing, brightness, display power |
| Stream Deck | 5x3 | 0060 |
✓ | ✗ | Same grid and paging as the MK.2 |
| Stream Deck V2 | 5x3 | 006d |
✓ | ✗ | Same grid and paging as the MK.2 |
| Stream Deck Scissor Keys | 5x3 | 00a5 |
✓ | ✗ | Same as the MK.2; only the key switches differ |
| Stream Deck Mini | 3x2 | 0063 |
✓ | ✗ | Three by two grid, paging, same editing surface |
| Stream Deck Mini MK.2 | 3x2 | 0090 |
✓ | ✗ | Three by two grid, paging, same editing surface |
| Stream Deck Mini: Discord Edition | 3x2 | 00b3 |
✓ | ✗ | Driven identically to the Mini |
| Stream Deck XL | 8x4 | 006c |
✓ | ✗ | Eight by four grid, paging, same editing surface |
| Stream Deck XL V2 | 8x4 | 008f |
✓ | ✗ | Eight by four grid, paging, same editing surface |
| Stream Deck Neo | 4x2 | 009a |
✓ | ✗ | Key grid and paging; the two touch keys are not addressable |
| Stream Deck + | 4x2 | 0084 |
✓ | ✗ | Key grid and paging; dials and touch strip are not addressable |
| Stream Deck Pedal | 3 | 0086 |
✓ | ✓ | Tap, hold and double bindings for each of the three pedals |
| Stream Deck Module (6 keys) | 3x2 | 00b8 |
✓ | ✗ | OEM panel-mount module, driven as its desktop equivalent |
| Stream Deck Module (15 keys) | 5x3 | 00b9 |
✓ | ✗ | OEM panel-mount module, driven as its desktop equivalent |
| Stream Deck Module (32 keys) | 8x4 | 00ba |
✓ | ✗ | OEM panel-mount module, driven as its desktop equivalent |
| Stream Deck + XL | 9x4 | 00c6 |
✗ | ✗ | Not yet in the device library, so nothing here recognises it |
| Stream Deck Studio | 16x2 | 00aa |
✗ | ✗ | Not in the device library; the rack unit is unrecognised |
| Stream Deck Mobile, Virtual Stream Deck | - | - | ✗ | ✗ | Software, not USB hardware; out of scope |
Auto-pagination puts its arrows at the two ends of the bottom row, which is sensible on every grid but has only been seen on a 5x3.
Discovered over mDNS as _elg._tcp and driven over the local /elgato/lights
HTTP API on port 9123. The plugin reads and writes on, brightness and
temperature, so temperature-based lights are fully covered and hue-based ones
are not.
| Device | Colour model | Supported | Tested | What the plugin does with it |
|---|---|---|---|---|
| Key Light | Temperature | ✓ | ✓ | Power, brightness, temperature, saved default, rename and reorder |
| Key Light Air | Temperature | ✓ | ✓ | Power, brightness, temperature, saved default, rename and reorder |
| Key Light Mini | Temperature | ✓ | ✗ | Same API; discontinued; battery level is not read |
| Ring Light | Temperature | ✓ | ✗ | Power, brightness and temperature; discontinued |
| Light Strip | Hue | ✓ | ✗ | Power and brightness only; it is hue based, so no colour control |
| Key Light Neo | Temperature | ? | ✗ | Local HTTP API is not confirmed on this model |
| Key Light Air MK.2 | Temperature | ? | ✗ | Local HTTP API is not confirmed on this model |
| Light Strip Pro | Hue, addressable | ? | ✗ | Per-LED addressing does not fit the flat API this plugin speaks |
Three of these are marked ? because the vendor does not document the local API and
the newer models are absent from every source that lists it. One command settles
it on your own hardware, and the answer is worth an issue either way:
curl -s http://<light-ip>:9123/elgato/lightscamlink-ctl matches /dev/v4l/by-id against a device_pattern, so any device the
kernel exposes as a UVC webcam can drive the overlay. You can change it to something else
by editing ~/.config/camlink-ctl/config.toml:
device_pattern = "Facecam"| Device | Supported | Tested | What the plugin does with it |
|---|---|---|---|
| Cam Link 4K | ✓ | ✓ | Overlay placement by corner or dragged area, plus recording |
| Facecam MK.2 | ✓ | ✗ | UVC 1.5; set device_pattern and the overlay works |
| Facecam 4K | ✓ | ✗ | UVC 1.5; set device_pattern and the overlay works |
| Facecam Neo | ✓ | ✗ | UVC 1.5; set device_pattern and the overlay works |
| Facecam | ✓ | ✗ | UVC; discontinued |
| Facecam Pro | ✓ | ✗ | UVC; discontinued |
| Cam Link Pro | ✗ | ✗ | PCIe card exposing DirectShow, not UVC; no Linux support |
These present as video devices too, so the same overlay and recording path applies where the kernel binds them.
| Device | Supported | Tested | What the plugin does with it |
|---|---|---|---|
| Game Capture HD60 X | ✓ | ✗ | UVC compatible; usable as an overlay source |
| Game Capture 4K X | ✓ | ✗ | UVC; needs the mainline USB_QUIRK_NO_BOS fix for full bandwidth |
| Game Capture 4K S | ✓ | ✗ | UVC compliant; usable as an overlay source |
| Video Capture | ✓ | ✗ | Analogue capture bound by em28xx or cx231xx in mainline |
| Game Capture Neo | ? | ✗ | Plug and play, but UVC is not documented |
| Game Capture 4K Pro | ✗ | ✗ | PCIe with a proprietary Windows-only driver |
Nothing in this plugin yet drives audio hardware.
| Device | Supported | Tested | Why not |
|---|---|---|---|
| Wave:3, Wave:3 MK.2 | ✗ | ✗ | USB audio class; capture works, hardware controls do not |
| Wave XLR, Wave XLR MK.2, Wave XLR Pro | ✗ | ✗ | Interface controls need a protocol this plugin does not speak |
| Wave Neo | ✗ | ✗ | USB audio class; capture works, hardware controls do not |
| Wave DX | ✗ | ✗ | A passive XLR microphone; there is nothing to control |
| XLR Dock MK.2 | ✗ | ✗ | Attaches to a Stream Deck +, but exposes no controls here |
Nothing in this plugin yet supports teleprompters.
| Device | Supported | Tested | Why not |
|---|---|---|---|
| Prompter | ✗ | ✗ | Presents as an extra USB-C display, not a controllable device |
| Prompter XL | ✗ | ✗ | Presents as an extra USB display, not a controllable device |
omarchy plugin add https://github.com/data-goblin/omgato --enable
~/.config/omarchy/plugins/io.github.data-goblin.omgato/scripts/install
streamdeck-ctl enableomarchy plugin add only clones the plugin and places the widget on the right of
the bar. scripts/install is what builds the workspace, links the four binaries
onto PATH, installs the systemd user units, links the agent skill, and applies a
starter Stream Deck layout only if you have no configuration yet. Pass
--no-preset or --no-skill to skip either. It prints the one privileged step it
will not take for you: installing the udev rule.
Omarchy installs plugins as git checkouts and never pulls them, so an update is two steps. The second one rebuilds the binaries, and skipping it leaves you running the old ones:
omarchy plugin update io.github.data-goblin.omgato
~/.config/omarchy/plugins/io.github.data-goblin.omgato/scripts/installOrder matters. scripts/uninstall uses the binaries to undo their own Hyprland
edits, so run it before the plugin directory goes away:
~/.config/omarchy/plugins/io.github.data-goblin.omgato/scripts/uninstall
omarchy plugin remove io.github.data-goblin.omgatoWhat survives on purpose: your Stream Deck and Pedal configuration, your light
names and display order, and the udev rule at
/etc/udev/rules.d/70-streamdeck-ctl.rules, which needs root to remove. Pass
--purge to scripts/uninstall to drop the saved state as well.
Ten changes of undo history are kept, and separately you can pin one arrangement as the default and come back to it whenever a session has wandered:
omgato-panel save-default # remember the lights exactly as they are
omgato-panel restore-default # put every light back to it
omgato-panel undo # or step back one change at a timeBoth are buttons in the Key Lights view. Restore is greyed out until a default has been saved.
The panel opens over the top-right corner, which is where the camera overlay
usually sits. Rather than hiding it, the panel tells camlink-ctl the rectangle it
occupies and the overlay slides clear, returning to its own position when the
panel closes. A placement that does not overlap is left exactly where you put
it, and an overlay switched on while the panel is already open still lands clear
of it.
The overlay switch is the toggle in the top right of the Cam Link view.
Each section can be switched off, so a rig with only Key Lights shows only Key Lights. Set them in the widget settings, or from a script:
omarchy bar set io.github.data-goblin.omgato showDeck false --json| Setting | Default | What it controls |
|---|---|---|
showLights |
true |
Brightness, temperature and per-light control for Key Lights |
showDeck |
true |
Key grid, pages and pedal bindings for a Stream Deck |
showCamera |
true |
Camera overlay placement and quick screen recording |
manifest.json plugin manifest, entry point and settings schema
ui/Panel.qml the bar widget and panel
src/keylight-ctl/ Key Lights over their local HTTP API
src/streamdeck-ctl/ Stream Deck and Pedal daemons, rendering, TUI, preset
src/camlink-ctl/ Cam Link overlay and status
src/omgato-panel/ status aggregation, names, shortcuts, undo/redo history
src/skill/ the agent skill describing the tools
scripts/install build, link, install units, link the skill
scripts/install-skill link only the agent skill
scripts/uninstall reverse the installer
omgato-panel is the only crate the panel asks for state. It gathers one JSON
document from the other three tools, keeps local light names and display order
keyed by MAC so a DHCP lease change cannot orphan them, and owns the undo
histories. Device commands go straight from the panel to
keylight-ctl, streamdeck-ctl and camlink-ctl, so nothing sits between a click and
the hardware.
The plugin writes ~/.config/hypr/omgato-bindings.lua and adds a single guarded
line to your bindings.lua to source it. Your own bindings file is never
rewritten, and removing the plugin cannot break your config. Install them from
the panel, or:
omgato-panel install-shortcuts
omgato-panel set-shortcut --id lights.toggle --keys "SUPER + ALT + L"
omgato-panel uninstall-shortcutsOnly a handful are bound out of the box: one for the lights, none for the deck, and the camera and recording ones. Everything else is listed unbound in the panel, ready to be given a combination. Shortcuts are listed per section, rebindable from the panel, and warn you when a combination is already claimed.
Plugins run unsandboxed with your user permissions, so here is everything this one touches outside its own directory.
~/.local/bin/: symlinks to the four binaries it builds
~/.config/systemd/user/: streamdeck-ctl.service and streamdeck-ctl-deck.service,
the Stream Deck and Pedal daemons
~/.config/hypr/: omgato-bindings.lua, plus one guarded pcall line added
to bindings.lua, only when you install shortcuts
~/.local/state/omgato-panel/: light display names, display order, undo history
~/.cache/omgato-panel/: rendered key previews
~/.config/streamdeck-ctl/config.toml: your deck and pedal configuration
$XDG_RUNTIME_DIR/camlink-ctl/: overlay position and pid, cleared on reboot
agent skill directories: a symlink to src/skill/, only where the directory
already exists, and only if you did not pass --no-skillscripts/uninstall reverses all of it. It keeps your device configuration
unless you pass --purge.
No sudoers rule ships with the plugin, and scripts/install performs no
privileged step itself. Two operations need root:
-
installing the udev rule that grants access to Stream Deck hardware. The installer prints these and leaves them to you:
sudo install -m 0644 \ ~/.config/omarchy/plugins/io.github.data-goblin.omgato/src/streamdeck-ctl/udev/70-streamdeck-ctl.rules \ /etc/udev/rules.d/70-streamdeck-ctl.rules sudo udevadm control --reload-rules && sudo udevadm trigger
-
re-authorizing the Cam Link over USB, which clears the wedged state the device enters when its capture stream is cut. This is the one place the code calls
sudofor you:camlink-ctlrunssudo -n tee /sys/bus/usb/devices/<dev>/authorized, writing0and then1. It fires oncamlink-ctl reset, and again insidecamlink-ctl showwhen the previous session left the device wedged. The-nmeans it never prompts, so with no cached credential and no passwordless rule of your own the reset fails, a warning goes to stderr, and the overlay starts anyway.
Runtime pid state lives in $XDG_RUNTIME_DIR/camlink-ctl/, created mode 0700.
With XDG_RUNTIME_DIR unset it falls back to a uid-suffixed directory under the
temp dir, also 0700.
keylight-ctl talks HTTP to Key Lights on your local network, discovered over mDNS
with avahi-browse (_elg._tcp). No other code in the plugin makes network calls.
Discovery accepts at most 32 unique services from 256 KiB of browse output, and
each HTTP JSON response is limited to 16 KiB. Device names, addresses and the
64 KiB cache are validated before retention. The cache is opened without
following symlinks or blocking on special files, then accepted only when that
same descriptor is a regular file owned by the current user. Live probes use a
fixed pool of at most four threads, so an untrusted or noisy LAN peer cannot grow
memory, cached device state or worker creation without bound.
The daemons are ordinary systemd user services; no second Quickshell process is
ever started.
Omarchy Quattro: the Quickshell shell this plugin draws into
Rust 1.89+: to build the workspace (rust)
avahi-browse: Key Light discovery over mDNS (avahi)
mpv: the Cam Link overlay window (mpv)
pw-dump: PipeWire state for camera status (pipewire)
gpu-screen-recorder: screen recording (gpu-screen-recorder)
ffmpeg: recording post-pass (ffmpeg)
slurp: region picking for the overlay and recording (slurp)
fonts: a text font and a Nerd Font for rendering deck keys;
fontconfig supplies a stand-in if the configured paths
do not exist
systemd --user: the Stream Deck and Pedal daemonsPackage names in brackets are the Arch packages that provide each binary.
No Stream Deck found: the udev rule is missing. Install it as shown above,
then unplug and replug the device
Keys never update: the daemons are not running. Check with
systemctl --user status streamdeck-ctl-deck
No lights discovered: run keylight-ctl discover. Key Lights answer over mDNS,
so the machine must be on the same subnet as the lights
A light reads unreachable: probes retry inside a 500ms budget, then every command
rediscovers over mDNS once and retries against the
addresses it finds, so a DHCP lease change heals itself.
A light that still reports unreachable is genuinely not
answering. Confirm with keylight-ctl ls --json
Camera overlay is black: the Cam Link is single-open. If a user service holds
it, the overlay borrows the device and gives it back
on hide. A plain process is named rather than killed.
If it stays wedged, camlink-ctl resetIssues and pull requests are welcome, particularly for hardware not marked tested
above. Please run cargo clippy --release --workspace and cargo build --release
before opening one. Release history is in CHANGELOG.md.
MIT. See LICENSE.
Elgato, Stream Deck, Key Light and Cam Link are trademarks of Corsair Gaming and its Elgato brand, which does not sponsor or endorse this project.
This is an unofficial, community-built plugin and is not affiliated with Elgato.



