Driver and Niri integration for the Ulanzi Stream Controller D200X
Project description
ulanzi-studio-niri
A Linux daemon that drives the Ulanzi Stream Controller D200X and integrates it with the Niri Wayland compositor.
Buttons can:
- Trigger Niri actions (
niri msg action ...) - Launch arbitrary commands
- Control media (
playerctl/wpctl) - Take screenshots (
grim/slurp) - Send keystrokes (
wtype/ydotool) - Switch between configured pages
- Adjust deck brightness
The wide bottom-right LCD displays a clock, system stats, or live encoder information.
Hardware
- 13 LCD buttons at 196×196
- 1 wide LCD button at 458×196 (bottom-right; driven by the small-window subsystem)
- 2 plain physical buttons
- 3 rotary encoders (each with click)
Installation
This project uses uv and runs against the system Python interpreter.
# Development install
uv venv --python /usr/bin/python3 .venv
uv sync
# End-user install (creates ~/.local/bin/ulanzi-niri)
uv tool install .
udev rule (required)
Out of the box the deck's hidraw nodes are owned by root. Install the udev
rule so the daemon can talk to it as your user:
ulanzi-niri install-udev
# follow the printed `sudo` commands; replug the deck afterwards
Run as a service
mkdir -p ~/.config/systemd/user
cp packaging/ulanzi-niri.service ~/.config/systemd/user/
systemctl --user enable --now ulanzi-niri
Configuration
Configuration lives at ~/.config/ulanzi-niri/config.toml. See
examples/config.toml.
Icons
Icon names in [[page.button]] are resolved in this order, first match
wins:
~/.config/ulanzi-niri/icons/<name>— your own overrides<install>/assets/icons/<name>— bundled icons (if any)~/.local/share/icons/,/usr/share/icons/,/usr/share/pixmaps/— freedesktop icon directories, searched recursively
A name with an extension (firefox.png) matches that filename anywhere
under the search roots. A bare name (firefox) matches firefox.png or
firefox.xpm, preferring the largest available pixel size (parsed from
NxN directory components). SVG icons are not currently supported — drop
a PNG into ~/.config/ulanzi-niri/icons/ for SVG-only themes.
Development
uv run ulanzi-niri doctor # diagnose environment
uv run ulanzi-niri push # one-shot push of current config
uv run ulanzi-niri sniff # observe HID traffic
uv run pytest # tests
uv run ruff check . # lint
Status
All physical inputs are wired: the 13 LCD buttons, the wide tile (pos 13),
the two plain hardware buttons (pos 14, 15), and all three rotary encoders
(press + rotate). Streaming for the 4th-row buttons and encoders requires a
one-time ENABLE_INPUT_STREAMING (cmd 0x0002) packet on connect, which the
daemon sends automatically; without it the firmware silently consumes
encoder rotates (routing them to its built-in brightness handler). Opcode
discovered empirically by sweep.
Credits
Protocol details adapted from redphx/strmdck.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ulanzi_studio_niri-1.0.0.tar.gz.
File metadata
- Download URL: ulanzi_studio_niri-1.0.0.tar.gz
- Upload date:
- Size: 84.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3df8143b0a451ef8abdfb7813e6270f8c03e38ade1f8534c7fb22d98b0f7e4ce
|
|
| MD5 |
363b3bb3123932b511d2e4eb697addec
|
|
| BLAKE2b-256 |
4691fda7a3e2dfab68de6c0efda3dd312ba22ec81d39a4d6e7e7276ea52944fa
|
Provenance
The following attestation bundles were made for ulanzi_studio_niri-1.0.0.tar.gz:
Publisher:
publish.yml on doitian/ulanzi-studio-niri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ulanzi_studio_niri-1.0.0.tar.gz -
Subject digest:
3df8143b0a451ef8abdfb7813e6270f8c03e38ade1f8534c7fb22d98b0f7e4ce - Sigstore transparency entry: 1443584914
- Sigstore integration time:
-
Permalink:
doitian/ulanzi-studio-niri@3fcce8307743fdee19c66459348e2f0652ce83d4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/doitian
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3fcce8307743fdee19c66459348e2f0652ce83d4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ulanzi_studio_niri-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ulanzi_studio_niri-1.0.0-py3-none-any.whl
- Upload date:
- Size: 34.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
598819ff7c893964f0f873adedb5186072348a4fdd044e298d7d9202d2addfdf
|
|
| MD5 |
c0c5d42d80d19f43882f59c1862130cb
|
|
| BLAKE2b-256 |
b5e7644b1bfd4db94157a5359766d586acac37b0496dc7b274359a79f0561a8e
|
Provenance
The following attestation bundles were made for ulanzi_studio_niri-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on doitian/ulanzi-studio-niri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ulanzi_studio_niri-1.0.0-py3-none-any.whl -
Subject digest:
598819ff7c893964f0f873adedb5186072348a4fdd044e298d7d9202d2addfdf - Sigstore transparency entry: 1443584989
- Sigstore integration time:
-
Permalink:
doitian/ulanzi-studio-niri@3fcce8307743fdee19c66459348e2f0652ce83d4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/doitian
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3fcce8307743fdee19c66459348e2f0652ce83d4 -
Trigger Event:
push
-
Statement type: