Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.0.1
* Bump `flutter_web_bluetooth` to `^1.2.0`.
* Web: use the dependency's advertisement abort controller and `startupTimeout` instead of the hand-rolled abort/timeout workaround. `unwatchAdvertisements` now also aborts a pending advertisement startup.

## 3.0.0
* **Breaking:** Add `QueueType.auto` which auto-selects the best queueing strategy per platform: Android, Web, and Linux use a per-device queue (their BLE stacks reject or misbehave on overlapping operations), while all other platforms run commands in parallel. It is now the default for both `UniversalBle` and `UniversalBlePeripheral`, replacing the previous `QueueType.global` default. Any exhaustive `switch` over `QueueType` must handle the new `auto` value.
* **Breaking:** Device IDs are now emitted in lower-case on every platform (scan results, connection/value/pairing/connection-parameter callbacks and streams). Previously each platform reported its native case — Android upper-cased MACs, Windows/WinRT lower-cased them. IDs are now canonicalised to lower-case throughout the Dart layer; the native side converts back to the case it requires at its boundary (Android's `getRemoteDevice` needs upper-case). Callers that stored or compared an emitted ID by exact case (e.g. an Android upper-case MAC) must now lower-case it, or compare case-insensitively. Web is exempt: Web Bluetooth IDs are opaque, case-sensitive browser tokens rather than addresses, so they are emitted and matched verbatim. Follow-up to the case-insensitive matching in 2.1.1. Custom `UniversalBlePlatform` implementations must report the canonical form too: the event-stream filters now match the canonical id directly, so a platform that pushes a non-canonical id straight onto the exported stream controllers (instead of calling the `update*` handlers, which canonicalise) will no longer match a query made with a different casing. The same applies to peripheral mode: central device IDs in `UniversalBlePeripheral` streams, read/write request handlers, and `getSubscribedClients` results are emitted in lower-case, and IDs passed to `updateCharacteristicValue` / `getMaximumNotifyLength` are accepted in any case.
Expand Down
4 changes: 2 additions & 2 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
sha256: "71fa37f699032b4416d56aeb5214363ba057f6aed4eba544cc26293e2582b85d"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
Expand Down
4 changes: 2 additions & 2 deletions hil/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ packages:
dependency: transitive
description:
name: flutter_web_bluetooth
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
sha256: "71fa37f699032b4416d56aeb5214363ba057f6aed4eba544cc26293e2582b85d"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
Expand Down
47 changes: 10 additions & 37 deletions lib/src/universal_ble_web/universal_ble_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import 'dart:collection';

import 'package:flutter/foundation.dart';
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
import 'package:flutter_web_bluetooth/js_web_bluetooth.dart'
show WatchAdvertisementsOptions;
import 'package:flutter_web_bluetooth/web/js/js.dart' show AbortController;
import 'package:universal_ble/src/utils/universal_logger.dart';
import 'package:universal_ble/universal_ble.dart';

Expand All @@ -26,8 +23,6 @@ class UniversalBleWeb extends UniversalBlePlatform {

final Map<String, BluetoothDevice> _bluetoothDeviceList = {};
final Map<String, StreamSubscription> _deviceAdvertisementStreamList = {};
final Map<String, Future<void>> _advertisementStartOperations = {};
final Map<String, AbortController> _advertisementControllers = {};
static const _advertisementTimeout = Duration(seconds: 5);
final Map<String, StreamSubscription> _connectedDeviceStreamList = {};
final Map<String, StreamSubscription> _characteristicStreamList = {};
Expand Down Expand Up @@ -338,28 +333,11 @@ class UniversalBleWeb extends UniversalBlePlatform {
);
});
device.advertisementsUseMemory = true;
// Own the abort signal: the dependency does not retain its controller
// when watchAdvertisements is called without a timeout. A lifetime
// timeout would also stop a successfully started scan after five seconds.
final controller = AbortController();
_advertisementControllers[device.id] = controller;
// The wrapper has no API accepting a caller-owned abort signal.
// ignore: deprecated_member_use
final operation = device.nativeDevice
.watchAdvertisements(
WatchAdvertisementsOptions(signal: controller.signal))
.timeout(_advertisementTimeout, onTimeout: () {
controller.abort();
throw TimeoutException('Advertisement startup timed out');
});
_advertisementStartOperations[device.id] = operation;
try {
await operation;
} finally {
if (identical(_advertisementStartOperations[device.id], operation)) {
_advertisementStartOperations.remove(device.id);
}
}
// Watch indefinitely but bound only the startup. The dependency keeps its
// own abort controller for the whole watch, so `unwatchAdvertisements`
// aborts a pending start as well as a running watch, and `startupTimeout`
// expires without stopping a scan that started successfully.
await device.watchAdvertisements(null, _advertisementTimeout);
} catch (e) {
UniversalLogger.logError("WebWatchAdvertisementError: $e");
}
Expand Down Expand Up @@ -727,17 +705,12 @@ class UniversalBleWeb extends UniversalBlePlatform {
for (final id in deviceIds) {
final subscription = _deviceAdvertisementStreamList[id];
final device = _getDeviceById(id);
// Abort pending startup before waiting for it. An aborted signal also
// prevents a late native completion from restarting advertisements.
_advertisementControllers.remove(id)?.abort();
await subscription?.cancel();
try {
await _advertisementStartOperations[id];
} catch (_) {
// Advertisement startup reports its own error and is optional.
}
// Let unexpected stop failures propagate: connecting while the browser
// is still watching would recreate the scan/connect overlap.
// `unwatchAdvertisements` aborts a pending startup before stopping a
// running watch, which also prevents a late native completion from
// restarting advertisements. Let unexpected stop failures propagate:
// connecting while the browser is still watching would recreate the
// scan/connect overlap.
try {
await device?.unwatchAdvertisements().timeout(_advertisementTimeout);
} finally {
Expand Down
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: universal_ble
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
version: 3.0.0
version: 3.0.1
homepage: https://navideck.com
repository: https://github.com/Navideck/universal_ble
issue_tracker: https://github.com/Navideck/universal_ble/issues
Expand Down Expand Up @@ -31,7 +31,7 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.8
flutter_web_bluetooth: ^1.1.0
flutter_web_bluetooth: ^1.2.0
bluez: ^0.8.3
meta: ^1.16.0

Expand Down
Loading