QuantStack / QuantStack/jupyter-bluetooth-manager

Connection issue when a user chooses a device that is not appairable anymore

Open
#40 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
0
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Description:
When a user wants to pair a Lego Move Hub, the user experience with the bluetooth-manager is the following one:

https://github.com/user-attachments/assets/8707c9f0-e1d4-42cb-8cad-87b65c6341a5

To pair a Move Hub device, the user has to press its green button. This opens a pairing time window that lasts about 15 s. In the previous screencast, the green button has been pressed once the Chrome chooser pop up is already opened and we can see that the device name appearing once detected. Note that the Move Hub remains in the list even if it is not appairable anymore. : there is no update for this device related to the scanning searching for bluetooth devices. As a consequence, it may happen that the user clicks on the Pair button a bit too late and thus outside of the pairing window. We are here reporting the behavior observed in that precise situation:

The snippet of code related to connection and getting the services available for this type of device is the following one:

async connectAndGetAllServices(): Promise<
      Array<BluetoothRemoteGATTService> | undefined
    > {
      this.native.addEventListener('gattserverdisconnected', event => {
        this.isConnected = false;
        this.disconnected.emit(true);
      });
      const server = this.native.gatt
      if (server) {
        server.connect();
        if (server.connected === true) {
          this.isConnected = true;
          const services = await server.getPrimaryServices();
          if (!services || services.length === 0) {
            throw new Error('Server exists but no service found on the device.');
          } else { return services; }
        }
        else {
          throw new Error('There is no connection to server. No attempt to get a service.')
        }
      }
      else {
        throw new Error('Server is not defined.');
      }
    }

There is first this error message:

BluetoothManager.ts:215 Uncaught (in promise) Error: There is no connection to server. No attempt to get a service.
    at MoveHub.connectAndGetAllServices (

And then after a bit more than 40 s, there is this other error:

Uncaught (in promise) NetworkError: Connection Error: Connection attempt failed.

After this failed tentative of connection, if the user tries to connnect the same device again, goes back on the Chrome pop up and clicks on the Move Hub to try a reconnection, this one gets automatically connected without having to click on Pair. This may not be what the user wants and we would like to "stop" the possibility of "auto-re-connection without explicitly clicking on Pair". Note that nevertheless the connection is not maintained and this error message is sent:

moveHub.ts:116 The connection state is false

This warning message comes from the initDevice method from the MoveHub class, related to the disconnected signal.

this.disconnected.connect(async (sender, disconnected: boolean) => {
      if (disconnected) {
        this.deviceInfo.connected = false;
        this.isConnected = false;
      }
      console.warn(
        'The connection state is',
        this.isConnected
      );
    });

We have tried to prevent the attempt of connection to last 40 s, by using a timeout with 5 s delay and we have also called the gatt.disconnect method to interrupt any pending connection attempts as mentioned in the specification https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect. We are sharing the updated version of the connectAndGetAllServices function:

    async connectAndGetAllServices(): Promise<
      Array<BluetoothRemoteGATTService> | undefined
    > {
      this.native.addEventListener('gattserverdisconnected', event => {
        this.isConnected = false;
        this.disconnected.emit(true);
      });
      const server = this.native.gatt
      if (server) {
        const timeout = 5000;
        const connectWithTimeout = new Promise<void>((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            reject(
              new Error('Connection to GATT server timed out'));
            server.disconnect();
            this.dispose();
          }, timeout);

          server.connect().then(async () => {
            clearTimeout(timeoutId);
            resolve();
            this.isConnected = true;
            this.connected.emit(true);
          })
            .catch((error) => {
              server.disconnect();
              reject(error);
            });
        });
        await connectWithTimeout
        if (server.connected === true) {
          const services = await server.getPrimaryServices();
          if (!services || services.length === 0) {
            throw new Error('Server exists but no service found on the device.');
          } else { return services; }
        }
        else {
          throw new Error('There is no connection to server. No attempt to get a service.')
        }
      }
      else {
        throw new Error('Server is not defined.');
      }
    }

If the user tries to connect too late as explained previously, this error messages after 5 s is now displayed:

Uncaught (in promise) Error: Connection to GATT server timed out

When the user tries to reconnect, the behavior is exactly the same as previously: the device is reconnected without having to click on Pair. The server.disconnect does not seem to stop any pending connection attempts. This may be related to the issue reported here on Chromium: https://issues.chromium.org/issues/40502943.

Note that the connection is lost after few seconds with this error message:

hub.ts:686 Error while writing:  - Error InvalidStateError: Failed to execute 'writeValue' on 'BluetoothRemoteGATTCharacteristic': Characteristic with UUID 00001624-1212-efde-1623-785feabcd123 is no longer valid. Remember to retrieve the characteristic again after reconnecting.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with connectAndGetAllServices in BluetoothManager.ts and reproduce the late-pairing flow described in the issue. Then trace MoveHub's initDevice handling in moveHub.ts and the characteristic write path in hub.ts, including the reported Chromium behavior. Done means a failed late connection does not cause an unwanted later reconnection and the connection state and characteristic writes remain consistent.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
embedded-iot, networking
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.