@stoprocent/noble 1.10.2 → 1.10.4

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 (67) hide show
  1. package/.github/workflows/build.yml +77 -0
  2. package/.github/workflows/nodepackage.yml +6 -15
  3. package/README.md +13 -23
  4. package/binding.gyp +22 -0
  5. package/examples/advertisement-discovery.js +1 -1
  6. package/examples/cache-gatt-discovery.js +1 -1
  7. package/examples/cache-gatt-reconnect.js +1 -1
  8. package/examples/echo.js +1 -1
  9. package/examples/enter-exit.js +2 -2
  10. package/examples/ext-advertisement-discovery.js +65 -0
  11. package/examples/peripheral-explorer-async.js +1 -1
  12. package/examples/peripheral-explorer.js +1 -1
  13. package/examples/pizza/central.js +1 -1
  14. package/index.d.ts +187 -194
  15. package/index.js +1 -6
  16. package/lib/distributed/bindings.js +33 -33
  17. package/lib/hci-socket/bindings.js +1 -3
  18. package/lib/hci-socket/crypto.js +4 -4
  19. package/lib/hci-socket/gap.js +22 -18
  20. package/lib/hci-socket/gatt.js +4 -4
  21. package/lib/hci-socket/hci.js +3 -3
  22. package/lib/mac/binding.gyp +23 -11
  23. package/lib/noble.js +10 -6
  24. package/lib/peripheral.js +0 -7
  25. package/lib/resolve-bindings.js +39 -3
  26. package/lib/webbluetooth/bindings.js +2 -2
  27. package/lib/websocket/bindings.js +32 -32
  28. package/lib/win/binding.gyp +25 -5
  29. package/lib/win/src/ble_manager.cc +4 -1
  30. package/lib/win/src/ble_manager.h +1 -1
  31. package/lib/win/src/peripheral_winrt.cc +2 -0
  32. package/lib/win/src/radio_watcher.cc +1 -0
  33. package/lib/win/src/winrt_cpp.cc +1 -0
  34. package/package.json +31 -37
  35. package/prebuilds/android-arm/node.napi.armv7.node +0 -0
  36. package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
  37. package/prebuilds/darwin-x64/node.napi.node +0 -0
  38. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  39. package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
  40. package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
  41. package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
  42. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  43. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  44. package/prebuilds/win32-ia32/node.napi.node +0 -0
  45. package/prebuilds/win32-x64/node.napi.node +0 -0
  46. package/test/lib/distributed/bindings.test.js +15 -15
  47. package/test/lib/hci-socket/gap.test.js +39 -0
  48. package/test/lib/hci-socket/hci.test.js +1 -1
  49. package/test/lib/resolve-bindings.test.js +102 -0
  50. package/test/lib/webbluetooth/bindings.test.js +2 -2
  51. package/test/lib/websocket/bindings.test.js +2 -2
  52. package/test/mocha.setup.js +0 -0
  53. package/test/noble.test.js +12 -11
  54. package/test.custom.js +131 -0
  55. package/test.js +1 -1
  56. package/with-custom-binding.js +6 -0
  57. package/ws-slave.js +10 -10
  58. package/.github/workflows/npm-publish.yml +0 -26
  59. package/lib/hci-uart/bindings.js +0 -569
  60. package/lib/hci-uart/hci-serial-parser.js +0 -70
  61. package/lib/hci-uart/hci.js +0 -1360
  62. package/lib/manufacture.js +0 -48
  63. package/misc/0001-hci-uart-on-usb-cdc.patch +0 -161
  64. package/misc/nrf52840-usb-cdc.hex +0 -8907
  65. package/misc/nrf52840dk.hex +0 -6921
  66. package/misc/prj.conf +0 -43
  67. package/test/lib/manufacture.test.js +0 -77
@@ -1,1360 +0,0 @@
1
- const debug = require('debug')('hci');
2
-
3
- const events = require('events');
4
- const util = require('util');
5
-
6
- const async = require('async');
7
- const { SerialPort } = require('serialport');
8
- const HciSerialParser = require('./hci-serial-parser');
9
-
10
- const HCI_COMMAND_PKT = 0x01;
11
- const HCI_ACLDATA_PKT = 0x02;
12
- const HCI_EVENT_PKT = 0x04;
13
-
14
- const ACL_START_NO_FLUSH = 0x00;
15
- const ACL_CONT = 0x01;
16
- const ACL_START = 0x02;
17
-
18
- const EVT_DISCONN_COMPLETE = 0x05;
19
- const EVT_ENCRYPT_CHANGE = 0x08;
20
- const EVT_CMD_COMPLETE = 0x0e;
21
- const EVT_CMD_STATUS = 0x0f;
22
- const EVT_NUMBER_OF_COMPLETED_PACKETS = 0x13;
23
- const EVT_LE_META_EVENT = 0x3e;
24
-
25
- const EVT_LE_CONN_COMPLETE = 0x01;
26
- const EVT_LE_ADVERTISING_REPORT = 0x02;
27
- const EVT_LE_ENHANCED_CONN_COMPLETE = 0x0a;
28
- const EVT_LE_EXTENDED_ADVERTISING_REPORT = 0x0d;
29
- const EVT_LE_CONN_UPDATE_COMPLETE = 0x03;
30
-
31
- const OGF_LINK_CTL = 0x01;
32
- const OCF_DISCONNECT = 0x0006;
33
-
34
- const OGF_HOST_CTL = 0x03;
35
- const OCF_SET_EVENT_MASK = 0x0001;
36
- const OCF_RESET = 0x0003;
37
- const OCF_SET_EVENT_FILTER = 0x0005;
38
- const OCF_SET_RANDOM_MAC = 0x0005;
39
- const OCF_SET_PHY = 0x0031;
40
- const OCF_READ_LE_HOST_SUPPORTED = 0x006c;
41
- const OCF_WRITE_LE_HOST_SUPPORTED = 0x006d;
42
-
43
- const OGF_INFO_PARAM = 0x04;
44
- const OCF_READ_LOCAL_VERSION = 0x0001;
45
- const OCF_READ_SUPPORTED_COMMANDS = 0x0002;
46
- const OCF_READ_BUFFER_SIZE = 0x0005;
47
- const OCF_READ_BD_ADDR = 0x0009;
48
-
49
- const OGF_STATUS_PARAM = 0x05;
50
- const OCF_READ_RSSI = 0x0005;
51
-
52
- const OGF_LE_CTL = 0x08;
53
- const OCF_LE_SET_EVENT_MASK = 0x0001;
54
- const OCF_LE_READ_BUFFER_SIZE = 0x0002;
55
- const OCF_LE_SET_EXTENDED_SCAN_PARAMETERS = 0x0041;
56
- const OCF_LE_SET_EXTENDED_SCAN_ENABLE = 0x0042;
57
- const OCF_LE_SET_SCAN_PARAMETERS = 0x000b;
58
- const OCF_LE_SET_SCAN_ENABLE = 0x000c;
59
- const OCF_LE_CREATE_CONN = 0x000d;
60
- const OCF_LE_CREATE_EXTENDED_CONN = 0x0043;
61
- const OCF_LE_CANCEL_CONN = 0x000e;
62
- const OCF_LE_CONN_UPDATE = 0x0013;
63
- const OCF_LE_START_ENCRYPTION = 0x0019;
64
- const DISCONNECT_CMD = OCF_DISCONNECT | (OGF_LINK_CTL << 10);
65
-
66
- const SET_EVENT_MASK_CMD = OCF_SET_EVENT_MASK | (OGF_HOST_CTL << 10);
67
- const RESET_CMD = OCF_RESET | (OGF_HOST_CTL << 10);
68
- const SET_EVENT_FILTER_CMD = OCF_SET_EVENT_FILTER | OGF_HOST_CTL << 10;
69
- const READ_LE_HOST_SUPPORTED_CMD =
70
- OCF_READ_LE_HOST_SUPPORTED | (OGF_HOST_CTL << 10);
71
- const WRITE_LE_HOST_SUPPORTED_CMD =
72
- OCF_WRITE_LE_HOST_SUPPORTED | (OGF_HOST_CTL << 10);
73
-
74
- const READ_LOCAL_VERSION_CMD = OCF_READ_LOCAL_VERSION | (OGF_INFO_PARAM << 10);
75
- const READ_SUPPORTED_COMMANDS_CMD =
76
- OCF_READ_SUPPORTED_COMMANDS | (OGF_INFO_PARAM << 10);
77
- const READ_BUFFER_SIZE_CMD = OCF_READ_BUFFER_SIZE | (OGF_INFO_PARAM << 10);
78
- const READ_BD_ADDR_CMD = OCF_READ_BD_ADDR | (OGF_INFO_PARAM << 10);
79
-
80
- const READ_RSSI_CMD = OCF_READ_RSSI | (OGF_STATUS_PARAM << 10);
81
-
82
- const LE_SET_EVENT_MASK_CMD = OCF_LE_SET_EVENT_MASK | (OGF_LE_CTL << 10);
83
- const LE_READ_BUFFER_SIZE_CMD = OCF_LE_READ_BUFFER_SIZE | (OGF_LE_CTL << 10);
84
- const LE_SET_EXTENDED_SCAN_PARAMETERS_CMD =
85
- OCF_LE_SET_EXTENDED_SCAN_PARAMETERS | (OGF_LE_CTL << 10);
86
- const LE_SET_EXTENDED_SCAN_ENABLE_CMD =
87
- OCF_LE_SET_EXTENDED_SCAN_ENABLE | (OGF_LE_CTL << 10);
88
- const LE_SET_SCAN_PARAMETERS_CMD =
89
- OCF_LE_SET_SCAN_PARAMETERS | (OGF_LE_CTL << 10);
90
- const LE_SET_SCAN_ENABLE_CMD = OCF_LE_SET_SCAN_ENABLE | (OGF_LE_CTL << 10);
91
- const LE_CREATE_CONN_CMD = OCF_LE_CREATE_CONN | (OGF_LE_CTL << 10);
92
- const LE_CREATE_EXTENDED_CONN_CMD =
93
- OCF_LE_CREATE_EXTENDED_CONN | (OGF_LE_CTL << 10);
94
- const LE_CONN_UPDATE_CMD = OCF_LE_CONN_UPDATE | (OGF_LE_CTL << 10);
95
- const LE_CANCEL_CONN_CMD = OCF_LE_CANCEL_CONN | (OGF_LE_CTL << 10);
96
- const LE_START_ENCRYPTION_CMD = OCF_LE_START_ENCRYPTION | (OGF_LE_CTL << 10);
97
- const HCI_OE_USER_ENDED_CONNECTION = 0x13;
98
-
99
- const STATUS_MAPPER = require('../hci-socket/hci-status');
100
-
101
- const Hci = function (options) {
102
- options = options || {};
103
-
104
- this._socket = null;
105
- this._socketParser = null;
106
- this._socketQueue = null;
107
- this._deviceUpTimer = null;
108
- this._state = null;
109
-
110
- this._handleBuffers = {};
111
-
112
- this._aclBuffers = undefined;
113
- this._resolveAclBuffers = undefined;
114
- const aclBuffersPromise = new Promise((resolve) => {
115
- this._resolveAclBuffers = resolve;
116
- });
117
- this.getAclBuffers = async function () {
118
- if (this._aclBuffers) return this._aclBuffers;
119
- return await aclBuffersPromise;
120
- }.bind(this);
121
- this.setAclBuffers = function (length, num) {
122
- if (this._aclBuffers) {
123
- this._aclBuffers.length = length;
124
- this._aclBuffers.num = num;
125
- return;
126
- }
127
- this._aclBuffers = {
128
- length,
129
- num
130
- };
131
- this._resolveAclBuffers(this._aclBuffers);
132
- }.bind(this);
133
-
134
- this._aclConnections = new Map();
135
-
136
- this._aclQueue = [];
137
-
138
- this.on('stateChange', this.onStateChange.bind(this));
139
- };
140
-
141
- util.inherits(Hci, events.EventEmitter);
142
-
143
- Hci.STATUS_MAPPER = STATUS_MAPPER;
144
-
145
- Hci.prototype.init = function (options) {
146
- // Create Serial Port
147
- this._socket = new SerialPort({
148
- path: process.env.NOBLE_HCI_UART_PORT || undefined,
149
- baudRate: parseInt(process.env.NOBLE_HCI_UART_BAUD_RATE) || 1000000,
150
- autoOpen: false,
151
- flowControl: true
152
- });
153
-
154
- // Message Queue
155
- this._socketQueue = async.queue(function (task, callback) {
156
- debug(`socket queue - writing: ${task.buffer.toString('hex')}`);
157
- this._socket.write(task.buffer, (a, b) => setTimeout(callback, task.delay || 0));
158
- }.bind(this));
159
-
160
- // Serial Port Parser
161
- this._socketParser = this._socket.pipe(new HciSerialParser());
162
- this._socketParser.on('data', this.onSocketData.bind(this));
163
- this._socketParser.on('raw', this.waitForReset.bind(this));
164
-
165
- // Check if reset was successfull
166
- this._deviceUpTimer = setTimeout(_ => this.emit('stateChange', 'poweredOff'), 2000);
167
-
168
- // Serial Port Event Listeners
169
- this._socket.on('error', this.onSocketError.bind(this));
170
- this._socket.on('open', this.reset.bind(this));
171
-
172
- // Open Serial Port to begin
173
- this._socket.open(error => error !== null ? this.onSocketError(error) : {});
174
- };
175
-
176
- Hci.prototype.cleanup = function () {
177
- this._socket.close();
178
- this._socketQueue.kill();
179
- };
180
-
181
- Hci.prototype.waitForReset = function (buffer) {
182
- // This is a bit ugly hack to detect if reset reply was present somwhere in a buffer
183
- if (buffer.toString('hex').includes('0e0401030c00')) {
184
- this._socketParser.reset();
185
- this._socketParser.removeListener('raw', this.waitForReset.bind(this));
186
- this.setupDevice();
187
- }
188
- };
189
-
190
- Hci.prototype.setupDevice = function () {
191
- clearTimeout(this._deviceUpTimer);
192
-
193
- if (this._isExtended) {
194
- this.setCodedPhySupport();
195
- }
196
- this.setSocketFilter();
197
- this.setEventMask();
198
- this.setLeEventMask();
199
- this.readLocalVersion();
200
- this.writeLeHostSupported();
201
- this.readLeHostSupported();
202
- this.readLeBufferSize();
203
- this.readBdAddr();
204
- };
205
-
206
- Hci.prototype.setCodedPhySupport = function () {
207
- const cmd = Buffer.alloc(7);
208
-
209
- // header
210
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
211
- cmd.writeUInt16LE(OCF_SET_PHY | (OGF_LE_CTL << 10), 1);
212
-
213
- // length
214
- cmd.writeUInt8(0x03, 3);
215
-
216
- // data
217
- cmd.writeUInt8(0x00, 4); // all phy prefs
218
- cmd.writeUInt8(0x05, 5); // tx phy: 0x01 - LE 1M, 0x03 - LE 1M + LE 2M, 0x05 - LE 1M + LE CODED, 0x07 - LE 1M + LE 2M + LE CODED
219
- cmd.writeUInt8(0x05, 6); // rx phy: 0x01 - LE 1M, 0x03 - LE 1M + LE 2M, 0x05 - LE 1M + LE CODED, 0x07 - LE 1M + LE 2M + LE CODED
220
-
221
- debug(`set all phys supporting - writing: ${cmd.toString('hex')}`);
222
- this._socketQueue.push({ buffer: cmd });
223
- };
224
-
225
- Hci.prototype.setRandomMAC = function () {
226
- const cmd = Buffer.alloc(10);
227
-
228
- // header
229
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
230
- cmd.writeUInt16LE(OCF_SET_RANDOM_MAC | (OGF_LE_CTL << 10), 1);
231
-
232
- // length
233
- cmd.writeUInt8(0x06, 3);
234
-
235
- // data
236
- cmd.writeUInt8(0x05, 4); // mac 6 byte
237
- cmd.writeUInt8(0x04, 5); // mac 5 byte
238
- cmd.writeUInt8(0x03, 6); // mac 4 byte
239
- cmd.writeUInt8(0x02, 7); // mac 3 byte
240
- cmd.writeUInt8(0x01, 8); // mac 2 byte
241
- cmd.writeUInt8(0x00, 9); // mac 1 byte
242
-
243
- debug(`set random mac address - writing: ${cmd.toString('hex')}`);
244
- this._socketQueue.push({ buffer: cmd });
245
- };
246
-
247
- Hci.prototype.setSocketFilter = function () {
248
- const cmd = Buffer.alloc(18);
249
- const typeMask =
250
- (1 << HCI_COMMAND_PKT) | (1 << HCI_EVENT_PKT) | (1 << HCI_ACLDATA_PKT);
251
- const eventMask1 =
252
- (1 << EVT_DISCONN_COMPLETE) |
253
- (1 << EVT_ENCRYPT_CHANGE) |
254
- (1 << EVT_CMD_COMPLETE) |
255
- (1 << EVT_CMD_STATUS) |
256
- (1 << EVT_NUMBER_OF_COMPLETED_PACKETS);
257
- const eventMask2 = 1 << (EVT_LE_META_EVENT - 32);
258
- const opcode = 0;
259
-
260
- // header
261
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
262
- cmd.writeUInt16LE(SET_EVENT_FILTER_CMD, 1);
263
-
264
- // length
265
- cmd.writeUInt8(14, 3);
266
-
267
- cmd.writeUInt32LE(typeMask, 4);
268
- cmd.writeUInt32LE(eventMask1, 8);
269
- cmd.writeUInt32LE(eventMask2, 12);
270
- cmd.writeUInt16LE(opcode, 16);
271
-
272
- // header
273
- debug(`setting filter to: ${cmd.toString('hex')}`);
274
- this._socketQueue.push({ buffer: cmd });
275
- };
276
-
277
- Hci.prototype.setEventMask = function () {
278
- const cmd = Buffer.alloc(12);
279
- const eventMask = Buffer.from('fffffbff07f8bf3d', 'hex');
280
-
281
- // header
282
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
283
- cmd.writeUInt16LE(SET_EVENT_MASK_CMD, 1);
284
-
285
- // length
286
- cmd.writeUInt8(eventMask.length, 3);
287
-
288
- eventMask.copy(cmd, 4);
289
-
290
- debug(`set event mask - writing: ${cmd.toString('hex')}`);
291
- this._socketQueue.push({ buffer: cmd });
292
- };
293
-
294
- Hci.prototype.reset = function () {
295
- const cmd = Buffer.alloc(4);
296
-
297
- // header
298
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
299
- cmd.writeUInt16LE(RESET_CMD, 1);
300
-
301
- // length
302
- cmd.writeUInt8(0x00, 3);
303
-
304
- debug(`reset - writing: ${cmd.toString('hex')}`);
305
- this._socketQueue.push({ buffer: cmd });
306
- };
307
-
308
- Hci.prototype.readLocalVersion = function () {
309
- const cmd = Buffer.alloc(4);
310
-
311
- // header
312
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
313
- cmd.writeUInt16LE(READ_LOCAL_VERSION_CMD, 1);
314
-
315
- // length
316
- cmd.writeUInt8(0x0, 3);
317
-
318
- debug(`read local version - writing: ${cmd.toString('hex')}`);
319
- this._socketQueue.push({ buffer: cmd });
320
- };
321
-
322
- Hci.prototype.readBufferSize = function () {
323
- const cmd = Buffer.alloc(4);
324
-
325
- // header
326
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
327
- cmd.writeUInt16LE(READ_BUFFER_SIZE_CMD, 1);
328
-
329
- // length
330
- cmd.writeUInt8(0x0, 3);
331
-
332
- debug(`read buffer size - writing: ${cmd.toString('hex')}`);
333
- this._socketQueue.push({ buffer: cmd });
334
- };
335
-
336
- Hci.prototype.readBdAddr = function () {
337
- const cmd = Buffer.alloc(4);
338
-
339
- // header
340
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
341
- cmd.writeUInt16LE(READ_BD_ADDR_CMD, 1);
342
-
343
- // length
344
- cmd.writeUInt8(0x0, 3);
345
-
346
- debug(`read bd addr - writing: ${cmd.toString('hex')}`);
347
- this._socketQueue.push({ buffer: cmd });
348
- };
349
-
350
- Hci.prototype.setEventMask = function () {
351
- const cmd = Buffer.alloc(12);
352
- const eventMask = Buffer.from('fffffbff07f8bf3d', 'hex');
353
-
354
- // header
355
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
356
- cmd.writeUInt16LE(SET_EVENT_MASK_CMD, 1);
357
-
358
- // length
359
- cmd.writeUInt8(eventMask.length, 3);
360
-
361
- eventMask.copy(cmd, 4);
362
-
363
- debug(`set event mask - writing: ${cmd.toString('hex')}`);
364
- this._socketQueue.push({ buffer: cmd });
365
- };
366
-
367
- Hci.prototype.reset = function () {
368
- const cmd = Buffer.alloc(4);
369
-
370
- // header
371
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
372
- cmd.writeUInt16LE(RESET_CMD, 1);
373
-
374
- // length
375
- cmd.writeUInt8(0x00, 3);
376
-
377
- debug(`reset - writing: ${cmd.toString('hex')}`);
378
- this._socketQueue.push({ buffer: cmd });
379
- };
380
-
381
- Hci.prototype.readSupportedCommands = function () {
382
- const cmd = Buffer.alloc(4);
383
-
384
- // header
385
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
386
- cmd.writeUInt16LE(READ_SUPPORTED_COMMANDS_CMD, 1);
387
-
388
- // length
389
- cmd.writeUInt8(0x0, 3);
390
-
391
- debug(`read supported commands - writing: ${cmd.toString('hex')}`);
392
- this._socketQueue.push({ buffer: cmd });
393
- };
394
-
395
- Hci.prototype.readLocalVersion = function () {
396
- const cmd = Buffer.alloc(4);
397
-
398
- // header
399
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
400
- cmd.writeUInt16LE(READ_LOCAL_VERSION_CMD, 1);
401
-
402
- // length
403
- cmd.writeUInt8(0x0, 3);
404
-
405
- debug(`read local version - writing: ${cmd.toString('hex')}`);
406
- this._socketQueue.push({ buffer: cmd });
407
- };
408
-
409
- Hci.prototype.readBufferSize = function () {
410
- const cmd = Buffer.alloc(4);
411
-
412
- // header
413
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
414
- cmd.writeUInt16LE(READ_BUFFER_SIZE_CMD, 1);
415
-
416
- // length
417
- cmd.writeUInt8(0x0, 3);
418
-
419
- debug(`read buffer size - writing: ${cmd.toString('hex')}`);
420
- this._socketQueue.push({ buffer: cmd });
421
- };
422
-
423
- Hci.prototype.readBdAddr = function () {
424
- const cmd = Buffer.alloc(4);
425
-
426
- // header
427
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
428
- cmd.writeUInt16LE(READ_BD_ADDR_CMD, 1);
429
-
430
- // length
431
- cmd.writeUInt8(0x0, 3);
432
-
433
- debug(`read bd addr - writing: ${cmd.toString('hex')}`);
434
- this._socketQueue.push({ buffer: cmd });
435
- };
436
-
437
- Hci.prototype.setLeEventMask = function () {
438
- const cmd = Buffer.alloc(12);
439
- const leEventMask = this._isExtended
440
- ? Buffer.from('1fff000000000000', 'hex')
441
- : Buffer.from('1f00000000000000', 'hex');
442
-
443
- // header
444
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
445
- cmd.writeUInt16LE(LE_SET_EVENT_MASK_CMD, 1);
446
-
447
- // length
448
- cmd.writeUInt8(leEventMask.length, 3);
449
-
450
- leEventMask.copy(cmd, 4);
451
-
452
- debug(`set le event mask - writing: ${cmd.toString('hex')}`);
453
- this._socketQueue.push({ buffer: cmd });
454
- };
455
-
456
- Hci.prototype.readLeBufferSize = function () {
457
- const cmd = Buffer.alloc(4);
458
-
459
- // header
460
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
461
- cmd.writeUInt16LE(LE_READ_BUFFER_SIZE_CMD, 1);
462
-
463
- // length
464
- cmd.writeUInt8(0x0, 3);
465
-
466
- debug(`le read buffer size - writing: ${cmd.toString('hex')}`);
467
- this._socketQueue.push({ buffer: cmd });
468
- };
469
-
470
- Hci.prototype.readLeHostSupported = function () {
471
- const cmd = Buffer.alloc(4);
472
-
473
- // header
474
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
475
- cmd.writeUInt16LE(READ_LE_HOST_SUPPORTED_CMD, 1);
476
-
477
- // length
478
- cmd.writeUInt8(0x00, 3);
479
-
480
- debug(`read LE host supported - writing: ${cmd.toString('hex')}`);
481
- this._socketQueue.push({ buffer: cmd });
482
- };
483
-
484
- Hci.prototype.writeLeHostSupported = function () {
485
- const cmd = Buffer.alloc(6);
486
-
487
- // header
488
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
489
- cmd.writeUInt16LE(WRITE_LE_HOST_SUPPORTED_CMD, 1);
490
-
491
- // length
492
- cmd.writeUInt8(0x02, 3);
493
-
494
- // data
495
- cmd.writeUInt8(0x01, 4); // le
496
- cmd.writeUInt8(0x00, 5); // simul
497
-
498
- debug(`write LE host supported - writing: ${cmd.toString('hex')}`);
499
- this._socketQueue.push({ buffer: cmd });
500
- };
501
-
502
- Hci.prototype.setScanParameters = function (
503
- interval = 0x0012,
504
- window = 0x0012
505
- ) {
506
- const cmd = Buffer.alloc(this._isExtended ? 17 : 11);
507
-
508
- // header
509
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
510
- cmd.writeUInt16LE(
511
- this._isExtended
512
- ? LE_SET_EXTENDED_SCAN_PARAMETERS_CMD
513
- : LE_SET_SCAN_PARAMETERS_CMD,
514
- 1
515
- );
516
-
517
- if (this._isExtended) {
518
- // length
519
- cmd.writeUInt8(0x0d, 3);
520
-
521
- // data
522
- cmd.writeUInt8(0x00, 4); // own address type: 0 -> public, 1 -> random
523
- cmd.writeUInt8(0x00, 5); // filter: 0 -> all event types
524
- cmd.writeUInt8(0x05, 6); // phy: LE 1M + LE CODED
525
- // phy 1M
526
- cmd.writeUInt8(0x01, 7); // type: 0 -> passive, 1 -> active
527
- cmd.writeUInt16LE(interval, 8); // interval, ms * 1.6
528
- cmd.writeUInt16LE(window, 10); // window, ms * 1.6
529
- // phy coded
530
- cmd.writeUInt8(0x01, 12); // type: 0 -> passive, 1 -> active
531
- cmd.writeUInt16LE(interval, 13); // interval, ms * 1.6
532
- cmd.writeUInt16LE(window, 15); // window, ms * 1.6
533
- } else {
534
- // length
535
- cmd.writeUInt8(0x07, 3);
536
-
537
- // data
538
- cmd.writeUInt8(0x01, 4); // type: 0 -> passive, 1 -> active
539
- cmd.writeUInt16LE(interval, 5); // interval, ms * 1.6
540
- cmd.writeUInt16LE(window, 7); // window, ms * 1.6
541
- cmd.writeUInt8(0x00, 9); // own address type: 0 -> public, 1 -> random
542
- cmd.writeUInt8(0x00, 10); // filter: 0 -> all event types
543
- }
544
-
545
- debug(`set scan parameters - writing: ${cmd.toString('hex')}`);
546
- this._socketQueue.push({ buffer: cmd });
547
- };
548
-
549
- Hci.prototype.setScanEnabled = function (enabled, filterDuplicates) {
550
- const cmd = Buffer.alloc(this._isExtended ? 10 : 6);
551
-
552
- // header
553
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
554
- cmd.writeUInt16LE(
555
- this._isExtended ? LE_SET_EXTENDED_SCAN_ENABLE_CMD : LE_SET_SCAN_ENABLE_CMD,
556
- 1
557
- );
558
-
559
- if (this._isExtended) {
560
- // length
561
- cmd.writeUInt8(0x06, 3);
562
-
563
- // data
564
- cmd.writeUInt8(enabled ? 0x01 : 0x00, 4); // enable: 0 -> disabled, 1 -> enabled
565
- cmd.writeUInt8(filterDuplicates ? 0x01 : 0x00, 5); // duplicates: 0 -> duplicates, 1 -> all
566
- cmd.writeUInt16LE(0x0000, 6); // duration
567
- cmd.writeUInt16LE(0x0000, 8); // period
568
- } else {
569
- // length
570
- cmd.writeUInt8(0x02, 3);
571
-
572
- // data
573
- cmd.writeUInt8(enabled ? 0x01 : 0x00, 4); // enable: 0 -> disabled, 1 -> enabled
574
- cmd.writeUInt8(filterDuplicates ? 0x01 : 0x00, 5); // duplicates: 0 -> duplicates, 0 -> duplicates
575
- }
576
-
577
- debug(`set scan enabled - writing: ${cmd.toString('hex')}`);
578
- this._socketQueue.push({ buffer: cmd });
579
- };
580
-
581
- Hci.prototype.createLeConn = function (address, addressType, parameters = {}) {
582
- const {
583
- minInterval = 0x0006,
584
- maxInterval = 0x0012,
585
- latency = 0x0000,
586
- timeout = 0x002a
587
- } = parameters;
588
-
589
- const cmd = Buffer.alloc(this._isExtended ? 46 : 29);
590
-
591
- // header
592
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
593
- cmd.writeUInt16LE(
594
- this._isExtended
595
- ? LE_CREATE_EXTENDED_CONN_CMD
596
- : LE_CREATE_CONN_CMD,
597
- 1
598
- );
599
-
600
- if (this._isExtended) {
601
- // length
602
- cmd.writeUInt8(0x2a, 3);
603
-
604
- // data
605
- cmd.writeUInt8(0x00, 4); // filter policy: white list is not used
606
- cmd.writeUInt8(0x00, 5); // own address type
607
- cmd.writeUInt8(addressType === 'random' ? 0x01 : 0x00, 6); // peer address type
608
- Buffer.from(address.split(':').reverse().join(''), 'hex').copy(cmd, 7); // peer address
609
- cmd.writeUInt8(0x05, 13); // initiating PHYs: LE 1M + LE Coded
610
-
611
- // LE 1M PHY
612
- cmd.writeUInt16LE(0x0060, 14); // scan interval 60 msec
613
- cmd.writeUInt16LE(0x0060, 16); // scan window 60 msec
614
- cmd.writeUInt16LE(minInterval, 18); // min interval
615
- cmd.writeUInt16LE(maxInterval, 20); // max interval
616
- cmd.writeUInt16LE(latency, 22); // latency
617
- cmd.writeUInt16LE(timeout, 24); // supervision timeout
618
- cmd.writeUInt16LE(0x0000, 26); // min ce length
619
- cmd.writeUInt16LE(0x0000, 28); // max ce length
620
-
621
- // LE Coded PHY
622
- cmd.writeUInt16LE(0x0060, 30); // scan interval 60 msec
623
- cmd.writeUInt16LE(0x0060, 32); // scan window 60 msec
624
- cmd.writeUInt16LE(minInterval, 34); // min interval
625
- cmd.writeUInt16LE(maxInterval, 36); // max interval
626
- cmd.writeUInt16LE(latency, 38); // latency
627
- cmd.writeUInt16LE(timeout, 40); // supervision timeout
628
- cmd.writeUInt16LE(0x0000, 42); // min ce length
629
- cmd.writeUInt16LE(0x0000, 44); // max ce length
630
- } else {
631
- // length
632
- cmd.writeUInt8(0x19, 3);
633
-
634
- // data
635
- cmd.writeUInt16LE(0x0060, 4); // interval
636
- cmd.writeUInt16LE(0x0030, 6); // window
637
- cmd.writeUInt8(0x00, 8); // initiator filter
638
-
639
- cmd.writeUInt8(addressType === 'random' ? 0x01 : 0x00, 9); // peer address type
640
- Buffer.from(address.split(':').reverse().join(''), 'hex').copy(cmd, 10); // peer address
641
-
642
- cmd.writeUInt8(0x00, 16); // own address type
643
-
644
- cmd.writeUInt16LE(minInterval, 17); // min interval
645
- cmd.writeUInt16LE(maxInterval, 19); // max interval
646
- cmd.writeUInt16LE(latency, 21); // latency
647
- cmd.writeUInt16LE(timeout, 23); // supervision timeout
648
- cmd.writeUInt16LE(0x0004, 25); // min ce length
649
- cmd.writeUInt16LE(0x0006, 27); // max ce length
650
- }
651
-
652
- debug(`create le conn - writing: ${cmd.toString('hex')}`);
653
- this._socketQueue.push({ buffer: cmd });
654
- };
655
-
656
- Hci.prototype.connUpdateLe = function (
657
- handle,
658
- minInterval,
659
- maxInterval,
660
- latency,
661
- supervisionTimeout
662
- ) {
663
- const cmd = Buffer.alloc(18);
664
-
665
- // header
666
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
667
- cmd.writeUInt16LE(LE_CONN_UPDATE_CMD, 1);
668
-
669
- // length
670
- cmd.writeUInt8(0x0e, 3);
671
-
672
- // data
673
- cmd.writeUInt16LE(handle, 4);
674
- cmd.writeUInt16LE(Math.floor(minInterval / 1.25), 6); // min interval
675
- cmd.writeUInt16LE(Math.floor(maxInterval / 1.25), 8); // max interval
676
- cmd.writeUInt16LE(latency, 10); // latency
677
- cmd.writeUInt16LE(Math.floor(supervisionTimeout / 10), 12); // supervision timeout
678
- cmd.writeUInt16LE(0x0000, 14); // min ce length
679
- cmd.writeUInt16LE(0x0000, 16); // max ce length
680
-
681
- debug(`conn update le - writing: ${cmd.toString('hex')}`);
682
- this._socketQueue.push({ buffer: cmd });
683
- };
684
-
685
- Hci.prototype.cancelConnect = function () {
686
- const cmd = Buffer.alloc(4);
687
-
688
- // header
689
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
690
- cmd.writeUInt16LE(LE_CANCEL_CONN_CMD, 1);
691
-
692
- // length
693
- cmd.writeUInt8(0x0, 3);
694
-
695
- debug('cancel le conn - writing: ' + cmd.toString('hex'));
696
- this._socketQueue.push({ buffer: cmd });
697
- };
698
-
699
- Hci.prototype.startLeEncryption = function (handle, random, diversifier, key) {
700
- const cmd = Buffer.alloc(32);
701
-
702
- // header
703
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
704
- cmd.writeUInt16LE(LE_START_ENCRYPTION_CMD, 1);
705
-
706
- // length
707
- cmd.writeUInt8(0x1c, 3);
708
-
709
- // data
710
- cmd.writeUInt16LE(handle, 4); // handle
711
- random.copy(cmd, 6);
712
- diversifier.copy(cmd, 14);
713
- key.copy(cmd, 16);
714
-
715
- debug(`start le encryption - writing: ${cmd.toString('hex')}`);
716
- this._socketQueue.push({ buffer: cmd });
717
- };
718
-
719
- Hci.prototype.disconnect = function (handle, reason) {
720
- const cmd = Buffer.alloc(7);
721
-
722
- reason = reason || HCI_OE_USER_ENDED_CONNECTION;
723
-
724
- // header
725
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
726
- cmd.writeUInt16LE(DISCONNECT_CMD, 1);
727
-
728
- // length
729
- cmd.writeUInt8(0x03, 3);
730
-
731
- // data
732
- cmd.writeUInt16LE(handle, 4); // handle
733
- cmd.writeUInt8(reason, 6); // reason
734
-
735
- debug(`disconnect - writing: ${cmd.toString('hex')}`);
736
- this._socketQueue.push({ buffer: cmd });
737
- };
738
-
739
- Hci.prototype.readRssi = function (handle) {
740
- const cmd = Buffer.alloc(6);
741
-
742
- // header
743
- cmd.writeUInt8(HCI_COMMAND_PKT, 0);
744
- cmd.writeUInt16LE(READ_RSSI_CMD, 1);
745
-
746
- // length
747
- cmd.writeUInt8(0x02, 3);
748
-
749
- // data
750
- cmd.writeUInt16LE(handle, 4); // handle
751
-
752
- debug(`read rssi - writing: ${cmd.toString('hex')}`);
753
- this._socketQueue.push({ buffer: cmd });
754
- };
755
-
756
- Hci.prototype.writeAclDataPkt = async function (handle, cid, data) {
757
- const l2capLength = 4 /* l2cap header */ + data.length;
758
-
759
- const aclBuffers = await this.getAclBuffers();
760
- const aclLength = Math.min(l2capLength, aclBuffers.length);
761
-
762
- const minFirstLength = Math.max(aclLength + 5 /* acl header */, 9 /* prevent buffer overflow */);
763
- const first = Buffer.alloc(minFirstLength);
764
-
765
- // acl header
766
- first.writeUInt8(HCI_ACLDATA_PKT, 0);
767
- first.writeUInt16LE(handle | (ACL_START_NO_FLUSH << 12), 1);
768
- first.writeUInt16LE(aclLength, 3);
769
-
770
- // l2cap header
771
- first.writeUInt16LE(data.length, 5);
772
- first.writeUInt16LE(cid, 7);
773
-
774
- data.copy(first, 9);
775
- data = data.slice(first.length - 9);
776
-
777
- debug(`push to acl queue: ${first.toString('hex')}`);
778
- this._aclQueue.push({ handle, packet: first });
779
-
780
- while (data.length > 0) {
781
- const fragAclLength = Math.min(data.length, aclBuffers.length);
782
- const frag = Buffer.alloc(fragAclLength + 5 /* acl header */);
783
- // acl header
784
- frag.writeUInt8(HCI_ACLDATA_PKT, 0);
785
- frag.writeUInt16LE(handle | (ACL_CONT << 12), 1);
786
- frag.writeUInt16LE(fragAclLength, 3);
787
-
788
- data.copy(frag, 5);
789
- data = data.slice(frag.length - 5);
790
-
791
- debug(`push fragment to acl queue: ${frag.toString('hex')}`);
792
- this._aclQueue.push({ handle, packet: frag });
793
- }
794
-
795
- this.flushAcl();
796
- };
797
-
798
- Hci.prototype.flushAcl = async function () {
799
- const pendingPackets = () => {
800
- let totalPending = 0;
801
- for (const { pending } of this._aclConnections.values()) {
802
- totalPending += pending;
803
- }
804
- return totalPending;
805
- };
806
-
807
- debug(
808
- `flush - pending: ${pendingPackets()} queue length: ${
809
- this._aclQueue.length
810
- }`
811
- );
812
-
813
- const aclBuffers = await this.getAclBuffers();
814
- while (this._aclQueue.length > 0 && pendingPackets() < aclBuffers.num) {
815
- const { handle, packet } = this._aclQueue.shift();
816
- this._aclConnections.get(handle).pending++;
817
- debug(`write acl data packet - writing: ${packet.toString('hex')}`);
818
-
819
- this._socketQueue.push({ buffer: packet });
820
- }
821
- };
822
-
823
- Hci.prototype.onSocketData = function (data) {
824
- debug(`onSocketData: ${data.toString('hex')}`);
825
-
826
- const eventType = data.readUInt8(0);
827
- let handle;
828
- let cmd;
829
- let status;
830
-
831
- debug(`\tevent type = ${eventType}`);
832
-
833
- if (HCI_EVENT_PKT === eventType) {
834
- const subEventType = data.readUInt8(1);
835
-
836
- debug(`\tsub event type = ${subEventType}`);
837
-
838
- if (subEventType === EVT_DISCONN_COMPLETE) {
839
- handle = data.readUInt16LE(4);
840
- const reason = data.readUInt8(6);
841
-
842
- debug(`\t\thandle = ${handle}`);
843
- debug(`\t\treason = ${reason}`);
844
-
845
- this._aclQueue = this._aclQueue.filter((acl) => acl.handle !== handle);
846
- this._aclConnections.delete(handle);
847
- this.flushAcl();
848
-
849
- this.emit('disconnComplete', handle, reason);
850
- } else if (subEventType === EVT_ENCRYPT_CHANGE) {
851
- handle = data.readUInt16LE(4);
852
- const encrypt = data.readUInt8(6);
853
-
854
- debug(`\t\thandle = ${handle}`);
855
- debug(`\t\tencrypt = ${encrypt}`);
856
-
857
- this.emit('encryptChange', handle, encrypt);
858
- } else if (subEventType === EVT_CMD_COMPLETE) {
859
- cmd = data.readUInt16LE(4);
860
- status = data.readUInt8(6);
861
- const result = data.slice(7);
862
-
863
- debug(`\t\tcmd = ${cmd}`);
864
- debug(`\t\tstatus = ${status}`);
865
- debug(`\t\tresult = ${result.toString('hex')}`);
866
-
867
- this.processCmdCompleteEvent(cmd, status, result);
868
- } else if (subEventType === EVT_CMD_STATUS) {
869
- status = data.readUInt8(3);
870
- cmd = data.readUInt16LE(5);
871
-
872
- debug(`\t\tstatus = ${status}`);
873
- debug(`\t\tcmd = ${cmd}`);
874
-
875
- this.processCmdStatusEvent(cmd, status);
876
- } else if (subEventType === EVT_LE_META_EVENT) {
877
- const leMetaEventLength = data.readUInt8(2);
878
- const leMetaEventType = data.readUInt8(3);
879
- const leMetaEventNumReports = data.readUInt8(4);
880
- const leMetaEventData = data.slice(5);
881
-
882
- debug(`\t\tLE meta event type = ${leMetaEventType}`);
883
- debug(`\t\tLE meta event data length = ${leMetaEventLength}`);
884
- debug(`\t\tLE meta event num reports = ${leMetaEventNumReports}`);
885
- debug(`\t\tLE meta event data = ${leMetaEventData.toString('hex')}`);
886
-
887
- this.processLeMetaEvent(
888
- leMetaEventType,
889
- leMetaEventNumReports,
890
- leMetaEventData
891
- );
892
- } else if (subEventType === EVT_NUMBER_OF_COMPLETED_PACKETS) {
893
- const handles = data.readUInt8(3);
894
- for (let h = 0; h < handles; h++) {
895
- const handle = data.readUInt16LE(4 + h * 4);
896
- const pkts = data.readUInt16LE(6 + h * 4);
897
- debug(`\thandle = ${handle}`);
898
- debug(`\t\tcompleted = ${pkts}`);
899
-
900
- if (!this._aclConnections.has(handle)) {
901
- debug('\t\tclosed');
902
- continue;
903
- }
904
-
905
- const connection = this._aclConnections.get(handle);
906
-
907
- connection.pending -= pkts;
908
-
909
- if (connection.pending < 0) {
910
- connection.pending = 0;
911
- }
912
- }
913
- this.flushAcl();
914
- }
915
- } else if (HCI_ACLDATA_PKT === eventType) {
916
- const flags = data.readUInt16LE(1) >> 12;
917
- handle = data.readUInt16LE(1) & 0x0fff;
918
-
919
- if (ACL_START === flags) {
920
- const cid = data.readUInt16LE(7);
921
-
922
- const length = data.readUInt16LE(5);
923
- const pktData = data.slice(9);
924
-
925
- debug(`\t\tcid = ${cid}`);
926
-
927
- if (length === pktData.length) {
928
- debug(`\t\thandle = ${handle}`);
929
- debug(`\t\tdata = ${pktData.toString('hex')}`);
930
-
931
- this.emit('aclDataPkt', handle, cid, pktData);
932
- } else {
933
- this._handleBuffers[handle] = {
934
- length: length,
935
- cid: cid,
936
- data: pktData
937
- };
938
- }
939
- } else if (ACL_CONT === flags) {
940
- if (!this._handleBuffers[handle] || !this._handleBuffers[handle].data) {
941
- return;
942
- }
943
-
944
- this._handleBuffers[handle].data = Buffer.concat([
945
- this._handleBuffers[handle].data,
946
- data.slice(5)
947
- ]);
948
-
949
- if (
950
- this._handleBuffers[handle].data.length ===
951
- this._handleBuffers[handle].length
952
- ) {
953
- this.emit(
954
- 'aclDataPkt',
955
- handle,
956
- this._handleBuffers[handle].cid,
957
- this._handleBuffers[handle].data
958
- );
959
-
960
- delete this._handleBuffers[handle];
961
- }
962
- }
963
- } else if (HCI_COMMAND_PKT === eventType) {
964
- cmd = data.readUInt16LE(1);
965
- const len = data.readUInt8(3);
966
-
967
- debug(`\t\tcmd = ${cmd}`);
968
- debug(`\t\tdata len = ${len}`);
969
-
970
- if (
971
- cmd === LE_SET_SCAN_ENABLE_CMD ||
972
- cmd === LE_SET_EXTENDED_SCAN_ENABLE_CMD
973
- ) {
974
- const enable = data.readUInt8(4) === 0x1;
975
- const filterDuplicates = data.readUInt8(5) === 0x1;
976
-
977
- debug('\t\t\tLE enable scan command');
978
- debug(`\t\t\tenable scanning = ${enable}`);
979
- debug(`\t\t\tfilter duplicates = ${filterDuplicates}`);
980
-
981
- this.emit('leScanEnableSetCmd', enable, filterDuplicates);
982
- }
983
- }
984
- };
985
-
986
- Hci.prototype.onSocketError = function (error) {
987
- debug(`onSocketError: ${error.message}`);
988
-
989
- if (error.code === 'EPERM') {
990
- this.emit('stateChange', 'unauthorized');
991
- } else if (error.message === 'Network is down') {
992
- // no-op
993
- }
994
- };
995
-
996
- Hci.prototype.processCmdCompleteEvent = function (cmd, status, result) {
997
- if (cmd === RESET_CMD) {
998
- if (this._isExtended) {
999
- this.setCodedPhySupport();
1000
- }
1001
- this.setEventMask();
1002
- this.setLeEventMask();
1003
- this.readLocalVersion();
1004
- this.readBdAddr();
1005
- } else if (cmd === READ_LE_HOST_SUPPORTED_CMD) {
1006
- if (status === 0) {
1007
- const le = result.readUInt8(0);
1008
- const simul = result.readUInt8(1);
1009
-
1010
- debug(`\t\t\tle = ${le}`);
1011
- debug(`\t\t\tsimul = ${simul}`);
1012
- }
1013
- } else if (cmd === READ_LOCAL_VERSION_CMD) {
1014
- const hciVer = result.readUInt8(0);
1015
- const hciRev = result.readUInt16LE(1);
1016
- const lmpVer = result.readInt8(3);
1017
- const manufacturer = result.readUInt16LE(4);
1018
- const lmpSubVer = result.readUInt16LE(6);
1019
-
1020
- if (hciVer < 0x06) {
1021
- this.emit('stateChange', 'unsupported');
1022
- } else if (this._state !== 'poweredOn') {
1023
- this.setScanEnabled(false, true);
1024
- this.setScanParameters();
1025
- }
1026
-
1027
- this.emit(
1028
- 'readLocalVersion',
1029
- hciVer,
1030
- hciRev,
1031
- lmpVer,
1032
- manufacturer,
1033
- lmpSubVer
1034
- );
1035
- } else if (cmd === READ_SUPPORTED_COMMANDS_CMD) {
1036
- const extendedScanParameters = result.readUInt8(37) & 0x10; // LE Set Extended Scan Parameters (Octet 37 - Bit 5)
1037
- const extendedScan = result.readUInt8(37) & 0x20; // LE Set Extended Scan Enable (Octet 37 - Bit 6)
1038
-
1039
- debug(
1040
- `Extended advertising features: parameters = ${
1041
- extendedScanParameters ? 'true' : 'false'
1042
- }, num = ${extendedScan ? 'true' : 'false'}`
1043
- );
1044
-
1045
- this._isExtended = extendedScanParameters && extendedScan;
1046
- } else if (cmd === READ_BD_ADDR_CMD) {
1047
- this.addressType = 'public';
1048
- this.address = result
1049
- .toString('hex')
1050
- .match(/.{1,2}/g)
1051
- .reverse()
1052
- .join(':');
1053
-
1054
- debug(`address = ${this.address}`);
1055
-
1056
- this.emit('addressChange', this.address);
1057
- } else if (
1058
- cmd === LE_SET_SCAN_PARAMETERS_CMD ||
1059
- cmd === LE_SET_EXTENDED_SCAN_PARAMETERS_CMD
1060
- ) {
1061
- this.emit('stateChange', 'poweredOn');
1062
-
1063
- this.emit('leScanParametersSet');
1064
- } else if (
1065
- cmd === LE_SET_SCAN_ENABLE_CMD ||
1066
- cmd === LE_SET_EXTENDED_SCAN_ENABLE_CMD
1067
- ) {
1068
- this.emit('leScanEnableSet', status);
1069
- } else if (cmd === READ_RSSI_CMD) {
1070
- const handle = result.readUInt16LE(0);
1071
- const rssi = result.readInt8(2);
1072
-
1073
- debug(`\t\t\thandle = ${handle}`);
1074
- debug(`\t\t\trssi = ${rssi}`);
1075
-
1076
- this.emit('rssiRead', handle, rssi);
1077
- } else if (cmd === LE_READ_BUFFER_SIZE_CMD) {
1078
- if (status === 0) {
1079
- const aclLength = result.readUInt16LE(0);
1080
- const aclNum = result.readUInt8(2);
1081
-
1082
- /* Spec Vol 4 Part E.7.8
1083
- /* No dedicated LE Buffer exists. Use the HCI_Read_Buffer_Size command. */
1084
- if (aclLength === 0 || aclNum === 0) {
1085
- debug('using br/edr buffer size');
1086
- this.readBufferSize();
1087
- } else {
1088
- debug(`le buffer size: length = ${aclLength}, num = ${aclNum}`);
1089
- this.setAclBuffers(aclLength, aclNum);
1090
- }
1091
- }
1092
- } else if (cmd === READ_BUFFER_SIZE_CMD) {
1093
- const aclLength = result.readUInt16LE(0);
1094
- const aclNum = result.readUInt16LE(3);
1095
-
1096
- debug(`buffer size: length = ${aclLength}, num = ${aclNum}`);
1097
- this.setAclBuffers(aclLength, aclNum);
1098
- }
1099
- };
1100
-
1101
- Hci.prototype.processLeMetaEvent = function (eventType, numReports, data) {
1102
- if (eventType === EVT_LE_CONN_COMPLETE) {
1103
- this.processLeConnComplete(numReports, data);
1104
- } else if (eventType === EVT_LE_ENHANCED_CONN_COMPLETE) {
1105
- this.processLeEnhancedConnComplete(numReports, data);
1106
- } else if (eventType === EVT_LE_ADVERTISING_REPORT) {
1107
- this.processLeAdvertisingReport(numReports, data);
1108
- } else if (eventType === EVT_LE_EXTENDED_ADVERTISING_REPORT) {
1109
- this.processLeExtendedAdvertisingReport(numReports, data);
1110
- } else if (eventType === EVT_LE_CONN_UPDATE_COMPLETE) {
1111
- this.processLeConnUpdateComplete(numReports, data);
1112
- }
1113
- };
1114
-
1115
- Hci.prototype.processLeMetaEvent = function (eventType, status, data) {
1116
- if (eventType === EVT_LE_CONN_COMPLETE) {
1117
- this.processLeConnComplete(status, data);
1118
- } else if (eventType === EVT_LE_ADVERTISING_REPORT) {
1119
- this.processLeAdvertisingReport(status, data);
1120
- } else if (eventType === EVT_LE_CONN_UPDATE_COMPLETE) {
1121
- this.processLeConnUpdateComplete(status, data);
1122
- }
1123
- };
1124
-
1125
- Hci.prototype.processLeConnComplete = function (status, data) {
1126
- const handle = data.readUInt16LE(0);
1127
- const role = data.readUInt8(2);
1128
- const addressType = data.readUInt8(3) === 0x01 ? 'random' : 'public';
1129
- const address = data
1130
- .slice(4, 10)
1131
- .toString('hex')
1132
- .match(/.{1,2}/g)
1133
- .reverse()
1134
- .join(':');
1135
- const interval = data.readUInt16LE(10) * 1.25;
1136
- const latency = data.readUInt16LE(12); // TODO: multiplier?
1137
- const supervisionTimeout = data.readUInt16LE(14) * 10;
1138
- const masterClockAccuracy = data.readUInt8(16); // TODO: multiplier?
1139
-
1140
- debug(`\t\t\thandle = ${handle}`);
1141
- debug(`\t\t\trole = ${role}`);
1142
- debug(`\t\t\taddress type = ${addressType}`);
1143
- debug(`\t\t\taddress = ${address}`);
1144
- debug(`\t\t\tinterval = ${interval}`);
1145
- debug(`\t\t\tlatency = ${latency}`);
1146
- debug(`\t\t\tsupervision timeout = ${supervisionTimeout}`);
1147
- debug(`\t\t\tmaster clock accuracy = ${masterClockAccuracy}`);
1148
-
1149
- this._aclConnections.set(handle, { pending: 0 });
1150
-
1151
- this.emit(
1152
- 'leConnComplete',
1153
- status,
1154
- handle,
1155
- role,
1156
- addressType,
1157
- address,
1158
- interval,
1159
- latency,
1160
- supervisionTimeout,
1161
- masterClockAccuracy
1162
- );
1163
- };
1164
-
1165
- Hci.prototype.processLeEnhancedConnComplete = function (status, data) {
1166
- const handle = data.readUInt16LE(0);
1167
- const role = data.readUInt8(2);
1168
- const addressType = data.readUInt8(3) === 0x01 ? 'random' : 'public';
1169
- const address = data
1170
- .slice(4, 10)
1171
- .toString('hex')
1172
- .match(/.{1,2}/g)
1173
- .reverse()
1174
- .join(':');
1175
- const localResolvablePrivateAddress = data
1176
- .slice(10, 16)
1177
- .toString('hex')
1178
- .match(/.{1,2}/g)
1179
- .reverse()
1180
- .join(':');
1181
- const peerResolvablePrivateAddress = data
1182
- .slice(16, 22)
1183
- .toString('hex')
1184
- .match(/.{1,2}/g)
1185
- .reverse()
1186
- .join(':');
1187
- const interval = data.readUInt16LE(22) * 1.25;
1188
- const latency = data.readUInt16LE(24); // TODO: multiplier?
1189
- const supervisionTimeout = data.readUInt16LE(26) * 10;
1190
- const masterClockAccuracy = data.readUInt8(28); // TODO: multiplier?
1191
-
1192
- debug(`\t\t\thandle = ${handle}`);
1193
- debug(`\t\t\trole = ${role}`);
1194
- debug(`\t\t\taddress type = ${addressType}`);
1195
- debug(`\t\t\taddress = ${address}`);
1196
- debug(
1197
- `\t\t\tLocal resolvable private address = ${localResolvablePrivateAddress}`
1198
- );
1199
- debug(
1200
- `\t\t\tPeer resolvable private address = ${peerResolvablePrivateAddress}`
1201
- );
1202
- debug(`\t\t\tinterval = ${interval}`);
1203
- debug(`\t\t\tlatency = ${latency}`);
1204
- debug(`\t\t\tsupervision timeout = ${supervisionTimeout}`);
1205
- debug(`\t\t\tmaster clock accuracy = ${masterClockAccuracy}`);
1206
-
1207
- this._aclConnections.set(handle, { pending: 0 });
1208
-
1209
- this.emit(
1210
- 'leConnComplete',
1211
- status,
1212
- handle,
1213
- role,
1214
- addressType,
1215
- address,
1216
- interval,
1217
- latency,
1218
- supervisionTimeout,
1219
- masterClockAccuracy
1220
- );
1221
- };
1222
-
1223
- Hci.prototype.processLeAdvertisingReport = function (numReports, data) {
1224
- try {
1225
- for (let i = 0; i < numReports; i++) {
1226
- const type = data.readUInt8(0);
1227
- const addressType = data.readUInt8(1) === 0x01 ? 'random' : 'public';
1228
- const address = data
1229
- .slice(2, 8)
1230
- .toString('hex')
1231
- .match(/.{1,2}/g)
1232
- .reverse()
1233
- .join(':');
1234
- const eirLength = data.readUInt8(8);
1235
- const eir = data.slice(9, eirLength + 9);
1236
- const rssi = data.readInt8(eirLength + 9);
1237
-
1238
- debug(`\t\t\ttype = ${type}`);
1239
- debug(`\t\t\taddress = ${address}`);
1240
- debug(`\t\t\taddress type = ${addressType}`);
1241
- debug(`\t\t\teir = ${eir.toString('hex')}`);
1242
- debug(`\t\t\trssi = ${rssi}`);
1243
-
1244
- this.emit(
1245
- 'leAdvertisingReport',
1246
- 0,
1247
- type,
1248
- address,
1249
- addressType,
1250
- eir,
1251
- rssi
1252
- );
1253
-
1254
- data = data.slice(eirLength + 10);
1255
- }
1256
- } catch (e) {
1257
- console.warn(
1258
- `processLeAdvertisingReport: Caught illegal packet (buffer overflow): ${e}`
1259
- );
1260
- }
1261
- };
1262
-
1263
- Hci.prototype.processLeExtendedAdvertisingReport = function (numReports, data) {
1264
- try {
1265
- for (let i = 0; i < numReports; i++) {
1266
- const type = data.readUInt16LE(0);
1267
- const addressType = data.readUInt8(2) === 0x01 ? 'random' : 'public';
1268
- const address = data
1269
- .slice(3, 9)
1270
- .toString('hex')
1271
- .match(/.{1,2}/g)
1272
- .reverse()
1273
- .join(':');
1274
- const primaryPHY = data.readUInt8(9);
1275
- const secondaryPHY = data.readUInt8(10);
1276
- const sid = data.readUInt8(11);
1277
- const txpower = data.readUInt8(12);
1278
- const rssi = data.readInt8(13);
1279
- const periodicAdvInterval = data.readUInt16LE(14);
1280
- const directAddressType =
1281
- data.readUInt8(16) === 0x01 ? 'random' : 'public';
1282
- const directAddress = data
1283
- .slice(17, 23)
1284
- .toString('hex')
1285
- .match(/.{1,2}/g)
1286
- .reverse()
1287
- .join(':');
1288
- const eirLength = data.readUInt8(23);
1289
- const eir = data.slice(24);
1290
-
1291
- debug(`\t\t\ttype = ${type}`);
1292
- debug(`\t\t\taddress = ${address}`);
1293
- debug(`\t\t\taddress type = ${addressType}`);
1294
- debug(`\t\t\tprimary phy = ${primaryPHY.toString(16)}`);
1295
- debug(`\t\t\tsecondary phy = ${secondaryPHY.toString(16)}`);
1296
- debug(`\t\t\tSID = ${sid.toString(16)}`);
1297
- debug(`\t\t\tTX power = ${txpower}`);
1298
- debug(`\t\t\tRSSI = ${rssi}`);
1299
- debug(
1300
- `\t\t\tperiodic advertising interval = ${periodicAdvInterval} msec`
1301
- );
1302
- debug(`\t\t\tdirect address type = ${directAddressType}`);
1303
- debug(`\t\t\tdirect address = ${directAddress}`);
1304
- debug(`\t\t\teir length = ${eirLength}`);
1305
- debug(`\t\t\teir = ${eir.toString('hex')}`);
1306
-
1307
- this.emit(
1308
- 'leExtendedAdvertisingReport',
1309
- 0,
1310
- type,
1311
- address,
1312
- addressType,
1313
- txpower,
1314
- rssi,
1315
- eir
1316
- );
1317
-
1318
- data = data.slice(eirLength + 24);
1319
- }
1320
- } catch (e) {
1321
- console.warn(
1322
- `processLeExtendedAdvertisingReport: Caught illegal packet (buffer overflow): ${e}`
1323
- );
1324
- }
1325
- };
1326
-
1327
- Hci.prototype.processLeConnUpdateComplete = function (status, data) {
1328
- const handle = data.readUInt16LE(0);
1329
- const interval = data.readUInt16LE(2) * 1.25;
1330
- const latency = data.readUInt16LE(4); // TODO: multiplier?
1331
- const supervisionTimeout = data.readUInt16LE(6) * 10;
1332
-
1333
- debug(`\t\t\thandle = ${handle}`);
1334
- debug(`\t\t\tinterval = ${interval}`);
1335
- debug(`\t\t\tlatency = ${latency}`);
1336
- debug(`\t\t\tsupervision timeout = ${supervisionTimeout}`);
1337
-
1338
- this.emit(
1339
- 'leConnUpdateComplete',
1340
- status,
1341
- handle,
1342
- interval,
1343
- latency,
1344
- supervisionTimeout
1345
- );
1346
- };
1347
-
1348
- Hci.prototype.processCmdStatusEvent = function (cmd, status) {
1349
- if (cmd === LE_CREATE_CONN_CMD) {
1350
- if (status !== 0) {
1351
- this.emit('leConnComplete', status);
1352
- }
1353
- }
1354
- };
1355
-
1356
- Hci.prototype.onStateChange = function (state) {
1357
- this._state = state;
1358
- };
1359
-
1360
- module.exports = Hci;