@stoprocent/bleno 0.8.6 → 0.10.0
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.
- package/README.md +197 -30
- package/examples/echo/async.js +47 -0
- package/examples/echo/characteristic.js +15 -17
- package/examples/echo/main.js +19 -1
- package/examples/uart/main.js +3 -2
- package/examples/with-bindings/main.js +35 -0
- package/index.d.ts +166 -121
- package/index.js +5 -1
- package/lib/bleno.js +108 -17
- package/lib/characteristic.js +27 -10
- package/lib/common/include/ThreadSafeCallback.h +95 -0
- package/lib/hci-socket/acl-stream.js +3 -3
- package/lib/hci-socket/bindings.js +36 -29
- package/lib/hci-socket/gatt.js +177 -105
- package/lib/hci-socket/hci.js +5 -3
- package/lib/hci-socket/mgmt.js +1 -1
- package/lib/hci-socket/smp.js +5 -4
- package/lib/mac/binding.gyp +2 -6
- package/lib/mac/bindings.js +2 -3
- package/lib/mac/src/ble_peripheral_manager.h +3 -7
- package/lib/mac/src/ble_peripheral_manager.mm +101 -171
- package/lib/mac/src/bleno_mac.h +0 -1
- package/lib/mac/src/bleno_mac.mm +21 -33
- package/lib/mac/src/callbacks.h +12 -48
- package/lib/mac/src/callbacks.mm +35 -45
- package/lib/mac/src/napi_objc.mm +9 -30
- package/lib/mac/src/objc_cpp.h +2 -2
- package/lib/mac/src/objc_cpp.mm +3 -37
- package/lib/resolve-bindings.js +27 -6
- package/package.json +7 -10
- package/prebuilds/darwin-x64+arm64/@stoprocent+bleno.node +0 -0
- package/prebuilds/win32-ia32/@stoprocent+bleno.node +0 -0
- package/prebuilds/win32-x64/@stoprocent+bleno.node +0 -0
- package/test/characteristic.test.js +158 -11
- package/examples/battery-service/README.md +0 -14
- package/examples/blink1/README.md +0 -44
- package/examples/pizza/README.md +0 -16
- package/lib/mac/src/noble_mac.h +0 -34
- package/lib/mac/src/noble_mac.mm +0 -267
- package/lib/mac/src/peripheral.h +0 -23
- package/with-bindings.js +0 -5
- package/with-custom-binding.js +0 -6
package/lib/hci-socket/gatt.js
CHANGED
|
@@ -68,18 +68,36 @@ class Gatt extends EventEmitter {
|
|
|
68
68
|
super();
|
|
69
69
|
|
|
70
70
|
this.maxMtu = 256;
|
|
71
|
-
|
|
72
|
-
this.
|
|
71
|
+
|
|
72
|
+
this._name = process.env.BLENO_DEVICE_NAME || os.hostname();
|
|
73
|
+
this._connections = new Map();
|
|
74
|
+
this._preparedWriteRequests = new Map();
|
|
75
|
+
this._aclStreamCallbacks = new Map();
|
|
76
|
+
this._lastIndicatedAttributes = new Map();
|
|
77
|
+
this._handles = [];
|
|
73
78
|
|
|
74
79
|
this.setServices([]);
|
|
80
|
+
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
this.
|
|
82
|
+
updateName (name) {
|
|
83
|
+
this._name = name;
|
|
84
|
+
let valueHandle = undefined;
|
|
85
|
+
for (let i = 0; i < this._handles.length; i++) {
|
|
86
|
+
//
|
|
87
|
+
if (typeof this._handles[i] !== 'object') continue;
|
|
88
|
+
// Update the value of the characteristic
|
|
89
|
+
if (this._handles[i].type === 'characteristic' && this._handles[i].uuid === '2a00') {
|
|
90
|
+
this._handles[i].attribute.value = Buffer.from(name);
|
|
91
|
+
valueHandle = this._handles[i].valueHandle;
|
|
92
|
+
}
|
|
93
|
+
// Update the value of the characteristic value
|
|
94
|
+
if (this._handles[i].type === 'characteristicValue' && this._handles[i].handle === valueHandle) {
|
|
95
|
+
this._handles[i].value = Buffer.from(name);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
setServices (services) {
|
|
81
|
-
const deviceName = process.env.BLENO_DEVICE_NAME || os.hostname();
|
|
82
|
-
|
|
83
101
|
// base services and characteristics
|
|
84
102
|
const allServices = [
|
|
85
103
|
{
|
|
@@ -89,7 +107,7 @@ class Gatt extends EventEmitter {
|
|
|
89
107
|
uuid: '2a00',
|
|
90
108
|
properties: ['read'],
|
|
91
109
|
secure: [],
|
|
92
|
-
value: Buffer.from(
|
|
110
|
+
value: Buffer.from(this._name),
|
|
93
111
|
descriptors: []
|
|
94
112
|
},
|
|
95
113
|
{
|
|
@@ -213,7 +231,7 @@ class Gatt extends EventEmitter {
|
|
|
213
231
|
attribute: characteristic,
|
|
214
232
|
properties: (0x02 | 0x04 | 0x08), // read/write
|
|
215
233
|
secure: (secure & 0x10) ? (0x02 | 0x04 | 0x08) : 0,
|
|
216
|
-
|
|
234
|
+
values: new Map()
|
|
217
235
|
};
|
|
218
236
|
}
|
|
219
237
|
|
|
@@ -255,43 +273,68 @@ class Gatt extends EventEmitter {
|
|
|
255
273
|
debug('handles = ' + JSON.stringify(debugHandles, null, 2));
|
|
256
274
|
}
|
|
257
275
|
|
|
258
|
-
|
|
259
|
-
this.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
registerAclStream (aclStream, connection) {
|
|
277
|
+
this._connections.set(connection, {
|
|
278
|
+
aclStream,
|
|
279
|
+
mtu: 23,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
this._aclStreamCallbacks.set(connection, {
|
|
283
|
+
onData: function (connection, cid, data) {
|
|
284
|
+
this.onAclStreamData(connection, cid, data);
|
|
285
|
+
},
|
|
286
|
+
onEnd: function (connection) {
|
|
287
|
+
this.onAclStreamEnd(connection);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
263
290
|
|
|
264
|
-
|
|
265
|
-
|
|
291
|
+
aclStream.on('data', this._aclStreamCallbacks.get(connection).onData.bind(this));
|
|
292
|
+
aclStream.on('end', this._aclStreamCallbacks.get(connection).onEnd.bind(this));
|
|
266
293
|
}
|
|
267
294
|
|
|
268
|
-
onAclStreamData (cid, data) {
|
|
295
|
+
onAclStreamData (connection, cid, data) {
|
|
269
296
|
if (cid !== ATT_CID) {
|
|
270
297
|
return;
|
|
271
298
|
}
|
|
272
299
|
|
|
273
|
-
this.handleRequest(data);
|
|
300
|
+
this.handleRequest(data, connection);
|
|
274
301
|
}
|
|
275
302
|
|
|
276
|
-
onAclStreamEnd () {
|
|
277
|
-
this.
|
|
278
|
-
|
|
303
|
+
onAclStreamEnd (connection) {
|
|
304
|
+
if (this._connections.has(connection) === false) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
279
307
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
308
|
+
const { aclStream } = this._connections.get(connection);
|
|
309
|
+
|
|
310
|
+
aclStream.removeListener('data', this._aclStreamCallbacks.get(connection).onData);
|
|
311
|
+
aclStream.removeListener('end', this._aclStreamCallbacks.get(connection).onEnd);
|
|
284
312
|
|
|
313
|
+
// Clean up subscriptions for this connection
|
|
314
|
+
for (let i = 0; i < this._handles.length; i++) {
|
|
315
|
+
if (this._handles[i] &&
|
|
316
|
+
this._handles[i].type === 'descriptor' &&
|
|
317
|
+
this._handles[i].uuid === '2902' &&
|
|
318
|
+
this._handles[i].values &&
|
|
319
|
+
this._handles[i].values.has(connection)) {
|
|
320
|
+
|
|
321
|
+
this._handles[i].values.delete(connection);
|
|
322
|
+
|
|
285
323
|
if (this._handles[i].attribute && this._handles[i].attribute.emit) {
|
|
286
|
-
this._handles[i].attribute.emit('unsubscribe');
|
|
324
|
+
this._handles[i].attribute.emit('unsubscribe', connection);
|
|
287
325
|
}
|
|
288
326
|
}
|
|
289
327
|
}
|
|
328
|
+
|
|
329
|
+
this._connections.delete(connection);
|
|
330
|
+
this._preparedWriteRequests.delete(connection);
|
|
290
331
|
}
|
|
291
332
|
|
|
292
|
-
send (data) {
|
|
333
|
+
send (data, connection) {
|
|
293
334
|
debug('send: ' + data.toString('hex'));
|
|
294
|
-
this.
|
|
335
|
+
if (this._connections.has(connection)) {
|
|
336
|
+
this._connections.get(connection).aclStream.write(ATT_CID, data);
|
|
337
|
+
}
|
|
295
338
|
}
|
|
296
339
|
|
|
297
340
|
errorResponse (opcode, handle, status) {
|
|
@@ -305,53 +348,53 @@ class Gatt extends EventEmitter {
|
|
|
305
348
|
return buf;
|
|
306
349
|
}
|
|
307
350
|
|
|
308
|
-
handleRequest (request) {
|
|
309
|
-
debug('
|
|
351
|
+
handleRequest (request, connection) {
|
|
352
|
+
debug('handling request: ' + request.toString('hex'));
|
|
310
353
|
|
|
311
354
|
const requestType = request[0];
|
|
312
355
|
let response = null;
|
|
313
356
|
|
|
314
357
|
switch (requestType) {
|
|
315
358
|
case ATT_OP_MTU_REQ:
|
|
316
|
-
response = this.handleMtuRequest(request);
|
|
359
|
+
response = this.handleMtuRequest(request, connection);
|
|
317
360
|
break;
|
|
318
361
|
|
|
319
362
|
case ATT_OP_FIND_INFO_REQ:
|
|
320
|
-
response = this.handleFindInfoRequest(request);
|
|
363
|
+
response = this.handleFindInfoRequest(request, connection);
|
|
321
364
|
break;
|
|
322
365
|
|
|
323
366
|
case ATT_OP_FIND_BY_TYPE_REQ:
|
|
324
|
-
response = this.handleFindByTypeRequest(request);
|
|
367
|
+
response = this.handleFindByTypeRequest(request, connection);
|
|
325
368
|
break;
|
|
326
369
|
|
|
327
370
|
case ATT_OP_READ_BY_TYPE_REQ:
|
|
328
|
-
response = this.handleReadByTypeRequest(request);
|
|
371
|
+
response = this.handleReadByTypeRequest(request, connection);
|
|
329
372
|
break;
|
|
330
373
|
|
|
331
374
|
case ATT_OP_READ_REQ:
|
|
332
375
|
case ATT_OP_READ_BLOB_REQ:
|
|
333
|
-
response = this.handleReadOrReadBlobRequest(request);
|
|
376
|
+
response = this.handleReadOrReadBlobRequest(request, connection);
|
|
334
377
|
break;
|
|
335
378
|
|
|
336
379
|
case ATT_OP_READ_BY_GROUP_REQ:
|
|
337
|
-
response = this.handleReadByGroupRequest(request);
|
|
380
|
+
response = this.handleReadByGroupRequest(request, connection);
|
|
338
381
|
break;
|
|
339
382
|
|
|
340
383
|
case ATT_OP_WRITE_REQ:
|
|
341
384
|
case ATT_OP_WRITE_CMD:
|
|
342
|
-
response = this.handleWriteRequestOrCommand(request);
|
|
385
|
+
response = this.handleWriteRequestOrCommand(request, connection);
|
|
343
386
|
break;
|
|
344
387
|
|
|
345
388
|
case ATT_OP_PREP_WRITE_REQ:
|
|
346
|
-
response = this.handlePrepareWriteRequest(request);
|
|
389
|
+
response = this.handlePrepareWriteRequest(request, connection);
|
|
347
390
|
break;
|
|
348
391
|
|
|
349
392
|
case ATT_OP_EXEC_WRITE_REQ:
|
|
350
|
-
response = this.handleExecuteWriteRequest(request);
|
|
393
|
+
response = this.handleExecuteWriteRequest(request, connection);
|
|
351
394
|
break;
|
|
352
395
|
|
|
353
396
|
case ATT_OP_HANDLE_CNF:
|
|
354
|
-
this.handleConfirmation(request);
|
|
397
|
+
this.handleConfirmation(request, connection);
|
|
355
398
|
break;
|
|
356
399
|
|
|
357
400
|
default:
|
|
@@ -361,12 +404,11 @@ class Gatt extends EventEmitter {
|
|
|
361
404
|
|
|
362
405
|
if (response) {
|
|
363
406
|
debug('response: ' + response.toString('hex'));
|
|
364
|
-
|
|
365
|
-
this.send(response);
|
|
407
|
+
this.send(response, connection);
|
|
366
408
|
}
|
|
367
409
|
}
|
|
368
410
|
|
|
369
|
-
handleMtuRequest (request) {
|
|
411
|
+
handleMtuRequest (request, connection) {
|
|
370
412
|
let mtu = request.readUInt16LE(1);
|
|
371
413
|
|
|
372
414
|
if (mtu < 23) {
|
|
@@ -375,9 +417,11 @@ class Gatt extends EventEmitter {
|
|
|
375
417
|
mtu = this.maxMtu;
|
|
376
418
|
}
|
|
377
419
|
|
|
378
|
-
this.
|
|
420
|
+
if (this._connections.has(connection)) {
|
|
421
|
+
this._connections.get(connection).mtu = mtu;
|
|
422
|
+
}
|
|
379
423
|
|
|
380
|
-
this.emit('mtuChange',
|
|
424
|
+
this.emit('mtuChange', mtu);
|
|
381
425
|
|
|
382
426
|
const response = Buffer.alloc(3);
|
|
383
427
|
|
|
@@ -387,7 +431,9 @@ class Gatt extends EventEmitter {
|
|
|
387
431
|
return response;
|
|
388
432
|
}
|
|
389
433
|
|
|
390
|
-
handleFindInfoRequest (request) {
|
|
434
|
+
handleFindInfoRequest (request, connection) {
|
|
435
|
+
const { mtu } = this._connections.get(connection);
|
|
436
|
+
|
|
391
437
|
const startHandle = request.readUInt16LE(1);
|
|
392
438
|
const endHandle = request.readUInt16LE(3);
|
|
393
439
|
|
|
@@ -435,7 +481,7 @@ class Gatt extends EventEmitter {
|
|
|
435
481
|
}
|
|
436
482
|
|
|
437
483
|
const lengthPerInfo = (uuidSize === 2) ? 4 : 18;
|
|
438
|
-
const maxInfo = Math.floor((
|
|
484
|
+
const maxInfo = Math.floor((mtu - 2) / lengthPerInfo);
|
|
439
485
|
numInfo = Math.min(numInfo, maxInfo);
|
|
440
486
|
|
|
441
487
|
const response = Buffer.alloc(2 + numInfo * lengthPerInfo);
|
|
@@ -459,7 +505,9 @@ class Gatt extends EventEmitter {
|
|
|
459
505
|
return this.errorResponse(ATT_OP_FIND_INFO_REQ, startHandle, ATT_ECODE_ATTR_NOT_FOUND);
|
|
460
506
|
}
|
|
461
507
|
|
|
462
|
-
handleFindByTypeRequest (request) {
|
|
508
|
+
handleFindByTypeRequest (request, connection) {
|
|
509
|
+
const { mtu } = this._connections.get(connection);
|
|
510
|
+
|
|
463
511
|
const startHandle = request.readUInt16LE(1);
|
|
464
512
|
const endHandle = request.readUInt16LE(3);
|
|
465
513
|
const uuid = request.slice(5, 7).toString('hex').match(/.{1,2}/g).reverse().join('');
|
|
@@ -486,7 +534,7 @@ class Gatt extends EventEmitter {
|
|
|
486
534
|
if (handles.length) {
|
|
487
535
|
const lengthPerHandle = 4;
|
|
488
536
|
let numHandles = handles.length;
|
|
489
|
-
const maxHandles = Math.floor((
|
|
537
|
+
const maxHandles = Math.floor((mtu - 1) / lengthPerHandle);
|
|
490
538
|
|
|
491
539
|
numHandles = Math.min(numHandles, maxHandles);
|
|
492
540
|
|
|
@@ -506,7 +554,9 @@ class Gatt extends EventEmitter {
|
|
|
506
554
|
return this.errorResponse(ATT_OP_FIND_BY_TYPE_REQ, startHandle, ATT_ECODE_ATTR_NOT_FOUND);
|
|
507
555
|
}
|
|
508
556
|
|
|
509
|
-
handleReadByGroupRequest (request) {
|
|
557
|
+
handleReadByGroupRequest (request, connection) {
|
|
558
|
+
const { mtu } = this._connections.get(connection);
|
|
559
|
+
|
|
510
560
|
const startHandle = request.readUInt16LE(1);
|
|
511
561
|
const endHandle = request.readUInt16LE(3);
|
|
512
562
|
const uuid = request.slice(5).toString('hex').match(/.{1,2}/g).reverse().join('');
|
|
@@ -541,7 +591,7 @@ class Gatt extends EventEmitter {
|
|
|
541
591
|
}
|
|
542
592
|
|
|
543
593
|
const lengthPerService = (uuidSize === 2) ? 6 : 20;
|
|
544
|
-
const maxServices = Math.floor((
|
|
594
|
+
const maxServices = Math.floor((mtu - 2) / lengthPerService);
|
|
545
595
|
numServices = Math.min(numServices, maxServices);
|
|
546
596
|
|
|
547
597
|
const response = Buffer.alloc(2 + numServices * lengthPerService);
|
|
@@ -569,7 +619,9 @@ class Gatt extends EventEmitter {
|
|
|
569
619
|
return this.errorResponse(ATT_OP_READ_BY_GROUP_REQ, startHandle, ATT_ECODE_UNSUPP_GRP_TYPE);
|
|
570
620
|
}
|
|
571
621
|
|
|
572
|
-
handleReadByTypeRequest (request) {
|
|
622
|
+
handleReadByTypeRequest (request, connection) {
|
|
623
|
+
const { mtu } = this._connections.get(connection);
|
|
624
|
+
|
|
573
625
|
let response = null;
|
|
574
626
|
const requestType = request[0];
|
|
575
627
|
|
|
@@ -606,7 +658,7 @@ class Gatt extends EventEmitter {
|
|
|
606
658
|
}
|
|
607
659
|
|
|
608
660
|
const lengthPerCharacteristic = (uuidSize === 2) ? 7 : 21;
|
|
609
|
-
const maxCharacteristics = Math.floor((
|
|
661
|
+
const maxCharacteristics = Math.floor((mtu - 2) / lengthPerCharacteristic);
|
|
610
662
|
numCharacteristics = Math.min(numCharacteristics, maxCharacteristics);
|
|
611
663
|
|
|
612
664
|
response = Buffer.alloc(2 + numCharacteristics * lengthPerCharacteristic);
|
|
@@ -653,15 +705,15 @@ class Gatt extends EventEmitter {
|
|
|
653
705
|
}
|
|
654
706
|
}
|
|
655
707
|
|
|
656
|
-
if (secure && !this.
|
|
708
|
+
if (secure && !this._connections.get(connection).aclStream.encrypted) {
|
|
657
709
|
response = this.errorResponse(ATT_OP_READ_BY_TYPE_REQ, startHandle, ATT_ECODE_AUTHENTICATION);
|
|
658
710
|
} else if (valueHandle) {
|
|
659
|
-
const callback = (function (valueHandle) {
|
|
711
|
+
const callback = (function (requestType, valueHandle, connection) {
|
|
660
712
|
return function (result, data) {
|
|
661
713
|
let callbackResponse;
|
|
662
714
|
|
|
663
715
|
if (ATT_ECODE_SUCCESS === result) {
|
|
664
|
-
const dataLength = Math.min(data.length,
|
|
716
|
+
const dataLength = Math.min(data.length, mtu - 4);
|
|
665
717
|
callbackResponse = Buffer.alloc(4 + dataLength);
|
|
666
718
|
|
|
667
719
|
callbackResponse[0] = ATT_OP_READ_BY_TYPE_RESP;
|
|
@@ -676,16 +728,22 @@ class Gatt extends EventEmitter {
|
|
|
676
728
|
|
|
677
729
|
debug('read by type response: ' + callbackResponse.toString('hex'));
|
|
678
730
|
|
|
679
|
-
this.send(callbackResponse);
|
|
731
|
+
this.send(callbackResponse, connection);
|
|
680
732
|
}.bind(this);
|
|
681
|
-
}.bind(this))(valueHandle);
|
|
733
|
+
}.bind(this))(requestType, valueHandle, connection);
|
|
682
734
|
|
|
683
|
-
|
|
735
|
+
let data = undefined;
|
|
736
|
+
if (this._handles[valueHandle].values && this._handles[valueHandle].values.has(connection)) {
|
|
737
|
+
data = this._handles[valueHandle].values.get(connection);
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
data = this._handles[valueHandle].value;
|
|
741
|
+
}
|
|
684
742
|
|
|
685
743
|
if (data) {
|
|
686
744
|
callback(ATT_ECODE_SUCCESS, data);
|
|
687
745
|
} else if (handleAttribute) {
|
|
688
|
-
handleAttribute.emit('readRequest', 0, callback);
|
|
746
|
+
handleAttribute.emit('readRequest', connection, 0, callback);
|
|
689
747
|
} else {
|
|
690
748
|
callback(ATT_ECODE_UNLIKELY);
|
|
691
749
|
}
|
|
@@ -697,7 +755,7 @@ class Gatt extends EventEmitter {
|
|
|
697
755
|
return response;
|
|
698
756
|
}
|
|
699
757
|
|
|
700
|
-
handleReadOrReadBlobRequest (request) {
|
|
758
|
+
handleReadOrReadBlobRequest (request, connection) {
|
|
701
759
|
let response = null;
|
|
702
760
|
|
|
703
761
|
const requestType = request[0];
|
|
@@ -711,12 +769,13 @@ class Gatt extends EventEmitter {
|
|
|
711
769
|
let data = null;
|
|
712
770
|
const handleType = handle.type;
|
|
713
771
|
|
|
714
|
-
const callback = (function (requestType, valueHandle) {
|
|
772
|
+
const callback = (function (requestType, valueHandle, connection) {
|
|
715
773
|
return function (result, data) {
|
|
716
774
|
let callbackResponse;
|
|
775
|
+
const { mtu } = this._connections.get(connection);
|
|
717
776
|
|
|
718
777
|
if (ATT_ECODE_SUCCESS === result) {
|
|
719
|
-
const dataLength = Math.min(data.length,
|
|
778
|
+
const dataLength = Math.min(data.length, mtu - 1);
|
|
720
779
|
callbackResponse = Buffer.alloc(1 + dataLength);
|
|
721
780
|
|
|
722
781
|
callbackResponse[0] = (requestType === ATT_OP_READ_BLOB_REQ) ? ATT_OP_READ_BLOB_RESP : ATT_OP_READ_RESP;
|
|
@@ -729,9 +788,9 @@ class Gatt extends EventEmitter {
|
|
|
729
788
|
|
|
730
789
|
debug('read response: ' + callbackResponse.toString('hex'));
|
|
731
790
|
|
|
732
|
-
this.send(callbackResponse);
|
|
791
|
+
this.send(callbackResponse, connection);
|
|
733
792
|
}.bind(this);
|
|
734
|
-
}.bind(this))(requestType, valueHandle);
|
|
793
|
+
}.bind(this))(requestType, valueHandle, connection);
|
|
735
794
|
|
|
736
795
|
if (handleType === 'service' || handleType === 'includedService') {
|
|
737
796
|
result = ATT_ECODE_SUCCESS;
|
|
@@ -758,7 +817,7 @@ class Gatt extends EventEmitter {
|
|
|
758
817
|
}
|
|
759
818
|
|
|
760
819
|
if (handleProperties & 0x02) {
|
|
761
|
-
if (handleSecure & 0x02 && !this.
|
|
820
|
+
if (handleSecure & 0x02 && !this._connections.get(connection).aclStream.encrypted) {
|
|
762
821
|
result = ATT_ECODE_AUTHENTICATION;
|
|
763
822
|
} else {
|
|
764
823
|
data = handle.value;
|
|
@@ -766,7 +825,7 @@ class Gatt extends EventEmitter {
|
|
|
766
825
|
if (data) {
|
|
767
826
|
result = ATT_ECODE_SUCCESS;
|
|
768
827
|
} else {
|
|
769
|
-
handleAttribute.emit('readRequest', offset, callback);
|
|
828
|
+
handleAttribute.emit('readRequest', connection, offset, callback);
|
|
770
829
|
}
|
|
771
830
|
}
|
|
772
831
|
} else {
|
|
@@ -797,7 +856,7 @@ class Gatt extends EventEmitter {
|
|
|
797
856
|
return response;
|
|
798
857
|
}
|
|
799
858
|
|
|
800
|
-
handleWriteRequestOrCommand (request) {
|
|
859
|
+
handleWriteRequestOrCommand (request, connection) {
|
|
801
860
|
let response = null;
|
|
802
861
|
|
|
803
862
|
const requestType = request[0];
|
|
@@ -817,7 +876,7 @@ class Gatt extends EventEmitter {
|
|
|
817
876
|
const handleSecure = handle.secure;
|
|
818
877
|
|
|
819
878
|
if (handleProperties && (withoutResponse ? (handleProperties & 0x04) : (handleProperties & 0x08))) {
|
|
820
|
-
const callback = (function (requestType, valueHandle, withoutResponse) {
|
|
879
|
+
const callback = (function (requestType, valueHandle, withoutResponse, connection) {
|
|
821
880
|
return function (result) {
|
|
822
881
|
if (!withoutResponse) {
|
|
823
882
|
let callbackResponse;
|
|
@@ -830,12 +889,12 @@ class Gatt extends EventEmitter {
|
|
|
830
889
|
|
|
831
890
|
debug('write response: ' + callbackResponse.toString('hex'));
|
|
832
891
|
|
|
833
|
-
this.send(callbackResponse);
|
|
892
|
+
this.send(callbackResponse, connection);
|
|
834
893
|
}
|
|
835
894
|
}.bind(this);
|
|
836
|
-
}.bind(this))(requestType, valueHandle, withoutResponse);
|
|
895
|
+
}.bind(this))(requestType, valueHandle, withoutResponse, connection);
|
|
837
896
|
|
|
838
|
-
if (handleSecure & (withoutResponse ? 0x04 : 0x08) && !this.
|
|
897
|
+
if (handleSecure & (withoutResponse ? 0x04 : 0x08) && !this._connections.get(connection).aclStream.encrypted) {
|
|
839
898
|
response = this.errorResponse(requestType, valueHandle, ATT_ECODE_AUTHENTICATION);
|
|
840
899
|
} else if (handle.type === 'descriptor' || handle.uuid === '2902') {
|
|
841
900
|
let result;
|
|
@@ -846,12 +905,18 @@ class Gatt extends EventEmitter {
|
|
|
846
905
|
const value = data.readUInt16LE(0);
|
|
847
906
|
const handleAttribute = handle.attribute;
|
|
848
907
|
|
|
849
|
-
handle.
|
|
908
|
+
if (handle.values) {
|
|
909
|
+
handle.values.set(connection, data);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
handle.value = data;
|
|
913
|
+
}
|
|
850
914
|
|
|
851
915
|
if (value & 0x0003) {
|
|
852
|
-
const updateValueCallback = (function (valueHandle, attribute) {
|
|
916
|
+
const updateValueCallback = (function (valueHandle, attribute, connection) {
|
|
853
917
|
return function (data) {
|
|
854
|
-
const
|
|
918
|
+
const { mtu } = this._connections.get(connection);
|
|
919
|
+
const dataLength = Math.min(data.length, mtu - 3);
|
|
855
920
|
const useNotify = attribute.properties.indexOf('notify') !== -1;
|
|
856
921
|
const useIndicate = attribute.properties.indexOf('indicate') !== -1;
|
|
857
922
|
|
|
@@ -866,9 +931,9 @@ class Gatt extends EventEmitter {
|
|
|
866
931
|
}
|
|
867
932
|
|
|
868
933
|
debug('notify message: ' + notifyMessage.toString('hex'));
|
|
869
|
-
this.send(notifyMessage);
|
|
934
|
+
this.send(notifyMessage, connection);
|
|
870
935
|
|
|
871
|
-
attribute.emit('notify');
|
|
936
|
+
attribute.emit('notify', connection);
|
|
872
937
|
} else if (useIndicate) {
|
|
873
938
|
const indicateMessage = Buffer.alloc(3 + dataLength);
|
|
874
939
|
|
|
@@ -879,20 +944,21 @@ class Gatt extends EventEmitter {
|
|
|
879
944
|
indicateMessage[3 + i] = data[i];
|
|
880
945
|
}
|
|
881
946
|
|
|
882
|
-
this.
|
|
947
|
+
this._lastIndicatedAttributes.set(connection, attribute);
|
|
883
948
|
|
|
884
949
|
debug('indicate message: ' + indicateMessage.toString('hex'));
|
|
885
|
-
this.send(indicateMessage);
|
|
950
|
+
this.send(indicateMessage, connection);
|
|
886
951
|
}
|
|
887
952
|
}.bind(this);
|
|
888
|
-
}.bind(this))(valueHandle - 1, handleAttribute);
|
|
953
|
+
}.bind(this))(valueHandle - 1, handleAttribute, connection);
|
|
889
954
|
|
|
890
955
|
if (handleAttribute.emit) {
|
|
891
|
-
|
|
956
|
+
const { mtu } = this._connections.get(connection);
|
|
957
|
+
handleAttribute.emit('subscribe', connection, mtu - 3, updateValueCallback);
|
|
892
958
|
}
|
|
893
959
|
} else {
|
|
894
960
|
if (handleAttribute.emit) {
|
|
895
|
-
handleAttribute.emit('unsubscribe');
|
|
961
|
+
handleAttribute.emit('unsubscribe', connection);
|
|
896
962
|
}
|
|
897
963
|
}
|
|
898
964
|
|
|
@@ -901,7 +967,7 @@ class Gatt extends EventEmitter {
|
|
|
901
967
|
|
|
902
968
|
callback(result);
|
|
903
969
|
} else {
|
|
904
|
-
handle.attribute.emit('writeRequest', data, offset, withoutResponse, callback);
|
|
970
|
+
handle.attribute.emit('writeRequest', connection, data, offset, withoutResponse, callback);
|
|
905
971
|
}
|
|
906
972
|
} else {
|
|
907
973
|
response = this.errorResponse(requestType, valueHandle, ATT_ECODE_WRITE_NOT_PERM);
|
|
@@ -913,7 +979,7 @@ class Gatt extends EventEmitter {
|
|
|
913
979
|
return response;
|
|
914
980
|
}
|
|
915
981
|
|
|
916
|
-
handlePrepareWriteRequest (request) {
|
|
982
|
+
handlePrepareWriteRequest (request, connection) {
|
|
917
983
|
let response;
|
|
918
984
|
|
|
919
985
|
const requestType = request[0];
|
|
@@ -931,14 +997,15 @@ class Gatt extends EventEmitter {
|
|
|
931
997
|
const handleSecure = handle.secure;
|
|
932
998
|
|
|
933
999
|
if (handleProperties && (handleProperties & 0x08)) {
|
|
934
|
-
if ((handleSecure & 0x08) && !this.
|
|
1000
|
+
if ((handleSecure & 0x08) && !this._connections.get(connection).aclStream.encrypted) {
|
|
935
1001
|
response = this.errorResponse(requestType, valueHandle, ATT_ECODE_AUTHENTICATION);
|
|
936
|
-
} else if (this.
|
|
937
|
-
|
|
1002
|
+
} else if (this._preparedWriteRequests.has(connection)) {
|
|
1003
|
+
const preparedWriteRequest = this._preparedWriteRequests.get(connection);
|
|
1004
|
+
if (preparedWriteRequest.handle !== handle) {
|
|
938
1005
|
response = this.errorResponse(requestType, valueHandle, ATT_ECODE_UNLIKELY);
|
|
939
|
-
} else if (offset === (
|
|
940
|
-
|
|
941
|
-
|
|
1006
|
+
} else if (offset === (preparedWriteRequest.offset + preparedWriteRequest.data.length)) {
|
|
1007
|
+
preparedWriteRequest.data = Buffer.concat([
|
|
1008
|
+
preparedWriteRequest.data,
|
|
942
1009
|
data
|
|
943
1010
|
]);
|
|
944
1011
|
|
|
@@ -949,12 +1016,12 @@ class Gatt extends EventEmitter {
|
|
|
949
1016
|
response = this.errorResponse(requestType, valueHandle, ATT_ECODE_INVALID_OFFSET);
|
|
950
1017
|
}
|
|
951
1018
|
} else {
|
|
952
|
-
this.
|
|
1019
|
+
this._preparedWriteRequests.set(connection, {
|
|
953
1020
|
handle,
|
|
954
1021
|
valueHandle,
|
|
955
1022
|
offset,
|
|
956
1023
|
data
|
|
957
|
-
};
|
|
1024
|
+
});
|
|
958
1025
|
|
|
959
1026
|
response = Buffer.alloc(request.length);
|
|
960
1027
|
request.copy(response);
|
|
@@ -973,19 +1040,20 @@ class Gatt extends EventEmitter {
|
|
|
973
1040
|
return response;
|
|
974
1041
|
}
|
|
975
1042
|
|
|
976
|
-
handleExecuteWriteRequest (request) {
|
|
1043
|
+
handleExecuteWriteRequest (request, connection) {
|
|
977
1044
|
let response = null;
|
|
978
1045
|
|
|
979
1046
|
const requestType = request[0];
|
|
980
1047
|
const flag = request[1];
|
|
981
1048
|
|
|
982
|
-
if (this.
|
|
983
|
-
const
|
|
1049
|
+
if (this._preparedWriteRequests.has(connection)) {
|
|
1050
|
+
const preparedWriteRequest = this._preparedWriteRequests.get(connection);
|
|
1051
|
+
const valueHandle = preparedWriteRequest.valueHandle;
|
|
984
1052
|
|
|
985
1053
|
if (flag === 0x00) {
|
|
986
1054
|
response = Buffer.from([ATT_OP_EXEC_WRITE_RESP]);
|
|
987
1055
|
} else if (flag === 0x01) {
|
|
988
|
-
const callback = (function (requestType, valueHandle) {
|
|
1056
|
+
const callback = (function (requestType, valueHandle, connection) {
|
|
989
1057
|
return function (result) {
|
|
990
1058
|
let callbackResponse;
|
|
991
1059
|
|
|
@@ -997,16 +1065,16 @@ class Gatt extends EventEmitter {
|
|
|
997
1065
|
|
|
998
1066
|
debug('execute write response: ' + callbackResponse.toString('hex'));
|
|
999
1067
|
|
|
1000
|
-
this.send(callbackResponse);
|
|
1068
|
+
this.send(callbackResponse, connection);
|
|
1001
1069
|
}.bind(this);
|
|
1002
|
-
}.bind(this))(requestType,
|
|
1070
|
+
}.bind(this))(requestType, valueHandle, connection);
|
|
1003
1071
|
|
|
1004
|
-
|
|
1072
|
+
preparedWriteRequest.handle.attribute.emit('writeRequest', connection, preparedWriteRequest.data, preparedWriteRequest.offset, false, callback);
|
|
1005
1073
|
} else {
|
|
1006
1074
|
response = this.errorResponse(requestType, 0x0000, ATT_ECODE_UNLIKELY);
|
|
1007
1075
|
}
|
|
1008
1076
|
|
|
1009
|
-
this.
|
|
1077
|
+
this._preparedWriteRequests.delete(connection);
|
|
1010
1078
|
} else {
|
|
1011
1079
|
response = this.errorResponse(requestType, 0x0000, ATT_ECODE_UNLIKELY);
|
|
1012
1080
|
}
|
|
@@ -1014,14 +1082,18 @@ class Gatt extends EventEmitter {
|
|
|
1014
1082
|
return response;
|
|
1015
1083
|
}
|
|
1016
1084
|
|
|
1017
|
-
handleConfirmation (request) {
|
|
1018
|
-
if (this.
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1085
|
+
handleConfirmation (request, connection) {
|
|
1086
|
+
if (this._lastIndicatedAttributes.has(connection) === false) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1022
1089
|
|
|
1023
|
-
|
|
1090
|
+
const lastIndicatedAttribute = this._lastIndicatedAttributes.get(connection);
|
|
1091
|
+
if (lastIndicatedAttribute.emit) {
|
|
1092
|
+
lastIndicatedAttribute.emit('indicate', connection);
|
|
1024
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
this._lastIndicatedAttributes.delete(connection);
|
|
1096
|
+
this._lastIndicatedAttributes.delete(connection);
|
|
1025
1097
|
}
|
|
1026
1098
|
}
|
|
1027
1099
|
|
package/lib/hci-socket/hci.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const debug = require('debug')('hci');
|
|
2
2
|
|
|
3
3
|
const { EventEmitter } = require('events');
|
|
4
|
+
const { loadDriver } = require('@stoprocent/bluetooth-hci-socket');
|
|
4
5
|
|
|
5
|
-
const BluetoothHciSocket = require('@stoprocent/bluetooth-hci-socket');
|
|
6
6
|
const vendorSpecific = require('./vs');
|
|
7
7
|
|
|
8
8
|
const HCI_COMMAND_PKT = 0x01;
|
|
@@ -77,10 +77,12 @@ const STATUS_MAPPER = require('./hci-status');
|
|
|
77
77
|
class Hci extends EventEmitter {
|
|
78
78
|
constructor (options) {
|
|
79
79
|
super();
|
|
80
|
-
|
|
81
80
|
options = options || {};
|
|
82
|
-
|
|
81
|
+
|
|
82
|
+
const BluetoothHciSocket = loadDriver(options.hciDriver || 'default');
|
|
83
83
|
this._socket = new BluetoothHciSocket();
|
|
84
|
+
|
|
85
|
+
this._manufacturer = null;
|
|
84
86
|
this._isDevUp = null;
|
|
85
87
|
this._state = null;
|
|
86
88
|
this._deviceId = null;
|
package/lib/hci-socket/mgmt.js
CHANGED