Improve scan filter api (#78)
* Improve scan filter api * Implement Windows filters * Add manufacturerDataList and deprecate manufacturerData * Fix Windows * Update Readme and changelog * Remove import * Fix Linux * Update readme and changelog * Cleanup Todo * Improve ManufacturerData * Rename webConfig and Manufacturer.data * Return false for receivesAdvertisements on Linux/Web * Add delay in discoverServices on Linux * Remove import * Update changelog --------- Co-authored-by: fotidim <foti@navideck.com>
This commit is contained in:
@@ -9,8 +9,12 @@ class BleDevice {
|
||||
bool? isPaired;
|
||||
List<String> services;
|
||||
bool? isSystemDevice;
|
||||
Uint8List? manufacturerDataHead;
|
||||
Uint8List? manufacturerData;
|
||||
List<ManufacturerData> manufacturerDataList;
|
||||
|
||||
@Deprecated("Use `manufacturerDataList` instead")
|
||||
Uint8List? get manufacturerData => manufacturerDataList.isEmpty
|
||||
? null
|
||||
: manufacturerDataList.first.toUint8List();
|
||||
|
||||
/// Returns connection state of the device.
|
||||
/// All platforms will return `Connected/Disconnected` states.
|
||||
@@ -30,12 +34,8 @@ class BleDevice {
|
||||
this.isPaired,
|
||||
this.services = const [],
|
||||
this.isSystemDevice,
|
||||
Uint8List? manufacturerData,
|
||||
Uint8List? manufacturerDataHead,
|
||||
}) {
|
||||
this.manufacturerDataHead = manufacturerDataHead ?? Uint8List.fromList([]);
|
||||
this.manufacturerData = manufacturerData ?? manufacturerDataHead;
|
||||
}
|
||||
this.manufacturerDataList = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -46,34 +46,6 @@ class BleDevice {
|
||||
'isPaired: $isPaired, '
|
||||
'services: $services, '
|
||||
'isSystemDevice: $isSystemDevice, '
|
||||
'manufacturerDataHead: $manufacturerDataHead, '
|
||||
'manufacturerData: $manufacturerData';
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the manufacturer data of a BLE device.
|
||||
/// Use [BleDevice.manufacturerData] with [ManufacturerData.fromData] to create an instance of this class.
|
||||
class ManufacturerData {
|
||||
final int? companyId;
|
||||
final Uint8List? data;
|
||||
String? companyIdRadix16;
|
||||
ManufacturerData(this.companyId, this.data) {
|
||||
if (companyId != null) {
|
||||
companyIdRadix16 = "0x0${companyId!.toRadixString(16)}";
|
||||
}
|
||||
}
|
||||
|
||||
factory ManufacturerData.fromData(Uint8List data) {
|
||||
if (data.length < 2) return ManufacturerData(null, data);
|
||||
int manufacturerIdInt = (data[0] + (data[1] << 8));
|
||||
return ManufacturerData(
|
||||
manufacturerIdInt,
|
||||
data.sublist(2),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ManufacturerData: companyId: $companyIdRadix16, data: $data';
|
||||
'manufacturerDataList: $manufacturerDataList';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Represents the manufacturer data of a BLE device.
|
||||
class ManufacturerData {
|
||||
final int companyId;
|
||||
final Uint8List payload;
|
||||
ManufacturerData(this.companyId, this.payload);
|
||||
|
||||
String get companyIdRadix16 => "0x0${companyId.toRadixString(16)}";
|
||||
|
||||
factory ManufacturerData.fromData(Uint8List data) {
|
||||
if (data.length < 2) {
|
||||
throw const FormatException("Invalid Manufacturer Data");
|
||||
}
|
||||
return ManufacturerData(
|
||||
(data[0] + (data[1] << 8)),
|
||||
data.sublist(2),
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List toUint8List() {
|
||||
final byteData = ByteData(2);
|
||||
byteData.setInt16(0, companyId, Endian.host);
|
||||
return Uint8List.fromList(
|
||||
byteData.buffer.asUint8List() + payload.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => companyId.hashCode ^ payload.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ManufacturerData) return false;
|
||||
return companyId == other.companyId && listEquals(payload, other.payload);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Manufacturer: $companyIdRadix16 - $payload';
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'package:universal_ble/src/models/manufacturer_data.dart';
|
||||
export 'package:universal_ble/src/models/platform_config.dart';
|
||||
export 'package:universal_ble/src/models/queue_type.dart';
|
||||
export 'package:universal_ble/src/models/ble_uuid_parser.dart';
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/// Platform specific config to scan devices
|
||||
class PlatformConfig {
|
||||
WebConfig? web;
|
||||
WebOptions? web;
|
||||
|
||||
PlatformConfig({this.web});
|
||||
}
|
||||
|
||||
/// Web config to scan devices
|
||||
/// Web options to scan devices
|
||||
/// [optionalServices] is a list of service uuid's to ensure that you can access the specified services after connecting to the device,
|
||||
/// by default services from scanFilter will be used
|
||||
/// [optionalManufacturerData] is list of `CompanyIdentifier's` and used to add `ManufacturerData` in advertisement results of selected device from web dialog,
|
||||
/// by default manufacturerData from scanFilter will be used
|
||||
/// Checkout more details on [web](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice)
|
||||
/// Note: you will only get advertisements if Experimental Flag is enabled in the browser
|
||||
class WebConfig {
|
||||
class WebOptions {
|
||||
List<String> optionalServices;
|
||||
List<int> optionalManufacturerData;
|
||||
|
||||
WebConfig({
|
||||
WebOptions({
|
||||
this.optionalServices = const [],
|
||||
this.optionalManufacturerData = const [],
|
||||
});
|
||||
|
||||
@@ -18,10 +18,10 @@ class ScanFilter {
|
||||
}
|
||||
|
||||
class ManufacturerDataFilter {
|
||||
int? companyIdentifier;
|
||||
int companyIdentifier;
|
||||
|
||||
// Mask and data must be of same length
|
||||
Uint8List? data;
|
||||
Uint8List? payload;
|
||||
|
||||
/// For any bit in the mask, set it the 1 if it needs to match
|
||||
/// the one in manufacturer data, otherwise set it to 0.
|
||||
@@ -29,13 +29,13 @@ class ManufacturerDataFilter {
|
||||
Uint8List? mask;
|
||||
|
||||
ManufacturerDataFilter({
|
||||
this.companyIdentifier,
|
||||
this.data,
|
||||
required this.companyIdentifier,
|
||||
this.payload,
|
||||
this.mask,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ManufacturerDataFilter(companyIdentifier: $companyIdentifier, data: $data, mask: $mask)';
|
||||
return 'ManufacturerDataFilter(companyIdentifier: $companyIdentifier, payload: $payload, mask: $mask)';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
// A default Filter on dart side
|
||||
// Used on Linux only
|
||||
class UniversalBleFilterUtil {
|
||||
ScanFilter? scanFilter;
|
||||
|
||||
bool filterDevice(BleDevice device) {
|
||||
final filter = scanFilter;
|
||||
if (filter == null) return true;
|
||||
|
||||
final hasNamePrefixFilter = filter.withNamePrefix.isNotEmpty;
|
||||
final hasServiceFilter = filter.withServices.isNotEmpty;
|
||||
final hasManufacturerDataFilter = filter.withManufacturerData.isNotEmpty;
|
||||
|
||||
// If there is no filter at all, then allow device
|
||||
if (!hasNamePrefixFilter &&
|
||||
!hasServiceFilter &&
|
||||
!hasManufacturerDataFilter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Else check one of the filter passes
|
||||
return hasNamePrefixFilter && isNameMatchingFilters(filter, device) ||
|
||||
hasServiceFilter && isServicesMatchingFilters(filter, device) ||
|
||||
hasManufacturerDataFilter &&
|
||||
isManufacturerDataMatchingFilters(filter, device);
|
||||
}
|
||||
|
||||
bool isNameMatchingFilters(ScanFilter scanFilter, BleDevice device) {
|
||||
var namePrefixFilter = scanFilter.withNamePrefix;
|
||||
if (namePrefixFilter.isEmpty) return true;
|
||||
|
||||
String? name = device.name;
|
||||
if (name == null || name.isEmpty) return false;
|
||||
return namePrefixFilter.any(name.startsWith);
|
||||
}
|
||||
|
||||
bool isServicesMatchingFilters(ScanFilter scanFilter, BleDevice device) {
|
||||
var serviceFilters = scanFilter.withServices;
|
||||
if (serviceFilters.isEmpty) return true;
|
||||
|
||||
List<String> serviceUuids = device.services;
|
||||
if (serviceUuids.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return serviceFilters.any(serviceUuids.contains);
|
||||
}
|
||||
|
||||
bool isManufacturerDataMatchingFilters(
|
||||
ScanFilter scanFilter,
|
||||
BleDevice device,
|
||||
) {
|
||||
final filterMfdList = scanFilter.withManufacturerData;
|
||||
if (filterMfdList.isEmpty) return true;
|
||||
|
||||
List<ManufacturerData> manufacturerDataList = device.manufacturerDataList;
|
||||
if (manufacturerDataList.isEmpty) return false;
|
||||
|
||||
return manufacturerDataList.any((deviceMfd) => filterMfdList.any(
|
||||
(filterMfd) => _isManufacturerDataMatch(filterMfd, deviceMfd),
|
||||
));
|
||||
}
|
||||
|
||||
bool _isManufacturerDataMatch(
|
||||
ManufacturerDataFilter filterMfd,
|
||||
ManufacturerData deviceMfd,
|
||||
) {
|
||||
if (filterMfd.companyIdentifier != deviceMfd.companyId) return false;
|
||||
|
||||
Uint8List? filterPayload = filterMfd.payload;
|
||||
Uint8List devicePayload = deviceMfd.payload;
|
||||
|
||||
if (filterPayload == null || filterPayload.isEmpty) return true;
|
||||
if (devicePayload.isEmpty) return false;
|
||||
if (filterPayload.length > devicePayload.length) return false;
|
||||
|
||||
Uint8List? filterMask = filterMfd.mask;
|
||||
|
||||
if (filterMask != null && filterMask.length == filterPayload.length) {
|
||||
for (int i = 0; i < filterPayload.length; i++) {
|
||||
if ((filterPayload[i] & filterMask[i]) !=
|
||||
(devicePayload[i] & filterMask[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < filterPayload.length; i++) {
|
||||
if (filterPayload[i] != devicePayload[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluez/bluez.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:universal_ble/src/models/model_exports.dart';
|
||||
import 'package:universal_ble/src/universal_ble_filter_util.dart';
|
||||
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
|
||||
class UniversalBleLinux extends UniversalBlePlatform {
|
||||
@@ -14,12 +14,13 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
bool isInitialized = false;
|
||||
|
||||
final BlueZClient _client = BlueZClient();
|
||||
|
||||
late final UniversalBleFilterUtil _bleFilter = UniversalBleFilterUtil();
|
||||
BlueZAdapter? _activeAdapter;
|
||||
ScanFilter? _scanFilter;
|
||||
Completer<void>? _initializationCompleter;
|
||||
final Map<String, BlueZDevice> _devices = {};
|
||||
final Map<String, StreamSubscription> _deviceStreamSubscriptions = {};
|
||||
final Map<String, StreamSubscription> _deviceUpdateStreamSubscriptions = {};
|
||||
final Map<String, StreamSubscription> _deviceAdvertisementSubscriptions = {};
|
||||
|
||||
final Map<String, StreamSubscription> _characteristicPropertiesSubscriptions =
|
||||
{};
|
||||
|
||||
@@ -58,15 +59,44 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
PlatformConfig? platformConfig,
|
||||
}) async {
|
||||
await _ensureInitialized();
|
||||
await super.startScan(scanFilter: scanFilter);
|
||||
if (_activeAdapter?.discovering != true) {
|
||||
// Add services filter
|
||||
_activeAdapter?.setDiscoveryFilter(
|
||||
uuids: scanFilter?.withServices.toValidUUIDList(),
|
||||
);
|
||||
_scanFilter = scanFilter;
|
||||
await _activeAdapter?.startDiscovery();
|
||||
_client.devices.forEach(_onDeviceAdd);
|
||||
var adapter = _activeAdapter;
|
||||
if (adapter == null) {
|
||||
throw "Adapter not available";
|
||||
}
|
||||
|
||||
// Stop scan and clean all old advertisement listeners
|
||||
await stopScan();
|
||||
|
||||
bool hasCustomFilter = scanFilter?.hasCustomFilter() ?? false;
|
||||
List<String> withServicesFilter = [];
|
||||
|
||||
if (hasCustomFilter) {
|
||||
_bleFilter.scanFilter = scanFilter;
|
||||
} else {
|
||||
_bleFilter.scanFilter = null;
|
||||
withServicesFilter = scanFilter?.withServices.toValidUUIDList() ?? [];
|
||||
}
|
||||
|
||||
// Add services filter
|
||||
await adapter.setDiscoveryFilter(
|
||||
uuids: withServicesFilter,
|
||||
);
|
||||
|
||||
await _activeAdapter?.startDiscovery();
|
||||
|
||||
// Apply custom Services filter to these devices
|
||||
ScanFilter customServicesFilter = ScanFilter(
|
||||
withServices: withServicesFilter,
|
||||
);
|
||||
for (var device in _client.devices) {
|
||||
if (!hasCustomFilter && withServicesFilter.isNotEmpty) {
|
||||
if (_bleFilter.isServicesMatchingFilters(
|
||||
customServicesFilter, device.toBleDevice())) {
|
||||
_onDeviceAdd(device);
|
||||
}
|
||||
} else {
|
||||
_onDeviceAdd(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +107,11 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
if (_activeAdapter?.discovering == true) {
|
||||
await _activeAdapter?.stopDiscovery();
|
||||
}
|
||||
// Clean all advertiseemnt listeners
|
||||
_deviceAdvertisementSubscriptions.removeWhere((e, value) {
|
||||
value.cancel();
|
||||
return true;
|
||||
});
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
"stopScan error: $e",
|
||||
@@ -139,6 +174,9 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
});
|
||||
}
|
||||
|
||||
// Few ble devices requires delay to perform operations after discovering services
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
if (device.gattServices.isEmpty && !device.servicesResolved) {
|
||||
throw "Failed to resolve services";
|
||||
}
|
||||
@@ -413,40 +451,41 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
void _onDeviceAdd(BlueZDevice device) {
|
||||
if (!_isValidDevice(device)) return;
|
||||
|
||||
// Apply Filters
|
||||
if (_scanFilter != null) {
|
||||
List<ManufacturerDataFilter>? manufacturerDataFilter =
|
||||
_scanFilter?.withManufacturerData;
|
||||
if (manufacturerDataFilter != null &&
|
||||
manufacturerDataFilter.isNotEmpty) {}
|
||||
BleDevice bleDevice = device.toBleDevice();
|
||||
if (!_bleFilter.filterDevice(bleDevice)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update scan results only if rssi is available
|
||||
if (device.rssi != 0) updateScanResult(device.toBleDevice());
|
||||
if (device.rssi != 0) {
|
||||
updateScanResult(bleDevice);
|
||||
}
|
||||
|
||||
// Setup Cache
|
||||
_devices[device.address] = device;
|
||||
|
||||
// Setup update listener
|
||||
if (_deviceStreamSubscriptions[device.address] != null) {
|
||||
_deviceStreamSubscriptions[device.address]?.cancel();
|
||||
}
|
||||
// Setup advertisements Listener
|
||||
_deviceAdvertisementSubscriptions[device.address] ??= device
|
||||
.propertiesChanged
|
||||
.where((e) =>
|
||||
e.contains(BluezProperty.rssi) ||
|
||||
e.contains(BluezProperty.manufacturerData) ||
|
||||
e.contains(BluezProperty.uuids))
|
||||
.listen((_) {
|
||||
if (_bleFilter.filterDevice(bleDevice)) {
|
||||
updateScanResult(device.toBleDevice());
|
||||
}
|
||||
});
|
||||
|
||||
_deviceStreamSubscriptions[device.address] =
|
||||
// Setup update listener
|
||||
_deviceUpdateStreamSubscriptions[device.address] ??=
|
||||
device.propertiesChanged.listen((properties) {
|
||||
for (final property in properties) {
|
||||
switch (property) {
|
||||
case BluezProperty.rssi:
|
||||
updateScanResult(device.toBleDevice());
|
||||
break;
|
||||
// Connection/Pair updates
|
||||
case BluezProperty.connected:
|
||||
updateConnection(device.address, device.connected);
|
||||
break;
|
||||
case BluezProperty.manufacturerData:
|
||||
updateScanResult(device.toBleDevice());
|
||||
break;
|
||||
case BluezProperty.paired:
|
||||
updatePairingState(device.address, device.paired);
|
||||
break;
|
||||
@@ -458,95 +497,37 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
case BluezProperty.txPower:
|
||||
case BluezProperty.address:
|
||||
case BluezProperty.addressType:
|
||||
case BluezProperty.rssi:
|
||||
case BluezProperty.manufacturerData:
|
||||
break;
|
||||
default:
|
||||
UniversalBlePlatform.logInfo(
|
||||
"UnhandledDevicePropertyChanged ${device.name} ${device.address}: $property");
|
||||
"UnhandledDevicePropertyChanged ${device.name} ${device.address}: $property",
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _isValidDevice(BlueZDevice device) {
|
||||
ScanFilter? scanFilter = _scanFilter;
|
||||
if (scanFilter == null) return true;
|
||||
|
||||
// Check manufacturerData filter
|
||||
if (!_isValidManufacturerData(
|
||||
scanFilter.withManufacturerData,
|
||||
device.manufacturerDataFilter,
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _isValidManufacturerData(
|
||||
List<ManufacturerDataFilter> filterMfdList,
|
||||
List<ManufacturerDataFilter> deviceMfdList,
|
||||
) {
|
||||
if (filterMfdList.isEmpty) return true;
|
||||
if (deviceMfdList.isEmpty) return false;
|
||||
|
||||
// Check all filters
|
||||
for (final filterMfd in filterMfdList) {
|
||||
// Check if device have manufacturerData for this filter
|
||||
for (final deviceMfd in deviceMfdList) {
|
||||
// Check companyIdentifier
|
||||
if (filterMfd.companyIdentifier != deviceMfd.companyIdentifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check data
|
||||
Uint8List? filterData = filterMfd.data;
|
||||
Uint8List? deviceData = deviceMfd.data;
|
||||
|
||||
// If filter data is null and device data is not, continue to next deviceMfd
|
||||
if (filterData != null && deviceData == null) continue;
|
||||
|
||||
if (filterData == null || deviceData == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filterData.length > deviceData.length) continue;
|
||||
|
||||
// Apply mask
|
||||
Uint8List? filterMask = filterMfd.mask;
|
||||
bool dataMatched = true;
|
||||
if (filterMask != null && (filterMask.length == filterData.length)) {
|
||||
for (int i = 0; i < filterData.length; i++) {
|
||||
if ((filterData[i] & filterMask[i]) !=
|
||||
(deviceData[i] & filterMask[i])) {
|
||||
dataMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare data directly
|
||||
else {
|
||||
for (int i = 0; i < filterData.length; i++) {
|
||||
if (filterData[i] != deviceData[i]) {
|
||||
dataMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dataMatched) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void _onDeviceRemoved(BlueZDevice device) {
|
||||
_devices.remove(device.address);
|
||||
// Stop listener
|
||||
_deviceStreamSubscriptions[device.address]?.cancel();
|
||||
_deviceStreamSubscriptions
|
||||
.removeWhere((key, value) => key == device.address);
|
||||
// Clean Update listeners
|
||||
_deviceUpdateStreamSubscriptions.removeWhere((key, value) {
|
||||
if (key == device.address) {
|
||||
value.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Clean Advertisement listeners
|
||||
_deviceAdvertisementSubscriptions.removeWhere((key, value) {
|
||||
if (key == device.address) {
|
||||
value.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,62 +551,6 @@ class BluezProperty {
|
||||
static const String propertyClass = 'Class';
|
||||
}
|
||||
|
||||
extension BlueZDeviceExtension on BlueZDevice {
|
||||
List<ManufacturerDataFilter> get manufacturerDataFilter {
|
||||
try {
|
||||
if (manufacturerData.isEmpty) return [];
|
||||
return manufacturerData.entries
|
||||
.map((e) => ManufacturerDataFilter(
|
||||
companyIdentifier: e.key.id,
|
||||
data: Uint8List.fromList(e.value),
|
||||
))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
'Error parsing manufacturerData: $e',
|
||||
isError: true,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List get manufacturerDataHead {
|
||||
try {
|
||||
if (manufacturerData.isEmpty) return Uint8List(0);
|
||||
final sorted = manufacturerData.entries.toList()
|
||||
..sort((a, b) => a.key.id - b.key.id);
|
||||
int companyId = sorted.first.key.id;
|
||||
List<int> manufacturerDataValue = sorted.first.value;
|
||||
final byteData = ByteData(2);
|
||||
// TODO: Verify that this works regardless of the endianess
|
||||
byteData.setInt16(0, companyId, Endian.host);
|
||||
List<int> bytes = byteData.buffer.asUint8List();
|
||||
return Uint8List.fromList(bytes + manufacturerDataValue);
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
'Error parsing manufacturerData: $e',
|
||||
isError: true,
|
||||
);
|
||||
return Uint8List(0);
|
||||
}
|
||||
}
|
||||
|
||||
BleDevice toBleDevice({
|
||||
bool? isSystemDevice,
|
||||
}) {
|
||||
return BleDevice(
|
||||
name: alias,
|
||||
deviceId: address,
|
||||
isPaired: paired,
|
||||
manufacturerData: manufacturerDataHead,
|
||||
manufacturerDataHead: manufacturerDataHead,
|
||||
rssi: rssi,
|
||||
isSystemDevice: isSystemDevice,
|
||||
services: uuids.map((e) => e.toString()).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on BlueZGattCharacteristicFlag {
|
||||
CharacteristicProperty? toCharacteristicProperty() {
|
||||
return switch (this) {
|
||||
@@ -664,3 +589,28 @@ extension on BlueZFailedException {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on ScanFilter {
|
||||
bool hasCustomFilter() {
|
||||
return withNamePrefix.isNotEmpty || withManufacturerData.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
extension BlueZDeviceExtension on BlueZDevice {
|
||||
List<ManufacturerData> get manufacturerDataList => manufacturerData.entries
|
||||
.map((MapEntry<BlueZManufacturerId, List<int>> data) =>
|
||||
ManufacturerData(data.key.id, Uint8List.fromList(data.value)))
|
||||
.toList();
|
||||
|
||||
BleDevice toBleDevice({bool? isSystemDevice}) {
|
||||
return BleDevice(
|
||||
name: name,
|
||||
deviceId: address,
|
||||
isPaired: paired,
|
||||
rssi: rssi,
|
||||
isSystemDevice: isSystemDevice,
|
||||
services: uuids.map((e) => e.toString()).toList(),
|
||||
manufacturerDataList: manufacturerDataList,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ PlatformException _createConnectionError(String channelName) {
|
||||
);
|
||||
}
|
||||
|
||||
List<Object?> wrapResponse(
|
||||
{Object? result, PlatformException? error, bool empty = false}) {
|
||||
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
|
||||
if (empty) {
|
||||
return <Object?>[];
|
||||
}
|
||||
@@ -32,8 +31,7 @@ class UniversalBleScanResult {
|
||||
this.name,
|
||||
this.isPaired,
|
||||
this.rssi,
|
||||
this.manufacturerData,
|
||||
this.manufacturerDataHead,
|
||||
this.manufacturerDataList,
|
||||
this.services,
|
||||
});
|
||||
|
||||
@@ -45,9 +43,7 @@ class UniversalBleScanResult {
|
||||
|
||||
int? rssi;
|
||||
|
||||
Uint8List? manufacturerData;
|
||||
|
||||
Uint8List? manufacturerDataHead;
|
||||
List<UniversalManufacturerData?>? manufacturerDataList;
|
||||
|
||||
List<String?>? services;
|
||||
|
||||
@@ -57,8 +53,7 @@ class UniversalBleScanResult {
|
||||
name,
|
||||
isPaired,
|
||||
rssi,
|
||||
manufacturerData,
|
||||
manufacturerDataHead,
|
||||
manufacturerDataList,
|
||||
services,
|
||||
];
|
||||
}
|
||||
@@ -70,9 +65,8 @@ class UniversalBleScanResult {
|
||||
name: result[1] as String?,
|
||||
isPaired: result[2] as bool?,
|
||||
rssi: result[3] as int?,
|
||||
manufacturerData: result[4] as Uint8List?,
|
||||
manufacturerDataHead: result[5] as Uint8List?,
|
||||
services: (result[6] as List<Object?>?)?.cast<String?>(),
|
||||
manufacturerDataList: (result[4] as List<Object?>?)?.cast<UniversalManufacturerData?>(),
|
||||
services: (result[5] as List<Object?>?)?.cast<String?>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -98,8 +92,7 @@ class UniversalBleService {
|
||||
result as List<Object?>;
|
||||
return UniversalBleService(
|
||||
uuid: result[0]! as String,
|
||||
characteristics:
|
||||
(result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic?>(),
|
||||
characteristics: (result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic?>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -134,16 +127,20 @@ class UniversalBleCharacteristic {
|
||||
class UniversalScanFilter {
|
||||
UniversalScanFilter({
|
||||
required this.withServices,
|
||||
required this.withNamePrefix,
|
||||
required this.withManufacturerData,
|
||||
});
|
||||
|
||||
List<String?> withServices;
|
||||
|
||||
List<String?> withNamePrefix;
|
||||
|
||||
List<UniversalManufacturerDataFilter?> withManufacturerData;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
withServices,
|
||||
withNamePrefix,
|
||||
withManufacturerData,
|
||||
];
|
||||
}
|
||||
@@ -152,20 +149,20 @@ class UniversalScanFilter {
|
||||
result as List<Object?>;
|
||||
return UniversalScanFilter(
|
||||
withServices: (result[0] as List<Object?>?)!.cast<String?>(),
|
||||
withManufacturerData: (result[1] as List<Object?>?)!
|
||||
.cast<UniversalManufacturerDataFilter?>(),
|
||||
withNamePrefix: (result[1] as List<Object?>?)!.cast<String?>(),
|
||||
withManufacturerData: (result[2] as List<Object?>?)!.cast<UniversalManufacturerDataFilter?>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UniversalManufacturerDataFilter {
|
||||
UniversalManufacturerDataFilter({
|
||||
this.companyIdentifier,
|
||||
required this.companyIdentifier,
|
||||
this.data,
|
||||
this.mask,
|
||||
});
|
||||
|
||||
int? companyIdentifier;
|
||||
int companyIdentifier;
|
||||
|
||||
Uint8List? data;
|
||||
|
||||
@@ -182,13 +179,40 @@ class UniversalManufacturerDataFilter {
|
||||
static UniversalManufacturerDataFilter decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return UniversalManufacturerDataFilter(
|
||||
companyIdentifier: result[0] as int?,
|
||||
companyIdentifier: result[0]! as int,
|
||||
data: result[1] as Uint8List?,
|
||||
mask: result[2] as Uint8List?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UniversalManufacturerData {
|
||||
UniversalManufacturerData({
|
||||
required this.companyIdentifier,
|
||||
required this.data,
|
||||
});
|
||||
|
||||
int companyIdentifier;
|
||||
|
||||
Uint8List data;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
companyIdentifier,
|
||||
data,
|
||||
];
|
||||
}
|
||||
|
||||
static UniversalManufacturerData decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return UniversalManufacturerData(
|
||||
companyIdentifier: result[0]! as int,
|
||||
data: result[1]! as Uint8List,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _PigeonCodec extends StandardMessageCodec {
|
||||
const _PigeonCodec();
|
||||
@override
|
||||
@@ -196,18 +220,21 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
if (value is UniversalBleScanResult) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleService) {
|
||||
} else if (value is UniversalBleService) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
buffer.putUint8(131);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalScanFilter) {
|
||||
} else if (value is UniversalScanFilter) {
|
||||
buffer.putUint8(132);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
buffer.putUint8(133);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
buffer.putUint8(134);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
@@ -216,16 +243,18 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 129:
|
||||
case 129:
|
||||
return UniversalBleScanResult.decode(readValue(buffer)!);
|
||||
case 130:
|
||||
case 130:
|
||||
return UniversalBleService.decode(readValue(buffer)!);
|
||||
case 131:
|
||||
case 131:
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
case 132:
|
||||
case 132:
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
case 133:
|
||||
case 133:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
case 134:
|
||||
return UniversalManufacturerData.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
}
|
||||
@@ -237,11 +266,9 @@ class UniversalBlePlatformChannel {
|
||||
/// Constructor for [UniversalBlePlatformChannel]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
UniversalBlePlatformChannel(
|
||||
{BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
UniversalBlePlatformChannel({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: __pigeon_binaryMessenger = binaryMessenger,
|
||||
__pigeon_messageChannelSuffix =
|
||||
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
__pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? __pigeon_binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
@@ -249,10 +276,8 @@ class UniversalBlePlatformChannel {
|
||||
final String __pigeon_messageChannelSuffix;
|
||||
|
||||
Future<int> getBluetoothAvailabilityState() async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -278,10 +303,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<bool> enableBluetooth() async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -307,10 +330,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<void> startScan(UniversalScanFilter? filter) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -331,10 +352,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<void> stopScan() async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -355,10 +374,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<void> connect(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -379,10 +396,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<void> disconnect(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -402,19 +417,15 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setNotifiable(String deviceId, String service,
|
||||
String characteristic, int bleInputProperty) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
Future<void> setNotifiable(String deviceId, String service, String characteristic, int bleInputProperty) async {
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
);
|
||||
final List<Object?>? __pigeon_replyList = await __pigeon_channel.send(
|
||||
<Object?>[deviceId, service, characteristic, bleInputProperty])
|
||||
as List<Object?>?;
|
||||
final List<Object?>? __pigeon_replyList =
|
||||
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic, bleInputProperty]) as List<Object?>?;
|
||||
if (__pigeon_replyList == null) {
|
||||
throw _createConnectionError(__pigeon_channelName);
|
||||
} else if (__pigeon_replyList.length > 1) {
|
||||
@@ -429,10 +440,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<List<UniversalBleService?>> discoverServices(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -453,23 +462,19 @@ class UniversalBlePlatformChannel {
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (__pigeon_replyList[0] as List<Object?>?)!
|
||||
.cast<UniversalBleService?>();
|
||||
return (__pigeon_replyList[0] as List<Object?>?)!.cast<UniversalBleService?>();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List> readValue(
|
||||
String deviceId, String service, String characteristic) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
Future<Uint8List> readValue(String deviceId, String service, String characteristic) async {
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
);
|
||||
final List<Object?>? __pigeon_replyList = await __pigeon_channel
|
||||
.send(<Object?>[deviceId, service, characteristic]) as List<Object?>?;
|
||||
final List<Object?>? __pigeon_replyList =
|
||||
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic]) as List<Object?>?;
|
||||
if (__pigeon_replyList == null) {
|
||||
throw _createConnectionError(__pigeon_channelName);
|
||||
} else if (__pigeon_replyList.length > 1) {
|
||||
@@ -489,16 +494,14 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<int> requestMtu(String deviceId, int expectedMtu) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
);
|
||||
final List<Object?>? __pigeon_replyList = await __pigeon_channel
|
||||
.send(<Object?>[deviceId, expectedMtu]) as List<Object?>?;
|
||||
final List<Object?>? __pigeon_replyList =
|
||||
await __pigeon_channel.send(<Object?>[deviceId, expectedMtu]) as List<Object?>?;
|
||||
if (__pigeon_replyList == null) {
|
||||
throw _createConnectionError(__pigeon_channelName);
|
||||
} else if (__pigeon_replyList.length > 1) {
|
||||
@@ -517,24 +520,15 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> writeValue(String deviceId, String service,
|
||||
String characteristic, Uint8List value, int bleOutputProperty) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
Future<void> writeValue(String deviceId, String service, String characteristic, Uint8List value, int bleOutputProperty) async {
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
);
|
||||
final List<Object?>? __pigeon_replyList = await __pigeon_channel
|
||||
.send(<Object?>[
|
||||
deviceId,
|
||||
service,
|
||||
characteristic,
|
||||
value,
|
||||
bleOutputProperty
|
||||
]) as List<Object?>?;
|
||||
final List<Object?>? __pigeon_replyList =
|
||||
await __pigeon_channel.send(<Object?>[deviceId, service, characteristic, value, bleOutputProperty]) as List<Object?>?;
|
||||
if (__pigeon_replyList == null) {
|
||||
throw _createConnectionError(__pigeon_channelName);
|
||||
} else if (__pigeon_replyList.length > 1) {
|
||||
@@ -549,10 +543,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<bool> isPaired(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -578,10 +570,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<bool> pair(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -607,10 +597,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
|
||||
Future<void> unPair(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -630,12 +618,9 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<UniversalBleScanResult?>> getSystemDevices(
|
||||
List<String?> withServices) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
Future<List<UniversalBleScanResult?>> getSystemDevices(List<String?> withServices) async {
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -656,16 +641,13 @@ class UniversalBlePlatformChannel {
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (__pigeon_replyList[0] as List<Object?>?)!
|
||||
.cast<UniversalBleScanResult?>();
|
||||
return (__pigeon_replyList[0] as List<Object?>?)!.cast<UniversalBleScanResult?>();
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> getConnectionState(String deviceId) async {
|
||||
final String __pigeon_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel =
|
||||
BasicMessageChannel<Object?>(
|
||||
final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$__pigeon_messageChannelSuffix';
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
__pigeon_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: __pigeon_binaryMessenger,
|
||||
@@ -701,30 +683,22 @@ abstract class UniversalBleCallbackChannel {
|
||||
|
||||
void onScanResult(UniversalBleScanResult result);
|
||||
|
||||
void onValueChanged(
|
||||
String deviceId, String characteristicId, Uint8List value);
|
||||
void onValueChanged(String deviceId, String characteristicId, Uint8List value);
|
||||
|
||||
void onConnectionChanged(String deviceId, bool connected);
|
||||
|
||||
static void setUp(
|
||||
UniversalBleCallbackChannel? api, {
|
||||
BinaryMessenger? binaryMessenger,
|
||||
String messageChannelSuffix = '',
|
||||
}) {
|
||||
messageChannelSuffix =
|
||||
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
static void setUp(UniversalBleCallbackChannel? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
|
||||
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
{
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
__pigeon_channel.setMessageHandler(null);
|
||||
} else {
|
||||
__pigeon_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.');
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_state = (args[0] as int?);
|
||||
assert(arg_state != null,
|
||||
@@ -734,25 +708,22 @@ abstract class UniversalBleCallbackChannel {
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
__pigeon_channel.setMessageHandler(null);
|
||||
} else {
|
||||
__pigeon_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.');
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_deviceId = (args[0] as String?);
|
||||
assert(arg_deviceId != null,
|
||||
@@ -766,28 +737,24 @@ abstract class UniversalBleCallbackChannel {
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
__pigeon_channel.setMessageHandler(null);
|
||||
} else {
|
||||
__pigeon_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.');
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final UniversalBleScanResult? arg_result =
|
||||
(args[0] as UniversalBleScanResult?);
|
||||
final UniversalBleScanResult? arg_result = (args[0] as UniversalBleScanResult?);
|
||||
assert(arg_result != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null, expected non-null UniversalBleScanResult.');
|
||||
try {
|
||||
@@ -795,25 +762,22 @@ abstract class UniversalBleCallbackChannel {
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
__pigeon_channel.setMessageHandler(null);
|
||||
} else {
|
||||
__pigeon_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.');
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_deviceId = (args[0] as String?);
|
||||
assert(arg_deviceId != null,
|
||||
@@ -825,30 +789,26 @@ abstract class UniversalBleCallbackChannel {
|
||||
assert(arg_value != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null, expected non-null Uint8List.');
|
||||
try {
|
||||
api.onValueChanged(
|
||||
arg_deviceId!, arg_characteristicId!, arg_value!);
|
||||
api.onValueChanged(arg_deviceId!, arg_characteristicId!, arg_value!);
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix', pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
__pigeon_channel.setMessageHandler(null);
|
||||
} else {
|
||||
__pigeon_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.');
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_deviceId = (args[0] as String?);
|
||||
assert(arg_deviceId != null,
|
||||
@@ -861,9 +821,8 @@ abstract class UniversalBleCallbackChannel {
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
} catch (e) {
|
||||
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
ScanFilter? scanFilter,
|
||||
PlatformConfig? platformConfig,
|
||||
}) async {
|
||||
await super.startScan(scanFilter: scanFilter);
|
||||
await _channel.startScan(
|
||||
scanFilter.toUniversalScanFilter(),
|
||||
);
|
||||
@@ -194,22 +193,16 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
|
||||
}
|
||||
|
||||
extension _UniversalBleScanResultExtension on UniversalBleScanResult {
|
||||
BleDevice toBleDevice({
|
||||
bool? isSystemDevice,
|
||||
}) {
|
||||
var mnfDataHead = manufacturerDataHead ?? Uint8List.fromList([]);
|
||||
var mnfData = manufacturerData ?? mnfDataHead;
|
||||
BleDevice toBleDevice({bool? isSystemDevice}) {
|
||||
return BleDevice(
|
||||
name: name,
|
||||
deviceId: deviceId,
|
||||
manufacturerData: mnfData,
|
||||
manufacturerDataHead: mnfDataHead,
|
||||
rssi: rssi,
|
||||
isPaired: isPaired,
|
||||
isSystemDevice: isSystemDevice,
|
||||
services: services
|
||||
?.where((e) => e != null)
|
||||
.map((e) => BleUuidParser.string(e!))
|
||||
services: services?.nonNulls.map(BleUuidParser.string).toList() ?? [],
|
||||
manufacturerDataList: manufacturerDataList?.nonNulls
|
||||
.map((e) => ManufacturerData(e.companyIdentifier, e.data))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
@@ -222,7 +215,7 @@ extension _ScanFilterExtension on ScanFilter? {
|
||||
?.withManufacturerData
|
||||
.map((e) => UniversalManufacturerDataFilter(
|
||||
companyIdentifier: e.companyIdentifier,
|
||||
data: e.data,
|
||||
data: e.payload,
|
||||
mask: e.mask,
|
||||
))
|
||||
.toList();
|
||||
@@ -230,6 +223,7 @@ extension _ScanFilterExtension on ScanFilter? {
|
||||
// Windows crashes if it's null, so we need to pass empty scan filter in this case
|
||||
return UniversalScanFilter(
|
||||
withServices: this?.withServices.toValidUUIDList() ?? [],
|
||||
withNamePrefix: this?.withNamePrefix ?? [],
|
||||
withManufacturerData: manufacturerDataFilters ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'dart:typed_data';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
abstract class UniversalBlePlatform {
|
||||
ScanFilter? _scanFilter;
|
||||
StreamController? _connectionStreamController;
|
||||
final Map<String, bool> _pairStateMap = {};
|
||||
|
||||
@@ -15,9 +14,7 @@ abstract class UniversalBlePlatform {
|
||||
Future<void> startScan({
|
||||
ScanFilter? scanFilter,
|
||||
PlatformConfig? platformConfig,
|
||||
}) async {
|
||||
_scanFilter = scanFilter;
|
||||
}
|
||||
});
|
||||
|
||||
Future<void> stopScan();
|
||||
|
||||
@@ -68,13 +65,6 @@ abstract class UniversalBlePlatform {
|
||||
}
|
||||
|
||||
void updateScanResult(BleDevice bleDevice) {
|
||||
// Filter by name
|
||||
ScanFilter? scanFilter = _scanFilter;
|
||||
if (scanFilter != null && scanFilter.withNamePrefix.isNotEmpty) {
|
||||
if (bleDevice.name == null ||
|
||||
!scanFilter.withNamePrefix
|
||||
.any((e) => bleDevice.name?.startsWith(e) == true)) return;
|
||||
}
|
||||
onScanResult?.call(bleDevice);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
|
||||
import 'package:universal_ble/src/models/model_exports.dart';
|
||||
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
@@ -95,8 +95,14 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
@override
|
||||
bool receivesAdvertisements(String deviceId) =>
|
||||
_getDeviceById(deviceId)?.hasWatchAdvertisements() ?? false;
|
||||
bool receivesAdvertisements(String deviceId) {
|
||||
// Advertisements do not work on Linux/Web even with the "Experimental Web Platform features" flag enabled. Verified with Chrome Version 128.0.6613.138
|
||||
if (kIsWeb && defaultTargetPlatform == TargetPlatform.linux) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _getDeviceById(deviceId)?.hasWatchAdvertisements() ?? false;
|
||||
}
|
||||
|
||||
/// This will work only if `chrome://flags/#enable-experimental-web-platform-features` is enabled
|
||||
Future<void> _watchDeviceAdvertisements(BluetoothDevice device) async {
|
||||
@@ -342,22 +348,22 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
|
||||
RequestOptionsBuilder _getRequestOptionBuilder(
|
||||
ScanFilter? scanFilter,
|
||||
WebConfig? webConfig,
|
||||
WebOptions? webOptions,
|
||||
) {
|
||||
List<RequestFilterBuilder> filters = [];
|
||||
List<int> optionalManufacturerData = [];
|
||||
List<String> optionalServices = [];
|
||||
|
||||
if (webConfig != null) {
|
||||
optionalServices.addAll(webConfig.optionalServices.toValidUUIDList());
|
||||
optionalManufacturerData.addAll(webConfig.optionalManufacturerData);
|
||||
if (webOptions != null) {
|
||||
optionalServices.addAll(webOptions.optionalServices.toValidUUIDList());
|
||||
optionalManufacturerData.addAll(webOptions.optionalManufacturerData);
|
||||
}
|
||||
|
||||
if (scanFilter != null) {
|
||||
// Add services filter
|
||||
for (var service in scanFilter.withServices.toValidUUIDList()) {
|
||||
filters.add(RequestFilterBuilder(services: [service]));
|
||||
if (webConfig == null || webConfig.optionalServices.isEmpty) {
|
||||
if (webOptions == null || webOptions.optionalServices.isEmpty) {
|
||||
optionalServices.add(service);
|
||||
}
|
||||
}
|
||||
@@ -369,19 +375,16 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
manufacturerData: [
|
||||
ManufacturerDataFilterBuilder(
|
||||
companyIdentifier: manufacturerData.companyIdentifier,
|
||||
dataPrefix: manufacturerData.data,
|
||||
dataPrefix: manufacturerData.payload,
|
||||
mask: manufacturerData.mask,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Add optionalManufacturerData from scanFilter if webConfig is not provided
|
||||
if (webConfig == null || webConfig.optionalManufacturerData.isEmpty) {
|
||||
int? companyId = manufacturerData.companyIdentifier;
|
||||
if (companyId != null) {
|
||||
optionalManufacturerData.add(companyId);
|
||||
}
|
||||
// Add optionalManufacturerData from scanFilter if webOptions is not provided
|
||||
if (webOptions == null || webOptions.optionalManufacturerData.isEmpty) {
|
||||
optionalManufacturerData.add(manufacturerData.companyIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,8 +425,7 @@ extension _BluetoothDeviceExtension on BluetoothDevice {
|
||||
return BleDevice(
|
||||
name: name,
|
||||
deviceId: id,
|
||||
manufacturerData: manufacturerDataMap?.toUint8List(),
|
||||
manufacturerDataHead: manufacturerDataMap?.toUint8List(),
|
||||
manufacturerDataList: manufacturerDataMap?.toManufacturerDataList() ?? [],
|
||||
rssi: rssi,
|
||||
services: services,
|
||||
);
|
||||
@@ -431,17 +433,10 @@ extension _BluetoothDeviceExtension on BluetoothDevice {
|
||||
}
|
||||
|
||||
extension _UnmodifiableMapViewExtension on UnmodifiableMapView<int, ByteData> {
|
||||
Uint8List? toUint8List() {
|
||||
List<MapEntry<int, ByteData>> sorted = entries.toList()
|
||||
..sort((a, b) => a.key - b.key);
|
||||
if (sorted.isEmpty) return null;
|
||||
int companyId = sorted.first.key;
|
||||
List<int> manufacturerDataValue = sorted.first.value.buffer.asUint8List();
|
||||
final byteData = ByteData(2);
|
||||
byteData.setInt16(0, companyId, Endian.host);
|
||||
List<int> bytes = byteData.buffer.asUint8List();
|
||||
return Uint8List.fromList(bytes + manufacturerDataValue);
|
||||
}
|
||||
List<ManufacturerData>? toManufacturerDataList() => entries
|
||||
.map((MapEntry<int, ByteData> data) =>
|
||||
ManufacturerData(data.key, data.value.buffer.asUint8List()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
class _UniversalWebBluetoothService {
|
||||
|
||||
Reference in New Issue
Block a user