react-native-ble-mesh 2.0.0 → 2.1.2

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 (93) hide show
  1. package/README.md +2 -2
  2. package/docs/OPTIMIZATION.md +165 -52
  3. package/package.json +1 -1
  4. package/src/MeshNetwork.js +63 -53
  5. package/src/constants/audio.js +4 -4
  6. package/src/constants/ble.js +1 -1
  7. package/src/constants/crypto.js +1 -1
  8. package/src/constants/errors.js +2 -2
  9. package/src/constants/events.js +1 -1
  10. package/src/constants/protocol.js +2 -2
  11. package/src/crypto/AutoCrypto.js +16 -3
  12. package/src/crypto/CryptoProvider.js +17 -17
  13. package/src/crypto/providers/ExpoCryptoProvider.js +15 -9
  14. package/src/crypto/providers/QuickCryptoProvider.js +41 -12
  15. package/src/crypto/providers/TweetNaClProvider.js +10 -8
  16. package/src/errors/AudioError.js +2 -1
  17. package/src/errors/ConnectionError.js +2 -2
  18. package/src/errors/CryptoError.js +1 -1
  19. package/src/errors/HandshakeError.js +2 -2
  20. package/src/errors/MeshError.js +4 -4
  21. package/src/errors/MessageError.js +2 -2
  22. package/src/errors/ValidationError.js +3 -3
  23. package/src/expo/withBLEMesh.js +10 -10
  24. package/src/hooks/AppStateManager.js +11 -2
  25. package/src/hooks/useMesh.js +23 -10
  26. package/src/hooks/useMessages.js +17 -16
  27. package/src/hooks/usePeers.js +19 -14
  28. package/src/index.js +2 -2
  29. package/src/mesh/dedup/BloomFilter.js +45 -57
  30. package/src/mesh/dedup/DedupManager.js +36 -8
  31. package/src/mesh/dedup/MessageCache.js +3 -0
  32. package/src/mesh/fragment/Assembler.js +5 -4
  33. package/src/mesh/fragment/Fragmenter.js +3 -3
  34. package/src/mesh/monitor/ConnectionQuality.js +59 -25
  35. package/src/mesh/monitor/NetworkMonitor.js +80 -28
  36. package/src/mesh/peer/Peer.js +9 -11
  37. package/src/mesh/peer/PeerDiscovery.js +18 -19
  38. package/src/mesh/peer/PeerManager.js +29 -17
  39. package/src/mesh/router/MessageRouter.js +28 -20
  40. package/src/mesh/router/PathFinder.js +10 -13
  41. package/src/mesh/router/RouteTable.js +25 -14
  42. package/src/mesh/store/StoreAndForwardManager.js +32 -24
  43. package/src/protocol/deserializer.js +9 -10
  44. package/src/protocol/header.js +13 -7
  45. package/src/protocol/message.js +18 -14
  46. package/src/protocol/serializer.js +9 -12
  47. package/src/protocol/validator.js +29 -10
  48. package/src/service/BatteryOptimizer.js +22 -18
  49. package/src/service/EmergencyManager.js +18 -25
  50. package/src/service/HandshakeManager.js +112 -18
  51. package/src/service/MeshService.js +106 -22
  52. package/src/service/SessionManager.js +50 -13
  53. package/src/service/audio/AudioManager.js +80 -38
  54. package/src/service/audio/buffer/FrameBuffer.js +7 -8
  55. package/src/service/audio/buffer/JitterBuffer.js +1 -1
  56. package/src/service/audio/codec/LC3Codec.js +18 -19
  57. package/src/service/audio/codec/LC3Decoder.js +10 -10
  58. package/src/service/audio/codec/LC3Encoder.js +11 -9
  59. package/src/service/audio/session/AudioSession.js +14 -17
  60. package/src/service/audio/session/VoiceMessage.js +15 -22
  61. package/src/service/audio/transport/AudioFragmenter.js +17 -9
  62. package/src/service/audio/transport/AudioFramer.js +8 -12
  63. package/src/service/file/FileAssembler.js +4 -2
  64. package/src/service/file/FileChunker.js +1 -1
  65. package/src/service/file/FileManager.js +26 -20
  66. package/src/service/file/FileMessage.js +7 -12
  67. package/src/service/text/TextManager.js +75 -42
  68. package/src/service/text/broadcast/BroadcastManager.js +14 -17
  69. package/src/service/text/channel/Channel.js +10 -14
  70. package/src/service/text/channel/ChannelManager.js +10 -10
  71. package/src/service/text/message/TextMessage.js +12 -19
  72. package/src/service/text/message/TextSerializer.js +2 -2
  73. package/src/storage/AsyncStorageAdapter.js +17 -14
  74. package/src/storage/MemoryStorage.js +11 -8
  75. package/src/storage/MessageStore.js +77 -32
  76. package/src/storage/Storage.js +9 -9
  77. package/src/transport/BLETransport.js +27 -16
  78. package/src/transport/MockTransport.js +7 -2
  79. package/src/transport/MultiTransport.js +43 -11
  80. package/src/transport/Transport.js +9 -9
  81. package/src/transport/WiFiDirectTransport.js +26 -20
  82. package/src/transport/adapters/BLEAdapter.js +19 -19
  83. package/src/transport/adapters/NodeBLEAdapter.js +24 -23
  84. package/src/transport/adapters/RNBLEAdapter.js +14 -11
  85. package/src/utils/EventEmitter.js +15 -16
  86. package/src/utils/LRUCache.js +10 -4
  87. package/src/utils/RateLimiter.js +1 -1
  88. package/src/utils/bytes.js +12 -10
  89. package/src/utils/compression.js +10 -8
  90. package/src/utils/encoding.js +39 -8
  91. package/src/utils/retry.js +11 -13
  92. package/src/utils/time.js +9 -4
  93. package/src/utils/validation.js +1 -1
@@ -16,7 +16,7 @@ const Transport = require('./Transport');
16
16
 
17
17
  /**
18
18
  * Transport selection strategies
19
- * @constant {Object}
19
+ * @constant {any}
20
20
  */
21
21
  const STRATEGY = Object.freeze({
22
22
  /** Always use BLE */
@@ -45,6 +45,7 @@ class MultiTransport extends Transport {
45
45
  * @param {number} [options.wifiThresholdBytes=1024] - Use Wi-Fi Direct for payloads above this size
46
46
  */
47
47
  constructor(options = {}) {
48
+ // @ts-ignore
48
49
  super(options);
49
50
 
50
51
  this._bleTransport = options.bleTransport || null;
@@ -54,6 +55,9 @@ class MultiTransport extends Transport {
54
55
 
55
56
  /** @type {Map<string, string>} peerId → preferred transport name */
56
57
  this._peerTransportMap = new Map();
58
+
59
+ /** @type {Array<{transport: Transport, event: string, handler: Function}>} */
60
+ this._wiredHandlers = [];
57
61
  }
58
62
 
59
63
  /**
@@ -80,6 +84,7 @@ class MultiTransport extends Transport {
80
84
  if (this._bleTransport) {
81
85
  startPromises.push(
82
86
  this._bleTransport.start()
87
+ // @ts-ignore
83
88
  .then(() => this._wireTransport(this._bleTransport, 'ble'))
84
89
  .catch(err => {
85
90
  this.emit('transportError', { transport: 'ble', error: err });
@@ -90,6 +95,7 @@ class MultiTransport extends Transport {
90
95
  if (this._wifiTransport) {
91
96
  startPromises.push(
92
97
  this._wifiTransport.start()
98
+ // @ts-ignore
93
99
  .then(() => this._wireTransport(this._wifiTransport, 'wifi-direct'))
94
100
  .catch(err => {
95
101
  this.emit('transportError', { transport: 'wifi-direct', error: err });
@@ -122,6 +128,13 @@ class MultiTransport extends Transport {
122
128
  if (this._wifiTransport) { stopPromises.push(this._wifiTransport.stop().catch(() => {})); }
123
129
 
124
130
  await Promise.allSettled(stopPromises);
131
+
132
+ // Remove wired event handlers to prevent listener leaks
133
+ for (const { transport, event, handler } of this._wiredHandlers) {
134
+ transport.off(event, handler);
135
+ }
136
+ this._wiredHandlers = [];
137
+
125
138
  this._peers.clear();
126
139
  this._peerTransportMap.clear();
127
140
  this._setState(Transport.STATE.STOPPED);
@@ -160,6 +173,7 @@ class MultiTransport extends Transport {
160
173
  if (!this.isRunning) { throw new Error('Transport is not running'); }
161
174
 
162
175
  const allPeerIds = new Set();
176
+ /** @type {string[]} */
163
177
  const successPeerIds = [];
164
178
 
165
179
  // Collect all peers from all transports
@@ -181,6 +195,7 @@ class MultiTransport extends Transport {
181
195
  if (r.status === 'fulfilled') { successPeerIds.push(r.value); }
182
196
  });
183
197
 
198
+ // @ts-ignore
184
199
  return successPeerIds;
185
200
  }
186
201
 
@@ -218,9 +233,11 @@ class MultiTransport extends Transport {
218
233
  _selectTransport(peerId, dataSize) {
219
234
  switch (this._strategy) {
220
235
  case STRATEGY.BLE_ONLY:
236
+ // @ts-ignore
221
237
  return this._bleTransport;
222
238
 
223
239
  case STRATEGY.WIFI_ONLY:
240
+ // @ts-ignore
224
241
  return this._wifiTransport;
225
242
 
226
243
  case STRATEGY.AUTO:
@@ -272,13 +289,13 @@ class MultiTransport extends Transport {
272
289
  * @private
273
290
  */
274
291
  _wireTransport(transport, name) {
275
- transport.on('peerConnected', (info) => {
292
+ const onPeerConnected = (/** @type {any} */ info) => {
276
293
  this._peerTransportMap.set(info.peerId, name);
277
294
  this._peers.set(info.peerId, { ...info, transport: name });
278
295
  this.emit('peerConnected', { ...info, transport: name });
279
- });
296
+ };
280
297
 
281
- transport.on('peerDisconnected', (info) => {
298
+ const onPeerDisconnected = (/** @type {any} */ info) => {
282
299
  // Only remove if no other transport has this peer
283
300
  const otherTransport = this._getFallbackTransport(transport);
284
301
  if (!otherTransport || !otherTransport.isConnected(info.peerId)) {
@@ -286,19 +303,34 @@ class MultiTransport extends Transport {
286
303
  this._peerTransportMap.delete(info.peerId);
287
304
  this.emit('peerDisconnected', { ...info, transport: name });
288
305
  }
289
- });
306
+ };
290
307
 
291
- transport.on('message', (msg) => {
308
+ const onMessage = (/** @type {any} */ msg) => {
292
309
  this.emit('message', { ...msg, transport: name });
293
- });
310
+ };
294
311
 
295
- transport.on('deviceDiscovered', (info) => {
312
+ const onDeviceDiscovered = (/** @type {any} */ info) => {
296
313
  this.emit('deviceDiscovered', { ...info, transport: name });
297
- });
314
+ };
298
315
 
299
- transport.on('error', (err) => {
316
+ const onError = (/** @type {any} */ err) => {
300
317
  this.emit('transportError', { transport: name, error: err });
301
- });
318
+ };
319
+
320
+ transport.on('peerConnected', onPeerConnected);
321
+ transport.on('peerDisconnected', onPeerDisconnected);
322
+ transport.on('message', onMessage);
323
+ transport.on('deviceDiscovered', onDeviceDiscovered);
324
+ transport.on('error', onError);
325
+
326
+ // Store references for cleanup
327
+ this._wiredHandlers.push(
328
+ { transport, event: 'peerConnected', handler: onPeerConnected },
329
+ { transport, event: 'peerDisconnected', handler: onPeerDisconnected },
330
+ { transport, event: 'message', handler: onMessage },
331
+ { transport, event: 'deviceDiscovered', handler: onDeviceDiscovered },
332
+ { transport, event: 'error', handler: onError }
333
+ );
302
334
  }
303
335
  }
304
336
 
@@ -9,7 +9,7 @@ const EventEmitter = require('../utils/EventEmitter');
9
9
 
10
10
  /**
11
11
  * Transport states
12
- * @constant {Object.<string, string>}
12
+ * @type {Record<string, string>}
13
13
  */
14
14
  const TRANSPORT_STATE = Object.freeze({
15
15
  STOPPED: 'stopped',
@@ -44,7 +44,7 @@ class Transport extends EventEmitter {
44
44
 
45
45
  /**
46
46
  * Transport options
47
- * @type {Object}
47
+ * @type {any}
48
48
  * @protected
49
49
  */
50
50
  this._options = {
@@ -61,7 +61,7 @@ class Transport extends EventEmitter {
61
61
 
62
62
  /**
63
63
  * Connected peers map (peerId -> connection info)
64
- * @type {Map<string, Object>}
64
+ * @type {Map<string, any>}
65
65
  * @protected
66
66
  */
67
67
  this._peers = new Map();
@@ -119,23 +119,23 @@ class Transport extends EventEmitter {
119
119
  /**
120
120
  * Sends data to a specific peer
121
121
  * @abstract
122
- * @param {string} peerId - Target peer ID
123
- * @param {Uint8Array} data - Data to send
122
+ * @param {string} _peerId - Target peer ID
123
+ * @param {Uint8Array} _data - Data to send
124
124
  * @returns {Promise<void>}
125
125
  * @throws {Error} If not implemented by subclass
126
126
  */
127
- async send(_peerId, _data) {
127
+ async send(/** @type {any} */ _peerId, _data) {
128
128
  throw new Error('Transport.send() must be implemented by subclass');
129
129
  }
130
130
 
131
131
  /**
132
132
  * Broadcasts data to all connected peers
133
133
  * @abstract
134
- * @param {Uint8Array} data - Data to broadcast
134
+ * @param {Uint8Array} _data - Data to broadcast
135
135
  * @returns {Promise<string[]>} Array of peer IDs that received the broadcast
136
136
  * @throws {Error} If not implemented by subclass
137
137
  */
138
- async broadcast(_data) {
138
+ async broadcast(/** @type {any} */ _data) {
139
139
  throw new Error('Transport.broadcast() must be implemented by subclass');
140
140
  }
141
141
 
@@ -159,7 +159,7 @@ class Transport extends EventEmitter {
159
159
  /**
160
160
  * Gets connection info for a peer
161
161
  * @param {string} peerId - Peer ID
162
- * @returns {Object|undefined} Connection info or undefined
162
+ * @returns {any} Connection info or undefined
163
163
  */
164
164
  getPeerInfo(peerId) {
165
165
  return this._peers.get(peerId);
@@ -13,7 +13,7 @@ const { ConnectionError } = require('../errors');
13
13
 
14
14
  /**
15
15
  * Wi-Fi Direct transport states
16
- * @constant {Object}
16
+ * @constant {any}
17
17
  */
18
18
  const WIFI_DIRECT_STATE = Object.freeze({
19
19
  AVAILABLE: 'available',
@@ -46,6 +46,7 @@ class WiFiDirectTransport extends Transport {
46
46
  this._isDiscovering = false;
47
47
  this._isGroupOwner = false;
48
48
  this._groupInfo = null;
49
+ /** @type {any} */
49
50
  this._subscriptions = [];
50
51
  }
51
52
 
@@ -75,16 +76,16 @@ class WiFiDirectTransport extends Transport {
75
76
 
76
77
  try {
77
78
  const p2p = this._getWifiP2p();
78
- await p2p.initialize();
79
+ await p2p?.initialize();
79
80
 
80
81
  // Check if Wi-Fi Direct is supported
81
- const isAvailable = await p2p.isSuccessfulInitialize();
82
+ const isAvailable = await p2p?.isSuccessfulInitialize();
82
83
  if (!isAvailable) {
83
84
  throw new ConnectionError('Wi-Fi Direct is not available on this device', 'E100');
84
85
  }
85
86
 
86
87
  this._setState(Transport.STATE.RUNNING);
87
- } catch (error) {
88
+ } catch (/** @type {any} */ error) {
88
89
  this._setState(Transport.STATE.ERROR);
89
90
  throw error;
90
91
  }
@@ -107,13 +108,13 @@ class WiFiDirectTransport extends Transport {
107
108
 
108
109
  // Disconnect from group
109
110
  try {
110
- await p2p.removeGroup();
111
+ await p2p?.removeGroup();
111
112
  } catch (e) {
112
113
  // Ignore — may not be in a group
113
114
  }
114
115
 
115
116
  // Cleanup subscriptions
116
- this._subscriptions.forEach(sub => {
117
+ this._subscriptions.forEach((/** @type {any} */ sub) => {
117
118
  if (sub && typeof sub.remove === 'function') { sub.remove(); }
118
119
  });
119
120
  this._subscriptions = [];
@@ -132,7 +133,7 @@ class WiFiDirectTransport extends Transport {
132
133
  if (!this.isRunning || this._isDiscovering) { return; }
133
134
 
134
135
  const p2p = this._getWifiP2p();
135
- await p2p.discoverPeers();
136
+ await p2p?.discoverPeers();
136
137
  this._isDiscovering = true;
137
138
  this.emit('discoveryStarted');
138
139
  }
@@ -146,7 +147,7 @@ class WiFiDirectTransport extends Transport {
146
147
 
147
148
  const p2p = this._getWifiP2p();
148
149
  try {
149
- await p2p.stopDiscoveringPeers();
150
+ await p2p?.stopDiscoveringPeers();
150
151
  } catch (e) {
151
152
  // Ignore
152
153
  }
@@ -173,9 +174,9 @@ class WiFiDirectTransport extends Transport {
173
174
  const p2p = this._getWifiP2p();
174
175
 
175
176
  try {
176
- await p2p.connect(peerId);
177
+ await p2p?.connect(peerId);
177
178
 
178
- const connectionInfo = await p2p.getConnectionInfo();
179
+ const connectionInfo = await p2p?.getConnectionInfo();
179
180
  this._isGroupOwner = connectionInfo.isGroupOwner || false;
180
181
  this._groupInfo = connectionInfo;
181
182
 
@@ -187,7 +188,7 @@ class WiFiDirectTransport extends Transport {
187
188
  });
188
189
 
189
190
  this.emit('peerConnected', { peerId, transport: 'wifi-direct' });
190
- } catch (error) {
191
+ } catch (/** @type {any} */ error) {
191
192
  throw ConnectionError.connectionFailed(peerId, { cause: error.message });
192
193
  }
193
194
  }
@@ -202,7 +203,7 @@ class WiFiDirectTransport extends Transport {
202
203
 
203
204
  const p2p = this._getWifiP2p();
204
205
  try {
205
- await p2p.removeGroup();
206
+ await p2p?.removeGroup();
206
207
  } catch (e) {
207
208
  // Ignore
208
209
  }
@@ -231,10 +232,10 @@ class WiFiDirectTransport extends Transport {
231
232
 
232
233
  if (this._isGroupOwner) {
233
234
  // Group owner sends via server socket
234
- await p2p.sendMessage(base64);
235
+ await p2p?.sendMessage(base64);
235
236
  } else {
236
237
  // Client sends to group owner address
237
- await p2p.sendMessageTo(peerInfo.groupOwnerAddress, this._port, base64);
238
+ await p2p?.sendMessageTo(peerInfo.groupOwnerAddress, this._port, base64);
238
239
  }
239
240
  }
240
241
 
@@ -261,16 +262,20 @@ class WiFiDirectTransport extends Transport {
261
262
  if (!this.isRunning) { return []; }
262
263
  const p2p = this._getWifiP2p();
263
264
  try {
264
- return await p2p.getAvailablePeers();
265
+ return await p2p?.getAvailablePeers();
265
266
  } catch (e) {
266
267
  return [];
267
268
  }
268
269
  }
269
270
 
270
- /** @private */
271
+ /**
272
+ * @private
273
+ * @returns {any}
274
+ */
271
275
  _getWifiP2p() {
272
276
  if (!this._wifiP2p) {
273
277
  try {
278
+ // @ts-ignore
274
279
  this._wifiP2p = require('react-native-wifi-p2p');
275
280
  } catch (e) {
276
281
  throw new Error(
@@ -283,11 +288,12 @@ class WiFiDirectTransport extends Transport {
283
288
  }
284
289
 
285
290
  /** @private */
286
- _uint8ArrayToBase64(bytes) {
287
- let binary = '';
288
- for (let i = 0; i < bytes.length; i++) {
289
- binary += String.fromCharCode(bytes[i]);
291
+ _uint8ArrayToBase64(/** @type {any} */ bytes) {
292
+ const chunks = [];
293
+ for (let i = 0; i < bytes.length; i += 8192) {
294
+ chunks.push(String.fromCharCode.apply(null, bytes.subarray(i, Math.min(i + 8192, bytes.length))));
290
295
  }
296
+ const binary = chunks.join('');
291
297
  return typeof btoa !== 'undefined' ? btoa(binary) : Buffer.from(bytes).toString('base64');
292
298
  }
293
299
  }
@@ -17,12 +17,12 @@ const { BLUETOOTH_STATE } = require('../../constants');
17
17
  class BLEAdapter {
18
18
  /**
19
19
  * Creates a new BLEAdapter instance
20
- * @param {Object} [options={}] - Adapter options
20
+ * @param {any} [options={}] - Adapter options
21
21
  */
22
22
  constructor(options = {}) {
23
23
  /**
24
24
  * Adapter options
25
- * @type {Object}
25
+ * @type {any}
26
26
  * @protected
27
27
  */
28
28
  this._options = options;
@@ -73,12 +73,12 @@ class BLEAdapter {
73
73
  /**
74
74
  * Starts scanning for BLE devices
75
75
  * @abstract
76
- * @param {string[]} serviceUUIDs - Service UUIDs to filter by
77
- * @param {Function} callback - Callback for discovered devices
76
+ * @param {string[]} _serviceUUIDs - Service UUIDs to filter by
77
+ * @param {Function} _callback - Callback for discovered devices
78
78
  * @returns {Promise<void>}
79
79
  * @throws {Error} If not implemented by subclass
80
80
  */
81
- async startScan(_serviceUUIDs, _callback) {
81
+ async startScan(/** @type {any} */ _serviceUUIDs, _callback) {
82
82
  throw new Error('BLEAdapter.startScan() must be implemented by subclass');
83
83
  }
84
84
 
@@ -94,50 +94,50 @@ class BLEAdapter {
94
94
  /**
95
95
  * Connects to a BLE device
96
96
  * @abstract
97
- * @param {string} deviceId - Device ID to connect to
97
+ * @param {string} _deviceId - Device ID to connect to
98
98
  * @returns {Promise<Object>} Connected device info
99
99
  * @throws {Error} If not implemented by subclass
100
100
  */
101
- async connect(_deviceId) {
101
+ async connect(/** @type {any} */ _deviceId) {
102
102
  throw new Error('BLEAdapter.connect() must be implemented by subclass');
103
103
  }
104
104
 
105
105
  /**
106
106
  * Disconnects from a BLE device
107
107
  * @abstract
108
- * @param {string} deviceId - Device ID to disconnect from
108
+ * @param {string} _deviceId - Device ID to disconnect from
109
109
  * @returns {Promise<void>}
110
110
  * @throws {Error} If not implemented by subclass
111
111
  */
112
- async disconnect(_deviceId) {
112
+ async disconnect(/** @type {any} */ _deviceId) {
113
113
  throw new Error('BLEAdapter.disconnect() must be implemented by subclass');
114
114
  }
115
115
 
116
116
  /**
117
117
  * Writes data to a characteristic
118
118
  * @abstract
119
- * @param {string} deviceId - Target device ID
120
- * @param {string} serviceUUID - Service UUID
121
- * @param {string} charUUID - Characteristic UUID
122
- * @param {Uint8Array} data - Data to write
119
+ * @param {string} _deviceId - Target device ID
120
+ * @param {string} _serviceUUID - Service UUID
121
+ * @param {string} _charUUID - Characteristic UUID
122
+ * @param {Uint8Array} _data - Data to write
123
123
  * @returns {Promise<void>}
124
124
  * @throws {Error} If not implemented by subclass
125
125
  */
126
- async write(_deviceId, _serviceUUID, _charUUID, _data) {
126
+ async write(/** @type {any} */ _deviceId, _serviceUUID, _charUUID, _data) {
127
127
  throw new Error('BLEAdapter.write() must be implemented by subclass');
128
128
  }
129
129
 
130
130
  /**
131
131
  * Subscribes to characteristic notifications
132
132
  * @abstract
133
- * @param {string} deviceId - Target device ID
134
- * @param {string} serviceUUID - Service UUID
135
- * @param {string} charUUID - Characteristic UUID
136
- * @param {Function} callback - Notification callback
133
+ * @param {string} _deviceId - Target device ID
134
+ * @param {string} _serviceUUID - Service UUID
135
+ * @param {string} _charUUID - Characteristic UUID
136
+ * @param {Function} _callback - Notification callback
137
137
  * @returns {Promise<void>}
138
138
  * @throws {Error} If not implemented by subclass
139
139
  */
140
- async subscribe(_deviceId, _serviceUUID, _charUUID, _callback) {
140
+ async subscribe(/** @type {any} */ _deviceId, _serviceUUID, _charUUID, _callback) {
141
141
  throw new Error('BLEAdapter.subscribe() must be implemented by subclass');
142
142
  }
143
143
 
@@ -21,35 +21,35 @@ class NodeBLEAdapter extends BLEAdapter {
21
21
  /**
22
22
  * Creates a new NodeBLEAdapter instance
23
23
  * @param {Object} [options={}] - Adapter options
24
- * @param {Object} [options.noble] - Noble instance
24
+ * @param {any} [options.noble] - Noble instance
25
25
  */
26
26
  constructor(options = {}) {
27
27
  super(options);
28
28
 
29
29
  /**
30
30
  * Noble instance
31
- * @type {Object|null}
31
+ * @type {any}
32
32
  * @private
33
33
  */
34
34
  this._noble = options.noble || null;
35
35
 
36
36
  /**
37
37
  * Connected peripherals map
38
- * @type {Map<string, Object>}
38
+ * @type {Map<string, any>}
39
39
  * @private
40
40
  */
41
41
  this._peripherals = new Map();
42
42
 
43
43
  /**
44
44
  * Discovered peripherals cache
45
- * @type {Map<string, Object>}
45
+ * @type {Map<string, any>}
46
46
  * @private
47
47
  */
48
48
  this._discoveredPeripherals = new Map();
49
49
 
50
50
  /**
51
51
  * Subscription handlers map
52
- * @type {Map<string, Object>}
52
+ * @type {Map<string, any>}
53
53
  * @private
54
54
  */
55
55
  this._subscriptions = new Map();
@@ -82,6 +82,7 @@ class NodeBLEAdapter extends BLEAdapter {
82
82
  // Try to load noble if not provided
83
83
  if (!this._noble) {
84
84
  try {
85
+ // @ts-ignore
85
86
  this._noble = require('@abandonware/noble');
86
87
  } catch (error) {
87
88
  throw new Error(
@@ -91,7 +92,7 @@ class NodeBLEAdapter extends BLEAdapter {
91
92
  }
92
93
 
93
94
  // Set up state change listener
94
- this._noble.on('stateChange', (state) => {
95
+ this._noble.on('stateChange', (/** @type {any} */ state) => {
95
96
  this._notifyStateChange(this._mapState(state));
96
97
  });
97
98
 
@@ -141,7 +142,7 @@ class NodeBLEAdapter extends BLEAdapter {
141
142
  uuid.toLowerCase().replace(/-/g, '')
142
143
  );
143
144
 
144
- this._noble.on('discover', (peripheral) => {
145
+ this._noble.on('discover', (/** @type {any} */ peripheral) => {
145
146
  this._discoveredPeripherals.set(peripheral.id, peripheral);
146
147
 
147
148
  if (this._scanCallback) {
@@ -284,7 +285,7 @@ class NodeBLEAdapter extends BLEAdapter {
284
285
  throw new Error(`Characteristic ${charUUID} not found`);
285
286
  }
286
287
 
287
- characteristic.on('data', (data) => {
288
+ characteristic.on('data', (/** @type {any} */ data) => {
288
289
  callback(new Uint8Array(data));
289
290
  });
290
291
 
@@ -318,7 +319,7 @@ class NodeBLEAdapter extends BLEAdapter {
318
319
  poweredOff: BLEAdapter.STATE.POWERED_OFF,
319
320
  poweredOn: BLEAdapter.STATE.POWERED_ON
320
321
  };
321
- return stateMap[state] || BLEAdapter.STATE.UNKNOWN;
322
+ return /** @type {any} */ (stateMap)[state] || BLEAdapter.STATE.UNKNOWN;
322
323
  }
323
324
 
324
325
  /**
@@ -336,7 +337,7 @@ class NodeBLEAdapter extends BLEAdapter {
336
337
  reject(new Error('Bluetooth initialization timeout'));
337
338
  }, 10000);
338
339
 
339
- this._noble.once('stateChange', (state) => {
340
+ this._noble.once('stateChange', (/** @type {any} */ state) => {
340
341
  clearTimeout(timeout);
341
342
  if (state === 'poweredOn') {
342
343
  resolve();
@@ -349,13 +350,13 @@ class NodeBLEAdapter extends BLEAdapter {
349
350
 
350
351
  /**
351
352
  * Connects to a peripheral
352
- * @param {Object} peripheral - Noble peripheral
353
+ * @param {any} peripheral - Noble peripheral
353
354
  * @returns {Promise<void>}
354
355
  * @private
355
356
  */
356
357
  _connectPeripheral(peripheral) {
357
358
  return new Promise((resolve, reject) => {
358
- peripheral.connect((error) => {
359
+ peripheral.connect((/** @type {any} */ error) => {
359
360
  if (error) { reject(error); } else { resolve(); }
360
361
  });
361
362
  });
@@ -363,7 +364,7 @@ class NodeBLEAdapter extends BLEAdapter {
363
364
 
364
365
  /**
365
366
  * Disconnects from a peripheral
366
- * @param {Object} peripheral - Noble peripheral
367
+ * @param {any} peripheral - Noble peripheral
367
368
  * @returns {Promise<void>}
368
369
  * @private
369
370
  */
@@ -375,13 +376,13 @@ class NodeBLEAdapter extends BLEAdapter {
375
376
 
376
377
  /**
377
378
  * Discovers services and characteristics
378
- * @param {Object} peripheral - Noble peripheral
379
+ * @param {any} peripheral - Noble peripheral
379
380
  * @returns {Promise<void>}
380
381
  * @private
381
382
  */
382
383
  _discoverServices(peripheral) {
383
384
  return new Promise((resolve, reject) => {
384
- peripheral.discoverAllServicesAndCharacteristics((error) => {
385
+ peripheral.discoverAllServicesAndCharacteristics((/** @type {any} */ error) => {
385
386
  if (error) { reject(error); } else { resolve(); }
386
387
  });
387
388
  });
@@ -389,10 +390,10 @@ class NodeBLEAdapter extends BLEAdapter {
389
390
 
390
391
  /**
391
392
  * Finds a characteristic on a peripheral
392
- * @param {Object} peripheral - Noble peripheral
393
+ * @param {any} peripheral - Noble peripheral
393
394
  * @param {string} serviceUUID - Service UUID
394
395
  * @param {string} charUUID - Characteristic UUID
395
- * @returns {Object|null} Characteristic or null
396
+ * @returns {any} Characteristic or null
396
397
  * @private
397
398
  */
398
399
  _findCharacteristic(peripheral, serviceUUID, charUUID) {
@@ -400,23 +401,23 @@ class NodeBLEAdapter extends BLEAdapter {
400
401
  const formattedCharUUID = charUUID.toLowerCase().replace(/-/g, '');
401
402
 
402
403
  const service = peripheral.services?.find(
403
- s => s.uuid === formattedServiceUUID
404
+ (/** @type {any} */ s) => s.uuid === formattedServiceUUID
404
405
  );
405
406
  return service?.characteristics?.find(
406
- c => c.uuid === formattedCharUUID
407
+ (/** @type {any} */ c) => c.uuid === formattedCharUUID
407
408
  ) || null;
408
409
  }
409
410
 
410
411
  /**
411
412
  * Writes to a characteristic
412
- * @param {Object} characteristic - Noble characteristic
413
+ * @param {any} characteristic - Noble characteristic
413
414
  * @param {Buffer} data - Data to write
414
415
  * @returns {Promise<void>}
415
416
  * @private
416
417
  */
417
418
  _writeCharacteristic(characteristic, data) {
418
419
  return new Promise((resolve, reject) => {
419
- characteristic.write(data, false, (error) => {
420
+ characteristic.write(data, false, (/** @type {any} */ error) => {
420
421
  if (error) { reject(error); } else { resolve(); }
421
422
  });
422
423
  });
@@ -424,13 +425,13 @@ class NodeBLEAdapter extends BLEAdapter {
424
425
 
425
426
  /**
426
427
  * Subscribes to a characteristic
427
- * @param {Object} characteristic - Noble characteristic
428
+ * @param {any} characteristic - Noble characteristic
428
429
  * @returns {Promise<void>}
429
430
  * @private
430
431
  */
431
432
  _subscribeCharacteristic(characteristic) {
432
433
  return new Promise((resolve, reject) => {
433
- characteristic.subscribe((error) => {
434
+ characteristic.subscribe((/** @type {any} */ error) => {
434
435
  if (error) { reject(error); } else { resolve(); }
435
436
  });
436
437
  });