Skip to content

Dataplex api #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 139 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
d845634
begin de-future-ifying the values
MoralCode May 10, 2022
ac60bfe
add and use classes for Time and Duration-stamped packets
MoralCode May 10, 2022
2ff4a79
split workout summary into a second class for the part 2 data
MoralCode May 10, 2022
f2d3c6a
remove completer fields and split into appropriate classes
MoralCode May 10, 2022
f60c3c0
syntax and stuff
MoralCode May 10, 2022
9b16ef5
use fromBytes in super call
MoralCode May 10, 2022
1f9f93d
move WorkoutSummary to internal packets folder
MoralCode May 10, 2022
4e0f13d
move base packet classes to a base file
MoralCode May 10, 2022
26bc846
update helpers import
MoralCode May 10, 2022
035a177
bring in status data stub classes
MoralCode May 10, 2022
1fec0b1
document the data multiplex thing
MoralCode May 15, 2022
6d905e2
create a basic passthrough wrapper for a stream controller
MoralCode May 15, 2022
4ffa22d
rename and expand on dartdoc
MoralCode May 15, 2022
306bcac
add datapoint identifiers field
MoralCode May 15, 2022
730927e
deprecate monitorForWorkoutSummary
MoralCode May 15, 2022
876f86e
move src underneath lib per https://dart.dev/guides/libraries/create-…
MoralCode May 15, 2022
514b919
move definitions from internal to src folder
MoralCode May 15, 2022
567e14d
add and hook up initial stubs of the internal dataplex class
MoralCode May 15, 2022
2b2758f
add basic stream creation flow to Dataplex
MoralCode May 15, 2022
c0ae411
pass peripheral to dataplex
MoralCode May 15, 2022
5b14581
add some basic methods for creating new subscriptions to data from th…
MoralCode May 15, 2022
9da47eb
add a base class covering all concept2 data packet types
MoralCode May 15, 2022
5d9b70c
inject a data identifier into packets that dont have it
MoralCode May 15, 2022
a6680c4
fix fromBytes WorkoutSummary Constructor
MoralCode May 15, 2022
90d99b4
rename workoutsummary to workoutsummarypacket
MoralCode May 15, 2022
0551f9a
uncomment packet type parsing function and update types
MoralCode May 15, 2022
fe2340e
parse packet type when reading incoming packet from erg
MoralCode May 15, 2022
8226ec6
rename second packet
MoralCode May 15, 2022
17d4158
update byte parsing syntax for WorkoutSummaryPacket2
MoralCode May 15, 2022
380943b
add TODO notes for expanding statusdata types
MoralCode May 15, 2022
6e2d0c9
update second workout summary packet to inherit from TimestampedData
MoralCode May 15, 2022
60a5be8
remove string implementation from workoutSummary
MoralCode May 15, 2022
e90ffe6
add stub for workout summary packet 3
MoralCode May 15, 2022
65be514
stub out the remaining types
MoralCode May 16, 2022
40e7594
rename identifyPacket function
MoralCode May 16, 2022
7a9ce26
add packet parsing logic
MoralCode May 16, 2022
e70f7e1
define type for custom streamcontroller
MoralCode May 16, 2022
ee03d5e
apply filtering when adding values to the stream
MoralCode May 16, 2022
5a4e24f
generate close listener/callback
MoralCode May 16, 2022
6105ea3
add todo for converting packet to a map
MoralCode May 16, 2022
74fab4e
add maps to constants for mapping the different keys to the character…
MoralCode May 16, 2022
7f1f8c0
replace nonexistnet third packet with force curve data instead (thats…
MoralCode May 19, 2022
8ba7175
remove IdempotentCompleter helper
MoralCode May 19, 2022
5677591
return workout summary packets as maps
MoralCode May 21, 2022
07d79be
create unit test for parsing the CSV data
MoralCode May 21, 2022
adf00a5
use packet parsing/identifying function in helpers
MoralCode May 22, 2022
7ae43f5
refactor CSV retrieval into function
MoralCode May 22, 2022
fbf1694
rename DurationStampedDate -> ElapsedTimeStampedData
MoralCode May 22, 2022
119bcc4
remove printing from parsing helper
MoralCode May 22, 2022
cb11f79
use central parsing helper and separate printing
MoralCode May 22, 2022
04d9906
parse elapsedTime from more packets
MoralCode May 22, 2022
4d188e7
parse more datapoints out of segmentData
MoralCode May 22, 2022
7312975
refactor segment data to pull out a shared data field for segment Number
MoralCode May 22, 2022
38803d5
its called elapsed time in the spec i think
MoralCode May 25, 2022
919a3d9
move datatypes test to new folder
MoralCode Jun 4, 2022
7194842
start adding data keys to a constants file
MoralCode Jun 4, 2022
4740e1d
start implementing asMap() for WorkoutSummaries
MoralCode Jun 4, 2022
eccf9a7
send data to outgoing streams
MoralCode Jun 8, 2022
b89c436
add basic map of characteristics to packets
MoralCode Jun 8, 2022
97fe1d2
add some docs and tweak error messages
MoralCode Jun 8, 2022
23af539
add stub for stream validation function
MoralCode Jun 8, 2022
bc51344
import constants
MoralCode Jun 8, 2022
271b62e
close output streams too
MoralCode Jun 8, 2022
c1cf68d
change UUID to type mao into a UUID to dataKey map
MoralCode Jul 4, 2022
0b57d66
start implementing validation logic
MoralCode Jul 4, 2022
623270f
add datapointIdentifiers getter to WorkourSummary packets
MoralCode Jul 4, 2022
04a1af6
docs and some comments
MoralCode Jul 9, 2022
78f2f35
install reactive_ble
MoralCode Nov 11, 2022
345b7ad
first pass at migrating bluetooth scanning over to reactive ble
MoralCode Nov 11, 2022
707a6b9
update connection and IO related stuff to use reactive
MoralCode Nov 11, 2022
e2179dc
add flutterReactiveBle class
MoralCode Nov 11, 2022
d001e45
combine MonitorConnectionState with connection function
MoralCode Nov 11, 2022
a3cbd3a
converted to reactive calls and removed various errors
Aldaniee Nov 12, 2022
de7d632
make flutterReactiveBle property private
MoralCode Nov 12, 2022
e639294
fix other uses of the flutterReactiveBle field
MoralCode Nov 15, 2022
dae2d91
bump minsdkversion on android to make the example app compile
MoralCode Nov 15, 2022
89bb035
remove now-unused flutter_ble_lib dependency
MoralCode Nov 15, 2022
e07ba7f
add android bluetooth permissions to demo app
MoralCode Nov 15, 2022
305f47b
other android build parameter changes
MoralCode Nov 15, 2022
4a32c11
request permissions android
MoralCode Nov 15, 2022
3788c11
misc updates to settings, mainly concerning hooking up cocoapods
MoralCode Nov 16, 2022
d2750b5
xcode recommended updates
MoralCode Nov 16, 2022
3b63796
more pod stuff maybe?
MoralCode Nov 16, 2022
f6ce67a
commit podfile
MoralCode Nov 16, 2022
89b8880
fixed scanning issue and added scan button
Aldaniee Nov 16, 2022
9ae13ca
code cleaning
Aldaniee Nov 17, 2022
1942a5f
some updates to README
MoralCode Nov 16, 2022
91aad2d
missed setState
Aldaniee Nov 17, 2022
339db59
Merge remote-tracking branch 'origin/reactive-rewrite' into reactive-…
Aldaniee Nov 17, 2022
712e537
ignore use of old deprecated functions
MoralCode Nov 17, 2022
897f30b
misc formatting
MoralCode Nov 17, 2022
5ebad8b
unused import
MoralCode Nov 18, 2022
a5b5698
add location permission to android manifest
MoralCode Nov 19, 2022
42a3c85
remove old doc comment
MoralCode Nov 19, 2022
0801f51
update doc comments for Ergometer and ErgBleManager to account for ne…
MoralCode Nov 19, 2022
d61a021
update READMEs to reflect the newer version of the demo app
MoralCode Nov 19, 2022
e5554a6
update scanning to work more asynchronously
MoralCode Nov 19, 2022
8bd7250
updates to standalone docs
MoralCode Nov 19, 2022
7b6cea1
Merge branch 'reactive-rewrite' into dataplex-api
MoralCode Nov 19, 2022
da92321
swap bluetooth backend in dataplex class
MoralCode Nov 19, 2022
d199551
make dataplex device object private
MoralCode Nov 19, 2022
d1a0d9d
remove monitorForWorkoutSummary method
MoralCode Nov 19, 2022
bc82877
remove docs on data objects (they are being replaced with the dataplex)
MoralCode Nov 19, 2022
27b8738
remove README documentation relating to how monitorForWorkoutSummary …
MoralCode Nov 19, 2022
94d0794
Add Core Values section to API docs
MoralCode Nov 19, 2022
166d56c
rename core api concepts heading
MoralCode Nov 19, 2022
9fcdc15
tweak the "table of contents" for the new sections
MoralCode Nov 19, 2022
7def01f
more silent protector stuff
MoralCode Nov 19, 2022
e2148e5
rename segment distance to elapsedDistance
MoralCode Nov 19, 2022
a410a72
fix byte index of the segment count
MoralCode Nov 19, 2022
666bdd0
rename SegmentData to SharedSegmentData
MoralCode Nov 19, 2022
84f94ad
finish parsing out the bytes for the SegmentData packets
MoralCode Nov 19, 2022
d3f6e4f
commit silent protector image for the docs
MoralCode Nov 19, 2022
676388a
document the use of the term "segment" from SegmentData packet name
MoralCode Nov 19, 2022
40d0fec
update example app to use the new dataplex api
MoralCode Nov 19, 2022
6e05164
formatting
MoralCode Nov 22, 2022
1b41f85
add keys for SegmentData
MoralCode Dec 21, 2022
bdecbb8
add a list of all data keys to the dataplex
MoralCode Dec 21, 2022
20eace4
add definitions for segmentData.zero() functions
MoralCode Dec 21, 2022
08e7f1b
add more zero() and datapointIdentifiers functions
MoralCode Dec 24, 2022
decae2d
update elapsed time key
MoralCode Dec 24, 2022
1fd5c18
add elapsed distance key
MoralCode Dec 24, 2022
c9e8bcb
formatting for allDatapoints set
MoralCode Dec 24, 2022
dbc032f
include elapsed time and distance in the map for workout summaries
MoralCode Dec 24, 2022
bbc16bf
use keys for monitoring for workout summary
MoralCode Dec 24, 2022
5cca58b
encapsulate Keys in a Keys class so they can be exported for end user…
MoralCode Dec 24, 2022
111afa4
monitor for interval summaries
MoralCode Dec 24, 2022
1bd9dd7
actually subscribe to receiving the data
MoralCode Dec 24, 2022
2c156cf
correct the SegmentData keys for distance
MoralCode Dec 24, 2022
26cbcc7
correct the values for erg machine type (0 is actually allowed)
MoralCode Dec 24, 2022
9fac002
add multiplexed characteristic ID to constants
MoralCode Dec 24, 2022
7e1eb4c
display both general and segment distance in the UI
MoralCode Dec 24, 2022
f489af3
remove extra parameter to sublist call when parsing workout summaries
MoralCode Dec 24, 2022
d6ea2a8
Migration to a flutter 3 android compatible config.
MonsieurRz Apr 6, 2025
a54ba74
updating gitignore from up-to-date template
MonsieurRz Apr 6, 2025
2cba017
Updating Manifest according to current state of flutter_reactive_ble
MonsieurRz Apr 6, 2025
94069c6
migrate permission handling
MonsieurRz Apr 7, 2025
c67a08d
ignore vscode folder
MonsieurRz Apr 7, 2025
5289074
fixing workout value as futures
MonsieurRz Apr 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 129 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Do not remove or rename entries in this file, only add new ones
# See https://github.com/flutter/flutter/issues/128635 for more context.

# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
Expand All @@ -15,26 +19,143 @@
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Visual Studio Code related
.classpath
.project
.settings/
.vscode/
.ccls-cache

# This file, on the master branch, should never exist or be checked-in.
#
# On a *final* release branch, that is, what will ship to stable or beta, the
# file can be force added (git add --force) and checked-in in order to effectively
# "pin" the engine artifact version so the flutter tool does not need to use git
# to determine the engine artifacts.
#
# See https://github.com/flutter/flutter/blob/main/docs/tool/Engine-artifacts.md.
/bin/internal/engine.version

# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/internal/engine.realm
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/api_docs.zip
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json

# packages file containing multi-root paths
.packages.generated

# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-preload-cache/
.pub-cache/
.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds

# Android related
**/android/**/gradle-wrapper.jar
.gradle/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
local.properties
**/.cxx/

# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*

# macOS
**/Flutter/ephemeral/
**/Pods/
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/ephemeral
**/xcuserdata/

# Windows
**/windows/flutter/ephemeral/
**/windows/flutter/generated_plugin_registrant.cc
**/windows/flutter/generated_plugin_registrant.h
**/windows/flutter/generated_plugins.cmake

# Linux
**/linux/flutter/ephemeral/
**/linux/flutter/generated_plugin_registrant.cc
**/linux/flutter/generated_plugin_registrant.h
**/linux/flutter/generated_plugins.cmake

# Coverage
coverage/

# Symbols
app.*.symbols

## Custom stuff
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json

coverage/
# Monorepo
.cipd
.gclient
.gclient_entries
.python-version
.gclient_previous_custom_vars
.gclient_previous_sync_commits
72 changes: 27 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
C2Bluetooth is a flutter package designed to provide an easy API for accessing data from Concept2 PM5 Indoor fitness machines via bluetooth. This library implements the [Concept2 Performance Monitor Bluetooth Smart Communications Interface Definition](https://www.concept2.com/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf) Specification ([newer versions](https://www.c2forum.com/viewtopic.php?f=15&t=193697#p527068) are also available). It also relies heavily on the [CSAFE specification](https://web.archive.org/web/20060718175014/http://www.fitlinxx.com/csafe/specification.htm) from FitLinxx.

## Demo
This package comes with a demo app in the `example/` directory.

![A demo showing the distance completed after a workout](docs/images/demo/demo1-small.jpg)
See the [`example/README.md`](example/README.md) for more detailed information about the demo app and how to use it.

This is a relatively old screenshot of the included example app using an older version of the library to display the completed distance from a short 20-second test workout. Many improvements to expose more datapoints have been made since this screenshot was taken.
## Key Features
## Key Library Features

Currently this library supports a few basic features such as:
- retrieving workout summary information from the erg after a workout
Expand Down Expand Up @@ -44,7 +44,6 @@ Similar to how the underlying bluetooth library works, pretty much everything be

```dart
ErgBleManager bleManager = ErgBleManager();
bleManager.init(); //ready to go!
```
### Scanning for devices
Next, you need to start scanning for available devices. This uses a Stream to return an instance of the `Ergometer` class for each erg found. Each of these instances represents an erg and should be stored for later reuse as these act as the base upon which everything else (retrieving data, sending workouts .etc) is based.
Expand All @@ -54,27 +53,43 @@ Next, you need to start scanning for available devices. This uses a Stream to re
```dart
Ergometer myErg;

bleManager.startErgScan().listen((erg) {
StreamSubscription<Ergometer> ergScanStream = bleManager.startErgScan().listen((erg) {
//your code for detecting an erg here.
myErg = erg

//you can store the erg instance somewhere
myErg = erg;

//or connect to it (see later examples)

//or stop scanning
ergScanStream.cancel();

return erg;
});
```
This block of code is where you can do things like:
- determine what erg(s) you want to work with (this can be based on name, user choice, or basicaly anything)
- store the erg instance somewhere more permanent, like the `myErg` variable to allow you to be able to access it after you stop scanning.
- call `bleManager.stopErgScan()` if you know you are done scanning early. As an example, one way to immediately connect to the first erg found is to unconditionally call `stopErgScan` within this function so the scan stops after the first erg is received. Don't forget to close the stream too!
- cancel the stream if you are done scanning.


### Connecting and disconnecting
Once you have the `Ergometer` instance for the erg you want to connect to, you can call `connectAndDiscover()` on it to connect.
Once you have the `Ergometer` instance for the erg you want to connect to, you can call `connectAndDiscover()` on it to connect. This will provide you with a stream indicating the connection state of the erg.

```dart
await myErg.connectAndDiscover();
StreamSubscription<Ergometer> ergConnectionStream = myErg.connectAndDiscover().listen((event) {
if(event == ErgometerConnectionState.connected) {
//do stuff here once the erg is connected
} else if (event == ErgometerConnectionState.disconnected) {
//handle disconnection here
}
});
}
```

When you are done, make sure to disconnect from your erg:
When you are done, disconnect from your erg by cancelling the stream:
```dart
await myErg.disconnectOrCancel();
ergConnectionStream.cancel();
```

### Getting data from the erg
Expand All @@ -99,40 +114,7 @@ To Be Implemented/Documented
To Be Implemented/Documented
#### Workout Summary data

Workout summary data can be `monitorForWorkoutSummary()`. This is a stream that returns a `WorkoutSummary` object that allows you to access the data from a completed workout (this includes programmed pieces as well as "Just row" pieces that are longer than 1 minute)

```dart
myErg.monitorForWorkoutSummary().listen((workoutSummary) {
//do whatever here
});
```

Each workout summary is a flutter object that contains the following fields:
- `Future<DateTime> timestamp`
- `Future<double> workTime`
- `Future<double> workDistance`
- `Future<int> avgSPM`
- `Future<int> endHeartRate`
- `Future<int> avgHeartRate`
- `Future<int> minHeartRate`
- `Future<int> maxHeartRate`
- `Future<int> avgDragFactor`
- `Future<int> recoveryHeartRate`
- `Future<WorkoutType> workoutType`
- `Future<double> avgPace`
- `Future<IntervalType> intervalType`
- `Future<int> intervalSize`
- `Future<int> intervalCount`
- `Future<int> totalCalories`
- `Future<int> watts`
- `Future<int> totalRestDistance`
- `Future<int> intervalRestTime`
- `Future<int> avgCalories`

Futures are handy here since the erg can send back different data at different times. The primary reason for this is the `recoveryHeartRate` field which the erg sends after the user has been resting for 1 minute. If the workout is cancelled or the erg is turned off before the end of this minute, the data may never arrive. See #10.

Overall this method of accessing workout summary data is not the most ideal, and is likely to change later if a better solution is found. See #11.

To be implemented.



Expand Down
45 changes: 26 additions & 19 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,61 @@
This document is the starting point for learning more about the c2bluetooth API and inner workings at varying levels of detail:

- The broadest overview comes from using the API as documented in the README
- sections like [Overall API Design](#overall-api-design) explain some of the core concepts or goals that we wanted to achieve with the API.
- For people looking to get into the internals of c2bluetooth, the [Core API Concepts](#core-api-concepts) section below is a good mid-level overview of the various groups or categories of classes that are used in the API and what their purpose is.
- For summaries of how c2bluetooth works internally and all the things it "takes care of" for end users, see the [internals](internals.md) document
- sections like [Core Values](#core-values) help explain more of the "why" behind some of the design choices made in the API.
- The [Basic api details](#basic-api-details) section is a detailed, mid-level overview of as man aspects of the public API as posible and what their purpose is.
- For people looking to get into the internals of c2bluetooth or make contributions, see the [internals](internals.md) document.
- Obviously the most detailed explaination of how the code works comes from reading the code and inline comments themselves. It is helpful to understand the general goals first

## Terms used

"implementor" generally refers to users of this library. This is intended to be an audience of primarily other flutter developers looking to use this library in their apps.

## Overall API design

## Core values

These are some of the core values that were considered as part of the development of this library and forms the ethos/soul of the project. These should be used as general guidance when questions of scope or direction of the library are considered.

### The Silent Protector

This principle shares its name with the [popular meme template](https://knowyourmeme.com/memes/the-silent-protector) depicting a soldier kneeling over and protecting a sleeping child.

![A remix of the "silent protector" meme depicting c2bluetooth protecting the apps that use it from low-level details of bluetooth communications with a concept2 erg](../docs/images/silent-protector.jpg)

This is essentially a graphical analogy to represent the idea that this library aims to "take on" as much responsibility for abstracting low-level details and hiding away various "gotchas" and complexities of the Concept2 Bluetooth interface specification as possible.

To my knowledge this is not currently a software design principle found in the broader software industry. However I think it is an awesome way to explain the concept.

### Inspiration
In order for this library to be a good fit within the community and provide a good user experience for developers, the goal is to design the interface for this library after other existing libraries interfacing with Concept2 rowing machines. The libraries looked at were [Py3Row](https://github.com/droogmic/Py3Row) (Python, BSD-2), [BoutFitness/Concept2-SDK](https://github.com/BoutFitness/Concept2-SDK) (Swift, MIT), [ErgometerJS](https://github.com/tijmenvangulik/ErgometerJS) (Javascript, Apache 2).

There are likely more libraries like these available, but these were just the first few that were looked at based on a GitHub search.
There are likely more libraries like these available, but these were just the first few that were looked at based on a GitHub search at the time of writing.

### Object Oriented
These three examples all seem to use some kind of Class-based approach where a particular instance of an object represents a particular rowing machine and contains functions to make interaction with the machine easier, like getting data.
These three examples all seem to use some kind of object-oriented approach where a particular instance of an object represents a particular rowing machine and contains functions to make interaction with the machine easier, like getting data.

Designing the library in an object oriented way seemed to make the most sense given what other projects in the space seem to ave done. This should also should keep things relatively straightforward to program and maintain.
Designing the library in an object oriented way seemed to make the most sense given what other projects in the space seem to have done. This should also should keep things relatively straightforward to program and maintain.

### Subscription-based data access
Both BoutFitness/Concept2-SDK and ErgometerJS also seemed to have a way to asynchronously "subscribe" to get a callback when particular values change so the screen can be updated. Since the FlutterBleLib bluetooth library also exposes [Flutter streams](https://apgapg.medium.com/using-streams-in-flutter-62fed41662e4) for reading values in this way, it seems like a good choice to follow this model when exposing data about a PM5.
Both BoutFitness/Concept2-SDK and ErgometerJS also seemed to have a way to asynchronously "subscribe" to get a callback when particular values change so values being displayed on the screen in the implementors flutter app can be updated. Since many Flutter bluetooth libraries also expose notification data from bluetooth devices as [Flutter streams](https://apgapg.medium.com/using-streams-in-flutter-62fed41662e4), this seems like a good, clean way to expose data about a PM5.

#### Single Values
For getting single values from an erg, such as serial number, software and hardware version info, and other things that likely wont change that often, Streams may be unnecessary and it might be easier to have a simple synchronous interface for grabbing a value either from the erg or from somewhere in the memory allocated to the particular Erg object being used.

Whether or not this is actually a good solution is still TBD

<!-- ### Modularity
Since a lot of the architecture is already provided by FlutterBleLib and will likely just pass through most of the aspects of the existing bluetooth APIs, it seems like it may be useful to make this passthrough more explicit. By duplicating any of the types and methods exposed by FlutterBleLib this package will be be more able to maintain a stable API, even in the event that there is a technical need (or desire from users) to be able to change the underlying bluetooth implementation, potentially even grouping the methods that handle the actual bluetooth access into a class/interface. This is something whtat would be helpful to keep in mind during initial development but shouldn't take too much energy until later versions. -->



## Core API Concepts
## Basic API details

This library is built from a few core concepts, some of which are shared with the `csafe-fitness` library. These core concepts represent general groupings of classes that serve a particular purpose or abstract certain aspects of communicating with an erg.

These concepts are roughly divided up into "external" (i.e. those that are part of the libraries public API) and "internal". If you are just using the library in your app, the external concepts should be all you need. Anyone looking to contribute to this library might find the "internal" concepts helpful

### External Concepts
#### Data Objects
Data objects, like the WorkoutSummary class, are essentially wrappers around data provided by the PM and allow the data to be accessed as an object by an application.

Data objects are primarily one-way communication from a PM to your application.

Data objects are located in the `data` directory and represent a large chunk of the public API for c2bluetooth


#### Model Objects
This is a gairly general group of classes that represent various indoor rowing conceptsas objects for ease of use by applications looking to interact with ergs. Some examples of classses in this category are the `Ergometer` and `Workout` classes. Unlike Data Objects, they are intended to be able to enable bidirectional data flow. For example, an `Ergometer` object may have properties for getting data (like Data Objects) but also may contain methods like `sendWorkout()` that allow you to provide a `Workout` object to set up on the erg. `Workout` objects could also be returned by other methods as a way to represent a workout if needed.
This is a gairly general group of classes that represent various indoor rowing concepts (in the form of objects). Some examples of classses in this category are the `Ergometer` and `Workout` classes. Unlike Data Objects, they are intended to be able to enable bidirectional data flow. For example, an `Ergometer` object may have properties for getting data (such as Data Objects) but also may contain methods like `sendWorkout()` that allow you to provide a `Workout` object to set up on the erg. `Workout` objects could also be returned by other methods as a way to represent a workout.

Model objects are located in the `models` directory and represent a large chunk of the public API for c2bluetooth
Model objects are located in the `models` directory.
Loading