Initial commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/// BLE GATT-server peripheral host adapter for the DALI Toolkit relay feature.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```dart
|
||||
/// final host = BleRelayHost();
|
||||
/// if (host.isSupported) {
|
||||
/// await host.start(
|
||||
/// advertisedName: 'DALI-Bridge',
|
||||
/// onClientWrite: (data) async { /* forward to DALI upstream */ },
|
||||
/// );
|
||||
/// // Push DALI responses back to connected BLE clients:
|
||||
/// await host.push(responseBytes);
|
||||
/// await host.stop();
|
||||
/// }
|
||||
/// ```
|
||||
library ble_relay_host;
|
||||
|
||||
export 'src/ble_relay_host.dart';
|
||||
@@ -0,0 +1,101 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// GATT service UUID shared between DALI Toolkit BLE central (client) and the
|
||||
/// relay host peripheral. Must match the UUID used in `ble.dart` / `ble_web.dart`.
|
||||
const String kBleRelayServiceUuid = '0000fff0-0000-1000-8000-00805f9b34fb';
|
||||
|
||||
/// GATT characteristic UUID for bidirectional DALI frame data.
|
||||
const String kBleRelayCharacteristicUuid = '0000fff1-0000-1000-8000-00805f9b34fb';
|
||||
|
||||
/// Method-channel name (must match Android/iOS/macOS native implementations).
|
||||
const String _kChannelName = 'com.dalimaster.ble_relay_host';
|
||||
|
||||
/// An abstraction over the platform BLE GATT-server implementation.
|
||||
///
|
||||
/// On Android (API 21+) this wires into [BluetoothGattServer] + [BluetoothLeAdvertiser].
|
||||
/// On all other platforms, [isSupported] returns `false` and every method is a no-op.
|
||||
class BleRelayHost {
|
||||
BleRelayHost() : _channel = const MethodChannel(_kChannelName);
|
||||
|
||||
@visibleForTesting
|
||||
BleRelayHost.withChannel(MethodChannel channel) : _channel = channel;
|
||||
|
||||
final MethodChannel _channel;
|
||||
|
||||
StreamSubscription<dynamic>? _eventSub;
|
||||
Future<void> Function(Uint8List data)? _onClientWrite;
|
||||
bool _running = false;
|
||||
|
||||
bool get isRunning => _running;
|
||||
|
||||
/// Returns true if the current platform has a native implementation.
|
||||
/// On Web this is always false; on non-Android platforms this calls the
|
||||
/// native `isSupported` method to let the platform decide.
|
||||
bool get isSupported {
|
||||
if (kIsWeb) return false;
|
||||
// Synchronous check is not possible via MethodChannel; instead we rely on
|
||||
// the static per-platform capability flags defined at the package root.
|
||||
// See [BleRelayHostCapability.isSupported].
|
||||
return defaultTargetPlatform == TargetPlatform.android;
|
||||
}
|
||||
|
||||
/// Starts BLE advertising and opens the GATT server.
|
||||
///
|
||||
/// [advertisedName] — the Bluetooth device name visible to scanning clients.
|
||||
/// [onClientWrite] — callback invoked whenever a connected client writes a
|
||||
/// frame to the relay characteristic. Forward the bytes to
|
||||
/// the upstream DALI transport.
|
||||
Future<void> start({
|
||||
required String advertisedName,
|
||||
required Future<void> Function(Uint8List data) onClientWrite,
|
||||
}) async {
|
||||
if (!isSupported) return;
|
||||
_onClientWrite = onClientWrite;
|
||||
|
||||
// Listen for inbound writes from BLE clients via event channel.
|
||||
const eventChannel = EventChannel('$_kChannelName/clientWrites');
|
||||
_eventSub?.cancel();
|
||||
_eventSub = eventChannel.receiveBroadcastStream().listen((dynamic event) {
|
||||
if (event is Uint8List) {
|
||||
_onClientWrite?.call(event);
|
||||
} else if (event is List) {
|
||||
_onClientWrite?.call(Uint8List.fromList(event.cast<int>()));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await _channel.invokeMethod<void>('start', {
|
||||
'advertisedName': advertisedName,
|
||||
'serviceUuid': kBleRelayServiceUuid,
|
||||
'characteristicUuid': kBleRelayCharacteristicUuid,
|
||||
});
|
||||
_running = true;
|
||||
} catch (_) {
|
||||
await _eventSub?.cancel();
|
||||
_eventSub = null;
|
||||
_onClientWrite = null;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends [data] as a GATT notification to all connected clients.
|
||||
Future<void> push(Uint8List data) async {
|
||||
if (!_running) return;
|
||||
await _channel.invokeMethod<void>('push', {'data': data});
|
||||
}
|
||||
|
||||
/// Stops advertising, disconnects clients, and closes the GATT server.
|
||||
Future<void> stop() async {
|
||||
await _eventSub?.cancel();
|
||||
_eventSub = null;
|
||||
_onClientWrite = null;
|
||||
if (_running) {
|
||||
await _channel.invokeMethod<void>('stop');
|
||||
}
|
||||
_running = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user