feat(train): rowing on the WP 5000 over FTMS (ADR 0030, D28)
Rowing indoors is offered again, with the rower chosen on the Record sheet: a sheet looks for Fitness Machine Service rowers, and the one chosen is remembered in the person's folder. The recorder service connects to it as to the strap (one NotifyLink for both), parses FTMS's Rower Data in Kotlin (tested on the JVM on frames written from the specification and D8's), counts its totals from its first frame, pauses 5 s after the last stroke, and gets the connectedDevice type. A row's screen shows its metres, split per 500 m, stroke rate and watts; its page its average split, stroke rate and power, the highest power, its strokes, splits per 500 m and charts of the split, stroke rate and power. Not yet tried on the WP 5000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
de5fd43e06
commit
f3d08e4e69
5 changed files with 2266 additions and 514 deletions
|
|
@ -94,11 +94,40 @@
|
|||
"strapUnnamed": "Brustgurt ohne Namen",
|
||||
"strapForget": "Brustgurt vergessen",
|
||||
"strapRefused": "Train braucht Geräte in der Nähe, um deinen Brustgurt zu finden. Du kannst das in den Systemeinstellungen erlauben.",
|
||||
"strapBluetoothOff": "Bluetooth ist aus. Schalte es ein, um deinen Brustgurt zu finden.",
|
||||
"strapBluetoothOff": "Bluetooth ist aus. Schalte es ein, um nach Geräten in der Nähe zu suchen.",
|
||||
"recordingHeartRate": "Herzfrequenz",
|
||||
"recordingStrapLost": "Brustgurt nicht verbunden",
|
||||
"workoutAverageHeartRate": "mittlere Herzfrequenz",
|
||||
"workoutMaxHeartRate": "höchste Herzfrequenz",
|
||||
"workoutBpmValue": "{bpm} bpm",
|
||||
"workoutHeartRateChart": "Herzfrequenz"
|
||||
"workoutHeartRateChart": "Herzfrequenz",
|
||||
|
||||
"recordRower": "Rudergerät",
|
||||
"recordNoRower": "Wähle dein Rudergerät",
|
||||
"recordRowerRefused": "Train braucht Geräte in der Nähe, um von deinem Rudergerät aufzuzeichnen. Du kannst das in den Systemeinstellungen erlauben.",
|
||||
"rowerSheetTitle": "Rudergerät",
|
||||
"rowerSearching": "Suche Rudergeräte in der Nähe …",
|
||||
"rowerSearchingHint": "Weck die Konsole: Die meisten erscheinen, sobald du ziehst oder eine Taste drückst. Eines, das eine andere App hält (Christopeit Connect, Kinomap), ist nicht zu finden: Schließ diese App zuerst.",
|
||||
"rowerUnnamed": "Rudergerät ohne Namen",
|
||||
"rowerForget": "Rudergerät vergessen",
|
||||
"rowerRefused": "Train braucht Geräte in der Nähe, um dein Rudergerät zu finden. Du kannst das in den Systemeinstellungen erlauben.",
|
||||
"recordingRowerConnecting": "Suche das Rudergerät",
|
||||
"recordingRowerLost": "Rudergerät nicht verbunden",
|
||||
"recordingSplit": "Zeit pro 500 m",
|
||||
"recordingStrokeRate": "Schläge pro Minute",
|
||||
"recordingWatts": "Watt",
|
||||
"workoutSplitValue": "{split} /500 m",
|
||||
"workoutAverageSplit": "mittlere Zeit pro 500 m",
|
||||
"workoutAverageStrokeRate": "mittlere Schlagzahl",
|
||||
"workoutStrokeRateValue": "{rate} /min",
|
||||
"workoutAveragePower": "mittlere Leistung",
|
||||
"workoutMaxPower": "höchste Leistung",
|
||||
"workoutWattsValue": "{watts} W",
|
||||
"workoutStrokes": "Schläge",
|
||||
"workoutSplitMetres": "{from}–{to} m",
|
||||
"workoutSplitChart": "Zeit pro 500 m",
|
||||
"workoutStrokeRateChart": "Schlagzahl",
|
||||
"workoutPowerChart": "Leistung",
|
||||
"recordAutoPauseRowingHint": "Wenn du aufhörst zu rudern, weiter mit dem nächsten Schlag",
|
||||
"recordingMetres": "Meter"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@
|
|||
"@strapUnnamed": {"description": "A strap that sends no name."},
|
||||
"strapForget": "Forget strap",
|
||||
"strapRefused": "Train needs nearby devices to find your strap. You can allow it in the system's settings.",
|
||||
"strapBluetoothOff": "Bluetooth is off. Turn it on to find your strap.",
|
||||
"strapBluetoothOff": "Bluetooth is off. Turn it on to look for devices nearby.",
|
||||
"recordingHeartRate": "heart rate",
|
||||
"@recordingHeartRate": {"description": "Under the heart rate, lower case."},
|
||||
"recordingStrapLost": "Strap lost",
|
||||
|
|
@ -131,5 +131,48 @@
|
|||
"@workoutMaxHeartRate": {"description": "Lower case, under a figure."},
|
||||
"workoutBpmValue": "{bpm} bpm",
|
||||
"@workoutBpmValue": {"placeholders": {"bpm": {"type": "int"}}},
|
||||
"workoutHeartRateChart": "Heart rate"
|
||||
"workoutHeartRateChart": "Heart rate",
|
||||
|
||||
"recordRower": "Rowing machine",
|
||||
"recordNoRower": "Choose your rowing machine",
|
||||
"recordRowerRefused": "Train needs nearby devices to record from your rowing machine. You can allow it in the system's settings.",
|
||||
"rowerSheetTitle": "Rowing machine",
|
||||
"rowerSearching": "Looking for rowing machines nearby…",
|
||||
"rowerSearchingHint": "Wake the console: most show once you pull or press a button. One another app holds (Christopeit Connect, Kinomap) can't be found: close that app first.",
|
||||
"rowerUnnamed": "Unnamed rowing machine",
|
||||
"rowerForget": "Forget rowing machine",
|
||||
"rowerRefused": "Train needs nearby devices to find your rowing machine. You can allow it in the system's settings.",
|
||||
"recordingRowerConnecting": "Looking for the rowing machine",
|
||||
"@recordingRowerConnecting": {"description": "While recording a row: the rower isn't connected yet. Another app may hold it."},
|
||||
"recordingRowerLost": "Rowing machine lost",
|
||||
"@recordingRowerLost": {"description": "While recording a row: the rower is out of reach or off; Train keeps asking for it."},
|
||||
"recordingSplit": "split",
|
||||
"@recordingSplit": {"description": "Lower case, under a figure: how long 500 m take now."},
|
||||
"recordingStrokeRate": "strokes a minute",
|
||||
"@recordingStrokeRate": {"description": "Lower case, under a figure."},
|
||||
"recordingWatts": "watts",
|
||||
"@recordingWatts": {"description": "Lower case, under a figure."},
|
||||
"workoutSplitValue": "{split} /500 m",
|
||||
"@workoutSplitValue": {"description": "Minutes and seconds 500 m take.", "placeholders": {"split": {"type": "String", "example": "2:04"}}},
|
||||
"workoutAverageSplit": "average split",
|
||||
"@workoutAverageSplit": {"description": "Lower case, under a figure."},
|
||||
"workoutAverageStrokeRate": "average stroke rate",
|
||||
"@workoutAverageStrokeRate": {"description": "Lower case, under a figure."},
|
||||
"workoutStrokeRateValue": "{rate} /min",
|
||||
"@workoutStrokeRateValue": {"description": "Strokes a minute.", "placeholders": {"rate": {"type": "String", "example": "24"}}},
|
||||
"workoutAveragePower": "average power",
|
||||
"@workoutAveragePower": {"description": "Lower case, under a figure."},
|
||||
"workoutMaxPower": "highest power",
|
||||
"@workoutMaxPower": {"description": "Lower case, under a figure."},
|
||||
"workoutWattsValue": "{watts} W",
|
||||
"@workoutWattsValue": {"placeholders": {"watts": {"type": "int"}}},
|
||||
"workoutStrokes": "strokes",
|
||||
"@workoutStrokes": {"description": "Lower case, under a figure: how many strokes."},
|
||||
"workoutSplitMetres": "{from}–{to} m",
|
||||
"@workoutSplitMetres": {"description": "A rowing split of 500 m: where it starts and ends.", "placeholders": {"from": {"type": "int"}, "to": {"type": "int"}}},
|
||||
"workoutSplitChart": "Split",
|
||||
"workoutStrokeRateChart": "Stroke rate",
|
||||
"workoutPowerChart": "Power",
|
||||
"recordAutoPauseRowingHint": "When you stop rowing, going on with the next stroke",
|
||||
"recordingMetres": "metres"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @strapBluetoothOff.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Bluetooth is off. Turn it on to find your strap.'**
|
||||
/// **'Bluetooth is off. Turn it on to look for devices nearby.'**
|
||||
String get strapBluetoothOff;
|
||||
|
||||
/// Under the heart rate, lower case.
|
||||
|
|
@ -657,6 +657,174 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Heart rate'**
|
||||
String get workoutHeartRateChart;
|
||||
|
||||
/// No description provided for @recordRower.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Rowing machine'**
|
||||
String get recordRower;
|
||||
|
||||
/// No description provided for @recordNoRower.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose your rowing machine'**
|
||||
String get recordNoRower;
|
||||
|
||||
/// No description provided for @recordRowerRefused.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Train needs nearby devices to record from your rowing machine. You can allow it in the system\'s settings.'**
|
||||
String get recordRowerRefused;
|
||||
|
||||
/// No description provided for @rowerSheetTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Rowing machine'**
|
||||
String get rowerSheetTitle;
|
||||
|
||||
/// No description provided for @rowerSearching.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Looking for rowing machines nearby…'**
|
||||
String get rowerSearching;
|
||||
|
||||
/// No description provided for @rowerSearchingHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Wake the console: most show once you pull or press a button. One another app holds (Christopeit Connect, Kinomap) can\'t be found: close that app first.'**
|
||||
String get rowerSearchingHint;
|
||||
|
||||
/// No description provided for @rowerUnnamed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unnamed rowing machine'**
|
||||
String get rowerUnnamed;
|
||||
|
||||
/// No description provided for @rowerForget.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Forget rowing machine'**
|
||||
String get rowerForget;
|
||||
|
||||
/// No description provided for @rowerRefused.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Train needs nearby devices to find your rowing machine. You can allow it in the system\'s settings.'**
|
||||
String get rowerRefused;
|
||||
|
||||
/// While recording a row: the rower isn't connected yet. Another app may hold it.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Looking for the rowing machine'**
|
||||
String get recordingRowerConnecting;
|
||||
|
||||
/// While recording a row: the rower is out of reach or off; Train keeps asking for it.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Rowing machine lost'**
|
||||
String get recordingRowerLost;
|
||||
|
||||
/// Lower case, under a figure: how long 500 m take now.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'split'**
|
||||
String get recordingSplit;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'strokes a minute'**
|
||||
String get recordingStrokeRate;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'watts'**
|
||||
String get recordingWatts;
|
||||
|
||||
/// Minutes and seconds 500 m take.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{split} /500 m'**
|
||||
String workoutSplitValue(String split);
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'average split'**
|
||||
String get workoutAverageSplit;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'average stroke rate'**
|
||||
String get workoutAverageStrokeRate;
|
||||
|
||||
/// Strokes a minute.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{rate} /min'**
|
||||
String workoutStrokeRateValue(String rate);
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'average power'**
|
||||
String get workoutAveragePower;
|
||||
|
||||
/// Lower case, under a figure.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'highest power'**
|
||||
String get workoutMaxPower;
|
||||
|
||||
/// No description provided for @workoutWattsValue.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{watts} W'**
|
||||
String workoutWattsValue(int watts);
|
||||
|
||||
/// Lower case, under a figure: how many strokes.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'strokes'**
|
||||
String get workoutStrokes;
|
||||
|
||||
/// A rowing split of 500 m: where it starts and ends.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{from}–{to} m'**
|
||||
String workoutSplitMetres(int from, int to);
|
||||
|
||||
/// No description provided for @workoutSplitChart.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Split'**
|
||||
String get workoutSplitChart;
|
||||
|
||||
/// No description provided for @workoutStrokeRateChart.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stroke rate'**
|
||||
String get workoutStrokeRateChart;
|
||||
|
||||
/// No description provided for @workoutPowerChart.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Power'**
|
||||
String get workoutPowerChart;
|
||||
|
||||
/// No description provided for @recordAutoPauseRowingHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'When you stop rowing, going on with the next stroke'**
|
||||
String get recordAutoPauseRowingHint;
|
||||
|
||||
/// No description provided for @recordingMetres.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'metres'**
|
||||
String get recordingMetres;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get strapBluetoothOff =>
|
||||
'Bluetooth ist aus. Schalte es ein, um deinen Brustgurt zu finden.';
|
||||
'Bluetooth ist aus. Schalte es ein, um nach Geräten in der Nähe zu suchen.';
|
||||
|
||||
@override
|
||||
String get recordingHeartRate => 'Herzfrequenz';
|
||||
|
|
@ -354,4 +354,100 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get workoutHeartRateChart => 'Herzfrequenz';
|
||||
|
||||
@override
|
||||
String get recordRower => 'Rudergerät';
|
||||
|
||||
@override
|
||||
String get recordNoRower => 'Wähle dein Rudergerät';
|
||||
|
||||
@override
|
||||
String get recordRowerRefused =>
|
||||
'Train braucht Geräte in der Nähe, um von deinem Rudergerät aufzuzeichnen. Du kannst das in den Systemeinstellungen erlauben.';
|
||||
|
||||
@override
|
||||
String get rowerSheetTitle => 'Rudergerät';
|
||||
|
||||
@override
|
||||
String get rowerSearching => 'Suche Rudergeräte in der Nähe …';
|
||||
|
||||
@override
|
||||
String get rowerSearchingHint =>
|
||||
'Weck die Konsole: Die meisten erscheinen, sobald du ziehst oder eine Taste drückst. Eines, das eine andere App hält (Christopeit Connect, Kinomap), ist nicht zu finden: Schließ diese App zuerst.';
|
||||
|
||||
@override
|
||||
String get rowerUnnamed => 'Rudergerät ohne Namen';
|
||||
|
||||
@override
|
||||
String get rowerForget => 'Rudergerät vergessen';
|
||||
|
||||
@override
|
||||
String get rowerRefused =>
|
||||
'Train braucht Geräte in der Nähe, um dein Rudergerät zu finden. Du kannst das in den Systemeinstellungen erlauben.';
|
||||
|
||||
@override
|
||||
String get recordingRowerConnecting => 'Suche das Rudergerät';
|
||||
|
||||
@override
|
||||
String get recordingRowerLost => 'Rudergerät nicht verbunden';
|
||||
|
||||
@override
|
||||
String get recordingSplit => 'Zeit pro 500 m';
|
||||
|
||||
@override
|
||||
String get recordingStrokeRate => 'Schläge pro Minute';
|
||||
|
||||
@override
|
||||
String get recordingWatts => 'Watt';
|
||||
|
||||
@override
|
||||
String workoutSplitValue(String split) {
|
||||
return '$split /500 m';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutAverageSplit => 'mittlere Zeit pro 500 m';
|
||||
|
||||
@override
|
||||
String get workoutAverageStrokeRate => 'mittlere Schlagzahl';
|
||||
|
||||
@override
|
||||
String workoutStrokeRateValue(String rate) {
|
||||
return '$rate /min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutAveragePower => 'mittlere Leistung';
|
||||
|
||||
@override
|
||||
String get workoutMaxPower => 'höchste Leistung';
|
||||
|
||||
@override
|
||||
String workoutWattsValue(int watts) {
|
||||
return '$watts W';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutStrokes => 'Schläge';
|
||||
|
||||
@override
|
||||
String workoutSplitMetres(int from, int to) {
|
||||
return '$from–$to m';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutSplitChart => 'Zeit pro 500 m';
|
||||
|
||||
@override
|
||||
String get workoutStrokeRateChart => 'Schlagzahl';
|
||||
|
||||
@override
|
||||
String get workoutPowerChart => 'Leistung';
|
||||
|
||||
@override
|
||||
String get recordAutoPauseRowingHint =>
|
||||
'Wenn du aufhörst zu rudern, weiter mit dem nächsten Schlag';
|
||||
|
||||
@override
|
||||
String get recordingMetres => 'Meter';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get strapBluetoothOff =>
|
||||
'Bluetooth is off. Turn it on to find your strap.';
|
||||
'Bluetooth is off. Turn it on to look for devices nearby.';
|
||||
|
||||
@override
|
||||
String get recordingHeartRate => 'heart rate';
|
||||
|
|
@ -351,6 +351,102 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get workoutHeartRateChart => 'Heart rate';
|
||||
|
||||
@override
|
||||
String get recordRower => 'Rowing machine';
|
||||
|
||||
@override
|
||||
String get recordNoRower => 'Choose your rowing machine';
|
||||
|
||||
@override
|
||||
String get recordRowerRefused =>
|
||||
'Train needs nearby devices to record from your rowing machine. You can allow it in the system\'s settings.';
|
||||
|
||||
@override
|
||||
String get rowerSheetTitle => 'Rowing machine';
|
||||
|
||||
@override
|
||||
String get rowerSearching => 'Looking for rowing machines nearby…';
|
||||
|
||||
@override
|
||||
String get rowerSearchingHint =>
|
||||
'Wake the console: most show once you pull or press a button. One another app holds (Christopeit Connect, Kinomap) can\'t be found: close that app first.';
|
||||
|
||||
@override
|
||||
String get rowerUnnamed => 'Unnamed rowing machine';
|
||||
|
||||
@override
|
||||
String get rowerForget => 'Forget rowing machine';
|
||||
|
||||
@override
|
||||
String get rowerRefused =>
|
||||
'Train needs nearby devices to find your rowing machine. You can allow it in the system\'s settings.';
|
||||
|
||||
@override
|
||||
String get recordingRowerConnecting => 'Looking for the rowing machine';
|
||||
|
||||
@override
|
||||
String get recordingRowerLost => 'Rowing machine lost';
|
||||
|
||||
@override
|
||||
String get recordingSplit => 'split';
|
||||
|
||||
@override
|
||||
String get recordingStrokeRate => 'strokes a minute';
|
||||
|
||||
@override
|
||||
String get recordingWatts => 'watts';
|
||||
|
||||
@override
|
||||
String workoutSplitValue(String split) {
|
||||
return '$split /500 m';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutAverageSplit => 'average split';
|
||||
|
||||
@override
|
||||
String get workoutAverageStrokeRate => 'average stroke rate';
|
||||
|
||||
@override
|
||||
String workoutStrokeRateValue(String rate) {
|
||||
return '$rate /min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutAveragePower => 'average power';
|
||||
|
||||
@override
|
||||
String get workoutMaxPower => 'highest power';
|
||||
|
||||
@override
|
||||
String workoutWattsValue(int watts) {
|
||||
return '$watts W';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutStrokes => 'strokes';
|
||||
|
||||
@override
|
||||
String workoutSplitMetres(int from, int to) {
|
||||
return '$from–$to m';
|
||||
}
|
||||
|
||||
@override
|
||||
String get workoutSplitChart => 'Split';
|
||||
|
||||
@override
|
||||
String get workoutStrokeRateChart => 'Stroke rate';
|
||||
|
||||
@override
|
||||
String get workoutPowerChart => 'Power';
|
||||
|
||||
@override
|
||||
String get recordAutoPauseRowingHint =>
|
||||
'When you stop rowing, going on with the next stroke';
|
||||
|
||||
@override
|
||||
String get recordingMetres => 'metres';
|
||||
}
|
||||
|
||||
/// The translations for English, as used in the United Kingdom (`en_GB`).
|
||||
|
|
|
|||
Loading…
Reference in a new issue