Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export 'src/knx_usb_hid_support.dart';
|
||||
export 'src/knx_usb_hid_transport.dart';
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:knx_stack/knx_stack.dart';
|
||||
|
||||
final class KnxUsbHidSupport {
|
||||
const KnxUsbHidSupport._();
|
||||
|
||||
static KnxRuntimeSupport current({KnxPlatform? platform}) {
|
||||
final resolvedPlatform = platform ?? currentKnxPlatform();
|
||||
|
||||
final transportSupport = switch (resolvedPlatform) {
|
||||
KnxPlatform.android ||
|
||||
KnxPlatform.macos ||
|
||||
KnxPlatform.linux ||
|
||||
KnxPlatform.windows => KnxFeatureSupport.supported(
|
||||
feature: KnxFeature.usbHidTransport,
|
||||
platform: resolvedPlatform,
|
||||
reason: 'usb_hid exposes a native HID implementation on this platform.',
|
||||
),
|
||||
KnxPlatform.web => KnxFeatureSupport.conditional(
|
||||
feature: KnxFeature.usbHidTransport,
|
||||
platform: resolvedPlatform,
|
||||
reason: 'Requires a browser with WebHID support and a user-triggered device picker.',
|
||||
),
|
||||
KnxPlatform.ios => KnxFeatureSupport.unsupported(
|
||||
feature: KnxFeature.usbHidTransport,
|
||||
platform: resolvedPlatform,
|
||||
reason: 'usb_hid does not provide an iOS implementation.',
|
||||
),
|
||||
KnxPlatform.fuchsia ||
|
||||
KnxPlatform.unknown => KnxFeatureSupport.unsupported(
|
||||
feature: KnxFeature.usbHidTransport,
|
||||
platform: resolvedPlatform,
|
||||
reason: 'No KNX USB HID adapter is implemented for this runtime.',
|
||||
),
|
||||
};
|
||||
|
||||
return KnxRuntimeSupport.base(platform: resolvedPlatform).merge(
|
||||
KnxRuntimeSupport(platform: resolvedPlatform, features: [transportSupport]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:knx_stack/knx_stack.dart';
|
||||
import 'package:usb_hid/usb_hid.dart';
|
||||
|
||||
import 'knx_usb_hid_support.dart';
|
||||
|
||||
final class KnxUsbHidTransport implements KnxTransport {
|
||||
KnxUsbHidTransport({
|
||||
UsbHid? usbHid,
|
||||
this.filters = const [],
|
||||
HidDeviceInfo? selectedDevice,
|
||||
}) : _usbHid = usbHid ?? UsbHid(),
|
||||
_selectedDevice = selectedDevice;
|
||||
|
||||
final UsbHid _usbHid;
|
||||
final List<HidDeviceFilter> filters;
|
||||
final StreamController<Uint8List> _frames = StreamController<Uint8List>.broadcast();
|
||||
|
||||
HidDeviceInfo? _selectedDevice;
|
||||
HidDevice? _device;
|
||||
StreamSubscription<HidInputReport>? _subscription;
|
||||
bool _isConnected = false;
|
||||
|
||||
@override
|
||||
String get transportId => 'knx_usb_hid';
|
||||
|
||||
@override
|
||||
KnxFeature get feature => KnxFeature.usbHidTransport;
|
||||
|
||||
@override
|
||||
KnxFeatureSupport get support => KnxUsbHidSupport.current().supportFor(feature);
|
||||
|
||||
@override
|
||||
bool get isConnected => _isConnected;
|
||||
|
||||
@override
|
||||
Stream<Uint8List> get inboundFrames => _frames.stream;
|
||||
|
||||
HidDeviceInfo? get selectedDevice => _selectedDevice;
|
||||
|
||||
Future<List<HidDeviceInfo>> listDevices() {
|
||||
return _usbHid.listDevices(filters: filters.isEmpty ? null : filters);
|
||||
}
|
||||
|
||||
Future<HidDeviceInfo?> requestDevice() async {
|
||||
support.requireSupported('requestDevice');
|
||||
if (filters.isEmpty) {
|
||||
throw StateError('requestDevice requires at least one HID filter.');
|
||||
}
|
||||
|
||||
_selectedDevice = await _usbHid.requestDevice(filters: filters);
|
||||
return _selectedDevice;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect() async {
|
||||
support.requireSupported('connect');
|
||||
if (_isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
final deviceInfo = _selectedDevice;
|
||||
if (deviceInfo == null) {
|
||||
throw StateError('No KNX HID device selected. Call requestDevice() or pass selectedDevice first.');
|
||||
}
|
||||
|
||||
final device = await _usbHid.openDevice(deviceInfo);
|
||||
_device = device;
|
||||
_subscription = device.inputReports.listen((report) {
|
||||
_frames.add(Uint8List.fromList(report.data));
|
||||
});
|
||||
_isConnected = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect() async {
|
||||
await _subscription?.cancel();
|
||||
_subscription = null;
|
||||
final device = _device;
|
||||
_device = null;
|
||||
_isConnected = false;
|
||||
if (device != null) {
|
||||
await device.close();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> send(Uint8List frame) async {
|
||||
support.requireSupported('send');
|
||||
final device = _device;
|
||||
if (device == null) {
|
||||
throw StateError('Transport is not connected.');
|
||||
}
|
||||
|
||||
await device.sendOutputReport(frame);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user