Wait for writeWithoutResponse completion (#166)
* Wait for writeWithoutResponse request completion on Android * Wait for writeWithoutResponse request completion on Darwin * Add WriteWithoutResponse button in example app * Update changelog and bump version * Apply suggestions from code review Co-authored-by: Foti Dim <foti@navideck.com> --------- Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
## 0.20.1
|
||||||
|
* Fix `writeWithoutResponse()` which was not properly waiting for completion on Android and Apple
|
||||||
|
|
||||||
## 0.20.0
|
## 0.20.0
|
||||||
* Add new high level API. Services are auto-discovered. the BleDevice class offers convenient member methods and properties.
|
* Add new high level API. Services are auto-discovered. the BleDevice class offers convenient member methods and properties.
|
||||||
* BREAKING CHANGE: `bleDevice.isPaired` is now `bleDevice.paired`
|
* BREAKING CHANGE: `bleDevice.isPaired` is now `bleDevice.paired`
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ fun Int.parseGattErrorCode(): String? {
|
|||||||
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> "GATT_INSUFFICIENT_ENCRYPTION"
|
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> "GATT_INSUFFICIENT_ENCRYPTION"
|
||||||
BluetoothGatt.GATT_CONNECTION_CONGESTED -> "GATT_CONNECTION_CONGESTED"
|
BluetoothGatt.GATT_CONNECTION_CONGESTED -> "GATT_CONNECTION_CONGESTED"
|
||||||
BluetoothGatt.GATT_FAILURE -> "GATT_FAILURE"
|
BluetoothGatt.GATT_FAILURE -> "GATT_FAILURE"
|
||||||
|
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED"
|
||||||
|
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY"
|
||||||
|
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED"
|
||||||
0x01 -> "GATT_INVALID_HANDLE"
|
0x01 -> "GATT_INVALID_HANDLE"
|
||||||
0x04 -> "GATT_INVALID_PDU"
|
0x04 -> "GATT_INVALID_PDU"
|
||||||
0x09 -> "GATT_PREPARE_QUEUE_FULL"
|
0x09 -> "GATT_PREPARE_QUEUE_FULL"
|
||||||
|
|||||||
@@ -469,34 +469,39 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
|||||||
writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val writeFuture = WriteResultFuture(
|
||||||
|
gatt.device.address,
|
||||||
|
gattCharacteristic.uuid.toString(),
|
||||||
|
gattCharacteristic.service.uuid.toString(),
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wait for the result
|
||||||
|
writeResultFutureList.add(writeFuture)
|
||||||
|
|
||||||
val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
val writeResult = gatt.writeCharacteristic(gattCharacteristic, value, writeType)
|
gatt.writeCharacteristic(gattCharacteristic, value, writeType)
|
||||||
writeResult == BluetoothGatt.GATT_SUCCESS
|
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
gattCharacteristic.value = value
|
gattCharacteristic.value = value
|
||||||
gattCharacteristic.writeType = writeType
|
gattCharacteristic.writeType = writeType
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
gatt.writeCharacteristic(gattCharacteristic)
|
val status = gatt.writeCharacteristic(gattCharacteristic)
|
||||||
|
if (status) BluetoothGatt.GATT_SUCCESS else BluetoothGatt.GATT_FAILURE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (result != BluetoothGatt.GATT_SUCCESS) {
|
||||||
callback(Result.failure(FlutterError("Failed", "Failed to write", null)))
|
writeResultFutureList.remove(writeFuture)
|
||||||
return
|
callback(
|
||||||
}
|
Result.failure(
|
||||||
|
FlutterError(
|
||||||
if (writeType == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) {
|
"WriteError",
|
||||||
// wait for the result
|
"Failed to write: ${result.parseGattErrorCode()}",
|
||||||
writeResultFutureList.add(
|
null
|
||||||
WriteResultFuture(
|
)
|
||||||
gatt.device.address,
|
|
||||||
gattCharacteristic.uuid.toString(),
|
|
||||||
gattCharacteristic.service.uuid.toString(),
|
|
||||||
callback
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
callback(Result.success(Unit))
|
|
||||||
}
|
}
|
||||||
} catch (e: FlutterError) {
|
} catch (e: FlutterError) {
|
||||||
callback(Result.failure(e))
|
callback(Result.failure(e))
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
|||||||
private var discoveredServicesProgressMap: [String: [UniversalBleService]] = [:]
|
private var discoveredServicesProgressMap: [String: [UniversalBleService]] = [:]
|
||||||
private var characteristicReadFutures = [CharacteristicReadFuture]()
|
private var characteristicReadFutures = [CharacteristicReadFuture]()
|
||||||
private var characteristicWriteFutures = [CharacteristicWriteFuture]()
|
private var characteristicWriteFutures = [CharacteristicWriteFuture]()
|
||||||
|
private var characteristicWriteWithoutResponseFutures = [CharacteristicWriteFuture]()
|
||||||
private var characteristicNotifyFutures = [CharacteristicNotifyFuture]()
|
private var characteristicNotifyFutures = [CharacteristicNotifyFuture]()
|
||||||
private var discoverServicesFutures = [DiscoverServicesFuture]()
|
private var discoverServicesFutures = [DiscoverServicesFuture]()
|
||||||
|
|
||||||
@@ -263,11 +264,12 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
|||||||
}
|
}
|
||||||
peripheral.writeValue(value.data, for: gattCharacteristic, type: type)
|
peripheral.writeValue(value.data, for: gattCharacteristic, type: type)
|
||||||
|
|
||||||
|
// Wait for future response
|
||||||
|
let future = CharacteristicWriteFuture(deviceId: deviceId, characteristicId: gattCharacteristic.uuid.uuidStr, serviceId: gattCharacteristic.service?.uuid.uuidStr, result: completion)
|
||||||
if type == CBCharacteristicWriteType.withResponse {
|
if type == CBCharacteristicWriteType.withResponse {
|
||||||
// Wait for future response
|
characteristicWriteFutures.append(future)
|
||||||
characteristicWriteFutures.append(CharacteristicWriteFuture(deviceId: deviceId, characteristicId: gattCharacteristic.uuid.uuidStr, serviceId: gattCharacteristic.service?.uuid.uuidStr, result: completion))
|
|
||||||
} else {
|
} else {
|
||||||
completion(Result.success({}()))
|
characteristicWriteWithoutResponseFutures.append(future)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,6 +414,16 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
|
||||||
|
characteristicWriteWithoutResponseFutures.removeAll { future in
|
||||||
|
if future.deviceId == peripheral.uuid.uuidString {
|
||||||
|
future.result(Result.success({}()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
|
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||||
characteristicWriteFutures.removeAll { future in
|
characteristicWriteFutures.removeAll { future in
|
||||||
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
|
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _writeValue() async {
|
Future<void> _writeValue({required bool withResponse}) async {
|
||||||
BleCharacteristic? selectedCharacteristic = this.selectedCharacteristic;
|
BleCharacteristic? selectedCharacteristic = this.selectedCharacteristic;
|
||||||
if (selectedCharacteristic == null ||
|
if (selectedCharacteristic == null ||
|
||||||
!valueFormKey.currentState!.validate() ||
|
!valueFormKey.currentState!.validate() ||
|
||||||
@@ -146,13 +146,8 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await selectedCharacteristic.write(
|
await selectedCharacteristic.write(value, withResponse: withResponse);
|
||||||
value,
|
_addLog('Write${withResponse ? "" : "WithoutResponse"}', value);
|
||||||
withResponse: _hasSelectedCharacteristicProperty(
|
|
||||||
[CharacteristicProperty.writeWithoutResponse],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_addLog('Write', value);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint(e.toString());
|
debugPrint(e.toString());
|
||||||
_addLog('WriteError', e);
|
_addLog('WriteError', e);
|
||||||
@@ -377,11 +372,19 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
|||||||
discoveredServices.isNotEmpty &&
|
discoveredServices.isNotEmpty &&
|
||||||
_hasSelectedCharacteristicProperty([
|
_hasSelectedCharacteristicProperty([
|
||||||
CharacteristicProperty.write,
|
CharacteristicProperty.write,
|
||||||
CharacteristicProperty.writeWithoutResponse
|
|
||||||
]),
|
]),
|
||||||
onPressed: _writeValue,
|
onPressed: () => _writeValue(withResponse: true),
|
||||||
text: 'Write',
|
text: 'Write',
|
||||||
),
|
),
|
||||||
|
PlatformButton(
|
||||||
|
enabled: isConnected &&
|
||||||
|
discoveredServices.isNotEmpty &&
|
||||||
|
_hasSelectedCharacteristicProperty([
|
||||||
|
CharacteristicProperty.writeWithoutResponse,
|
||||||
|
]),
|
||||||
|
onPressed: () => _writeValue(withResponse: false),
|
||||||
|
text: 'WriteWithoutResponse',
|
||||||
|
),
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
enabled: isConnected &&
|
enabled: isConnected &&
|
||||||
discoveredServices.isNotEmpty &&
|
discoveredServices.isNotEmpty &&
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
device_info_plus: b0fafc687fb901e2af612763340f1b0d4352f8e5
|
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
universal_ble: ff19787898040d721109c6324472e5dd4bc86adc
|
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
||||||
|
|
||||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
name: universal_ble
|
name: universal_ble
|
||||||
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
|
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
|
||||||
version: 0.20.0
|
version: 0.20.1
|
||||||
homepage: https://navideck.com
|
homepage: https://navideck.com
|
||||||
repository: https://github.com/Navideck/universal_ble
|
repository: https://github.com/Navideck/universal_ble
|
||||||
issue_tracker: https://github.com/Navideck/universal_ble/issues
|
issue_tracker: https://github.com/Navideck/universal_ble/issues
|
||||||
|
|||||||
Reference in New Issue
Block a user