Untitled
unknown
plain_text
2 years ago
8.4 kB
8
Indexable
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:polar/polar.dart';
import 'package:uuid/uuid.dart';
import 'package:self_science_station/provider/sensor_provider.dart';
import 'package:self_science_station/model/sensor_model.dart';
import 'package:self_science_station/sensor/sensorInstance.dart';
import 'package:flutter/services.dart';
class PolarAPI extends SensorInstance {
//Function _onSensorChange;
final identifier;
final polarModel;
final polar = Polar();
final logs = ['Service started'];
PolarExerciseEntry? exerciseEntry;
//SensorProvider sp;
// A constructor that calls the super constructor with the model instance and type parameters
// -- this shouldn't be necessary when all child-instances are created by the super-class SensorProvider
//PolarAPI(SensorInstanceModel sInstance, SensorTypeModel sType) : super(SensorInstanceModel sInstance, SensorTypeModel sType);
PolarAPI(super.sp, super.sInstance, super.sType, this.identifier,
this.polarModel) {
initState();
}
void initState() async {
await polar.requestPermissions();
// Disconnect device upon instantiation in case it was connected
await disconnectSensor();
polar.batteryLevel.listen((e) => log('Battery: ${e.level}'));
polar.deviceConnecting.listen((_) {
print("CONNECTING POLAR EVENT");
onConnecting();
});
polar.deviceConnected.listen((_) {
print("CONNECTED POLAR EVENT");
onConnected();
});
polar.deviceDisconnected.listen((_) {
print("DISCONNECTED POLAR EVENT");
if (!isConnecting) {
onDisconnected();
}
});
}
@override
Future<bool> connectSensor() async {
await disconnectSensor();
onConnecting();
log('Connecting to device: $identifier');
try {
await polar.connectToDevice(identifier);
} catch (e) {
log('Failed to connect device $identifier: ${e.toString()}');
onConnectFail();
return false;
}
return true;
}
@override
void connectSensorWithTimeout(Duration timeout) async {
print("Connecting sensor $identifier with timeout $timeout");
await disconnectSensor();
print("Disconnected before connecting again");
onConnecting();
await polar.connectToDevice(identifier);
print("Called connectToDevice");
Future.delayed(timeout, () {
if (!isConnected) {
print("Connection timeout reached. Bailing.");
onConnectFail();
}
});
}
@override
Future<bool> disconnectSensor() async {
log('Disconnecting from device: $identifier');
try {
await polar.disconnectFromDevice(identifier);
} catch (e) {
log('Failed to disconnect device $identifier: ${e.toString()}');
return false;
}
onDisconnected();
return true;
}
@override
Future<bool> startRecording(String? recordingIdentifier) async {
if (recordingIdentifier == null) {
log('No recording identifier provided. Aborting recording.');
return false;
}
log('Starting recording');
await polar.startRecording(
identifier,
exerciseId: recordingIdentifier,
interval: RecordingInterval.interval_1s,
sampleType: SampleType.rr,
);
onRecordingStarted();
log('Started recording');
return true;
}
@override
Future<bool> stopRecording() async {
log('Stopping recording');
await polar.stopRecording(identifier);
onRecordingStopped();
log('Stopped recording');
return true;
}
@override
Future<bool> isRecording() async {
log('Getting recording status');
try {
final status = await polar.requestRecordingStatus(identifier);
log('Recording status: $status');
print(status);
return status.ongoing;
} catch (e) {
if (e is PlatformException) {
print(
"Error when trying to get recording status. isRecording set to false. Error: ${e.message}");
return false;
}
}
return false;
}
@override
Future<List<int>> fetchData(String? recordingIdentifier) async {
print('Fetching recording with id $recordingIdentifier');
final entries = await polar.listExercises(identifier);
for (var entry in entries) {
if (entry.entryId == recordingIdentifier) {
final data = await polar.fetchExercise(identifier, entry);
print('Fetched recording: $data');
// We no longer need this data to be taking space on the device
await polar.removeExercise(identifier, entry);
return data.samples;
}
}
return [];
}
@override
Future<void> emptyRecordings() async {
print('Emptying recordings');
final entries = await polar.listExercises(identifier);
for (var entry in entries) {
await polar.removeExercise(identifier, entry);
}
}
@override
Future<void> resetSensor() async {
print('Resetting sensor $identifier');
await polar.doFactoryReset(identifier, true /* preserve pairing info */);
}
@override
StreamSubscription<PolarDeviceInfo> scanForSensor(Function onDeviceFound) {
return polar.searchForDevice().listen((e) {
log('Device found from scan: ${e.deviceId}, isConnectable: ${e.isConnectable}');
if (e.isConnectable) {
onDeviceFound(e.deviceId);
}
});
}
// For Online Streaming. Polar has an offline API but it doesn't seem available through this flutter wrapper
void streamWhenReady() async {
await polar.sdkFeatureReady.firstWhere(
(e) =>
e.identifier == identifier &&
e.feature ==
PolarSdkFeature
.onlineStreaming, // Online, but it continues offline if disconnected by itself
);
final availabletypes =
await polar.getAvailableOnlineStreamDataTypes(identifier);
debugPrint('available types: $availabletypes');
if (availabletypes.contains(PolarDataType.hr)) {
polar
.startHrStreaming(identifier)
.listen((e) => log('Heart rate: ${e.samples.map((e) => e.hr)}'));
}
if (availabletypes.contains(PolarDataType.ecg)) {
polar
.startEcgStreaming(identifier)
.listen((e) => log('ECG data received'));
}
if (availabletypes.contains(PolarDataType.acc)) {
polar
.startAccStreaming(identifier)
.listen((e) => log('ACC data received'));
}
}
void log(String log) {
// ignore: avoid_print
print(log);
}
Future<void> _handleRecordingAction(RecordingAction action) async {
switch (action) {
case RecordingAction.start:
log('Starting recording');
await polar.startRecording(
identifier,
exerciseId: const Uuid().v4(),
interval: RecordingInterval.interval_1s,
sampleType: SampleType.rr,
);
onRecordingStarted();
log('Started recording');
break;
case RecordingAction.stop:
log('Stopping recording');
await polar.stopRecording(identifier);
onRecordingStopped();
log('Stopped recording');
break;
case RecordingAction.status:
log('Getting recording status');
final status = await polar.requestRecordingStatus(identifier);
log('Recording status: $status');
// TODO update the sensor state depending on the status
break;
case RecordingAction.list:
log('Listing recordings');
final entries = await polar.listExercises(identifier);
log('Recordings: $entries');
// H10 can only store one recording at a time
exerciseEntry = entries.first;
break;
case RecordingAction.fetch:
log('Fetching recording');
if (exerciseEntry == null) {
log('Exercises not yet listed');
await _handleRecordingAction(RecordingAction.list);
}
final entry = await polar.fetchExercise(identifier, exerciseEntry!);
log('Fetched recording: $entry');
break;
case RecordingAction.remove:
log('Removing recording');
if (exerciseEntry == null) {
log('No exercise to remove. Try calling list first.');
return;
}
await polar.removeExercise(identifier, exerciseEntry!);
log('Removed recording');
break;
}
}
}
enum RecordingAction {
start,
stop,
status,
list,
fetch,
remove,
}
Editor is loading...
Leave a Comment