Add support for Ulanzi Stream Controller D200 - #14
Open
bksenhuck wants to merge 1 commit into
Open
Conversation
Adds a UlanziD200 Device class (USB VID:PID 2207:0019) alongside the existing Elgato and Mirabox support, following the same pattern as the Mirabox293S integration. Protocol notes (see docstring in UlanziD200.py for details): - 14 physical keys in a 5x3 grid; the 15th grid slot has no LCD (it's the small stats/clock window), modeled as an inert phantom key so the library's KEY_ROWS x KEY_COLS grid math stays rectangular. - The device only accepts a button image upload for a short window after opening; a periodic "priming" upload keeps that window open until the caller's first real upload arrives. - Button image uploads for multiple keys are combined into a single multi-packet upload, matching how other D200 clients (e.g. homedeck) operate; the device merges an upload into what's currently displayed rather than replacing it wholesale. - open() is made idempotent per connection, since callers such as StreamController call it more than once during their own startup with no is_open() guard, and doing that repeatedly within milliseconds left the device unable to render images for the rest of the session. Also exposes hidapi's interface_number on LibUSBHIDAPI enumeration results (previously unused) and lets DeviceManager._default_factory() filter by it, since the D200 exposes a second, unrelated keyboard-emulation HID interface under the same VID:PID that must not be wrapped as a StreamDeck. Protocol reverse-engineered by redphx: https://github.com/redphx/strmdck https://github.com/redphx/homedeck
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds native support for the Ulanzi Stream Controller D200 (USB VID:PID
2207:0019), following the same integration pattern already used for the Mirabox Stream Dock 293S (Mirabox293S.py).The device's protocol isn't documented anywhere by the vendor. It was reverse-engineered by redphx in strmdck and homedeck (both MIT-licensed); this PR adapts that protocol knowledge into a
StreamDecksubclass for this library, plus a few hardware-specific workarounds discovered while integrating it with StreamController (full details in theUlanziD200class docstring).What changed
Devices/UlanziD200.py(new): the device implementation. Handles the D200's 14-key grid, button-press reporting, brightness, and multi-key image uploads. Documents (in the class docstring) several non-obvious hardware quirks this works around:open()is made idempotent, since StreamController (the primary consumer of this library) calls it multiple times during its own startup with no guard of its own.DeviceManager.py: registers the D200 in_default_factory(), alongside Elgato and Mirabox. Also extends theproductslist format to optionally filter by USB interface number, since the D200 exposes two HID interfaces under the same VID/PID (a keyboard-emulation interface, unrelated to the deck protocol) and only one may be wrapped as aStreamDeck.ProductIDs.py: addsUSB_VID_ULANZI/USB_PID_ULANZI_D200.Transport/LibUSBHIDAPI.py: exposes hidapi'sinterface_numberfield (previously read but discarded), which the interface filtering above depends on.Compatibility
LibUSBHIDAPI(thelibusbbackend)DeviceManager._default_factory) preserves prior behavior for entries without an interface-number filterTested against real hardware with StreamController, on Linux.