@stoprocent/noble 1.9.2-16

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 (112) hide show
  1. package/.editorconfig +11 -0
  2. package/.eslintrc.js +25 -0
  3. package/.github/FUNDING.yml +2 -0
  4. package/.github/workflows/fediverse-action.yml +16 -0
  5. package/.github/workflows/nodepackage.yml +77 -0
  6. package/.github/workflows/npm-publish.yml +26 -0
  7. package/.github/workflows/prebuild.yml +65 -0
  8. package/.nycrc.json +4 -0
  9. package/CHANGELOG.md +119 -0
  10. package/LICENSE +20 -0
  11. package/MAINTAINERS.md +1 -0
  12. package/README.md +833 -0
  13. package/assets/noble-logo.png +0 -0
  14. package/assets/noble-logo.svg +13 -0
  15. package/binding.gyp +19 -0
  16. package/codecov.yml +5 -0
  17. package/examples/advertisement-discovery.js +65 -0
  18. package/examples/cache-gatt-discovery.js +198 -0
  19. package/examples/cache-gatt-reconnect.js +164 -0
  20. package/examples/echo.js +104 -0
  21. package/examples/enter-exit.js +78 -0
  22. package/examples/peripheral-explorer-async.js +133 -0
  23. package/examples/peripheral-explorer.js +225 -0
  24. package/examples/pizza/README.md +15 -0
  25. package/examples/pizza/central.js +194 -0
  26. package/examples/pizza/pizza.js +60 -0
  27. package/index.d.ts +203 -0
  28. package/index.js +6 -0
  29. package/lib/characteristic.js +161 -0
  30. package/lib/characteristics.json +449 -0
  31. package/lib/descriptor.js +72 -0
  32. package/lib/descriptors.json +47 -0
  33. package/lib/distributed/bindings.js +326 -0
  34. package/lib/hci-socket/acl-stream.js +60 -0
  35. package/lib/hci-socket/bindings.js +788 -0
  36. package/lib/hci-socket/crypto.js +74 -0
  37. package/lib/hci-socket/gap.js +432 -0
  38. package/lib/hci-socket/gatt.js +809 -0
  39. package/lib/hci-socket/hci-status.json +71 -0
  40. package/lib/hci-socket/hci.js +1264 -0
  41. package/lib/hci-socket/signaling.js +76 -0
  42. package/lib/hci-socket/smp.js +140 -0
  43. package/lib/hci-uart/bindings.js +569 -0
  44. package/lib/hci-uart/hci-serial-parser.js +70 -0
  45. package/lib/hci-uart/hci.js +1336 -0
  46. package/lib/mac/binding.gyp +26 -0
  47. package/lib/mac/bindings.js +11 -0
  48. package/lib/mac/src/ble_manager.h +41 -0
  49. package/lib/mac/src/ble_manager.mm +435 -0
  50. package/lib/mac/src/callbacks.cc +222 -0
  51. package/lib/mac/src/callbacks.h +84 -0
  52. package/lib/mac/src/napi_objc.h +12 -0
  53. package/lib/mac/src/napi_objc.mm +50 -0
  54. package/lib/mac/src/noble_mac.h +34 -0
  55. package/lib/mac/src/noble_mac.mm +264 -0
  56. package/lib/mac/src/objc_cpp.h +26 -0
  57. package/lib/mac/src/objc_cpp.mm +126 -0
  58. package/lib/mac/src/peripheral.h +23 -0
  59. package/lib/manufacture.js +48 -0
  60. package/lib/noble.js +593 -0
  61. package/lib/peripheral.js +219 -0
  62. package/lib/resolve-bindings-web.js +9 -0
  63. package/lib/resolve-bindings.js +44 -0
  64. package/lib/service.js +72 -0
  65. package/lib/services.json +92 -0
  66. package/lib/webbluetooth/bindings.js +368 -0
  67. package/lib/websocket/bindings.js +321 -0
  68. package/lib/win/binding.gyp +23 -0
  69. package/lib/win/bindings.js +11 -0
  70. package/lib/win/src/ble_manager.cc +802 -0
  71. package/lib/win/src/ble_manager.h +77 -0
  72. package/lib/win/src/callbacks.cc +274 -0
  73. package/lib/win/src/callbacks.h +33 -0
  74. package/lib/win/src/napi_winrt.cc +76 -0
  75. package/lib/win/src/napi_winrt.h +12 -0
  76. package/lib/win/src/noble_winrt.cc +308 -0
  77. package/lib/win/src/noble_winrt.h +34 -0
  78. package/lib/win/src/notify_map.cc +62 -0
  79. package/lib/win/src/notify_map.h +50 -0
  80. package/lib/win/src/peripheral.h +23 -0
  81. package/lib/win/src/peripheral_winrt.cc +296 -0
  82. package/lib/win/src/peripheral_winrt.h +82 -0
  83. package/lib/win/src/radio_watcher.cc +125 -0
  84. package/lib/win/src/radio_watcher.h +61 -0
  85. package/lib/win/src/winrt_cpp.cc +82 -0
  86. package/lib/win/src/winrt_cpp.h +11 -0
  87. package/lib/win/src/winrt_guid.cc +12 -0
  88. package/lib/win/src/winrt_guid.h +13 -0
  89. package/misc/nrf52840dk.hex +6921 -0
  90. package/misc/prj.conf +43 -0
  91. package/package.json +96 -0
  92. package/test/lib/characteristic.test.js +791 -0
  93. package/test/lib/descriptor.test.js +249 -0
  94. package/test/lib/distributed/bindings.test.js +918 -0
  95. package/test/lib/hci-socket/acl-stream.test.js +188 -0
  96. package/test/lib/hci-socket/bindings.test.js +1756 -0
  97. package/test/lib/hci-socket/crypto.test.js +55 -0
  98. package/test/lib/hci-socket/gap.test.js +1089 -0
  99. package/test/lib/hci-socket/gatt.test.js +2392 -0
  100. package/test/lib/hci-socket/hci.test.js +1891 -0
  101. package/test/lib/hci-socket/signaling.test.js +94 -0
  102. package/test/lib/hci-socket/smp.test.js +268 -0
  103. package/test/lib/manufacture.test.js +77 -0
  104. package/test/lib/peripheral.test.js +623 -0
  105. package/test/lib/resolve-bindings.test.js +102 -0
  106. package/test/lib/service.test.js +195 -0
  107. package/test/lib/webbluetooth/bindings.test.js +190 -0
  108. package/test/lib/websocket/bindings.test.js +456 -0
  109. package/test/noble.test.js +1565 -0
  110. package/test.js +131 -0
  111. package/with-bindings.js +5 -0
  112. package/ws-slave.js +404 -0
@@ -0,0 +1,2392 @@
1
+ const should = require('should');
2
+ const sinon = require('sinon');
3
+
4
+ const { assert } = sinon;
5
+
6
+ const Gatt = require('../../../lib/hci-socket/gatt');
7
+
8
+ describe('hci-socket gatt', () => {
9
+ let gatt;
10
+ const address = 'address';
11
+ const aclStream = {
12
+ on: sinon.spy()
13
+ };
14
+
15
+ beforeEach(() => {
16
+ gatt = new Gatt(address, aclStream);
17
+ });
18
+
19
+ afterEach(() => {
20
+ sinon.reset();
21
+ });
22
+
23
+ it('constructor', () => {
24
+ should(gatt._address).equal(address);
25
+ should(gatt._aclStream).deepEqual(aclStream);
26
+ should(gatt._services).deepEqual({});
27
+ should(gatt._characteristics).deepEqual({});
28
+ should(gatt._descriptors).deepEqual({});
29
+ should(gatt._currentCommand).equal(null);
30
+ should(gatt._commandQueue).deepEqual([]);
31
+ should(gatt._mtu).equal(23);
32
+ should(gatt._security).equal('low');
33
+
34
+ assert.callCount(aclStream.on, 4);
35
+ assert.calledWithMatch(aclStream.on, 'data', sinon.match.func);
36
+ assert.calledWithMatch(aclStream.on, 'encrypt', sinon.match.func);
37
+ assert.calledWithMatch(aclStream.on, 'encryptFail', sinon.match.func);
38
+ assert.calledWithMatch(aclStream.on, 'end', sinon.match.func);
39
+ });
40
+
41
+ describe('onAclStreamData', () => {
42
+ const handleNotifyCallback = sinon.spy();
43
+ const handleConfirmationCallback = sinon.spy();
44
+ const notificationCallback = sinon.spy();
45
+
46
+ it('cid !== ATT_CID', () => {
47
+ const cid = 'NOT_ATT_CID';
48
+ const data = Buffer.from([0x00]);
49
+
50
+ // Register events
51
+ gatt.on('handleNotify', handleNotifyCallback);
52
+ gatt.on('handleConfirmation', handleConfirmationCallback);
53
+ gatt.on('notification', notificationCallback);
54
+ gatt.onAclStreamData(cid, data);
55
+
56
+ // No changes
57
+ should(gatt._address).equal(address);
58
+ should(gatt._aclStream).deepEqual(aclStream);
59
+ should(gatt._services).deepEqual({});
60
+ should(gatt._characteristics).deepEqual({});
61
+ should(gatt._descriptors).deepEqual({});
62
+ should(gatt._currentCommand).equal(null);
63
+ should(gatt._commandQueue).deepEqual([]);
64
+ should(gatt._mtu).equal(23);
65
+ should(gatt._security).equal('low');
66
+
67
+ // No events
68
+ assert.notCalled(handleNotifyCallback);
69
+ assert.notCalled(handleConfirmationCallback);
70
+ assert.notCalled(notificationCallback);
71
+ });
72
+
73
+ it('send same current command', () => {
74
+ // ATT_CID
75
+ const cid = 0x0004;
76
+ const data = Buffer.from([0x00]);
77
+ const currentCommand = {
78
+ buffer: data
79
+ };
80
+
81
+ // Setup current command
82
+ gatt._currentCommand = currentCommand;
83
+ // Register events
84
+ gatt.on('handleNotify', handleNotifyCallback);
85
+ gatt.on('handleConfirmation', handleConfirmationCallback);
86
+ gatt.on('notification', notificationCallback);
87
+ gatt.onAclStreamData(cid, data);
88
+
89
+ // No changes
90
+ should(gatt._address).equal(address);
91
+ should(gatt._aclStream).deepEqual(aclStream);
92
+ should(gatt._services).deepEqual({});
93
+ should(gatt._characteristics).deepEqual({});
94
+ should(gatt._descriptors).deepEqual({});
95
+ should(gatt._currentCommand).equal(currentCommand);
96
+ should(gatt._commandQueue).deepEqual([]);
97
+ should(gatt._mtu).equal(23);
98
+ should(gatt._security).equal('low');
99
+
100
+ // No events
101
+ assert.notCalled(handleNotifyCallback);
102
+ assert.notCalled(handleConfirmationCallback);
103
+ assert.notCalled(notificationCallback);
104
+ });
105
+
106
+ it('REQ_NOT_SUPP - not same as current', () => {
107
+ aclStream.write = sinon.spy();
108
+
109
+ // ATT_CID
110
+ const cid = 0x0004;
111
+ const data = Buffer.from([0x00]);
112
+ const currentCommand = {
113
+ buffer: Buffer.from([0xff])
114
+ };
115
+
116
+ // Setup current command
117
+ gatt._currentCommand = currentCommand;
118
+ // Register events
119
+ gatt.on('handleNotify', handleNotifyCallback);
120
+ gatt.on('handleConfirmation', handleConfirmationCallback);
121
+ gatt.on('notification', notificationCallback);
122
+ gatt.onAclStreamData(cid, data);
123
+
124
+ // No changes
125
+ should(gatt._address).equal(address);
126
+ should(gatt._aclStream).deepEqual(aclStream);
127
+ should(gatt._services).deepEqual({});
128
+ should(gatt._characteristics).deepEqual({});
129
+ should(gatt._descriptors).deepEqual({});
130
+ should(gatt._currentCommand).equal(currentCommand);
131
+ should(gatt._commandQueue).deepEqual([]);
132
+ should(gatt._mtu).equal(23);
133
+ should(gatt._security).equal('low');
134
+
135
+ // No events
136
+ assert.notCalled(handleNotifyCallback);
137
+ assert.notCalled(handleConfirmationCallback);
138
+ assert.notCalled(notificationCallback);
139
+
140
+ assert.calledOnceWithExactly(
141
+ aclStream.write,
142
+ 4,
143
+ Buffer.from([0x01, 0x00, 0x00, 0x00, 0x06])
144
+ );
145
+ });
146
+
147
+ it('ATT_OP_HANDLE_NOTIFY', () => {
148
+ // ATT_CID
149
+ const cid = 0x0004;
150
+ const data = Buffer.from([0x1b, 0x01, 0x02, 0x03, 0x04]);
151
+
152
+ const services = { service1: {}, service2: {} };
153
+ const characteristics = {
154
+ service1: {
155
+ char1: {
156
+ valueHandle: 0
157
+ },
158
+ char2: {
159
+ valueHandle: 513
160
+ }
161
+ },
162
+ service2: {
163
+ char3: {
164
+ valueHandle: 513
165
+ }
166
+ }
167
+ };
168
+
169
+ // Setup
170
+ gatt._services = services;
171
+ gatt._characteristics = characteristics;
172
+ // Register events
173
+ gatt.on('handleNotify', handleNotifyCallback);
174
+ gatt.on('handleConfirmation', handleConfirmationCallback);
175
+ gatt.on('notification', notificationCallback);
176
+ gatt.onAclStreamData(cid, data);
177
+
178
+ // No changes
179
+ should(gatt._address).equal(address);
180
+ should(gatt._aclStream).deepEqual(aclStream);
181
+ should(gatt._services).deepEqual(services);
182
+ should(gatt._characteristics).deepEqual(characteristics);
183
+ should(gatt._descriptors).deepEqual({});
184
+ should(gatt._currentCommand).equal(null);
185
+ should(gatt._commandQueue).deepEqual([]);
186
+ should(gatt._mtu).equal(23);
187
+ should(gatt._security).equal('low');
188
+
189
+ // Events
190
+ assert.calledOnceWithExactly(
191
+ handleNotifyCallback,
192
+ address,
193
+ 513,
194
+ Buffer.from([0x03, 0x04])
195
+ );
196
+ assert.notCalled(handleConfirmationCallback);
197
+ assert.callCount(notificationCallback, 2);
198
+ assert.calledWithExactly(
199
+ notificationCallback,
200
+ address,
201
+ 'service1',
202
+ 'char2',
203
+ Buffer.from([0x03, 0x04])
204
+ );
205
+ assert.calledWithExactly(
206
+ notificationCallback,
207
+ address,
208
+ 'service1',
209
+ 'char2',
210
+ Buffer.from([0x03, 0x04])
211
+ );
212
+ });
213
+
214
+ it('ATT_OP_HANDLE_IND', () => {
215
+ // ATT_CID
216
+ const cid = 0x0004;
217
+ const data = Buffer.from([0x1d, 0x01, 0x02, 0x03, 0x04]);
218
+
219
+ const services = { service1: {}, service2: {} };
220
+ const characteristics = {
221
+ service1: {
222
+ char1: {
223
+ valueHandle: 0
224
+ },
225
+ char2: {
226
+ valueHandle: 513
227
+ }
228
+ },
229
+ service2: {
230
+ char3: {
231
+ valueHandle: 513
232
+ }
233
+ }
234
+ };
235
+
236
+ // Setup
237
+ gatt._currentCommand = { buffer: Buffer.from([0x01]) };
238
+ gatt._services = services;
239
+ gatt._characteristics = characteristics;
240
+ // Register events
241
+ gatt.on('handleNotify', handleNotifyCallback);
242
+ gatt.on('handleConfirmation', handleConfirmationCallback);
243
+ gatt.on('notification', notificationCallback);
244
+ gatt.onAclStreamData(cid, data);
245
+
246
+ // No changes
247
+ should(gatt._address).equal(address);
248
+ should(gatt._aclStream).deepEqual(aclStream);
249
+ should(gatt._services).deepEqual(services);
250
+ should(gatt._characteristics).deepEqual(characteristics);
251
+ should(gatt._descriptors).deepEqual({});
252
+ should(gatt._currentCommand).deepEqual({ buffer: Buffer.from([0x01]) });
253
+ should(gatt._commandQueue).has.size(1);
254
+ should(gatt._mtu).equal(23);
255
+ should(gatt._security).equal('low');
256
+
257
+ // Events
258
+ assert.calledOnceWithExactly(
259
+ handleNotifyCallback,
260
+ address,
261
+ 513,
262
+ Buffer.from([0x03, 0x04])
263
+ );
264
+ assert.notCalled(handleConfirmationCallback);
265
+ assert.callCount(notificationCallback, 2);
266
+ assert.calledWithExactly(
267
+ notificationCallback,
268
+ address,
269
+ 'service1',
270
+ 'char2',
271
+ Buffer.from([0x03, 0x04])
272
+ );
273
+ assert.calledWithExactly(
274
+ notificationCallback,
275
+ address,
276
+ 'service1',
277
+ 'char2',
278
+ Buffer.from([0x03, 0x04])
279
+ );
280
+ });
281
+
282
+ it('no current command', () => {
283
+ // ATT_CID
284
+ const cid = 0x0004;
285
+ const data = Buffer.from([0xFF, 0x01, 0x02, 0x03, 0x04]);
286
+
287
+ // Register events
288
+ gatt.on('handleNotify', handleNotifyCallback);
289
+ gatt.on('handleConfirmation', handleConfirmationCallback);
290
+ gatt.on('notification', notificationCallback);
291
+ gatt.onAclStreamData(cid, data);
292
+
293
+ // No changes
294
+ should(gatt._address).equal(address);
295
+ should(gatt._aclStream).deepEqual(aclStream);
296
+ should(gatt._services).deepEqual({});
297
+ should(gatt._characteristics).deepEqual({});
298
+ should(gatt._descriptors).deepEqual({});
299
+ should(gatt._currentCommand).equal(null);
300
+ should(gatt._commandQueue).deepEqual([]);
301
+ should(gatt._mtu).equal(23);
302
+ should(gatt._security).equal('low');
303
+
304
+ // Events
305
+ assert.notCalled(handleNotifyCallback);
306
+ assert.notCalled(handleConfirmationCallback);
307
+ assert.notCalled(notificationCallback);
308
+ });
309
+
310
+ [0x05, 0x08, 0x0f].forEach((errorCode) => {
311
+ it(`ATT_OP_ERROR ${errorCode} and security low`, () => {
312
+ aclStream.encrypt = sinon.spy();
313
+
314
+ // ATT_CID
315
+ const cid = 0x0004;
316
+ const data = Buffer.from([0x01, 0x01, 0x02, 0x03, errorCode]);
317
+
318
+ // Setup
319
+ gatt._currentCommand = { buffer: Buffer.from([0x00]) };
320
+ // Register events
321
+ gatt.on('handleNotify', handleNotifyCallback);
322
+ gatt.on('handleConfirmation', handleConfirmationCallback);
323
+ gatt.on('notification', notificationCallback);
324
+ gatt.onAclStreamData(cid, data);
325
+
326
+ // No changes
327
+ should(gatt._address).equal(address);
328
+ should(gatt._aclStream).deepEqual(aclStream);
329
+ should(gatt._services).deepEqual({});
330
+ should(gatt._characteristics).deepEqual({});
331
+ should(gatt._descriptors).deepEqual({});
332
+ should(gatt._currentCommand).deepEqual({ buffer: Buffer.from([0x00]) });
333
+ should(gatt._commandQueue).deepEqual([]);
334
+ should(gatt._mtu).equal(23);
335
+ should(gatt._security).equal('low');
336
+
337
+ // Events
338
+ assert.notCalled(handleNotifyCallback);
339
+ assert.notCalled(handleConfirmationCallback);
340
+ assert.notCalled(notificationCallback);
341
+
342
+ assert.calledOnceWithExactly(aclStream.encrypt);
343
+ });
344
+
345
+ it(`ATT_OP_ERROR ${errorCode} and security medium`, () => {
346
+ // ATT_CID
347
+ const cid = 0x0004;
348
+ const data = Buffer.from([0x01, 0x01, 0x02, 0x03, errorCode]);
349
+
350
+ const callback = sinon.spy();
351
+ const currentCommand = { buffer: Buffer.from([0x00]), callback };
352
+
353
+ // Setup
354
+ gatt._currentCommand = currentCommand;
355
+ gatt._security = 'medium';
356
+ // Register events
357
+ gatt.on('handleNotify', handleNotifyCallback);
358
+ gatt.on('handleConfirmation', handleConfirmationCallback);
359
+ gatt.on('notification', notificationCallback);
360
+ gatt.onAclStreamData(cid, data);
361
+
362
+ // No changes
363
+ should(gatt._address).equal(address);
364
+ should(gatt._aclStream).deepEqual(aclStream);
365
+ should(gatt._services).deepEqual({});
366
+ should(gatt._characteristics).deepEqual({});
367
+ should(gatt._descriptors).deepEqual({});
368
+ should(gatt._currentCommand).equal(null);
369
+ should(gatt._commandQueue).deepEqual([]);
370
+ should(gatt._mtu).equal(23);
371
+ should(gatt._security).equal('medium');
372
+
373
+ // Events
374
+ assert.notCalled(handleNotifyCallback);
375
+ assert.notCalled(handleConfirmationCallback);
376
+ assert.notCalled(notificationCallback);
377
+
378
+ assert.calledOnceWithExactly(callback, data);
379
+ });
380
+ });
381
+
382
+ it('command callback with queue', () => {
383
+ // ATT_CID
384
+ const cid = 0x0004;
385
+ const data = Buffer.from([0x01, 0x01, 0x02, 0x03, 0x00]);
386
+
387
+ const callback = sinon.spy();
388
+ const currentCommand = { buffer: Buffer.from([0x00]), callback };
389
+ const queueCallback = sinon.spy();
390
+ const queueWriteCallback = sinon.spy();
391
+ const commandQueue = [{ buffer: Buffer.from([0x98]), callback: queueCallback }, { buffer: Buffer.from([0x99]), writeCallback: queueWriteCallback }];
392
+
393
+ // Setup
394
+ gatt._currentCommand = currentCommand;
395
+ gatt._commandQueue = [...commandQueue];
396
+ // Register events
397
+ gatt.on('handleNotify', handleNotifyCallback);
398
+ gatt.on('handleConfirmation', handleConfirmationCallback);
399
+ gatt.on('notification', notificationCallback);
400
+ gatt.onAclStreamData(cid, data);
401
+
402
+ // No changes
403
+ should(gatt._address).equal(address);
404
+ should(gatt._aclStream).deepEqual(aclStream);
405
+ should(gatt._services).deepEqual({});
406
+ should(gatt._characteristics).deepEqual({});
407
+ should(gatt._descriptors).deepEqual({});
408
+ should(gatt._currentCommand).deepEqual(commandQueue[0]);
409
+ should(gatt._commandQueue).deepEqual([commandQueue[1]]);
410
+ should(gatt._mtu).equal(23);
411
+ should(gatt._security).equal('low');
412
+
413
+ // Events
414
+ assert.notCalled(handleNotifyCallback);
415
+ assert.notCalled(handleConfirmationCallback);
416
+ assert.notCalled(notificationCallback);
417
+
418
+ assert.calledOnceWithExactly(callback, data);
419
+ assert.notCalled(queueCallback);
420
+ assert.notCalled(queueWriteCallback);
421
+
422
+ assert.calledOnceWithExactly(aclStream.write, 4, Buffer.from([0x98]));
423
+ });
424
+
425
+ it('write command callback with queue', () => {
426
+ // ATT_CID
427
+ const cid = 0x0004;
428
+ const data = Buffer.from([0x01, 0x01, 0x02, 0x03, 0x00]);
429
+
430
+ const callback = sinon.spy();
431
+ const currentCommand = { buffer: Buffer.from([0x00]), callback };
432
+ const queueCallback = sinon.spy();
433
+ const queueWriteCallback = sinon.spy();
434
+ const commandQueue = [{ buffer: Buffer.from([0x99]), writeCallback: queueWriteCallback }, { buffer: Buffer.from([0x98]), callback: queueCallback }];
435
+
436
+ // Setup
437
+ gatt._currentCommand = currentCommand;
438
+ gatt._commandQueue = [...commandQueue];
439
+ // Register events
440
+ gatt.on('handleNotify', handleNotifyCallback);
441
+ gatt.on('handleConfirmation', handleConfirmationCallback);
442
+ gatt.on('notification', notificationCallback);
443
+ gatt.onAclStreamData(cid, data);
444
+
445
+ // No changes
446
+ should(gatt._address).equal(address);
447
+ should(gatt._aclStream).deepEqual(aclStream);
448
+ should(gatt._services).deepEqual({});
449
+ should(gatt._characteristics).deepEqual({});
450
+ should(gatt._descriptors).deepEqual({});
451
+ should(gatt._currentCommand).deepEqual(commandQueue[1]);
452
+ should(gatt._commandQueue).deepEqual([]);
453
+ should(gatt._mtu).equal(23);
454
+ should(gatt._security).equal('low');
455
+
456
+ // Events
457
+ assert.notCalled(handleNotifyCallback);
458
+ assert.notCalled(handleConfirmationCallback);
459
+ assert.notCalled(notificationCallback);
460
+
461
+ assert.calledOnceWithExactly(callback, data);
462
+ assert.notCalled(queueCallback);
463
+ assert.calledOnceWithExactly(queueWriteCallback);
464
+
465
+ assert.callCount(aclStream.write, 2);
466
+ assert.calledWithExactly(aclStream.write, 4, Buffer.from([0x98]));
467
+ assert.calledWithExactly(aclStream.write, 4, Buffer.from([0x99]));
468
+ });
469
+ });
470
+
471
+ describe('onAclStreamEncrypt', () => {
472
+ it('should not write attribute', () => {
473
+ aclStream.write = sinon.spy();
474
+
475
+ // Setup
476
+ gatt._security = 'low';
477
+ gatt.onAclStreamEncrypt(false);
478
+
479
+ should(gatt._security).equal('low');
480
+ assert.notCalled(aclStream.write);
481
+ });
482
+
483
+ it('should write attribute', () => {
484
+ aclStream.write = sinon.spy();
485
+
486
+ const buffer = Buffer.from([0x01, 0x99]);
487
+
488
+ // Setup
489
+ gatt._security = 'low';
490
+ gatt._currentCommand = { buffer };
491
+ gatt.onAclStreamEncrypt(true);
492
+
493
+ should(gatt._security).equal('medium');
494
+ assert.calledOnceWithExactly(aclStream.write, 4, buffer);
495
+ });
496
+ });
497
+
498
+ it('onAclStreamEnd should remove listeners', () => {
499
+ aclStream.removeListener = sinon.spy();
500
+
501
+ gatt.onAclStreamEnd();
502
+
503
+ assert.callCount(aclStream.removeListener, 4);
504
+ assert.calledWithMatch(aclStream.removeListener, 'data', sinon.match.func);
505
+ assert.calledWithMatch(aclStream.removeListener, 'encrypt', sinon.match.func);
506
+ assert.calledWithMatch(aclStream.removeListener, 'encryptFail', sinon.match.func);
507
+ assert.calledWithMatch(aclStream.removeListener, 'end', sinon.match.func);
508
+ });
509
+
510
+ it('writeAtt should call acl write', () => {
511
+ aclStream.write = sinon.spy();
512
+
513
+ gatt.writeAtt('data');
514
+
515
+ assert.calledOnceWithMatch(aclStream.write, 4, 'data');
516
+ });
517
+
518
+ it('errorResponse', () => {
519
+ aclStream.write = sinon.spy();
520
+
521
+ const opcode = 8;
522
+ const handle = 3000;
523
+ const status = 7;
524
+
525
+ const result = gatt.errorResponse(opcode, handle, status);
526
+
527
+ should(result).deepEqual(Buffer.from([0x01, 0x08, 0xb8, 0x0b, 0x07]));
528
+ });
529
+
530
+ describe('queueCommand', () => {
531
+ it('should only queue', () => {
532
+ const buffer = Buffer.from([0x01, 0x01, 0x02, 0x03, 0x00]);
533
+ const callback = sinon.spy();
534
+ const writeCallback = sinon.spy();
535
+
536
+ // Setup
537
+ gatt._currentCommand = 'command';
538
+ gatt._queueCommand(buffer, callback, writeCallback);
539
+
540
+ // No changes
541
+ should(gatt._address).equal(address);
542
+ should(gatt._aclStream).deepEqual(aclStream);
543
+ should(gatt._services).deepEqual({});
544
+ should(gatt._characteristics).deepEqual({});
545
+ should(gatt._descriptors).deepEqual({});
546
+ should(gatt._currentCommand).equal('command');
547
+ should(gatt._commandQueue).deepEqual([{ buffer, callback, writeCallback }]);
548
+ should(gatt._mtu).equal(23);
549
+
550
+ assert.notCalled(callback);
551
+ assert.notCalled(writeCallback);
552
+ });
553
+
554
+ it('should queue and unqueue', () => {
555
+ aclStream.write = sinon.spy();
556
+
557
+ const buffer = Buffer.from([0x01, 0x01, 0x02, 0x03, 0x00]);
558
+ const callback = sinon.spy();
559
+ const writeCallback = sinon.spy();
560
+
561
+ const queueCallback = sinon.spy();
562
+ const queueWriteCallback = sinon.spy();
563
+ const commandQueue = [{ buffer: Buffer.from([0x98]), callback: queueCallback }, { buffer: Buffer.from([0x99]), writeCallback: queueWriteCallback }];
564
+
565
+ // Setup
566
+ gatt._commandQueue = [...commandQueue];
567
+ gatt._queueCommand(buffer, callback, writeCallback);
568
+
569
+ // No changes
570
+ should(gatt._address).equal(address);
571
+ should(gatt._aclStream).deepEqual(aclStream);
572
+ should(gatt._services).deepEqual({});
573
+ should(gatt._characteristics).deepEqual({});
574
+ should(gatt._descriptors).deepEqual({});
575
+ should(gatt._currentCommand).deepEqual(commandQueue[0]);
576
+ should(gatt._commandQueue).deepEqual([commandQueue[1], { buffer, callback, writeCallback }]);
577
+ should(gatt._mtu).equal(23);
578
+ should(gatt._security).equal('low');
579
+
580
+ assert.notCalled(queueCallback);
581
+ assert.notCalled(queueWriteCallback);
582
+
583
+ assert.calledOnceWithExactly(aclStream.write, 4, Buffer.from([0x98]));
584
+ });
585
+
586
+ it('write command callback with queue', () => {
587
+ aclStream.write = sinon.spy();
588
+
589
+ const buffer = Buffer.from([0x01, 0x01, 0x02, 0x03, 0x00]);
590
+ const callback = sinon.spy();
591
+ const writeCallback = sinon.spy();
592
+
593
+ const queueCallback = sinon.spy();
594
+ const queueWriteCallback = sinon.spy();
595
+ const commandQueue = [{ buffer: Buffer.from([0x99]), writeCallback: queueWriteCallback }, { buffer: Buffer.from([0x98]), callback: queueCallback }];
596
+
597
+ // Setup
598
+ gatt._commandQueue = [...commandQueue];
599
+ gatt._queueCommand(buffer, callback, writeCallback);
600
+
601
+ // No changes
602
+ should(gatt._address).equal(address);
603
+ should(gatt._aclStream).deepEqual(aclStream);
604
+ should(gatt._services).deepEqual({});
605
+ should(gatt._characteristics).deepEqual({});
606
+ should(gatt._descriptors).deepEqual({});
607
+ should(gatt._currentCommand).deepEqual(commandQueue[1]);
608
+ should(gatt._commandQueue).deepEqual([{ buffer, callback, writeCallback }]);
609
+ should(gatt._mtu).equal(23);
610
+ should(gatt._security).equal('low');
611
+
612
+ assert.notCalled(queueCallback);
613
+ assert.calledOnceWithExactly(queueWriteCallback);
614
+
615
+ assert.callCount(aclStream.write, 2);
616
+ assert.calledWithExactly(aclStream.write, 4, Buffer.from([0x98]));
617
+ assert.calledWithExactly(aclStream.write, 4, Buffer.from([0x99]));
618
+ });
619
+ });
620
+
621
+ it('mtuRequest', () => {
622
+ const mtu = 67;
623
+
624
+ const result = gatt.mtuRequest(mtu);
625
+
626
+ should(result).deepEqual(Buffer.from([0x02, 0x43, 0x00]));
627
+ });
628
+
629
+ it('readByGroupRequest', () => {
630
+ const startHandle = 8;
631
+ const endHandle = 3000;
632
+ const groupUuid = 7;
633
+
634
+ const result = gatt.readByGroupRequest(startHandle, endHandle, groupUuid);
635
+
636
+ should(result).deepEqual(Buffer.from([0x10, 0x08, 0x00, 0xb8, 0x0b, 0x07, 0x00]));
637
+ });
638
+
639
+ it('readByTypeRequest', () => {
640
+ const startHandle = 8;
641
+ const endHandle = 3000;
642
+ const groupUuid = 7;
643
+
644
+ const result = gatt.readByTypeRequest(startHandle, endHandle, groupUuid);
645
+
646
+ should(result).deepEqual(Buffer.from([0x08, 0x08, 0x00, 0xb8, 0x0b, 0x07, 0x00]));
647
+ });
648
+
649
+ it('readRequest', () => {
650
+ const handle = 67;
651
+
652
+ const result = gatt.readRequest(handle);
653
+
654
+ should(result).deepEqual(Buffer.from([0x0a, 0x43, 0x00]));
655
+ });
656
+
657
+ it('readBlobRequest', () => {
658
+ const handle = 67;
659
+ const offset = 68;
660
+
661
+ const result = gatt.readBlobRequest(handle, offset);
662
+
663
+ should(result).deepEqual(Buffer.from([0x0c, 0x43, 0x00, 0x44, 0x00]));
664
+ });
665
+
666
+ it('findInfoRequest', () => {
667
+ const startHandle = 67;
668
+ const endHandle = 68;
669
+
670
+ const result = gatt.findInfoRequest(startHandle, endHandle);
671
+
672
+ should(result).deepEqual(Buffer.from([0x04, 0x43, 0x00, 0x44, 0x00]));
673
+ });
674
+
675
+ it('writeRequest withoutResponse', () => {
676
+ const handle = 67;
677
+ const data = Buffer.from([0x05, 0x06, 0x07]);
678
+ const withoutResponse = true;
679
+
680
+ const result = gatt.writeRequest(handle, data, withoutResponse);
681
+
682
+ should(result).deepEqual(Buffer.from([0x52, 0x43, 0x00, 0x05, 0x06, 0x07]));
683
+ });
684
+
685
+ it('writeRequest withResponse', () => {
686
+ const handle = 67;
687
+ const data = Buffer.from([0x05, 0x06, 0x07]);
688
+ const withResponse = false;
689
+
690
+ const result = gatt.writeRequest(handle, data, withResponse);
691
+
692
+ should(result).deepEqual(Buffer.from([0x12, 0x43, 0x00, 0x05, 0x06, 0x07]));
693
+ });
694
+
695
+ it('prepareWriteRequest', () => {
696
+ const handle = 67;
697
+ const offset = 68;
698
+ const data = Buffer.from([0x05, 0x06, 0x07]);
699
+
700
+ const result = gatt.prepareWriteRequest(handle, offset, data);
701
+
702
+ should(result).deepEqual(Buffer.from([0x16, 0x43, 0x00, 0x44, 0x00, 0x05, 0x06, 0x07]));
703
+ });
704
+
705
+ it('executeWriteRequest cancelPreparedWrites', () => {
706
+ const handle = 67;
707
+ const cancelPreparedWrites = true;
708
+
709
+ const result = gatt.executeWriteRequest(handle, cancelPreparedWrites);
710
+
711
+ should(result).deepEqual(Buffer.from([0x18, 0x00]));
712
+ });
713
+
714
+ it('executeWriteRequest preparedWrites', () => {
715
+ const handle = 67;
716
+ const cancelPreparedWrites = false;
717
+
718
+ const result = gatt.executeWriteRequest(handle, cancelPreparedWrites);
719
+
720
+ should(result).deepEqual(Buffer.from([0x18, 0x01]));
721
+ });
722
+
723
+ it('handleConfirmation', () => {
724
+ const result = gatt.handleConfirmation();
725
+
726
+ should(result).deepEqual(Buffer.from([0x1e]));
727
+ });
728
+
729
+ it('exchangeMtu', () => {
730
+ const mtu = 63;
731
+ const queueCommand = sinon.spy();
732
+ const callback = sinon.stub();
733
+
734
+ gatt._queueCommand = queueCommand;
735
+ gatt._mtu = 22;
736
+ gatt.on('mtu', callback);
737
+ gatt.exchangeMtu(mtu);
738
+
739
+ assert.calledOnce(queueCommand);
740
+
741
+ queueCommand.callArgWith(1, ['d', 'a', 't', 'a']);
742
+ assert.calledOnceWithExactly(callback, address, 22);
743
+ should(gatt._mtu).equal(22);
744
+ });
745
+
746
+ it('exchangeMtu ATT_OP_MTU_RESP', () => {
747
+ const mtu = 63;
748
+ const queueCommand = sinon.spy();
749
+ const callback = sinon.stub();
750
+
751
+ gatt._queueCommand = queueCommand;
752
+ gatt._mtu = 22;
753
+ gatt.on('mtu', callback);
754
+ gatt.exchangeMtu(mtu);
755
+
756
+ assert.calledOnce(queueCommand);
757
+
758
+ queueCommand.callArgWith(1, Buffer.from([0x03, 0x12, 0x33]));
759
+ assert.calledOnceWithExactly(callback, address, 13074);
760
+ should(gatt._mtu).equal(13074);
761
+ });
762
+
763
+ it('addService', () => {
764
+ const service = { uuid: 'service' };
765
+
766
+ gatt.addService(service);
767
+
768
+ should(gatt._services).deepEqual({ service });
769
+ });
770
+
771
+ describe('discoverServices', () => {
772
+ beforeEach(() => {
773
+ gatt._queueCommand = sinon.spy();
774
+ gatt.readByGroupRequest = sinon.spy();
775
+ });
776
+
777
+ it('not ATT_OP_READ_BY_GROUP_RESP > non discovered', () => {
778
+ const callbackDiscovered = sinon.stub();
779
+ const callbackDiscover = sinon.stub();
780
+
781
+ gatt.on('servicesDiscovered', callbackDiscovered);
782
+ gatt.on('servicesDiscover', callbackDiscover);
783
+ gatt.discoverServices(['service1']);
784
+
785
+ gatt._queueCommand.callArgWith(1, Buffer.from([0x00]));
786
+
787
+ assert.calledOnceWithExactly(callbackDiscovered, address, []);
788
+ assert.calledOnceWithExactly(callbackDiscover, address, []);
789
+ assert.callCount(gatt._queueCommand, 1);
790
+ assert.calledOnceWithExactly(gatt.readByGroupRequest, 0x0001, 0xffff, 10240);
791
+ });
792
+
793
+ it('ATT_OP_READ_BY_GROUP_RESP > queue', () => {
794
+ const callbackDiscovered = sinon.stub();
795
+ const callbackDiscover = sinon.stub();
796
+
797
+ gatt.on('servicesDiscovered', callbackDiscovered);
798
+ gatt.on('servicesDiscover', callbackDiscover);
799
+ gatt.discoverServices(['service1']);
800
+
801
+ // Build data
802
+ const data = [17, 6];
803
+ for (let i = 0; i < data[1]; i++) {
804
+ for (let j = 0; j < 8; j++) {
805
+ data.push(i * 10 + j);
806
+ }
807
+ }
808
+
809
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
810
+
811
+ assert.notCalled(callbackDiscovered);
812
+ assert.notCalled(callbackDiscover);
813
+ assert.callCount(gatt._queueCommand, 2);
814
+ assert.callCount(gatt.readByGroupRequest, 2);
815
+ assert.calledWithExactly(gatt.readByGroupRequest, 0x0001, 0xffff, 10240);
816
+ assert.calledWithExactly(gatt.readByGroupRequest, 14135, 0xffff, 10240);
817
+ });
818
+
819
+ it('ATT_OP_READ_BY_GROUP_RESP > event', () => {
820
+ const callbackDiscovered = sinon.stub();
821
+ const callbackDiscover = sinon.stub();
822
+
823
+ gatt.on('servicesDiscovered', callbackDiscovered);
824
+ gatt.on('servicesDiscover', callbackDiscover);
825
+ gatt.discoverServices([]);
826
+
827
+ // Build data
828
+ const data = [17, 7];
829
+ for (let i = 0; i < data[1]; i++) {
830
+ for (let j = 0; j < 8; j++) {
831
+ data.push(255);
832
+ }
833
+ }
834
+
835
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
836
+
837
+ const services = [{
838
+ startHandle: 65535,
839
+ endHandle: 65535,
840
+ uuid: 'ffffffffffffffffffffffffffffffff'
841
+ },
842
+ {
843
+ startHandle: 65535,
844
+ endHandle: 65535,
845
+ uuid: 'ffffffffffffffffffffffffffffffff'
846
+ },
847
+ {
848
+ startHandle: 65535,
849
+ endHandle: 65535,
850
+ uuid: 'ffffffffffffffffffffffffffffffff'
851
+ },
852
+ {
853
+ startHandle: 65535,
854
+ endHandle: 65535,
855
+ uuid: 'ffffffffffffffffffffffffffffffff'
856
+ },
857
+ {
858
+ startHandle: 65535,
859
+ endHandle: 65535,
860
+ uuid: 'ffffffffffffffffffffffffffffffff'
861
+ },
862
+ {
863
+ startHandle: 65535,
864
+ endHandle: 65535,
865
+ uuid: 'ffffffffffffffffffffffffffffffff'
866
+ },
867
+ {
868
+ startHandle: 65535,
869
+ endHandle: 65535,
870
+ uuid: 'ffffffffffffffffffff'
871
+ },
872
+ { startHandle: 65535, endHandle: 65535, uuid: 'ffffff' }];
873
+
874
+ assert.calledOnceWithExactly(callbackDiscovered, address, services);
875
+ const serviceUuids = [
876
+ 'ffffffffffffffffffffffffffffffff',
877
+ 'ffffffffffffffffffff',
878
+ 'ffffff'
879
+ ];
880
+ assert.calledOnceWithExactly(callbackDiscover, address, serviceUuids);
881
+ assert.callCount(gatt._queueCommand, 1);
882
+ assert.calledOnceWithExactly(gatt.readByGroupRequest, 0x0001, 0xffff, 10240);
883
+
884
+ should(gatt._services).deepEqual({
885
+ ffffffffffffffffffffffffffffffff: {
886
+ startHandle: 65535,
887
+ endHandle: 65535,
888
+ uuid: 'ffffffffffffffffffffffffffffffff'
889
+ },
890
+ ffffffffffffffffffff: {
891
+ startHandle: 65535,
892
+ endHandle: 65535,
893
+ uuid: 'ffffffffffffffffffff'
894
+ },
895
+ ffffff: {
896
+ startHandle: 65535,
897
+ endHandle: 65535,
898
+ uuid: 'ffffff'
899
+ }
900
+ });
901
+ });
902
+
903
+ it('ATT_OP_READ_BY_GROUP_RESP > event matching uuid', () => {
904
+ const callbackDiscovered = sinon.stub();
905
+ const callbackDiscover = sinon.stub();
906
+
907
+ gatt.on('servicesDiscovered', callbackDiscovered);
908
+ gatt.on('servicesDiscover', callbackDiscover);
909
+ gatt.discoverServices(['ffffffffffffffffffff']);
910
+
911
+ // Build data
912
+ const data = [17, 7];
913
+ for (let i = 0; i < data[1]; i++) {
914
+ for (let j = 0; j < 8; j++) {
915
+ data.push(255);
916
+ }
917
+ }
918
+
919
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
920
+
921
+ const services = [{
922
+ startHandle: 65535,
923
+ endHandle: 65535,
924
+ uuid: 'ffffffffffffffffffffffffffffffff'
925
+ },
926
+ {
927
+ startHandle: 65535,
928
+ endHandle: 65535,
929
+ uuid: 'ffffffffffffffffffffffffffffffff'
930
+ },
931
+ {
932
+ startHandle: 65535,
933
+ endHandle: 65535,
934
+ uuid: 'ffffffffffffffffffffffffffffffff'
935
+ },
936
+ {
937
+ startHandle: 65535,
938
+ endHandle: 65535,
939
+ uuid: 'ffffffffffffffffffffffffffffffff'
940
+ },
941
+ {
942
+ startHandle: 65535,
943
+ endHandle: 65535,
944
+ uuid: 'ffffffffffffffffffffffffffffffff'
945
+ },
946
+ {
947
+ startHandle: 65535,
948
+ endHandle: 65535,
949
+ uuid: 'ffffffffffffffffffffffffffffffff'
950
+ },
951
+ {
952
+ startHandle: 65535,
953
+ endHandle: 65535,
954
+ uuid: 'ffffffffffffffffffff'
955
+ },
956
+ { startHandle: 65535, endHandle: 65535, uuid: 'ffffff' }];
957
+
958
+ assert.calledOnceWithExactly(callbackDiscovered, address, services);
959
+ const serviceUuids = [
960
+ 'ffffffffffffffffffff'
961
+ ];
962
+ assert.calledOnceWithExactly(callbackDiscover, address, serviceUuids);
963
+ assert.callCount(gatt._queueCommand, 1);
964
+ assert.calledOnceWithExactly(gatt.readByGroupRequest, 0x0001, 0xffff, 10240);
965
+
966
+ should(gatt._services).deepEqual({
967
+ ffffffffffffffffffffffffffffffff: {
968
+ startHandle: 65535,
969
+ endHandle: 65535,
970
+ uuid: 'ffffffffffffffffffffffffffffffff'
971
+ },
972
+ ffffffffffffffffffff: {
973
+ startHandle: 65535,
974
+ endHandle: 65535,
975
+ uuid: 'ffffffffffffffffffff'
976
+ },
977
+ ffffff: {
978
+ startHandle: 65535,
979
+ endHandle: 65535,
980
+ uuid: 'ffffff'
981
+ }
982
+ });
983
+ });
984
+
985
+ it('ATT_OP_READ_BY_GROUP_RESP > event not matching uuid', () => {
986
+ const callbackDiscovered = sinon.stub();
987
+ const callbackDiscover = sinon.stub();
988
+
989
+ gatt.on('servicesDiscovered', callbackDiscovered);
990
+ gatt.on('servicesDiscover', callbackDiscover);
991
+ gatt.discoverServices(['bbbbbbbbbbbbbbbbbbbbbbbbbbb']);
992
+
993
+ // Build data
994
+ const data = [17, 7];
995
+ for (let i = 0; i < data[1]; i++) {
996
+ for (let j = 0; j < 8; j++) {
997
+ data.push(255);
998
+ }
999
+ }
1000
+
1001
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1002
+
1003
+ const services = [{
1004
+ startHandle: 65535,
1005
+ endHandle: 65535,
1006
+ uuid: 'ffffffffffffffffffffffffffffffff'
1007
+ },
1008
+ {
1009
+ startHandle: 65535,
1010
+ endHandle: 65535,
1011
+ uuid: 'ffffffffffffffffffffffffffffffff'
1012
+ },
1013
+ {
1014
+ startHandle: 65535,
1015
+ endHandle: 65535,
1016
+ uuid: 'ffffffffffffffffffffffffffffffff'
1017
+ },
1018
+ {
1019
+ startHandle: 65535,
1020
+ endHandle: 65535,
1021
+ uuid: 'ffffffffffffffffffffffffffffffff'
1022
+ },
1023
+ {
1024
+ startHandle: 65535,
1025
+ endHandle: 65535,
1026
+ uuid: 'ffffffffffffffffffffffffffffffff'
1027
+ },
1028
+ {
1029
+ startHandle: 65535,
1030
+ endHandle: 65535,
1031
+ uuid: 'ffffffffffffffffffffffffffffffff'
1032
+ },
1033
+ {
1034
+ startHandle: 65535,
1035
+ endHandle: 65535,
1036
+ uuid: 'ffffffffffffffffffff'
1037
+ },
1038
+ { startHandle: 65535, endHandle: 65535, uuid: 'ffffff' }];
1039
+
1040
+ assert.calledOnceWithExactly(callbackDiscovered, address, services);
1041
+ const serviceUuids = [];
1042
+ assert.calledOnceWithExactly(callbackDiscover, address, serviceUuids);
1043
+ assert.callCount(gatt._queueCommand, 1);
1044
+ assert.calledOnceWithExactly(gatt.readByGroupRequest, 0x0001, 0xffff, 10240);
1045
+
1046
+ should(gatt._services).deepEqual({
1047
+ ffffffffffffffffffffffffffffffff: {
1048
+ startHandle: 65535,
1049
+ endHandle: 65535,
1050
+ uuid: 'ffffffffffffffffffffffffffffffff'
1051
+ },
1052
+ ffffffffffffffffffff: {
1053
+ startHandle: 65535,
1054
+ endHandle: 65535,
1055
+ uuid: 'ffffffffffffffffffff'
1056
+ },
1057
+ ffffff: {
1058
+ startHandle: 65535,
1059
+ endHandle: 65535,
1060
+ uuid: 'ffffff'
1061
+ }
1062
+ });
1063
+ });
1064
+ });
1065
+
1066
+ describe('discoverIncludedServices', () => {
1067
+ beforeEach(() => {
1068
+ gatt._queueCommand = sinon.spy();
1069
+ gatt.readByTypeRequest = sinon.spy();
1070
+ });
1071
+ it('not ATT_OP_READ_BY_TYPE_RESP > non discovered', () => {
1072
+ const callback = sinon.stub();
1073
+
1074
+ const service = {
1075
+ startHandle: 0xaaaa,
1076
+ endHandle: 0xdddd,
1077
+ uuid: 'uuid'
1078
+ };
1079
+
1080
+ gatt._services[service.uuid] = service;
1081
+ gatt.on('includedServicesDiscover', callback);
1082
+ gatt.discoverIncludedServices(service.uuid);
1083
+
1084
+ gatt._queueCommand.callArgWith(1, Buffer.from([0x00]));
1085
+ assert.calledOnceWithExactly(callback, address, service.uuid, []);
1086
+ assert.callCount(gatt._queueCommand, 1);
1087
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1088
+ });
1089
+
1090
+ it('ATT_OP_READ_BY_TYPE_RESP > queue', () => {
1091
+ const callback = sinon.stub();
1092
+
1093
+ const service = {
1094
+ startHandle: 0xaaaa,
1095
+ endHandle: 0xdddd,
1096
+ uuid: 'uuid'
1097
+ };
1098
+
1099
+ gatt._services[service.uuid] = service;
1100
+ gatt.on('includedServicesDiscover', callback);
1101
+ gatt.discoverIncludedServices(service.uuid);
1102
+
1103
+ // Build data
1104
+ const data = [9, 8];
1105
+ for (let i = 0; i < data[1]; i++) {
1106
+ for (let j = 0; j < 10; j++) {
1107
+ data.push(i * 10 + j);
1108
+ }
1109
+ }
1110
+
1111
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1112
+
1113
+ assert.notCalled(callback);
1114
+ assert.callCount(gatt._queueCommand, 2);
1115
+ assert.callCount(gatt.readByTypeRequest, 2);
1116
+ assert.calledWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1117
+ assert.calledWithExactly(gatt.readByTypeRequest, 18761, 56797, 10242);
1118
+ });
1119
+
1120
+ it('ATT_OP_READ_BY_TYPE_RESP > event', () => {
1121
+ const callback = sinon.stub();
1122
+
1123
+ const service = {
1124
+ startHandle: 0xaaaa,
1125
+ endHandle: 0xffff,
1126
+ uuid: 'uuid'
1127
+ };
1128
+
1129
+ gatt._services[service.uuid] = service;
1130
+ gatt.on('includedServicesDiscover', callback);
1131
+ gatt.discoverIncludedServices(service.uuid, []);
1132
+
1133
+ // Build data
1134
+ const data = [9, 7];
1135
+ for (let i = 0; i < data[1]; i++) {
1136
+ for (let j = 0; j < 10; j++) {
1137
+ data.push(255);
1138
+ }
1139
+ }
1140
+
1141
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1142
+
1143
+ const includedServiceUuids = [
1144
+ 'ffffffffffffffffffffffffffffffff',
1145
+ 'ffffffffffffffffffffffffffffffff',
1146
+ 'ffffffffffffffffffffffffffffffff',
1147
+ 'ffffffffffffffffffffffffffffffff',
1148
+ 'ffffffffffffffffffffffffffffffff',
1149
+ 'ffffffffffffffffffffffffffffffff',
1150
+ 'ffffffffffffffffffffffffffffffff',
1151
+ 'ffffffffffffffffffffffffffffff',
1152
+ 'ffffffffffffffff',
1153
+ 'ff'
1154
+ ];
1155
+ assert.calledOnceWithExactly(callback, address, service.uuid, includedServiceUuids);
1156
+ assert.callCount(gatt._queueCommand, 1);
1157
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1158
+ });
1159
+
1160
+ it('ATT_OP_READ_BY_TYPE_RESP > event matching uuid', () => {
1161
+ const callback = sinon.stub();
1162
+
1163
+ const service = {
1164
+ startHandle: 0xaaaa,
1165
+ endHandle: 0xffff,
1166
+ uuid: 'uuid'
1167
+ };
1168
+
1169
+ gatt._services[service.uuid] = service;
1170
+ gatt.on('includedServicesDiscover', callback);
1171
+ gatt.discoverIncludedServices(service.uuid, ['ff']);
1172
+
1173
+ // Build data
1174
+ const data = [9, 7];
1175
+ for (let i = 0; i < data[1]; i++) {
1176
+ for (let j = 0; j < 10; j++) {
1177
+ data.push(255);
1178
+ }
1179
+ }
1180
+
1181
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1182
+
1183
+ assert.calledOnceWithExactly(callback, address, service.uuid, ['ff']);
1184
+ assert.callCount(gatt._queueCommand, 1);
1185
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1186
+ });
1187
+
1188
+ it('ATT_OP_READ_BY_TYPE_RESP > event not matching uuid', () => {
1189
+ const callback = sinon.stub();
1190
+
1191
+ const service = {
1192
+ startHandle: 0xaaaa,
1193
+ endHandle: 0xcccc,
1194
+ uuid: 'uuid'
1195
+ };
1196
+
1197
+ gatt._services[service.uuid] = service;
1198
+ gatt.on('includedServicesDiscover', callback);
1199
+ gatt.discoverIncludedServices(service.uuid, ['ff']);
1200
+
1201
+ // Build data
1202
+ const data = [9, 7];
1203
+ for (let i = 0; i < data[1]; i++) {
1204
+ for (let j = 0; j < 10; j++) {
1205
+ data.push(255);
1206
+ }
1207
+ }
1208
+
1209
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1210
+
1211
+ assert.notCalled(callback);
1212
+ assert.callCount(gatt._queueCommand, 2);
1213
+ assert.callCount(gatt.readByTypeRequest, 2);
1214
+ assert.calledWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1215
+ assert.calledWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10242);
1216
+ });
1217
+ });
1218
+
1219
+ describe('addCharacteristics', () => {
1220
+ it('register none', () => {
1221
+ const callback = sinon.stub();
1222
+
1223
+ const serviceUuid = 'uuid';
1224
+ const characteristics = [];
1225
+
1226
+ gatt.on('includedServicesDiscover', callback);
1227
+ gatt.addCharacteristics(serviceUuid, characteristics);
1228
+
1229
+ should(gatt._characteristics).deepEqual({ [serviceUuid]: {} });
1230
+ should(gatt._descriptors).deepEqual({ [serviceUuid]: {} });
1231
+ });
1232
+
1233
+ it('register new', () => {
1234
+ const callback = sinon.stub();
1235
+
1236
+ const serviceUuid = 'uuid';
1237
+ const characteristics = [
1238
+ {
1239
+ uuid: 'c_uuid',
1240
+ number: 1
1241
+ },
1242
+ {
1243
+ uuid: 'c_uuid',
1244
+ number: 2
1245
+ },
1246
+ {
1247
+ uuid: 'c_uuid_2'
1248
+ }
1249
+ ];
1250
+
1251
+ gatt.on('includedServicesDiscover', callback);
1252
+ gatt.addCharacteristics(serviceUuid, characteristics);
1253
+
1254
+ should(gatt._characteristics).deepEqual({
1255
+ [serviceUuid]: {
1256
+ c_uuid: {
1257
+ uuid: 'c_uuid',
1258
+ number: 2
1259
+ },
1260
+ c_uuid_2: {
1261
+ uuid: 'c_uuid_2'
1262
+ }
1263
+ }
1264
+ });
1265
+ should(gatt._descriptors).deepEqual({ [serviceUuid]: {} });
1266
+ });
1267
+
1268
+ it('replace existing', () => {
1269
+ const callback = sinon.stub();
1270
+
1271
+ const serviceUuid = 'uuid';
1272
+ const characteristics = [
1273
+ {
1274
+ uuid: 'c_uuid',
1275
+ number: 2
1276
+ }
1277
+ ];
1278
+
1279
+ gatt._characteristics[serviceUuid] = {
1280
+ c_uuid: {
1281
+ uuid: 'c_uuid',
1282
+ number: 1
1283
+ }
1284
+ };
1285
+ gatt.on('includedServicesDiscover', callback);
1286
+ gatt.addCharacteristics(serviceUuid, characteristics);
1287
+
1288
+ should(gatt._characteristics).deepEqual({
1289
+ [serviceUuid]: {
1290
+ c_uuid: {
1291
+ uuid: 'c_uuid',
1292
+ number: 2
1293
+ }
1294
+ }
1295
+ });
1296
+ should(gatt._descriptors).deepEqual({ [serviceUuid]: {} });
1297
+ });
1298
+ });
1299
+
1300
+ describe('discoverCharacteristics', () => {
1301
+ beforeEach(() => {
1302
+ gatt._queueCommand = sinon.spy();
1303
+ gatt.readByTypeRequest = sinon.spy();
1304
+ });
1305
+
1306
+ it('not ATT_OP_READ_BY_TYPE_RESP > non discovered', () => {
1307
+ const callbackDiscovered = sinon.stub();
1308
+ const callbackDiscover = sinon.stub();
1309
+
1310
+ const service = {
1311
+ startHandle: 0xaaaa,
1312
+ endHandle: 0xdddd,
1313
+ uuid: 'uuid'
1314
+ };
1315
+
1316
+ gatt._services[service.uuid] = service;
1317
+ gatt.on('characteristicsDiscovered', callbackDiscovered);
1318
+ gatt.on('characteristicsDiscover', callbackDiscover);
1319
+ gatt.discoverCharacteristics(service.uuid);
1320
+
1321
+ gatt._queueCommand.callArgWith(1, Buffer.from([0x00]));
1322
+
1323
+ assert.calledOnceWithExactly(callbackDiscovered, address, service.uuid, []);
1324
+ assert.calledOnceWithExactly(callbackDiscover, address, service.uuid, []);
1325
+ assert.callCount(gatt._queueCommand, 1);
1326
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10243);
1327
+ });
1328
+
1329
+ it('ATT_OP_READ_BY_TYPE_RESP > queue', () => {
1330
+ const callbackDiscovered = sinon.stub();
1331
+ const callbackDiscover = sinon.stub();
1332
+
1333
+ const service = {
1334
+ startHandle: 0xaaaa,
1335
+ endHandle: 0xdddd,
1336
+ uuid: 'uuid'
1337
+ };
1338
+
1339
+ gatt._services[service.uuid] = service;
1340
+ gatt.on('characteristicsDiscovered', callbackDiscovered);
1341
+ gatt.on('characteristicsDiscover', callbackDiscover);
1342
+ gatt.discoverCharacteristics(service.uuid);
1343
+
1344
+ // Build data
1345
+ const data = [9, 8];
1346
+ for (let i = 0; i < data[1]; i++) {
1347
+ for (let j = 0; j < 10; j++) {
1348
+ data.push(i * 10 + j);
1349
+ }
1350
+ }
1351
+
1352
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1353
+
1354
+ assert.notCalled(callbackDiscovered);
1355
+ assert.notCalled(callbackDiscover);
1356
+ assert.callCount(gatt._queueCommand, 2);
1357
+ assert.callCount(gatt.readByTypeRequest, 2);
1358
+ assert.calledWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10243);
1359
+ assert.calledWithExactly(gatt.readByTypeRequest, 19532, 56797, 10243);
1360
+ });
1361
+
1362
+ it('ATT_OP_READ_BY_TYPE_RESP > event', () => {
1363
+ const callbackDiscovered = sinon.stub();
1364
+ const callbackDiscover = sinon.stub();
1365
+
1366
+ const service = {
1367
+ startHandle: 0xaaaa,
1368
+ endHandle: 0xffff,
1369
+ uuid: 'uuid'
1370
+ };
1371
+
1372
+ gatt._services[service.uuid] = service;
1373
+ gatt.on('characteristicsDiscovered', callbackDiscovered);
1374
+ gatt.on('characteristicsDiscover', callbackDiscover);
1375
+ gatt.discoverCharacteristics(service.uuid, []);
1376
+
1377
+ // Build data
1378
+ const data = [9, 8];
1379
+ for (let i = 0; i < data[1]; i++) {
1380
+ for (let j = 0; j < 7; j++) {
1381
+ data.push(255);
1382
+ }
1383
+ }
1384
+
1385
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1386
+
1387
+ const characteristics = [
1388
+ {
1389
+ properties: [
1390
+ 'broadcast',
1391
+ 'read',
1392
+ 'writeWithoutResponse',
1393
+ 'write',
1394
+ 'notify',
1395
+ 'indicate',
1396
+ 'authenticatedSignedWrites',
1397
+ 'extendedProperties'
1398
+ ],
1399
+ uuid: 'ffffffffffffffffffffffffffffffff'
1400
+ },
1401
+ {
1402
+ properties: [
1403
+ 'broadcast',
1404
+ 'read',
1405
+ 'writeWithoutResponse',
1406
+ 'write',
1407
+ 'notify',
1408
+ 'indicate',
1409
+ 'authenticatedSignedWrites',
1410
+ 'extendedProperties'
1411
+ ],
1412
+ uuid: 'ffffffffffffffffffffffffffffffff'
1413
+ },
1414
+ {
1415
+ properties: [
1416
+ 'broadcast',
1417
+ 'read',
1418
+ 'writeWithoutResponse',
1419
+ 'write',
1420
+ 'notify',
1421
+ 'indicate',
1422
+ 'authenticatedSignedWrites',
1423
+ 'extendedProperties'
1424
+ ],
1425
+ uuid: 'ffffffffffffffffffffffffffffffff'
1426
+ },
1427
+ {
1428
+ properties: [
1429
+ 'broadcast',
1430
+ 'read',
1431
+ 'writeWithoutResponse',
1432
+ 'write',
1433
+ 'notify',
1434
+ 'indicate',
1435
+ 'authenticatedSignedWrites',
1436
+ 'extendedProperties'
1437
+ ],
1438
+ uuid: 'ffffffffffffffffffffffffffffffff'
1439
+ },
1440
+ {
1441
+ properties: [
1442
+ 'broadcast',
1443
+ 'read',
1444
+ 'writeWithoutResponse',
1445
+ 'write',
1446
+ 'notify',
1447
+ 'indicate',
1448
+ 'authenticatedSignedWrites',
1449
+ 'extendedProperties'
1450
+ ],
1451
+ uuid: 'ffffffffffffffffffffffffffffffff'
1452
+ },
1453
+ {
1454
+ properties: [
1455
+ 'broadcast',
1456
+ 'read',
1457
+ 'writeWithoutResponse',
1458
+ 'write',
1459
+ 'notify',
1460
+ 'indicate',
1461
+ 'authenticatedSignedWrites',
1462
+ 'extendedProperties'
1463
+ ],
1464
+ uuid: 'ffffffffffffffffffffff'
1465
+ },
1466
+ {
1467
+ properties: [
1468
+ 'broadcast',
1469
+ 'read',
1470
+ 'writeWithoutResponse',
1471
+ 'write',
1472
+ 'notify',
1473
+ 'indicate',
1474
+ 'authenticatedSignedWrites',
1475
+ 'extendedProperties'
1476
+ ],
1477
+ uuid: 'ffffff'
1478
+ }
1479
+ ];
1480
+ assert.calledOnceWithExactly(callbackDiscover, address, service.uuid, characteristics);
1481
+
1482
+ const discoveredChars = [
1483
+ {
1484
+ startHandle: 65535,
1485
+ properties: 255,
1486
+ valueHandle: 65535,
1487
+ uuid: 'ffffffffffffffffffffffffffffffff',
1488
+ propsDecoded: [
1489
+ 'broadcast',
1490
+ 'read',
1491
+ 'writeWithoutResponse',
1492
+ 'write',
1493
+ 'notify',
1494
+ 'indicate',
1495
+ 'authenticatedSignedWrites',
1496
+ 'extendedProperties'
1497
+ ],
1498
+ rawProps: 255,
1499
+ endHandle: 65534
1500
+ },
1501
+ {
1502
+ startHandle: 65535,
1503
+ properties: 255,
1504
+ valueHandle: 65535,
1505
+ uuid: 'ffffffffffffffffffffffffffffffff',
1506
+ propsDecoded: [
1507
+ 'broadcast',
1508
+ 'read',
1509
+ 'writeWithoutResponse',
1510
+ 'write',
1511
+ 'notify',
1512
+ 'indicate',
1513
+ 'authenticatedSignedWrites',
1514
+ 'extendedProperties'
1515
+ ],
1516
+ rawProps: 255,
1517
+ endHandle: 65534
1518
+ },
1519
+ {
1520
+ startHandle: 65535,
1521
+ properties: 255,
1522
+ valueHandle: 65535,
1523
+ uuid: 'ffffffffffffffffffffffffffffffff',
1524
+ propsDecoded: [
1525
+ 'broadcast',
1526
+ 'read',
1527
+ 'writeWithoutResponse',
1528
+ 'write',
1529
+ 'notify',
1530
+ 'indicate',
1531
+ 'authenticatedSignedWrites',
1532
+ 'extendedProperties'
1533
+ ],
1534
+ rawProps: 255,
1535
+ endHandle: 65534
1536
+ },
1537
+ {
1538
+ startHandle: 65535,
1539
+ properties: 255,
1540
+ valueHandle: 65535,
1541
+ uuid: 'ffffffffffffffffffffffffffffffff',
1542
+ propsDecoded: [
1543
+ 'broadcast',
1544
+ 'read',
1545
+ 'writeWithoutResponse',
1546
+ 'write',
1547
+ 'notify',
1548
+ 'indicate',
1549
+ 'authenticatedSignedWrites',
1550
+ 'extendedProperties'
1551
+ ],
1552
+ rawProps: 255,
1553
+ endHandle: 65534
1554
+ },
1555
+ {
1556
+ startHandle: 65535,
1557
+ properties: 255,
1558
+ valueHandle: 65535,
1559
+ uuid: 'ffffffffffffffffffffffffffffffff',
1560
+ propsDecoded: [
1561
+ 'broadcast',
1562
+ 'read',
1563
+ 'writeWithoutResponse',
1564
+ 'write',
1565
+ 'notify',
1566
+ 'indicate',
1567
+ 'authenticatedSignedWrites',
1568
+ 'extendedProperties'
1569
+ ],
1570
+ rawProps: 255,
1571
+ endHandle: 65534
1572
+ },
1573
+ {
1574
+ startHandle: 65535,
1575
+ properties: 255,
1576
+ valueHandle: 65535,
1577
+ uuid: 'ffffffffffffffffffffff',
1578
+ propsDecoded: [
1579
+ 'broadcast',
1580
+ 'read',
1581
+ 'writeWithoutResponse',
1582
+ 'write',
1583
+ 'notify',
1584
+ 'indicate',
1585
+ 'authenticatedSignedWrites',
1586
+ 'extendedProperties'
1587
+ ],
1588
+ rawProps: 255,
1589
+ endHandle: 65534
1590
+ },
1591
+ {
1592
+ startHandle: 65535,
1593
+ properties: 255,
1594
+ valueHandle: 65535,
1595
+ uuid: 'ffffff',
1596
+ propsDecoded: [
1597
+ 'broadcast',
1598
+ 'read',
1599
+ 'writeWithoutResponse',
1600
+ 'write',
1601
+ 'notify',
1602
+ 'indicate',
1603
+ 'authenticatedSignedWrites',
1604
+ 'extendedProperties'
1605
+ ],
1606
+ rawProps: 255,
1607
+ endHandle: 65535
1608
+ }
1609
+ ];
1610
+ assert.calledOnceWithExactly(callbackDiscovered, address, service.uuid, discoveredChars);
1611
+ assert.callCount(gatt._queueCommand, 1);
1612
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10243);
1613
+ });
1614
+
1615
+ it('ATT_OP_READ_BY_TYPE_RESP > event not matching uuid', () => {
1616
+ const callbackDiscover = sinon.stub();
1617
+
1618
+ const service = {
1619
+ startHandle: 0xaaaa,
1620
+ endHandle: 0xffff,
1621
+ uuid: 'uuid'
1622
+ };
1623
+
1624
+ gatt._services[service.uuid] = service;
1625
+ gatt.on('characteristicsDiscover', callbackDiscover);
1626
+ gatt.discoverCharacteristics(service.uuid, ['ffffff']);
1627
+
1628
+ // Build data
1629
+ const data = [9, 8];
1630
+ for (let i = 0; i < data[1]; i++) {
1631
+ for (let j = 0; j < 7; j++) {
1632
+ data.push(255);
1633
+ }
1634
+ }
1635
+
1636
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1637
+
1638
+ const discoveredChars = [
1639
+ {
1640
+ properties: [
1641
+ 'broadcast',
1642
+ 'read',
1643
+ 'writeWithoutResponse',
1644
+ 'write',
1645
+ 'notify',
1646
+ 'indicate',
1647
+ 'authenticatedSignedWrites',
1648
+ 'extendedProperties'
1649
+ ],
1650
+ uuid: 'ffffff'
1651
+ }
1652
+ ];
1653
+ assert.calledOnceWithExactly(callbackDiscover, address, service.uuid, discoveredChars);
1654
+ assert.callCount(gatt._queueCommand, 1);
1655
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, service.startHandle, service.endHandle, 10243);
1656
+ });
1657
+
1658
+ [{ prop: 'broadcast', byte: 1 }, { prop: 'read', byte: 2 }, { prop: 'writeWithoutResponse', byte: 4 }, { prop: 'write', byte: 8 }, { prop: 'notify', byte: 16 }, { prop: 'indicate', byte: 32 }, { prop: 'authenticatedSignedWrites', byte: 64 }, { prop: 'extendedProperties', byte: 128 }].forEach(data => {
1659
+ const { prop, byte } = data;
1660
+ it(`ATT_OP_READ_BY_TYPE_RESP > event - ${prop}`, () => {
1661
+ const callbackDiscover = sinon.stub();
1662
+
1663
+ const service = {
1664
+ startHandle: 0xaaaa,
1665
+ endHandle: 0xffff,
1666
+ uuid: 'uuid'
1667
+ };
1668
+
1669
+ gatt._services[service.uuid] = service;
1670
+ gatt.on('characteristicsDiscover', callbackDiscover);
1671
+ gatt.discoverCharacteristics(service.uuid, []);
1672
+
1673
+ // Build data
1674
+ const data = [9, 7];
1675
+ for (let i = 0; i < data[1]; i++) {
1676
+ data.push(255);
1677
+ data.push(255);
1678
+ data.push(byte);
1679
+ data.push(255);
1680
+ data.push(255);
1681
+ data.push(255);
1682
+ data.push(255);
1683
+ }
1684
+
1685
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1686
+
1687
+ assert.calledOnceWithMatch(callbackDiscover, address, service.uuid, sinon.match.every(sinon.match({
1688
+ properties: [prop]
1689
+ })));
1690
+ });
1691
+ });
1692
+ });
1693
+
1694
+ describe('read', () => {
1695
+ const serviceUuid = 'serviceUuid';
1696
+ const characteristic = {
1697
+ valueHandle: 0xaaaa,
1698
+ uuid: 'cUuid'
1699
+ };
1700
+
1701
+ beforeEach(() => {
1702
+ gatt._queueCommand = sinon.spy();
1703
+ gatt.readRequest = sinon.spy();
1704
+ gatt._characteristics = {
1705
+ [serviceUuid]: {
1706
+ [characteristic.uuid]: characteristic
1707
+ }
1708
+ };
1709
+ });
1710
+
1711
+ it('not ATT_OP_READ_RESP not ATT_OP_READ_BLOB_RESP', () => {
1712
+ const callback = sinon.stub();
1713
+
1714
+ gatt.on('read', callback);
1715
+ gatt.read(serviceUuid, characteristic.uuid);
1716
+
1717
+ const data = Buffer.from([0]);
1718
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1719
+
1720
+ assert.calledOnce(gatt._queueCommand);
1721
+ assert.calledOnceWithExactly(gatt.readRequest, characteristic.valueHandle);
1722
+
1723
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid, Buffer.alloc(0));
1724
+ });
1725
+
1726
+ [11, 13].forEach(opcode => {
1727
+ it(`opcode = ${opcode} should send event`, () => {
1728
+ const callback = sinon.stub();
1729
+
1730
+ gatt.on('read', callback);
1731
+ gatt.read(serviceUuid, characteristic.uuid);
1732
+
1733
+ const data = Buffer.from([opcode, 1, 2, 3]);
1734
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1735
+
1736
+ assert.calledOnce(gatt._queueCommand);
1737
+ assert.calledOnceWithExactly(gatt.readRequest, characteristic.valueHandle);
1738
+
1739
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid, Buffer.from([1, 2, 3]));
1740
+ });
1741
+
1742
+ it(`opcode = ${opcode} should queueCommand`, () => {
1743
+ const callback = sinon.stub();
1744
+ const readBlobRequest = sinon.stub();
1745
+
1746
+ const data = Buffer.from([opcode, 1, 2, 3]);
1747
+
1748
+ gatt._mtu = data.length;
1749
+ gatt.readBlobRequest = readBlobRequest;
1750
+ gatt.on('read', callback);
1751
+ gatt.read(serviceUuid, characteristic.uuid);
1752
+
1753
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
1754
+
1755
+ assert.callCount(gatt._queueCommand, 2);
1756
+ assert.calledOnceWithExactly(gatt.readRequest, characteristic.valueHandle);
1757
+
1758
+ assert.calledOnceWithExactly(readBlobRequest, characteristic.valueHandle, 3);
1759
+ assert.notCalled(callback);
1760
+ });
1761
+ });
1762
+ });
1763
+
1764
+ describe('write', () => {
1765
+ const serviceUuid = 'serviceUuid';
1766
+ const characteristic = {
1767
+ valueHandle: 0xaaaa,
1768
+ uuid: 'cUuid'
1769
+ };
1770
+
1771
+ beforeEach(() => {
1772
+ gatt._characteristics = {
1773
+ [serviceUuid]: {
1774
+ [characteristic.uuid]: characteristic
1775
+ }
1776
+ };
1777
+ gatt._queueCommand = sinon.spy();
1778
+ gatt.writeRequest = sinon.spy();
1779
+ });
1780
+
1781
+ it('withoutReponse should queue and emit event', () => {
1782
+ const callback = sinon.stub();
1783
+
1784
+ const data = Buffer.from([1, 2, 3]);
1785
+
1786
+ gatt._mtu = data.length;
1787
+ gatt.on('write', callback);
1788
+ gatt.write(serviceUuid, characteristic.uuid, data, true);
1789
+
1790
+ gatt._queueCommand.callArg(2);
1791
+
1792
+ assert.calledOnce(gatt._queueCommand);
1793
+ assert.calledOnceWithExactly(gatt.writeRequest, characteristic.valueHandle, data, true);
1794
+
1795
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid);
1796
+ });
1797
+
1798
+ it('should delegate to longWrite', () => {
1799
+ const callback = sinon.stub();
1800
+
1801
+ const data = Buffer.from([1, 2, 3]);
1802
+
1803
+ gatt._mtu = data.length;
1804
+ gatt.longWrite = sinon.stub();
1805
+ gatt.on('write', callback);
1806
+ gatt.write(serviceUuid, characteristic.uuid, data, false);
1807
+
1808
+ assert.notCalled(gatt._queueCommand);
1809
+ assert.notCalled(gatt.writeRequest);
1810
+ assert.notCalled(callback);
1811
+
1812
+ assert.calledOnceWithExactly(gatt.longWrite, serviceUuid, characteristic.uuid, data, false);
1813
+ });
1814
+
1815
+ it('withReponse should not emit event', () => {
1816
+ const callback = sinon.stub();
1817
+
1818
+ const data = Buffer.from([1, 2, 3]);
1819
+
1820
+ gatt._mtu = data.length + 5;
1821
+ gatt.on('write', callback);
1822
+ gatt.write(serviceUuid, characteristic.uuid, data, false);
1823
+
1824
+ gatt._queueCommand.callArgWith(1, data);
1825
+
1826
+ assert.calledOnce(gatt._queueCommand);
1827
+ assert.calledOnceWithExactly(gatt.writeRequest, characteristic.valueHandle, data, false);
1828
+
1829
+ assert.notCalled(callback);
1830
+ });
1831
+
1832
+ it('withReponse should emit event', () => {
1833
+ const callback = sinon.stub();
1834
+
1835
+ const data = Buffer.from([19, 2, 3]);
1836
+
1837
+ gatt._mtu = data.length + 5;
1838
+ gatt.on('write', callback);
1839
+ gatt.write(serviceUuid, characteristic.uuid, data, false);
1840
+
1841
+ gatt._queueCommand.callArgWith(1, data);
1842
+
1843
+ assert.calledOnce(gatt._queueCommand);
1844
+ assert.calledOnceWithExactly(gatt.writeRequest, characteristic.valueHandle, data, false);
1845
+
1846
+ assert.calledWithExactly(callback, address, serviceUuid, characteristic.uuid);
1847
+ });
1848
+ });
1849
+
1850
+ describe('longWrite', () => {
1851
+ const serviceUuid = 'serviceUuid';
1852
+ const characteristic = {
1853
+ valueHandle: 0xaaaa,
1854
+ uuid: 'cUuid'
1855
+ };
1856
+
1857
+ beforeEach(() => {
1858
+ gatt._queueCommand = sinon.spy();
1859
+ gatt.prepareWriteRequest = sinon.spy();
1860
+ gatt.executeWriteRequest = sinon.spy();
1861
+
1862
+ gatt._characteristics = {
1863
+ [serviceUuid]: {
1864
+ [characteristic.uuid]: characteristic
1865
+ }
1866
+ };
1867
+ });
1868
+
1869
+ it('should queue but not emit event', () => {
1870
+ const callback = sinon.stub();
1871
+ const data = Buffer.from([19, 2, 3, 4, 5, 6]);
1872
+
1873
+ gatt._mtu = 10;
1874
+ gatt.on('write', callback);
1875
+ gatt.longWrite(serviceUuid, characteristic.uuid, data, false);
1876
+
1877
+ assert.callCount(gatt._queueCommand, 3);
1878
+
1879
+ assert.callCount(gatt.prepareWriteRequest, 2);
1880
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 0, Buffer.from([19, 2, 3, 4, 5]));
1881
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 5, Buffer.from([6]));
1882
+
1883
+ assert.calledOnceWithExactly(gatt.executeWriteRequest, characteristic.valueHandle);
1884
+
1885
+ const resp = Buffer.from([0]);
1886
+ gatt._queueCommand.getCall(2).callArgWith(1, resp);
1887
+
1888
+ assert.notCalled(callback);
1889
+ });
1890
+
1891
+ it('should queue not emit event (withoutResponse)', () => {
1892
+ const callback = sinon.stub();
1893
+
1894
+ const data = Buffer.from([19, 2, 3, 4, 5, 6]);
1895
+
1896
+ gatt._mtu = 10;
1897
+ gatt.on('write', callback);
1898
+ gatt.longWrite(serviceUuid, characteristic.uuid, data, true);
1899
+
1900
+ assert.callCount(gatt._queueCommand, 3);
1901
+
1902
+ assert.callCount(gatt.prepareWriteRequest, 2);
1903
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 0, Buffer.from([19, 2, 3, 4, 5]));
1904
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 5, Buffer.from([6]));
1905
+
1906
+ assert.calledOnceWithExactly(gatt.executeWriteRequest, characteristic.valueHandle);
1907
+
1908
+ const resp = Buffer.from([25]);
1909
+ gatt._queueCommand.getCall(2).callArgWith(1, resp);
1910
+
1911
+ assert.notCalled(callback);
1912
+ });
1913
+
1914
+ it('should queue and emit event', () => {
1915
+ const callback = sinon.stub();
1916
+ const data = Buffer.from([19, 2, 3, 4, 5, 6]);
1917
+
1918
+ gatt._mtu = 10;
1919
+ gatt.on('write', callback);
1920
+ gatt.longWrite(serviceUuid, characteristic.uuid, data, false);
1921
+
1922
+ assert.callCount(gatt._queueCommand, 3);
1923
+
1924
+ assert.callCount(gatt.prepareWriteRequest, 2);
1925
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 0, Buffer.from([19, 2, 3, 4, 5]));
1926
+ assert.calledWithExactly(gatt.prepareWriteRequest, characteristic.valueHandle, 5, Buffer.from([6]));
1927
+
1928
+ assert.calledOnceWithExactly(gatt.executeWriteRequest, characteristic.valueHandle);
1929
+
1930
+ const resp = Buffer.from([25]);
1931
+ gatt._queueCommand.getCall(2).callArgWith(1, resp);
1932
+
1933
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid);
1934
+ });
1935
+ });
1936
+
1937
+ describe('broadcast', () => {
1938
+ const serviceUuid = 'serviceUuid';
1939
+ const characteristic = {
1940
+ startHandle: 0xaaaa,
1941
+ endHandle: 0xcccc,
1942
+ valueHandle: 0xbbbb,
1943
+ uuid: 'cUuid'
1944
+ };
1945
+
1946
+ beforeEach(() => {
1947
+ gatt._queueCommand = sinon.spy();
1948
+ gatt.readByTypeRequest = sinon.spy();
1949
+
1950
+ gatt._characteristics = {
1951
+ [serviceUuid]: {
1952
+ [characteristic.uuid]: characteristic
1953
+ }
1954
+ };
1955
+ });
1956
+
1957
+ it('no queue, no event', () => {
1958
+ const callback = sinon.stub();
1959
+
1960
+ gatt.on('broadcast', callback);
1961
+ gatt.broadcast(serviceUuid, characteristic.uuid, true);
1962
+
1963
+ const data = Buffer.from([0]);
1964
+ gatt._queueCommand.callArgWith(1, data);
1965
+
1966
+ assert.calledOnce(gatt._queueCommand);
1967
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10499);
1968
+
1969
+ assert.notCalled(callback);
1970
+ });
1971
+
1972
+ it('queue, no event', () => {
1973
+ const writeRequest = sinon.spy();
1974
+ const callback = sinon.stub();
1975
+
1976
+ gatt.writeRequest = writeRequest;
1977
+ gatt.on('broadcast', callback);
1978
+ gatt.broadcast(serviceUuid, characteristic.uuid, true);
1979
+
1980
+ const data = Buffer.from([9, 2, 3, 4, 5, 6, 7, 8]);
1981
+ gatt._queueCommand.getCall(0).callArgWith(1, data);
1982
+
1983
+ const writeData = Buffer.from([9, 2, 3, 4]);
1984
+ gatt._queueCommand.getCall(1).callArgWith(1, writeData);
1985
+
1986
+ assert.callCount(gatt._queueCommand, 2);
1987
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10499);
1988
+ assert.calledOnceWithExactly(writeRequest, 1027, Buffer.from([5, 6]), false);
1989
+
1990
+ assert.notCalled(callback);
1991
+ });
1992
+
1993
+ it('queue and event', () => {
1994
+ const writeRequest = sinon.spy();
1995
+ const callback = sinon.stub();
1996
+
1997
+ gatt.writeRequest = writeRequest;
1998
+ gatt.on('broadcast', callback);
1999
+ gatt.broadcast(serviceUuid, characteristic.uuid, false);
2000
+
2001
+ const data = Buffer.from([9, 2, 3, 4, 5, 6, 7, 8]);
2002
+ gatt._queueCommand.getCall(0).callArgWith(1, data);
2003
+
2004
+ const writeData = Buffer.from([19, 2, 3, 4]);
2005
+ gatt._queueCommand.getCall(1).callArgWith(1, writeData);
2006
+
2007
+ assert.callCount(gatt._queueCommand, 2);
2008
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10499);
2009
+ assert.calledOnceWithExactly(writeRequest, 1027, Buffer.from([4, 6]), false);
2010
+
2011
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid, false);
2012
+ });
2013
+ });
2014
+
2015
+ describe('notify', () => {
2016
+ const serviceUuid = 'serviceUuid';
2017
+ let characteristic;
2018
+
2019
+ beforeEach(() => {
2020
+ characteristic = {
2021
+ uuid: 'cUuid',
2022
+ startHandle: 0xaaaa,
2023
+ endHandle: 0xdddd
2024
+ };
2025
+
2026
+ gatt._queueCommand = sinon.spy();
2027
+ gatt.readByTypeRequest = sinon.spy();
2028
+
2029
+ gatt._characteristics = {
2030
+ [serviceUuid]: {
2031
+ [characteristic.uuid]: characteristic
2032
+ }
2033
+ };
2034
+ });
2035
+
2036
+ it('not ATT_OP_READ_BY_TYPE_RESP, no queue, no event', () => {
2037
+ const callback = sinon.spy();
2038
+
2039
+ gatt.on('notify', callback);
2040
+ gatt.notify(serviceUuid, characteristic.uuid, true);
2041
+
2042
+ const data = Buffer.from([0]);
2043
+ gatt._queueCommand.callArgWith(1, data);
2044
+
2045
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10498);
2046
+ assert.callCount(gatt._queueCommand, 1);
2047
+ assert.notCalled(callback);
2048
+ });
2049
+
2050
+ [
2051
+ { name: 'notify, no properties', notify: true, properties: 0x0, expectedValue: [4, 5] },
2052
+ { name: 'notify, useNotify', notify: true, properties: 0x10, expectedValue: [5, 5] },
2053
+ { name: 'notify, useIndicate', notify: true, properties: 0x20, expectedValue: [6, 5] },
2054
+ { name: 'not notify, no properties', notify: false, properties: 0x0, expectedValue: [4, 5] },
2055
+ { name: 'not notify, useNotify', notify: false, properties: 0x10, expectedValue: [4, 5] },
2056
+ { name: 'not notify, useIndicate', notify: false, properties: 0x20, expectedValue: [4, 5] }
2057
+ ].forEach(props => {
2058
+ const { name, notify, expectedValue, properties } = props;
2059
+
2060
+ it(`no event with ${name}`, () => {
2061
+ characteristic.properties = properties;
2062
+ const callback = sinon.spy();
2063
+
2064
+ gatt.on('notify', callback);
2065
+ gatt.writeRequest = sinon.spy();
2066
+ gatt.notify(serviceUuid, characteristic.uuid, notify);
2067
+
2068
+ const data = Buffer.from([9, 1, 2, 3, 4, 5]);
2069
+ gatt._queueCommand.getCall(0).callArgWith(1, data);
2070
+ gatt._queueCommand.getCall(1).callArgWith(1, data);
2071
+
2072
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10498);
2073
+ assert.callCount(gatt._queueCommand, 2);
2074
+ assert.calledOnceWithExactly(gatt.writeRequest, 770, Buffer.from(expectedValue), false);
2075
+ assert.notCalled(callback);
2076
+ });
2077
+ });
2078
+
2079
+ it('should emit event', () => {
2080
+ const callback = sinon.spy();
2081
+
2082
+ gatt.on('notify', callback);
2083
+ gatt.writeRequest = sinon.spy();
2084
+ gatt.notify(serviceUuid, characteristic.uuid, true);
2085
+
2086
+ const data = Buffer.from([9, 1, 2, 3, 4, 5]);
2087
+ gatt._queueCommand.callArgWith(1, data);
2088
+ const eventData = Buffer.from([19]);
2089
+ gatt._queueCommand.getCall(1).callArgWith(1, eventData);
2090
+
2091
+ assert.calledOnceWithExactly(gatt.readByTypeRequest, characteristic.startHandle, characteristic.endHandle, 10498);
2092
+ assert.callCount(gatt._queueCommand, 2);
2093
+ assert.calledOnceWithExactly(gatt.writeRequest, 770, Buffer.from([4, 5]), false);
2094
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid, true);
2095
+ });
2096
+ });
2097
+
2098
+ describe('discoverDescriptors', () => {
2099
+ const serviceUuid = 'serviceUuid';
2100
+ const characteristic = {
2101
+ uuid: 'cUuid',
2102
+ valueHandle: 0xcccc,
2103
+ endHandle: 0xeeee
2104
+ };
2105
+
2106
+ beforeEach(() => {
2107
+ gatt._characteristics = {
2108
+ [serviceUuid]: {
2109
+ [characteristic.uuid]: characteristic
2110
+ }
2111
+ };
2112
+ gatt._descriptors = {
2113
+ [serviceUuid]: {
2114
+ }
2115
+ };
2116
+
2117
+ gatt._queueCommand = sinon.spy();
2118
+ gatt.findInfoRequest = sinon.spy();
2119
+ });
2120
+
2121
+ it('should enqueue command', () => {
2122
+ const callback = sinon.spy();
2123
+
2124
+ gatt.on('descriptorsDiscover', callback);
2125
+ gatt.discoverDescriptors(serviceUuid, characteristic.uuid);
2126
+
2127
+ // Build data
2128
+ const data = [5, 1];
2129
+ for (let i = 0; i < data[1]; i++) {
2130
+ for (let j = 0; j < 4; j++) {
2131
+ data.push(i * 10 + j);
2132
+ }
2133
+ }
2134
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
2135
+
2136
+ assert.callCount(gatt._queueCommand, 2);
2137
+ assert.callCount(gatt.findInfoRequest, 2);
2138
+ assert.calledWithExactly(gatt.findInfoRequest, characteristic.valueHandle + 1, characteristic.endHandle);
2139
+ assert.calledWithExactly(gatt.findInfoRequest, 257, characteristic.endHandle);
2140
+ assert.notCalled(callback);
2141
+ });
2142
+
2143
+ it('should enqueue command mismatch descriptor handle', () => {
2144
+ const callback = sinon.spy();
2145
+
2146
+ gatt.on('descriptorsDiscover', callback);
2147
+ gatt.discoverDescriptors(serviceUuid, characteristic.uuid);
2148
+
2149
+ // Build data
2150
+ const data = [5, 1];
2151
+ for (let i = 0; i < data[1]; i++) {
2152
+ for (let j = 0; j < 4; j++) {
2153
+ data.push(i * 10 + j);
2154
+ }
2155
+ }
2156
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
2157
+
2158
+ assert.callCount(gatt._queueCommand, 2);
2159
+ assert.callCount(gatt.findInfoRequest, 2);
2160
+ assert.calledWithExactly(gatt.findInfoRequest, characteristic.valueHandle + 1, characteristic.endHandle);
2161
+ assert.calledWithExactly(gatt.findInfoRequest, 257, characteristic.endHandle);
2162
+ assert.notCalled(callback);
2163
+ });
2164
+
2165
+ it('should emit event', () => {
2166
+ const callback = sinon.spy();
2167
+
2168
+ gatt.on('descriptorsDiscover', callback);
2169
+ gatt.discoverDescriptors(serviceUuid, characteristic.uuid);
2170
+
2171
+ // Build data
2172
+ const data = [5, 1];
2173
+ for (let i = 0; i < data[1]; i++) {
2174
+ for (let j = 0; j < 4; j++) {
2175
+ data.push(238);
2176
+ }
2177
+ }
2178
+ gatt._queueCommand.callArgWith(1, Buffer.from(data));
2179
+
2180
+ assert.callCount(gatt._queueCommand, 1);
2181
+ assert.calledOnceWithExactly(gatt.findInfoRequest, characteristic.valueHandle + 1, characteristic.endHandle);
2182
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristic.uuid, ['eeee']);
2183
+ });
2184
+ });
2185
+
2186
+ describe('readValue', () => {
2187
+ const serviceUuid = 'serviceUuid';
2188
+ const characteristicUuid = 'characteristicUuid';
2189
+ const descriptor = {
2190
+ uuid: 'descriptorUuid',
2191
+ handle: 0x3344
2192
+ };
2193
+ let callback;
2194
+
2195
+ beforeEach(() => {
2196
+ callback = sinon.spy();
2197
+
2198
+ gatt._descriptors = {
2199
+ [serviceUuid]: {
2200
+ [characteristicUuid]: {
2201
+ [descriptor.uuid]: descriptor
2202
+ }
2203
+ }
2204
+ };
2205
+
2206
+ gatt._queueCommand = sinon.spy();
2207
+ gatt.readRequest = sinon.spy();
2208
+ gatt.readBlobRequest = sinon.spy();
2209
+ gatt.on('valueRead', callback);
2210
+ });
2211
+
2212
+ it('should emit event by default', () => {
2213
+ gatt.readValue(serviceUuid, characteristicUuid, descriptor.uuid);
2214
+
2215
+ const data = Buffer.from([0]);
2216
+ gatt._queueCommand.callArgWith(1, data);
2217
+
2218
+ assert.callCount(gatt._queueCommand, 1);
2219
+ assert.calledOnceWithExactly(gatt.readRequest, descriptor.handle);
2220
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristicUuid, descriptor.uuid, Buffer.alloc(0));
2221
+ });
2222
+
2223
+ it('should emit event on different data.length/mtu', () => {
2224
+ gatt._mtu = 80;
2225
+ gatt.readValue(serviceUuid, characteristicUuid, descriptor.uuid);
2226
+
2227
+ const data = Buffer.from([11]);
2228
+ gatt._queueCommand.callArgWith(1, data);
2229
+
2230
+ assert.callCount(gatt._queueCommand, 1);
2231
+ assert.calledOnceWithExactly(gatt.readRequest, descriptor.handle);
2232
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristicUuid, descriptor.uuid, Buffer.alloc(0));
2233
+ });
2234
+
2235
+ it('should enqueue on same data.length/mtu', () => {
2236
+ gatt._mtu = 7;
2237
+ gatt.readValue(serviceUuid, characteristicUuid, descriptor.uuid);
2238
+
2239
+ const data = Buffer.from([13, 1, 2, 3, 4, 5, 6]);
2240
+ gatt._queueCommand.callArgWith(1, data);
2241
+
2242
+ assert.callCount(gatt._queueCommand, 2);
2243
+ assert.calledOnceWithExactly(gatt.readRequest, descriptor.handle);
2244
+ assert.calledOnceWithExactly(gatt.readBlobRequest, descriptor.handle, 6);
2245
+ assert.notCalled(callback);
2246
+ });
2247
+ });
2248
+
2249
+ describe('writeValue', () => {
2250
+ const serviceUuid = 'serviceUuid';
2251
+ const characteristicUuid = 'characteristicUuid';
2252
+ const descriptor = {
2253
+ uuid: 'descriptorUuid',
2254
+ handle: 0x3344
2255
+ };
2256
+ let callback;
2257
+
2258
+ beforeEach(() => {
2259
+ callback = sinon.spy();
2260
+
2261
+ gatt._descriptors = {
2262
+ [serviceUuid]: {
2263
+ [characteristicUuid]: {
2264
+ [descriptor.uuid]: descriptor
2265
+ }
2266
+ }
2267
+ };
2268
+
2269
+ gatt._queueCommand = sinon.spy();
2270
+ gatt.writeRequest = sinon.spy();
2271
+ gatt.on('valueWrite', callback);
2272
+ });
2273
+
2274
+ it('should not emit event', () => {
2275
+ const data = Buffer.from([0]);
2276
+ gatt.writeValue(serviceUuid, characteristicUuid, descriptor.uuid, data);
2277
+
2278
+ gatt._queueCommand.callArgWith(1, data);
2279
+
2280
+ assert.callCount(gatt._queueCommand, 1);
2281
+ assert.calledOnceWithExactly(gatt.writeRequest, descriptor.handle, data, false);
2282
+ assert.notCalled(callback);
2283
+ });
2284
+
2285
+ it('should emit event', () => {
2286
+ const data = Buffer.from([19]);
2287
+ gatt.writeValue(serviceUuid, characteristicUuid, descriptor.uuid, data);
2288
+
2289
+ gatt._queueCommand.callArgWith(1, data);
2290
+
2291
+ assert.callCount(gatt._queueCommand, 1);
2292
+ assert.calledOnceWithExactly(gatt.writeRequest, descriptor.handle, data, false);
2293
+ assert.calledOnceWithExactly(callback, address, serviceUuid, characteristicUuid, descriptor.uuid);
2294
+ });
2295
+ });
2296
+
2297
+ describe('readHandle', () => {
2298
+ let callback;
2299
+ const handle = 'handle';
2300
+
2301
+ beforeEach(() => {
2302
+ callback = sinon.spy();
2303
+
2304
+ gatt._queueCommand = sinon.spy();
2305
+ gatt.readRequest = sinon.spy();
2306
+ gatt.readBlobRequest = sinon.spy();
2307
+ gatt.on('handleRead', callback);
2308
+ });
2309
+
2310
+ it('should emit event by default', () => {
2311
+ gatt.readHandle(handle);
2312
+
2313
+ const data = Buffer.from([0]);
2314
+ gatt._queueCommand.callArgWith(1, data);
2315
+
2316
+ assert.callCount(gatt._queueCommand, 1);
2317
+ assert.calledOnceWithExactly(gatt.readRequest, handle);
2318
+ assert.calledOnceWithExactly(callback, address, handle, Buffer.alloc(0));
2319
+ });
2320
+
2321
+ it('should emit event on different data.length/mtu', () => {
2322
+ gatt._mtu = 80;
2323
+ gatt.readHandle(handle);
2324
+
2325
+ const data = Buffer.from([11]);
2326
+ gatt._queueCommand.callArgWith(1, data);
2327
+
2328
+ assert.callCount(gatt._queueCommand, 1);
2329
+ assert.calledOnceWithExactly(gatt.readRequest, handle);
2330
+ assert.calledOnceWithExactly(callback, address, handle, Buffer.alloc(0));
2331
+ });
2332
+
2333
+ it('should enqueue on same data.length/mtu', () => {
2334
+ gatt._mtu = 7;
2335
+ gatt.readHandle(handle);
2336
+
2337
+ const data = Buffer.from([13, 1, 2, 3, 4, 5, 6]);
2338
+ gatt._queueCommand.callArgWith(1, data);
2339
+
2340
+ assert.callCount(gatt._queueCommand, 2);
2341
+ assert.calledOnceWithExactly(gatt.readRequest, handle);
2342
+ assert.calledOnceWithExactly(gatt.readBlobRequest, handle, 6);
2343
+ assert.notCalled(callback);
2344
+ });
2345
+ });
2346
+
2347
+ describe('writeHandle', () => {
2348
+ const handle = 'handle';
2349
+ let callback;
2350
+
2351
+ beforeEach(() => {
2352
+ callback = sinon.spy();
2353
+
2354
+ gatt._queueCommand = sinon.spy();
2355
+ gatt.writeRequest = sinon.spy();
2356
+ gatt.on('handleWrite', callback);
2357
+ });
2358
+
2359
+ it('should not emit event', () => {
2360
+ const data = Buffer.from([0]);
2361
+ gatt.writeHandle(handle, data, false);
2362
+
2363
+ gatt._queueCommand.callArgWith(1, data);
2364
+
2365
+ assert.callCount(gatt._queueCommand, 1);
2366
+ assert.calledOnceWithExactly(gatt.writeRequest, handle, data, false);
2367
+ assert.notCalled(callback);
2368
+ });
2369
+
2370
+ it('should emit event', () => {
2371
+ const data = Buffer.from([19]);
2372
+ gatt.writeHandle(handle, data, false);
2373
+
2374
+ gatt._queueCommand.callArgWith(1, data);
2375
+
2376
+ assert.callCount(gatt._queueCommand, 1);
2377
+ assert.calledOnceWithExactly(gatt.writeRequest, handle, data, false);
2378
+ assert.calledOnceWithExactly(callback, address, handle);
2379
+ });
2380
+
2381
+ it('should emit event on withoutResponse', () => {
2382
+ const data = Buffer.from([0]);
2383
+ gatt.writeHandle(handle, data, true);
2384
+
2385
+ gatt._queueCommand.callArgWith(2, data);
2386
+
2387
+ assert.callCount(gatt._queueCommand, 1);
2388
+ assert.calledOnceWithExactly(gatt.writeRequest, handle, data, true);
2389
+ assert.calledOnceWithExactly(callback, address, handle);
2390
+ });
2391
+ });
2392
+ });