@stoprocent/noble 2.1.6 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ try {
13
13
  }
14
14
 
15
15
  const starTime = Date.now();
16
+ let cancelConnectTimeout;
16
17
 
17
18
  let peripheral;
18
19
  async function main () {
@@ -20,12 +21,13 @@ async function main () {
20
21
  await noble.waitForPoweredOnAsync();
21
22
 
22
23
  // Cancel the connection after 5 seconds if it is still connecting
23
- setTimeout(() => {
24
+ cancelConnectTimeout = setTimeout(() => {
24
25
  noble.cancelConnect(peripheralIdOrAddress);
25
26
  }, 5000);
26
27
 
27
28
  if (directConnect === '1') {
28
29
  peripheral = await noble.connectAsync(peripheralIdOrAddress, { addressType });
30
+ clearTimeout(cancelConnectTimeout);
29
31
  await explore(peripheral);
30
32
  } else {
31
33
  await noble.startScanningAsync([], false);
@@ -92,13 +94,18 @@ const explore = async (peripheral) => {
92
94
  console.log('noble stopped');
93
95
  });
94
96
 
97
+ peripheral.on('mtu', (mtu) => {
98
+ console.log('MTU Updated: ', mtu);
99
+ });
100
+
95
101
  if (peripheral.state !== 'connected') {
96
102
  await peripheral.connectAsync();
103
+ clearTimeout(cancelConnectTimeout);
97
104
  }
98
105
 
99
106
  const rssi = await peripheral.updateRssiAsync();
100
107
  console.log('RSSI', rssi);
101
-
108
+
102
109
  const services = await peripheral.discoverServicesAsync([]);
103
110
 
104
111
  for (const service of services) {
@@ -18,6 +18,7 @@ public:
18
18
  void Scan(const std::string& uuid, int rssi, const Peripheral& peripheral);
19
19
  void Connected(const std::string& uuid, const std::string& error = "");
20
20
  void Disconnected(const std::string& uuid);
21
+ void MTU(const std::string& uuid, int mtu);
21
22
  void RSSI(const std::string& uuid, int rssi, const std::string& error = "");
22
23
  void ServicesDiscovered(const std::string& uuid, const std::vector<std::string>& serviceUuids, const std::string& error = "");
23
24
  void IncludedServicesDiscovered(const std::string& uuid, const std::string& serviceUuid, const std::vector<std::string>& serviceUuids, const std::string& error = "");
@@ -162,6 +162,14 @@ void Emit::Disconnected(const std::string& uuid)
162
162
  });
163
163
  }
164
164
 
165
+ void Emit::MTU(const std::string& uuid, int mtu)
166
+ {
167
+ mCallback->call([uuid, mtu](Napi::Env env, std::vector<napi_value>& args) {
168
+ // emit('onMtu', deviceUuid, mtu);
169
+ args = { _s("onMtu"), _u(uuid), _n(mtu) };
170
+ });
171
+ }
172
+
165
173
  void Emit::RSSI(const std::string& uuid, int rssi, const std::string& error)
166
174
  {
167
175
  mCallback->call([uuid, rssi, error](Napi::Env env, std::vector<napi_value>& args) {
@@ -130,7 +130,7 @@ NobleBindings.prototype.connect = function (peripheralUuid, parameters = {}) {
130
130
  const processNextConnection = () => {
131
131
  if (this._connectionQueue.length === 1) {
132
132
  const nextConn = this._connectionQueue[0]; // Look at next connection but don't remove yet
133
- this._hci.createLeConn(nextConn.address, nextConn.addressType, nextConn.params);
133
+ this._hci.createLeConn(nextConn.address, nextConn.addressType, nextConn.params, Object.keys(this._handles).length === 0);
134
134
  }
135
135
  };
136
136
 
@@ -404,7 +404,7 @@ NobleBindings.prototype.onLeConnComplete = function (
404
404
  // Process next connection in queue if any
405
405
  if (this._connectionQueue.length > 0 && !this._isScanning) {
406
406
  const nextConn = this._connectionQueue[0];
407
- this._hci.createLeConn(nextConn.address, nextConn.addressType, nextConn.params);
407
+ this._hci.createLeConn(nextConn.address, nextConn.addressType, nextConn.params, Object.keys(this._handles).length === 0);
408
408
  }
409
409
  };
410
410
 
@@ -40,11 +40,6 @@ Gap.prototype.setScanParameters = function (interval, window) {
40
40
  };
41
41
 
42
42
  Gap.prototype.startScanning = function (allowDuplicates) {
43
- this._hci.once('reset', () => this.startScanningAfterReset(allowDuplicates));
44
- this._hci.reset();
45
- };
46
-
47
- Gap.prototype.startScanningAfterReset = function (allowDuplicates) {
48
43
  this._scanState = 'starting';
49
44
  this._scanFilterDuplicates = !allowDuplicates;
50
45
 
@@ -508,9 +508,19 @@ Hci.prototype.setScanEnabled = function (enabled, filterDuplicates) {
508
508
  this._socket.write(cmd);
509
509
  };
510
510
 
511
- Hci.prototype.createLeConn = function (address, addressType, parameters = {}) {
512
- this.once('reset', () => this.createLeConnAfterReset(address, addressType, parameters));
513
- this.reset();
511
+ // This is a mystery case for me.
512
+ // I don't know why we need to reset the hci socket before creating a LE connection.
513
+ // Issues related to this:
514
+ // https://github.com/stoprocent/noble/issues/12
515
+ // https://github.com/bluez/bluez/issues/1178
516
+ Hci.prototype.createLeConn = function (address, addressType, parameters = {}, reset = true) {
517
+ if (reset) {
518
+ this.once('reset', () => this.createLeConnAfterReset(address, addressType, parameters));
519
+ this.reset();
520
+ }
521
+ else {
522
+ this.createLeConnAfterReset(address, addressType, parameters);
523
+ }
514
524
  };
515
525
 
516
526
  Hci.prototype.createLeConnAfterReset = function (address, addressType, parameters = {}) {
@@ -14,8 +14,10 @@
14
14
  @property (assign) CBManagerState lastState;
15
15
  @property dispatch_queue_t dispatchQueue;
16
16
  @property NSMutableDictionary *peripherals;
17
+ @property NSMutableDictionary *mtus;
17
18
  @property NSMutableSet *discovered;
18
19
 
20
+
19
21
  - (instancetype)init: (const Napi::Value&) receiver with: (const Napi::Function&) callback;
20
22
  - (void)scan: (NSArray<NSString*> *)serviceUUIDs allowDuplicates: (BOOL)allowDuplicates;
21
23
  - (void)stopScan;
@@ -4,6 +4,10 @@
4
4
 
5
5
  #import <Foundation/Foundation.h>
6
6
 
7
+ @interface BLEManager ()
8
+ - (void)updateMtuForPeripheral:(CBPeripheral*) peripheral;
9
+ @end
10
+
7
11
  @implementation BLEManager
8
12
 
9
13
  - (instancetype)init: (const Napi::Value&) receiver with: (const Napi::Function&) callback
@@ -16,10 +20,20 @@
16
20
  self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:self.dispatchQueue];
17
21
  self.discovered = [NSMutableSet set];
18
22
  self.peripherals = [NSMutableDictionary dictionaryWithCapacity:10];
23
+ self.mtus = [NSMutableDictionary dictionaryWithCapacity:10];
19
24
  }
20
25
  return self;
21
26
  }
22
27
 
28
+ - (void)updateMtuForPeripheral:(CBPeripheral*) peripheral {
29
+ NSUInteger mtu = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse];
30
+ NSNumber *mtuNumber = [self.mtus objectForKey: peripheral.identifier];
31
+ if (!mtuNumber || [mtuNumber unsignedIntegerValue] != mtu) {
32
+ emit.MTU(getUuid(peripheral), mtu);
33
+ [self.mtus setObject:[NSNumber numberWithInt:mtu] forKey: peripheral.identifier];
34
+ }
35
+ }
36
+
23
37
  - (void)centralManagerDidUpdateState:(CBCentralManager *)central
24
38
  {
25
39
  if (central.state != self.lastState && self.lastState == CBManagerStatePoweredOff && central.state == CBManagerStatePoweredOn) {
@@ -154,6 +168,8 @@
154
168
 
155
169
  std::string uuid = getUuid(peripheral);
156
170
  emit.Connected(uuid, "");
171
+ [self updateMtuForPeripheral:peripheral];
172
+
157
173
  }
158
174
 
159
175
  - (void) centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
@@ -213,6 +229,7 @@
213
229
  - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
214
230
  std::string uuid = getUuid(peripheral);
215
231
  std::vector<std::string> services = getServices(peripheral.services);
232
+ [self updateMtuForPeripheral:peripheral];
216
233
  emit.ServicesDiscovered(uuid, services, error ? error.localizedDescription.UTF8String : "");
217
234
  }
218
235
 
@@ -263,6 +280,7 @@
263
280
  std::string uuid = getUuid(peripheral);
264
281
  std::string serviceUuid = std::string([service.UUID.UUIDString UTF8String]);
265
282
  auto characteristics = getCharacteristics(service.characteristics);
283
+ [self updateMtuForPeripheral:peripheral];
266
284
  emit.CharacteristicsDiscovered(uuid, serviceUuid, characteristics, error ? error.localizedDescription.UTF8String : "");
267
285
  }
268
286
 
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "license": "MIT",
7
7
  "name": "@stoprocent/noble",
8
8
  "description": "A Node.js BLE (Bluetooth Low Energy) central library.",
9
- "version": "2.1.6",
9
+ "version": "2.2.0",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "https://github.com/stoprocent/noble.git"
@@ -239,7 +239,7 @@ describe('hci-socket bindings', () => {
239
239
  should(bindings._connectionQueue[0].params).eql({ addressType: 'public' });
240
240
 
241
241
  expect(bindings._hci.createLeConn).toHaveBeenCalledTimes(1);
242
- expect(bindings._hci.createLeConn).toHaveBeenCalledWith('11:22:33:44:55:66', 'public', { addressType: 'public' });
242
+ expect(bindings._hci.createLeConn).toHaveBeenCalledWith('11:22:33:44:55:66', 'public', { addressType: 'public' }, true);
243
243
  });
244
244
 
245
245
  it('missing peripheral, no queue, random address', () => {
@@ -252,7 +252,7 @@ describe('hci-socket bindings', () => {
252
252
  should(bindings._connectionQueue[0].params).eql({ addressType: 'random' });
253
253
 
254
254
  expect(bindings._hci.createLeConn).toHaveBeenCalledTimes(1);
255
- expect(bindings._hci.createLeConn).toHaveBeenCalledWith('f3:22:33:44:55:66', 'random', { addressType: 'random' });
255
+ expect(bindings._hci.createLeConn).toHaveBeenCalledWith('f3:22:33:44:55:66', 'random', { addressType: 'random' }, true);
256
256
  });
257
257
 
258
258
  it('existing peripheral, no queue', () => {
@@ -271,7 +271,7 @@ describe('hci-socket bindings', () => {
271
271
  should(bindings._connectionQueue[0].params).eql('parameters');
272
272
 
273
273
  expect(bindings._hci.createLeConn).toHaveBeenCalledTimes(1);
274
- expect(bindings._hci.createLeConn).toHaveBeenCalledWith('address', 'addressType', 'parameters');
274
+ expect(bindings._hci.createLeConn).toHaveBeenCalledWith('address', 'addressType', 'parameters', true);
275
275
  });
276
276
 
277
277
  it('missing peripheral, with queue', () => {
@@ -801,8 +801,8 @@ describe('hci-socket bindings', () => {
801
801
  expect(connectCallback).toHaveBeenCalledTimes(1);
802
802
  expect(connectCallback).toHaveBeenCalledWith('112233445566', null);
803
803
  expect(Hci.createLeConnSpy).toHaveBeenCalledTimes(2);
804
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' });
805
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' });
804
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' }, true);
805
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' }, false);
806
806
 
807
807
  should(bindings._connectionQueue).length(1);
808
808
  });
@@ -822,14 +822,16 @@ describe('hci-socket bindings', () => {
822
822
  bindings.connect('queuedId_2', { addressType: 'public' });
823
823
  bindings.connect('queuedId_3', { addressType: 'random' });
824
824
 
825
+ bindings.emit('reset');
826
+
825
827
  bindings.on('connect', connectCallback);
826
828
  bindings.onLeConnComplete(status, handle, role, addressType, address);
827
829
 
828
830
  expect(connectCallback).toHaveBeenCalledTimes(1);
829
831
  expect(connectCallback).toHaveBeenCalledWith('112233445566', null);
830
832
  expect(Hci.createLeConnSpy).toHaveBeenCalledTimes(2);
831
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' });
832
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' });
833
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' }, true);
834
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' }, false);
833
835
  expect(bindings._connectionQueue).toHaveLength(2);
834
836
  });
835
837
 
@@ -865,9 +867,9 @@ describe('hci-socket bindings', () => {
865
867
  expect(connectCallback).toHaveBeenCalledWith('998877665544', null);
866
868
 
867
869
  expect(Hci.createLeConnSpy).toHaveBeenCalledTimes(3);
868
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' });
869
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' });
870
- expect(Hci.createLeConnSpy).toHaveBeenCalledWith('aabbccddeeff', 'random', { addressType: 'random' });
870
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('112233445566', 'random', { addressType: 'random' }, true);
871
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('998877665544', 'public', { addressType: 'public' }, false);
872
+ expect(Hci.createLeConnSpy).toHaveBeenCalledWith('aabbccddeeff', 'random', { addressType: 'random' }, false);
871
873
  expect(bindings._connectionQueue).toHaveLength(1);
872
874
  });
873
875
  });
@@ -46,7 +46,6 @@ describe('hci-socket gap', () => {
46
46
  const hci = {
47
47
  on: sinon.spy(),
48
48
  once: sinon.spy(),
49
- reset: sinon.spy(),
50
49
  setScanEnabled: sinon.spy(),
51
50
  setScanParameters: sinon.spy()
52
51
  };
@@ -54,21 +53,6 @@ describe('hci-socket gap', () => {
54
53
  const gap = new Gap(hci);
55
54
  gap.startScanning(true);
56
55
 
57
- assert.callCount(hci.once, 1);
58
- assert.calledWithExactly(hci.reset);
59
- });
60
-
61
- it('startScanningAfterReset', () => {
62
- const hci = {
63
- on: sinon.spy(),
64
- once: sinon.spy(),
65
- setScanEnabled: sinon.spy(),
66
- setScanParameters: sinon.spy()
67
- };
68
-
69
- const gap = new Gap(hci);
70
- gap.startScanningAfterReset(true);
71
-
72
56
  should(gap._scanState).equal('starting');
73
57
  should(gap._scanFilterDuplicates).equal(false);
74
58