Implement characteristic descriptor discovery (#196)
* Implement characteristic descriptor discovery * Improve Darwin services discovery * Minor mac fix * Implement Windows support * discover descriptors in DiscoverServicesAsync api on windows * Add withDescriptors in discoverServices api * Implement withDescriptors for Apple and Android * Fix ai comment and format pigeon generated file * Resolve Ai comment * Capitalize API * Handle empty services scenario --------- Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
@@ -78,9 +78,11 @@ extension BleDeviceExtension on BleDevice {
|
||||
/// Returns cached services if already discovered after connection.
|
||||
Future<List<BleService>> discoverServices({
|
||||
Duration? timeout,
|
||||
bool withDescriptors = false,
|
||||
}) async {
|
||||
List<BleService> servicesCache = await UniversalBle.discoverServices(
|
||||
deviceId,
|
||||
withDescriptors: withDescriptors,
|
||||
timeout: timeout,
|
||||
);
|
||||
CacheHandler.instance.saveServices(deviceId, servicesCache);
|
||||
|
||||
@@ -18,11 +18,13 @@ class BleService {
|
||||
class BleCharacteristic {
|
||||
String uuid;
|
||||
List<CharacteristicProperty> properties;
|
||||
List<BleDescriptor> descriptors;
|
||||
({String deviceId, String serviceId})? metaData;
|
||||
|
||||
BleCharacteristic(
|
||||
String uuid,
|
||||
this.properties,
|
||||
this.descriptors,
|
||||
) : uuid = BleUuidParser.string(uuid);
|
||||
|
||||
BleCharacteristic.withMetaData({
|
||||
@@ -30,6 +32,7 @@ class BleCharacteristic {
|
||||
required String serviceId,
|
||||
required String uuid,
|
||||
required this.properties,
|
||||
required this.descriptors,
|
||||
}) : uuid = BleUuidParser.string(uuid),
|
||||
metaData = (
|
||||
deviceId: deviceId,
|
||||
@@ -55,6 +58,23 @@ class BleCharacteristic {
|
||||
int get hashCode => uuid.hashCode ^ properties.hashCode ^ metaData.hashCode;
|
||||
}
|
||||
|
||||
class BleDescriptor {
|
||||
String uuid;
|
||||
BleDescriptor(String uuid) : uuid = BleUuidParser.string(uuid);
|
||||
|
||||
@override
|
||||
String toString() => 'BleDescriptor{uuid: $uuid}';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is! BleDescriptor) return false;
|
||||
return other.uuid == uuid;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => uuid.hashCode;
|
||||
}
|
||||
|
||||
enum CharacteristicProperty {
|
||||
broadcast,
|
||||
read,
|
||||
|
||||
@@ -183,12 +183,14 @@ class UniversalBle {
|
||||
}
|
||||
|
||||
/// Discover services of a device.
|
||||
/// Set [withDescriptors] to `true` to discover characteristics with descriptors.
|
||||
static Future<List<BleService>> discoverServices(
|
||||
String deviceId, {
|
||||
bool withDescriptors = false,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
return await _bleCommandQueue.queueCommand(
|
||||
() => _platform.discoverServices(deviceId),
|
||||
() => _platform.discoverServices(deviceId, withDescriptors),
|
||||
timeout: timeout,
|
||||
deviceId: deviceId,
|
||||
);
|
||||
|
||||
@@ -160,7 +160,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<BleService>> discoverServices(String deviceId) async {
|
||||
Future<List<BleService>> discoverServices(
|
||||
String deviceId,
|
||||
bool withDescriptors,
|
||||
) async {
|
||||
final device = _findDeviceById(deviceId);
|
||||
if (device.gattServices.isEmpty && !device.servicesResolved) {
|
||||
await device.propertiesChanged.firstWhere((element) {
|
||||
@@ -205,6 +208,11 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
serviceId: serviceId,
|
||||
uuid: e.uuid.toString(),
|
||||
properties: properties,
|
||||
descriptors: withDescriptors
|
||||
? e.descriptors
|
||||
.map((e) => BleDescriptor(e.uuid.toString()))
|
||||
.toList()
|
||||
: [],
|
||||
);
|
||||
}).toList();
|
||||
services.add(
|
||||
|
||||
@@ -224,16 +224,20 @@ class UniversalBleCharacteristic {
|
||||
UniversalBleCharacteristic({
|
||||
required this.uuid,
|
||||
required this.properties,
|
||||
required this.descriptors,
|
||||
});
|
||||
|
||||
String uuid;
|
||||
|
||||
List<int> properties;
|
||||
|
||||
List<UniversalBleDescriptor> descriptors;
|
||||
|
||||
List<Object?> _toList() {
|
||||
return <Object?>[
|
||||
uuid,
|
||||
properties,
|
||||
descriptors,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -246,6 +250,8 @@ class UniversalBleCharacteristic {
|
||||
return UniversalBleCharacteristic(
|
||||
uuid: result[0]! as String,
|
||||
properties: (result[1] as List<Object?>?)!.cast<int>(),
|
||||
descriptors:
|
||||
(result[2] as List<Object?>?)!.cast<UniversalBleDescriptor>(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -267,6 +273,47 @@ class UniversalBleCharacteristic {
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
}
|
||||
|
||||
class UniversalBleDescriptor {
|
||||
UniversalBleDescriptor({
|
||||
required this.uuid,
|
||||
});
|
||||
|
||||
String uuid;
|
||||
|
||||
List<Object?> _toList() {
|
||||
return <Object?>[
|
||||
uuid,
|
||||
];
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
|
||||
static UniversalBleDescriptor decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return UniversalBleDescriptor(
|
||||
uuid: result[0]! as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
bool operator ==(Object other) {
|
||||
if (other is! UniversalBleDescriptor || other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return _deepEquals(encode(), other.encode());
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
}
|
||||
|
||||
/// Scan Filters
|
||||
class UniversalScanFilter {
|
||||
UniversalScanFilter({
|
||||
@@ -438,15 +485,18 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
buffer.putUint8(132);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalScanFilter) {
|
||||
} else if (value is UniversalBleDescriptor) {
|
||||
buffer.putUint8(133);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
} else if (value is UniversalScanFilter) {
|
||||
buffer.putUint8(134);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
buffer.putUint8(135);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
buffer.putUint8(136);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
@@ -465,10 +515,12 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
case 132:
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
case 133:
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
return UniversalBleDescriptor.decode(readValue(buffer)!);
|
||||
case 134:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
case 135:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
case 136:
|
||||
return UniversalManufacturerData.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
@@ -768,7 +820,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<UniversalBleService>> discoverServices(String deviceId) async {
|
||||
Future<List<UniversalBleService>> discoverServices(
|
||||
String deviceId, bool withDescriptors) async {
|
||||
final String pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$pigeonVar_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> pigeonVar_channel =
|
||||
@@ -778,7 +831,7 @@ class UniversalBlePlatformChannel {
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture =
|
||||
pigeonVar_channel.send(<Object?>[deviceId]);
|
||||
pigeonVar_channel.send(<Object?>[deviceId, withDescriptors]);
|
||||
final List<Object?>? pigeonVar_replyList =
|
||||
await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
|
||||
@@ -77,10 +77,13 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
_executeWithErrorHandling(() => _channel.disconnect(deviceId));
|
||||
|
||||
@override
|
||||
Future<List<BleService>> discoverServices(String deviceId) async {
|
||||
Future<List<BleService>> discoverServices(
|
||||
String deviceId,
|
||||
bool withDescriptors,
|
||||
) async {
|
||||
List<UniversalBleService?> universalBleServices =
|
||||
await _executeWithErrorHandling(
|
||||
() => _channel.discoverServices(deviceId));
|
||||
() => _channel.discoverServices(deviceId, withDescriptors));
|
||||
return List<BleService>.from(universalBleServices
|
||||
.where((e) => e != null)
|
||||
.map((e) => e!.toBleService(deviceId))
|
||||
@@ -210,6 +213,8 @@ extension _BleServiceExtension on UniversalBleService {
|
||||
deviceId: deviceId,
|
||||
serviceId: uuid,
|
||||
uuid: characteristic.uuid,
|
||||
descriptors: List<BleDescriptor>.from(
|
||||
characteristic.descriptors.map((e) => BleDescriptor(e.uuid))),
|
||||
properties: List<CharacteristicProperty>.from(
|
||||
properties.map((e) => CharacteristicProperty.parse(e ?? 1)),
|
||||
),
|
||||
|
||||
@@ -51,7 +51,8 @@ abstract class UniversalBlePlatform {
|
||||
|
||||
Future<void> disconnect(String deviceId);
|
||||
|
||||
Future<List<BleService>> discoverServices(String deviceId);
|
||||
Future<List<BleService>> discoverServices(
|
||||
String deviceId, bool withDescriptors);
|
||||
|
||||
Future<void> setNotifiable(String deviceId, String service,
|
||||
String characteristic, BleInputProperty bleInputProperty);
|
||||
|
||||
@@ -62,10 +62,14 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<BleService>> discoverServices(String deviceId) async =>
|
||||
(await _getServices(deviceId))
|
||||
.map((e) => e._bleService(deviceId))
|
||||
.toList();
|
||||
Future<List<BleService>> discoverServices(
|
||||
String deviceId, bool withDescriptors) async {
|
||||
List<BleService> services = [];
|
||||
for (var service in await _getServices(deviceId)) {
|
||||
services.add(await service._toBleService(deviceId, withDescriptors));
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AvailabilityState> getBluetoothAvailabilityState() async {
|
||||
@@ -536,24 +540,40 @@ class _UniversalWebBluetoothService {
|
||||
return null;
|
||||
}
|
||||
|
||||
BleService _bleService(String deviceId) => BleService(
|
||||
service.uuid,
|
||||
characteristics.map((e) {
|
||||
return BleCharacteristic.withMetaData(
|
||||
deviceId: deviceId,
|
||||
serviceId: service.uuid,
|
||||
uuid: e.uuid,
|
||||
properties: [
|
||||
if (e.properties.broadcast) CharacteristicProperty.broadcast,
|
||||
if (e.properties.read) CharacteristicProperty.read,
|
||||
if (e.properties.write) CharacteristicProperty.write,
|
||||
if (e.properties.writeWithoutResponse)
|
||||
CharacteristicProperty.writeWithoutResponse,
|
||||
if (e.properties.notify) CharacteristicProperty.notify,
|
||||
if (e.properties.indicate) CharacteristicProperty.indicate,
|
||||
if (e.properties.authenticatedSignedWrites)
|
||||
CharacteristicProperty.authenticatedSignedWrites,
|
||||
]);
|
||||
}).toList(),
|
||||
);
|
||||
Future<BleService> _toBleService(
|
||||
String deviceId,
|
||||
bool withDescriptors,
|
||||
) async {
|
||||
List<BleCharacteristic> bleCharacteristics = [];
|
||||
for (var characteristic in characteristics) {
|
||||
List<BleDescriptor> descriptors = [];
|
||||
if (withDescriptors) {
|
||||
try {
|
||||
var bluetoothDescriptors = await characteristic.getDescriptors();
|
||||
descriptors =
|
||||
bluetoothDescriptors.map((e) => BleDescriptor(e.uuid)).toList();
|
||||
} catch (_) {}
|
||||
}
|
||||
bleCharacteristics.add(BleCharacteristic.withMetaData(
|
||||
deviceId: deviceId,
|
||||
serviceId: service.uuid,
|
||||
uuid: characteristic.uuid,
|
||||
properties: [
|
||||
if (characteristic.properties.broadcast)
|
||||
CharacteristicProperty.broadcast,
|
||||
if (characteristic.properties.read) CharacteristicProperty.read,
|
||||
if (characteristic.properties.write) CharacteristicProperty.write,
|
||||
if (characteristic.properties.writeWithoutResponse)
|
||||
CharacteristicProperty.writeWithoutResponse,
|
||||
if (characteristic.properties.notify) CharacteristicProperty.notify,
|
||||
if (characteristic.properties.indicate)
|
||||
CharacteristicProperty.indicate,
|
||||
if (characteristic.properties.authenticatedSignedWrites)
|
||||
CharacteristicProperty.authenticatedSignedWrites,
|
||||
],
|
||||
descriptors: descriptors,
|
||||
));
|
||||
}
|
||||
return BleService(service.uuid, bleCharacteristics);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user