@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.
- package/.editorconfig +11 -0
- package/.eslintrc.js +25 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/workflows/fediverse-action.yml +16 -0
- package/.github/workflows/nodepackage.yml +77 -0
- package/.github/workflows/npm-publish.yml +26 -0
- package/.github/workflows/prebuild.yml +65 -0
- package/.nycrc.json +4 -0
- package/CHANGELOG.md +119 -0
- package/LICENSE +20 -0
- package/MAINTAINERS.md +1 -0
- package/README.md +833 -0
- package/assets/noble-logo.png +0 -0
- package/assets/noble-logo.svg +13 -0
- package/binding.gyp +19 -0
- package/codecov.yml +5 -0
- package/examples/advertisement-discovery.js +65 -0
- package/examples/cache-gatt-discovery.js +198 -0
- package/examples/cache-gatt-reconnect.js +164 -0
- package/examples/echo.js +104 -0
- package/examples/enter-exit.js +78 -0
- package/examples/peripheral-explorer-async.js +133 -0
- package/examples/peripheral-explorer.js +225 -0
- package/examples/pizza/README.md +15 -0
- package/examples/pizza/central.js +194 -0
- package/examples/pizza/pizza.js +60 -0
- package/index.d.ts +203 -0
- package/index.js +6 -0
- package/lib/characteristic.js +161 -0
- package/lib/characteristics.json +449 -0
- package/lib/descriptor.js +72 -0
- package/lib/descriptors.json +47 -0
- package/lib/distributed/bindings.js +326 -0
- package/lib/hci-socket/acl-stream.js +60 -0
- package/lib/hci-socket/bindings.js +788 -0
- package/lib/hci-socket/crypto.js +74 -0
- package/lib/hci-socket/gap.js +432 -0
- package/lib/hci-socket/gatt.js +809 -0
- package/lib/hci-socket/hci-status.json +71 -0
- package/lib/hci-socket/hci.js +1264 -0
- package/lib/hci-socket/signaling.js +76 -0
- package/lib/hci-socket/smp.js +140 -0
- package/lib/hci-uart/bindings.js +569 -0
- package/lib/hci-uart/hci-serial-parser.js +70 -0
- package/lib/hci-uart/hci.js +1336 -0
- package/lib/mac/binding.gyp +26 -0
- package/lib/mac/bindings.js +11 -0
- package/lib/mac/src/ble_manager.h +41 -0
- package/lib/mac/src/ble_manager.mm +435 -0
- package/lib/mac/src/callbacks.cc +222 -0
- package/lib/mac/src/callbacks.h +84 -0
- package/lib/mac/src/napi_objc.h +12 -0
- package/lib/mac/src/napi_objc.mm +50 -0
- package/lib/mac/src/noble_mac.h +34 -0
- package/lib/mac/src/noble_mac.mm +264 -0
- package/lib/mac/src/objc_cpp.h +26 -0
- package/lib/mac/src/objc_cpp.mm +126 -0
- package/lib/mac/src/peripheral.h +23 -0
- package/lib/manufacture.js +48 -0
- package/lib/noble.js +593 -0
- package/lib/peripheral.js +219 -0
- package/lib/resolve-bindings-web.js +9 -0
- package/lib/resolve-bindings.js +44 -0
- package/lib/service.js +72 -0
- package/lib/services.json +92 -0
- package/lib/webbluetooth/bindings.js +368 -0
- package/lib/websocket/bindings.js +321 -0
- package/lib/win/binding.gyp +23 -0
- package/lib/win/bindings.js +11 -0
- package/lib/win/src/ble_manager.cc +802 -0
- package/lib/win/src/ble_manager.h +77 -0
- package/lib/win/src/callbacks.cc +274 -0
- package/lib/win/src/callbacks.h +33 -0
- package/lib/win/src/napi_winrt.cc +76 -0
- package/lib/win/src/napi_winrt.h +12 -0
- package/lib/win/src/noble_winrt.cc +308 -0
- package/lib/win/src/noble_winrt.h +34 -0
- package/lib/win/src/notify_map.cc +62 -0
- package/lib/win/src/notify_map.h +50 -0
- package/lib/win/src/peripheral.h +23 -0
- package/lib/win/src/peripheral_winrt.cc +296 -0
- package/lib/win/src/peripheral_winrt.h +82 -0
- package/lib/win/src/radio_watcher.cc +125 -0
- package/lib/win/src/radio_watcher.h +61 -0
- package/lib/win/src/winrt_cpp.cc +82 -0
- package/lib/win/src/winrt_cpp.h +11 -0
- package/lib/win/src/winrt_guid.cc +12 -0
- package/lib/win/src/winrt_guid.h +13 -0
- package/misc/nrf52840dk.hex +6921 -0
- package/misc/prj.conf +43 -0
- package/package.json +96 -0
- package/test/lib/characteristic.test.js +791 -0
- package/test/lib/descriptor.test.js +249 -0
- package/test/lib/distributed/bindings.test.js +918 -0
- package/test/lib/hci-socket/acl-stream.test.js +188 -0
- package/test/lib/hci-socket/bindings.test.js +1756 -0
- package/test/lib/hci-socket/crypto.test.js +55 -0
- package/test/lib/hci-socket/gap.test.js +1089 -0
- package/test/lib/hci-socket/gatt.test.js +2392 -0
- package/test/lib/hci-socket/hci.test.js +1891 -0
- package/test/lib/hci-socket/signaling.test.js +94 -0
- package/test/lib/hci-socket/smp.test.js +268 -0
- package/test/lib/manufacture.test.js +77 -0
- package/test/lib/peripheral.test.js +623 -0
- package/test/lib/resolve-bindings.test.js +102 -0
- package/test/lib/service.test.js +195 -0
- package/test/lib/webbluetooth/bindings.test.js +190 -0
- package/test/lib/websocket/bindings.test.js +456 -0
- package/test/noble.test.js +1565 -0
- package/test.js +131 -0
- package/with-bindings.js +5 -0
- package/ws-slave.js +404 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
const should = require('should');
|
|
2
|
+
const sinon = require('sinon');
|
|
3
|
+
|
|
4
|
+
const { assert } = sinon;
|
|
5
|
+
|
|
6
|
+
const Gap = require('../../../lib/hci-socket/gap');
|
|
7
|
+
|
|
8
|
+
describe('hci-socket gap', () => {
|
|
9
|
+
it('constructor', () => {
|
|
10
|
+
const hci = {
|
|
11
|
+
on: sinon.spy()
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const gap = new Gap(hci);
|
|
15
|
+
|
|
16
|
+
should(gap._hci).equal(hci);
|
|
17
|
+
should(gap._scanState).equal(null);
|
|
18
|
+
should(gap._scanFilterDuplicates).equal(null);
|
|
19
|
+
should(gap._discoveries).deepEqual({});
|
|
20
|
+
|
|
21
|
+
assert.callCount(hci.on, 6);
|
|
22
|
+
assert.calledWithMatch(hci.on, 'error', sinon.match.func);
|
|
23
|
+
assert.calledWithMatch(hci.on, 'leScanParametersSet', sinon.match.func);
|
|
24
|
+
assert.calledWithMatch(hci.on, 'leScanEnableSet', sinon.match.func);
|
|
25
|
+
assert.calledWithMatch(hci.on, 'leAdvertisingReport', sinon.match.func);
|
|
26
|
+
assert.calledWithMatch(hci.on, 'leScanEnableSetCmd', sinon.match.func);
|
|
27
|
+
assert.calledWithMatch(hci.on, 'leExtendedAdvertisingReport', sinon.match.func);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('setScanParameters', () => {
|
|
31
|
+
const hci = {
|
|
32
|
+
on: sinon.spy(),
|
|
33
|
+
setScanParameters: sinon.spy()
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const interval = 'interval';
|
|
37
|
+
const window = 'window';
|
|
38
|
+
|
|
39
|
+
const gap = new Gap(hci);
|
|
40
|
+
gap.setScanParameters(interval, window);
|
|
41
|
+
|
|
42
|
+
assert.calledOnceWithExactly(hci.setScanParameters, interval, window);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('startScanning', () => {
|
|
46
|
+
const hci = {
|
|
47
|
+
on: sinon.spy(),
|
|
48
|
+
setScanEnabled: sinon.spy(),
|
|
49
|
+
setScanParameters: sinon.spy()
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const gap = new Gap(hci);
|
|
53
|
+
gap.startScanning(true);
|
|
54
|
+
|
|
55
|
+
should(gap._scanState).equal('starting');
|
|
56
|
+
should(gap._scanFilterDuplicates).equal(false);
|
|
57
|
+
|
|
58
|
+
assert.callCount(hci.setScanEnabled, 2);
|
|
59
|
+
assert.calledWithExactly(hci.setScanEnabled, false, true);
|
|
60
|
+
assert.calledWithExactly(hci.setScanEnabled, true, false);
|
|
61
|
+
assert.calledOnceWithExactly(hci.setScanParameters);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('stopScanning', () => {
|
|
65
|
+
const hci = {
|
|
66
|
+
on: sinon.spy(),
|
|
67
|
+
setScanEnabled: sinon.spy()
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const gap = new Gap(hci);
|
|
71
|
+
gap.stopScanning();
|
|
72
|
+
|
|
73
|
+
should(gap._scanState).equal('stopping');
|
|
74
|
+
|
|
75
|
+
assert.calledOnceWithExactly(hci.setScanEnabled, false, true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('onHciLeScanParametersSet', () => {
|
|
79
|
+
const hci = {
|
|
80
|
+
on: sinon.spy()
|
|
81
|
+
};
|
|
82
|
+
const callback = sinon.spy();
|
|
83
|
+
|
|
84
|
+
const gap = new Gap(hci);
|
|
85
|
+
gap.on('scanParametersSet', callback);
|
|
86
|
+
gap.onHciLeScanParametersSet();
|
|
87
|
+
|
|
88
|
+
assert.calledOnceWithExactly(callback);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('onHciLeScanEnableSet', () => {
|
|
92
|
+
it('status 1', () => {
|
|
93
|
+
const hci = {
|
|
94
|
+
on: sinon.spy()
|
|
95
|
+
};
|
|
96
|
+
const scanStartCallback = sinon.spy();
|
|
97
|
+
const scanStopCallback = sinon.spy();
|
|
98
|
+
|
|
99
|
+
const gap = new Gap(hci);
|
|
100
|
+
gap.on('scanStart', scanStartCallback);
|
|
101
|
+
gap.on('scanStop', scanStopCallback);
|
|
102
|
+
gap.onHciLeScanEnableSet(1);
|
|
103
|
+
|
|
104
|
+
assert.notCalled(scanStartCallback);
|
|
105
|
+
assert.notCalled(scanStopCallback);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('status 0 but invalid state', () => {
|
|
109
|
+
const hci = {
|
|
110
|
+
on: sinon.spy()
|
|
111
|
+
};
|
|
112
|
+
const scanStartCallback = sinon.spy();
|
|
113
|
+
const scanStopCallback = sinon.spy();
|
|
114
|
+
|
|
115
|
+
const gap = new Gap(hci);
|
|
116
|
+
gap._scanState = 'unknown';
|
|
117
|
+
gap.on('scanStart', scanStartCallback);
|
|
118
|
+
gap.on('scanStop', scanStopCallback);
|
|
119
|
+
gap.onHciLeScanEnableSet(0);
|
|
120
|
+
|
|
121
|
+
should(gap._scanState).equal('unknown');
|
|
122
|
+
|
|
123
|
+
assert.notCalled(scanStartCallback);
|
|
124
|
+
assert.notCalled(scanStopCallback);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('status 0 state starting', () => {
|
|
128
|
+
const hci = {
|
|
129
|
+
on: sinon.spy()
|
|
130
|
+
};
|
|
131
|
+
const scanStartCallback = sinon.spy();
|
|
132
|
+
const scanStopCallback = sinon.spy();
|
|
133
|
+
|
|
134
|
+
const gap = new Gap(hci);
|
|
135
|
+
gap._scanFilterDuplicates = true;
|
|
136
|
+
gap._scanState = 'starting';
|
|
137
|
+
gap.on('scanStart', scanStartCallback);
|
|
138
|
+
gap.on('scanStop', scanStopCallback);
|
|
139
|
+
gap.onHciLeScanEnableSet(0);
|
|
140
|
+
|
|
141
|
+
should(gap._scanState).equal('started');
|
|
142
|
+
|
|
143
|
+
assert.calledOnceWithExactly(scanStartCallback, true);
|
|
144
|
+
assert.notCalled(scanStopCallback);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('status 0 state stopping', () => {
|
|
148
|
+
const hci = {
|
|
149
|
+
on: sinon.spy()
|
|
150
|
+
};
|
|
151
|
+
const scanStartCallback = sinon.spy();
|
|
152
|
+
const scanStopCallback = sinon.spy();
|
|
153
|
+
|
|
154
|
+
const gap = new Gap(hci);
|
|
155
|
+
gap._scanState = 'stopping';
|
|
156
|
+
gap.on('scanStart', scanStartCallback);
|
|
157
|
+
gap.on('scanStop', scanStopCallback);
|
|
158
|
+
gap.onHciLeScanEnableSet(0);
|
|
159
|
+
|
|
160
|
+
should(gap._scanState).equal('stopped');
|
|
161
|
+
|
|
162
|
+
assert.notCalled(scanStartCallback);
|
|
163
|
+
assert.calledOnceWithExactly(scanStopCallback);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('onLeScanEnableSetCmd', () => {
|
|
168
|
+
it('invalid state', () => {
|
|
169
|
+
const hci = {
|
|
170
|
+
on: sinon.spy()
|
|
171
|
+
};
|
|
172
|
+
const scanStartCallback = sinon.spy();
|
|
173
|
+
const scanStopCallback = sinon.spy();
|
|
174
|
+
|
|
175
|
+
const enable = false;
|
|
176
|
+
const filterDuplicates = false;
|
|
177
|
+
|
|
178
|
+
const gap = new Gap(hci);
|
|
179
|
+
gap._scanState = 'unknown';
|
|
180
|
+
gap.on('scanStart', scanStartCallback);
|
|
181
|
+
gap.on('scanStop', scanStopCallback);
|
|
182
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
183
|
+
|
|
184
|
+
should(gap._scanState).equal('unknown');
|
|
185
|
+
should(gap._scanFilterDuplicates).equal(null);
|
|
186
|
+
|
|
187
|
+
assert.notCalled(scanStartCallback);
|
|
188
|
+
assert.notCalled(scanStopCallback);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
['starting', 'started'].forEach(state => {
|
|
192
|
+
it(`state ${state}, do not enable`, () => {
|
|
193
|
+
const hci = {
|
|
194
|
+
on: sinon.spy()
|
|
195
|
+
};
|
|
196
|
+
const scanStartCallback = sinon.spy();
|
|
197
|
+
const scanStopCallback = sinon.spy();
|
|
198
|
+
|
|
199
|
+
const enable = false;
|
|
200
|
+
const filterDuplicates = false;
|
|
201
|
+
|
|
202
|
+
const gap = new Gap(hci);
|
|
203
|
+
gap._scanState = state;
|
|
204
|
+
gap.on('scanStart', scanStartCallback);
|
|
205
|
+
gap.on('scanStop', scanStopCallback);
|
|
206
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
207
|
+
|
|
208
|
+
should(gap._scanState).equal(state);
|
|
209
|
+
should(gap._scanFilterDuplicates).equal(null);
|
|
210
|
+
|
|
211
|
+
assert.notCalled(scanStartCallback);
|
|
212
|
+
assert.calledOnceWithExactly(scanStopCallback);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
['starting', 'started'].forEach(state => {
|
|
217
|
+
it(`state ${state}, do enable`, () => {
|
|
218
|
+
const hci = {
|
|
219
|
+
on: sinon.spy()
|
|
220
|
+
};
|
|
221
|
+
const scanStartCallback = sinon.spy();
|
|
222
|
+
const scanStopCallback = sinon.spy();
|
|
223
|
+
|
|
224
|
+
const enable = true;
|
|
225
|
+
const filterDuplicates = false;
|
|
226
|
+
|
|
227
|
+
const gap = new Gap(hci);
|
|
228
|
+
gap._scanState = state;
|
|
229
|
+
gap.on('scanStart', scanStartCallback);
|
|
230
|
+
gap.on('scanStop', scanStopCallback);
|
|
231
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
232
|
+
|
|
233
|
+
should(gap._scanState).equal(state);
|
|
234
|
+
should(gap._scanFilterDuplicates).equal(filterDuplicates);
|
|
235
|
+
|
|
236
|
+
assert.calledOnceWithExactly(scanStartCallback, filterDuplicates);
|
|
237
|
+
assert.notCalled(scanStopCallback);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
['starting', 'started'].forEach(state => {
|
|
242
|
+
it(`state ${state}, do enable, same filter`, () => {
|
|
243
|
+
const hci = {
|
|
244
|
+
on: sinon.spy()
|
|
245
|
+
};
|
|
246
|
+
const scanStartCallback = sinon.spy();
|
|
247
|
+
const scanStopCallback = sinon.spy();
|
|
248
|
+
|
|
249
|
+
const enable = true;
|
|
250
|
+
const filterDuplicates = false;
|
|
251
|
+
|
|
252
|
+
const gap = new Gap(hci);
|
|
253
|
+
gap._scanState = state;
|
|
254
|
+
gap._scanFilterDuplicates = filterDuplicates;
|
|
255
|
+
gap.on('scanStart', scanStartCallback);
|
|
256
|
+
gap.on('scanStop', scanStopCallback);
|
|
257
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
258
|
+
|
|
259
|
+
should(gap._scanState).equal(state);
|
|
260
|
+
should(gap._scanFilterDuplicates).equal(filterDuplicates);
|
|
261
|
+
|
|
262
|
+
assert.notCalled(scanStartCallback);
|
|
263
|
+
assert.notCalled(scanStopCallback);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
['stopping', 'stopped'].forEach(state => {
|
|
268
|
+
it(`state ${state}, do not enable`, () => {
|
|
269
|
+
const hci = {
|
|
270
|
+
on: sinon.spy()
|
|
271
|
+
};
|
|
272
|
+
const scanStartCallback = sinon.spy();
|
|
273
|
+
const scanStopCallback = sinon.spy();
|
|
274
|
+
|
|
275
|
+
const enable = false;
|
|
276
|
+
const filterDuplicates = false;
|
|
277
|
+
|
|
278
|
+
const gap = new Gap(hci);
|
|
279
|
+
gap._scanState = state;
|
|
280
|
+
gap.on('scanStart', scanStartCallback);
|
|
281
|
+
gap.on('scanStop', scanStopCallback);
|
|
282
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
283
|
+
|
|
284
|
+
should(gap._scanState).equal(state);
|
|
285
|
+
should(gap._scanFilterDuplicates).equal(null);
|
|
286
|
+
|
|
287
|
+
assert.notCalled(scanStartCallback);
|
|
288
|
+
assert.notCalled(scanStopCallback);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
['stopping', 'stopped'].forEach(state => {
|
|
293
|
+
it(`state ${state}, do enable`, () => {
|
|
294
|
+
const hci = {
|
|
295
|
+
on: sinon.spy()
|
|
296
|
+
};
|
|
297
|
+
const scanStartCallback = sinon.spy();
|
|
298
|
+
const scanStopCallback = sinon.spy();
|
|
299
|
+
|
|
300
|
+
const enable = true;
|
|
301
|
+
const filterDuplicates = false;
|
|
302
|
+
|
|
303
|
+
const gap = new Gap(hci);
|
|
304
|
+
gap._scanState = state;
|
|
305
|
+
gap.on('scanStart', scanStartCallback);
|
|
306
|
+
gap.on('scanStop', scanStopCallback);
|
|
307
|
+
gap.onLeScanEnableSetCmd(enable, filterDuplicates);
|
|
308
|
+
|
|
309
|
+
should(gap._scanState).equal(state);
|
|
310
|
+
should(gap._scanFilterDuplicates).equal(null);
|
|
311
|
+
|
|
312
|
+
assert.calledOnceWithExactly(scanStartCallback, null);
|
|
313
|
+
assert.notCalled(scanStopCallback);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('onHciLeAdvertisingReport', () => {
|
|
319
|
+
it('type === 0x04 / no eir', () => {
|
|
320
|
+
const hci = {
|
|
321
|
+
on: sinon.spy()
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const status = 'status';
|
|
325
|
+
const type = 0x04;
|
|
326
|
+
const address = 'a:d:d:r:e:s:s';
|
|
327
|
+
const addressType = 'addressType';
|
|
328
|
+
const eir = [];
|
|
329
|
+
const rssi = 'rssi';
|
|
330
|
+
|
|
331
|
+
const discoverCallback = sinon.spy();
|
|
332
|
+
|
|
333
|
+
const gap = new Gap(hci);
|
|
334
|
+
gap.on('discover', discoverCallback);
|
|
335
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
336
|
+
|
|
337
|
+
const expectedDiscovery = {
|
|
338
|
+
address,
|
|
339
|
+
addressType,
|
|
340
|
+
connectable: true,
|
|
341
|
+
advertisement: {
|
|
342
|
+
localName: undefined,
|
|
343
|
+
txPowerLevel: undefined,
|
|
344
|
+
manufacturerData: undefined,
|
|
345
|
+
serviceData: [],
|
|
346
|
+
serviceUuids: [],
|
|
347
|
+
solicitationServiceUuids: []
|
|
348
|
+
},
|
|
349
|
+
rssi,
|
|
350
|
+
count: 1,
|
|
351
|
+
hasScanResponse: true
|
|
352
|
+
};
|
|
353
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
354
|
+
|
|
355
|
+
assert.calledOnceWithExactly(discoverCallback, status, address, addressType, expectedDiscovery.connectable, expectedDiscovery.advertisement, rssi, false);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('type === 0x04 / no eir / previously discovered', () => {
|
|
359
|
+
const hci = {
|
|
360
|
+
on: sinon.spy()
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const status = 'status';
|
|
364
|
+
const type = 0x04;
|
|
365
|
+
const address = 'a:d:d:r:e:s:s';
|
|
366
|
+
const addressType = 'addressType';
|
|
367
|
+
const eir = [];
|
|
368
|
+
const rssi = 'rssi';
|
|
369
|
+
|
|
370
|
+
const discoverCallback = sinon.spy();
|
|
371
|
+
|
|
372
|
+
const advertisement = {
|
|
373
|
+
localName: 'localName',
|
|
374
|
+
txPowerLevel: 'txPowerLevel',
|
|
375
|
+
manufacturerData: 'manufacturerData',
|
|
376
|
+
serviceData: ['data'],
|
|
377
|
+
serviceUuids: ['uuids'],
|
|
378
|
+
solicitationServiceUuids: ['solicitation']
|
|
379
|
+
};
|
|
380
|
+
const count = 8;
|
|
381
|
+
const hasScanResponse = true;
|
|
382
|
+
const connectable = false;
|
|
383
|
+
|
|
384
|
+
const gap = new Gap(hci);
|
|
385
|
+
gap._discoveries[address] = { advertisement, count, hasScanResponse, connectable };
|
|
386
|
+
gap.on('discover', discoverCallback);
|
|
387
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
388
|
+
|
|
389
|
+
const expectedDiscovery = {
|
|
390
|
+
address,
|
|
391
|
+
addressType,
|
|
392
|
+
connectable,
|
|
393
|
+
advertisement,
|
|
394
|
+
rssi,
|
|
395
|
+
count: count + 1,
|
|
396
|
+
hasScanResponse
|
|
397
|
+
};
|
|
398
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
399
|
+
|
|
400
|
+
assert.calledOnceWithExactly(discoverCallback, status, address, addressType, expectedDiscovery.connectable, expectedDiscovery.advertisement, rssi, false);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('type === 0x06 / scannable', () => {
|
|
404
|
+
const hci = {
|
|
405
|
+
on: sinon.spy()
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const status = 'status';
|
|
409
|
+
const type = 0x06;
|
|
410
|
+
const address = 'a:d:d:r:e:s:s';
|
|
411
|
+
const addressType = 'addressType';
|
|
412
|
+
const eir = [];
|
|
413
|
+
const rssi = 'rssi';
|
|
414
|
+
|
|
415
|
+
const discoverCallback = sinon.spy();
|
|
416
|
+
|
|
417
|
+
const advertisement = {
|
|
418
|
+
localName: 'localName',
|
|
419
|
+
txPowerLevel: 'txPowerLevel',
|
|
420
|
+
manufacturerData: 'manufacturerData',
|
|
421
|
+
serviceData: ['data'],
|
|
422
|
+
serviceUuids: ['uuids'],
|
|
423
|
+
solicitationServiceUuids: ['solicitation']
|
|
424
|
+
};
|
|
425
|
+
const count = 8;
|
|
426
|
+
const hasScanResponse = false;
|
|
427
|
+
const connectable = false;
|
|
428
|
+
|
|
429
|
+
const gap = new Gap(hci);
|
|
430
|
+
gap._discoveries[address] = { advertisement, count, hasScanResponse, connectable };
|
|
431
|
+
gap.on('discover', discoverCallback);
|
|
432
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
433
|
+
|
|
434
|
+
const expectedDiscovery = {
|
|
435
|
+
address,
|
|
436
|
+
addressType,
|
|
437
|
+
connectable: true,
|
|
438
|
+
advertisement,
|
|
439
|
+
rssi,
|
|
440
|
+
count: count + 1,
|
|
441
|
+
hasScanResponse
|
|
442
|
+
};
|
|
443
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
444
|
+
|
|
445
|
+
assert.calledOnceWithExactly(discoverCallback, status, address, addressType, expectedDiscovery.connectable, expectedDiscovery.advertisement, rssi, true);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('type !== 0x04 / no eir', () => {
|
|
449
|
+
const hci = {
|
|
450
|
+
on: sinon.spy()
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const status = 'status';
|
|
454
|
+
const type = 0x01;
|
|
455
|
+
const address = 'a:d:d:r:e:s:s';
|
|
456
|
+
const addressType = 'addressType';
|
|
457
|
+
const eir = [];
|
|
458
|
+
const rssi = 'rssi';
|
|
459
|
+
|
|
460
|
+
const discoverCallback = sinon.spy();
|
|
461
|
+
|
|
462
|
+
const gap = new Gap(hci);
|
|
463
|
+
gap.on('discover', discoverCallback);
|
|
464
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
465
|
+
|
|
466
|
+
const expectedDiscovery = {
|
|
467
|
+
address,
|
|
468
|
+
addressType,
|
|
469
|
+
connectable: true,
|
|
470
|
+
advertisement: {
|
|
471
|
+
localName: undefined,
|
|
472
|
+
txPowerLevel: undefined,
|
|
473
|
+
manufacturerData: undefined,
|
|
474
|
+
serviceData: [],
|
|
475
|
+
serviceUuids: [],
|
|
476
|
+
solicitationServiceUuids: [],
|
|
477
|
+
serviceSolicitationUuids: []
|
|
478
|
+
},
|
|
479
|
+
rssi,
|
|
480
|
+
count: 1,
|
|
481
|
+
hasScanResponse: false
|
|
482
|
+
};
|
|
483
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
484
|
+
|
|
485
|
+
assert.notCalled(discoverCallback);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('invalid EIR data, length === 0', () => {
|
|
489
|
+
const hci = {
|
|
490
|
+
on: sinon.spy()
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const status = 'status';
|
|
494
|
+
const type = 0x01;
|
|
495
|
+
const address = 'a:d:d:r:e:s:s';
|
|
496
|
+
const addressType = 'addressType';
|
|
497
|
+
const eir = Buffer.from([0x00, 0x00]);
|
|
498
|
+
const rssi = 'rssi';
|
|
499
|
+
|
|
500
|
+
const discoverCallback = sinon.spy();
|
|
501
|
+
|
|
502
|
+
const gap = new Gap(hci);
|
|
503
|
+
gap.on('discover', discoverCallback);
|
|
504
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
505
|
+
|
|
506
|
+
const expectedDiscovery = {
|
|
507
|
+
address,
|
|
508
|
+
addressType,
|
|
509
|
+
connectable: true,
|
|
510
|
+
advertisement: {
|
|
511
|
+
localName: undefined,
|
|
512
|
+
txPowerLevel: undefined,
|
|
513
|
+
manufacturerData: undefined,
|
|
514
|
+
serviceData: [],
|
|
515
|
+
serviceUuids: [],
|
|
516
|
+
solicitationServiceUuids: [],
|
|
517
|
+
serviceSolicitationUuids: []
|
|
518
|
+
},
|
|
519
|
+
rssi,
|
|
520
|
+
count: 1,
|
|
521
|
+
hasScanResponse: false
|
|
522
|
+
};
|
|
523
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
524
|
+
|
|
525
|
+
assert.notCalled(discoverCallback);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('invalid EIR data, out of range of buffer length', () => {
|
|
529
|
+
const hci = {
|
|
530
|
+
on: sinon.spy()
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const status = 'status';
|
|
534
|
+
const type = 0x01;
|
|
535
|
+
const address = 'a:d:d:r:e:s:s';
|
|
536
|
+
const addressType = 'addressType';
|
|
537
|
+
const eir = Buffer.from([0x03, 0x01, 0x02]);
|
|
538
|
+
const rssi = 'rssi';
|
|
539
|
+
|
|
540
|
+
const discoverCallback = sinon.spy();
|
|
541
|
+
|
|
542
|
+
const gap = new Gap(hci);
|
|
543
|
+
gap.on('discover', discoverCallback);
|
|
544
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
545
|
+
|
|
546
|
+
const expectedDiscovery = {
|
|
547
|
+
address,
|
|
548
|
+
addressType,
|
|
549
|
+
connectable: true,
|
|
550
|
+
advertisement: {
|
|
551
|
+
localName: undefined,
|
|
552
|
+
txPowerLevel: undefined,
|
|
553
|
+
manufacturerData: undefined,
|
|
554
|
+
serviceData: [],
|
|
555
|
+
serviceUuids: [],
|
|
556
|
+
solicitationServiceUuids: [],
|
|
557
|
+
serviceSolicitationUuids: []
|
|
558
|
+
},
|
|
559
|
+
rssi,
|
|
560
|
+
count: 1,
|
|
561
|
+
hasScanResponse: false
|
|
562
|
+
};
|
|
563
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
564
|
+
|
|
565
|
+
assert.notCalled(discoverCallback);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
[0x02, 0x03].forEach(eirType => {
|
|
569
|
+
it(`List of 16-bit Service Class UUIDs (EIR type = ${eirType})`, () => {
|
|
570
|
+
const hci = {
|
|
571
|
+
on: sinon.spy()
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const status = 'status';
|
|
575
|
+
const type = 0x01;
|
|
576
|
+
const address = 'a:d:d:r:e:s:s';
|
|
577
|
+
const addressType = 'addressType';
|
|
578
|
+
const rssi = 'rssi';
|
|
579
|
+
|
|
580
|
+
const serviceUuid = Buffer.from([0x01, 0x02]);
|
|
581
|
+
const eirLength = (serviceUuid.length * 2) + 1;
|
|
582
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
583
|
+
// Add service twice to check unicity
|
|
584
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceUuid]);
|
|
585
|
+
|
|
586
|
+
const discoverCallback = sinon.spy();
|
|
587
|
+
|
|
588
|
+
const gap = new Gap(hci);
|
|
589
|
+
gap.on('discover', discoverCallback);
|
|
590
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
591
|
+
|
|
592
|
+
const expectedDiscovery = {
|
|
593
|
+
address,
|
|
594
|
+
addressType,
|
|
595
|
+
connectable: true,
|
|
596
|
+
advertisement: {
|
|
597
|
+
localName: undefined,
|
|
598
|
+
txPowerLevel: undefined,
|
|
599
|
+
manufacturerData: undefined,
|
|
600
|
+
serviceData: [],
|
|
601
|
+
serviceUuids: ['201'],
|
|
602
|
+
solicitationServiceUuids: [],
|
|
603
|
+
serviceSolicitationUuids: []
|
|
604
|
+
},
|
|
605
|
+
rssi,
|
|
606
|
+
count: 1,
|
|
607
|
+
hasScanResponse: false
|
|
608
|
+
};
|
|
609
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
610
|
+
|
|
611
|
+
assert.notCalled(discoverCallback);
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
[0x06, 0x07].forEach(eirType => {
|
|
616
|
+
it(`List of 128-bit Service Class UUIDs (EIR type = ${eirType})`, () => {
|
|
617
|
+
const hci = {
|
|
618
|
+
on: sinon.spy()
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const status = 'status';
|
|
622
|
+
const type = 0x01;
|
|
623
|
+
const address = 'a:d:d:r:e:s:s';
|
|
624
|
+
const addressType = 'addressType';
|
|
625
|
+
const rssi = 'rssi';
|
|
626
|
+
|
|
627
|
+
const serviceUuid = Buffer.from([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]);
|
|
628
|
+
const eirLength = (serviceUuid.length * 2) + 1;
|
|
629
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
630
|
+
// Add service twice to check unicity
|
|
631
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceUuid]);
|
|
632
|
+
|
|
633
|
+
const discoverCallback = sinon.spy();
|
|
634
|
+
|
|
635
|
+
const gap = new Gap(hci);
|
|
636
|
+
gap.on('discover', discoverCallback);
|
|
637
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
638
|
+
|
|
639
|
+
const expectedDiscovery = {
|
|
640
|
+
address,
|
|
641
|
+
addressType,
|
|
642
|
+
connectable: true,
|
|
643
|
+
advertisement: {
|
|
644
|
+
localName: undefined,
|
|
645
|
+
txPowerLevel: undefined,
|
|
646
|
+
manufacturerData: undefined,
|
|
647
|
+
serviceData: [],
|
|
648
|
+
serviceUuids: ['0f0e0d0c0b0a09080706050403020100'],
|
|
649
|
+
solicitationServiceUuids: [],
|
|
650
|
+
serviceSolicitationUuids: []
|
|
651
|
+
},
|
|
652
|
+
rssi,
|
|
653
|
+
count: 1,
|
|
654
|
+
hasScanResponse: false
|
|
655
|
+
};
|
|
656
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
657
|
+
|
|
658
|
+
assert.notCalled(discoverCallback);
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
[0x08, 0x09].forEach(eirType => {
|
|
663
|
+
it(`Local name (EIR type = ${eirType})`, () => {
|
|
664
|
+
const hci = {
|
|
665
|
+
on: sinon.spy()
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
const status = 'status';
|
|
669
|
+
const type = 0x01;
|
|
670
|
+
const address = 'a:d:d:r:e:s:s';
|
|
671
|
+
const addressType = 'addressType';
|
|
672
|
+
const rssi = 'rssi';
|
|
673
|
+
|
|
674
|
+
const localNameHex = Buffer.from([0x6d, 0x79, 0x2d, 0x6e, 0x61, 0x6d, 0x65]);
|
|
675
|
+
const eirLength = localNameHex.length + 1;
|
|
676
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
677
|
+
const eir = Buffer.concat([eirLengthAndType, localNameHex]);
|
|
678
|
+
|
|
679
|
+
const discoverCallback = sinon.spy();
|
|
680
|
+
|
|
681
|
+
const gap = new Gap(hci);
|
|
682
|
+
gap.on('discover', discoverCallback);
|
|
683
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
684
|
+
|
|
685
|
+
const expectedDiscovery = {
|
|
686
|
+
address,
|
|
687
|
+
addressType,
|
|
688
|
+
connectable: true,
|
|
689
|
+
advertisement: {
|
|
690
|
+
localName: 'my-name',
|
|
691
|
+
txPowerLevel: undefined,
|
|
692
|
+
manufacturerData: undefined,
|
|
693
|
+
serviceData: [],
|
|
694
|
+
serviceUuids: [],
|
|
695
|
+
solicitationServiceUuids: [],
|
|
696
|
+
serviceSolicitationUuids: []
|
|
697
|
+
},
|
|
698
|
+
rssi,
|
|
699
|
+
count: 1,
|
|
700
|
+
hasScanResponse: false
|
|
701
|
+
};
|
|
702
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
703
|
+
|
|
704
|
+
assert.notCalled(discoverCallback);
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('txPowerLevel (EIR type = 10)', () => {
|
|
709
|
+
const hci = {
|
|
710
|
+
on: sinon.spy()
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const status = 'status';
|
|
714
|
+
const type = 0x01;
|
|
715
|
+
const address = 'a:d:d:r:e:s:s';
|
|
716
|
+
const addressType = 'addressType';
|
|
717
|
+
const rssi = 'rssi';
|
|
718
|
+
|
|
719
|
+
const eirType = 0x0a;
|
|
720
|
+
const localNameHex = Buffer.from([0x20]);
|
|
721
|
+
const eirLength = localNameHex.length + 1;
|
|
722
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
723
|
+
const eir = Buffer.concat([eirLengthAndType, localNameHex]);
|
|
724
|
+
|
|
725
|
+
const discoverCallback = sinon.spy();
|
|
726
|
+
|
|
727
|
+
const gap = new Gap(hci);
|
|
728
|
+
gap.on('discover', discoverCallback);
|
|
729
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
730
|
+
|
|
731
|
+
const expectedDiscovery = {
|
|
732
|
+
address,
|
|
733
|
+
addressType,
|
|
734
|
+
connectable: true,
|
|
735
|
+
advertisement: {
|
|
736
|
+
localName: undefined,
|
|
737
|
+
txPowerLevel: 32,
|
|
738
|
+
manufacturerData: undefined,
|
|
739
|
+
serviceData: [],
|
|
740
|
+
serviceUuids: [],
|
|
741
|
+
solicitationServiceUuids: [],
|
|
742
|
+
serviceSolicitationUuids: []
|
|
743
|
+
},
|
|
744
|
+
rssi,
|
|
745
|
+
count: 1,
|
|
746
|
+
hasScanResponse: false
|
|
747
|
+
};
|
|
748
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
749
|
+
|
|
750
|
+
assert.notCalled(discoverCallback);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('List of 16 bit solicitation UUIDs (EIR type = 20)', () => {
|
|
754
|
+
const hci = {
|
|
755
|
+
on: sinon.spy()
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
const status = 'status';
|
|
759
|
+
const type = 0x01;
|
|
760
|
+
const address = 'a:d:d:r:e:s:s';
|
|
761
|
+
const addressType = 'addressType';
|
|
762
|
+
const rssi = 'rssi';
|
|
763
|
+
|
|
764
|
+
const eirType = 0x14;
|
|
765
|
+
const serviceUuid = Buffer.from([0x01, 0x02]);
|
|
766
|
+
const eirLength = (serviceUuid.length * 2) + 1;
|
|
767
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
768
|
+
// Add service twice to check unicity
|
|
769
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceUuid]);
|
|
770
|
+
|
|
771
|
+
const discoverCallback = sinon.spy();
|
|
772
|
+
|
|
773
|
+
const gap = new Gap(hci);
|
|
774
|
+
gap.on('discover', discoverCallback);
|
|
775
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
776
|
+
|
|
777
|
+
const expectedDiscovery = {
|
|
778
|
+
address,
|
|
779
|
+
addressType,
|
|
780
|
+
connectable: true,
|
|
781
|
+
advertisement: {
|
|
782
|
+
localName: undefined,
|
|
783
|
+
txPowerLevel: undefined,
|
|
784
|
+
manufacturerData: undefined,
|
|
785
|
+
serviceData: [],
|
|
786
|
+
serviceUuids: [],
|
|
787
|
+
solicitationServiceUuids: [],
|
|
788
|
+
serviceSolicitationUuids: ['201']
|
|
789
|
+
},
|
|
790
|
+
rssi,
|
|
791
|
+
count: 1,
|
|
792
|
+
hasScanResponse: false
|
|
793
|
+
};
|
|
794
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
795
|
+
|
|
796
|
+
assert.notCalled(discoverCallback);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('List of 128 bit solicitation UUIDs (EIR type = 21)', () => {
|
|
800
|
+
const hci = {
|
|
801
|
+
on: sinon.spy()
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
const status = 'status';
|
|
805
|
+
const type = 0x01;
|
|
806
|
+
const address = 'a:d:d:r:e:s:s';
|
|
807
|
+
const addressType = 'addressType';
|
|
808
|
+
const rssi = 'rssi';
|
|
809
|
+
|
|
810
|
+
const eirType = 0x15;
|
|
811
|
+
const serviceUuid = Buffer.from([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]);
|
|
812
|
+
const eirLength = (serviceUuid.length * 2) + 1;
|
|
813
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
814
|
+
// Add service twice to check unicity
|
|
815
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceUuid]);
|
|
816
|
+
|
|
817
|
+
const discoverCallback = sinon.spy();
|
|
818
|
+
|
|
819
|
+
const gap = new Gap(hci);
|
|
820
|
+
gap.on('discover', discoverCallback);
|
|
821
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
822
|
+
|
|
823
|
+
const expectedDiscovery = {
|
|
824
|
+
address,
|
|
825
|
+
addressType,
|
|
826
|
+
connectable: true,
|
|
827
|
+
advertisement: {
|
|
828
|
+
localName: undefined,
|
|
829
|
+
txPowerLevel: undefined,
|
|
830
|
+
manufacturerData: undefined,
|
|
831
|
+
serviceData: [],
|
|
832
|
+
serviceUuids: [],
|
|
833
|
+
solicitationServiceUuids: [],
|
|
834
|
+
serviceSolicitationUuids: ['0f0e0d0c0b0a09080706050403020100']
|
|
835
|
+
},
|
|
836
|
+
rssi,
|
|
837
|
+
count: 1,
|
|
838
|
+
hasScanResponse: false
|
|
839
|
+
};
|
|
840
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
841
|
+
|
|
842
|
+
assert.notCalled(discoverCallback);
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it('16-bit Service Data (EIR type = 22)', () => {
|
|
846
|
+
const hci = {
|
|
847
|
+
on: sinon.spy()
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const status = 'status';
|
|
851
|
+
const type = 0x01;
|
|
852
|
+
const address = 'a:d:d:r:e:s:s';
|
|
853
|
+
const addressType = 'addressType';
|
|
854
|
+
const rssi = 'rssi';
|
|
855
|
+
|
|
856
|
+
const eirType = 0x16;
|
|
857
|
+
const serviceUuid = Buffer.from([0x01, 0x02]);
|
|
858
|
+
const serviceData = Buffer.from([0x03, 0x04]);
|
|
859
|
+
const eirLength = serviceUuid.length + serviceData.length + 1;
|
|
860
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
861
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceData]);
|
|
862
|
+
|
|
863
|
+
const discoverCallback = sinon.spy();
|
|
864
|
+
|
|
865
|
+
const gap = new Gap(hci);
|
|
866
|
+
gap.on('discover', discoverCallback);
|
|
867
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
868
|
+
|
|
869
|
+
const expectedDiscovery = {
|
|
870
|
+
address,
|
|
871
|
+
addressType,
|
|
872
|
+
connectable: true,
|
|
873
|
+
advertisement: {
|
|
874
|
+
localName: undefined,
|
|
875
|
+
txPowerLevel: undefined,
|
|
876
|
+
manufacturerData: undefined,
|
|
877
|
+
serviceData: [
|
|
878
|
+
{
|
|
879
|
+
uuid: '0201',
|
|
880
|
+
data: serviceData
|
|
881
|
+
}
|
|
882
|
+
],
|
|
883
|
+
serviceUuids: [],
|
|
884
|
+
solicitationServiceUuids: [],
|
|
885
|
+
serviceSolicitationUuids: []
|
|
886
|
+
},
|
|
887
|
+
rssi,
|
|
888
|
+
count: 1,
|
|
889
|
+
hasScanResponse: false
|
|
890
|
+
};
|
|
891
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
892
|
+
|
|
893
|
+
assert.notCalled(discoverCallback);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('32-bit Service Data (EIR type = 32)', () => {
|
|
897
|
+
const hci = {
|
|
898
|
+
on: sinon.spy()
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const status = 'status';
|
|
902
|
+
const type = 0x01;
|
|
903
|
+
const address = 'a:d:d:r:e:s:s';
|
|
904
|
+
const addressType = 'addressType';
|
|
905
|
+
const rssi = 'rssi';
|
|
906
|
+
|
|
907
|
+
const eirType = 0x20;
|
|
908
|
+
const serviceUuid = Buffer.from([0x01, 0x02, 0x03, 0x04]);
|
|
909
|
+
const serviceData = Buffer.from([0x05, 0x06]);
|
|
910
|
+
const eirLength = serviceUuid.length + serviceData.length + 1;
|
|
911
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
912
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceData]);
|
|
913
|
+
|
|
914
|
+
const discoverCallback = sinon.spy();
|
|
915
|
+
|
|
916
|
+
const gap = new Gap(hci);
|
|
917
|
+
gap.on('discover', discoverCallback);
|
|
918
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
919
|
+
|
|
920
|
+
const expectedDiscovery = {
|
|
921
|
+
address,
|
|
922
|
+
addressType,
|
|
923
|
+
connectable: true,
|
|
924
|
+
advertisement: {
|
|
925
|
+
localName: undefined,
|
|
926
|
+
txPowerLevel: undefined,
|
|
927
|
+
manufacturerData: undefined,
|
|
928
|
+
serviceData: [
|
|
929
|
+
{
|
|
930
|
+
uuid: '04030201',
|
|
931
|
+
data: serviceData
|
|
932
|
+
}
|
|
933
|
+
],
|
|
934
|
+
serviceUuids: [],
|
|
935
|
+
solicitationServiceUuids: [],
|
|
936
|
+
serviceSolicitationUuids: []
|
|
937
|
+
},
|
|
938
|
+
rssi,
|
|
939
|
+
count: 1,
|
|
940
|
+
hasScanResponse: false
|
|
941
|
+
};
|
|
942
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
943
|
+
|
|
944
|
+
assert.notCalled(discoverCallback);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
it('128-bit Service Data (EIR type = 33)', () => {
|
|
948
|
+
const hci = {
|
|
949
|
+
on: sinon.spy()
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
const status = 'status';
|
|
953
|
+
const type = 0x01;
|
|
954
|
+
const address = 'a:d:d:r:e:s:s';
|
|
955
|
+
const addressType = 'addressType';
|
|
956
|
+
const rssi = 'rssi';
|
|
957
|
+
|
|
958
|
+
const eirType = 0x21;
|
|
959
|
+
const serviceUuid = Buffer.from([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]);
|
|
960
|
+
const serviceData = Buffer.from([0x05, 0x06]);
|
|
961
|
+
const eirLength = serviceUuid.length + serviceData.length + 1;
|
|
962
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
963
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceData]);
|
|
964
|
+
|
|
965
|
+
const discoverCallback = sinon.spy();
|
|
966
|
+
|
|
967
|
+
const gap = new Gap(hci);
|
|
968
|
+
gap.on('discover', discoverCallback);
|
|
969
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
970
|
+
|
|
971
|
+
const expectedDiscovery = {
|
|
972
|
+
address,
|
|
973
|
+
addressType,
|
|
974
|
+
connectable: true,
|
|
975
|
+
advertisement: {
|
|
976
|
+
localName: undefined,
|
|
977
|
+
txPowerLevel: undefined,
|
|
978
|
+
manufacturerData: undefined,
|
|
979
|
+
serviceData: [
|
|
980
|
+
{
|
|
981
|
+
uuid: '0f0e0d0c0b0a09080706050403020100',
|
|
982
|
+
data: serviceData
|
|
983
|
+
}
|
|
984
|
+
],
|
|
985
|
+
serviceUuids: [],
|
|
986
|
+
solicitationServiceUuids: [],
|
|
987
|
+
serviceSolicitationUuids: []
|
|
988
|
+
},
|
|
989
|
+
rssi,
|
|
990
|
+
count: 1,
|
|
991
|
+
hasScanResponse: false
|
|
992
|
+
};
|
|
993
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
994
|
+
|
|
995
|
+
assert.notCalled(discoverCallback);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
it('List of 32 bit solicitation UUIDs (EIR type = 31)', () => {
|
|
999
|
+
const hci = {
|
|
1000
|
+
on: sinon.spy()
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
const status = 'status';
|
|
1004
|
+
const type = 0x01;
|
|
1005
|
+
const address = 'a:d:d:r:e:s:s';
|
|
1006
|
+
const addressType = 'addressType';
|
|
1007
|
+
const rssi = 'rssi';
|
|
1008
|
+
|
|
1009
|
+
const eirType = 0x1f;
|
|
1010
|
+
const serviceUuid = Buffer.from([0x01, 0x02, 0x03, 0x04]);
|
|
1011
|
+
const eirLength = (serviceUuid.length * 2) + 1;
|
|
1012
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
1013
|
+
// Add service twice to check unicity
|
|
1014
|
+
const eir = Buffer.concat([eirLengthAndType, serviceUuid, serviceUuid]);
|
|
1015
|
+
|
|
1016
|
+
const discoverCallback = sinon.spy();
|
|
1017
|
+
|
|
1018
|
+
const gap = new Gap(hci);
|
|
1019
|
+
gap.on('discover', discoverCallback);
|
|
1020
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
1021
|
+
|
|
1022
|
+
const expectedDiscovery = {
|
|
1023
|
+
address,
|
|
1024
|
+
addressType,
|
|
1025
|
+
connectable: true,
|
|
1026
|
+
advertisement: {
|
|
1027
|
+
localName: undefined,
|
|
1028
|
+
txPowerLevel: undefined,
|
|
1029
|
+
manufacturerData: undefined,
|
|
1030
|
+
serviceData: [],
|
|
1031
|
+
serviceUuids: [],
|
|
1032
|
+
solicitationServiceUuids: [],
|
|
1033
|
+
serviceSolicitationUuids: ['4030201']
|
|
1034
|
+
},
|
|
1035
|
+
rssi,
|
|
1036
|
+
count: 1,
|
|
1037
|
+
hasScanResponse: false
|
|
1038
|
+
};
|
|
1039
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
1040
|
+
|
|
1041
|
+
assert.notCalled(discoverCallback);
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('Manufacturer Specific Data (EIR type = 255)', () => {
|
|
1045
|
+
const hci = {
|
|
1046
|
+
on: sinon.spy()
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
const status = 'status';
|
|
1050
|
+
const type = 0x01;
|
|
1051
|
+
const address = 'a:d:d:r:e:s:s';
|
|
1052
|
+
const addressType = 'addressType';
|
|
1053
|
+
const rssi = 'rssi';
|
|
1054
|
+
|
|
1055
|
+
const eirType = 0xff;
|
|
1056
|
+
const manufacturerData = Buffer.from([0x01, 0x02, 0x03, 0x04]);
|
|
1057
|
+
const eirLength = manufacturerData.length + 1;
|
|
1058
|
+
const eirLengthAndType = Buffer.from([eirLength, eirType]);
|
|
1059
|
+
const eir = Buffer.concat([eirLengthAndType, manufacturerData, manufacturerData]);
|
|
1060
|
+
|
|
1061
|
+
const discoverCallback = sinon.spy();
|
|
1062
|
+
|
|
1063
|
+
const gap = new Gap(hci);
|
|
1064
|
+
gap.on('discover', discoverCallback);
|
|
1065
|
+
gap.onHciLeAdvertisingReport(status, type, address, addressType, eir, rssi);
|
|
1066
|
+
|
|
1067
|
+
const expectedDiscovery = {
|
|
1068
|
+
address,
|
|
1069
|
+
addressType,
|
|
1070
|
+
connectable: true,
|
|
1071
|
+
advertisement: {
|
|
1072
|
+
localName: undefined,
|
|
1073
|
+
txPowerLevel: undefined,
|
|
1074
|
+
manufacturerData,
|
|
1075
|
+
serviceData: [],
|
|
1076
|
+
serviceUuids: [],
|
|
1077
|
+
solicitationServiceUuids: [],
|
|
1078
|
+
serviceSolicitationUuids: []
|
|
1079
|
+
},
|
|
1080
|
+
rssi,
|
|
1081
|
+
count: 1,
|
|
1082
|
+
hasScanResponse: false
|
|
1083
|
+
};
|
|
1084
|
+
should(gap._discoveries[address]).deepEqual(expectedDiscovery);
|
|
1085
|
+
|
|
1086
|
+
assert.notCalled(discoverCallback);
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
});
|