react-native-ble-manager 7.6.2 → 8.0.2
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.
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Before open an issue**
|
|
11
|
+
- Check the closed issues, your question maybe is not new.
|
|
12
|
+
- We can't debug or reproduce hardware issue.
|
|
13
|
+
- If the library is not working 99% you did something wrong, in the code, installation or in the phone permissions.
|
|
14
|
+
|
|
15
|
+
**Describe the bug**
|
|
16
|
+
A clear and concise description of what the bug is.
|
|
17
|
+
|
|
18
|
+
**To Reproduce**
|
|
19
|
+
Steps to reproduce the behavior:
|
|
20
|
+
1. Go to '...'
|
|
21
|
+
2. Click on '....'
|
|
22
|
+
3. Scroll down to '....'
|
|
23
|
+
4. See error
|
|
24
|
+
|
|
25
|
+
**Expected behavior**
|
|
26
|
+
A clear and concise description of what you expected to happen.
|
|
27
|
+
|
|
28
|
+
**Screenshots**
|
|
29
|
+
If applicable, add screenshots to help explain your problem.
|
|
30
|
+
|
|
31
|
+
**Smartphone (please complete the following information):**
|
|
32
|
+
- Device: [e.g. iPhone6]
|
|
33
|
+
- OS: [e.g. iOS8.1]
|
|
34
|
+
- react-native-ble-manager version:
|
|
35
|
+
- react-native version:
|
|
36
|
+
|
|
37
|
+
**Additional context**
|
|
38
|
+
Add any other context about the problem here.
|
package/README.md
CHANGED
|
@@ -22,23 +22,45 @@ RN 0.30-0.39 supported until 2.4.3
|
|
|
22
22
|
|
|
23
23
|
```shell
|
|
24
24
|
npm i --save react-native-ble-manager
|
|
25
|
-
npx react-native link
|
|
26
25
|
```
|
|
26
|
+
The library support the react native autolink feature.
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
##### Android - Update Manifest
|
|
30
30
|
|
|
31
31
|
```xml
|
|
32
32
|
// file: android/app/src/main/AndroidManifest.xml
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
<!-- Add xmlns:tools -->
|
|
34
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
35
|
+
xmlns:tools="http://schemas.android.com/tools"
|
|
36
|
+
package="YOUR_PACKAGE_NAME">
|
|
37
|
+
|
|
38
|
+
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
|
39
|
+
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
|
40
|
+
|
|
41
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
|
|
42
|
+
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:targetApi="Q"/>
|
|
43
|
+
|
|
44
|
+
<!-- Only when targeting Android 12 or higher -->
|
|
45
|
+
<!-- Please make sure you read the following documentation to have a
|
|
46
|
+
better understanging of the new permissions.
|
|
47
|
+
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location
|
|
48
|
+
-->
|
|
49
|
+
|
|
50
|
+
<!-- If your app doesn't use Bluetooth scan results to derive physical location information,
|
|
51
|
+
you can strongly assert that your app
|
|
52
|
+
doesn't derive physical location. -->
|
|
53
|
+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
|
|
54
|
+
android:usesPermissionFlags="neverForLocation"
|
|
55
|
+
tools:targetApi="s" />
|
|
56
|
+
|
|
57
|
+
<!-- Needed only if your app looks for Bluetooth devices. -->
|
|
58
|
+
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
|
59
|
+
<!-- Needed only if your app makes the device discoverable to Bluetooth devices. -->
|
|
60
|
+
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
|
37
61
|
...
|
|
38
62
|
```
|
|
39
63
|
|
|
40
|
-
In Android API 29 >= you need to use "ACCESS_FINE_LOCATION" instead of "ACCESS_COARSE_LOCATION".
|
|
41
|
-
|
|
42
64
|
If you need communication while the app is not in the foreground you need the "ACCESS_BACKGROUND_LOCATION" permission.
|
|
43
65
|
|
|
44
66
|
##### iOS - Update Info.plist
|
|
@@ -53,6 +75,7 @@ In iOS >= 13 you need to add the `NSBluetoothAlwaysUsageDescription` string key.
|
|
|
53
75
|
- Android API >= 29 require the ACCESS_FINE_LOCATION permission to scan for peripherals.
|
|
54
76
|
React-Native 0.63.X started targeting Android API 29.
|
|
55
77
|
- Before write, read or start notification you need to call `retrieveServices` method
|
|
78
|
+
- Because location and bluetooth permissions are runtime permissions, you **must** request these permissions at runtime along with declaring them in your manifest.
|
|
56
79
|
|
|
57
80
|
## Example
|
|
58
81
|
|
|
@@ -758,6 +781,14 @@ A peripheral was disconnected.
|
|
|
758
781
|
- `peripheral` - `String` - the id of the peripheral
|
|
759
782
|
- `status` - `Number` - [Android only] disconnect [`reasons`](<https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onConnectionStateChange(android.bluetooth.BluetoothGatt,%20int,%20int)>)
|
|
760
783
|
|
|
784
|
+
### BleManagerPeripheralDidBond
|
|
785
|
+
|
|
786
|
+
A bond with a peripheral was established
|
|
787
|
+
|
|
788
|
+
**Arguments**
|
|
789
|
+
|
|
790
|
+
Object with information about the device
|
|
791
|
+
|
|
761
792
|
### BleManagerCentralManagerWillRestoreState [iOS only]
|
|
762
793
|
|
|
763
794
|
This is fired when [`centralManager:WillRestoreState:`](https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518819-centralmanager) is called (app relaunched in the background to handle a bluetooth event).
|
|
@@ -193,7 +193,7 @@ class BleManager extends ReactContextBaseJavaModule {
|
|
|
193
193
|
for (Iterator<Map.Entry<String, Peripheral>> iterator = peripherals.entrySet().iterator(); iterator
|
|
194
194
|
.hasNext(); ) {
|
|
195
195
|
Map.Entry<String, Peripheral> entry = iterator.next();
|
|
196
|
-
if (!entry.getValue().isConnected()) {
|
|
196
|
+
if (!(entry.getValue().isConnected() || entry.getValue().isConnecting())) {
|
|
197
197
|
iterator.remove();
|
|
198
198
|
}
|
|
199
199
|
}
|
|
@@ -33,6 +33,8 @@ import java.util.Arrays;
|
|
|
33
33
|
import java.util.Iterator;
|
|
34
34
|
import java.util.List;
|
|
35
35
|
import java.util.UUID;
|
|
36
|
+
import java.util.Queue;
|
|
37
|
+
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
36
38
|
|
|
37
39
|
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
|
38
40
|
import static com.facebook.react.common.ReactConstants.TAG;
|
|
@@ -43,12 +45,15 @@ import static com.facebook.react.common.ReactConstants.TAG;
|
|
|
43
45
|
public class Peripheral extends BluetoothGattCallback {
|
|
44
46
|
|
|
45
47
|
private static final String CHARACTERISTIC_NOTIFICATION_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
|
|
48
|
+
public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5;
|
|
49
|
+
public static final int GATT_AUTH_FAIL = 137;
|
|
46
50
|
|
|
47
51
|
private final BluetoothDevice device;
|
|
48
52
|
private final Map<String, NotifyBufferContainer> bufferedCharacteristics;
|
|
49
53
|
protected byte[] advertisingDataBytes = new byte[0];
|
|
50
54
|
protected int advertisingRSSI;
|
|
51
55
|
private boolean connected = false;
|
|
56
|
+
private boolean connecting = false;
|
|
52
57
|
private ReactContext reactContext;
|
|
53
58
|
|
|
54
59
|
private BluetoothGatt gatt;
|
|
@@ -61,6 +66,11 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
61
66
|
private Callback registerNotifyCallback;
|
|
62
67
|
private Callback requestMTUCallback;
|
|
63
68
|
|
|
69
|
+
private final Queue<Runnable> commandQueue = new ConcurrentLinkedQueue<>();
|
|
70
|
+
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
71
|
+
private Runnable discoverServicesRunnable;
|
|
72
|
+
private boolean commandQueueBusy = false;
|
|
73
|
+
|
|
64
74
|
private List<byte[]> writeQueue = new ArrayList<>();
|
|
65
75
|
|
|
66
76
|
public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord, ReactContext reactContext) {
|
|
@@ -95,6 +105,7 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
95
105
|
if (!connected) {
|
|
96
106
|
BluetoothDevice device = getDevice();
|
|
97
107
|
this.connectCallback = callback;
|
|
108
|
+
this.connecting = true;
|
|
98
109
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
99
110
|
Log.d(BleManager.LOG_TAG, " Is Or Greater than M $mBluetoothDevice");
|
|
100
111
|
gatt = device.connectGatt(activity, false, this, BluetoothDevice.TRANSPORT_LE);
|
|
@@ -127,6 +138,9 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
127
138
|
connectCallback = null;
|
|
128
139
|
connected = false;
|
|
129
140
|
clearBuffers();
|
|
141
|
+
commandQueue.clear();
|
|
142
|
+
commandQueueBusy = false;
|
|
143
|
+
|
|
130
144
|
if (gatt != null) {
|
|
131
145
|
try {
|
|
132
146
|
gatt.disconnect();
|
|
@@ -240,6 +254,10 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
240
254
|
return connected;
|
|
241
255
|
}
|
|
242
256
|
|
|
257
|
+
public boolean isConnecting() {
|
|
258
|
+
return connecting;
|
|
259
|
+
}
|
|
260
|
+
|
|
243
261
|
public BluetoothDevice getDevice() {
|
|
244
262
|
return device;
|
|
245
263
|
}
|
|
@@ -272,11 +290,28 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
272
290
|
|
|
273
291
|
if (status != BluetoothGatt.GATT_SUCCESS) {
|
|
274
292
|
gatt.close();
|
|
293
|
+
// change the state to ensure the connection is teared down properly in the code below
|
|
294
|
+
newState = BluetoothProfile.STATE_DISCONNECTED;
|
|
275
295
|
}
|
|
276
296
|
|
|
297
|
+
connecting = false;
|
|
277
298
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
|
278
299
|
connected = true;
|
|
279
300
|
|
|
301
|
+
discoverServicesRunnable = new Runnable() {
|
|
302
|
+
@Override
|
|
303
|
+
public void run() {
|
|
304
|
+
try {
|
|
305
|
+
gatt.discoverServices();
|
|
306
|
+
} catch (NullPointerException e) {
|
|
307
|
+
Log.d(BleManager.LOG_TAG, "onConnectionStateChange connected but gatt of Run method was null");
|
|
308
|
+
}
|
|
309
|
+
discoverServicesRunnable = null;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
mainHandler.post(discoverServicesRunnable);
|
|
314
|
+
|
|
280
315
|
sendConnectionEvent(device, "BleManagerConnectPeripheral", status);
|
|
281
316
|
|
|
282
317
|
if (connectCallback != null) {
|
|
@@ -287,9 +322,11 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
287
322
|
|
|
288
323
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
|
289
324
|
|
|
290
|
-
|
|
325
|
+
if (discoverServicesRunnable != null) {
|
|
326
|
+
mainHandler.removeCallbacks(discoverServicesRunnable);
|
|
327
|
+
discoverServicesRunnable = null;
|
|
328
|
+
}
|
|
291
329
|
|
|
292
|
-
sendConnectionEvent(device, "BleManagerDisconnectPeripheral", status);
|
|
293
330
|
List<Callback> callbacks = Arrays.asList(writeCallback, retrieveServicesCallback, readRSSICallback,
|
|
294
331
|
readCallback, registerNotifyCallback, requestMTUCallback);
|
|
295
332
|
for (Callback currentCallback : callbacks) {
|
|
@@ -308,6 +345,10 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
308
345
|
readRSSICallback = null;
|
|
309
346
|
registerNotifyCallback = null;
|
|
310
347
|
requestMTUCallback = null;
|
|
348
|
+
commandQueue.clear();
|
|
349
|
+
commandQueueBusy = false;
|
|
350
|
+
|
|
351
|
+
this.disconnect(true);
|
|
311
352
|
}
|
|
312
353
|
|
|
313
354
|
}
|
|
@@ -367,54 +408,60 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
367
408
|
@Override
|
|
368
409
|
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
|
369
410
|
super.onCharacteristicRead(gatt, characteristic, status);
|
|
370
|
-
Log.d(BleManager.LOG_TAG, "onCharacteristicRead " + characteristic);
|
|
371
411
|
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (readCallback != null) {
|
|
378
|
-
readCallback.invoke(null, BleManager.bytesToWritableArray(dataValue));
|
|
379
|
-
}
|
|
380
|
-
} else {
|
|
381
|
-
readCallback.invoke("Error reading " + characteristic.getUuid() + " status=" + status, null);
|
|
412
|
+
if (status != BluetoothGatt.GATT_SUCCESS) {
|
|
413
|
+
if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
|
|
414
|
+
Log.d(BleManager.LOG_TAG, "Read needs bonding");
|
|
415
|
+
// *not* doing completedCommand()
|
|
416
|
+
return;
|
|
382
417
|
}
|
|
383
|
-
|
|
418
|
+
sendBackrError(readCallback, "Error reading " + characteristic.getUuid() + " status=" + status);
|
|
419
|
+
} else if (readCallback != null) {
|
|
420
|
+
final byte[] dataValue = copyOf(characteristic.getValue());
|
|
421
|
+
final Callback callback = readCallback;
|
|
384
422
|
readCallback = null;
|
|
385
|
-
|
|
423
|
+
mainHandler.post(new Runnable() {
|
|
424
|
+
@Override
|
|
425
|
+
public void run() {
|
|
426
|
+
callback.invoke(null, BleManager.bytesToWritableArray(dataValue));
|
|
427
|
+
}
|
|
428
|
+
});
|
|
386
429
|
}
|
|
430
|
+
|
|
431
|
+
completedCommand();
|
|
387
432
|
}
|
|
388
433
|
|
|
389
434
|
@Override
|
|
390
435
|
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
|
391
436
|
super.onCharacteristicWrite(gatt, characteristic, status);
|
|
392
437
|
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
writeCallback.invoke();
|
|
403
|
-
} else {
|
|
404
|
-
Log.e(BleManager.LOG_TAG, "Error onCharacteristicWrite:" + status);
|
|
405
|
-
writeCallback.invoke("Error writing status: " + status);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
writeCallback = null;
|
|
438
|
+
if (writeQueue.size() > 0) {
|
|
439
|
+
byte[] data = writeQueue.get(0);
|
|
440
|
+
writeQueue.remove(0);
|
|
441
|
+
doWrite(characteristic, data, writeCallback);
|
|
442
|
+
} else if (status != BluetoothGatt.GATT_SUCCESS) {
|
|
443
|
+
if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
|
|
444
|
+
Log.d(BleManager.LOG_TAG, "Write needs bonding");
|
|
445
|
+
// *not* doing completedCommand()
|
|
446
|
+
return;
|
|
409
447
|
}
|
|
410
|
-
|
|
411
|
-
|
|
448
|
+
sendBackrError(writeCallback, "Error writing " + characteristic.getUuid() + " status=" + status);
|
|
449
|
+
} else if (writeCallback != null) {
|
|
450
|
+
final Callback callback = writeCallback;
|
|
451
|
+
mainHandler.post(new Runnable() {
|
|
452
|
+
@Override
|
|
453
|
+
public void run() {
|
|
454
|
+
callback.invoke();
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
writeCallback = null;
|
|
412
458
|
}
|
|
459
|
+
|
|
460
|
+
completedCommand();
|
|
413
461
|
}
|
|
414
462
|
|
|
415
463
|
@Override
|
|
416
464
|
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
|
417
|
-
super.onDescriptorWrite(gatt, descriptor, status);
|
|
418
465
|
if (registerNotifyCallback != null) {
|
|
419
466
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
420
467
|
registerNotifyCallback.invoke();
|
|
@@ -428,6 +475,8 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
428
475
|
} else {
|
|
429
476
|
Log.e(BleManager.LOG_TAG, "onDescriptorWrite with no callback");
|
|
430
477
|
}
|
|
478
|
+
|
|
479
|
+
completedCommand();
|
|
431
480
|
}
|
|
432
481
|
|
|
433
482
|
@Override
|
|
@@ -454,85 +503,90 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
454
503
|
entry.getValue().resetBuffer();
|
|
455
504
|
}
|
|
456
505
|
|
|
457
|
-
private void setNotify(UUID serviceUUID, UUID characteristicUUID, Boolean notify,
|
|
458
|
-
|
|
459
|
-
if (!isConnected()) {
|
|
506
|
+
private void setNotify(UUID serviceUUID, UUID characteristicUUID, final Boolean notify, Callback callback) {
|
|
507
|
+
if (! isConnected() || gatt == null) {
|
|
460
508
|
callback.invoke("Device is not connected", null);
|
|
461
509
|
return;
|
|
462
510
|
}
|
|
463
|
-
Log.d(BleManager.LOG_TAG, "setNotify");
|
|
464
511
|
|
|
465
|
-
|
|
466
|
-
|
|
512
|
+
BluetoothGattService service = gatt.getService(serviceUUID);
|
|
513
|
+
final BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
|
|
514
|
+
|
|
515
|
+
if (characteristic == null) {
|
|
516
|
+
callback.invoke("Characteristic " + characteristicUUID + " not found");
|
|
467
517
|
return;
|
|
468
518
|
}
|
|
469
|
-
BluetoothGattService service = gatt.getService(serviceUUID);
|
|
470
|
-
BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
|
|
471
519
|
|
|
472
|
-
if (characteristic
|
|
473
|
-
|
|
520
|
+
if (! gatt.setCharacteristicNotification(characteristic, notify)) {
|
|
521
|
+
callback.invoke("Failed to register notification for " + characteristicUUID);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
474
524
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
525
|
+
final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUIDHelper.uuidFromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
|
|
526
|
+
if (descriptor == null) {
|
|
527
|
+
callback.invoke("Set notification failed for " + characteristicUUID);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Prefer notify over indicate
|
|
532
|
+
byte[] value;
|
|
533
|
+
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
|
|
534
|
+
Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
|
|
535
|
+
value = notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
|
536
|
+
} else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
|
|
537
|
+
Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
|
|
538
|
+
value = notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
|
539
|
+
} else {
|
|
540
|
+
String msg = "Characteristic " + characteristicUUID + " does not have NOTIFY or INDICATE property set";
|
|
541
|
+
Log.d(BleManager.LOG_TAG, msg);
|
|
542
|
+
callback.invoke(msg);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
final byte[] finalValue = notify ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
|
|
546
|
+
final Callback finalCallback = callback;
|
|
480
547
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
|
|
488
|
-
descriptor.setValue(notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
|
489
|
-
: BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
|
|
490
|
-
} else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
|
|
491
|
-
Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
|
|
492
|
-
descriptor.setValue(notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
|
|
493
|
-
: BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
|
|
494
|
-
} else {
|
|
495
|
-
Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID
|
|
496
|
-
+ " does not have NOTIFY or INDICATE property set");
|
|
548
|
+
boolean result = commandQueue.add(new Runnable() {
|
|
549
|
+
@Override
|
|
550
|
+
public void run() {
|
|
551
|
+
if (! isConnected()) {
|
|
552
|
+
completedCommand();
|
|
553
|
+
return;
|
|
497
554
|
}
|
|
498
555
|
|
|
556
|
+
boolean result = false;
|
|
499
557
|
try {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
} catch (Exception e) {
|
|
509
|
-
Log.d(BleManager.LOG_TAG, "Error on setNotify", e);
|
|
510
|
-
callback.invoke("Failed to set client characteristic notification for " + characteristicUUID
|
|
511
|
-
+ ", error: " + e.getMessage());
|
|
558
|
+
result = gatt.setCharacteristicNotification(characteristic, notify);
|
|
559
|
+
// Then write to descriptor
|
|
560
|
+
descriptor.setValue(finalValue);
|
|
561
|
+
registerNotifyCallback = finalCallback;
|
|
562
|
+
result = gatt.writeDescriptor(descriptor);
|
|
563
|
+
} catch(Exception e) {
|
|
564
|
+
Log.d(BleManager.LOG_TAG, "Exception in setNotify", e);
|
|
512
565
|
}
|
|
513
566
|
|
|
514
|
-
|
|
515
|
-
|
|
567
|
+
if (! result) {
|
|
568
|
+
sendBackrError(registerNotifyCallback, "writeDescriptor failed for descriptor: " + descriptor.getUuid());
|
|
569
|
+
registerNotifyCallback = null;
|
|
570
|
+
completedCommand();
|
|
571
|
+
}
|
|
516
572
|
}
|
|
573
|
+
});
|
|
517
574
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
575
|
+
if (result) {
|
|
576
|
+
nextCommand();
|
|
522
577
|
} else {
|
|
523
|
-
|
|
578
|
+
Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command");
|
|
524
579
|
}
|
|
525
|
-
|
|
526
580
|
}
|
|
527
581
|
|
|
528
582
|
public void registerNotify(UUID serviceUUID, UUID characteristicUUID, Integer buffer, Callback callback) {
|
|
529
583
|
Log.d(BleManager.LOG_TAG, "registerNotify");
|
|
530
|
-
this.setNotify(serviceUUID, characteristicUUID, true,
|
|
584
|
+
this.setNotify(serviceUUID, characteristicUUID, true, callback);
|
|
531
585
|
}
|
|
532
586
|
|
|
533
587
|
public void removeNotify(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
|
|
534
588
|
Log.d(BleManager.LOG_TAG, "removeNotify");
|
|
535
|
-
this.setNotify(serviceUUID, characteristicUUID, false,
|
|
589
|
+
this.setNotify(serviceUUID, characteristicUUID, false, callback);
|
|
536
590
|
}
|
|
537
591
|
|
|
538
592
|
// Some devices reuse UUIDs across characteristics, so we can't use
|
|
@@ -572,30 +626,100 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
572
626
|
|
|
573
627
|
public void read(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
|
|
574
628
|
|
|
575
|
-
if (!isConnected()) {
|
|
629
|
+
if (!isConnected() || gatt == null) {
|
|
576
630
|
callback.invoke("Device is not connected", null);
|
|
577
631
|
return;
|
|
578
632
|
}
|
|
579
|
-
if (gatt == null) {
|
|
580
|
-
callback.invoke("BluetoothGatt is null", null);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
633
|
|
|
584
634
|
BluetoothGattService service = gatt.getService(serviceUUID);
|
|
585
|
-
BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
|
|
635
|
+
final BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
|
|
586
636
|
|
|
587
637
|
if (characteristic == null) {
|
|
588
638
|
callback.invoke("Characteristic " + characteristicUUID + " not found.", null);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
final Callback reactcb = callback;
|
|
643
|
+
boolean result = commandQueue.add(new Runnable() {
|
|
644
|
+
@Override
|
|
645
|
+
public void run() {
|
|
646
|
+
readCallback = reactcb;
|
|
647
|
+
if (! gatt.readCharacteristic(characteristic)) {
|
|
648
|
+
readCallback = null;
|
|
649
|
+
sendBackrError(reactcb, "Read failed");
|
|
650
|
+
completedCommand();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
if (result) {
|
|
656
|
+
nextCommand();
|
|
589
657
|
} else {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
658
|
+
Log.d(BleManager.LOG_TAG, "Could not queue read characteristic command");
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private void sendBackrError(final Callback callback, final String message) {
|
|
663
|
+
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
|
664
|
+
@Override
|
|
665
|
+
public void run() {
|
|
666
|
+
callback.invoke(message, null);
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private byte[] copyOf(byte[] source) {
|
|
672
|
+
if (source == null) return new byte[0];
|
|
673
|
+
final int sourceLength = source.length;
|
|
674
|
+
final byte[] copy = new byte[sourceLength];
|
|
675
|
+
System.arraycopy(source, 0, copy, 0, sourceLength);
|
|
676
|
+
return copy;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private void completedCommand() {
|
|
680
|
+
commandQueue.poll();
|
|
681
|
+
commandQueueBusy = false;
|
|
682
|
+
nextCommand();
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
private void nextCommand() {
|
|
686
|
+
synchronized (this) {
|
|
687
|
+
if (commandQueueBusy) {
|
|
688
|
+
Log.d(BleManager.LOG_TAG, "Command queue busy");
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
final Runnable nextCommand = commandQueue.peek();
|
|
693
|
+
if (nextCommand == null) {
|
|
694
|
+
Log.d(BleManager.LOG_TAG, "Command queue empty");
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Check if we still have a valid gatt object
|
|
699
|
+
if (gatt == null) {
|
|
700
|
+
Log.d(BleManager.LOG_TAG, "Error, gatt is null");
|
|
701
|
+
commandQueue.clear();
|
|
702
|
+
commandQueueBusy = false;
|
|
703
|
+
return;
|
|
594
704
|
}
|
|
705
|
+
|
|
706
|
+
// Execute the next command in the queue
|
|
707
|
+
commandQueueBusy = true;
|
|
708
|
+
mainHandler.post(new Runnable() {
|
|
709
|
+
@Override
|
|
710
|
+
public void run() {
|
|
711
|
+
try {
|
|
712
|
+
nextCommand.run();
|
|
713
|
+
} catch (Exception ex) {
|
|
714
|
+
Log.d(BleManager.LOG_TAG, "Error, command exception");
|
|
715
|
+
completedCommand();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
});
|
|
595
719
|
}
|
|
596
720
|
}
|
|
597
721
|
|
|
598
|
-
public void readRSSI(Callback callback) {
|
|
722
|
+
public void readRSSI(final Callback callback) {
|
|
599
723
|
if (!isConnected()) {
|
|
600
724
|
callback.invoke("Device is not connected", null);
|
|
601
725
|
return;
|
|
@@ -605,11 +729,26 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
605
729
|
return;
|
|
606
730
|
}
|
|
607
731
|
|
|
608
|
-
|
|
732
|
+
final boolean result = commandQueue.add(new Runnable() {
|
|
733
|
+
@Override
|
|
734
|
+
public void run() {
|
|
735
|
+
if (isConnected()) {
|
|
736
|
+
readRSSICallback = callback;
|
|
737
|
+
if (! gatt.readRemoteRssi()) {
|
|
738
|
+
readCallback = null;
|
|
739
|
+
sendBackrError(callback, "Read RSSI failed");
|
|
740
|
+
completedCommand();
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
completedCommand();
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
});
|
|
609
747
|
|
|
610
|
-
if (
|
|
611
|
-
|
|
612
|
-
|
|
748
|
+
if (result) {
|
|
749
|
+
nextCommand();
|
|
750
|
+
} else {
|
|
751
|
+
Log.d(BleManager.LOG_TAG, "Could not queue readRemoteRssi command");
|
|
613
752
|
}
|
|
614
753
|
}
|
|
615
754
|
|
|
@@ -667,112 +806,109 @@ public class Peripheral extends BluetoothGattCallback {
|
|
|
667
806
|
return null;
|
|
668
807
|
}
|
|
669
808
|
|
|
670
|
-
public boolean doWrite(BluetoothGattCharacteristic characteristic, byte[] data) {
|
|
671
|
-
|
|
809
|
+
public boolean doWrite(final BluetoothGattCharacteristic characteristic, byte[] data, final Callback callback) {
|
|
810
|
+
final byte[] copyOfData = copyOf(data);
|
|
811
|
+
boolean result = commandQueue.add(new Runnable() {
|
|
812
|
+
@Override
|
|
813
|
+
public void run() {
|
|
814
|
+
characteristic.setValue(copyOfData);
|
|
815
|
+
if (characteristic.getWriteType() == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
|
816
|
+
writeCallback = callback;
|
|
817
|
+
else
|
|
818
|
+
writeCallback = null;
|
|
819
|
+
if (!gatt.writeCharacteristic(characteristic)) {
|
|
820
|
+
sendBackrError(writeCallback, "Write failed");
|
|
821
|
+
writeCallback = null;
|
|
822
|
+
completedCommand();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
});
|
|
672
826
|
|
|
673
|
-
if (
|
|
674
|
-
|
|
675
|
-
|
|
827
|
+
if (result) {
|
|
828
|
+
nextCommand();
|
|
829
|
+
} else {
|
|
830
|
+
Log.d(BleManager.LOG_TAG, "Could not queue write characteristic command");
|
|
676
831
|
}
|
|
677
|
-
|
|
832
|
+
|
|
833
|
+
return result;
|
|
678
834
|
}
|
|
679
835
|
|
|
680
|
-
public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize,
|
|
681
|
-
|
|
682
|
-
if (!isConnected()) {
|
|
836
|
+
public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize, Integer queueSleepTime, Callback callback, int writeType) {
|
|
837
|
+
if (!isConnected() || gatt == null) {
|
|
683
838
|
callback.invoke("Device is not connected", null);
|
|
684
839
|
return;
|
|
685
840
|
}
|
|
686
|
-
if (gatt == null) {
|
|
687
|
-
callback.invoke("BluetoothGatt is null");
|
|
688
|
-
} else {
|
|
689
|
-
BluetoothGattService service = gatt.getService(serviceUUID);
|
|
690
|
-
BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID,
|
|
691
|
-
writeType);
|
|
692
841
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
} else {
|
|
696
|
-
characteristic.setWriteType(writeType);
|
|
842
|
+
BluetoothGattService service = gatt.getService(serviceUUID);
|
|
843
|
+
BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID, writeType);
|
|
697
844
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
845
|
+
if (characteristic == null) {
|
|
846
|
+
callback.invoke("Characteristic " + characteristicUUID + " not found.");
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
702
849
|
|
|
703
|
-
|
|
704
|
-
callback.invoke("You're already writing");
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
850
|
+
characteristic.setWriteType(writeType);
|
|
707
851
|
|
|
708
|
-
|
|
852
|
+
if (data.length <= maxByteSize) {
|
|
853
|
+
if (! doWrite(characteristic, data, callback)) {
|
|
854
|
+
callback.invoke("Write failed");
|
|
855
|
+
writeCallback = null;
|
|
856
|
+
} else {
|
|
857
|
+
if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
|
|
858
|
+
callback.invoke();
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
} else {
|
|
862
|
+
int dataLength = data.length;
|
|
863
|
+
int count = 0;
|
|
864
|
+
byte[] firstMessage = null;
|
|
865
|
+
List<byte[]> splittedMessage = new ArrayList<>();
|
|
866
|
+
|
|
867
|
+
while (count < dataLength && (dataLength - count > maxByteSize)) {
|
|
868
|
+
if (count == 0) {
|
|
869
|
+
firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
|
|
870
|
+
} else {
|
|
871
|
+
byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
|
|
872
|
+
splittedMessage.add(splitMessage);
|
|
873
|
+
}
|
|
874
|
+
count += maxByteSize;
|
|
875
|
+
}
|
|
876
|
+
if (count < dataLength) {
|
|
877
|
+
// Other bytes in queue
|
|
878
|
+
byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
|
|
879
|
+
splittedMessage.add(splitMessage);
|
|
880
|
+
}
|
|
709
881
|
|
|
710
|
-
|
|
711
|
-
|
|
882
|
+
if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
|
|
883
|
+
writeQueue.addAll(splittedMessage);
|
|
884
|
+
if (! doWrite(characteristic, firstMessage, callback)) {
|
|
885
|
+
writeQueue.clear();
|
|
886
|
+
writeCallback = null;
|
|
887
|
+
callback.invoke("Write failed");
|
|
888
|
+
}
|
|
889
|
+
} else {
|
|
890
|
+
try {
|
|
891
|
+
boolean writeError = false;
|
|
892
|
+
if (! doWrite(characteristic, firstMessage, callback)) {
|
|
893
|
+
writeError = true;
|
|
894
|
+
callback.invoke("Write failed");
|
|
712
895
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
List<byte[]> splittedMessage = new ArrayList<>();
|
|
719
|
-
|
|
720
|
-
while (count < dataLength && (dataLength - count > maxByteSize)) {
|
|
721
|
-
if (count == 0) {
|
|
722
|
-
firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
|
|
723
|
-
} else {
|
|
724
|
-
byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
|
|
725
|
-
splittedMessage.add(splitMessage);
|
|
726
|
-
}
|
|
727
|
-
count += maxByteSize;
|
|
728
|
-
}
|
|
729
|
-
if (count < dataLength) {
|
|
730
|
-
// Other bytes in queue
|
|
731
|
-
byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
|
|
732
|
-
splittedMessage.add(splitMessage);
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
|
|
736
|
-
writeQueue.addAll(splittedMessage);
|
|
737
|
-
if (!doWrite(characteristic, firstMessage)) {
|
|
738
|
-
writeQueue.clear();
|
|
739
|
-
writeCallback = null;
|
|
896
|
+
if (! writeError) {
|
|
897
|
+
Thread.sleep(queueSleepTime);
|
|
898
|
+
for (byte[] message : splittedMessage) {
|
|
899
|
+
if (! doWrite(characteristic, message, callback)) {
|
|
900
|
+
writeError = true;
|
|
740
901
|
callback.invoke("Write failed");
|
|
902
|
+
break;
|
|
741
903
|
}
|
|
742
|
-
|
|
743
|
-
try {
|
|
744
|
-
boolean writeError = false;
|
|
745
|
-
if (!doWrite(characteristic, firstMessage)) {
|
|
746
|
-
writeError = true;
|
|
747
|
-
callback.invoke("Write failed");
|
|
748
|
-
}
|
|
749
|
-
if (!writeError) {
|
|
750
|
-
Thread.sleep(queueSleepTime);
|
|
751
|
-
for (byte[] message : splittedMessage) {
|
|
752
|
-
if (!doWrite(characteristic, message)) {
|
|
753
|
-
writeError = true;
|
|
754
|
-
callback.invoke("Write failed");
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
Thread.sleep(queueSleepTime);
|
|
758
|
-
}
|
|
759
|
-
if (!writeError) {
|
|
760
|
-
callback.invoke();
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
} catch (InterruptedException e) {
|
|
764
|
-
callback.invoke("Error during writing");
|
|
765
|
-
}
|
|
904
|
+
Thread.sleep(queueSleepTime);
|
|
766
905
|
}
|
|
767
|
-
|
|
768
|
-
Log.d(BleManager.LOG_TAG, "Write completed");
|
|
769
|
-
if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
|
|
906
|
+
if (! writeError) {
|
|
770
907
|
callback.invoke();
|
|
771
908
|
}
|
|
772
|
-
} else {
|
|
773
|
-
callback.invoke("Write failed");
|
|
774
|
-
writeCallback = null;
|
|
775
909
|
}
|
|
910
|
+
} catch (InterruptedException e) {
|
|
911
|
+
callback.invoke("Error during writing");
|
|
776
912
|
}
|
|
777
913
|
}
|
|
778
914
|
}
|
package/ios/BleManager.m
CHANGED
|
@@ -720,14 +720,6 @@ RCT_EXPORT_METHOD(requestMTU:(NSString *)deviceUUID mtu:(NSInteger)mtu callback:
|
|
|
720
720
|
callback(@[@"Not supported"]);
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
-
RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
|
|
724
|
-
// Keep: Required for RN built in Event Emitter Calls.
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
|
|
728
|
-
// Keep: Required for RN built in Event Emitter Calls.
|
|
729
|
-
}
|
|
730
|
-
|
|
731
723
|
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
|
|
732
724
|
NSLog(@"didWrite");
|
|
733
725
|
|
package/package.json
CHANGED
package/ISSUE_TEMPLATE.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
### Before open an issue
|
|
2
|
-
- Check the closed issues, your question maybe is not new.
|
|
3
|
-
- We can't debug your physical devices.
|
|
4
|
-
- If the library is not working 99% you did something wrong, in the code or in the phone permissions.
|
|
5
|
-
|
|
6
|
-
### Version
|
|
7
|
-
|
|
8
|
-
Tell us which versions you are using:
|
|
9
|
-
|
|
10
|
-
- react-native-ble-manager v?.?.?
|
|
11
|
-
- react-native v?.?
|
|
12
|
-
- iOS/Android v.?
|
|
13
|
-
|
|
14
|
-
### Expected behaviour
|
|
15
|
-
|
|
16
|
-
Tell us what should happen
|
|
17
|
-
|
|
18
|
-
### Actual behaviour
|
|
19
|
-
|
|
20
|
-
Tell us what happens instead
|
|
21
|
-
|
|
22
|
-
### Steps to reproduce
|
|
23
|
-
|
|
24
|
-
1.
|
|
25
|
-
2.
|
|
26
|
-
3.
|
|
27
|
-
|
|
28
|
-
### Stack trace and console log
|
|
29
|
-
|
|
30
|
-
Hint: it would help a lot if you enable the debugger ("Pause on exceptions" in the "Source" panel of Chrome dev tools) and spot the place where the error is thrown
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
```
|