@matter/nodejs-ble 0.11.0-alpha.0-20241005-e3e4e4a7a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +55 -0
  3. package/dist/cjs/BleBroadcaster.d.ts +20 -0
  4. package/dist/cjs/BleBroadcaster.d.ts.map +1 -0
  5. package/dist/cjs/BleBroadcaster.js +121 -0
  6. package/dist/cjs/BleBroadcaster.js.map +6 -0
  7. package/dist/cjs/BlePeripheralInterface.d.ts +15 -0
  8. package/dist/cjs/BlePeripheralInterface.d.ts.map +1 -0
  9. package/dist/cjs/BlePeripheralInterface.js +54 -0
  10. package/dist/cjs/BlePeripheralInterface.js.map +6 -0
  11. package/dist/cjs/BleScanner.d.ts +52 -0
  12. package/dist/cjs/BleScanner.d.ts.map +1 -0
  13. package/dist/cjs/BleScanner.js +240 -0
  14. package/dist/cjs/BleScanner.js.map +6 -0
  15. package/dist/cjs/BlenoBleServer.d.ts +70 -0
  16. package/dist/cjs/BlenoBleServer.d.ts.map +1 -0
  17. package/dist/cjs/BlenoBleServer.js +337 -0
  18. package/dist/cjs/BlenoBleServer.js.map +6 -0
  19. package/dist/cjs/NobleBleChannel.d.ts +32 -0
  20. package/dist/cjs/NobleBleChannel.d.ts.map +1 -0
  21. package/dist/cjs/NobleBleChannel.js +266 -0
  22. package/dist/cjs/NobleBleChannel.js.map +6 -0
  23. package/dist/cjs/NobleBleClient.d.ts +20 -0
  24. package/dist/cjs/NobleBleClient.d.ts.map +1 -0
  25. package/dist/cjs/NobleBleClient.js +108 -0
  26. package/dist/cjs/NobleBleClient.js.map +6 -0
  27. package/dist/cjs/NodeJsBle.d.ts +22 -0
  28. package/dist/cjs/NodeJsBle.d.ts.map +1 -0
  29. package/dist/cjs/NodeJsBle.js +68 -0
  30. package/dist/cjs/NodeJsBle.js.map +6 -0
  31. package/dist/cjs/index.d.ts +9 -0
  32. package/dist/cjs/index.d.ts.map +1 -0
  33. package/dist/cjs/index.js +26 -0
  34. package/dist/cjs/index.js.map +6 -0
  35. package/dist/cjs/package.json +3 -0
  36. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  37. package/dist/esm/BleBroadcaster.d.ts +20 -0
  38. package/dist/esm/BleBroadcaster.d.ts.map +1 -0
  39. package/dist/esm/BleBroadcaster.js +101 -0
  40. package/dist/esm/BleBroadcaster.js.map +6 -0
  41. package/dist/esm/BlePeripheralInterface.d.ts +15 -0
  42. package/dist/esm/BlePeripheralInterface.d.ts.map +1 -0
  43. package/dist/esm/BlePeripheralInterface.js +34 -0
  44. package/dist/esm/BlePeripheralInterface.js.map +6 -0
  45. package/dist/esm/BleScanner.d.ts +52 -0
  46. package/dist/esm/BleScanner.d.ts.map +1 -0
  47. package/dist/esm/BleScanner.js +220 -0
  48. package/dist/esm/BleScanner.js.map +6 -0
  49. package/dist/esm/BlenoBleServer.d.ts +70 -0
  50. package/dist/esm/BlenoBleServer.d.ts.map +1 -0
  51. package/dist/esm/BlenoBleServer.js +327 -0
  52. package/dist/esm/BlenoBleServer.js.map +6 -0
  53. package/dist/esm/NobleBleChannel.d.ts +32 -0
  54. package/dist/esm/NobleBleChannel.d.ts.map +1 -0
  55. package/dist/esm/NobleBleChannel.js +266 -0
  56. package/dist/esm/NobleBleChannel.js.map +6 -0
  57. package/dist/esm/NobleBleClient.d.ts +20 -0
  58. package/dist/esm/NobleBleClient.d.ts.map +1 -0
  59. package/dist/esm/NobleBleClient.js +88 -0
  60. package/dist/esm/NobleBleClient.js.map +6 -0
  61. package/dist/esm/NodeJsBle.d.ts +22 -0
  62. package/dist/esm/NodeJsBle.d.ts.map +1 -0
  63. package/dist/esm/NodeJsBle.js +48 -0
  64. package/dist/esm/NodeJsBle.js.map +6 -0
  65. package/dist/esm/index.d.ts +9 -0
  66. package/dist/esm/index.d.ts.map +1 -0
  67. package/dist/esm/index.js +9 -0
  68. package/dist/esm/index.js.map +6 -0
  69. package/dist/esm/package.json +3 -0
  70. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  71. package/package.json +83 -0
  72. package/require/package.json +4 -0
  73. package/require/require.cjs +1 -0
  74. package/require/require.d.ts +1 -0
  75. package/require/require.mjs +3 -0
  76. package/src/BleBroadcaster.ts +126 -0
  77. package/src/BlePeripheralInterface.ts +36 -0
  78. package/src/BleScanner.ts +279 -0
  79. package/src/BlenoBleServer.ts +403 -0
  80. package/src/NobleBleChannel.ts +337 -0
  81. package/src/NobleBleClient.ts +117 -0
  82. package/src/NodeJsBle.ts +56 -0
  83. package/src/index.ts +9 -0
  84. package/src/tsconfig.json +16 -0
@@ -0,0 +1,337 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {
8
+ Channel,
9
+ ChannelType,
10
+ InternalError,
11
+ Logger,
12
+ NetInterface,
13
+ ServerAddress,
14
+ Time,
15
+ TransportInterface,
16
+ createPromise,
17
+ } from "@matter/general";
18
+ import {
19
+ BLE_MATTER_C1_CHARACTERISTIC_UUID,
20
+ BLE_MATTER_C2_CHARACTERISTIC_UUID,
21
+ BLE_MATTER_C3_CHARACTERISTIC_UUID,
22
+ BLE_MATTER_SERVICE_UUID,
23
+ BLE_MAXIMUM_BTP_MTU,
24
+ BTP_CONN_RSP_TIMEOUT_MS,
25
+ BTP_MAXIMUM_WINDOW_SIZE,
26
+ BTP_SUPPORTED_VERSIONS,
27
+ Ble,
28
+ BleChannel,
29
+ BleError,
30
+ BtpFlowError,
31
+ BtpSessionHandler,
32
+ } from "@project-chip/matter.js/ble";
33
+ import { BtpCodec } from "@project-chip/matter.js/codec";
34
+ import type { Characteristic, Peripheral } from "@stoprocent/noble";
35
+ import { BleScanner } from "./BleScanner.js";
36
+
37
+ const logger = Logger.get("BleChannel");
38
+
39
+ /**
40
+ * Convert a UUID in noble's format to a proper UUID.
41
+ *
42
+ * @param {string} uuid - UUID to convert
43
+ * @returns {string} UUID
44
+ */
45
+ function nobleUuidToUuid(uuid: string): string {
46
+ uuid = uuid.toUpperCase();
47
+
48
+ if (uuid.length !== 32) {
49
+ return uuid;
50
+ }
51
+
52
+ const parts = [
53
+ uuid.substring(0, 8),
54
+ uuid.substring(8, 12),
55
+ uuid.substring(12, 16),
56
+ uuid.substring(16, 20),
57
+ uuid.substring(20, 32),
58
+ ];
59
+
60
+ return parts.join("-");
61
+ }
62
+
63
+ export class NobleBleCentralInterface implements NetInterface {
64
+ private openChannels: Map<ServerAddress, Peripheral> = new Map();
65
+ private onMatterMessageListener: ((socket: Channel<Uint8Array>, data: Uint8Array) => void) | undefined;
66
+
67
+ openChannel(address: ServerAddress, tryCount = 1): Promise<Channel<Uint8Array>> {
68
+ return new Promise((resolve, reject) => {
69
+ if (this.onMatterMessageListener === undefined) {
70
+ reject(new InternalError(`Network Interface was not added to the system yet.`));
71
+ return;
72
+ }
73
+ if (address.type !== "ble") {
74
+ reject(new InternalError(`Unsupported address type ${address.type}.`));
75
+ return;
76
+ }
77
+
78
+ // Get the peripheral by address and connect to it.
79
+ const { peripheral, hasAdditionalAdvertisementData } = (
80
+ Ble.get().getBleScanner() as BleScanner
81
+ ).getDiscoveredDevice(address.peripheralAddress);
82
+
83
+ if (tryCount > 3) {
84
+ reject(new BleError(`Failed to connect to peripheral ${peripheral.address}`));
85
+ return;
86
+ }
87
+
88
+ logger.debug("BLE peripheral state", peripheral.state);
89
+ if (peripheral.state === "connected" || peripheral.state === "connecting") {
90
+ reject(
91
+ new BleError(
92
+ `Peripheral ${address.peripheralAddress} is already connected or connecting. Only one connection supported right now.`,
93
+ ),
94
+ );
95
+ return;
96
+ }
97
+ if (this.openChannels.has(address)) {
98
+ reject(
99
+ new BleError(
100
+ `Peripheral ${address.peripheralAddress} is already connected. Only one connection supported right now.`,
101
+ ),
102
+ );
103
+ return;
104
+ }
105
+ if (peripheral.state !== "disconnected") {
106
+ // Try to cleanup strange "in between" states
107
+ peripheral.disconnectAsync().then(() => this.openChannel(address, tryCount), reject);
108
+ return;
109
+ }
110
+
111
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
112
+ peripheral.once("connect", async () => {
113
+ if (this.onMatterMessageListener === undefined) {
114
+ reject(new InternalError(`Network Interface was not added to the system yet.`));
115
+ return;
116
+ }
117
+
118
+ const services = await peripheral.discoverServicesAsync([BLE_MATTER_SERVICE_UUID]);
119
+ logger.debug(`Found services: ${services.map(s => s.uuid).join(", ")}`);
120
+
121
+ for (const service of services) {
122
+ logger.debug(`found service: ${service.uuid}`);
123
+ if (service.uuid !== BLE_MATTER_SERVICE_UUID) continue;
124
+
125
+ // So, discover its characteristics.
126
+ const characteristics = await service.discoverCharacteristicsAsync();
127
+
128
+ let characteristicC1ForWrite: Characteristic | undefined;
129
+ let characteristicC2ForSubscribe: Characteristic | undefined;
130
+ let additionalCommissioningRelatedData: Uint8Array | undefined;
131
+
132
+ for (const characteristic of characteristics) {
133
+ // Loop through each characteristic and match them to the UUIDs that we know about.
134
+ logger.debug("found characteristic:", characteristic.uuid, characteristic.properties);
135
+
136
+ switch (nobleUuidToUuid(characteristic.uuid)) {
137
+ case BLE_MATTER_C1_CHARACTERISTIC_UUID:
138
+ logger.debug("found C1 characteristic");
139
+ characteristicC1ForWrite = characteristic;
140
+ break;
141
+
142
+ case BLE_MATTER_C2_CHARACTERISTIC_UUID:
143
+ logger.debug("found C2 characteristic");
144
+ characteristicC2ForSubscribe = characteristic;
145
+ break;
146
+
147
+ case BLE_MATTER_C3_CHARACTERISTIC_UUID:
148
+ logger.debug("found C3 characteristic");
149
+ if (hasAdditionalAdvertisementData) {
150
+ logger.debug("reading additional commissioning related data");
151
+ const data = await characteristic.readAsync();
152
+ additionalCommissioningRelatedData = new Uint8Array(data);
153
+ logger.debug("additional data", data);
154
+ }
155
+ }
156
+ }
157
+
158
+ if (!characteristicC1ForWrite || !characteristicC2ForSubscribe) {
159
+ logger.debug("missing characteristics");
160
+ continue;
161
+ }
162
+
163
+ peripheral.removeAllListeners("disconnect");
164
+ this.openChannels.set(address, peripheral);
165
+ resolve(
166
+ await NobleBleChannel.create(
167
+ peripheral,
168
+ characteristicC1ForWrite,
169
+ characteristicC2ForSubscribe,
170
+ this.onMatterMessageListener,
171
+ additionalCommissioningRelatedData,
172
+ ),
173
+ );
174
+ return;
175
+ }
176
+
177
+ peripheral.removeAllListeners("disconnect");
178
+ reject(new BleError(`Peripheral ${peripheral.address} does not have the required characteristics`));
179
+ });
180
+ const reTryHandler = async (error?: any) => {
181
+ if (error) {
182
+ logger.error(
183
+ `Peripheral ${peripheral.address} disconnected while trying to connect, try again`,
184
+ error,
185
+ );
186
+ } else {
187
+ logger.info(`Peripheral ${peripheral.address} disconnected while trying to connect, try gain`);
188
+ }
189
+ // Cleanup listeners and try again
190
+ peripheral.removeAllListeners("disconnect");
191
+ peripheral.removeAllListeners("connect");
192
+ this.openChannel(address, tryCount + 1)
193
+ .then(resolve)
194
+ .catch(reject);
195
+ };
196
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
197
+ peripheral.once("disconnect", reTryHandler);
198
+ logger.debug(`Connect to Peripheral now (try ${tryCount})`);
199
+ peripheral.connectAsync().catch(reTryHandler);
200
+ });
201
+ }
202
+
203
+ onData(listener: (socket: Channel<Uint8Array>, data: Uint8Array) => void): TransportInterface.Listener {
204
+ this.onMatterMessageListener = listener;
205
+ return {
206
+ close: async () => await this.close(),
207
+ };
208
+ }
209
+
210
+ async close() {
211
+ for (const peripheral of this.openChannels.values()) {
212
+ await peripheral.disconnectAsync();
213
+ }
214
+ }
215
+
216
+ supports(type: ChannelType, _address?: string) {
217
+ if (type !== ChannelType.BLE) {
218
+ return false;
219
+ }
220
+ return true;
221
+ }
222
+ }
223
+
224
+ export class NobleBleChannel extends BleChannel<Uint8Array> {
225
+ static async create(
226
+ peripheral: Peripheral,
227
+ characteristicC1ForWrite: Characteristic,
228
+ characteristicC2ForSubscribe: Characteristic,
229
+ onMatterMessageListener: (socket: Channel<Uint8Array>, data: Uint8Array) => void,
230
+ _additionalCommissioningRelatedData?: Uint8Array,
231
+ ): Promise<NobleBleChannel> {
232
+ let mtu = peripheral.mtu ?? 0;
233
+ if (mtu > BLE_MAXIMUM_BTP_MTU) {
234
+ mtu = BLE_MAXIMUM_BTP_MTU;
235
+ }
236
+ logger.debug(`Using MTU=${mtu} (Peripheral MTU=${peripheral.mtu})`);
237
+ const btpHandshakeRequest = BtpCodec.encodeBtpHandshakeRequest({
238
+ versions: BTP_SUPPORTED_VERSIONS,
239
+ attMtu: mtu,
240
+ clientWindowSize: BTP_MAXIMUM_WINDOW_SIZE,
241
+ });
242
+ logger.debug(`sending BTP handshake request: ${Logger.toJSON(btpHandshakeRequest)}`);
243
+ await characteristicC1ForWrite.writeAsync(Buffer.from(btpHandshakeRequest.buffer), false);
244
+
245
+ const btpHandshakeTimeout = Time.getTimer("BLE handshake timeout", BTP_CONN_RSP_TIMEOUT_MS, async () => {
246
+ await peripheral.disconnectAsync();
247
+ logger.debug("Handshake Response not received. Disconnected from peripheral");
248
+ }).start();
249
+
250
+ logger.debug("subscribing to C2 characteristic");
251
+ await characteristicC2ForSubscribe.subscribeAsync();
252
+
253
+ const { promise: handshakeResponseReceivedPromise, resolver } = createPromise<Buffer>();
254
+
255
+ characteristicC2ForSubscribe.once("data", (data, isNotification) => {
256
+ logger.debug(`received first data on C2: ${data.toString("hex")} (isNotification: ${isNotification})`);
257
+
258
+ if (data[0] === 0x65 && data[1] === 0x6c && data.length === 6) {
259
+ // Check if the first two bytes and length match the Matter handshake
260
+ logger.info(`Received Matter handshake response: ${data.toString("hex")}.`);
261
+ btpHandshakeTimeout.stop();
262
+ resolver(data);
263
+ }
264
+ });
265
+
266
+ const handshakeResponse = await handshakeResponseReceivedPromise;
267
+
268
+ const btpSession = await BtpSessionHandler.createAsCentral(
269
+ new Uint8Array(handshakeResponse),
270
+ // callback to write data to characteristic C1
271
+ async (data: Uint8Array) => {
272
+ return await characteristicC1ForWrite.writeAsync(Buffer.from(data.buffer), false);
273
+ },
274
+ // callback to disconnect the BLE connection
275
+ async () =>
276
+ void characteristicC2ForSubscribe
277
+ .unsubscribeAsync()
278
+ .then(() => peripheral.disconnectAsync().then(() => logger.debug("disconnected from peripheral"))),
279
+ // callback to forward decoded and de-assembled Matter messages to ExchangeManager
280
+ async (data: Uint8Array) => {
281
+ if (onMatterMessageListener === undefined) {
282
+ throw new InternalError(`No listener registered for Matter messages`);
283
+ }
284
+ onMatterMessageListener(nobleChannel, data);
285
+ },
286
+ );
287
+
288
+ characteristicC2ForSubscribe.on("data", (data, isNotification) => {
289
+ logger.debug(`received data on C2: ${data.toString("hex")} (isNotification: ${isNotification})`);
290
+
291
+ void btpSession.handleIncomingBleData(new Uint8Array(data));
292
+ });
293
+
294
+ const nobleChannel = new NobleBleChannel(peripheral, btpSession);
295
+ return nobleChannel;
296
+ }
297
+
298
+ private connected = true;
299
+
300
+ constructor(
301
+ private readonly peripheral: Peripheral,
302
+ private readonly btpSession: BtpSessionHandler,
303
+ ) {
304
+ super();
305
+ peripheral.once("disconnect", () => {
306
+ logger.debug(`Disconnected from peripheral ${peripheral.address}`);
307
+ this.connected = false;
308
+ void this.btpSession.close();
309
+ });
310
+ }
311
+
312
+ /**
313
+ * Send a Matter message to the connected device - need to do BTP assembly first.
314
+ *
315
+ * @param data
316
+ */
317
+ async send(data: Uint8Array) {
318
+ if (!this.connected) {
319
+ logger.debug("Cannot send data because not connected to peripheral.");
320
+ return;
321
+ }
322
+ if (this.btpSession === undefined) {
323
+ throw new BtpFlowError(`Cannot send data, no BTP session initialized`);
324
+ }
325
+ await this.btpSession.sendMatterMessage(data);
326
+ }
327
+
328
+ // Channel<Uint8Array>
329
+ get name() {
330
+ return `${this.type}://${this.peripheral.address}`;
331
+ }
332
+
333
+ async close() {
334
+ await this.btpSession.close();
335
+ await this.peripheral.disconnectAsync();
336
+ }
337
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "@matter/general";
8
+ import { require } from "@matter/nodejs-ble/require";
9
+ import { BLE_MATTER_SERVICE_UUID } from "@project-chip/matter.js/ble";
10
+ import type { Peripheral } from "@stoprocent/noble";
11
+ import { BleOptions } from "./NodeJsBle.js";
12
+
13
+ const logger = Logger.get("NobleBleClient");
14
+ let noble: typeof import("@stoprocent/noble");
15
+
16
+ function loadNoble(hciId?: number) {
17
+ // load noble driver with the correct device selected
18
+ if (hciId !== undefined) {
19
+ process.env.NOBLE_HCI_DEVICE_ID = hciId.toString();
20
+ }
21
+ noble = require("@stoprocent/noble");
22
+ if (typeof noble.on !== "function") {
23
+ // The following commit broke the default exported instance of noble:
24
+ // https://github.com/abandonware/noble/commit/b67eea246f719947fc45b1b52b856e61637a8a8e
25
+ noble = (noble as any)({ extended: false });
26
+ }
27
+ }
28
+
29
+ export class NobleBleClient {
30
+ private readonly discoveredPeripherals = new Map<
31
+ string,
32
+ { peripheral: Peripheral; matterServiceData: Uint8Array }
33
+ >();
34
+ private shouldScan = false;
35
+ private isScanning = false;
36
+ private nobleState = "unknown";
37
+ private deviceDiscoveredCallback: ((peripheral: Peripheral, manufacturerData: Uint8Array) => void) | undefined;
38
+
39
+ constructor(options?: BleOptions) {
40
+ loadNoble(options?.hciId);
41
+ /*try {
42
+ noble.reset();
43
+ } catch (error: any) {
44
+ logger.debug(
45
+ `Error resetting BLE device via noble (can be ignored, we just tried): ${
46
+ (error as unknown as Error).message
47
+ }`,
48
+ );
49
+ }*/
50
+ noble.on("stateChange", state => {
51
+ this.nobleState = state;
52
+ logger.debug(`Noble state changed to ${state}`);
53
+ if (state === "poweredOn") {
54
+ if (this.shouldScan) {
55
+ void this.startScanning();
56
+ }
57
+ } else {
58
+ void this.stopScanning();
59
+ }
60
+ });
61
+ noble.on("discover", peripheral => this.handleDiscoveredDevice(peripheral));
62
+ noble.on("scanStart", () => (this.isScanning = true));
63
+ noble.on("scanStop", () => (this.isScanning = false));
64
+ }
65
+
66
+ public setDiscoveryCallback(callback: (peripheral: Peripheral, manufacturerData: Uint8Array) => void) {
67
+ this.deviceDiscoveredCallback = callback;
68
+ for (const { peripheral, matterServiceData } of this.discoveredPeripherals.values()) {
69
+ this.deviceDiscoveredCallback(peripheral, matterServiceData);
70
+ }
71
+ }
72
+
73
+ public async startScanning() {
74
+ if (this.isScanning) return;
75
+
76
+ this.shouldScan = true;
77
+ if (this.nobleState === "poweredOn") {
78
+ logger.debug("Start BLE scanning for Matter Services ...");
79
+ await noble.startScanningAsync([BLE_MATTER_SERVICE_UUID], false);
80
+ } else {
81
+ logger.debug("noble state is not poweredOn ... delay scanning till poweredOn");
82
+ }
83
+ }
84
+
85
+ public async stopScanning() {
86
+ this.shouldScan = false;
87
+ logger.debug("Stop BLE scanning for Matter Services ...");
88
+ await noble.stopScanningAsync();
89
+ }
90
+
91
+ private handleDiscoveredDevice(peripheral: Peripheral) {
92
+ // The advertisement data contains a name, power level (if available), certain advertised service uuids,
93
+ // as well as manufacturer data.
94
+ // {"localName":"MATTER-3840","serviceData":[{"uuid":"fff6","data":{"type":"Buffer","data":[0,0,15,241,255,1,128,0]}}],"serviceUuids":["fff6"],"solicitationServiceUuids":[],"serviceSolicitationUuids":[]}
95
+ logger.debug(
96
+ `Found peripheral ${peripheral.address} (${peripheral.advertisement.localName}): ${Logger.toJSON(
97
+ peripheral.advertisement,
98
+ )}`,
99
+ );
100
+
101
+ if (!peripheral.connectable) {
102
+ logger.info(`Peripheral ${peripheral.address} is not connectable ... ignoring`);
103
+ return;
104
+ }
105
+ const matterServiceData = peripheral.advertisement.serviceData.find(
106
+ serviceData => serviceData.uuid === BLE_MATTER_SERVICE_UUID,
107
+ );
108
+ if (matterServiceData === undefined || matterServiceData.data.length !== 8) {
109
+ logger.info(`Peripheral ${peripheral.address} does not advertise Matter Service ... ignoring`);
110
+ return;
111
+ }
112
+
113
+ this.discoveredPeripherals.set(peripheral.address, { peripheral, matterServiceData: matterServiceData.data });
114
+
115
+ this.deviceDiscoveredCallback?.(peripheral, matterServiceData.data);
116
+ }
117
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { NetInterface, TransportInterface } from "@matter/general";
8
+ import { Ble } from "@project-chip/matter.js/ble";
9
+ import { InstanceBroadcaster, Scanner } from "@project-chip/matter.js/common";
10
+ import { BleBroadcaster } from "./BleBroadcaster.js";
11
+ import { BlePeripheralInterface } from "./BlePeripheralInterface.js";
12
+ import { BleScanner } from "./BleScanner.js";
13
+ import { BlenoBleServer } from "./BlenoBleServer.js";
14
+ import { NobleBleCentralInterface } from "./NobleBleChannel.js";
15
+ import { NobleBleClient } from "./NobleBleClient.js";
16
+
17
+ export type BleOptions = {
18
+ hciId?: number;
19
+ };
20
+
21
+ export class NodeJsBle extends Ble {
22
+ private blePeripheral: BlenoBleServer | undefined;
23
+ private bleCentral: NobleBleClient | undefined;
24
+
25
+ constructor(private readonly options?: BleOptions) {
26
+ super();
27
+ }
28
+
29
+ getBlePeripheralInterface(): TransportInterface {
30
+ if (this.blePeripheral === undefined) {
31
+ this.blePeripheral = new BlenoBleServer(this.options);
32
+ }
33
+ return new BlePeripheralInterface(this.blePeripheral);
34
+ }
35
+
36
+ getBleCentralInterface(): NetInterface {
37
+ if (this.bleCentral === undefined) {
38
+ this.bleCentral = new NobleBleClient(this.options);
39
+ }
40
+ return new NobleBleCentralInterface();
41
+ }
42
+
43
+ getBleBroadcaster(additionalAdvertisementData?: Uint8Array): InstanceBroadcaster {
44
+ if (this.blePeripheral === undefined) {
45
+ this.blePeripheral = new BlenoBleServer(this.options);
46
+ }
47
+ return new BleBroadcaster(this.blePeripheral, additionalAdvertisementData);
48
+ }
49
+
50
+ getBleScanner(): Scanner {
51
+ if (this.bleCentral === undefined) {
52
+ this.bleCentral = new NobleBleClient(this.options);
53
+ }
54
+ return new BleScanner(this.bleCentral);
55
+ }
56
+ }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./BleBroadcaster.js";
8
+ export * from "./BleScanner.js";
9
+ export * from "./NodeJsBle.js";
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tools/tsc/tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "types": [
5
+ "node"
6
+ ]
7
+ },
8
+ "references": [
9
+ {
10
+ "path": "../../general/src"
11
+ },
12
+ {
13
+ "path": "../../matter.js/src"
14
+ }
15
+ ]
16
+ }