From 859890b5bd22dca9774bf253ea88aa3c08012dce Mon Sep 17 00:00:00 2001
From: Dennis <45356478+DennisMoschina@users.noreply.github.com>
Date: Mon, 9 Dec 2024 13:49:11 +0100
Subject: [PATCH 01/12] added rough implementation of OpenEarable v2
---
example/.metadata | 30 +-
example/android/.gitignore | 2 +-
example/android/app/build.gradle | 53 +-
.../android/app/src/main/AndroidManifest.xml | 12 +
.../com/example/example/MainActivity.kt | 3 +-
example/android/build.gradle | 17 +-
example/android/gradle.properties | 2 +-
.../gradle/wrapper/gradle-wrapper.properties | 2 +-
example/android/settings.gradle | 19 +-
example/pubspec.lock | 8 +
example/test/widget_test.dart | 30 ++
lib/open_earable_flutter.dart | 54 +-
lib/src/managers/audio_player.dart | 154 ++++++
lib/src/managers/rgb_led.dart | 42 ++
lib/src/managers/sensor_manager.dart | 303 +++++++++++
lib/src/models/devices/open_earable_v2.dart | 496 ++++++++++++++++++
lib/src/models/discovered_device.dart | 23 +
pubspec.yaml | 1 +
18 files changed, 1165 insertions(+), 86 deletions(-)
create mode 100644 example/test/widget_test.dart
create mode 100644 lib/src/managers/audio_player.dart
create mode 100644 lib/src/managers/rgb_led.dart
create mode 100644 lib/src/managers/sensor_manager.dart
create mode 100644 lib/src/models/devices/open_earable_v2.dart
create mode 100644 lib/src/models/discovered_device.dart
diff --git a/example/.metadata b/example/.metadata
index de53984..2d1be89 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: "e1e47221e86272429674bec4f1bd36acc4fc7b77"
+ revision: "2663184aa79047d0a33a14a3b607954f8fdd8730"
channel: "stable"
project_type: app
@@ -13,26 +13,26 @@ project_type: app
migration:
platforms:
- platform: root
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: android
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: ios
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: linux
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: macos
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: web
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
- platform: windows
- create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
- base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77
+ create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
+ base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730
# User provided section
diff --git a/example/android/.gitignore b/example/android/.gitignore
index 6f56801..55afd91 100644
--- a/example/android/.gitignore
+++ b/example/android/.gitignore
@@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
-# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 118ee1d..b5511a9 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -1,67 +1,44 @@
plugins {
id "com.android.application"
id "kotlin-android"
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
-}
-
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
-}
-
android {
- namespace "com.example.example"
- compileSdkVersion flutter.compileSdkVersion
- ndkVersion flutter.ndkVersion
+ namespace = "com.example.example"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
- jvmTarget = '1.8'
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "com.example.example"
+ applicationId = "com.example.example"
// You can update the following values to match your application needs.
- // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion flutter.minSdkVersion
- targetSdkVersion flutter.targetSdkVersion
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
- signingConfig signingConfigs.debug
+ signingConfig = signingConfigs.debug
}
}
}
flutter {
- source '../..'
+ source = "../.."
}
-
-dependencies {}
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index a66e8e3..aa355bc 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
+ android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
@@ -30,6 +31,17 @@
android:name="flutterEmbedding"
android:value="2" />
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
index e793a00..70f8f08 100644
--- a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
+++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
@@ -2,5 +2,4 @@ package com.example.example
import io.flutter.embedding.android.FlutterActivity
-class MainActivity: FlutterActivity() {
-}
+class MainActivity: FlutterActivity()
diff --git a/example/android/build.gradle b/example/android/build.gradle
index f7eb7f6..d2ffbff 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,16 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.7.10'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.3.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
google()
@@ -18,12 +5,12 @@ allprojects {
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
- project.evaluationDependsOn(':app')
+ project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index 94adc3a..2597170 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index 3c472b9..7bb2df6 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 55c4ca8..b9e43bd 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -5,16 +5,21 @@ pluginManagement {
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
- }
- settings.ext.flutterSdkPath = flutterSdkPath()
+ }()
- includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
- plugins {
- id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
}
}
-include ":app"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.1.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.8.22" apply false
+}
-apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+include ":app"
diff --git a/example/pubspec.lock b/example/pubspec.lock
index a6bbe60..adcce7c 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -176,6 +176,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ logger:
+ dependency: transitive
+ description:
+ name: logger
+ sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.0"
logging:
dependency: transitive
description:
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
new file mode 100644
index 0000000..092d222
--- /dev/null
+++ b/example/test/widget_test.dart
@@ -0,0 +1,30 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:example/main.dart';
+
+void main() {
+ testWidgets('Counter increments smoke test', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(const MyApp());
+
+ // Verify that our counter starts at 0.
+ expect(find.text('0'), findsOneWidget);
+ expect(find.text('1'), findsNothing);
+
+ // Tap the '+' icon and trigger a frame.
+ await tester.tap(find.byIcon(Icons.add));
+ await tester.pump();
+
+ // Verify that our counter has incremented.
+ expect(find.text('0'), findsNothing);
+ expect(find.text('1'), findsOneWidget);
+ });
+}
diff --git a/lib/open_earable_flutter.dart b/lib/open_earable_flutter.dart
index cc02cb0..3d79979 100644
--- a/lib/open_earable_flutter.dart
+++ b/lib/open_earable_flutter.dart
@@ -2,6 +2,8 @@ library open_earable_flutter;
import 'dart:async';
+import 'package:logger/logger.dart';
+import 'package:open_earable_flutter/src/models/devices/open_earable_v2.dart';
import 'package:universal_ble/universal_ble.dart';
import 'src/managers/ble_manager.dart';
@@ -27,6 +29,12 @@ export 'src/models/capabilities/storage_path_audio_player.dart';
part 'src/constants.dart';
+Logger _logger = Logger();
+
+const String _deviceInfoServiceUuid = "45622510-6468-465a-b141-0b9b0f96b468";
+const String _deviceFirmwareVersionCharacteristicUuid =
+ "45622512-6468-465a-b141-0b9b0f96b468";
+
class WearableManager {
static final WearableManager _instance = WearableManager._internal();
@@ -59,12 +67,46 @@ class WearableManager {
disconnectNotifier.notifyListeners,
);
if (connectionResult.$1) {
- return OpenEarableV1(
- name: device.name,
- disconnectNotifier: disconnectNotifier,
- bleManager: _bleManager,
- discoveredDevice: device,
- );
+ _logger.d("found following BLEServices: ${connectionResult.$2}");
+
+ if (connectionResult.$2.any((service) => service.uuid == _deviceInfoServiceUuid)) {
+ List softwareGenerationBytes = await _bleManager.read(
+ deviceId: device.id,
+ serviceId: _deviceInfoServiceUuid,
+ characteristicId: _deviceFirmwareVersionCharacteristicUuid,
+ );
+ String softwareVersion = String.fromCharCodes(softwareGenerationBytes);
+ _logger.i("Softare version: $softwareVersion");
+
+ final versionRegex = RegExp(r'^\d+\.\d+\.\d+$');
+ if (!versionRegex.hasMatch(softwareVersion)) {
+ throw Exception('Invalid software version format');
+ }
+
+ final version1Regex = RegExp(r'^1\.\d+\.\d+$');
+ if (version1Regex.hasMatch(softwareVersion)) {
+ return OpenEarableV1(
+ name: device.name,
+ disconnectNotifier: disconnectNotifier,
+ bleManager: _bleManager,
+ discoveredDevice: device,
+ );
+ }
+
+ final v2Regex = RegExp(r'^\d+\.\d+.\d+$');
+ if (v2Regex.hasMatch(softwareVersion)) {
+ return OpenEarableV2(
+ name: device.name,
+ disconnectNotifier: disconnectNotifier,
+ bleManager: _bleManager,
+ discoveredDevice: device,
+ );
+ }
+
+ throw Exception('Unsupported Firmware Version');
+ } else {
+ throw Exception('Unsupported Device');
+ }
} else {
throw Exception('Failed to connect to device');
}
diff --git a/lib/src/managers/audio_player.dart b/lib/src/managers/audio_player.dart
new file mode 100644
index 0000000..6846539
--- /dev/null
+++ b/lib/src/managers/audio_player.dart
@@ -0,0 +1,154 @@
+part of open_earable_flutter;
+
+/// A class that manages the playback of audio on the OpenEarable device from
+/// an audio file on the SD card.
+///
+/// This class provides functionality for controlling and interacting with an
+/// audio player via Bluetooth Low Energy (BLE) communication. It allows you
+/// to send commands to the audio player.
+class AudioPlayer {
+ /// The BleManager instance used for Bluetooth communication.
+ final BleManager _bleManager;
+
+ /// Creates an [AudioPlayer] instance with the provided [bleManager].
+ ///
+ /// The [bleManager] is required for handling BLE communication.
+ AudioPlayer({required BleManager bleManager}) : _bleManager = bleManager;
+
+ /// Plays a WAV file with the specified [fileName].
+ ///
+ /// Example usage:
+ /// ```dart
+ /// setWavState('mySound.wav');
+ /// ```
+ void wavFile(String fileName) {
+ int type = 1; // 1 indicates it's a WAV file
+ Uint8List data = prepareData(type, fileName);
+ _bleManager.write(
+ serviceId: audioPlayerServiceUuid,
+ characteristicId: audioSourceCharacteristic,
+ byteData: data,
+ );
+ }
+
+ /// Plays a sound with a specified frequency and waveform.
+ ///
+ /// This method is used to generate and play sounds with a specific [waveType], [frequency] and [loudness]
+ /// Possible waveforms are:
+ ///
+ /// - 0: Sine.
+ /// - 1: Triangle.
+ /// - 2: Square.
+ /// - 3: Sawtooth.
+ ///
+ /// loudness must be between 0.0 - 1.0
+ ///
+ /// Example usage:
+ /// ```dart
+ /// setFrequencyState(1, 440.0, 1.0);
+ /// ```
+ void frequency(int waveType, double frequency, double loudness) {
+ int type = 2; // 2 indicates it's a frequency
+ var data = Uint8List(10);
+ data[0] = type;
+ data[1] = waveType;
+
+ var freqBytes = Float32List.fromList([frequency]);
+ var loudnessBytes = Float32List.fromList([loudness]);
+ data.setAll(2, freqBytes.buffer.asUint8List());
+ data.setAll(6, loudnessBytes.buffer.asUint8List());
+
+ _bleManager.write(
+ serviceId: audioPlayerServiceUuid,
+ characteristicId: audioSourceCharacteristic,
+ byteData: data,
+ );
+ }
+
+ /// Plays a jingle or short musical sound with [jingleId].
+ ///
+ /// following jingles are supported:
+ ///
+ /// 0: 'IDLE'
+ /// 1: 'NOTIFICATION'
+ /// 2: 'SUCCESS'
+ /// 3: 'ERROR'
+ /// 4: 'ALARM'
+ /// 5: 'PING'
+ /// 6: 'OPEN'
+ /// 7: 'CLOSE'
+ /// 8: 'CLICK'
+ ///
+ void jingle(int jingleId) {
+ int type = 3; // 3 indicates it's a jingle
+ Uint8List data = Uint8List(2);
+ data[0] = type;
+ data[1] = jingleId;
+ _bleManager.write(
+ serviceId: audioPlayerServiceUuid,
+ characteristicId: audioSourceCharacteristic,
+ byteData: data,
+ );
+ }
+
+ Uint8List prepareData(int type, String name) {
+ List nameBytes = utf8.encode(name);
+ Uint8List data = Uint8List(2 + nameBytes.length);
+ data[0] = type;
+ data[1] = nameBytes.length;
+ data.setRange(2, 2 + nameBytes.length, nameBytes);
+ return data;
+ }
+
+ /// Writes the audio state to the OpenEarable.
+ ///
+ /// The [state] parameter represents the playback state and should be one of
+ /// the following values from the [AudioPlayerState] enum:
+ ///
+ /// - [WavPlayerState.stop]: Stops audio playback.
+ /// - [WavPlayerState.start]: Starts audio playback.
+ /// - [WavPlayerState.pause]: Pauses audio playback.
+ /// - [WavPlayerState.unpause]: unpauses audio playback.
+ ///
+ void setState(AudioPlayerState state) async {
+ Uint8List data = Uint8List(1);
+ data[0] = getAudioPlayerStateValue(state);
+ await _bleManager.write(
+ serviceId: audioPlayerServiceUuid,
+ characteristicId: audioStateCharacteristic,
+ byteData: data,
+ );
+ }
+
+ /// Sets the audio player to the idle state.
+ ///
+ /// The audio player transitions to the idle state,
+ /// indicating that it is not currently playing any sound.
+ void setIdle() {
+ //_writeAudioPlayerState(SoundType.idle, AudioPlayerState.idle, ""); //TODO
+ }
+}
+
+int getAudioPlayerStateValue(AudioPlayerState state) {
+ switch(state) {
+ case AudioPlayerState.idle: return 0;
+ case AudioPlayerState.start: return 1;
+ case AudioPlayerState.pause: return 2;
+ case AudioPlayerState.stop: return 3;
+ }
+}
+
+/// An enumeration representing the possible states of the audio player.
+enum AudioPlayerState {
+ /// Idle state.
+ idle,
+
+ /// Play the audio file.
+ start,
+
+ /// Pause the audio file.
+ pause,
+
+ /// Stop the audio file.
+ stop,
+}
diff --git a/lib/src/managers/rgb_led.dart b/lib/src/managers/rgb_led.dart
new file mode 100644
index 0000000..7c65e7f
--- /dev/null
+++ b/lib/src/managers/rgb_led.dart
@@ -0,0 +1,42 @@
+part of open_earable_flutter;
+
+/// The `RgbLed` class provides methods to control the RGB LED on an OpenEarable device.
+///
+/// You can use this class to set the LED state to control its color and behavior.
+class RgbLed {
+ final BleManager _bleManager;
+
+ /// Creates an instance of the `RgbLed` class with the provided `BleManager` instance.
+ ///
+ /// The `BleManager` is used for communication with the OpenEarable device.
+ RgbLed({required BleManager bleManager}) : _bleManager = bleManager;
+
+ /// Writes the state of the RGB LED on the OpenEarable device.
+ ///
+ /// Parameters:
+ /// - `r`: The red color component value (0-255) for the LED.
+ /// - `g`: The green color component value (0-255) for the LED.
+ /// - `b`: The blue color component value (0-255) for the LED.
+ ///
+ /// Use this method to easily set the color of the in-built RGB LED on the OpenEarable device.
+ Future writeLedColor(
+ {required int r, required int g, required int b}) async {
+ if (!_bleManager.connected) {
+ Exception("Can't write sensor config. Earable not connected");
+ }
+ if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
+ throw ArgumentError('The color values must be in range 0-255');
+ }
+ ByteData data = ByteData(3);
+ data.setUint8(0, r);
+ data.setUint8(1, g);
+ data.setUint8(2, b);
+ await _bleManager.write(
+ serviceId: ledServiceUuid,
+ characteristicId: ledSetStateCharacteristic,
+ byteData: data.buffer.asUint8List());
+ }
+}
+
+/// Enum representing the LED state for an RGB LED.
+enum LedState { off, green, blue, red, cyan, yellow, magenta, white }
diff --git a/lib/src/managers/sensor_manager.dart b/lib/src/managers/sensor_manager.dart
new file mode 100644
index 0000000..483bea4
--- /dev/null
+++ b/lib/src/managers/sensor_manager.dart
@@ -0,0 +1,303 @@
+part of open_earable_flutter;
+
+/// Manages sensor-related functionality for the OpenEarable device.
+class SensorManager {
+ final imuID = 0;
+ final BleManager _bleManager;
+ final MahonyAHRS _mahonyAHRS = MahonyAHRS();
+ List? _sensorSchemes;
+
+ /// Creates a [SensorManager] instance with the specified [bleManager].
+ SensorManager({required BleManager bleManager}) : _bleManager = bleManager;
+
+ /// Writes the sensor configuration to the OpenEarable device.
+ ///
+ /// The [sensorConfig] parameter contains the sensor id, sampling rate
+ /// and latency of the sensor.
+ Future writeSensorConfig(OpenEarableSensorConfig sensorConfig) async {
+ if (!_bleManager.connected) {
+ Exception("Can't write sensor config. Earable not connected");
+ }
+ await _bleManager.write(
+ serviceId: sensorServiceUuid,
+ characteristicId: sensorConfigurationCharacteristicUuid,
+ byteData: sensorConfig.byteList);
+ if (_sensorSchemes == null) {
+ await _readSensorScheme();
+ }
+ }
+
+ /// Subscribes to sensor data for a specific sensor.
+ ///
+ /// The [sensorId] parameter specifies the ID of the sensor to subscribe to.
+ /// - 0: IMU data
+ /// - 1: Barometer data
+ /// Returns a [Stream] of sensor data as a [Map] of sensor values.
+ Stream