diff --git a/lib/src/constants.dart b/lib/src/constants.dart index e20e21a..370dccd 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -40,3 +40,4 @@ const String buttonStateCharacteristicUuid = const String ledServiceUuid = "81040a2e-4819-11ee-be56-0242ac120002"; const String ledSetStateCharacteristic = "81040e7a-4819-11ee-be56-0242ac120002"; +const String sensorErrorCharacteristicUuid = "1234567c-1234-5678-9abc-def123456789"; \ No newline at end of file diff --git a/lib/src/models/devices/open_earable_v2.dart b/lib/src/models/devices/open_earable_v2.dart index e2b617d..a7d0b4c 100644 --- a/lib/src/models/devices/open_earable_v2.dart +++ b/lib/src/models/devices/open_earable_v2.dart @@ -4,7 +4,7 @@ import 'dart:typed_data'; import 'package:open_earable_flutter/src/constants.dart'; import 'package:open_earable_flutter/src/models/devices/bluetooth_wearable.dart'; import 'package:pub_semver/pub_semver.dart'; - +import 'package:open_earable_flutter/src/models/error/sensor_error.dart'; import '../../../open_earable_flutter.dart' hide Version; import '../../managers/v2_sensor_handler.dart'; import '../capabilities/device_firmware_version.dart'; @@ -90,6 +90,31 @@ class OpenEarableV2 extends BluetoothWearable @override bool get isConnectedViaSystem => _isConnectedViaSystem; +final _errorController = StreamController.broadcast(); +Stream get onError => _errorController.stream; +// Add this method after the _errorController declaration +void _subscribeToErrorNotifications() { + bleManager + .subscribe( + deviceId: deviceId, + serviceId: sensorServiceUuid, + characteristicId: sensorErrorCharacteristicUuid, + ) + .listen( + (data) { + try { + final error = SensorError.fromBytes(Uint8List.fromList(data)); + logger.i('Received sensor error: $error'); + _errorController.add(error); + } catch (e) { + logger.e('Failed to parse sensor error: $e'); + } + }, + onError: (error) { + logger.e('Error in error notification stream: $error'); + }, + ); +} @override Stream> get sensorConfigurationStream { @@ -279,7 +304,10 @@ class OpenEarableV2 extends BluetoothWearable bool isConnectedViaSystem = false, }) : _sensors = sensors, _sensorConfigurations = sensorConfigurations, - _isConnectedViaSystem = isConnectedViaSystem; + _isConnectedViaSystem = isConnectedViaSystem { + // ADD THIS LINE HERE + _subscribeToErrorNotifications(); +} @override String get deviceId => discoveredDevice.id; diff --git a/lib/src/models/devices/wearable.dart b/lib/src/models/devices/wearable.dart index fdc2ac9..79d35f7 100644 --- a/lib/src/models/devices/wearable.dart +++ b/lib/src/models/devices/wearable.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:ui'; import '../../managers/wearable_disconnect_notifier.dart'; - +import 'package:open_earable_flutter/src/models/error/sensor_error.dart'; enum WearableIconVariant { single, left, @@ -44,7 +44,7 @@ abstract class Wearable { } return null; } - +Stream get onError => const Stream.empty(); /// Gets a specific capability of the wearable, throwing a StateError if not supported. T requireCapability() { final capability = getCapability(); diff --git a/lib/src/models/error/sensor_error.dart b/lib/src/models/error/sensor_error.dart new file mode 100644 index 0000000..c3a947f --- /dev/null +++ b/lib/src/models/error/sensor_error.dart @@ -0,0 +1,60 @@ +import 'dart:typed_data'; + +class SensorError { + final int errorCode; + final int sensorId; + final int timestamp; + final String message; + + SensorError({ + required this.errorCode, + required this.sensorId, + required this.timestamp, + required this.message, + }); + + factory SensorError.fromBytes(Uint8List bytes) { + if (bytes.length < 70) { + throw Exception('Invalid error data length: ${bytes.length}'); + } + + return SensorError( + errorCode: bytes[0], + sensorId: bytes[1], + timestamp: (bytes[5] << 24) | + (bytes[4] << 16) | + (bytes[3] << 8) | + bytes[2], + message: String.fromCharCodes(bytes.sublist(6, 70)).trim(), + ); + } + + String get errorDescription { + switch (errorCode) { + case 0x01: return 'Error streaming is initialized'; + case 0x02: return 'Sensor read failed'; + case 0x03: return 'SD card error'; + case 0x04: return 'Audio playback failed'; + case 0x05: return 'BLE notification failed'; + case 0xFF: return 'Steaming failed the queue is full!'; + default: return 'Unknown error (code: ${errorCode.toRadixString(16)})'; + } + } + + String get sensorName { + switch (sensorId) { + case 0: return 'IMU'; + case 1: return 'Barometer'; + case 2: return 'PPG'; + case 3: return 'Optic Temp'; + case 4: return 'Bone Conduction'; + case 5: return 'Microphone'; + default: return 'System'; + } + } + + String get formattedMessage => '[$sensorName] $errorDescription: $message'; + + @override + String toString() => formattedMessage; +} \ No newline at end of file