Skip to content
This repository was archived by the owner on Oct 15, 2026. It is now read-only.
data-goblinPublic archive

About

Omarchy Quattro panel for Key Lights, Stream Deck, Stream Deck Pedal and Cam Link 4K, each with a command line tool

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Repository files navigation


Note

This repo has been archived by the author. It's no longer receiving updates and remains public just for reference. Please continue to respect LICENSE.


Omgato

Release Licence Omarchy

An unofficial Omarchy plugin for controlling studio hardware: lights, stream deck, CamLink and teleprompter.

Switching the camera overlay on from the panel, watching it park clear, raising one Key Light and dimming the other, restoring the saved default, then switching everything back off

Watch the same demo as video

Key Lights view Stream Deck view Cam Link view

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!


Supported hardware

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.

Stream Deck

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, drag to rearrange, 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.

Lights

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/lights

Cameras

camlink-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

Capture cards

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

Audio

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

Teleprompter

Device Supported Tested Notes
Prompter ✓ ✓ Through the DisplayLink driver; tested on an NVIDIA desktop
Prompter XL ? ✗ Same DisplayLink path, untested

The Prompter has no video input: it is a DisplayLink screen over USB-C, which only the closed Synaptics driver can talk to. Install the AUR packages evdi-dkms and displaylink, then the udev rule the installer prints. The rule moves the virtual DisplayLink card off your seat, so Hyprland never opens it (on NVIDIA, Hyprland cannot draw to it and would leave a dead monitor), and it starts prompter-ctl.service whenever the Prompter appears.

prompter-ctl then owns the glass in one of three modes:

script:   draws a saved script itself, white on black, with a marker on the
          reading line; scrolls only while playing, so it idles at no cost
monitor:  adds a 1024x600 Hyprland output named PROMPTER and copies it to the
          glass, so any window can go there
off:      blanks the glass and removes the PROMPTER output

Brightness dims the picture rather than the backlight: the DisplayLink driver passes no DDC/CI, so the Prompter's backlight cannot be reached from Linux.

Scripts live in ~/.local/share/prompter-ctl/scripts/ as Markdown. Write them in the panel, or open them in the Omarchy editor from it; saving the loaded script redraws the glass in place. For monitor mode, give the output its size in ~/.config/hypr/monitors.lua:

hl.monitor({ output = "PROMPTER", mode = "1024x600@60", position = "auto", scale = 1 })

Stream Deck and Pedal keys can drive it: exec:prompter-ctl toggle starts and stops scrolling, and exec:prompter-ctl back and exec:prompter-ctl forward step one line, for example on the left and right Pedal taps.

Install

omarchy plugin add https://github.com/data-goblin/omgato --enable
~/.config/omarchy/plugins/io.github.data-goblin.omgato/scripts/install
streamdeck-ctl enable

omarchy 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 five binaries onto PATH, installs the systemd user units, and applies a starter Stream Deck layout only if you have no configuration yet; pass --no-preset to skip it. It links the agent skill only when you pass --with-skill, or run scripts/install-skill yourself. It prints the one privileged step it will not take for you: installing the udev rule.

Update

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/install

Remove

Order 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.omgato

What 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.

Lights, defaults and undo

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 time

Both are buttons in the Key Lights view. Restore is greyed out until a default has been saved.

Colouring a page

A page can name its own background, which every key on that page picks up unless it sets bg itself:

streamdeck-ctl deck page-bg media '#221a2c'
streamdeck-ctl deck page-bg media           # back to the deck-wide bg_color
[deck.pages.media]
bg = "#221a2c"

A tint per page makes the current page obvious at a glance. Generated pagination keys and empty keys deliberately keep the deck-wide bg_color, so they stay visually separate from the page's own keys.

Because the colour lives on the page rather than on every button, a script can recolour the deck without touching the layout, for instance to follow the desktop theme from a hook.

Rearranging deck keys

Drag a key in the grid and drop it where you want it. Dropping on an empty key moves it there; dropping on another key swaps the two. Nothing is retyped, and the deck's undo button steps the move back. The same thing from a script:

streamdeck-ctl deck move omarchy 3 7              # 3 and 7 trade places
streamdeck-ctl deck move omarchy 3 0 --to-page apps

A click still selects a key for editing, so only a drag of a few pixels starts a move, and only real keys move; the paging arrows are generated, not configured.

streamdeck-ctl tui has the same move on the Deck tab: m picks the selected key up, the arrows and [ ] choose where it lands, including on another page, then Enter drops it and Esc puts it back.

Following the Omarchy theme

The panel already retints itself when the theme changes. The keys can too:

streamdeck-ctl deck follow-theme true    # opt in; off by default
streamdeck-ctl deck theme                # apply now
streamdeck-ctl deck theme --dry-run      # print the colours, change nothing
streamdeck-ctl deck follow-theme false   # opt out and remove the hook

Opting in installs a theme-set hook through omarchy-hook-install, so from then on the deck recolours itself whenever you switch theme. Opting out removes it again.

Colours come from omarchy-theme-color, so they resolve exactly as every other Omarchy consumer sees them. The deck background and text follow the theme's background and foreground; each page takes a colour of its own.

Choosing those page colours is the fiddly part. Reading four fixed palette keys falls apart on a theme whose palette is warm or monochrome throughout, where they all land on the same colour. So the whole palette is considered and the entries furthest apart perceptually are chosen. Where a palette genuinely holds nothing distinct, Vantablack say, the pages are separated by weight instead, one accent laid on progressively thicker. Either way each colour is eased back toward the background if it would not leave the label readable.

theme_strength in the config, or --strength, sets how strongly a colour tints its page. It defaults to 0.38; lower is subtler.

streamdeck-ctl deck theme --dry-run --strength 0.5
streamdeck-ctl deck theme --dry-run --colors ~/.config/omarchy/themes/nord/colors.toml

The camera overlay and the panel

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.

Settings

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
showPrompter true Prompter scripts, scrolling, mirroring and monitor mode

Layout

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/prompter-ctl/      Prompter daemon: scripts, scrolling, monitor mode, udev rule
src/omgato-panel/      status aggregation, names, shortcuts, undo/redo history
src/skill/             the agent skill describing the tools
scripts/install        build, link, install units; the skill with --with-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 four 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.

Shortcuts

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-shortcuts

Only 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.

What it installs, and where

Plugins run unsandboxed with your user permissions, so here is everything this one touches outside its own directory.

~/.local/bin/:            symlinks to the five binaries it builds
~/.config/systemd/user/:  streamdeck-ctl.service and streamdeck-ctl-deck.service,
                          the Stream Deck and Pedal daemons, and prompter-ctl.service,
                          started only by its udev rule
~/.local/share/prompter-ctl/scripts/: your Prompter scripts
~/.local/state/prompter-ctl/settings.json: Prompter mode, script, speed, size, mirror
$XDG_RUNTIME_DIR/prompter-ctl/: the Prompter control socket, mode 0700
~/.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
~/.config/omarchy/hooks/theme-set.d/omgato-deck.hook: recolours the deck on a
                          theme change, only after `deck follow-theme true`;
                          `deck follow-theme false` or scripts/uninstall removes it
$XDG_RUNTIME_DIR/camlink-ctl/: overlay position and pid, cleared on reboot
agent skill directories:  a symlink to src/skill/, only when you pass --with-skill
                          or run scripts/install-skill, and only where the
                          directory already exists

scripts/uninstall reverses all of it. It keeps your device configuration unless you pass --purge.

Privileges

No sudoers rule ships with the plugin, and scripts/install performs no privileged step itself. Three 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
  • installing the Prompter udev rule, only if you have a Prompter. It assigns the DisplayLink card to a seat of its own, hands it to your user and starts prompter-ctl.service. The installer prints it with your user name filled in:

    sed "s/@USER@/$USER/" \
      ~/.config/omarchy/plugins/io.github.data-goblin.omgato/src/prompter-ctl/udev/72-prompter-ctl.rules \
      | sudo tee /etc/udev/rules.d/72-prompter-ctl.rules >/dev/null
    sudo udevadm control --reload && sudo systemctl restart displaylink
  • 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 sudo for you: camlink-ctl runs sudo -n tee /sys/bus/usb/devices/<dev>/authorized, writing 0 and then 1. It fires on camlink-ctl reset, and again inside camlink-ctl show when the previous session left the device wedged. The -n means 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.

Network and processes

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.

Requirements

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, Pedal and Prompter daemons
evdi-dkms, displaylink: the Prompter only (AUR); displaylink is closed source
fc-match:             the Prompter script font (fontconfig)

Package names in brackets are the Arch packages that provide each binary.

Troubleshooting

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 reset

Contributing

Issues 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.

Licence

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.

About

Omarchy Quattro panel for Key Lights, Stream Deck, Stream Deck Pedal and Cam Link 4K, each with a command line tool

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages