I'm hoping to migrate from using WebUSB to SerialAPI (which is explained nicely here).

Current Code:

try {
    let device = await navigator.usb.requestDevice({
    filters: [{
            usbVendorId: RECEIVER_VENDOR_ID
        }]
    })
    this.connect(device)
} catch (error) {
    console.log(DEVICE_NAME + ': Permission Denied')
}

New Code:

try {
    let device = await navigator.serial.requestPort({
    filters: [{
            usbVendorId: RECEIVER_VENDOR_ID
        }]
    })
    this.connect(device)
} catch (error) {
    console.log(DEVICE_NAME + ': Permission Denied')
}

The new code appears to work, but I think it's because the browser has already requested the device via the old code.

I've tried restarting Chrome as well as clearing all of the browsing history. Even closed the USB-claiming page and claimed the device with another app (during which it returns the DOMException: Unable to claim interface error), but Chrome doesn't seem to want to ask again. It just happily streams the data with the previous connection.

My hope was that using SerialAPI would be a way to avoid fighting over the USB with other processes, or at least losing to them.

Update

I had forgotten about:

Failed to execute 'requestPort' on 'Serial': "Must be handling a user gesture to show a permission request"

Does this mean that the user will need to use a button to connect to the device via SerialUSB? I think with WebUSB I was able to make the connect window automatically pop up.

3

There are 3 answers

0
Reilly Grant On BEST ANSWER

For both APIs, as is noted in the update, a user gesture is required in order to call the requestDevice() or requestPort() method. It is not possible to automatically pop up this prompt. (If there is that's a bug so please let the Chrome team know so we can fix it.)

Permissions granted to a site through the WebUSB API and Web Serial API are currently tracked separately so permission to access a device through one will not automatically translate into the other.

There is not currently a way to programatically forget a device permission. That would require the navigator.permissions.revoke() method which has been abandoned. You can however manually revoke permission to access the device by clicking on the "lock" icon in the address bar while visiting the site or going to chrome://settings/content/usbDevices (for USB devices) and chrome://settings/content/serialPorts (for serial ports).

0
MikeiLL On

The new code was NOT working. It just appeared to be because Chrome was already paired with the port via the old code. There is no way the "new code" could have worked because, as noted in Kalido's comment, the SerialAPI (due to its power) requires a user gesture to connect.

The code I'm using to actually connect and get data is basically built up from a few pieces from the above links in the OP:

navigator.serial.addEventListener('connect', e => {
  // Add |e.target| to the UI or automatically connect.
  console.log("connected");
});

navigator.serial.addEventListener('disconnect', e => {
  // Remove |e.target| from the UI. If the device was open the disconnection can
  // also be observed as a stream error.
  console.log("disconnected");
});

console.log(navigator.serial);

document.addEventListener('DOMContentLoaded', async () => {

    const connectButton = document.querySelector('#connect') as HTMLInputElement;

    if (connectButton) {
        connectButton.addEventListener('click', async () => {
            try {
        
                // Request Keiser Receiver from the user.
                const port = await navigator.serial.requestPort({
                    filters: [{ usbVendorId: 0x2341, usbProductId: not_required }]
                });
            
                try {
                    // Open and begin reading.
                    await port.open({ baudRate: 115200 });
                
                } catch (e) {
                    console.log(e);
                }
                while (port.readable) {
                  const reader = port.readable.getReader();

                  try {
                    while (true) {
                      const { value, done } = await reader.read();
                      if (done) {
                        // Allow the serial port to be closed later.
                        reader.releaseLock();
                        break;
                      }
                      if (value) {
                        console.log(value);
                      }
                    }
                  } catch (error) {
                    // TODO: Handle non-fatal read error.
                    console.log(error);
                  }
                }
            } catch (e) {
              console.log("Permission to access a device was denied implicitly or explicitly by the user.");
              console.log(e);
              console.log(port);
            }
        }
}

The device-specific Vendor and Product IDs would obviously change from device to device. In the above example I have inserted an Arduino vendor ID.

It doesn't answer the question of how to get Chrome to "forget", but I'm not sure if that's relevant when using SerialAPI because of the required gesture.

Hopefully someone with more experience will be able to post a more informative answer.

0
regmagik On

To get Chrome to "forget" the WebUSB device previously paired via navigator.usb.requestDevice API:

  1. Open the page paired to the device you want to forget
  2. Click on the icon in the address bar
  3. Click x next to device. If nothing is listed, then there are no paired devices for this web page. enter image description here