feat(train): a ride reads the bike's speed and cadence sensor and power meter (ADR 0030, D49)

11d, its sensors: a ride's Record sheet offers the CSC sensor (0x1816)
and the power meter (0x1818), found nearby and remembered as the strap
is; the person's devices are now one map of DeviceKind to DeviceStore.
The service parses CSC and Cycling Power measurements (JVM-tested,
counters' rollover, coasting), writes the wheel's revolutions, the
crank's cadence and the watts to the file and to series of their own.
The recording screen shows cadence and watts; the page the power's
chart, average, highest and normalised power and the cadence's chart and
average; GPX, TCX and FIT carry them. A plan's watts target reads the
power meter too.

The wheel's speed and distance follow. Not tried with a real sensor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 15:11:20 +01:00
commit 5c13e6f3c5
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -32,6 +32,8 @@ class FakeRecorderPlatform({
Map<String, Object?>? route,
Map<String, Object?>? music,
Map<String, Object?>? announce,
String? speedSensor,
String? powerMeter,
})
>[];
@ -42,6 +44,9 @@ class FakeRecorderPlatform({
/// whether Bluetooth is on.
List<FoundDevice> nearby = [];
List<FoundDevice> nearbyRowers = [];
/// The bike's sensors nearby (D49).
List<FoundDevice> nearbyBike = [];
bool bluetoothOn = true;
/// The scans started, of which kind.
@ -88,6 +93,8 @@ class FakeRecorderPlatform({
Map<String, Object?>? route,
Map<String, Object?>? music,
Map<String, Object?>? announce,
String? speedSensor,
String? powerMeter,
}) async {
started.add((
path: path,
@ -100,6 +107,8 @@ class FakeRecorderPlatform({
route: route,
music: music,
announce: announce,
speedSensor: speedSensor,
powerMeter: powerMeter,
));
notices.add(notice);
_file = File(path)
@ -163,7 +172,11 @@ class FakeRecorderPlatform({
if (!bluetoothOn) {
return Stream.error(BluetoothOff());
}
final found = kind == DeviceKind.strap ? nearby : nearbyRowers;
final found = switch (kind) {
DeviceKind.strap => nearby,
DeviceKind.rower => nearbyRowers,
DeviceKind.speedSensor || DeviceKind.powerMeter => nearbyBike,
};
// Open until cancelled, as a scan is.
return Stream.multi((listener) => found.forEach(listener.add));
}