@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,74 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
function r () {
|
|
4
|
+
return crypto.randomBytes(16);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function c1 (k, r, pres, preq, iat, ia, rat, ra) {
|
|
8
|
+
const p1 = Buffer.concat([
|
|
9
|
+
iat,
|
|
10
|
+
rat,
|
|
11
|
+
preq,
|
|
12
|
+
pres
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const p2 = Buffer.concat([
|
|
16
|
+
ra,
|
|
17
|
+
ia,
|
|
18
|
+
Buffer.from('00000000', 'hex')
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
let res = xor(r, p1);
|
|
22
|
+
res = e(k, res);
|
|
23
|
+
res = xor(res, p2);
|
|
24
|
+
res = e(k, res);
|
|
25
|
+
|
|
26
|
+
return res;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function s1 (k, r1, r2) {
|
|
30
|
+
return e(k, Buffer.concat([
|
|
31
|
+
r2.slice(0, 8),
|
|
32
|
+
r1.slice(0, 8)
|
|
33
|
+
]));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function e (key, data) {
|
|
37
|
+
key = swap(key);
|
|
38
|
+
data = swap(data);
|
|
39
|
+
|
|
40
|
+
const cipher = crypto.createCipheriv('aes-128-ecb', key, '');
|
|
41
|
+
cipher.setAutoPadding(false);
|
|
42
|
+
|
|
43
|
+
return swap(Buffer.concat([
|
|
44
|
+
cipher.update(data),
|
|
45
|
+
cipher.final()
|
|
46
|
+
]));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function xor (b1, b2) {
|
|
50
|
+
const result = Buffer.alloc(b1.length);
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < b1.length; i++) {
|
|
53
|
+
result[i] = b1[i] ^ b2[i];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function swap (input) {
|
|
60
|
+
const output = Buffer.alloc(input.length);
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < output.length; i++) {
|
|
63
|
+
output[i] = input[input.length - i - 1];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return output;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
r: r,
|
|
71
|
+
c1: c1,
|
|
72
|
+
s1: s1,
|
|
73
|
+
e: e
|
|
74
|
+
};
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
const debug = require('debug')('gap');
|
|
2
|
+
|
|
3
|
+
const events = require('events');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const util = require('util');
|
|
6
|
+
|
|
7
|
+
const isChip = os.platform() === 'linux' && os.release().indexOf('-ntc') !== -1;
|
|
8
|
+
|
|
9
|
+
const LE_META_EVENT_TYPE_CONNECTABLE = 0x3;
|
|
10
|
+
const LE_META_EVENT_TYPE_SCAN_RESPONSE = 0x4;
|
|
11
|
+
const LE_META_EVENT_TYPE_SCANNABLE = 0x6;
|
|
12
|
+
|
|
13
|
+
const LE_META_EXTENDED_EVENT_TYPE_CONNECTABLE_MASK = 0x1;
|
|
14
|
+
const LE_META_EXTENDED_EVENT_TYPE_SCANNABLE_MASK = 0x2;
|
|
15
|
+
const LE_META_EXTENDED_EVENT_TYPE_SCAN_RESPONSE_MASK = 0x8;
|
|
16
|
+
const LE_META_EXTENDED_EVENT_TYPE_INCOMPLETE_MASK = 0x20;
|
|
17
|
+
|
|
18
|
+
const Gap = function (hci) {
|
|
19
|
+
this._hci = hci;
|
|
20
|
+
|
|
21
|
+
this._scanState = null;
|
|
22
|
+
this._scanFilterDuplicates = null;
|
|
23
|
+
this._discoveries = {};
|
|
24
|
+
|
|
25
|
+
this._hci.on('error', this.onHciError.bind(this));
|
|
26
|
+
this._hci.on('leScanParametersSet', this.onHciLeScanParametersSet.bind(this));
|
|
27
|
+
this._hci.on('leScanEnableSet', this.onHciLeScanEnableSet.bind(this));
|
|
28
|
+
this._hci.on('leAdvertisingReport', this.onHciLeAdvertisingReport.bind(this));
|
|
29
|
+
this._hci.on(
|
|
30
|
+
'leExtendedAdvertisingReport',
|
|
31
|
+
this.onHciLeExtendedAdvertisingReport.bind(this)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
this._hci.on('leScanEnableSetCmd', this.onLeScanEnableSetCmd.bind(this));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
util.inherits(Gap, events.EventEmitter);
|
|
38
|
+
|
|
39
|
+
Gap.prototype.setScanParameters = function (interval, window) {
|
|
40
|
+
this._hci.setScanParameters(interval, window);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
Gap.prototype.startScanning = function (allowDuplicates) {
|
|
44
|
+
this._scanState = 'starting';
|
|
45
|
+
this._scanFilterDuplicates = !allowDuplicates;
|
|
46
|
+
|
|
47
|
+
// Always set scan parameters before scanning
|
|
48
|
+
// https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=229737
|
|
49
|
+
// p106 - p107
|
|
50
|
+
this._hci.setScanEnabled(false, true);
|
|
51
|
+
this._hci.setScanParameters();
|
|
52
|
+
|
|
53
|
+
if (isChip) {
|
|
54
|
+
// work around for Next Thing Co. C.H.I.P, always allow duplicates, to get scan response
|
|
55
|
+
this._scanFilterDuplicates = false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this._hci.setScanEnabled(true, this._scanFilterDuplicates);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Gap.prototype.stopScanning = function () {
|
|
62
|
+
this._scanState = 'stopping';
|
|
63
|
+
|
|
64
|
+
this._hci.setScanEnabled(false, true);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
Gap.prototype.onHciError = function (error) {
|
|
68
|
+
console.warn(error); // TODO: Better error handling
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
Gap.prototype.onHciLeScanParametersSet = function () {
|
|
72
|
+
this.emit('scanParametersSet');
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Called when receive an event "Command Complete" for "LE Set Scan Enable"
|
|
76
|
+
Gap.prototype.onHciLeScanEnableSet = function (status) {
|
|
77
|
+
// Check the status we got from the command complete function.
|
|
78
|
+
if (status !== 0) {
|
|
79
|
+
// If it is non-zero there was an error, and we should not change
|
|
80
|
+
// our status as a result.
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this._scanState === 'starting') {
|
|
85
|
+
this._scanState = 'started';
|
|
86
|
+
|
|
87
|
+
this.emit('scanStart', this._scanFilterDuplicates);
|
|
88
|
+
} else if (this._scanState === 'stopping') {
|
|
89
|
+
this._scanState = 'stopped';
|
|
90
|
+
|
|
91
|
+
this.emit('scanStop');
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Called when we see the actual command "LE Set Scan Enable"
|
|
96
|
+
Gap.prototype.onLeScanEnableSetCmd = function (enable, filterDuplicates) {
|
|
97
|
+
// Check to see if the new settings differ from what we expect.
|
|
98
|
+
// If we are scanning, then a change happens if the new command stops
|
|
99
|
+
// scanning or if duplicate filtering changes.
|
|
100
|
+
// If we are not scanning, then a change happens if scanning was enabled.
|
|
101
|
+
if (this._scanState === 'starting' || this._scanState === 'started') {
|
|
102
|
+
if (!enable) {
|
|
103
|
+
this.emit('scanStop');
|
|
104
|
+
} else if (this._scanFilterDuplicates !== filterDuplicates) {
|
|
105
|
+
this._scanFilterDuplicates = filterDuplicates;
|
|
106
|
+
|
|
107
|
+
this.emit('scanStart', this._scanFilterDuplicates);
|
|
108
|
+
}
|
|
109
|
+
} else if (
|
|
110
|
+
(this._scanState === 'stopping' || this._scanState === 'stopped') &&
|
|
111
|
+
enable
|
|
112
|
+
) {
|
|
113
|
+
// Someone started scanning on us.
|
|
114
|
+
this.emit('scanStart', this._scanFilterDuplicates);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
Gap.prototype.onHciLeAdvertisingReport = function (
|
|
119
|
+
status,
|
|
120
|
+
type,
|
|
121
|
+
address,
|
|
122
|
+
addressType,
|
|
123
|
+
eir,
|
|
124
|
+
rssi
|
|
125
|
+
) {
|
|
126
|
+
const previouslyDiscovered = !!this._discoveries[address];
|
|
127
|
+
|
|
128
|
+
let discoveryCount = previouslyDiscovered
|
|
129
|
+
? this._discoveries[address].count
|
|
130
|
+
: 0;
|
|
131
|
+
let hasScanResponse = previouslyDiscovered
|
|
132
|
+
? this._discoveries[address].hasScanResponse
|
|
133
|
+
: false;
|
|
134
|
+
|
|
135
|
+
if (type === LE_META_EVENT_TYPE_SCAN_RESPONSE) {
|
|
136
|
+
hasScanResponse = true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
discoveryCount++;
|
|
140
|
+
|
|
141
|
+
const advertisement = this.parseServices(
|
|
142
|
+
address,
|
|
143
|
+
eir,
|
|
144
|
+
previouslyDiscovered,
|
|
145
|
+
hasScanResponse
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
debug(`advertisement = ${JSON.stringify(advertisement, null, 0)}`);
|
|
149
|
+
|
|
150
|
+
const connectable =
|
|
151
|
+
type === LE_META_EVENT_TYPE_SCAN_RESPONSE && previouslyDiscovered
|
|
152
|
+
? this._discoveries[address].connectable
|
|
153
|
+
: type !== LE_META_EVENT_TYPE_CONNECTABLE;
|
|
154
|
+
const scannable = type === LE_META_EVENT_TYPE_SCANNABLE;
|
|
155
|
+
|
|
156
|
+
this._discoveries[address] = {
|
|
157
|
+
address: address,
|
|
158
|
+
addressType: addressType,
|
|
159
|
+
connectable: connectable,
|
|
160
|
+
advertisement: advertisement,
|
|
161
|
+
rssi: rssi,
|
|
162
|
+
count: discoveryCount,
|
|
163
|
+
hasScanResponse: hasScanResponse
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// only report after a scan response event or if non-connectable or more than one discovery without a scan response, so more data can be collected
|
|
167
|
+
if (
|
|
168
|
+
type === LE_META_EVENT_TYPE_SCAN_RESPONSE ||
|
|
169
|
+
!connectable ||
|
|
170
|
+
(discoveryCount > 1 && !hasScanResponse) ||
|
|
171
|
+
process.env.NOBLE_REPORT_ALL_HCI_EVENTS
|
|
172
|
+
) {
|
|
173
|
+
this.emit(
|
|
174
|
+
'discover',
|
|
175
|
+
status,
|
|
176
|
+
address,
|
|
177
|
+
addressType,
|
|
178
|
+
connectable,
|
|
179
|
+
advertisement,
|
|
180
|
+
rssi,
|
|
181
|
+
scannable
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
Gap.prototype.onHciLeExtendedAdvertisingReport = function (
|
|
187
|
+
status,
|
|
188
|
+
type,
|
|
189
|
+
address,
|
|
190
|
+
addressType,
|
|
191
|
+
txpower,
|
|
192
|
+
rssi,
|
|
193
|
+
eir
|
|
194
|
+
) {
|
|
195
|
+
const previouslyDiscovered = !!this._discoveries[address];
|
|
196
|
+
|
|
197
|
+
let discoveryCount = previouslyDiscovered
|
|
198
|
+
? this._discoveries[address].count
|
|
199
|
+
: 0;
|
|
200
|
+
let hasScanResponse = previouslyDiscovered
|
|
201
|
+
? this._discoveries[address].hasScanResponse
|
|
202
|
+
: false;
|
|
203
|
+
|
|
204
|
+
if (type & LE_META_EXTENDED_EVENT_TYPE_SCAN_RESPONSE_MASK) {
|
|
205
|
+
hasScanResponse = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
discoveryCount++;
|
|
209
|
+
|
|
210
|
+
const advertisement = this.parseServices(
|
|
211
|
+
address,
|
|
212
|
+
eir,
|
|
213
|
+
previouslyDiscovered,
|
|
214
|
+
hasScanResponse,
|
|
215
|
+
txpower
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
debug(`advertisement = ${JSON.stringify(advertisement, null, 0)}`);
|
|
219
|
+
|
|
220
|
+
const connectable =
|
|
221
|
+
type & 0x8 && previouslyDiscovered
|
|
222
|
+
? this._discoveries[address].connectable
|
|
223
|
+
: type & LE_META_EXTENDED_EVENT_TYPE_CONNECTABLE_MASK;
|
|
224
|
+
const scannable = type & LE_META_EXTENDED_EVENT_TYPE_SCANNABLE_MASK ? 1 : 0;
|
|
225
|
+
const incomplete = type & LE_META_EXTENDED_EVENT_TYPE_INCOMPLETE_MASK ? 1 : 0;
|
|
226
|
+
|
|
227
|
+
this._discoveries[address] = {
|
|
228
|
+
address: address,
|
|
229
|
+
addressType: addressType,
|
|
230
|
+
connectable: connectable,
|
|
231
|
+
advertisement: advertisement,
|
|
232
|
+
rssi: rssi,
|
|
233
|
+
count: discoveryCount,
|
|
234
|
+
hasScanResponse: hasScanResponse
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// only report after a scan response event or if non-connectable or more than one discovery without a scan response, so more data can be collected
|
|
238
|
+
if (
|
|
239
|
+
type & LE_META_EXTENDED_EVENT_TYPE_SCAN_RESPONSE_MASK ||
|
|
240
|
+
(!connectable && !incomplete) ||
|
|
241
|
+
(discoveryCount > 1 && !hasScanResponse) ||
|
|
242
|
+
process.env.NOBLE_REPORT_ALL_HCI_EVENTS
|
|
243
|
+
) {
|
|
244
|
+
this.emit(
|
|
245
|
+
'discover',
|
|
246
|
+
status,
|
|
247
|
+
address,
|
|
248
|
+
addressType,
|
|
249
|
+
connectable,
|
|
250
|
+
advertisement,
|
|
251
|
+
rssi,
|
|
252
|
+
scannable
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
Gap.prototype.parseServices = function (
|
|
258
|
+
address,
|
|
259
|
+
eir,
|
|
260
|
+
previouslyDiscovered,
|
|
261
|
+
hasScanResponse,
|
|
262
|
+
txpower
|
|
263
|
+
) {
|
|
264
|
+
let i = 0;
|
|
265
|
+
const advertisement = previouslyDiscovered
|
|
266
|
+
? this._discoveries[address].advertisement
|
|
267
|
+
: {
|
|
268
|
+
localName: undefined,
|
|
269
|
+
txPowerLevel: txpower,
|
|
270
|
+
manufacturerData: undefined,
|
|
271
|
+
serviceData: [],
|
|
272
|
+
serviceUuids: [],
|
|
273
|
+
solicitationServiceUuids: []
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (!hasScanResponse) {
|
|
277
|
+
// reset service data every non-scan response event
|
|
278
|
+
advertisement.serviceData = [];
|
|
279
|
+
advertisement.serviceUuids = [];
|
|
280
|
+
advertisement.serviceSolicitationUuids = [];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
while (i + 1 < eir.length) {
|
|
284
|
+
const length = eir.readUInt8(i);
|
|
285
|
+
|
|
286
|
+
if (length < 1) {
|
|
287
|
+
debug(`invalid EIR data, length = ${length}`);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const eirType = eir.readUInt8(i + 1); // https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
|
|
292
|
+
|
|
293
|
+
if (i + length + 1 > eir.length) {
|
|
294
|
+
debug('invalid EIR data, out of range of buffer length');
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const bytes = eir.slice(i + 2).slice(0, length - 1);
|
|
299
|
+
|
|
300
|
+
switch (eirType) {
|
|
301
|
+
case 0x02: // Incomplete List of 16-bit Service Class UUID
|
|
302
|
+
case 0x03: // Complete List of 16-bit Service Class UUIDs
|
|
303
|
+
for (let j = 0; j < bytes.length - 1; j += 2) {
|
|
304
|
+
const serviceUuid = bytes.readUInt16LE(j).toString(16);
|
|
305
|
+
if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
|
|
306
|
+
advertisement.serviceUuids.push(serviceUuid);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
|
|
311
|
+
case 0x06: // Incomplete List of 128-bit Service Class UUIDs
|
|
312
|
+
case 0x07: // Complete List of 128-bit Service Class UUIDs
|
|
313
|
+
for (let j = 0; j < bytes.length - 15; j += 16) {
|
|
314
|
+
const serviceUuid = bytes
|
|
315
|
+
.slice(j, j + 16)
|
|
316
|
+
.toString('hex')
|
|
317
|
+
.match(/.{1,2}/g)
|
|
318
|
+
.reverse()
|
|
319
|
+
.join('');
|
|
320
|
+
if (advertisement.serviceUuids.indexOf(serviceUuid) === -1) {
|
|
321
|
+
advertisement.serviceUuids.push(serviceUuid);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
|
|
326
|
+
case 0x08: // Shortened Local Name
|
|
327
|
+
case 0x09: // Complete Local Name»
|
|
328
|
+
advertisement.localName = bytes.toString('utf8');
|
|
329
|
+
break;
|
|
330
|
+
|
|
331
|
+
case 0x0a: // Tx Power Level
|
|
332
|
+
advertisement.txPowerLevel = bytes.readInt8(0);
|
|
333
|
+
break;
|
|
334
|
+
|
|
335
|
+
case 0x14: // List of 16 bit solicitation UUIDs
|
|
336
|
+
for (let j = 0; j < bytes.length - 1; j += 2) {
|
|
337
|
+
const serviceSolicitationUuid = bytes.readUInt16LE(j).toString(16);
|
|
338
|
+
if (
|
|
339
|
+
advertisement.serviceSolicitationUuids.indexOf(
|
|
340
|
+
serviceSolicitationUuid
|
|
341
|
+
) === -1
|
|
342
|
+
) {
|
|
343
|
+
advertisement.serviceSolicitationUuids.push(
|
|
344
|
+
serviceSolicitationUuid
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
|
|
350
|
+
case 0x15: // List of 128 bit solicitation UUIDs
|
|
351
|
+
for (let j = 0; j < bytes.length - 15; j += 16) {
|
|
352
|
+
const serviceSolicitationUuid = bytes
|
|
353
|
+
.slice(j, j + 16)
|
|
354
|
+
.toString('hex')
|
|
355
|
+
.match(/.{1,2}/g)
|
|
356
|
+
.reverse()
|
|
357
|
+
.join('');
|
|
358
|
+
if (
|
|
359
|
+
advertisement.serviceSolicitationUuids.indexOf(
|
|
360
|
+
serviceSolicitationUuid
|
|
361
|
+
) === -1
|
|
362
|
+
) {
|
|
363
|
+
advertisement.serviceSolicitationUuids.push(
|
|
364
|
+
serviceSolicitationUuid
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
|
|
370
|
+
case 0x16: // 16-bit Service Data, there can be multiple occurences
|
|
371
|
+
advertisement.serviceData.push({
|
|
372
|
+
uuid: bytes
|
|
373
|
+
.slice(0, 2)
|
|
374
|
+
.toString('hex')
|
|
375
|
+
.match(/.{1,2}/g)
|
|
376
|
+
.reverse()
|
|
377
|
+
.join(''),
|
|
378
|
+
data: bytes.slice(2, bytes.length)
|
|
379
|
+
});
|
|
380
|
+
break;
|
|
381
|
+
|
|
382
|
+
case 0x20: // 32-bit Service Data, there can be multiple occurences
|
|
383
|
+
advertisement.serviceData.push({
|
|
384
|
+
uuid: bytes
|
|
385
|
+
.slice(0, 4)
|
|
386
|
+
.toString('hex')
|
|
387
|
+
.match(/.{1,2}/g)
|
|
388
|
+
.reverse()
|
|
389
|
+
.join(''),
|
|
390
|
+
data: bytes.slice(4, bytes.length)
|
|
391
|
+
});
|
|
392
|
+
break;
|
|
393
|
+
|
|
394
|
+
case 0x21: // 128-bit Service Data, there can be multiple occurences
|
|
395
|
+
advertisement.serviceData.push({
|
|
396
|
+
uuid: bytes
|
|
397
|
+
.slice(0, 16)
|
|
398
|
+
.toString('hex')
|
|
399
|
+
.match(/.{1,2}/g)
|
|
400
|
+
.reverse()
|
|
401
|
+
.join(''),
|
|
402
|
+
data: bytes.slice(16, bytes.length)
|
|
403
|
+
});
|
|
404
|
+
break;
|
|
405
|
+
|
|
406
|
+
case 0x1f: // List of 32 bit solicitation UUIDs
|
|
407
|
+
for (let j = 0; j < bytes.length - 3; j += 4) {
|
|
408
|
+
const serviceSolicitationUuid = bytes.readUInt32LE(j).toString(16);
|
|
409
|
+
if (
|
|
410
|
+
advertisement.serviceSolicitationUuids.indexOf(
|
|
411
|
+
serviceSolicitationUuid
|
|
412
|
+
) === -1
|
|
413
|
+
) {
|
|
414
|
+
advertisement.serviceSolicitationUuids.push(
|
|
415
|
+
serviceSolicitationUuid
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
|
|
421
|
+
case 0xff: // Manufacturer Specific Data
|
|
422
|
+
advertisement.manufacturerData = bytes;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
i += length + 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return advertisement;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
module.exports = Gap;
|