const int watchBridgeSchemaVersion = 5; enum WatchCommandType { startCurrentExercise, pauseSession, resumeSession, startPreparedTimedStep, completeCurrentStep, skipCurrentStep, skipCurrentPassage, finishCurrentSet, skipCurrentSet, skipCurrentRest, incrementScore, decrementScore, } enum WatchCommandAck { accepted, acceptedNoOp, rejectedStaleRevision, rejectedNotApplicable, rejectedNoActiveSession, rejectedSessionMismatch, rejectedPhoneBusy, } enum WatchSessionPhase { noActiveSession, ready, running, paused, nextTimerReady, restRunning, restPaused, betweenSetsReady, } enum WatchPrimaryAction { none, startCurrentExercise, pauseSession, resumeSession, startPreparedTimedStep, skipCurrentRest, } enum WatchSecondaryAction { skipCurrentStep, skipCurrentPassage, finishCurrentSet, skipCurrentSet, skipCurrentRest, } enum WatchTimerKind { rest, step, scoreStopwatch, setTimer } enum WatchTimerDisplayMode { countdown, elapsed } enum WatchTimerRunState { stopped, running, paused } enum WatchManualScoreScope { series, step } enum WatchStepType { time, reps } final class WatchCommandEnvelope { const WatchCommandEnvelope({ this.schemaVersion = watchBridgeSchemaVersion, required this.commandId, required this.type, required this.sessionId, required this.expectedRevision, required this.sentAtEpochMs, }); factory WatchCommandEnvelope.fromJson(Map json) { return WatchCommandEnvelope( schemaVersion: _intFromJson( json['schemaVersion'], watchBridgeSchemaVersion, ), commandId: _stringFromJson(json['commandId']), type: _enumFromJson( json['type'], WatchCommandType.values, WatchCommandType.startCurrentExercise, ), sessionId: _stringFromJson(json['sessionId']), expectedRevision: _intFromJson(json['expectedRevision'], 0), sentAtEpochMs: _intFromJson(json['sentAtEpochMs'], 0), ); } final int schemaVersion; final String commandId; final WatchCommandType type; final String sessionId; final int expectedRevision; final int sentAtEpochMs; Map toJson() { return { 'schemaVersion': schemaVersion, 'commandId': commandId, 'type': type.name, 'sessionId': sessionId, 'expectedRevision': expectedRevision, 'sentAtEpochMs': sentAtEpochMs, }; } @override bool operator ==(Object other) { return identical(this, other) || other is WatchCommandEnvelope && schemaVersion == other.schemaVersion && commandId == other.commandId && type == other.type && sessionId == other.sessionId && expectedRevision == other.expectedRevision && sentAtEpochMs == other.sentAtEpochMs; } @override int get hashCode { return Object.hash( schemaVersion, commandId, type, sessionId, expectedRevision, sentAtEpochMs, ); } } final class WatchSessionProjection { const WatchSessionProjection({ this.schemaVersion = watchBridgeSchemaVersion, required this.deviceSessionId, required this.revision, required this.projectedAtEpochMs, this.expiresAtEpochMs = 0, required this.phase, required this.phoneReachable, required this.seriesIndex, required this.seriesTotal, required this.exerciseName, this.programIndex, this.exerciseIndex, this.setIndex, this.passageIndex, this.passageTotal, this.stepIndex, this.stepTotal, this.stepName, this.stepType, this.stepTargetValue, this.dominantTimer, this.secondaryTimers = const [], required this.primaryAction, this.secondaryActions = const [], this.nextExerciseName, this.statusLabel, this.hasManualScore = false, this.currentManualScoreValue, this.canDecrementScore = false, this.manualScoreTargetValue, this.manualScoreTargetLabel, this.manualScoreRepsTargetValue, this.manualScoreScope, }); factory WatchSessionProjection.fromJson(Map json) { return WatchSessionProjection( schemaVersion: _intFromJson( json['schemaVersion'], watchBridgeSchemaVersion, ), deviceSessionId: _stringFromJson(json['deviceSessionId']), revision: _intFromJson(json['revision'], 0), projectedAtEpochMs: _intFromJson(json['projectedAtEpochMs'], 0), expiresAtEpochMs: _intFromJson(json['expiresAtEpochMs'], 0), phase: _enumFromJson( json['phase'], WatchSessionPhase.values, WatchSessionPhase.noActiveSession, ), phoneReachable: _boolFromJson(json['phoneReachable'], false), seriesIndex: _intFromJson(json['seriesIndex'], 0), seriesTotal: _intFromJson(json['seriesTotal'], 0), exerciseName: _stringFromJson(json['exerciseName']), programIndex: _nullableIntFromJson(json['programIndex']), exerciseIndex: _nullableIntFromJson(json['exerciseIndex']), setIndex: _nullableIntFromJson(json['setIndex']), passageIndex: _nullableIntFromJson(json['passageIndex']), passageTotal: _nullableIntFromJson(json['passageTotal']), stepIndex: _nullableIntFromJson(json['stepIndex']), stepTotal: _nullableIntFromJson(json['stepTotal']), stepName: _nullableStringFromJson(json['stepName']), stepType: _nullableEnumFromJson(json['stepType'], WatchStepType.values), stepTargetValue: _nullableIntFromJson(json['stepTargetValue']), dominantTimer: _timerFromJson(json['dominantTimer']), secondaryTimers: _timerListFromJson(json['secondaryTimers']), primaryAction: _enumFromJson( json['primaryAction'], WatchPrimaryAction.values, WatchPrimaryAction.none, ), secondaryActions: _enumListFromJson( json['secondaryActions'], WatchSecondaryAction.values, ), nextExerciseName: _nullableStringFromJson(json['nextExerciseName']), statusLabel: _nullableStringFromJson(json['statusLabel']), hasManualScore: _boolFromJson(json['hasManualScore'], false), currentManualScoreValue: _nullableDoubleFromJson( json['currentManualScoreValue'], ), canDecrementScore: _boolFromJson(json['canDecrementScore'], false), manualScoreTargetValue: _nullableDoubleFromJson( json['manualScoreTargetValue'], ), manualScoreTargetLabel: _nullableStringFromJson( json['manualScoreTargetLabel'], ), manualScoreRepsTargetValue: _nullableIntFromJson( json['manualScoreRepsTargetValue'], ), manualScoreScope: _nullableEnumFromJson( json['manualScoreScope'], WatchManualScoreScope.values, ), ); } final int schemaVersion; final String deviceSessionId; final int revision; final int projectedAtEpochMs; final int expiresAtEpochMs; final WatchSessionPhase phase; final bool phoneReachable; final int seriesIndex; final int seriesTotal; final String exerciseName; final int? programIndex; final int? exerciseIndex; final int? setIndex; final int? passageIndex; final int? passageTotal; final int? stepIndex; final int? stepTotal; final String? stepName; final WatchStepType? stepType; final int? stepTargetValue; final WatchTimerProjection? dominantTimer; final List secondaryTimers; final WatchPrimaryAction primaryAction; final List secondaryActions; final String? nextExerciseName; final String? statusLabel; final bool hasManualScore; final double? currentManualScoreValue; final bool canDecrementScore; final double? manualScoreTargetValue; final String? manualScoreTargetLabel; final int? manualScoreRepsTargetValue; final WatchManualScoreScope? manualScoreScope; Map toJson() { return { 'schemaVersion': schemaVersion, 'deviceSessionId': deviceSessionId, 'revision': revision, 'projectedAtEpochMs': projectedAtEpochMs, 'expiresAtEpochMs': expiresAtEpochMs, 'phase': phase.name, 'phoneReachable': phoneReachable, 'seriesIndex': seriesIndex, 'seriesTotal': seriesTotal, 'exerciseName': exerciseName, 'programIndex': programIndex, 'exerciseIndex': exerciseIndex, 'setIndex': setIndex, 'passageIndex': passageIndex, 'passageTotal': passageTotal, 'stepIndex': stepIndex, 'stepTotal': stepTotal, 'stepName': stepName, 'stepType': stepType?.name, 'stepTargetValue': stepTargetValue, 'dominantTimer': dominantTimer?.toJson(), 'secondaryTimers': secondaryTimers .map((timer) => timer.toJson()) .toList(), 'primaryAction': primaryAction.name, 'secondaryActions': secondaryActions .map((action) => action.name) .toList(), 'nextExerciseName': nextExerciseName, 'statusLabel': statusLabel, 'hasManualScore': hasManualScore, 'currentManualScoreValue': currentManualScoreValue, 'canDecrementScore': canDecrementScore, 'manualScoreTargetValue': manualScoreTargetValue, 'manualScoreTargetLabel': manualScoreTargetLabel, 'manualScoreRepsTargetValue': manualScoreRepsTargetValue, 'manualScoreScope': manualScoreScope?.name, }; } @override bool operator ==(Object other) { return identical(this, other) || other is WatchSessionProjection && schemaVersion == other.schemaVersion && deviceSessionId == other.deviceSessionId && revision == other.revision && projectedAtEpochMs == other.projectedAtEpochMs && expiresAtEpochMs == other.expiresAtEpochMs && phase == other.phase && phoneReachable == other.phoneReachable && seriesIndex == other.seriesIndex && seriesTotal == other.seriesTotal && exerciseName == other.exerciseName && programIndex == other.programIndex && exerciseIndex == other.exerciseIndex && setIndex == other.setIndex && passageIndex == other.passageIndex && passageTotal == other.passageTotal && stepIndex == other.stepIndex && stepTotal == other.stepTotal && stepName == other.stepName && stepType == other.stepType && stepTargetValue == other.stepTargetValue && dominantTimer == other.dominantTimer && _listEquals(secondaryTimers, other.secondaryTimers) && primaryAction == other.primaryAction && _listEquals(secondaryActions, other.secondaryActions) && nextExerciseName == other.nextExerciseName && statusLabel == other.statusLabel && hasManualScore == other.hasManualScore && currentManualScoreValue == other.currentManualScoreValue && canDecrementScore == other.canDecrementScore && manualScoreTargetValue == other.manualScoreTargetValue && manualScoreTargetLabel == other.manualScoreTargetLabel && manualScoreRepsTargetValue == other.manualScoreRepsTargetValue && manualScoreScope == other.manualScoreScope; } @override int get hashCode { return Object.hashAll([ schemaVersion, deviceSessionId, revision, projectedAtEpochMs, expiresAtEpochMs, phase, phoneReachable, seriesIndex, seriesTotal, exerciseName, programIndex, exerciseIndex, setIndex, passageIndex, passageTotal, stepIndex, stepTotal, stepName, stepType, stepTargetValue, dominantTimer, Object.hashAll(secondaryTimers), primaryAction, Object.hashAll(secondaryActions), nextExerciseName, statusLabel, hasManualScore, currentManualScoreValue, canDecrementScore, manualScoreTargetValue, manualScoreTargetLabel, manualScoreRepsTargetValue, manualScoreScope, ]); } } final class WatchSensorSummary { const WatchSensorSummary({ this.schemaVersion = watchBridgeSchemaVersion, required this.sessionId, required this.sampleCount, this.minHeartRateBpm, this.averageHeartRateBpm, this.maxHeartRateBpm, this.totalDistanceMeters, this.totalCaloriesKcal, }); factory WatchSensorSummary.fromJson(Map json) { return WatchSensorSummary( schemaVersion: _intFromJson( json['schemaVersion'], watchBridgeSchemaVersion, ), sessionId: _stringFromJson(json['sessionId']), sampleCount: _intFromJson(json['sampleCount'], 0), minHeartRateBpm: _nullableIntFromJson(json['minHeartRateBpm']), averageHeartRateBpm: _nullableDoubleFromJson(json['averageHeartRateBpm']), maxHeartRateBpm: _nullableIntFromJson(json['maxHeartRateBpm']), totalDistanceMeters: _nullableDoubleFromJson(json['totalDistanceMeters']), totalCaloriesKcal: _nullableDoubleFromJson(json['totalCaloriesKcal']), ); } final int schemaVersion; final String sessionId; final int sampleCount; final int? minHeartRateBpm; final double? averageHeartRateBpm; final int? maxHeartRateBpm; final double? totalDistanceMeters; final double? totalCaloriesKcal; Map toJson() { return { 'schemaVersion': schemaVersion, 'sessionId': sessionId, 'sampleCount': sampleCount, 'minHeartRateBpm': minHeartRateBpm, 'averageHeartRateBpm': averageHeartRateBpm, 'maxHeartRateBpm': maxHeartRateBpm, 'totalDistanceMeters': totalDistanceMeters, 'totalCaloriesKcal': totalCaloriesKcal, }; } @override bool operator ==(Object other) { return identical(this, other) || other is WatchSensorSummary && schemaVersion == other.schemaVersion && sessionId == other.sessionId && sampleCount == other.sampleCount && minHeartRateBpm == other.minHeartRateBpm && averageHeartRateBpm == other.averageHeartRateBpm && maxHeartRateBpm == other.maxHeartRateBpm && totalDistanceMeters == other.totalDistanceMeters && totalCaloriesKcal == other.totalCaloriesKcal; } @override int get hashCode { return Object.hash( schemaVersion, sessionId, sampleCount, minHeartRateBpm, averageHeartRateBpm, maxHeartRateBpm, totalDistanceMeters, totalCaloriesKcal, ); } } final class WatchSensorSample { const WatchSensorSample({ this.schemaVersion = watchBridgeSchemaVersion, this.sampleId, required this.sessionId, int? capturedAtEpochMs, int? recordedAtEpochMs, this.programIndex, this.exerciseIndex, this.setIndex, this.passageIndex, this.stepIndex, this.programSnapshotId, this.exerciseSnapshotId, this.stepSnapshotId, this.heartRateBpm, this.distanceMeters, this.caloriesKcal, }) : capturedAtEpochMs = capturedAtEpochMs ?? recordedAtEpochMs ?? 0; factory WatchSensorSample.fromJson(Map json) { return WatchSensorSample( schemaVersion: _intFromJson( json['schemaVersion'], watchBridgeSchemaVersion, ), sampleId: _nullableStringFromJson(json['sampleId']), sessionId: _stringFromJson(json['sessionId']), capturedAtEpochMs: _intFromJson( json['capturedAtEpochMs'], _intFromJson(json['recordedAtEpochMs'], 0), ), programIndex: _nullableIntFromJson(json['programIndex']), exerciseIndex: _nullableIntFromJson(json['exerciseIndex']), setIndex: _nullableIntFromJson(json['setIndex']), passageIndex: _nullableIntFromJson(json['passageIndex']), stepIndex: _nullableIntFromJson(json['stepIndex']), programSnapshotId: _nullableStringFromJson(json['programSnapshotId']), exerciseSnapshotId: _nullableStringFromJson(json['exerciseSnapshotId']), stepSnapshotId: _nullableStringFromJson(json['stepSnapshotId']), heartRateBpm: _nullableIntFromJson(json['heartRateBpm']), distanceMeters: _nullableDoubleFromJson(json['distanceMeters']), caloriesKcal: _nullableDoubleFromJson(json['caloriesKcal']), ); } final int schemaVersion; final String? sampleId; final String sessionId; final int capturedAtEpochMs; final int? programIndex; final int? exerciseIndex; final int? setIndex; final int? passageIndex; final int? stepIndex; final String? programSnapshotId; final String? exerciseSnapshotId; final String? stepSnapshotId; final int? heartRateBpm; final double? distanceMeters; final double? caloriesKcal; int get recordedAtEpochMs => capturedAtEpochMs; Map toJson() { return { 'schemaVersion': schemaVersion, 'sampleId': sampleId, 'sessionId': sessionId, 'capturedAtEpochMs': capturedAtEpochMs, 'recordedAtEpochMs': capturedAtEpochMs, 'programIndex': programIndex, 'exerciseIndex': exerciseIndex, 'setIndex': setIndex, 'passageIndex': passageIndex, 'stepIndex': stepIndex, 'programSnapshotId': programSnapshotId, 'exerciseSnapshotId': exerciseSnapshotId, 'stepSnapshotId': stepSnapshotId, 'heartRateBpm': heartRateBpm, 'distanceMeters': distanceMeters, 'caloriesKcal': caloriesKcal, }; } @override bool operator ==(Object other) { return identical(this, other) || other is WatchSensorSample && schemaVersion == other.schemaVersion && sampleId == other.sampleId && sessionId == other.sessionId && capturedAtEpochMs == other.capturedAtEpochMs && programIndex == other.programIndex && exerciseIndex == other.exerciseIndex && setIndex == other.setIndex && passageIndex == other.passageIndex && stepIndex == other.stepIndex && programSnapshotId == other.programSnapshotId && exerciseSnapshotId == other.exerciseSnapshotId && stepSnapshotId == other.stepSnapshotId && heartRateBpm == other.heartRateBpm && distanceMeters == other.distanceMeters && caloriesKcal == other.caloriesKcal; } @override int get hashCode { return Object.hash( schemaVersion, sampleId, sessionId, capturedAtEpochMs, programIndex, exerciseIndex, setIndex, passageIndex, stepIndex, programSnapshotId, exerciseSnapshotId, stepSnapshotId, heartRateBpm, distanceMeters, caloriesKcal, ); } } typedef WatchTelemetrySummary = WatchSensorSummary; typedef WatchTelemetrySample = WatchSensorSample; final class WatchTimerProjection { const WatchTimerProjection({ required this.kind, required this.label, required this.displayMode, required this.runState, required this.referenceEpochMs, required this.accumulatedMs, this.startedAtEpochMs, this.targetMs, }); factory WatchTimerProjection.fromJson(Map json) { return WatchTimerProjection( kind: _enumFromJson( json['kind'], WatchTimerKind.values, WatchTimerKind.setTimer, ), label: _stringFromJson(json['label']), displayMode: _enumFromJson( json['displayMode'], WatchTimerDisplayMode.values, WatchTimerDisplayMode.elapsed, ), runState: _enumFromJson( json['runState'], WatchTimerRunState.values, WatchTimerRunState.stopped, ), referenceEpochMs: _intFromJson(json['referenceEpochMs'], 0), accumulatedMs: _intFromJson(json['accumulatedMs'], 0), startedAtEpochMs: _nullableIntFromJson(json['startedAtEpochMs']), targetMs: _nullableIntFromJson(json['targetMs']), ); } final WatchTimerKind kind; final String label; final WatchTimerDisplayMode displayMode; final WatchTimerRunState runState; final int referenceEpochMs; final int accumulatedMs; final int? startedAtEpochMs; final int? targetMs; Map toJson() { return { 'kind': kind.name, 'label': label, 'displayMode': displayMode.name, 'runState': runState.name, 'referenceEpochMs': referenceEpochMs, 'accumulatedMs': accumulatedMs, 'startedAtEpochMs': startedAtEpochMs, 'targetMs': targetMs, }; } @override bool operator ==(Object other) { return identical(this, other) || other is WatchTimerProjection && kind == other.kind && label == other.label && displayMode == other.displayMode && runState == other.runState && referenceEpochMs == other.referenceEpochMs && accumulatedMs == other.accumulatedMs && startedAtEpochMs == other.startedAtEpochMs && targetMs == other.targetMs; } @override int get hashCode { return Object.hash( kind, label, displayMode, runState, referenceEpochMs, accumulatedMs, startedAtEpochMs, targetMs, ); } } T _enumFromJson(Object? value, List values, T fallback) { if (value is String) { for (final enumValue in values) { if (enumValue.name == value) { return enumValue; } } } return fallback; } T? _nullableEnumFromJson(Object? value, List values) { if (value is String) { for (final enumValue in values) { if (enumValue.name == value) { return enumValue; } } } return null; } List _enumListFromJson(Object? value, List values) { if (value is! List) { return const []; } return [ for (final item in value) if (item is String) for (final enumValue in values) if (enumValue.name == item) enumValue, ]; } WatchTimerProjection? _timerFromJson(Object? value) { if (value is! Map) { return null; } return WatchTimerProjection.fromJson(Map.from(value)); } List _timerListFromJson(Object? value) { if (value is! List) { return const []; } return [ for (final item in value) if (item is Map) WatchTimerProjection.fromJson(Map.from(item)), ]; } String _stringFromJson(Object? value) { return value is String ? value : ''; } String? _nullableStringFromJson(Object? value) { return value is String ? value : null; } int _intFromJson(Object? value, int fallback) { return value is int ? value : fallback; } int? _nullableIntFromJson(Object? value) { return value is int ? value : null; } double? _nullableDoubleFromJson(Object? value) { return switch (value) { double() => value, int() => value.toDouble(), _ => null, }; } bool _boolFromJson(Object? value, bool fallback) { return value is bool ? value : fallback; } bool _listEquals(List left, List right) { if (identical(left, right)) { return true; } if (left.length != right.length) { return false; } for (var index = 0; index < left.length; index += 1) { if (left[index] != right[index]) { return false; } } return true; }