An OpenDeck plugin that lets the official Stream Deck Mobile Android app (com.corsair.android.streamdeck, formerly Elgato) act as a virtual button panel for OpenDeck on Linux.
The plugin speaks the app's VSD2 protocol, so OpenDeck shows up to the phone like a Stream Deck desktop host: the phone finds it on the local network (or by QR code), you approve the pairing on the desktop, and a virtual device appears in OpenDeck. Icons are pushed to the phone, button presses come back as normal OpenDeck key events, and the phone can resize the key grid (up to 8x8) on the fly.
Disclaimer. This is an unofficial, independent project. It is not affiliated with, endorsed by, or supported by Elgato, Corsair, or the OpenDeck authors. "Stream Deck" is a trademark of its respective owner. The protocol implementation was written by observing the app's behaviour; it may break whenever the app is updated.
Working and tested with the Corsair-branded Stream Deck Mobile app on Android, against OpenDeck on Linux (CachyOS, KDE Plasma / Wayland):
- Pairing by QR code and by network discovery (mDNS), including reconnecting to a known computer
- Pairing approval dialog on the desktop
- Virtual device creation, rename, and removal from either side
- Live key grid resize from the phone
- Icon rendering and key presses in both directions
- Management window (device list, rename, remove, add device with QR) themed from OpenDeck's own colours
Linux only (x86_64 bundle). It relies on Unix sockets and, for network discovery, on Avahi.
- OpenDeck
- Rust toolchain (stable, edition 2024 — Rust 1.85 or newer) and
binutils(strings) to build from source avahi-daemonandavahi-utils(avahi-publish-service) for network discovery. Without them the plugin falls back to its own multicast announcer; QR pairing never needs mDNS- Phone and PC on the same network, with TCP ports 28197–28199 reachable on the PC and multicast UDP (mDNS, port 5353) allowed
Releases ship the plugin as a single .odPlugin file — no Rust toolchain needed.
- Download
com.toshtaru.streamdeck-mobile-v<version>.odPluginfrom the Releases page. Optionally check it against the.sha256file next to it:sha256sum -c <file>.sha256. - In OpenDeck, open Plugins and click Install from file, then select the downloaded
.odPluginfile. - Wait for the "Successfully installed" message. If the plugin does not show up or start, fully quit and restart OpenDeck.
To update, install the newer .odPlugin the same way; it replaces the old version. Your pairings and saved devices live outside the plugin folder (see Files and ports) and are kept.
An .odPlugin is an ordinary zip archive whose only top-level entry is the com.toshtaru.streamdeck-mobile.sdPlugin folder. If you prefer to install by hand, unzip it into ~/.config/opendeck/plugins/ and restart OpenDeck.
git clone <this repository>
cd streamdeck-mobile.odPlugin
./install-plugin.shThe script does a clean release build, copies the plugin to ~/.config/opendeck/plugins/com.toshtaru.streamdeck-mobile.sdPlugin, and checks that the manifest, Cargo.toml, VERSION, and the compiled binary all report the same version. Then fully quit and restart OpenDeck — it only loads native plugins at startup.
Options: --reset-pairing (forget all saved devices and trust), --verify (check the install and running process).
Set OPENDECK_CONFIG_DIR if your OpenDeck config is not in ~/.config/opendeck.
./build-plugin.shThis builds the release binary and writes dist/com.toshtaru.streamdeck-mobile-v<version>.odPlugin plus a .sha256 file. It is the same script the GitHub workflows run. Set SKIP_BUILD=1 to package an existing target/release binary without rebuilding.
- In OpenDeck, open the Stream Deck Mobile plugin settings. A management window opens.
- Click Add device and scan the QR code with the Stream Deck Mobile app — or just open the app and pick this computer from its network search.
- Compare the verification code on the pairing dialog with the one on your phone and click Approve.
- Create (or open) a virtual device in the app. It appears in OpenDeck as a new device; drag actions onto it as usual.
Removing the virtual device — from the phone or from the management window — does not un-pair the phone. A trusted phone can recreate its device at any time without pairing again. To make a phone untrusted, clear the app's data on the phone, or run ./install-plugin.sh --reset-pairing on the desktop.
| Path / port | Purpose |
|---|---|
~/.config/opendeck/streamdeck-mobile.json |
Saved virtual devices and trusted phone keys |
~/.config/opendeck/streamdeck-mobile-workstation-id |
Stable host ID presented to phones. Do not change or delete — see below |
~/.config/opendeck/streamdeck-mobile-theme.json |
Colours copied from OpenDeck for the plugin's windows |
~/.local/state/opendeck-streamdeck-mobile/plugin.log |
Plugin log |
| TCP 28197 / 28198 / 28199 | Legacy, VSD2 (QR pairing), and VSD2 for network-discovery connections |
The Stream Deck Mobile app remembers a computer's ID the first time it sees it and rejects any later connection that presents a different one. The plugin therefore generates its ID once and keeps it forever. If you delete the file, phones that already know this computer will refuse to reconnect until you forget the computer in the app (or re-pair by QR).
- Plugin not listed in OpenDeck — restart OpenDeck completely, and check
~/.local/share/opendeck/logs/opendeck.logfor a manifest error. - Phone does not find the computer — check that the phone is on the same subnet, that
avahi-daemonis running, and that a firewall is not blocking UDP 5353 or TCP 28197–28199. QR pairing works without discovery. - Phone connects but immediately drops — check
~/.local/state/opendeck-streamdeck-mobile/plugin.log. For phone-side details,adb logcat(tagsStreamDeckConnection,DiscoveryService) shows what the app is actually trying to do. - Debug capture — set
OPENDECK_MOBILE_RAW=1in the environment OpenDeck runs in to log every protocol frame to~/.config/opendeck/streamdeck-mobile-wire.log. This includes decrypted traffic; do not share the file publicly.
- Pairing always requires explicit approval on the desktop. The verification code in the dialog is the phone's key fingerprint; confirm it matches the phone before approving.
- The plugin listens on all interfaces on TCP 28197–28199 and announces itself over mDNS. Use it on networks you trust.
- The host's encryption key, workstation ID, and announced pairing token are derived deterministically from the hostname and
/etc/machine-id, so no secret file has to be stored. That also means they are only as private as those two values.
./uninstall-plugin.shThis removes the plugin bundle (you can also remove it from OpenDeck's Plugins dialog). Saved state in ~/.config/opendeck/streamdeck-mobile* and the log directory are left in place; delete them by hand for a clean slate.
CLAUDE.md contains detailed technical notes: protocol structure, the three-port layout, mDNS design, the trust-versus-virtual-device model, how OpenDeck handles grid resizing, and why the windows run as separate processes.
Source layout: src/mobile.rs (VSD2 protocol, pairing, mDNS), src/openaction.rs (OpenDeck WebSocket client), src/ui.rs (egui windows), src/ipc.rs (plugin ↔ window commands), src/main.rs (entry point).
When you change anything, keep VERSION, Cargo.toml, and com.toshtaru.streamdeck-mobile.sdPlugin/manifest.json in sync — the installer and the build script refuse to continue if they differ.
- CI (
.github/workflows/ci.yml) builds the plugin on every push tomainand on every pull request, and attaches the resulting.odPluginto the run as an artifact. - Release (
.github/workflows/release.yml) runs when av*tag is pushed. It builds the plugin, packages the.odPluginwith a SHA-256 checksum, and publishes both as a GitHub Release with generated notes. The tag must equalVERSION(for exampleVERSION0.8.9→ tagv0.8.9).
To cut a release: bump the version in the three files, commit, then
git tag v<version>
git push origin v<version>