diff --git a/README.md b/README.md index 82e6bb91..3278838f 100644 --- a/README.md +++ b/README.md @@ -389,14 +389,15 @@ Stop scanning for BLE devices. For an example, see [usage](#usage). ### connect(...) ```typescript -connect(deviceId: string) => Promise +connect(deviceId: string, onDisconnect?: ((deviceId: string) => void) | undefined) => Promise ``` Connect to a peripheral BLE device. For an example, see [usage](#usage). -| Param | Type | Description | -| -------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | -| **`deviceId`** | string | The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan)) | +| Param | Type | Description | +| ------------------ | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| **`deviceId`** | string | The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan)) | +| **`onDisconnect`** | ((deviceId: string) => void) | Optional disconnect callback function that will be used when the device disconnects | --- diff --git a/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt b/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt index af122997..5a396f1e 100644 --- a/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt +++ b/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt @@ -13,6 +13,7 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.os.ParcelUuid +import android.util.Log import com.getcapacitor.* import com.getcapacitor.Logger.config import java.util.* @@ -206,7 +207,13 @@ class BluetoothLe : Plugin() { val device: Device try { - device = Device(activity.applicationContext, bluetoothAdapter!!, deviceId) + device = Device( + activity.applicationContext, + bluetoothAdapter!!, + deviceId + ) { -> + onDisconnect(deviceId) + } } catch (e: IllegalArgumentException) { call.reject("Invalid deviceId") return @@ -223,6 +230,10 @@ class BluetoothLe : Plugin() { } } + private fun onDisconnect(deviceId: String) { + notifyListeners("disconnected|${deviceId}", null) + } + @PluginMethod fun disconnect(call: PluginCall) { val device = getDevice(call) ?: return diff --git a/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt b/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt index abdb148c..b311bd66 100644 --- a/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt +++ b/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt @@ -17,6 +17,7 @@ class Device( private val context: Context, private val bluetoothAdapter: BluetoothAdapter, private val address: String, + private val onDisconnect: () -> Unit ) { companion object { private val TAG = Device::class.java.simpleName @@ -51,6 +52,7 @@ class Device( } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { connectionState = STATE_DISCONNECTED + onDisconnect() Log.d(TAG, "Disconnected from GATT server.") resolve("disconnect", "Disconnected.") } diff --git a/ios/Plugin/DeviceManager.swift b/ios/Plugin/DeviceManager.swift index fbc8e87c..b5884c08 100644 --- a/ios/Plugin/DeviceManager.swift +++ b/ios/Plugin/DeviceManager.swift @@ -202,9 +202,14 @@ class DeviceManager: NSObject, CBCentralManagerDelegate { } self.reject(key, "Failed to connect.") } + + func setOnDisconnected(_ device: Device, _ callback: @escaping Callback) { + let key = "onDisconnected|\(device.getId())" + self.callbackMap[key] = callback + } func disconnect(_ device: Device, _ callback: @escaping Callback) { - let key = "disconnect|\(device.getPeripheral().identifier.uuidString)" + let key = "disconnect|\(device.getId())" self.callbackMap[key] = callback print("Disconnecting from peripheral", device.getPeripheral()) self.centralManager.cancelPeripheralConnection(device.getPeripheral()) @@ -214,7 +219,10 @@ class DeviceManager: NSObject, CBCentralManagerDelegate { // didDisconnectPeripheral func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { let key = "disconnect|\(peripheral.identifier.uuidString)" + let keyOnDisconnected = "onDisconnected|\(peripheral.identifier.uuidString)" + self.resolve(keyOnDisconnected, "Disconnected.") if error != nil { + print(error!.localizedDescription) self.reject(key, error!.localizedDescription) return } diff --git a/ios/Plugin/Plugin.swift b/ios/Plugin/Plugin.swift index 099de03f..961e1be4 100644 --- a/ios/Plugin/Plugin.swift +++ b/ios/Plugin/Plugin.swift @@ -115,6 +115,10 @@ public class BluetoothLe: CAPPlugin { call.reject(message) } }) + self.deviceManager?.setOnDisconnected(device, {(success, message) -> Void in + let key = "disconnected|\(device.getId())" + self.notifyListeners(key, data: nil) + }) self.deviceManager?.connect(device, {(success, message) -> Void in if success { print("Connected to peripheral. Waiting for service discovery.") diff --git a/src/bleClient.ts b/src/bleClient.ts index fd2f3bcf..b8301bbd 100644 --- a/src/bleClient.ts +++ b/src/bleClient.ts @@ -66,8 +66,12 @@ export interface BleClientInterface { /** * Connect to a peripheral BLE device. For an example, see [usage](#usage). * @param deviceId The ID of the device to use (obtained from [requestDevice](#requestDevice) or [requestLEScan](#requestLEScan)) + * @param onDisconnect Optional disconnect callback function that will be used when the device disconnects */ - connect(deviceId: string): Promise; + connect( + deviceId: string, + onDisconnect?: (deviceId: string) => void, + ): Promise; /** * Disconnect from a peripheral BLE device. For an example, see [usage](#usage). @@ -130,7 +134,7 @@ export interface BleClientInterface { class BleClientClass implements BleClientInterface { scanListener: PluginListenerHandle | null = null; - notifyListeners = new Map(); + eventListeners = new Map(); async initialize(): Promise { await BluetoothLe.initialize(); @@ -145,17 +149,17 @@ class BleClientClass implements BleClientInterface { callback: (value: boolean) => void, ): Promise { const key = `onEnabledChanged`; - this.notifyListeners.get(key)?.remove(); + this.eventListeners.get(key)?.remove(); const listener = BluetoothLe.addListener(key, result => { callback(result.value); }); - this.notifyListeners.set(key, listener); + this.eventListeners.set(key, listener); await BluetoothLe.startEnabledNotifications(); } async stopEnabledNotifications(): Promise { const key = `onEnabledChanged`; - this.notifyListeners.get(key)?.remove(); + this.eventListeners.get(key)?.remove(); await BluetoothLe.stopEnabledNotifications(); } @@ -189,7 +193,18 @@ class BleClientClass implements BleClientInterface { await BluetoothLe.stopLEScan(); } - async connect(deviceId: string): Promise { + async connect( + deviceId: string, + onDisconnect?: (deviceId: string) => void, + ): Promise { + if (onDisconnect) { + const key = `disconnected|${deviceId}`; + this.eventListeners.get(key)?.remove(); + const listener = BluetoothLe.addListener(key, () => { + onDisconnect(deviceId); + }); + this.eventListeners.set(key, listener); + } await BluetoothLe.connect({ deviceId }); } @@ -236,11 +251,11 @@ class BleClientClass implements BleClientInterface { callback: (value: DataView) => void, ): Promise { const key = `notification|${deviceId}|${service}|${characteristic}`; - this.notifyListeners.get(key)?.remove(); + this.eventListeners.get(key)?.remove(); const listener = BluetoothLe.addListener(key, (event: ReadResult) => { callback(this.convertValue(event?.value)); }); - this.notifyListeners.set(key, listener); + this.eventListeners.set(key, listener); await BluetoothLe.startNotifications({ deviceId, service, @@ -254,8 +269,8 @@ class BleClientClass implements BleClientInterface { characteristic: string, ): Promise { const key = `notification|${service}|${characteristic}`; - this.notifyListeners.get(key)?.remove(); - this.notifyListeners.delete(key); + this.eventListeners.get(key)?.remove(); + this.eventListeners.delete(key); await BluetoothLe.stopNotifications({ deviceId, service, diff --git a/src/web.ts b/src/web.ts index 1e342b27..dc330027 100644 --- a/src/web.ts +++ b/src/web.ts @@ -101,7 +101,16 @@ export class BluetoothLeWeb extends WebPlugin implements BluetoothLePlugin { } async connect(options: ConnectOptions): Promise { - await this.getDevice(options.deviceId).gatt?.connect(); + const device = await this.getDevice(options.deviceId); + device.removeEventListener('gattserverdisconnected', this.onDisconnected); + device.addEventListener('gattserverdisconnected', this.onDisconnected); + await device.gatt?.connect(); + } + + private onDisconnected(event: Event) { + const deviceId = (event.target as BluetoothDevice).id; + const key = `disconnected|${deviceId}`; + BluetoothLe.notifyListeners(key, null); } async disconnect(options: ConnectOptions): Promise {