diff --git a/src/bluetooth-extension/index.ts b/src/bluetooth-extension/index.ts index dc3170f..684a774 100644 --- a/src/bluetooth-extension/index.ts +++ b/src/bluetooth-extension/index.ts @@ -16,7 +16,7 @@ import { export namespace CommandIDs { export const openDeviceRegistryDialog = 'bluetooth-manager:open-dialog-for-devices-registry'; - export const disconnectDevice = 'bluetooth-manager:disconnect-device'; + export const disconnect = 'bluetooth-manager:disconnect-device'; } export function buildCompleteIdentifier(native: BluetoothDevice): string { @@ -67,13 +67,13 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin = { const openDeviceRegistryDialogLabel = trans.__('Add a Device'); let runningItemsList: Array; - app.commands.addCommand(CommandIDs.disconnectDevice, { + app.commands.addCommand(CommandIDs.disconnect, { execute: args => { const selectedDevice = bluetoothManager.deviceList.find( device => device.native.id === (args.deviceID as string) ); if (selectedDevice) { - bluetoothManager.disconnectDevice(selectedDevice); + bluetoothManager.disconnect(selectedDevice); return selectedDevice; } else { throw new Error('No device provided or device is invalid'); @@ -87,20 +87,24 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin = { execute: async () => { showDialog({ title: 'Select device type', - body: new DropDownRegistry(bluetoothManager.registry), + body: new DropDownRegistry(bluetoothManager.deviceTypeRegistry), buttons: [ Dialog.okButton({ label: 'Select' }), Dialog.cancelButton({ label: 'Cancel' }) ] }).then(async result => { if (result.button.accept) { - bluetoothManager.registry.itemsList.forEach(async item => { - if (item.deviceType === result.value) { - await bluetoothManager.connectDevice(item); - } else { - console.warn('There is no corresponding item in the registry!'); + bluetoothManager.deviceTypeRegistry.deviceTypes.forEach( + async item => { + if (item.deviceType === result.value) { + await bluetoothManager.connect(item); + } else { + console.warn( + 'There is no corresponding item in the registry!' + ); + } } - }); + ); } }); } @@ -151,12 +155,12 @@ export class DropDownRegistry extends Widget implements Dialog.IBodyWidget { - constructor(registry: BluetoothManager.DeviceRegistry) { + constructor(registry: BluetoothManager.DeviceTypeRegistry) { super(); this._selectList = document.createElement('select'); this.node.appendChild(this._selectList); this.registry = registry; - registry.itemsList.forEach(item => { + registry.deviceTypes.forEach(item => { const option = document.createElement('option'); option.value = item.deviceType; option.text = item.deviceType; @@ -169,7 +173,7 @@ export class DropDownRegistry } private _selectList: HTMLSelectElement; - public registry: BluetoothManager.DeviceRegistry; + public registry: BluetoothManager.DeviceTypeRegistry; } const BluetoothExtensionPlugins: JupyterFrontEndPlugin[] = [ diff --git a/src/bluetooth/BluetoothDeviceRunningItem.ts b/src/bluetooth/BluetoothDeviceRunningItem.ts index 158794c..3d4373f 100644 --- a/src/bluetooth/BluetoothDeviceRunningItem.ts +++ b/src/bluetooth/BluetoothDeviceRunningItem.ts @@ -5,7 +5,7 @@ import { buildCompleteIdentifier } from '../bluetooth-extension'; import { Menu } from '@lumino/widgets'; import { CommandRegistry } from '@lumino/commands'; -export const disconnectDevice = 'bluetooth-manager:disconnect-device'; +export const disconnect = 'bluetooth-manager:disconnect-device'; export class BluetoothDeviceRunningItem implements IRunningSessions.IRunningItem @@ -58,7 +58,7 @@ export class BluetoothDeviceRunningItem } shutdown() { - this.bluetoothManager.disconnectDevice(this._device); + this.bluetoothManager.disconnect(this._device); } private _device: BluetoothManager.Device; diff --git a/src/bluetooth/BluetoothManager.ts b/src/bluetooth/BluetoothManager.ts index c08376d..3022656 100644 --- a/src/bluetooth/BluetoothManager.ts +++ b/src/bluetooth/BluetoothManager.ts @@ -13,11 +13,7 @@ export class BluetoothManager implements IBluetoothManager { this.deviceListChanged = new Signal>( this ); - this.registeredByAPlugin = new Signal< - this, - BluetoothManager.DeviceRegistry - >(this); - this._registry = new BluetoothManager.DeviceRegistry(); + this._deviceTypeRegistry = new BluetoothManager.DeviceTypeRegistry(); this._deviceList = []; this._identifierRegistry = []; } @@ -26,54 +22,50 @@ export class BluetoothManager implements IBluetoothManager { return this._deviceList; } - get registry(): BluetoothManager.DeviceRegistry { - return this._registry; + get deviceTypeRegistry(): BluetoothManager.DeviceTypeRegistry { + return this._deviceTypeRegistry; } - get identifierRegistry(): Array { - return this._identifierRegistry; - } - - async connectDevice( - registryItem: IDeviceRegistryItem + 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); + this._addDeviceToList(device); device.disconnected.connect(async () => { - this.removeDeviceFromList(device); + this._removeDeviceFromList(device); }); return device; } } } - async disconnectDevice(device: BluetoothManager.Device) { + async disconnect(device: BluetoothManager.Device) { await device.disconnect(); device.dispose(); } // Method to add a device to the list - addDeviceToList(device: BluetoothManager.Device): void { + 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._identifierRegistry.push(identifier); } else { - console.warn('The device is already in the identifierRegistry'); + console.warn('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 - removeDeviceFromList(device: BluetoothManager.Device): void { + private _removeDeviceFromList(device: BluetoothManager.Device): void { const index = this._deviceList.indexOf(device); if (index > -1) { this._deviceList.splice(index, 1); - this.identifierRegistry.splice(index, 1); + this._identifierRegistry.splice(index, 1); // Emit the signal when the list changes this.deviceListChanged.emit(this._deviceList); } @@ -82,20 +74,11 @@ export class BluetoothManager implements IBluetoothManager { removeAllDevices() { this._deviceList.forEach((device, index) => { - this.removeDeviceFromList(device); + this._removeDeviceFromList(device); this.deviceListChanged.emit(this._deviceList); }); } - register(registryItem: IDeviceRegistryItem) { - this._registry.add(registryItem); - this.registeredByAPlugin.emit(this._registry); - console.warn( - `New item from category ${registryItem.deviceType} is added to the registry.` - ); - return this._registry; - } - async checkWebBluetoothSupport(): Promise { const isWebBluetoothSupported: boolean = navigator.bluetooth ? true : false; if (isWebBluetoothSupported === false) { @@ -109,7 +92,7 @@ export class BluetoothManager implements IBluetoothManager { } async requestDevice( - registryItem: IDeviceRegistryItem + registryItem: IDeviceTypeRegistryItem ): Promise { const isWebBluetoothSupported = await this.checkWebBluetoothSupport(); if (isWebBluetoothSupported) { @@ -124,11 +107,7 @@ export class BluetoothManager implements IBluetoothManager { private _deviceList: Array; public deviceListChanged: Signal>; - public registeredByAPlugin: Signal< - BluetoothManager, - BluetoothManager.DeviceRegistry - >; - private _registry: BluetoothManager.DeviceRegistry; + private _deviceTypeRegistry: BluetoothManager.DeviceTypeRegistry; private _identifierRegistry: Array; } @@ -204,32 +183,6 @@ export namespace BluetoothManager { } } - /*async connectAndGetAllServices(): Promise< - Array | 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) { - const services = await server.getPrimaryServices(); - this.isConnected = true; - 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.'); - } - }*/ - async disconnect(): Promise { if (this.native) { this.native.gatt?.disconnect(); @@ -283,19 +236,27 @@ export namespace BluetoothManager { } } - export class DeviceRegistry implements IDeviceRegistry { - private _registry: Array; - public registryItem: IDeviceRegistryItem; + export class DeviceTypeRegistry implements IDeviceTypeRegistry { constructor() { - this._registry = []; + this._deviceTypes = []; + this._added = new Signal(this); + } + + add(registryItem: IDeviceTypeRegistryItem) { + this._deviceTypes.push(registryItem); + this._added.emit(registryItem); } - add(registryItem: IDeviceRegistryItem) { - this._registry.push(registryItem); + get deviceTypes(): IDeviceTypeRegistryItem[] { + return this._deviceTypes; } - get itemsList(): Array { - return this._registry; + + get added(): Signal { + return this._added; } + + private _deviceTypes: Array; + private _added: Signal; } } @@ -303,23 +264,15 @@ export namespace BluetoothManager { * Interface for the bluetooth manager. */ export interface IBluetoothManager { - addDeviceToList(Device: BluetoothManager.Device): void; - removeDeviceFromList(Device: BluetoothManager.Device): void; removeAllDevices(Devices: Array): void; - register(registryItem: IDeviceRegistryItem): BluetoothManager.DeviceRegistry; - connectDevice(registryItem: IDeviceRegistryItem): any; - disconnectDevice(device: BluetoothManager.Device): void; + connect(registryItem: IDeviceTypeRegistryItem): any; + disconnect(device: BluetoothManager.Device): void; deviceListChanged: Signal>; - registeredByAPlugin: Signal< - BluetoothManager, - BluetoothManager.DeviceRegistry - >; get deviceList(): Array; - get registry(): BluetoothManager.DeviceRegistry; - get identifierRegistry(): Array; + get deviceTypeRegistry(): BluetoothManager.DeviceTypeRegistry; } -export interface IDeviceRegistryItem { +export interface IDeviceTypeRegistryItem { deviceType: string; factory: ( native: BluetoothDevice @@ -327,9 +280,9 @@ export interface IDeviceRegistryItem { options: IDeviceOptions; } -export interface IDeviceRegistry { - add: (registryItem: IDeviceRegistryItem) => void; - get itemsList(): Array; +export interface IDeviceTypeRegistry { + add: (registryItem: IDeviceTypeRegistryItem) => void; + get deviceTypes(): IDeviceTypeRegistryItem[]; } export const IBluetoothManager = new Token( diff --git a/src/movehub-extension/index.ts b/src/movehub-extension/index.ts index 19b2505..147d40b 100644 --- a/src/movehub-extension/index.ts +++ b/src/movehub-extension/index.ts @@ -6,7 +6,7 @@ import { ITranslator } from '@jupyterlab/translation'; import { IThemeManager, MainAreaWidget } from '@jupyterlab/apputils'; import { Toolbar } from '@jupyterlab/ui-components'; import { - IDeviceRegistryItem, + IDeviceTypeRegistryItem, IBluetoothManager, BluetoothManager } from '../bluetooth/BluetoothManager'; @@ -27,7 +27,7 @@ export const connectMoveHub = 'bluetooth-manager:connect-movehub'; export const disconnectMoveHub = 'bluetooth-manager:disconnect-movehub'; export const moveHubServiceUUID = '00001623-1212-efde-1623-785feabcd123'; export const moveHubCharacteristicUUID = '00001624-1212-efde-1623-785feabcd123'; -export const movehubRegistryItem: IDeviceRegistryItem = { +export const movehubRegistryItem: IDeviceTypeRegistryItem = { deviceType: 'LEGO® Move Hub', options: { acceptAllDevices: false, @@ -56,7 +56,14 @@ const MoveHubRegisterPlugin: JupyterFrontEndPlugin = { bluetoothManager: BluetoothManager ): void => { console.log('JupyterLab move-hub-register plugin is activated!'); - bluetoothManager.register(movehubRegistryItem); + bluetoothManager.deviceTypeRegistry.added.connect( + async (sender, movehubRegistryItem) => { + console.warn( + `New item from category ${movehubRegistryItem.deviceType} is added to the deviceType registry.` + ); + } + ); + bluetoothManager.deviceTypeRegistry.add(movehubRegistryItem); } }; @@ -125,7 +132,7 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin = { device => device.native.id === (args.deviceID as string) ); if (selectedDevice && selectedDevice instanceof MoveHub) { - bluetoothManager.disconnectDevice(selectedDevice); + bluetoothManager.disconnect(selectedDevice); return selectedDevice; } else { throw new Error('No device provided or device is invalid'); @@ -151,7 +158,7 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin = { app.commands.addCommand(connectMoveHub, { execute: args => { - const newDevice = bluetoothManager.connectDevice(movehubRegistryItem); + const newDevice = bluetoothManager.connect(movehubRegistryItem); return newDevice; }, caption: 'Connect MoveHub', diff --git a/src/movehub-extension/widget.tsx b/src/movehub-extension/widget.tsx index f5d7dd1..789ca4b 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.connectDevice(movehubRegistryItem); + await MoveHubModel.bluetoothManager.connect(movehubRegistryItem); } else { const selectedDevice = MoveHubModel.bluetoothManager.deviceList.find( device => device.native.id === identifier