From b4d77313f12f6d2af41d8ca97505df757e997f1a Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Fri, 17 Apr 2026 18:45:36 +0200 Subject: [PATCH 01/10] Improve BluetoothManager class with copilot comments. --- src/bluetooth/BluetoothManager.ts | 50 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 5a84fb9..a305da2 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -29,16 +29,21 @@ export class BluetoothManager implements IBluetoothManager { async connect( registryItem: IDeviceTypeRegistryItem ): Promise { - const native = await this.requestDevice(registryItem); - if (native) { - const device = await registryItem.factory(native); - if (device && device.isConnected) { - this._addDeviceToList(device); - device.disconnected.connect(async () => { - this._removeDeviceFromList(device); - }); - return device; + try { + const native = await this.requestDevice(registryItem); + if (native) { + const device = await registryItem.factory(native); + if (device && device.isConnected) { + this._addDeviceToList(device); + device.disconnected.connect(async () => { + this._removeDeviceFromList(device); + }); + return device; + } } + } catch (error) { + console.error('Connection failed:', error); + throw error; } } @@ -54,7 +59,7 @@ export class BluetoothManager implements IBluetoothManager { this._deviceList.push(device); this._identifierRegistry.push(identifier); } else { - console.warn('The device is already in the registry of identifiers'); + console.log('The device is already in the registry of identifiers'); } // Emit the signal when the list changes this.deviceListChanged.emit(this._deviceList); @@ -72,23 +77,24 @@ export class BluetoothManager implements IBluetoothManager { device.dispose(); } - removeAllDevices() { - this._deviceList.forEach((device, index) => { + removeAllDevices(deviceList: Array) { + const devicesToRemove = [...deviceList]; + for (const device of devicesToRemove) { this._removeDeviceFromList(device); - this.deviceListChanged.emit(this._deviceList); - }); + } + this.deviceListChanged.emit(deviceList); } async checkWebBluetoothSupport(): Promise { - const isWebBluetoothSupported: boolean = navigator.bluetooth ? true : false; - if (isWebBluetoothSupported === false) { + if (!('bluetooth' in navigator)) { showDialog({ title: 'Error', - body: 'Web Bluetooth is not supported on your browser. It works on Chrome and Edge (Firefox and Explorer are not supported). \n Please also check that the Web Bluetooth flag is properly set to enabled in the Chrome flags (chrome://flags/).', + body: 'Web Bluetooth is not supported in your browser. It works on Chrome and Edge. Make sure the Web Bluetooth flag is enabled in chrome://flags/.', buttons: [Dialog.okButton({ label: 'Close' })] }); + return false; } - return isWebBluetoothSupported; + return true; } async requestDevice( @@ -185,7 +191,11 @@ export namespace BluetoothManager { async disconnect(): Promise { if (this.native) { - this.native.gatt?.disconnect(); + try { + this.native.gatt?.disconnect(); + } catch (error) { + console.error('Failed to disconnect:', error); + } this.isConnected = false; } } @@ -264,7 +274,7 @@ export namespace BluetoothManager { * Interface for the bluetooth manager. */ export interface IBluetoothManager { - removeAllDevices(Devices: Array): void; + removeAllDevices(deviceList: Array): void; connect(registryItem: IDeviceTypeRegistryItem): any; disconnect(device: BluetoothManager.Device): void; deviceListChanged: Signal>; From b4ef59ef53dcc8074e74fa81cea1a1fde6931802 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Tue, 21 Apr 2026 18:30:46 +0200 Subject: [PATCH 02/10] Remove argument in removeAllDevices. --- src/bluetooth/BluetoothManager.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index a305da2..bd20a64 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -41,7 +41,8 @@ export class BluetoothManager implements IBluetoothManager { return device; } } - } catch (error) { + } + catch (error) { console.error('Connection failed:', error); throw error; } @@ -77,19 +78,19 @@ export class BluetoothManager implements IBluetoothManager { device.dispose(); } - removeAllDevices(deviceList: Array) { - const devicesToRemove = [...deviceList]; + removeAllDevices() { + const devicesToRemove = [...this._deviceList]; for (const device of devicesToRemove) { this._removeDeviceFromList(device); } - this.deviceListChanged.emit(deviceList); + this.deviceListChanged.emit(this._deviceList); } async checkWebBluetoothSupport(): Promise { if (!('bluetooth' in navigator)) { showDialog({ title: 'Error', - body: 'Web Bluetooth is not supported in your browser. It works on Chrome and Edge. Make sure the Web Bluetooth flag is enabled in chrome://flags/.', + body: `Web Bluetooth is not supported in your browser. It works on Chrome and Edge. Make sure the Web Bluetooth flag is enabled in chrome://flags/.`, buttons: [Dialog.okButton({ label: 'Close' })] }); return false; @@ -193,6 +194,7 @@ export namespace BluetoothManager { if (this.native) { try { this.native.gatt?.disconnect(); + } catch (error) { console.error('Failed to disconnect:', error); } @@ -274,7 +276,7 @@ export namespace BluetoothManager { * Interface for the bluetooth manager. */ export interface IBluetoothManager { - removeAllDevices(deviceList: Array): void; + removeAllDevices(): void; connect(registryItem: IDeviceTypeRegistryItem): any; disconnect(device: BluetoothManager.Device): void; deviceListChanged: Signal>; From 4e5954f98d9de7a43a332c479ca3a6f20a257be0 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 15:31:18 +0200 Subject: [PATCH 03/10] Keep on addressing comments. --- src/bluetooth/BluetoothManager.ts | 43 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index bd20a64..80c3cc2 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -31,18 +31,19 @@ export class BluetoothManager implements IBluetoothManager { ): Promise { try { const native = await this.requestDevice(registryItem); - if (native) { - const device = await registryItem.factory(native); - if (device && device.isConnected) { - this._addDeviceToList(device); - device.disconnected.connect(async () => { - this._removeDeviceFromList(device); - }); - return device; - } + if (!native) { + console.warn('No device selected'); + return undefined; } - } - catch (error) { + const device = await registryItem.factory(native); + if (device && device.isConnected) { + this._addDeviceToList(device); + device.disconnected.connect(() => { + this._removeDeviceFromList(device); + }); + return device; + } + } catch (error) { console.error('Connection failed:', error); throw error; } @@ -78,12 +79,11 @@ export class BluetoothManager implements IBluetoothManager { device.dispose(); } - removeAllDevices() { - const devicesToRemove = [...this._deviceList]; - for (const device of devicesToRemove) { + removeAllDevices(deviceList: Array) { + for (const device of deviceList) { this._removeDeviceFromList(device); } - this.deviceListChanged.emit(this._deviceList); + this.deviceListChanged.emit(deviceList); } async checkWebBluetoothSupport(): Promise { @@ -191,10 +191,11 @@ export namespace BluetoothManager { } async disconnect(): Promise { - if (this.native) { + if (this.native?.gatt) { try { - this.native.gatt?.disconnect(); - + if (this.native.gatt.connected) { + this.native.gatt.disconnect(); + } } catch (error) { console.error('Failed to disconnect:', error); } @@ -276,8 +277,10 @@ export namespace BluetoothManager { * Interface for the bluetooth manager. */ export interface IBluetoothManager { - removeAllDevices(): void; - connect(registryItem: IDeviceTypeRegistryItem): any; + removeAllDevices(deviceList: Array): void; + connect( + registryItem: IDeviceTypeRegistryItem + ): Promise; disconnect(device: BluetoothManager.Device): void; deviceListChanged: Signal>; get deviceList(): Array; From 65cca3c6cf830437d12217d9be4e8fe1866a09bf Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 15:41:04 +0200 Subject: [PATCH 04/10] Fix quote issue. --- src/bluetooth/BluetoothManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 80c3cc2..d734252 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -90,7 +90,7 @@ export class BluetoothManager implements IBluetoothManager { if (!('bluetooth' in navigator)) { showDialog({ title: 'Error', - body: `Web Bluetooth is not supported in your browser. It works on Chrome and Edge. Make sure the Web Bluetooth flag is enabled in chrome://flags/.`, + body: 'Web Bluetooth is not supported in your browser. It works on Chrome and Edge. Make sure the Web Bluetooth flag is enabled in chrome://flags/.', buttons: [Dialog.okButton({ label: 'Close' })] }); return false; From b8c53b3addd09ac78e428067e256ba2536ae81d7 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 15:48:44 +0200 Subject: [PATCH 05/10] Fix removeAllDevices method. --- src/bluetooth/BluetoothManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index d734252..388c31a 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -83,7 +83,7 @@ export class BluetoothManager implements IBluetoothManager { for (const device of deviceList) { this._removeDeviceFromList(device); } - this.deviceListChanged.emit(deviceList); + this.deviceListChanged.emit(this._deviceList); } async checkWebBluetoothSupport(): Promise { From 3f683f6ebc87048c62630732a78a733255a8a7b2 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 16:30:46 +0200 Subject: [PATCH 06/10] Update removeAllDevices. --- src/bluetooth/BluetoothManager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 388c31a..853fe9a 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -81,7 +81,12 @@ export class BluetoothManager implements IBluetoothManager { removeAllDevices(deviceList: Array) { for (const device of deviceList) { - this._removeDeviceFromList(device); + const index = this._deviceList.indexOf(device); + if (index > -1) { + this._deviceList.splice(index, 1); + this._identifierRegistry.splice(index, 1); + device.dispose(); + } } this.deviceListChanged.emit(this._deviceList); } From f32de0222f351eaf88b2976b9bcff48c119c55ed Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 16:56:40 +0200 Subject: [PATCH 07/10] Give the responsability to dispose the device to connectAndGetAllServices method. --- src/bluetooth/BluetoothManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 853fe9a..1759cf5 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -51,7 +51,6 @@ export class BluetoothManager implements IBluetoothManager { async disconnect(device: BluetoothManager.Device) { await device.disconnect(); - device.dispose(); } // Method to add a device to the list @@ -151,6 +150,8 @@ export namespace BluetoothManager { this.native.addEventListener('gattserverdisconnected', event => { this.isConnected = false; this.disconnected.emit(true); + this.dispose(); + this.isDisposed = true; }); const server = this.native.gatt; if (server) { From 08786010e3a0781f5495a674fef3b7d517c81f15 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Wed, 22 Apr 2026 17:21:02 +0200 Subject: [PATCH 08/10] Update connect method of BluetoothManager class with adding a return undefined for cases where the device is device.connected is false. --- src/bluetooth/BluetoothManager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 1759cf5..a96ad3e 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -43,6 +43,7 @@ export class BluetoothManager implements IBluetoothManager { }); return device; } + return undefined; } catch (error) { console.error('Connection failed:', error); throw error; From 8402d852ee168458950fb3c3d320dd010fe41728 Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Thu, 23 Apr 2026 17:01:07 +0200 Subject: [PATCH 09/10] Add more changes. --- src/bluetooth/BluetoothManager.ts | 36 ++++++++++++++++--------------- src/movehub-extension/widget.tsx | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index a96ad3e..24937f0 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -15,7 +15,7 @@ export class BluetoothManager implements IBluetoothManager { ); this._deviceTypeRegistry = new BluetoothManager.DeviceTypeRegistry(); this._deviceList = []; - this._identifierRegistry = []; + this._identifierMap = new Map(); } get deviceList(): Array { @@ -57,14 +57,14 @@ export class BluetoothManager implements IBluetoothManager { // Method to add a device to the list private _addDeviceToList(device: BluetoothManager.Device): void { const identifier = buildCompleteIdentifier(device.native); - if (this._identifierRegistry.includes(identifier) === false) { + if (this.identifierRegistry.includes(identifier) === false) { this._deviceList.push(device); - this._identifierRegistry.push(identifier); + this._identifierMap.set(identifier, device); + // Emit the signal when the list changes + this.deviceListChanged.emit(this._deviceList); } else { console.log('The device is already in the registry of identifiers'); } - // Emit the signal when the list changes - this.deviceListChanged.emit(this._deviceList); } // Method to remove a device from the list @@ -72,11 +72,18 @@ export class BluetoothManager implements IBluetoothManager { const index = this._deviceList.indexOf(device); if (index > -1) { this._deviceList.splice(index, 1); - this._identifierRegistry.splice(index, 1); + const identifier = buildCompleteIdentifier(device.native); + this._identifierMap.delete(identifier); // Emit the signal when the list changes this.deviceListChanged.emit(this._deviceList); } - device.dispose(); + if (!device.isDisposed) { + device.dispose(); + } + } + + get identifierRegistry(): Array { + return Array.from(this._identifierMap.keys()); } removeAllDevices(deviceList: Array) { @@ -84,7 +91,6 @@ export class BluetoothManager implements IBluetoothManager { const index = this._deviceList.indexOf(device); if (index > -1) { this._deviceList.splice(index, 1); - this._identifierRegistry.splice(index, 1); device.dispose(); } } @@ -106,21 +112,17 @@ export class BluetoothManager implements IBluetoothManager { async requestDevice( registryItem: IDeviceTypeRegistryItem ): Promise { - const isWebBluetoothSupported = await this.checkWebBluetoothSupport(); - if (isWebBluetoothSupported) { - const native = await navigator.bluetooth.requestDevice( - registryItem.options - ); - return native; - } else { - return; + const isSupported = await this.checkWebBluetoothSupport(); + if (!isSupported) { + return undefined; } + return await navigator.bluetooth.requestDevice(registryItem.options); } private _deviceList: Array; public deviceListChanged: Signal>; private _deviceTypeRegistry: BluetoothManager.DeviceTypeRegistry; - private _identifierRegistry: Array; + private _identifierMap: Map } export namespace BluetoothManager { diff --git a/src/movehub-extension/widget.tsx b/src/movehub-extension/widget.tsx index 789ca4b..3979dbd 100644 --- a/src/movehub-extension/widget.tsx +++ b/src/movehub-extension/widget.tsx @@ -174,7 +174,7 @@ export class MoveHubModel extends DOMWidgetModel { console.log('not connected yet');*/ if (identifier === '') { this.movehub = - await MoveHubModel.bluetoothManager.connect(movehubRegistryItem); + await MoveHubModel.bluetoothManager.connect(movehubRegistryItem) as MoveHub; } else { const selectedDevice = MoveHubModel.bluetoothManager.deviceList.find( device => device.native.id === identifier From f9fd9bc6c6b407ae29ba2c522e5e31d8c350fb0c Mon Sep 17 00:00:00 2001 From: Florence Haudin Date: Thu, 23 Apr 2026 17:36:44 +0200 Subject: [PATCH 10/10] Fix failing lint test. --- src/bluetooth/BluetoothManager.ts | 4 ++-- src/movehub-extension/widget.tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index 24937f0..1b9d4c7 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -83,7 +83,7 @@ export class BluetoothManager implements IBluetoothManager { } get identifierRegistry(): Array { - return Array.from(this._identifierMap.keys()); + return Array.from(this._identifierMap.keys()); } removeAllDevices(deviceList: Array) { @@ -122,7 +122,7 @@ export class BluetoothManager implements IBluetoothManager { private _deviceList: Array; public deviceListChanged: Signal>; private _deviceTypeRegistry: BluetoothManager.DeviceTypeRegistry; - private _identifierMap: Map + private _identifierMap: Map; } export namespace BluetoothManager { diff --git a/src/movehub-extension/widget.tsx b/src/movehub-extension/widget.tsx index 3979dbd..abc9d56 100644 --- a/src/movehub-extension/widget.tsx +++ b/src/movehub-extension/widget.tsx @@ -173,8 +173,9 @@ export class MoveHubModel extends DOMWidgetModel { /*if (!this.movehub.deviceInfo.connected) { console.log('not connected yet');*/ if (identifier === '') { - this.movehub = - await MoveHubModel.bluetoothManager.connect(movehubRegistryItem) as MoveHub; + this.movehub = (await MoveHubModel.bluetoothManager.connect( + movehubRegistryItem + )) as MoveHub; } else { const selectedDevice = MoveHubModel.bluetoothManager.deviceList.find( device => device.native.id === identifier