Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Mirabox Stream Dock 293S #148

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

rescbr
Copy link

@rescbr rescbr commented Aug 8, 2024

This commit adds unofficial basic support for the Mirabox Stream Dock 293S by reverse engineering the USB traffic of their Windows controller app.

This code could potentially support other Mirabox products and other brands that have Mirabox as the OEM, such as AJAZZ. I only tested with my generic unbranded deck bought on Aliexpress.

Features that are implemented:

  • 15 main keys with 85x85 resolution
  • Emulation of key down/up events, since the current firmware only fires events on key release
  • Imported new methods from HIDAPI

Missing support:

  • Non-touch side display - the device emulates three 80x80 keys on the side display. Currently this library (and as I understand, the apps that use this library) doesn't support multiple key resolutions, so it's not a simple matter of adding an additional column.

@Core447
Copy link
Contributor

Core447 commented Aug 23, 2024

Maybe we can add a get_key_resolution method that returns the resolution for a key based on it's index (or coords)?

@rescbr
Copy link
Author

rescbr commented Aug 28, 2024

Maybe we can add a get_key_resolution method that returns the resolution for a key based on it's index (or coords)?

At least for this specific device, returning the resolution by key index would be easier.

I'm not sure how to expose this new method to be used by downstream projects, or if it makes sense to do so, given that (as I currently understand) the Elgato devices don't have keys with distinct resolution.

I don't like the idea of simply rescaling the images to be displayed though.

@Julusian
Copy link

Julusian commented Aug 30, 2024

This is probably a bigger task than is wanted for this pr, but it could be useful for inspiration.

Something I recently did in the javascript streamdeck library was to remove all the loose properties describing functionality, and replace it with an array of controls on the class. (the typescript definitions of these are here)

This array contains entries such as:

{
  type: 'button',
  index: 3,
  hidIndex: 4,
  row: 0,
  column: 3,
  feedbackType: 'lcd',
  pixelSize: {
    width: 72,
    height: 72
  },
}

or

{
  type: 'encoder',
  index: 2,
  hidIndex: 2,
  row: 3,
  column: 2,
}

These objects also get passed to the keypress and other event handlers instead of the index that used to be provided.

This gives me the scope to handle things like varying resolution buttons in the future if needed. As well as adding new types or variations of existing controls, with minimal effort needed for consumers of the library.

By doing this, I managed to remove all the model specific handling in a consumer of the library, it is now able to rely solely on these control descriptions to translate to its internal grid system. Previously it had to know how the plus and neo were physically laid out, and handle the rgb only buttons of the neo differently.

@danielk117
Copy link

danielk117 commented Nov 18, 2024

There is an official Python-SDK (see https://github.com/MiraboxSpace/Linux-StreamDock-PythonSDK) for ther mirabox devices. Maybe helpful for this?

@rescbr
Copy link
Author

rescbr commented Nov 18, 2024

There is an official Python-SDK (see https://github.com/MiraboxSpace/Linux-StreamDock-PythonSDK) for ther mirabox devices. Maybe helpful for this?

I just took a look into that code. It's nice to see I wasn't that far off with my reverse engineering, and it's also good have some comments on the key resolution and the specific implementation details for the 293 model. It seems the official SDK only supports up to 80x80, while the displays themselves can go up to 85x85 without cropping (measured by eye 😄), so it takes into account the three not-button displays.

I don't like what they did to hide the USB commands into a binary .so, the commands are so simple that there is no need, but I understand the manufacturer wanting to protect their IP.

When I have some spare time I can work on this PR.

@danielk117
Copy link

It seems the official SDK only supports up to 80x80, while the displays themselves can go up to 85x85 without cropping

I think you're right. Configuring a button with the original software, uses a smaller area, compared to the "boot logo", which uses the whole area of a key.

When I have some spare time I can work on this PR.

That would be great 👍

@Gotolei
Copy link

Gotolei commented Dec 22, 2024

Heyo, this might be pushing the scope a bit but what would be needed for the MBox N3 to be able to be added as well? Instead of a 5x3 array, It has 6 of the same lcd buttons, 3 opaque buttons, and 3 encoders each of which also have a click-in. If they put it together the way I imagine they probably did, that comes out to being three extra buttons.

Edit: keymapping, and the images are 64x: MiraboxSpace/Linux-StreamDock-PythonSDK#15 (comment)

@fightforlife
Copy link

I also have a Mirabox 293S available. Is there something specific I can help with to get this CR going?
I am not much of a dev, but can read and write python with some help of google.
Is the PR in a state where it can be tested?

@rescbr
Copy link
Author

rescbr commented Jan 10, 2025

Heyo, this might be pushing the scope a bit but what would be needed for the MBox N3 to be able to be added as well? Instead of a 5x3 array, It has 6 of the same lcd buttons, 3 opaque buttons, and 3 encoders each of which also have a click-in. If they put it together the way I imagine they probably did, that comes out to being three extra buttons.

Edit: keymapping, and the images are 64x: MiraboxSpace/Linux-StreamDock-PythonSDK#15 (comment)

@Gotolei - at the protocol level I tend to agree with you, it shouldn't deviate too much from what we already have. From the keymap you linked, it looks like as if the encoders expose their status as two different key presses. Also notice the comment from @Heart-State that states the set button image command for N3 is a bit different from the 293. I'd take a look at the Stream Deck Plus implementation of the dials as a base to implement them for the N3.

I also have a Mirabox 293S available. Is there something specific I can help with to get this CR going? I am not much of a dev, but can read and write python with some help of google. Is the PR in a state where it can be tested?

@fightforlife - Yeah, the PR has working code for the main 15 buttons with 85x85 resolution (and I'm using it daily). I haven't added support for the side screen that are driven as if they were three 80x80 screens. They would be black in this case.

To support the side screen with my code, the easiest way would be to decrease the overall supported resolution down to 80x80 and update the key mapping:

KEY_COUNT = 18
KEY_COLS = 6
KEY_ROWS = 3

KEY_PIXEL_WIDTH = 80
KEY_PIXEL_HEIGHT = 80

KEY_NUM_TO_DEVICE_KEY_ID = [0x0d, 0x0a, 0x07, 0x04, 0x01, 0xe, 0xb, 0x08, 0x05, 0x02, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x10, 0x11, 0x12]

The not so easy way is to add support for multiple resolution buttons in this library and also on downstream applications that happen to use this library like @Julusian mentioned on their comment above.

@fightforlife
Copy link

Could we just resize the received image to 80x80 when the keyID belongs to one of the 3 virtual display keys? (somewhere in def set_key_image)
Then the upstream apps, would not see a difference and alls keys would work.

@rescbr
Copy link
Author

rescbr commented Jan 11, 2025

Sure, that would work. My problem with this approach - and why I didn't implement this before - is that the downstream app could be making the image on the fly considering the button's resolution. This is more concerning when text is overlaid to an icon or if the icon has pixel art style, as it would lose details.

@danielk117
Copy link

To support the side screen with my code, the easiest way would be to decrease the overall supported resolution down to 80x80 and update the key mapping

I stayed at 85x85 and it works fine. The keys on the side just gets a little bit cutted off. If you don't know it, you won't notice.

BTW, the correct order for the keys is:

KEY_NUM_TO_DEVICE_KEY_ID = [0x0d, 0x0a, 0x07, 0x04, 0x01, 0x10, 0xe, 0xb, 0x08, 0x05, 0x02, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x12]

@fightforlife
Copy link

Added this into my PR to this PR: rescbr#1
I am using this together with: StreamController/StreamController#318

Add dummy functions for compatibility with StreamController + upstream changes merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants