feat: 支持外围设备接口,优化中心设备接口 (#18)

* 临时提交

* 临时提交

* 临时提交

* fix: 调整接口

* fix: 修复问题

* fix: 调整 iOS 实现

* fix: 添加注释

* fix: 修改预览版本号

* fix: 修复已知问题

* fix: 优化接口

* fix: 解决 32 位 UUID 报错问题

* fix: 修复问题

* fix: 修复依赖项

* fix: 移除多余代码

* fix: 修复已知问题

* fix: 修复问题

* fix: 修改版本号

* fix: 修复问题

* fix: 发布正式版本
This commit is contained in:
Mr剑侠客
2023-10-10 05:01:25 -05:00
committed by GitHub
parent 073c2b9a2e
commit 79c50d638d
113 changed files with 9125 additions and 4560 deletions

View File

@ -1,3 +1,37 @@
## 3.0.0
* Add `PeripheralManager` api.
* Add `CentralManager#readRSSI` method.
* Add `CentralManager.instance` api.
* Add `PeripheralManager.instance` api.
* Move `CentralController` to `CentralManager`.
* Move `CentralState` to `BluetoothLowEnergyState`.
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
* Move `Advertisement` class to `AdvertiseData` class.
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
* Fix known issues.
## 3.0.0-dev.4
* Move `Advertisement` class to `AdvertiseData` class.
## 3.0.0-dev.3
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
## 3.0.0-dev.2
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
* Add `CentralManager.instance` api.
* Add `PeripheralManager.instance` api.
## 3.0.0-dev.1
* Implement new central manager api.
## 2.2.0
* Add `CentralController#getMaximumWriteLength` method.

View File

@ -1,9 +1,9 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'src/my_central_controller.dart';
import 'src/my_bluetooth_low_energy.dart';
abstract class BluetoothLowEnergyLinux {
static void registerWith() {
CentralController.instance = MyCentralController();
BluetoothLowEnergy.instance = MyBluetoothLowEnergy();
}
}

View File

@ -0,0 +1,12 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_central_manager.dart';
class MyBluetoothLowEnergy extends BluetoothLowEnergy {
@override
final MyCentralManager centralManager;
@override
PeripheralManager get peripheralManager => throw UnimplementedError();
MyBluetoothLowEnergy() : centralManager = MyCentralManager();
}

View File

@ -3,54 +3,87 @@ import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
extension MyBlueZAdapter on BlueZAdapter {
CentralState get state {
return powered ? CentralState.poweredOn : CentralState.poweredOff;
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
extension BlueZAdapterX on BlueZAdapter {
BluetoothLowEnergyState get myState {
return powered
? BluetoothLowEnergyState.poweredOn
: BluetoothLowEnergyState.poweredOff;
}
}
extension MyBlueZDevice on BlueZDevice {
extension BlueZDeviceX on BlueZDevice {
BlueZUUID get uuid {
final node = address.replaceAll(':', '');
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return BlueZUUID.fromString("00000000-0000-0000-0000-$node");
}
Advertisement get advertisement {
final name = this.name.isNotEmpty ? this.name : null;
final manufacturerSpecificData = manufacturerData.map((key, value) {
final id = key.id;
final data = Uint8List.fromList(value);
return MapEntry(id, data);
UUID get myUUID => uuid.toMyUUID();
List<MyGattService2> get myServices =>
gattServices.map((service) => MyGattService2(service)).toList();
AdvertiseData get myAdvertiseData {
final myName = name.isNotEmpty ? name : null;
final myServiceUUIDs = uuids.map((uuid) => uuid.toMyUUID()).toList();
final myServiceData = serviceData.map((uuid, data) {
final myUUID = uuid.toMyUUID();
final myData = Uint8List.fromList(data);
return MapEntry(myUUID, myData);
});
final serviceUUIDs = uuids.map((uuid) => uuid.toUUID()).toList();
final serviceData = this.serviceData.map((uuid, data) {
final key = uuid.toUUID();
final value = Uint8List.fromList(data);
return MapEntry(key, value);
});
return Advertisement(
name: name,
manufacturerSpecificData: manufacturerSpecificData,
serviceUUIDs: serviceUUIDs,
serviceData: serviceData,
return AdvertiseData(
name: myName,
serviceUUIDs: myServiceUUIDs,
serviceData: myServiceData,
manufacturerSpecificData: myManufacturerSpecificData,
);
}
ManufacturerSpecificData get myManufacturerSpecificData {
final entry = manufacturerData.entries.last;
final myId = entry.key.id;
final myData = Uint8List.fromList(entry.value);
return ManufacturerSpecificData(
id: myId,
data: myData,
);
}
}
extension MyBlueZUUID on BlueZUUID {
UUID toUUID() => UUID(value);
}
extension BlueZGattServiceX on BlueZGattService {
UUID get myUUID => uuid.toMyUUID();
extension MyBlueZGattCharacteristic on BlueZGattCharacteristic {
List<GattCharacteristicProperty> get properties => flags
.map((e) => e.toProperty())
.whereType<GattCharacteristicProperty>()
List<MyGattCharacteristic2> get myCharacteristics => characteristics
.map((characteristic) => MyGattCharacteristic2(characteristic))
.toList();
}
extension MyBlueZGattCharacteristicFlag on BlueZGattCharacteristicFlag {
GattCharacteristicProperty? toProperty() {
extension MyBlueZGattCharacteristic on BlueZGattCharacteristic {
UUID get myUUID => uuid.toMyUUID();
List<GattCharacteristicProperty> get myProperties => flags
.map((e) => e.toMyProperty())
.whereType<GattCharacteristicProperty>()
.toList();
List<MyGattDescriptor2> get myDescriptors =>
descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList();
}
extension BlueZGattDescriptorX on BlueZGattDescriptor {
UUID get myUUID => uuid.toMyUUID();
}
extension BlueZUUIDX on BlueZUUID {
UUID toMyUUID() => UUID(value);
}
extension BlueZGattCharacteristicFlagX on BlueZGattCharacteristicFlag {
GattCharacteristicProperty? toMyProperty() {
switch (this) {
case BlueZGattCharacteristicFlag.read:
return GattCharacteristicProperty.read;
@ -68,8 +101,8 @@ extension MyBlueZGattCharacteristicFlag on BlueZGattCharacteristicFlag {
}
}
extension MyGattCharacteristicWriteType on GattCharacteristicWriteType {
BlueZGattCharacteristicWriteType toBlueZ() {
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
BlueZGattCharacteristicWriteType toBlueZWriteType() {
switch (this) {
case GattCharacteristicWriteType.withResponse:
return BlueZGattCharacteristicWriteType.request;

View File

@ -1,443 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_linux/src/my_event_args.dart';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_gatt_characteristic.dart';
import 'my_gatt_descriptor.dart';
import 'my_gatt_service.dart';
import 'my_peripheral.dart';
class MyCentralController extends CentralController {
MyCentralController()
: _client = BlueZClient(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast(),
_myPeripheralDiscoveredController = StreamController.broadcast(),
_devicePropertiesChangedSubscriptions = {},
_characteristicPropertiesChangedSubscriptions = {},
_myPeripherals = {},
_myServices = {},
_myCharacteristics = {},
_myDescriptors = {},
_state = CentralState.unknown;
final BlueZClient _client;
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
final StreamController<MyPeripheralDiscoveredEventArgs>
_myPeripheralDiscoveredController;
final Map<int, StreamSubscription<List<String>>>
_devicePropertiesChangedSubscriptions;
final Map<int, StreamSubscription<List<String>>>
_characteristicPropertiesChangedSubscriptions;
final Map<int, MyPeripheral> _myPeripherals;
final Map<int, Map<int, MyGattService>> _myServices;
final Map<int, Map<int, MyGattCharacteristic>> _myCharacteristics;
final Map<int, Map<int, MyGattDescriptor>> _myDescriptors;
BlueZAdapter get _adapter => _client.adapters.first;
CentralState _state;
@override
CentralState get state => _state;
@override
Stream<CentralStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<CentralDiscoveredEventArgs> get discovered =>
_discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Stream<MyPeripheralDiscoveredEventArgs> get _myPeripheralDiscovered =>
_myPeripheralDiscoveredController.stream;
late StreamSubscription<List<String>> _adapterPropertiesChangedSubscription;
late StreamSubscription<BlueZDevice> _deviceAddedSubscription;
late StreamSubscription<BlueZDevice> _deviceRemovedSubscription;
Future<void> _throwWithState(CentralState state) async {
if (this.state == state) {
throw BluetoothLowEnergyError('$state is unexpected.');
}
}
Future<void> _throwWithoutState(CentralState state) async {
if (this.state != state) {
throw BluetoothLowEnergyError(
'$state is expected, but current state is ${this.state}.',
);
}
}
@override
Future<void> setUp() async {
await _throwWithoutState(CentralState.unknown);
await _client.connect();
_state =
_client.adapters.isEmpty ? CentralState.unsupported : _adapter.state;
if (_state == CentralState.unsupported) {
return;
}
for (var device in _client.devices) {
if (device.adapter.address != _adapter.address) {
continue;
}
_beginDevicePropertiesChangedListener(device);
}
_adapterPropertiesChangedSubscription = _adapter.propertiesChanged.listen(
_onAdapterPropertiesChanged,
);
_deviceAddedSubscription = _client.deviceAdded.listen(_onDeviceAdded);
_deviceRemovedSubscription = _client.deviceRemoved.listen(_onDeviceRemoved);
}
@override
Future<void> tearDown() async {
await _throwWithState(CentralState.unknown);
if (_state != CentralState.unsupported && _adapter.discovering) {
await _adapter.stopDiscovery();
}
for (var myPeripheral in _myPeripherals.values) {
final device = myPeripheral.device;
if (device.connected) {
await device.disconnect();
}
}
_myPeripherals.clear();
_myServices.clear();
_myCharacteristics.clear();
_myDescriptors.clear();
for (var device in _client.devices) {
if (device.adapter.address != _adapter.address) {
continue;
}
_endDevicePropertiesChangedListener(device);
}
_adapterPropertiesChangedSubscription.cancel();
_deviceAddedSubscription.cancel();
_deviceRemovedSubscription.cancel();
await _client.close();
_state = CentralState.unknown;
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(CentralState.poweredOn);
await _adapter.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(CentralState.poweredOn);
await _adapter.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final device = myPeripheral.device;
await device.connect();
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final device = myPeripheral.device;
await device.disconnect();
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
// TODO: 当前版本 `bluez` 插件不支持获取 MTU返回最小值 20.
return 20;
}
@override
Future<void> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final device = myPeripheral.device;
if (!device.connected) {
throw BluetoothLowEnergyError('Peripheral is disconnected.');
}
if (device.servicesResolved) {
return;
}
await _myPeripheralDiscovered.firstWhere(
(eventArgs) => eventArgs.myPeripheral == myPeripheral,
);
}
@override
Future<List<GattService>> getServices(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final myServices = _myServices[myPeripheral.hashCode];
if (myServices == null) {
throw ArgumentError();
}
return myServices.values.toList();
}
@override
Future<List<GattCharacteristic>> getCharacteristics(
GattService service,
) async {
await _throwWithoutState(CentralState.poweredOn);
final myService = service as MyGattService;
final myCharacteristics = _myCharacteristics[myService.hashCode];
if (myCharacteristics == null) {
throw ArgumentError();
}
return myCharacteristics.values.toList();
}
@override
Future<List<GattDescriptor>> getDescriptors(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final myDescriptors = _myDescriptors[myCharacteristic.hashCode];
if (myDescriptors == null) {
throw ArgumentError();
}
return myDescriptors.values.toList();
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final blueZCharacteristic = myCharacteristic.characteristic;
final blueZValue = await blueZCharacteristic.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final blueZCharacteristic = myCharacteristic.characteristic;
await blueZCharacteristic.writeValue(
value,
type: type.toBlueZ(),
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final blueZCharacteristic = myCharacteristic.characteristic;
if (state) {
await blueZCharacteristic.startNotify();
} else {
await blueZCharacteristic.stopNotify();
}
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(CentralState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor;
final blueZDescriptor = myDescriptor.descriptor;
final blueZValue = await blueZDescriptor.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor;
final blueZDescriptor = myDescriptor.descriptor;
await blueZDescriptor.writeValue(value);
}
void _onAdapterPropertiesChanged(List<String> properties) {
for (var property in properties) {
switch (property) {
case 'Powered':
final state = _adapter.state;
if (_state == state) {
return;
}
_state = state;
final eventArgs = CentralStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
break;
default:
break;
}
}
}
void _onDeviceAdded(BlueZDevice device) {
if (device.adapter.address != _adapter.address) {
return;
}
_onDiscovered(device);
_beginDevicePropertiesChangedListener(device);
}
void _onDeviceRemoved(BlueZDevice device) {
if (device.adapter.address != _adapter.address) {
return;
}
_endDevicePropertiesChangedListener(device);
}
void _onDiscovered(BlueZDevice device) {
final myPeripheral = MyPeripheral(device);
_myPeripherals[myPeripheral.hashCode] = myPeripheral;
final rssi = device.rssi;
final advertisement = device.advertisement;
final eventArgs = CentralDiscoveredEventArgs(
myPeripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
void _beginDevicePropertiesChangedListener(BlueZDevice device) {
final subscription = device.propertiesChanged.listen((properties) {
for (var property in properties) {
switch (property) {
case 'RSSI':
_onDiscovered(device);
break;
case 'Connected':
final myPeripheral =
_myPeripherals[device.hashCode] as MyPeripheral;
final state = device.connected;
final eventArgs = PeripheralStateChangedEventArgs(
myPeripheral,
state,
);
_peripheralStateChangedController.add(eventArgs);
break;
case 'UUIDs':
break;
case 'ServicesResolved':
if (device.servicesResolved) {
final myPeripheral =
_myPeripherals[device.hashCode] as MyPeripheral;
final myServices = <int, MyGattService>{};
for (var service in device.gattServices) {
final myService = MyGattService(service);
myServices[myService.hashCode] = myService;
final myCharacteristics = <int, MyGattCharacteristic>{};
for (var characteristic in service.characteristics) {
final myCharacteristic = MyGattCharacteristic(characteristic);
myCharacteristics[myCharacteristic.hashCode] =
myCharacteristic;
_beginCharacteristicPropertiesChangedListener(
service,
characteristic,
);
final myDescriptors = <int, MyGattDescriptor>{};
for (var descriptor in characteristic.descriptors) {
final myDescriptor = MyGattDescriptor(descriptor);
myDescriptors[myDescriptor.hashCode] = myDescriptor;
}
_myDescriptors[myCharacteristic.hashCode] = myDescriptors;
}
_myCharacteristics[myService.hashCode] = myCharacteristics;
}
_myServices[myPeripheral.hashCode] = myServices;
final eventArgs = MyPeripheralDiscoveredEventArgs(myPeripheral);
_myPeripheralDiscoveredController.add(eventArgs);
}
break;
default:
break;
}
}
});
_devicePropertiesChangedSubscriptions[device.hashCode] = subscription;
}
void _endDevicePropertiesChangedListener(BlueZDevice device) {
for (var service in device.gattServices) {
for (var characteristic in service.characteristics) {
_endCharacteristicPropertiesChangedListener(characteristic);
}
}
final subscription = _devicePropertiesChangedSubscriptions.remove(
device.address,
);
subscription?.cancel();
}
void _beginCharacteristicPropertiesChangedListener(
BlueZGattService service,
BlueZGattCharacteristic characteristic,
) {
final subscription = characteristic.propertiesChanged.listen((properties) {
for (var property in properties) {
switch (property) {
case 'Value':
final myCharacteristics = _myCharacteristics[service.hashCode];
if (myCharacteristics == null) {
throw ArgumentError();
}
final myCharacteristic = myCharacteristics[characteristic.hashCode]
as MyGattCharacteristic;
final value = Uint8List.fromList(characteristic.value);
final eventArgs = GattCharacteristicValueChangedEventArgs(
myCharacteristic,
value,
);
_characteristicValueChangedController.add(eventArgs);
break;
default:
break;
}
}
});
_characteristicPropertiesChangedSubscriptions[characteristic.hashCode] =
subscription;
}
void _endCharacteristicPropertiesChangedListener(
BlueZGattCharacteristic characteristic,
) {
final subscription = _characteristicPropertiesChangedSubscriptions.remove(
characteristic.hashCode,
);
subscription?.cancel();
}
}

View File

@ -0,0 +1,342 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_event_args.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
import 'my_peripheral2.dart';
class MyCentralManager extends CentralManager {
MyCentralManager()
: _client = BlueZClient(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast(),
_deviceServicesResolvedController = StreamController.broadcast(),
_myServicesOfMyPeripherals = {},
_characteristicPropertiesChangedSubscriptions = {},
_state = BluetoothLowEnergyState.unknown;
final BlueZClient _client;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
final StreamController<BlueZDeviceServicesResolvedEventArgs>
_deviceServicesResolvedController;
final Map<int, List<MyGattService2>> _myServicesOfMyPeripherals;
final Map<int, StreamSubscription>
_characteristicPropertiesChangedSubscriptions;
BlueZAdapter get _adapter => _client.adapters.first;
BluetoothLowEnergyState _state;
@override
BluetoothLowEnergyState get state => _state;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Stream<BlueZDeviceServicesResolvedEventArgs> get _servicesResolved =>
_deviceServicesResolvedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw BluetoothLowEnergyError(
'$state is expected, but current state is ${this.state}.',
);
}
}
@override
Future<void> setUp() async {
// TODO: hot restart is not handled.
await _client.connect();
_state = _client.adapters.isEmpty
? BluetoothLowEnergyState.unsupported
: _adapter.myState;
if (_state == BluetoothLowEnergyState.unsupported) {
return;
}
for (var device in _client.devices) {
if (device.adapter.address != _adapter.address) {
continue;
}
_beginDevicePropertiesChangedListener(device);
}
_adapter.propertiesChanged.listen(_onAdapterPropertiesChanged);
_client.deviceAdded.listen(_onDeviceAdded);
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _adapter.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _adapter.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
await device.connect();
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
await device.disconnect();
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
// TODO: 当前版本 `bluez` 插件不支持获取 MTU返回最小值 20.
return 20;
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
return device.rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
if (!device.connected) {
throw BluetoothLowEnergyError('Peripheral is disconnected.');
}
if (!device.servicesResolved) {
await _servicesResolved.firstWhere(
(eventArgs) => eventArgs.device == device,
);
}
final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode];
if (myServices == null) {
throw ArgumentError.notNull();
}
return myServices;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
final blueZValue = await blueZCharacteristic.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
await blueZCharacteristic.writeValue(
value,
type: type.toBlueZWriteType(),
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
if (state) {
await blueZCharacteristic.startNotify();
} else {
await blueZCharacteristic.stopNotify();
}
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor2;
final blueZDescriptor = myDescriptor.descriptor;
final blueZValue = await blueZDescriptor.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor2;
final blueZDescriptor = myDescriptor.descriptor;
await blueZDescriptor.writeValue(value);
}
void _onAdapterPropertiesChanged(List<String> properties) {
for (var property in properties) {
switch (property) {
case 'Powered':
final myState = _adapter.myState;
if (_state == myState) {
return;
}
_state = myState;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(myState);
_stateChangedController.add(eventArgs);
break;
default:
break;
}
}
}
void _onDeviceAdded(BlueZDevice device) {
if (device.adapter.address != _adapter.address) {
return;
}
_onDiscovered(device);
_beginDevicePropertiesChangedListener(device);
}
void _onDiscovered(BlueZDevice device) {
final myPeripheral = MyPeripheral2(device);
final myRSSI = device.rssi;
final myAdvertiseData = device.myAdvertiseData;
final eventArgs = DiscoveredEventArgs(
myPeripheral,
myRSSI,
myAdvertiseData,
);
_discoveredController.add(eventArgs);
}
void _beginDevicePropertiesChangedListener(BlueZDevice device) {
device.propertiesChanged.listen((properties) {
for (var property in properties) {
switch (property) {
case 'RSSI':
_onDiscovered(device);
break;
case 'Connected':
final myPeripheral = MyPeripheral2(device);
final state = device.connected;
final eventArgs = PeripheralStateChangedEventArgs(
myPeripheral,
state,
);
_peripheralStateChangedController.add(eventArgs);
if (!state) {
_endCharacteristicPropertiesChangedListener(myPeripheral);
}
break;
case 'UUIDs':
break;
case 'ServicesResolved':
if (device.servicesResolved) {
final myPeripheral = MyPeripheral2(device);
_endCharacteristicPropertiesChangedListener(myPeripheral);
final myServices = device.myServices;
_myServicesOfMyPeripherals[myPeripheral.hashCode] = myServices;
_beginCharacteristicPropertiesChangedListener(myPeripheral);
final eventArgs = BlueZDeviceServicesResolvedEventArgs(device);
_deviceServicesResolvedController.add(eventArgs);
}
break;
default:
break;
}
}
});
}
void _beginCharacteristicPropertiesChangedListener(
MyPeripheral myPeripheral,
) {
final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode];
if (myServices == null) {
throw ArgumentError.notNull();
}
for (var myService in myServices) {
final myCharacteristics =
myService.characteristics.cast<MyGattCharacteristic2>();
for (var myCharacteristic in myCharacteristics) {
final characteristic = myCharacteristic.characteristic;
final subscription = characteristic.propertiesChanged.listen(
(properties) {
for (var property in properties) {
switch (property) {
case 'Value':
final myValue = Uint8List.fromList(characteristic.value);
final eventArgs = GattCharacteristicValueChangedEventArgs(
myCharacteristic,
myValue,
);
_characteristicValueChangedController.add(eventArgs);
break;
default:
break;
}
}
},
);
_characteristicPropertiesChangedSubscriptions[
myCharacteristic.hashCode] = subscription;
}
}
}
void _endCharacteristicPropertiesChangedListener(MyPeripheral myPeripheral) {
final myServices = _myServicesOfMyPeripherals.remove(myPeripheral.hashCode);
if (myServices == null) {
return;
}
for (var myService in myServices) {
final myCharacteristics = myService.characteristics;
for (var myCharacteristic in myCharacteristics) {
final subscription = _characteristicPropertiesChangedSubscriptions
.remove(myCharacteristic.hashCode);
subscription?.cancel();
}
}
}
}

View File

@ -1,9 +1,8 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_peripheral.dart';
class BlueZDeviceServicesResolvedEventArgs extends EventArgs {
final BlueZDevice device;
class MyPeripheralDiscoveredEventArgs extends EventArgs {
final MyPeripheral myPeripheral;
MyPeripheralDiscoveredEventArgs(this.myPeripheral);
BlueZDeviceServicesResolvedEventArgs(this.device);
}

View File

@ -1,17 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_object.dart';
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
final BlueZGattCharacteristic characteristic;
MyGattCharacteristic(this.characteristic) : super(characteristic);
@override
UUID get uuid => characteristic.uuid.toUUID();
@override
List<GattCharacteristicProperty> get properties => characteristic.properties;
}

View File

@ -0,0 +1,16 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic {
final BlueZGattCharacteristic characteristic;
MyGattCharacteristic2(this.characteristic)
: super(
hashCode: characteristic.hashCode,
uuid: characteristic.myUUID,
properties: characteristic.myProperties,
descriptors: characteristic.myDescriptors,
);
}

View File

@ -1,14 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_object.dart';
class MyGattDescriptor extends MyObject implements GattDescriptor {
final BlueZGattDescriptor descriptor;
MyGattDescriptor(this.descriptor) : super(descriptor);
@override
UUID get uuid => descriptor.uuid.toUUID();
}

View File

@ -0,0 +1,14 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
class MyGattDescriptor2 extends MyGattDescriptor {
final BlueZGattDescriptor descriptor;
MyGattDescriptor2(this.descriptor)
: super(
hashCode: descriptor.hashCode,
uuid: descriptor.myUUID,
);
}

View File

@ -1,14 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_object.dart';
class MyGattService extends MyObject implements GattService {
final BlueZGattService service;
MyGattService(this.service) : super(service);
@override
UUID get uuid => service.uuid.toUUID();
}

View File

@ -0,0 +1,15 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
class MyGattService2 extends MyGattService {
final BlueZGattService service;
MyGattService2(this.service)
: super(
hashCode: service.hashCode,
uuid: service.myUUID,
characteristics: service.myCharacteristics,
);
}

View File

@ -1,11 +0,0 @@
abstract class MyObject {
@override
final int hashCode;
MyObject(Object instance) : hashCode = instance.hashCode;
@override
bool operator ==(Object other) {
return other is MyObject && other.hashCode == hashCode;
}
}

View File

@ -2,13 +2,13 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_object.dart';
class MyPeripheral extends MyObject implements Peripheral {
class MyPeripheral2 extends MyPeripheral {
final BlueZDevice device;
MyPeripheral(this.device) : super(device);
@override
UUID get uuid => device.uuid.toUUID();
MyPeripheral2(this.device)
: super(
hashCode: device.hashCode,
uuid: device.myUUID,
);
}

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_linux
description: Linux implementation of the bluetooth_low_energy plugin.
version: 2.2.0
version: 3.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment:
@ -10,7 +10,7 @@ environment:
dependencies:
flutter:
sdk: flutter
bluetooth_low_energy_platform_interface: ^2.2.0
bluetooth_low_energy_platform_interface: ^3.0.0
bluez: ^0.8.1
dev_dependencies: