Commit e8dac6d

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-03-23 00:12:37
prototype ble measurement input logic
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent becf884
app/lib/components/ble_input/ble_input_bloc.dart
@@ -0,0 +1,85 @@
+import 'dart:async';
+
+import 'package:blood_pressure_app/components/ble_input/ble_input_events.dart';
+import 'package:blood_pressure_app/components/ble_input/ble_input_state.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
+
+// TODO: docs
+class BleInputBloc extends Bloc<BleInputEvent, BleInputState> {
+  final _ble = FlutterReactiveBle();
+  final Set<DiscoveredDevice> _availableDevices = {};
+
+  final _requiredServices = [
+    Uuid.parse('1810'),
+  ];
+  // TODO: use repo
+
+  BleInputBloc(): super(BleInputClosed()) {
+    on<BleInputOpened>((event, emit) async {
+      try {
+        emit(BleInputLoadInProgress());
+        await _ble.initialize();
+        final deviceStream = _ble.scanForDevices(withServices: _requiredServices,);
+        await emit.forEach(deviceStream, onData: (DiscoveredDevice device) {
+          _availableDevices.add(device);
+          return BleInputLoadSuccess(_availableDevices.toList());
+        },);
+      } catch (e) { // TODO: ask for permission
+        // TODO: check its really this type of exception
+        emit(BleInputLoadFailure());
+      }
+    });
+    on<BleInputDeviceSelected>((event, emit) async {
+      emit(BleConnectInProgress());
+      try {
+        final connectionUpdateStream = _ble.connectToAdvertisingDevice(
+          id: event.device.id,
+          prescanDuration: const Duration(seconds: 5),
+          withServices: _requiredServices,
+          connectionTimeout: const Duration(minutes: 2),
+        );
+        await emit.forEach(connectionUpdateStream,
+          onData: (ConnectionStateUpdate update) {
+            if (update.failure != null) {
+              return BleConnectFailed();
+            } else if (update.connectionState == DeviceConnectionState.connected) {
+              // characteristics IDs (https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf?v=1711151578821):
+              // - Blood Pressure Measurement: 0x2A35 (https://bitbucket.org/bluetooth-SIG/public/src/main/gss/org.bluetooth.characteristic.blood_pressure_measurement.yaml)
+              // - Blood Pressure Records: 0x2A36 (https://bitbucket.org/bluetooth-SIG/public/src/main/gss/org.bluetooth.characteristic.blood_pressure_record.yaml)
+              //
+              // A record represents a stored measurement, so in theory we should
+              // search for a measurement.
+              // Definition: https://www.bluetooth.com/specifications/bls-1-1-1/
+              final characteristic = QualifiedCharacteristic(
+                characteristicId: Uuid.parse('2A35'),
+                serviceId: Uuid.parse('1810'),
+                deviceId: event.device.id,
+              );
+              _ble.subscribeToCharacteristic(characteristic).listen((event) {
+                // TODO: decode byte array and create measurement
+              });
+
+              // TODO: move reading code
+
+
+              return BleConnectSuccess();
+            } else if (update.connectionState == DeviceConnectionState.connecting) {
+              return BleConnectInProgress();
+            }
+            return BleConnectFailed();
+          },
+        );
+      } on TimeoutException {
+        emit(BleConnectFailed());
+      }
+
+    });
+    // TODO: use _ble.statusStream
+
+    // TODO: check if information about measurement start can be obtained
+    // through bluetooth
+
+  }
+
+}
\ No newline at end of file
app/lib/components/ble_input/ble_input_events.dart
@@ -0,0 +1,10 @@
+// TODO document
+import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
+
+sealed class BleInputEvent {}
+class BleInputOpened extends BleInputEvent {}
+class BleInputDeviceSelected extends BleInputEvent {
+  BleInputDeviceSelected(this.device);
+
+  final DiscoveredDevice device;
+}
app/lib/components/ble_input/ble_input_state.dart
@@ -0,0 +1,33 @@
+// TODO: doc
+import 'package:blood_pressure_app/model/blood_pressure/record.dart';
+import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
+
+sealed class BleInputState {}
+
+/// The ble input field is inactive.
+class BleInputClosed extends BleInputState {}
+
+/// Scanning for devices.
+class BleInputLoadInProgress extends BleInputState {}
+/// No device available.
+class BleInputLoadFailure extends BleInputState {}
+/// Found devices.
+class BleInputLoadSuccess extends BleInputState {
+  BleInputLoadSuccess(this.availableDevices);
+
+  final List<DiscoveredDevice> availableDevices;
+}
+
+/// Connecting to device.
+class BleConnectInProgress extends BleInputState {}
+/// Couldn't connect to device or closed connection.
+class BleConnectFailed extends BleInputState {}
+/// Is connected with device.
+class BleConnectSuccess extends BleInputState {}
+
+/// A measurement was taken through the bluetooth device.
+class BleMeasureSuccess extends BleInputState {
+  BleMeasureSuccess(this.record);
+
+  final BloodPressureRecord record;
+}
app/pubspec.lock
@@ -57,6 +57,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.10"
+  bloc:
+    dependency: transitive
+    description:
+      name: bloc
+      sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.3"
   boolean_selector:
     dependency: transitive
     description:
@@ -222,6 +230,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_bloc:
+    dependency: "direct main"
+    description:
+      name: flutter_bloc
+      sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.1.4"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -251,6 +267,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.17"
+  flutter_reactive_ble:
+    dependency: "direct main"
+    description:
+      name: flutter_reactive_ble
+      sha256: "247e2efa76de203d1ba11335c13754b5b9d0504b5423e5b0c93a600f016b24e0"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.3.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -277,6 +301,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.9.1"
+  functional_data:
+    dependency: transitive
+    description:
+      name: functional_data
+      sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
   glob:
     dependency: transitive
     description:
@@ -525,6 +557,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.7.4"
+  protobuf:
+    dependency: transitive
+    description:
+      name: protobuf
+      sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.0"
   provider:
     dependency: "direct main"
     description:
@@ -549,6 +589,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.0.1"
+  reactive_ble_mobile:
+    dependency: transitive
+    description:
+      name: reactive_ble_mobile
+      sha256: "9ec2b4c9c725e439950838d551579750060258fbccd5536d0543b4d07d225798"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.3.1"
+  reactive_ble_platform_interface:
+    dependency: transitive
+    description:
+      name: reactive_ble_platform_interface
+      sha256: "632c92401a2d69c9b94bd48f8fd47488a7013f3d1f9b291884350291a4a81813"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.3.1"
   restart_app:
     dependency: "direct main"
     description:
app/pubspec.yaml
@@ -32,6 +32,8 @@ dependencies:
   restart_app: ^1.2.1
   fluttertoast: ^8.2.4
   sqlparser: ^0.34.1
+  flutter_bloc: ^8.1.4
+  flutter_reactive_ble: ^5.3.1
 
 dev_dependencies:
   file: any
blood_pressure_app.iml
@@ -145,5 +145,6 @@
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="Dart SDK" level="project" />
     <orderEntry type="library" name="Flutter Plugins" level="project" />
+    <orderEntry type="library" name="Dart Packages" level="project" />
   </component>
 </module>
\ No newline at end of file