Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow to acces and choose the Hardware Adapter to use #189

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
28 changes: 28 additions & 0 deletions examples/list-adapters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Node Web Bluetooth
* Copyright (c) 2022 Rob Moran
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

const webbluetooth = require("../");

console.log(webbluetooth.getSimpleBleHardwareAdapters());
9 changes: 9 additions & 0 deletions src/adapters/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,13 @@ export interface Adapter extends EventEmitter {
disableNotify: (handle: string) => Promise<void>;
readDescriptor: (handle: string) => Promise<DataView>;
writeDescriptor: (handle: string, value: DataView) => Promise<void>;
useHardwareAdapter: (adapterIndex: number) => void;
getHardwareAdapters: () => Array<HardwareAdapterDetails>;
}

/** Basic Hardware Details for an Hardware adapter. */
export interface HardwareAdapterDetails {
identifier: string;
address: string;
active: boolean;
}
18 changes: 18 additions & 0 deletions src/adapters/simpleble-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ export class SimplebleAdapter extends EventEmitter implements BluetoothAdapter {
private descriptors = new Map<string, string[]>();
private charEvents = new Map<string, (value: DataView) => void>();

getHardwareAdapters() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getHardwareAdapters() {
public getHardwareAdapters() {

Please make this explicitly public

return getAdapters().map(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is to reduce the exposed fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because we do not want to expose the whole "adapter instance", but just the informative fields. I can add a comment

({ identifier, address, active }) =>
({ identifier, address, active })
);
}

useHardwareAdapter(adapterIndex: number) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
useHardwareAdapter(adapterIndex: number) {
public useHardwareAdapter(adapterIndex: number) {

Please make this explicitly public

if (this.adapter !== undefined) {
throw new Error('Can not change adapter after already used.');
}
const adapter = getAdapters()[adapterIndex];
if (adapter === undefined) {
throw new Error(`Adapter ${adapterIndex} not found.`);
}
this.adapter = getAdapters()[adapterIndex];
}

private validDevice(device: Partial<BluetoothDeviceImpl>, serviceUUIDs: Array<string>): boolean {
if (serviceUUIDs.length === 0) {
// Match any device
Expand Down
7 changes: 3 additions & 4 deletions src/adapters/simpleble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* SOFTWARE.
*/

import { HardwareAdapterDetails } from './adapter';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const simpleble = require('bindings')('simpleble.node');
module.exports = simpleble;
Expand Down Expand Up @@ -85,10 +87,7 @@ export interface Peripheral {
}

/** SimpleBLE Adapter. */
export interface Adapter {
identifier: string;
address: string;
active: boolean;
export interface Adapter extends HardwareAdapterDetails {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth merging these types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To have a clearly typed separateion between whats exposed as "information" and whats a full adapte rinstance I would leave it separated. Or what do you mean?

Copy link
Contributor Author

@Apollon77 Apollon77 Apr 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change names I would rather rename HardwareAdapterDetails to just AdapterDetails ...

But yes this naming overlapping that we have "native BLE adapters" aka "Hardware level" and "BLE providers like Noble OR SimpleBLE" (at least structurewise) that are also called "Adapters is difficult.

I could imagine renaing Adapter -> "Provider" (or maybe anopther better word to remove this multi-use of "Adapter" for the SimpleBle/Noble level and keep Adapter for the "native level". WDYT?

peripherals: Peripheral[];
pairedPeripherals: Peripheral[];
scanFor(ms: number): boolean;
Expand Down
13 changes: 13 additions & 0 deletions src/bluetooth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { adapter, EVENT_ENABLED } from './adapters';
import { BluetoothDeviceImpl, BluetoothDeviceEvents } from './device';
import { BluetoothUUID } from './uuid';
import { EventDispatcher, DOMEvent } from './events';
import { HardwareAdapterDetails } from './adapters/adapter';

/**
* Bluetooth Options interface
Expand All @@ -51,6 +52,11 @@ export interface BluetoothOptions {
* An optional referring device
*/
referringDevice?: BluetoothDevice;

/**
* An optional BLE Hardware Adapter index to use
*/
hardwareAdapterIndex?: number;
}

/**
Expand Down Expand Up @@ -88,6 +94,9 @@ export class BluetoothImpl extends EventDispatcher<BluetoothEvents> implements B
if (options.scanTime) {
this.scanTime = options.scanTime * 1000;
}
if (typeof options.hardwareAdapterIndex === 'number') {
adapter.useHardwareAdapter(options.hardwareAdapterIndex);
}

adapter.on(EVENT_ENABLED, _value => {
this.dispatchEvent(new DOMEvent(this, 'availabilitychanged'));
Expand Down Expand Up @@ -405,3 +414,7 @@ export class BluetoothImpl extends EventDispatcher<BluetoothEvents> implements B
throw new Error('requestLEScan error: method not implemented.');
}
}

export function getHardwareAdapters(): HardwareAdapterDetails[] {
return adapter.getHardwareAdapters();
}
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* SOFTWARE.
*/

import { BluetoothImpl, BluetoothOptions } from './bluetooth';
import { BluetoothImpl, BluetoothOptions, getHardwareAdapters } from './bluetooth';

/**
* Default bluetooth instance synonymous with `navigator.bluetooth`
Expand All @@ -33,7 +33,7 @@ export const bluetooth = new BluetoothImpl();
/**
* Bluetooth class for creating new instances
*/
export { BluetoothImpl as Bluetooth, BluetoothOptions };
export { BluetoothImpl as Bluetooth, BluetoothOptions, getHardwareAdapters as getSimpleBleHardwareAdapters };

/**
* Helper methods and enums
Expand Down