Implement readRSSI on Android and Darwin (#203)

This commit is contained in:
EricKwok
2026-01-06 00:25:19 +08:00
committed by Foti Dim
parent a5b6af6168
commit 1adfa9b485
25 changed files with 456 additions and 91 deletions
+3
View File
@@ -1,3 +1,6 @@
## 1.1.0
* Add readRssi method
## 1.0.1 ## 1.0.1
* Enforce C++20 standard for Windows builds * Enforce C++20 standard for Windows builds
+16
View File
@@ -131,3 +131,19 @@ This method will **attempt** to set the MTU (Maximum Transmission Unit) but it i
```dart ```dart
int mtu = await UniversalBle.requestMtu(widget.deviceId, 247); int mtu = await UniversalBle.requestMtu(widget.deviceId, 247);
``` ```
### Read RSSI
Read the signal strength (RSSI) of a connected device.
```dart
int rssi = await UniversalBle.readRssi(deviceId);
```
> ⚠️ Note: The device must be connected before reading RSSI.
#### Platform Limitations
* **Android / iOS / macOS**: Fully supported.
* **Windows / Linux / Web**: Not supported.
+20 -1
View File
@@ -27,6 +27,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE
- [Pairing](#pairing) - [Pairing](#pairing)
- [Bluetooth Availability](#bluetooth-availability) - [Bluetooth Availability](#bluetooth-availability)
- [Requesting MTU](#requesting-mtu) - [Requesting MTU](#requesting-mtu)
- [Reading RSSI](#reading-rssi)
- [Command Queue](#command-queue) - [Command Queue](#command-queue)
- [Timeout](#timeout) - [Timeout](#timeout)
- [Error Handling](#error-handling) - [Error Handling](#error-handling)
@@ -52,6 +53,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE
| enable/disable Bluetooth | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | | enable/disable Bluetooth | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ |
| onAvailabilityChange | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | onAvailabilityChange | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| requestMtu | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | | requestMtu | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
| readRssi | ✔️ | ✔️ | ✔️ | ❌ | 🚧 | ❌ |
| requestPermissions | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | requestPermissions | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
## Getting Started ## Getting Started
@@ -409,7 +411,7 @@ UniversalBle.disableBluetooth();
```dart ```dart
int mtu = await bleDevice.requestMtu(256); int mtu = await bleDevice.requestMtu(256);
```` ```
> ⚠️ Note: Requesting an MTU is a *best-effort* operation. > ⚠️ Note: Requesting an MTU is a *best-effort* operation.
> On many platforms the final MTU is fully controlled by the OS and remote device. > On many platforms the final MTU is fully controlled by the OS and remote device.
@@ -460,6 +462,23 @@ When developing cross-platform BLE applications and devices:
* Take advantage of higher MTUs when available, without depending on them * Take advantage of higher MTUs when available, without depending on them
### Reading RSSI
Read the signal strength (RSSI) of a connected device.
```dart
int rssi = await bleDevice.readRssi();
```
> ⚠️ Note: The device must be connected before reading RSSI.
#### Platform Limitations
* **Android / iOS / macOS**: Fully supported.
* **Windows / Linux / Web**: Not supported.
## Command Queue ## Command Queue
By default, all commands are executed in a global queue (`QueueType.global`), with each command waiting for the previous one to finish. While this method is slower it is the safest to avoid command exceptions and therefore is the default. By default, all commands are executed in a global queue (`QueueType.global`), with each command waiting for the previous one to finish. While this method is slower it is the safest to avoid command exceptions and therefore is the default.
@@ -529,6 +529,7 @@ interface UniversalBlePlatformChannel {
fun unPair(deviceId: String) fun unPair(deviceId: String)
fun getSystemDevices(withServices: List<String>, callback: (Result<List<UniversalBleScanResult>>) -> Unit) fun getSystemDevices(withServices: List<String>, callback: (Result<List<UniversalBleScanResult>>) -> Unit)
fun getConnectionState(deviceId: String): Long fun getConnectionState(deviceId: String): Long
fun readRssi(deviceId: String, callback: (Result<Long>) -> Unit)
fun setLogLevel(logLevel: UniversalBleLogLevel) fun setLogLevel(logLevel: UniversalBleLogLevel)
companion object { companion object {
@@ -919,6 +920,26 @@ interface UniversalBlePlatformChannel {
channel.setMessageHandler(null) channel.setMessageHandler(null)
} }
} }
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readRssi$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val deviceIdArg = args[0] as String
api.readRssi(deviceIdArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$separatedMessageChannelSuffix", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$separatedMessageChannelSuffix", codec)
if (api != null) { if (api != null) {
@@ -344,3 +344,8 @@ class SubscriptionResultFuture(
val serviceId: String, val serviceId: String,
val result: (Result<Unit>) -> Unit, val result: (Result<Unit>) -> Unit,
) )
class RssiResultFuture(
val deviceId: String,
val result: (Result<Long>) -> Unit,
)
@@ -60,6 +60,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
private val writeResultFutureList = mutableListOf<WriteResultFuture>() private val writeResultFutureList = mutableListOf<WriteResultFuture>()
private val subscriptionResultFutureList = mutableListOf<SubscriptionResultFuture>() private val subscriptionResultFutureList = mutableListOf<SubscriptionResultFuture>()
private val pairResultFutures = mutableMapOf<String, (Result<Boolean>) -> Unit>() private val pairResultFutures = mutableMapOf<String, (Result<Boolean>) -> Unit>()
private val rssiResultFutureList = mutableListOf<RssiResultFuture>()
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
UniversalBlePlatformChannel.setUp(flutterPluginBinding.binaryMessenger, this) UniversalBlePlatformChannel.setUp(flutterPluginBinding.binaryMessenger, this)
@@ -279,6 +280,49 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
UniversalBleLogger.setLogLevel(logLevel) UniversalBleLogger.setLogLevel(logLevel)
} }
override fun readRssi(deviceId: String, callback: (Result<Long>) -> Unit) {
try {
val gatt = deviceId.toBluetoothGatt()
if (gatt.readRemoteRssi()) {
rssiResultFutureList.add(RssiResultFuture(deviceId, callback))
} else {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to read RSSI"
)
)
)
}
} catch (e: FlutterError) {
callback(Result.failure(e))
}
}
override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
val deviceId = gatt?.device?.address ?: return
rssiResultFutureList.removeAll {
if (it.deviceId == deviceId) {
if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(rssi.toLong()))
} else {
it.result(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to read RSSI"
)
)
)
}
true
} else {
false
}
}
}
override fun discoverServices( override fun discoverServices(
deviceId: String, deviceId: String,
withDescriptors: Boolean, withDescriptors: Boolean,
@@ -311,8 +355,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status != BluetoothGatt.GATT_SUCCESS) { if (status != BluetoothGatt.GATT_SUCCESS) {
discoverServicesFutureList.filter { it.deviceId == gatt.device.address }.forEach { discoverServicesFutureList.removeAll {
discoverServicesFutureList.remove(it) if (it.deviceId == gatt.device.address) {
it.result( it.result(
Result.failure( Result.failure(
createFlutterError( createFlutterError(
@@ -321,12 +365,16 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
) )
) )
) )
true
} else {
false
}
} }
return return
} }
setCachedServices(gatt.device.address, gatt.services.map { it.uuid.toString() }) setCachedServices(gatt.device.address, gatt.services.map { it.uuid.toString() })
discoverServicesFutureList.filter { it.deviceId == gatt.device.address }.forEach { discoverServicesFutureList.removeAll {
discoverServicesFutureList.remove(it) if (it.deviceId == gatt.device.address) {
it.result(Result.success(gatt.services.map { service -> it.result(Result.success(gatt.services.map { service ->
UniversalBleService( UniversalBleService(
uuid = service.uuid.toString(), uuid = service.uuid.toString(),
@@ -341,6 +389,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} }
) )
})) }))
true
} else {
false
}
} }
} }
@@ -517,12 +569,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
value: ByteArray, value: ByteArray,
status: Int, status: Int,
) { ) {
readResultFutureList.filter { readResultFutureList.removeAll {
it.deviceId == gatt.device.address && if (it.deviceId == gatt.device.address &&
it.characteristicId == characteristic.uuid.toString() && it.characteristicId == characteristic.uuid.toString() &&
it.serviceId == characteristic.service.uuid.toString() it.serviceId == characteristic.service.uuid.toString()
}.forEach { ) {
readResultFutureList.remove(it)
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(value)) it.result(Result.success(value))
} else { } else {
@@ -539,7 +590,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
) )
) )
} }
true
} else {
false
}
} }
} }
@@ -640,12 +694,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
characteristic: BluetoothGattCharacteristic, characteristic: BluetoothGattCharacteristic,
status: Int, status: Int,
) { ) {
writeResultFutureList.filter { writeResultFutureList.removeAll {
it.deviceId == gatt?.device?.address && if (it.deviceId == gatt?.device?.address &&
it.characteristicId == characteristic.uuid.toString() && it.characteristicId == characteristic.uuid.toString() &&
it.serviceId == characteristic.service.uuid.toString() it.serviceId == characteristic.service.uuid.toString()
}.forEach { ) {
writeResultFutureList.remove(it)
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(Unit)) it.result(Result.success(Unit))
} else { } else {
@@ -662,6 +715,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
) )
) )
} }
true
} else {
false
}
} }
} }
@@ -679,8 +736,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) { override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
val deviceId = gatt?.device?.address ?: return val deviceId = gatt?.device?.address ?: return
mtuResultFutureList.filter { it.deviceId == deviceId }.forEach { mtuResultFutureList.removeAll {
mtuResultFutureList.remove(it) if (it.deviceId == deviceId) {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(mtu.toLong())) it.result(Result.success(mtu.toLong()))
} else { } else {
@@ -693,6 +750,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
) )
) )
} }
true
} else {
false
}
} }
} }
@@ -941,6 +1002,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
false false
} }
} }
rssiResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
}
}
} }
private fun onBondStateUpdate(deviceId: String, bonded: Boolean, error: String? = null) { private fun onBondStateUpdate(deviceId: String, bonded: Boolean, error: String? = null) {
@@ -1116,12 +1185,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
service: String, service: String,
status: Int, status: Int,
) { ) {
subscriptionResultFutureList.filter { subscriptionResultFutureList.removeAll {
it.deviceId == deviceId && if (it.deviceId == deviceId &&
it.characteristicId == characteristic && it.characteristicId == characteristic &&
it.serviceId == service it.serviceId == service
}.forEach { ) {
subscriptionResultFutureList.remove(it)
if (status != BluetoothGatt.GATT_SUCCESS) { if (status != BluetoothGatt.GATT_SUCCESS) {
it.result( it.result(
Result.failure( Result.failure(
@@ -1135,6 +1203,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} else { } else {
it.result(Result.success(Unit)) it.result(Result.success(Unit))
} }
true
} else {
false
}
} }
} }
+18
View File
@@ -548,6 +548,7 @@ protocol UniversalBlePlatformChannel {
func unPair(deviceId: String) throws func unPair(deviceId: String) throws
func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void)
func getConnectionState(deviceId: String) throws -> Int64 func getConnectionState(deviceId: String) throws -> Int64
func readRssi(deviceId: String, completion: @escaping (Result<Int64, Error>) -> Void)
func setLogLevel(logLevel: UniversalBleLogLevel) throws func setLogLevel(logLevel: UniversalBleLogLevel) throws
} }
@@ -882,6 +883,23 @@ class UniversalBlePlatformChannelSetup {
} else { } else {
getConnectionStateChannel.setMessageHandler(nil) getConnectionStateChannel.setMessageHandler(nil)
} }
let readRssiChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readRssi\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
readRssiChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let deviceIdArg = args[0] as! String
api.readRssi(deviceId: deviceIdArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
readRssiChannel.setMessageHandler(nil)
}
let setLogLevelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) let setLogLevelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
setLogLevelChannel.setMessageHandler { message, reply in setLogLevelChannel.setMessageHandler { message, reply in
+10
View File
@@ -258,3 +258,13 @@ class DiscoverServicesFuture {
self.result = result self.result = result
} }
} }
class RssiReadFuture {
let deviceId: String
let result: (Result<Int64, Error>) -> Void
init(deviceId: String, result: @escaping (Result<Int64, Error>) -> Void) {
self.deviceId = deviceId
self.result = result
}
}
+35
View File
@@ -40,6 +40,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
private var characteristicWriteWithoutResponseFutures = [CharacteristicWriteFuture]() private var characteristicWriteWithoutResponseFutures = [CharacteristicWriteFuture]()
private var characteristicNotifyFutures = [CharacteristicNotifyFuture]() private var characteristicNotifyFutures = [CharacteristicNotifyFuture]()
private var discoverServicesFutures = [DiscoverServicesFuture]() private var discoverServicesFutures = [DiscoverServicesFuture]()
private var rssiReadFutures = [RssiReadFuture]()
private var isManageScanning = false private var isManageScanning = false
init(callbackChannel: UniversalBleCallbackChannel) { init(callbackChannel: UniversalBleCallbackChannel) {
@@ -200,6 +201,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
} }
return false return false
} }
rssiReadFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(createFlutterError(code: .deviceDisconnected, message: "Device Disconnected"))
)
return true
}
return false
}
activeServiceDiscoveries[deviceId]?.cleanup() activeServiceDiscoveries[deviceId]?.cleanup()
activeServiceDiscoveries[deviceId] = nil activeServiceDiscoveries[deviceId] = nil
} }
@@ -334,6 +344,16 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
completion(Result.success(mtuResult)) completion(Result.success(mtuResult))
} }
func readRssi(deviceId: String, completion: @escaping (Result<Int64, Error>) -> Void) {
UniversalBleLogger.shared.logDebug("READ_RSSI -> \(deviceId)")
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(deviceId)")))
return
}
peripheral.readRSSI()
rssiReadFutures.append(RssiReadFuture(deviceId: deviceId, result: completion))
}
func isPaired(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) { func isPaired(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(createFlutterError(code: .notSupported))) completion(Result.failure(createFlutterError(code: .notSupported)))
} }
@@ -531,6 +551,21 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
return false return false
} }
} }
public func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
rssiReadFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString {
if let flutterError = error?.toFlutterError() {
UniversalBleLogger.shared.logError("READ_RSSI_FAILED <- \(peripheral.uuid.uuidString): \(flutterError.message ?? "")")
future.result(Result.failure(flutterError))
} else {
future.result(Result.success(RSSI.int64Value))
}
return true
}
return false
}
}
} }
extension CBPeripheral { extension CBPeripheral {
+6
View File
@@ -113,6 +113,12 @@ class MockUniversalBle extends UniversalBlePlatform {
return 512; return 512;
} }
@override
Future<int> readRssi(String deviceId) async {
await Future.delayed(const Duration(milliseconds: 500));
return -50; // Mock RSSI value in dBm
}
@override @override
Future<void> setNotifiable(String deviceId, String service, Future<void> setNotifiable(String deviceId, String service,
String characteristic, BleInputProperty bleInputProperty) async {} String characteristic, BleInputProperty bleInputProperty) async {}
@@ -1097,6 +1097,30 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
), ),
), ),
), ),
OutlinedButton.icon(
onPressed: isConnected
? () async {
try {
int rssi = await bleDevice.readRssi();
_addLog('RSSI', '$rssi dBm');
} catch (e) {
_addLog('ReadRssiError (${e.runtimeType})', e);
}
}
: null,
icon: const Icon(Icons.signal_cellular_alt),
label: const Text('Get RSSI'),
style: OutlinedButton.styleFrom(
foregroundColor: colorScheme.onSurface,
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
if (BleCapabilities.supportsRequestMtuApi) if (BleCapabilities.supportsRequestMtuApi)
ElevatedButton.icon( ElevatedButton.icon(
onPressed: isConnected onPressed: isConnected
+13
View File
@@ -26,6 +26,19 @@ class BleDevice {
Future<BleConnectionState> get connectionState => Future<BleConnectionState> get connectionState =>
UniversalBle.getConnectionState(deviceId); UniversalBle.getConnectionState(deviceId);
/// Read the RSSI value of this connected device.
///
/// Returns the current RSSI value in dBm. This value indicates the signal strength
/// between the device and the connected peripheral. Lower (more negative) values
/// indicate weaker signal, while higher (less negative) values indicate stronger signal.
///
/// **Note**: The device must be connected before reading RSSI.
///
/// Throws [BleException] if:
/// - The device is not connected
/// - Reading RSSI fails
Future<int> readRssi() => UniversalBle.readRssi(deviceId);
/// On web, it returns true if the web browser supports receiving advertisements from this device. /// On web, it returns true if the web browser supports receiving advertisements from this device.
/// The rest of the platforms will always return true. /// The rest of the platforms will always return true.
bool get receivesAdvertisements => bool get receivesAdvertisements =>
+22
View File
@@ -340,6 +340,28 @@ class UniversalBle {
); );
} }
/// Read the RSSI value of a connected device.
///
/// Returns the current RSSI value in dBm. This value indicates the signal strength
/// between the device and the connected peripheral. Lower (more negative) values
/// indicate weaker signal, while higher (less negative) values indicate stronger signal.
///
/// **Note**: The device must be connected before reading RSSI.
///
/// Throws [BleException] if:
/// - The device is not connected
/// - Reading RSSI fails
static Future<int> readRssi(
String deviceId, {
Duration? timeout,
}) async {
return await _bleCommandQueue.queueCommand(
() => _platform.readRssi(deviceId),
timeout: timeout,
deviceId: deviceId,
);
}
/// Check if a device is paired. /// Check if a device is paired.
/// ///
/// For `Apple` and `Web`, you have to pass a "pairingCommand" with an encrypted read or write characteristic. /// For `Apple` and `Web`, you have to pass a "pairingCommand" with an encrypted read or write characteristic.
@@ -378,6 +378,14 @@ class UniversalBleLinux extends UniversalBlePlatform {
); );
} }
@override
Future<int> readRssi(String deviceId) async {
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "readRssi is not implemented on Linux platform",
);
}
@override @override
Future<bool> pair(String deviceId) async { Future<bool> pair(String deviceId) async {
BlueZDevice device = _findDeviceById(deviceId); BlueZDevice device = _findDeviceById(deviceId);
@@ -1106,6 +1106,35 @@ class UniversalBlePlatformChannel {
} }
} }
Future<int> readRssi(String deviceId) async {
final pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readRssi$pigeonVar_messageChannelSuffix';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as int?)!;
}
}
Future<void> setLogLevel(UniversalBleLogLevel logLevel) async { Future<void> setLogLevel(UniversalBleLogLevel logLevel) async {
final pigeonVar_channelName = final pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$pigeonVar_messageChannelSuffix'; 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$pigeonVar_messageChannelSuffix';
@@ -133,6 +133,10 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
_executeWithErrorHandling( _executeWithErrorHandling(
() => _channel.requestMtu(deviceId, expectedMtu)); () => _channel.requestMtu(deviceId, expectedMtu));
@override
Future<int> readRssi(String deviceId) =>
_executeWithErrorHandling(() => _channel.readRssi(deviceId));
@override @override
Future<bool> isPaired(String deviceId) => Future<bool> isPaired(String deviceId) =>
_executeWithErrorHandling(() => _channel.isPaired(deviceId)); _executeWithErrorHandling(() => _channel.isPaired(deviceId));
@@ -79,6 +79,8 @@ abstract class UniversalBlePlatform {
Future<int> requestMtu(String deviceId, int expectedMtu); Future<int> requestMtu(String deviceId, int expectedMtu);
Future<int> readRssi(String deviceId);
Future<bool> isPaired(String deviceId); Future<bool> isPaired(String deviceId);
Future<bool> pair(String deviceId); Future<bool> pair(String deviceId);
@@ -283,6 +283,15 @@ class UniversalBleWeb extends UniversalBlePlatform {
); );
} }
/// `Unimplemented`
@override
Future<int> readRssi(String deviceId) {
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "readRssi is not implemented on Web platform",
);
}
@override @override
Future<bool> isPaired(String deviceId) { Future<bool> isPaired(String deviceId) {
throw UniversalBleException( throw UniversalBleException(
+3
View File
@@ -93,6 +93,9 @@ abstract class UniversalBlePlatformChannel {
int getConnectionState(String deviceId); int getConnectionState(String deviceId);
@async
int readRssi(String deviceId);
void setLogLevel(UniversalBleLogLevel logLevel); void setLogLevel(UniversalBleLogLevel logLevel);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
name: universal_ble name: universal_ble
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
version: 1.0.1+3 version: 1.1.0
homepage: https://navideck.com homepage: https://navideck.com
repository: https://github.com/Navideck/universal_ble repository: https://github.com/Navideck/universal_ble
issue_tracker: https://github.com/Navideck/universal_ble/issues issue_tracker: https://github.com/Navideck/universal_ble/issues
+5
View File
@@ -132,4 +132,9 @@ class _UniversalBleMock extends UniversalBlePlatformMock {
Future<void> requestPermissions({bool withAndroidFineLocation = false}) { Future<void> requestPermissions({bool withAndroidFineLocation = false}) {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<int> readRssi(String deviceId) async {
return -50; // Mock RSSI value
}
} }
+29
View File
@@ -1189,6 +1189,35 @@ void UniversalBlePlatformChannel::SetUp(
channel.SetMessageHandler(nullptr); channel.SetMessageHandler(nullptr);
} }
} }
{
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readRssi" + prepended_suffix, &GetCodec());
if (api != nullptr) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
if (encodable_device_id_arg.IsNull()) {
reply(WrapError("device_id_arg unexpectedly null."));
return;
}
const auto& device_id_arg = std::get<std::string>(encodable_device_id_arg);
api->ReadRssi(device_id_arg, [reply](ErrorOr<int64_t>&& output) {
if (output.has_error()) {
reply(WrapError(output.error()));
return;
}
EncodableList wrapped;
wrapped.push_back(EncodableValue(std::move(output).TakeValue()));
reply(EncodableValue(std::move(wrapped)));
});
} catch (const std::exception& exception) {
reply(WrapError(exception.what()));
}
});
} else {
channel.SetMessageHandler(nullptr);
}
}
{ {
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel" + prepended_suffix, &GetCodec()); BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel" + prepended_suffix, &GetCodec());
if (api != nullptr) { if (api != nullptr) {
+3
View File
@@ -435,6 +435,9 @@ class UniversalBlePlatformChannel {
const flutter::EncodableList& with_services, const flutter::EncodableList& with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0; std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0;
virtual ErrorOr<int64_t> GetConnectionState(const std::string& device_id) = 0; virtual ErrorOr<int64_t> GetConnectionState(const std::string& device_id) = 0;
virtual void ReadRssi(
const std::string& device_id,
std::function<void(ErrorOr<int64_t> reply)> result) = 0;
virtual std::optional<FlutterError> SetLogLevel(const UniversalBleLogLevel& log_level) = 0; virtual std::optional<FlutterError> SetLogLevel(const UniversalBleLogLevel& log_level) = 0;
// The codec used by UniversalBlePlatformChannel. // The codec used by UniversalBlePlatformChannel.
+7
View File
@@ -451,6 +451,13 @@ void UniversalBlePlugin::RequestMtu(
} }
} }
void UniversalBlePlugin::ReadRssi(
const std::string &device_id,
std::function<void(ErrorOr<int64_t> reply)> result) {
result(create_flutter_error(UniversalBleErrorCode::kNotImplemented,
"readRssi is not implemented on Windows platform"));
}
void UniversalBlePlugin::IsPaired( void UniversalBlePlugin::IsPaired(
const std::string &device_id, const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result) { std::function<void(ErrorOr<bool> reply)> result) {
+2
View File
@@ -200,6 +200,8 @@ private:
std::function<void(std::optional<FlutterError> reply)> result) override; std::function<void(std::optional<FlutterError> reply)> result) override;
void RequestMtu(const std::string &device_id, int64_t expected_mtu, void RequestMtu(const std::string &device_id, int64_t expected_mtu,
std::function<void(ErrorOr<int64_t> reply)> result) override; std::function<void(ErrorOr<int64_t> reply)> result) override;
void ReadRssi(const std::string &device_id,
std::function<void(ErrorOr<int64_t> reply)> result) override;
void IsPaired(const std::string &device_id, void IsPaired(const std::string &device_id,
std::function<void(ErrorOr<bool> reply)> result) override; std::function<void(ErrorOr<bool> reply)> result) override;
void Pair(const std::string &device_id, void Pair(const std::string &device_id,