Improve web support (#66)

* Improve web support

* Rename to _hasSelectedCharacteristicProperty

* Update comment

* Make _bleService private

---------

Co-authored-by: Foti Dim <fdimanidis@gmail.com>
This commit is contained in:
Rohit Sangwan
2024-07-31 12:58:50 +05:30
committed by GitHub
parent 28b0bc6b7c
commit 5ce6c3d362
2 changed files with 90 additions and 66 deletions
@@ -152,7 +152,10 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
selectedCharacteristic!.service.uuid,
selectedCharacteristic!.characteristic.uuid,
value,
BleOutputProperty.withResponse,
_hasSelectedCharacteristicProperty(
[CharacteristicProperty.writeWithoutResponse])
? BleOutputProperty.withoutResponse
: BleOutputProperty.withResponse,
);
_addLog('Write', value);
} catch (e) {
@@ -187,17 +190,6 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
}
}
bool isValidProperty(List<CharacteristicProperty> properties) {
for (CharacteristicProperty property in properties) {
if (selectedCharacteristic?.characteristic.properties
.contains(property) ??
false) {
return true;
}
}
return false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -307,7 +299,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
),
),
if (isValidProperty([
if (_hasSelectedCharacteristicProperty([
CharacteristicProperty.write,
CharacteristicProperty.writeWithoutResponse
]))
@@ -375,7 +367,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
PlatformButton(
enabled: isConnected &&
discoveredServices.isNotEmpty &&
isValidProperty([
_hasSelectedCharacteristicProperty([
CharacteristicProperty.read,
]),
onPressed: _readValue,
@@ -384,7 +376,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
PlatformButton(
enabled: isConnected &&
discoveredServices.isNotEmpty &&
isValidProperty([
_hasSelectedCharacteristicProperty([
CharacteristicProperty.write,
CharacteristicProperty.writeWithoutResponse
]),
@@ -394,7 +386,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
PlatformButton(
enabled: isConnected &&
discoveredServices.isNotEmpty &&
isValidProperty([
_hasSelectedCharacteristicProperty([
CharacteristicProperty.notify,
CharacteristicProperty.indicate
]),
@@ -405,7 +397,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
PlatformButton(
enabled: isConnected &&
discoveredServices.isNotEmpty &&
isValidProperty([
_hasSelectedCharacteristicProperty([
CharacteristicProperty.notify,
CharacteristicProperty.indicate
]),
@@ -476,4 +468,11 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
}),
);
}
bool _hasSelectedCharacteristicProperty(
List<CharacteristicProperty> properties) =>
properties.any((property) =>
selectedCharacteristic?.characteristic.properties
.contains(property) ??
false);
}
@@ -18,6 +18,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
final Map<String, StreamSubscription> _deviceAdvertisementStreamList = {};
final Map<String, StreamSubscription> _connectedDeviceStreamList = {};
final Map<String, StreamSubscription> _characteristicStreamList = {};
final Map<String, List<_UniversalWebBluetoothService>> _serviceCache = {};
@override
Future<BleConnectionState> getConnectionState(String deviceId) async {
@@ -56,47 +57,8 @@ class UniversalBleWeb extends UniversalBlePlatform {
}
@override
Future<List<BleService>> discoverServices(String deviceId) async {
final device = _getDeviceById(deviceId);
final services = await device?.discoverServices();
if (services == null) return [];
var discoveredServices = <BleService>[];
for (var service in services) {
final characteristics = await service.getCharacteristics();
List<BleCharacteristic> bleCharacteristics = [];
for (BluetoothCharacteristic characteristic in characteristics) {
List<CharacteristicProperty> properties = [];
if (characteristic.properties.broadcast) {
properties.add(CharacteristicProperty.broadcast);
}
if (characteristic.properties.read) {
properties.add(CharacteristicProperty.read);
}
if (characteristic.properties.writeWithoutResponse) {
properties.add(CharacteristicProperty.writeWithoutResponse);
}
if (characteristic.properties.write) {
properties.add(CharacteristicProperty.write);
}
if (characteristic.properties.notify) {
properties.add(CharacteristicProperty.notify);
}
if (characteristic.properties.indicate) {
properties.add(CharacteristicProperty.indicate);
}
if (characteristic.properties.authenticatedSignedWrites) {
properties.add(CharacteristicProperty.authenticatedSignedWrites);
}
bleCharacteristics.add(
BleCharacteristic(characteristic.uuid.toString(), properties),
);
}
discoveredServices.add(
BleService(service.uuid.toString(), bleCharacteristics),
);
}
return discoveredServices;
}
Future<List<BleService>> discoverServices(String deviceId) async =>
(await _getServices(deviceId)).map((e) => e._bleService).toList();
@override
Future<AvailabilityState> getBluetoothAvailabilityState() async {
@@ -118,6 +80,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
ScanFilter? scanFilter,
PlatformConfig? platformConfig,
}) async {
FlutterWebBluetooth.instance.isAvailable;
BluetoothDevice device = await FlutterWebBluetooth.instance.requestDevice(
_getRequestOptionBuilder(scanFilter, platformConfig?.web),
);
@@ -322,6 +285,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
return key.contains(deviceId);
});
_disposeAdvertisementWatcher(deviceId);
_serviceCache.remove(deviceId);
// _bluetoothDeviceList.removeWhere((element) => element.id == deviceId);
}
@@ -330,20 +294,33 @@ class UniversalBleWeb extends UniversalBlePlatform {
required String serviceId,
required String characteristicId,
}) async {
final device = _getDeviceById(deviceId);
List<BluetoothService> services = await device?.discoverServices() ?? [];
BluetoothService? service;
for (BluetoothService bleService in services) {
if (bleService.uuid.toString() == serviceId.toString()) {
service = bleService;
break;
for (var service in await _getServices(deviceId)) {
if (BleUuidParser.compareStrings(service.uuid, serviceId)) {
return service.getCharacteristic(characteristicId);
}
}
return await service?.getCharacteristic(characteristicId.toString());
return null;
}
BluetoothDevice? _getDeviceById(String id) => _bluetoothDeviceList[id];
/// Get services and their characteristics.
/// Services and characteristics are cached.
/// Clears cache on disconnection.
Future<List<_UniversalWebBluetoothService>> _getServices(
String deviceId,
) async {
BluetoothDevice? device = _getDeviceById(deviceId);
if (device == null) return [];
var services = _serviceCache[deviceId] ?? [];
if (services.isNotEmpty) return services;
for (var service in await device.discoverServices()) {
services.add(await _UniversalWebBluetoothService.fromService(service));
}
_serviceCache[deviceId] = services;
return services;
}
void _disposeAdvertisementWatcher([String? deviceId]) {
_deviceAdvertisementStreamList.removeWhere((key, value) {
if (deviceId != null && key != deviceId) return false;
@@ -463,3 +440,51 @@ extension _UnmodifiableMapViewExtension on UnmodifiableMapView<int, ByteData> {
return Uint8List.fromList(bytes + manufacturerDataValue);
}
}
class _UniversalWebBluetoothService {
late String uuid;
BluetoothService service;
List<BluetoothCharacteristic> characteristics;
_UniversalWebBluetoothService({
required this.service,
required this.characteristics,
}) {
uuid = service.uuid;
}
static Future<_UniversalWebBluetoothService> fromService(
BluetoothService service,
) async {
return _UniversalWebBluetoothService(
service: service,
characteristics: await service.getCharacteristics(),
);
}
BluetoothCharacteristic? getCharacteristic(String characteristicId) {
for (var characteristic in characteristics) {
if (BleUuidParser.compareStrings(characteristic.uuid, characteristicId)) {
return characteristic;
}
}
return null;
}
BleService get _bleService => BleService(
service.uuid,
characteristics.map((e) {
return BleCharacteristic(e.uuid, [
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(),
);
}