Commit e8dac6d
Changed files (6)
app
lib
components
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