@capacitor-community/bluetooth-le 3.0.0 → 3.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.
- package/README.md +37 -7
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt +22 -7
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt +161 -30
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceScanner.kt +2 -0
- package/dist/docs.json +28 -7
- package/dist/esm/bleClient.d.ts +8 -3
- package/dist/esm/bleClient.js +2 -2
- package/dist/esm/bleClient.js.map +1 -1
- package/dist/esm/definitions.d.ts +1 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.cjs.js +2 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +2 -2
- package/dist/plugin.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<a href="https://www.npmjs.com/package/@capacitor-community/bluetooth-le"><img src="https://img.shields.io/npm/dw/@capacitor-community/bluetooth-le?style=flat-square" /></a>
|
|
14
14
|
<a href="https://www.npmjs.com/package/@capacitor-community/bluetooth-le"><img src="https://img.shields.io/npm/v/@capacitor-community/bluetooth-le?style=flat-square" /></a>
|
|
15
15
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
16
|
-
<a href="#contributors-"><img src="https://img.shields.io/badge/all%20contributors-
|
|
16
|
+
<a href="#contributors-"><img src="https://img.shields.io/badge/all%20contributors-14-orange?style=flat-square" /></a>
|
|
17
17
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
18
18
|
</p>
|
|
19
19
|
|
|
@@ -122,6 +122,8 @@ If the app needs to use Bluetooth while it is in the background, you also have t
|
|
|
122
122
|
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
**Note**: Bluetooth is **not** available in the iOS simulator. The `initialize` call will be rejected with an error "BLE unsupported". You have to test your app on a real device.
|
|
126
|
+
|
|
125
127
|
### Android
|
|
126
128
|
|
|
127
129
|
On Android, no further steps are required to use the plugin (if you are using Capacitor 2, see [here](https://github.com/capacitor-community/bluetooth-le/blob/0.x/README.md#android)).
|
|
@@ -205,6 +207,8 @@ import { BleClient } from '@capacitor-community/bluetooth-le';
|
|
|
205
207
|
import { BluetoothLe } from '@capacitor-community/bluetooth-le';
|
|
206
208
|
```
|
|
207
209
|
|
|
210
|
+
### Heart rate monitor
|
|
211
|
+
|
|
208
212
|
Here is an example of how to use the plugin. It shows how to read the heart rate from a BLE heart rate monitor such as the Polar H10.
|
|
209
213
|
|
|
210
214
|
```typescript
|
|
@@ -277,7 +281,9 @@ function parseHeartRate(value: DataView): number {
|
|
|
277
281
|
}
|
|
278
282
|
```
|
|
279
283
|
|
|
280
|
-
|
|
284
|
+
### Scanning API
|
|
285
|
+
|
|
286
|
+
Here is an example of using the scanning API.
|
|
281
287
|
|
|
282
288
|
```typescript
|
|
283
289
|
import { BleClient, numberToUUID } from '@capacitor-community/bluetooth-le';
|
|
@@ -401,6 +407,7 @@ enable() => Promise<void>
|
|
|
401
407
|
|
|
402
408
|
Enable Bluetooth.
|
|
403
409
|
Only available on **Android**.
|
|
410
|
+
_deprecated_ See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()
|
|
404
411
|
|
|
405
412
|
---
|
|
406
413
|
|
|
@@ -412,6 +419,7 @@ disable() => Promise<void>
|
|
|
412
419
|
|
|
413
420
|
Disable Bluetooth.
|
|
414
421
|
Only available on **Android**.
|
|
422
|
+
_deprecated_ See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()
|
|
415
423
|
|
|
416
424
|
---
|
|
417
425
|
|
|
@@ -528,7 +536,7 @@ requestLEScan(options: RequestBleDeviceOptions, callback: (result: ScanResult) =
|
|
|
528
536
|
|
|
529
537
|
Start scanning for BLE devices to interact with according to the filters in the options. The callback will be invoked on each device that is found.
|
|
530
538
|
Scanning will continue until `stopLEScan` is called. For an example, see [usage](#usage).
|
|
531
|
-
**
|
|
539
|
+
**Note**: Use with care on **web** platform, the required API is still behind a flag in most browsers.
|
|
532
540
|
|
|
533
541
|
| Param | Type |
|
|
534
542
|
| -------------- | --------------------------------------------------------------------------- |
|
|
@@ -604,15 +612,16 @@ Connect to a peripheral BLE device. For an example, see [usage](#usage).
|
|
|
604
612
|
### createBond(...)
|
|
605
613
|
|
|
606
614
|
```typescript
|
|
607
|
-
createBond(deviceId: string) => Promise<void>
|
|
615
|
+
createBond(deviceId: string, options?: TimeoutOptions | undefined) => Promise<void>
|
|
608
616
|
```
|
|
609
617
|
|
|
610
618
|
Create a bond with a peripheral BLE device.
|
|
611
619
|
Only available on **Android**. On iOS bonding is handled by the OS.
|
|
612
620
|
|
|
613
|
-
| Param | Type
|
|
614
|
-
| -------------- |
|
|
615
|
-
| **`deviceId`** | <code>string</code>
|
|
621
|
+
| Param | Type | Description |
|
|
622
|
+
| -------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
623
|
+
| **`deviceId`** | <code>string</code> | The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan)) |
|
|
624
|
+
| **`options`** | <code><a href="#timeoutoptions">TimeoutOptions</a></code> | Options for plugin call |
|
|
616
625
|
|
|
617
626
|
---
|
|
618
627
|
|
|
@@ -1042,6 +1051,23 @@ await BleClient.disconnect(device.deviceId);
|
|
|
1042
1051
|
await BleClient.connect(device.deviceId);
|
|
1043
1052
|
```
|
|
1044
1053
|
|
|
1054
|
+
#### No devices found on Android
|
|
1055
|
+
|
|
1056
|
+
On Android, the `initialize` call requests the location permission. However, if location services are disable on the OS level, the app will not find any devices. You can check if the location is enabled and open the settings when not.
|
|
1057
|
+
|
|
1058
|
+
```typescript
|
|
1059
|
+
async function initialize() {
|
|
1060
|
+
// Check if location is enabled
|
|
1061
|
+
if (this.platform.is('android')) {
|
|
1062
|
+
const isLocationEnabled = await BleClient.isLocationEnabled();
|
|
1063
|
+
if (!isLocationEnabled) {
|
|
1064
|
+
await BleClient.openLocationSettings();
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
await BleClient.initialize();
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1045
1071
|
## Contributors ✨
|
|
1046
1072
|
|
|
1047
1073
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
@@ -1064,6 +1090,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
1064
1090
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jrobeson"><img src="https://avatars.githubusercontent.com/u/56908?v=4?s=100" width="100px;" alt="Johnny Robeson"/><br /><sub><b>Johnny Robeson</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=jrobeson" title="Code">💻</a></td>
|
|
1065
1091
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aadito123"><img src="https://avatars.githubusercontent.com/u/63646058?v=4?s=100" width="100px;" alt="Aadit Olkar"/><br /><sub><b>Aadit Olkar</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=aadito123" title="Code">💻</a></td>
|
|
1066
1092
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/y3nd"><img src="https://avatars.githubusercontent.com/u/18102153?v=4?s=100" width="100px;" alt="Yoann N."/><br /><sub><b>Yoann N.</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=y3nd" title="Code">💻</a></td>
|
|
1093
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Andy3189"><img src="https://avatars.githubusercontent.com/u/2084016?v=4?s=100" width="100px;" alt="Andy3189"/><br /><sub><b>Andy3189</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=Andy3189" title="Code">💻</a></td>
|
|
1094
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RFM69CW"><img src="https://avatars.githubusercontent.com/u/20404734?v=4?s=100" width="100px;" alt="Sammy"/><br /><sub><b>Sammy</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=RFM69CW" title="Documentation">📖</a></td>
|
|
1095
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/td-tomasz-joniec"><img src="https://avatars.githubusercontent.com/u/109506928?v=4?s=100" width="100px;" alt="td-tomasz-joniec"/><br /><sub><b>td-tomasz-joniec</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=td-tomasz-joniec" title="Code">💻</a></td>
|
|
1096
|
+
<td align="center" valign="top" width="14.28%"><a href="https://fanxj.com"><img src="https://avatars.githubusercontent.com/u/10436013?v=4?s=100" width="100px;" alt="Michele Ferrari"/><br /><sub><b>Michele Ferrari</b></sub></a><br /><a href="https://github.com/capacitor-community/bluetooth-le/commits?author=micheleypf" title="Code">💻</a></td>
|
|
1067
1097
|
</tr>
|
|
1068
1098
|
</tbody>
|
|
1069
1099
|
</table>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
package com.capacitorjs.community.plugins.bluetoothle
|
|
2
2
|
|
|
3
3
|
import android.Manifest
|
|
4
|
-
import android.
|
|
4
|
+
import android.annotation.SuppressLint
|
|
5
|
+
import android.bluetooth.BluetoothAdapter
|
|
6
|
+
import android.bluetooth.BluetoothDevice
|
|
7
|
+
import android.bluetooth.BluetoothGatt
|
|
8
|
+
import android.bluetooth.BluetoothGattCharacteristic
|
|
9
|
+
import android.bluetooth.BluetoothManager
|
|
10
|
+
import android.bluetooth.BluetoothProfile
|
|
5
11
|
import android.bluetooth.le.ScanFilter
|
|
6
12
|
import android.bluetooth.le.ScanResult
|
|
7
13
|
import android.bluetooth.le.ScanSettings
|
|
@@ -14,15 +20,24 @@ import android.location.LocationManager
|
|
|
14
20
|
import android.net.Uri
|
|
15
21
|
import android.os.Build
|
|
16
22
|
import android.os.ParcelUuid
|
|
17
|
-
import android.provider.Settings
|
|
23
|
+
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
|
24
|
+
import android.provider.Settings.ACTION_BLUETOOTH_SETTINGS
|
|
25
|
+
import android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS
|
|
18
26
|
import androidx.core.location.LocationManagerCompat
|
|
19
|
-
import com.getcapacitor
|
|
27
|
+
import com.getcapacitor.JSArray
|
|
28
|
+
import com.getcapacitor.JSObject
|
|
29
|
+
import com.getcapacitor.Logger
|
|
30
|
+
import com.getcapacitor.PermissionState
|
|
31
|
+
import com.getcapacitor.Plugin
|
|
32
|
+
import com.getcapacitor.PluginCall
|
|
33
|
+
import com.getcapacitor.PluginMethod
|
|
20
34
|
import com.getcapacitor.annotation.CapacitorPlugin
|
|
21
35
|
import com.getcapacitor.annotation.Permission
|
|
22
36
|
import com.getcapacitor.annotation.PermissionCallback
|
|
23
|
-
import java.util
|
|
37
|
+
import java.util.UUID
|
|
24
38
|
|
|
25
39
|
|
|
40
|
+
@SuppressLint("MissingPermission")
|
|
26
41
|
@CapacitorPlugin(
|
|
27
42
|
name = "BluetoothLe",
|
|
28
43
|
permissions = [
|
|
@@ -83,8 +98,7 @@ class BluetoothLe : Plugin() {
|
|
|
83
98
|
|
|
84
99
|
@PluginMethod
|
|
85
100
|
fun initialize(call: PluginCall) {
|
|
86
|
-
|
|
87
|
-
if (Build.VERSION.SDK_INT >= 31) {
|
|
101
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
88
102
|
val neverForLocation = call.getBoolean("androidNeverForLocation", false) as Boolean
|
|
89
103
|
aliases = if (neverForLocation) {
|
|
90
104
|
arrayOf(
|
|
@@ -426,7 +440,8 @@ class BluetoothLe : Plugin() {
|
|
|
426
440
|
@PluginMethod
|
|
427
441
|
fun createBond(call: PluginCall) {
|
|
428
442
|
val device = getOrCreateDevice(call) ?: return
|
|
429
|
-
|
|
443
|
+
val timeout = call.getFloat("timeout", DEFAULT_TIMEOUT)!!.toLong()
|
|
444
|
+
device.createBond(timeout) { response ->
|
|
430
445
|
run {
|
|
431
446
|
if (response.success) {
|
|
432
447
|
call.resolve()
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
package com.capacitorjs.community.plugins.bluetoothle
|
|
2
2
|
|
|
3
|
-
import android.
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.annotation.TargetApi
|
|
5
|
+
import android.bluetooth.BluetoothAdapter
|
|
6
|
+
import android.bluetooth.BluetoothDevice
|
|
7
|
+
import android.bluetooth.BluetoothGatt
|
|
8
|
+
import android.bluetooth.BluetoothGattCallback
|
|
9
|
+
import android.bluetooth.BluetoothGattCharacteristic
|
|
10
|
+
import android.bluetooth.BluetoothGattDescriptor
|
|
11
|
+
import android.bluetooth.BluetoothGattService
|
|
12
|
+
import android.bluetooth.BluetoothProfile
|
|
13
|
+
import android.bluetooth.BluetoothStatusCodes
|
|
4
14
|
import android.content.BroadcastReceiver
|
|
5
15
|
import android.content.Context
|
|
6
16
|
import android.content.Intent
|
|
@@ -8,14 +18,36 @@ import android.content.IntentFilter
|
|
|
8
18
|
import android.os.Build
|
|
9
19
|
import android.os.Handler
|
|
10
20
|
import android.os.Looper
|
|
21
|
+
import androidx.annotation.RequiresApi
|
|
11
22
|
import com.getcapacitor.Logger
|
|
12
|
-
import java.util
|
|
23
|
+
import java.util.UUID
|
|
24
|
+
import java.util.concurrent.ConcurrentLinkedQueue
|
|
13
25
|
|
|
14
26
|
class CallbackResponse(
|
|
15
27
|
val success: Boolean,
|
|
16
28
|
val value: String,
|
|
17
29
|
)
|
|
18
30
|
|
|
31
|
+
class TimeoutHandler(
|
|
32
|
+
val key: String,
|
|
33
|
+
val handler: Handler
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
fun <T> ConcurrentLinkedQueue<T>.popFirstMatch(predicate: (T) -> Boolean): T? {
|
|
37
|
+
synchronized(this) {
|
|
38
|
+
val iterator = this.iterator()
|
|
39
|
+
while (iterator.hasNext()) {
|
|
40
|
+
val nextItem = iterator.next()
|
|
41
|
+
if (predicate(nextItem)) {
|
|
42
|
+
iterator.remove()
|
|
43
|
+
return nextItem
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@SuppressLint("MissingPermission")
|
|
19
51
|
class Device(
|
|
20
52
|
private val context: Context,
|
|
21
53
|
bluetoothAdapter: BluetoothAdapter,
|
|
@@ -35,7 +67,7 @@ class Device(
|
|
|
35
67
|
private var device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(address)
|
|
36
68
|
private var bluetoothGatt: BluetoothGatt? = null
|
|
37
69
|
private var callbackMap = HashMap<String, ((CallbackResponse) -> Unit)>()
|
|
38
|
-
private
|
|
70
|
+
private val timeoutQueue = ConcurrentLinkedQueue<TimeoutHandler>()
|
|
39
71
|
private var bondStateReceiver: BroadcastReceiver? = null
|
|
40
72
|
private var currentMtu = -1
|
|
41
73
|
|
|
@@ -96,9 +128,15 @@ class Device(
|
|
|
96
128
|
}
|
|
97
129
|
}
|
|
98
130
|
|
|
131
|
+
@TargetApi(Build.VERSION_CODES.S_V2)
|
|
99
132
|
override fun onCharacteristicRead(
|
|
100
133
|
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
|
|
101
134
|
) {
|
|
135
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
136
|
+
// handled by new callback below
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
Logger.verbose(TAG, "Using deprecated onCharacteristicRead.")
|
|
102
140
|
super.onCharacteristicRead(gatt, characteristic, status)
|
|
103
141
|
val key = "read|${characteristic.service.uuid}|${characteristic.uuid}"
|
|
104
142
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
@@ -114,6 +152,24 @@ class Device(
|
|
|
114
152
|
}
|
|
115
153
|
}
|
|
116
154
|
|
|
155
|
+
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
|
156
|
+
override fun onCharacteristicRead(
|
|
157
|
+
gatt: BluetoothGatt,
|
|
158
|
+
characteristic: BluetoothGattCharacteristic,
|
|
159
|
+
data: ByteArray,
|
|
160
|
+
status: Int
|
|
161
|
+
) {
|
|
162
|
+
Logger.verbose(TAG, "Using onCharacteristicRead from API level 33.")
|
|
163
|
+
super.onCharacteristicRead(gatt, characteristic, data, status)
|
|
164
|
+
val key = "read|${characteristic.service.uuid}|${characteristic.uuid}"
|
|
165
|
+
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
166
|
+
val value = bytesToString(data)
|
|
167
|
+
resolve(key, value)
|
|
168
|
+
} else {
|
|
169
|
+
reject(key, "Reading characteristic failed.")
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
117
173
|
override fun onCharacteristicWrite(
|
|
118
174
|
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
|
|
119
175
|
) {
|
|
@@ -127,9 +183,15 @@ class Device(
|
|
|
127
183
|
|
|
128
184
|
}
|
|
129
185
|
|
|
186
|
+
@TargetApi(Build.VERSION_CODES.S_V2)
|
|
130
187
|
override fun onCharacteristicChanged(
|
|
131
188
|
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic
|
|
132
189
|
) {
|
|
190
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
191
|
+
// handled by new callback below
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
Logger.verbose(TAG, "Using deprecated onCharacteristicChanged.")
|
|
133
195
|
super.onCharacteristicChanged(gatt, characteristic)
|
|
134
196
|
val notifyKey = "notification|${characteristic.service.uuid}|${characteristic.uuid}"
|
|
135
197
|
val data = characteristic.value
|
|
@@ -139,9 +201,26 @@ class Device(
|
|
|
139
201
|
}
|
|
140
202
|
}
|
|
141
203
|
|
|
204
|
+
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
|
205
|
+
override fun onCharacteristicChanged(
|
|
206
|
+
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray
|
|
207
|
+
) {
|
|
208
|
+
Logger.verbose(TAG, "Using onCharacteristicChanged from API level 33.")
|
|
209
|
+
super.onCharacteristicChanged(gatt, characteristic, data)
|
|
210
|
+
val notifyKey = "notification|${characteristic.service.uuid}|${characteristic.uuid}"
|
|
211
|
+
val value = bytesToString(data)
|
|
212
|
+
callbackMap[notifyKey]?.invoke(CallbackResponse(true, value))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@TargetApi(Build.VERSION_CODES.S_V2)
|
|
142
216
|
override fun onDescriptorRead(
|
|
143
217
|
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
|
|
144
218
|
) {
|
|
219
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
220
|
+
// handled by new callback below
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
Logger.verbose(TAG, "Using deprecated onDescriptorRead.")
|
|
145
224
|
super.onDescriptorRead(gatt, descriptor, status)
|
|
146
225
|
val key =
|
|
147
226
|
"readDescriptor|${descriptor.characteristic.service.uuid}|${descriptor.characteristic.uuid}|${descriptor.uuid}"
|
|
@@ -158,6 +237,22 @@ class Device(
|
|
|
158
237
|
}
|
|
159
238
|
}
|
|
160
239
|
|
|
240
|
+
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
|
241
|
+
override fun onDescriptorRead(
|
|
242
|
+
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, data: ByteArray
|
|
243
|
+
) {
|
|
244
|
+
Logger.verbose(TAG, "Using onDescriptorRead from API level 33.")
|
|
245
|
+
super.onDescriptorRead(gatt, descriptor, status, data)
|
|
246
|
+
val key =
|
|
247
|
+
"readDescriptor|${descriptor.characteristic.service.uuid}|${descriptor.characteristic.uuid}|${descriptor.uuid}"
|
|
248
|
+
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
249
|
+
val value = bytesToString(data)
|
|
250
|
+
resolve(key, value)
|
|
251
|
+
} else {
|
|
252
|
+
reject(key, "Reading descriptor failed.")
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
161
256
|
override fun onDescriptorWrite(
|
|
162
257
|
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
|
|
163
258
|
) {
|
|
@@ -229,7 +324,7 @@ class Device(
|
|
|
229
324
|
return bluetoothGatt?.requestConnectionPriority(connectionPriority) ?: false
|
|
230
325
|
}
|
|
231
326
|
|
|
232
|
-
fun createBond(callback: (CallbackResponse) -> Unit) {
|
|
327
|
+
fun createBond(timeout: Long, callback: (CallbackResponse) -> Unit) {
|
|
233
328
|
val key = "createBond"
|
|
234
329
|
callbackMap[key] = callback
|
|
235
330
|
try {
|
|
@@ -250,6 +345,7 @@ class Device(
|
|
|
250
345
|
return
|
|
251
346
|
}
|
|
252
347
|
// otherwise, wait for bond state change
|
|
348
|
+
setTimeout(key, "Bonding timeout.", timeout)
|
|
253
349
|
}
|
|
254
350
|
|
|
255
351
|
private fun createBondStateReceiver() {
|
|
@@ -260,7 +356,14 @@ class Device(
|
|
|
260
356
|
if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
|
|
261
357
|
val key = "createBond"
|
|
262
358
|
val updatedDevice =
|
|
263
|
-
|
|
359
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
360
|
+
intent.getParcelableExtra(
|
|
361
|
+
BluetoothDevice.EXTRA_DEVICE,
|
|
362
|
+
BluetoothDevice::class.java
|
|
363
|
+
)
|
|
364
|
+
} else {
|
|
365
|
+
intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
|
366
|
+
}
|
|
264
367
|
// BroadcastReceiver receives bond state updates from all devices, need to filter by device
|
|
265
368
|
if (device.address == updatedDevice?.address) {
|
|
266
369
|
val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)
|
|
@@ -388,12 +491,21 @@ class Device(
|
|
|
388
491
|
return
|
|
389
492
|
}
|
|
390
493
|
val bytes = stringToBytes(value)
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
494
|
+
|
|
495
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
496
|
+
val statusCode = bluetoothGatt?.writeCharacteristic(characteristic, bytes, writeType)
|
|
497
|
+
if (statusCode != BluetoothStatusCodes.SUCCESS) {
|
|
498
|
+
reject(key, "Writing characteristic failed with status code $statusCode.")
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
characteristic.value = bytes
|
|
503
|
+
characteristic.writeType = writeType
|
|
504
|
+
val result = bluetoothGatt?.writeCharacteristic(characteristic)
|
|
505
|
+
if (result != true) {
|
|
506
|
+
reject(key, "Writing characteristic failed.")
|
|
507
|
+
return
|
|
508
|
+
}
|
|
397
509
|
}
|
|
398
510
|
setTimeout(key, "Write timeout.", timeout)
|
|
399
511
|
}
|
|
@@ -430,20 +542,32 @@ class Device(
|
|
|
430
542
|
return
|
|
431
543
|
}
|
|
432
544
|
|
|
433
|
-
if (enable) {
|
|
545
|
+
val value = if (enable) {
|
|
434
546
|
if ((characteristic.properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
|
|
435
|
-
|
|
547
|
+
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
|
436
548
|
} else if ((characteristic.properties and BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
|
|
437
|
-
|
|
549
|
+
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
|
|
550
|
+
} else {
|
|
551
|
+
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
|
|
438
552
|
}
|
|
439
553
|
} else {
|
|
440
|
-
|
|
554
|
+
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
|
|
441
555
|
}
|
|
442
556
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
557
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
558
|
+
val statusCode = bluetoothGatt?.writeDescriptor(descriptor, value)
|
|
559
|
+
if (statusCode != BluetoothStatusCodes.SUCCESS) {
|
|
560
|
+
reject(key, "Setting notification failed with status code $statusCode.")
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
descriptor.value = value
|
|
565
|
+
val resultDesc = bluetoothGatt?.writeDescriptor(descriptor)
|
|
566
|
+
if (resultDesc != true) {
|
|
567
|
+
reject(key, "Setting notification failed.")
|
|
568
|
+
return
|
|
569
|
+
}
|
|
570
|
+
|
|
447
571
|
}
|
|
448
572
|
// wait for onDescriptorWrite
|
|
449
573
|
}
|
|
@@ -498,11 +622,20 @@ class Device(
|
|
|
498
622
|
return
|
|
499
623
|
}
|
|
500
624
|
val bytes = stringToBytes(value)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
625
|
+
|
|
626
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
627
|
+
val statusCode = bluetoothGatt?.writeDescriptor(descriptor, bytes)
|
|
628
|
+
if (statusCode != BluetoothStatusCodes.SUCCESS) {
|
|
629
|
+
reject(key, "Writing descriptor failed with status code $statusCode.")
|
|
630
|
+
return
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
descriptor.value = bytes
|
|
634
|
+
val result = bluetoothGatt?.writeDescriptor(descriptor)
|
|
635
|
+
if (result != true) {
|
|
636
|
+
reject(key, "Writing descriptor failed.")
|
|
637
|
+
return
|
|
638
|
+
}
|
|
506
639
|
}
|
|
507
640
|
setTimeout(key, "Write timeout.", timeout)
|
|
508
641
|
}
|
|
@@ -510,20 +643,18 @@ class Device(
|
|
|
510
643
|
private fun resolve(key: String, value: String) {
|
|
511
644
|
if (callbackMap.containsKey(key)) {
|
|
512
645
|
Logger.debug(TAG, "resolve: $key $value")
|
|
646
|
+
timeoutQueue.popFirstMatch { it.key == key }?.handler?.removeCallbacksAndMessages(null)
|
|
513
647
|
callbackMap[key]?.invoke(CallbackResponse(true, value))
|
|
514
648
|
callbackMap.remove(key)
|
|
515
|
-
timeoutMap[key]?.removeCallbacksAndMessages(null)
|
|
516
|
-
timeoutMap.remove(key)
|
|
517
649
|
}
|
|
518
650
|
}
|
|
519
651
|
|
|
520
652
|
private fun reject(key: String, value: String) {
|
|
521
653
|
if (callbackMap.containsKey(key)) {
|
|
522
654
|
Logger.debug(TAG, "reject: $key $value")
|
|
655
|
+
timeoutQueue.popFirstMatch { it.key == key }?.handler?.removeCallbacksAndMessages(null)
|
|
523
656
|
callbackMap[key]?.invoke(CallbackResponse(false, value))
|
|
524
657
|
callbackMap.remove(key)
|
|
525
|
-
timeoutMap[key]?.removeCallbacksAndMessages(null)
|
|
526
|
-
timeoutMap.remove(key)
|
|
527
658
|
}
|
|
528
659
|
}
|
|
529
660
|
|
|
@@ -531,7 +662,7 @@ class Device(
|
|
|
531
662
|
key: String, message: String, timeout: Long
|
|
532
663
|
) {
|
|
533
664
|
val handler = Handler(Looper.getMainLooper())
|
|
534
|
-
|
|
665
|
+
timeoutQueue.add(TimeoutHandler(key, handler))
|
|
535
666
|
handler.postDelayed({
|
|
536
667
|
reject(key, message)
|
|
537
668
|
}, timeout)
|
|
@@ -544,7 +675,7 @@ class Device(
|
|
|
544
675
|
timeout: Long,
|
|
545
676
|
) {
|
|
546
677
|
val handler = Handler(Looper.getMainLooper())
|
|
547
|
-
|
|
678
|
+
timeoutQueue.add(TimeoutHandler(key, handler))
|
|
548
679
|
handler.postDelayed({
|
|
549
680
|
connectionState = STATE_DISCONNECTED
|
|
550
681
|
gatt?.disconnect()
|
package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceScanner.kt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.capacitorjs.community.plugins.bluetoothle
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint
|
|
3
4
|
import android.app.AlertDialog
|
|
4
5
|
import android.bluetooth.BluetoothAdapter
|
|
5
6
|
import android.bluetooth.BluetoothDevice
|
|
@@ -27,6 +28,7 @@ class DisplayStrings(
|
|
|
27
28
|
val noDeviceFound: String,
|
|
28
29
|
)
|
|
29
30
|
|
|
31
|
+
@SuppressLint("MissingPermission")
|
|
30
32
|
class DeviceScanner(
|
|
31
33
|
private val context: Context,
|
|
32
34
|
bluetoothAdapter: BluetoothAdapter,
|
package/dist/docs.json
CHANGED
|
@@ -38,8 +38,13 @@
|
|
|
38
38
|
"signature": "() => Promise<void>",
|
|
39
39
|
"parameters": [],
|
|
40
40
|
"returns": "Promise<void>",
|
|
41
|
-
"tags": [
|
|
42
|
-
|
|
41
|
+
"tags": [
|
|
42
|
+
{
|
|
43
|
+
"name": "deprecated",
|
|
44
|
+
"text": "See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"docs": "Enable Bluetooth.\nOnly available on **Android**.\n*deprecated* See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()",
|
|
43
48
|
"complexTypes": [],
|
|
44
49
|
"slug": "enable"
|
|
45
50
|
},
|
|
@@ -48,8 +53,13 @@
|
|
|
48
53
|
"signature": "() => Promise<void>",
|
|
49
54
|
"parameters": [],
|
|
50
55
|
"returns": "Promise<void>",
|
|
51
|
-
"tags": [
|
|
52
|
-
|
|
56
|
+
"tags": [
|
|
57
|
+
{
|
|
58
|
+
"name": "deprecated",
|
|
59
|
+
"text": "See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"docs": "Disable Bluetooth.\nOnly available on **Android**.\n*deprecated* See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()",
|
|
53
63
|
"complexTypes": [],
|
|
54
64
|
"slug": "disable"
|
|
55
65
|
},
|
|
@@ -197,7 +207,7 @@
|
|
|
197
207
|
"text": "callback"
|
|
198
208
|
}
|
|
199
209
|
],
|
|
200
|
-
"docs": "Start scanning for BLE devices to interact with according to the filters in the options. The callback will be invoked on each device that is found.\nScanning will continue until `stopLEScan` is called. For an example, see [usage](#usage).\n**
|
|
210
|
+
"docs": "Start scanning for BLE devices to interact with according to the filters in the options. The callback will be invoked on each device that is found.\nScanning will continue until `stopLEScan` is called. For an example, see [usage](#usage).\n**Note**: Use with care on **web** platform, the required API is still behind a flag in most browsers.",
|
|
201
211
|
"complexTypes": [
|
|
202
212
|
"RequestBleDeviceOptions",
|
|
203
213
|
"ScanResult"
|
|
@@ -303,12 +313,17 @@
|
|
|
303
313
|
},
|
|
304
314
|
{
|
|
305
315
|
"name": "createBond",
|
|
306
|
-
"signature": "(deviceId: string) => Promise<void>",
|
|
316
|
+
"signature": "(deviceId: string, options?: TimeoutOptions | undefined) => Promise<void>",
|
|
307
317
|
"parameters": [
|
|
308
318
|
{
|
|
309
319
|
"name": "deviceId",
|
|
310
320
|
"docs": "The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan))",
|
|
311
321
|
"type": "string"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"name": "options",
|
|
325
|
+
"docs": "Options for plugin call",
|
|
326
|
+
"type": "TimeoutOptions | undefined"
|
|
312
327
|
}
|
|
313
328
|
],
|
|
314
329
|
"returns": "Promise<void>",
|
|
@@ -316,10 +331,16 @@
|
|
|
316
331
|
{
|
|
317
332
|
"name": "param",
|
|
318
333
|
"text": "deviceId The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan))"
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"name": "param",
|
|
337
|
+
"text": "options Options for plugin call"
|
|
319
338
|
}
|
|
320
339
|
],
|
|
321
340
|
"docs": "Create a bond with a peripheral BLE device.\nOnly available on **Android**. On iOS bonding is handled by the OS.",
|
|
322
|
-
"complexTypes": [
|
|
341
|
+
"complexTypes": [
|
|
342
|
+
"TimeoutOptions"
|
|
343
|
+
],
|
|
323
344
|
"slug": "createbond"
|
|
324
345
|
},
|
|
325
346
|
{
|
package/dist/esm/bleClient.d.ts
CHANGED
|
@@ -15,11 +15,15 @@ export interface BleClientInterface {
|
|
|
15
15
|
/**
|
|
16
16
|
* Enable Bluetooth.
|
|
17
17
|
* Only available on **Android**.
|
|
18
|
+
* *deprecated* See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()
|
|
19
|
+
* @deprecated See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()
|
|
18
20
|
*/
|
|
19
21
|
enable(): Promise<void>;
|
|
20
22
|
/**
|
|
21
23
|
* Disable Bluetooth.
|
|
22
24
|
* Only available on **Android**.
|
|
25
|
+
* *deprecated* See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()
|
|
26
|
+
* @deprecated See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()
|
|
23
27
|
*/
|
|
24
28
|
disable(): Promise<void>;
|
|
25
29
|
/**
|
|
@@ -69,7 +73,7 @@ export interface BleClientInterface {
|
|
|
69
73
|
/**
|
|
70
74
|
* Start scanning for BLE devices to interact with according to the filters in the options. The callback will be invoked on each device that is found.
|
|
71
75
|
* Scanning will continue until `stopLEScan` is called. For an example, see [usage](#usage).
|
|
72
|
-
* **
|
|
76
|
+
* **Note**: Use with care on **web** platform, the required API is still behind a flag in most browsers.
|
|
73
77
|
* @param options
|
|
74
78
|
* @param callback
|
|
75
79
|
*/
|
|
@@ -105,8 +109,9 @@ export interface BleClientInterface {
|
|
|
105
109
|
* Create a bond with a peripheral BLE device.
|
|
106
110
|
* Only available on **Android**. On iOS bonding is handled by the OS.
|
|
107
111
|
* @param deviceId The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan))
|
|
112
|
+
* @param options Options for plugin call
|
|
108
113
|
*/
|
|
109
|
-
createBond(deviceId: string): Promise<void>;
|
|
114
|
+
createBond(deviceId: string, options?: TimeoutOptions): Promise<void>;
|
|
110
115
|
/**
|
|
111
116
|
* Report whether a peripheral BLE device is bonded.
|
|
112
117
|
* Only available on **Android**. On iOS bonding is handled by the OS.
|
|
@@ -237,7 +242,7 @@ declare class BleClientClass implements BleClientInterface {
|
|
|
237
242
|
getDevices(deviceIds: string[]): Promise<BleDevice[]>;
|
|
238
243
|
getConnectedDevices(services: string[]): Promise<BleDevice[]>;
|
|
239
244
|
connect(deviceId: string, onDisconnect?: (deviceId: string) => void, options?: TimeoutOptions): Promise<void>;
|
|
240
|
-
createBond(deviceId: string): Promise<void>;
|
|
245
|
+
createBond(deviceId: string, options?: TimeoutOptions): Promise<void>;
|
|
241
246
|
isBonded(deviceId: string): Promise<boolean>;
|
|
242
247
|
disconnect(deviceId: string): Promise<void>;
|
|
243
248
|
getServices(deviceId: string): Promise<BleService[]>;
|
package/dist/esm/bleClient.js
CHANGED
|
@@ -148,9 +148,9 @@ class BleClientClass {
|
|
|
148
148
|
await BluetoothLe.connect(Object.assign({ deviceId }, options));
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
-
async createBond(deviceId) {
|
|
151
|
+
async createBond(deviceId, options) {
|
|
152
152
|
await this.queue(async () => {
|
|
153
|
-
await BluetoothLe.createBond({ deviceId });
|
|
153
|
+
await BluetoothLe.createBond(Object.assign({ deviceId }, options));
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
156
|
async isBonded(deviceId) {
|