foxxyz / loupedeck

Node.js API for Loupedeck Controllers
MIT License
87 stars 10 forks source link

WebSerial support #22

Closed Xample closed 1 year ago

Xample commented 1 year ago

Hello, while it might not be as useful as in a node demon, how difficult would you think a port to Web USB would be ?

Julusian commented 1 year ago

Probably makes more sense to look at Web Serial

Xample commented 1 year ago

I did not know about Web Serial, I'm now wondering if Web USB is a subclass of Web Serial of it's own.

Xample commented 1 year ago

https://www.npmjs.com/package//usb#:~:text=Turn%20legacy%20Device%20into%20WebUSB%20compatible%20device

Julusian commented 1 year ago

those docs dont apply as this library isnt using that usb package. It is using https://www.npmjs.com/package/serialport

Xample commented 1 year ago

I just made a POC using WebUSB, while I can request the device using the vendorId, the device exposes 5 interfaces. Only 1 can be claimed, the others raise an exception SecurityError: Failed to execute 'claimInterface' on 'USBDevice': The requested interface implements a protected class. The loupedeck is probably seen as either a video or an HID (or even both but through 2 interfaces).

In short… WebUSB is not the good way to go.

[EDIT] This is not HID as it would otherwise be listed using the WebHID. Also the interface 2 is the flash drive [EDIT 2] I was wrong!, I just used wireshark to see the USB traffic, the device 1 and endpoint 1 are used, and those are the one we can connect to using WebUSB. I need to check why I was not able to read anything.

foxxyz commented 1 year ago

Should be totally possible. I would personally try using WebSerial instead of WebUSB, and add a little webpage/GUI component to it as an example and for testing. Could be fun.

Xample commented 1 year ago

For using WebSerial I'm wondering if I will get the same limitation with Chrome i.e. if you can do it with WebUSB it will then force you to use WebUSB. (The same applies with WebHID where you cannot use WebUSB)

Also, to what I've seen, the ws.js is using a websocket to connect to the discovered serialport (which one is exposed through a host address). I have not tested but Chome might prevent connecting (through a WS) to this local address, if at least I had an address because to what I've seen: https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API directly returns a port where we can pipe data, not a host we can connect to.

For now, using WebUSB, I've been able to send the handshake and received the response (using your WS_UPGRADE_HEADER data) but I don't know how to then read the upcoming incoming data. I can imagine I could in a perfect world have the abstraction WebUSB-> USBInterface -> Websocket -> LoupedeckRPC -> LoupedeckAPI -> Nice website. But the browser's WebSocket cannot connect to the USBInterface so I feel like I will have to fake it.

[UPDATE] It works now… i.e. I can receive data from the loupedeck, I think I had to reset the device.

Julusian commented 1 year ago

Older firmwares created a virtual network interface, and you had to open a websocket connection to them. Newer firmwares appear as a serialport.

I also expect that chrome will block access to the websocket version, so that route is probably a deadend. For the newer serial devices, using WebSerial would make sense. What I expect you have done is find the serialport through WebUSB, and are having to implement a serialport driver.

Xample commented 1 year ago

Little POC working: https://angular-ivy-pcm6gq.stackblitz.io/

But I've to click twice on the start button to make it work properly (I need to check why, I probably need to reset the usb interface or something like that), the data is sent to the console (when we move the knobs of course).

[EDIT] no need to send the handshake, this probably drops the support for older firmware, but conversely it's working properly now. Thank you @Julusian

Xample commented 1 year ago

Hi, here is a new working and more stable version using WebUSB https://github-e23slq.stackblitz.io/

Requirements:

That's it for now

Also: The trouble I had yesterday is that the device could stay in a dangling state if you handshake in the middle of a session (in which case the response will timeout and I call a reset on the usb) or if I'm reading using the transferIn but the promise is stuck because the device did not answer to it (no outcomming event to fire). In that case, either the device is stuck in a lock or chrome kept the unresolved promise after a refresh.

Julusian commented 1 year ago

Feel feel to delete this comment, as it is promoting an alternate loupedeck library.

I had a play around with this yesterday in my own library, and got something working. Its using WebSerial and is not very refined yet (has quirks like re-pairing the same device fails because its already open, can probably get stuck easily. code definitely needs some tidying).

The main code of interest is https://github.com/Julusian/node-loupedeck/blob/main/packages/web/src/serial.ts, which you are welcome to use as a reference for an implementation here. It will need some work, but will serve as a good reference to do a browser based implementation of https://github.com/foxxyz/loupedeck/blob/master/connections/serial.js
With a crude demo at https://julusian.github.io/node-loupedeck/

Xample commented 1 year ago

Nice! however, my Loupedeck CT is not found, is this because you do only expose the LoupedeckLive and LoupedeckLiveS ?

image

(Chrome MacOS)

Julusian commented 1 year ago

Yeah, it only allows known models, and the CT is not one of those unfortunately

foxxyz commented 1 year ago

I've published support for WebSerial in the 4.1.0-beta.0 release awaiting merge in #23, including a new example that can be used to test.

Please test it and let me know if you have success using it (or not!).

foxxyz commented 1 year ago

@Xample any chance you have an extra CT that you be willing to lend to me for a week so I could add support for it? I can pay for shipping!

Xample commented 1 year ago

@foxxyz I'm using it every day, but it would be helpful for everyone. The differences : There are 2 displays on the CT, one on the knob and the second one on the buttons. Also, the knob in the middle is a touchpad and will emit the related events. All the events coming from the CT are easy to record, the only hard part (that I did not yet) is to detect how the buffer is sent to the round touch button. Are you in LA ?

foxxyz commented 1 year ago

@Xample Yes I am. And you in Switzerland, correct? I tried to find a way to privately contact you, but wasn't able to.

My email is in every commit on this repository if you want to shoot me an email so we can work out the details.

Thanks and let me know!