Files
OpenMotion/lib/models/scooter_state.dart
T
2026-09-21 18:03:21 -04:00

194 lines
6.2 KiB
Dart

enum ScooterConnectionStatus {
disconnected,
connecting,
connected,
authenticating,
authenticated,
ready,
error,
}
/// Observable snapshot of everything the UI needs.
///
/// Numeric protocol values are kept in their native representation. Units
/// (km vs miles, Ah vs mAh, percent) are NOT asserted here until they have
/// been LIVE VERIFIED against a physical scooter.
class ScooterState {
final ScooterConnectionStatus connectionStatus;
final bool authenticated;
final bool canWrite;
/// Human-readable description of the last error, if [connectionStatus] is
/// [ScooterConnectionStatus.error].
final String? errorMessage;
/// Which connection step is in progress (0-based index into
/// [connectionSteps]) while [connectionStatus] is connecting or
/// authenticated-but-waiting. Null when no multi-step work is running.
final int? connectionStep;
/// Steps shown to the rider while a link is being established.
static const connectionSteps = [
'Connecting',
'Setting up services',
'Checking this is a supported device',
'Waiting for telemetry',
'Waiting for scooter confirmation',
];
final double? speed;
final double? voltage;
final double? current;
final double? power;
final double? tripDistance;
final double? odometer;
final int? batteryLevel;
final int? batteryTemperature;
final int? batteryCycles;
final int? motorTemperature;
final int? controllerTemperature;
final int? gear;
final bool? locked;
final bool? headlight;
final bool? atmosphereLight;
final bool? cruiseControl;
final bool? leftTurnSignal;
final bool? rightTurnSignal;
/// Scooter's own unit preference bit (monitor byte 22 bit 5).
final bool? imperial;
/// Speed limit for the current gear and the highest configured limit, in
/// native protocol units. Gear-to-mode mapping is INFERRED.
final int? speedLimit;
final int? maxSpeedLimit;
final String? displayId;
final String? displayVersion;
const ScooterState({
this.connectionStatus = ScooterConnectionStatus.disconnected,
this.authenticated = false,
this.canWrite = false,
this.errorMessage,
this.connectionStep,
this.speed,
this.voltage,
this.current,
this.power,
this.tripDistance,
this.odometer,
this.batteryLevel,
this.batteryTemperature,
this.batteryCycles,
this.motorTemperature,
this.controllerTemperature,
this.gear,
this.locked,
this.headlight,
this.atmosphereLight,
this.cruiseControl,
this.leftTurnSignal,
this.rightTurnSignal,
this.imperial,
this.speedLimit,
this.maxSpeedLimit,
this.displayId,
this.displayVersion,
});
/// Sentinel so callers can explicitly clear a nullable field.
static const Object _unset = Object();
ScooterState copyWith({
ScooterConnectionStatus? connectionStatus,
bool? authenticated,
bool? canWrite,
Object? errorMessage = _unset,
Object? connectionStep = _unset,
Object? speed = _unset,
Object? voltage = _unset,
Object? current = _unset,
Object? power = _unset,
Object? tripDistance = _unset,
Object? odometer = _unset,
Object? batteryLevel = _unset,
Object? batteryTemperature = _unset,
Object? batteryCycles = _unset,
Object? motorTemperature = _unset,
Object? controllerTemperature = _unset,
Object? gear = _unset,
Object? locked = _unset,
Object? headlight = _unset,
Object? atmosphereLight = _unset,
Object? cruiseControl = _unset,
Object? leftTurnSignal = _unset,
Object? rightTurnSignal = _unset,
Object? imperial = _unset,
Object? speedLimit = _unset,
Object? maxSpeedLimit = _unset,
Object? displayId = _unset,
Object? displayVersion = _unset,
}) {
return ScooterState(
connectionStatus: connectionStatus ?? this.connectionStatus,
authenticated: authenticated ?? this.authenticated,
canWrite: canWrite ?? this.canWrite,
errorMessage: errorMessage == _unset
? this.errorMessage
: errorMessage as String?,
connectionStep:
connectionStep == _unset ? this.connectionStep : connectionStep as int?,
speed: speed == _unset ? this.speed : speed as double?,
voltage: voltage == _unset ? this.voltage : voltage as double?,
current: current == _unset ? this.current : current as double?,
power: power == _unset ? this.power : power as double?,
tripDistance:
tripDistance == _unset ? this.tripDistance : tripDistance as double?,
odometer: odometer == _unset ? this.odometer : odometer as double?,
batteryLevel:
batteryLevel == _unset ? this.batteryLevel : batteryLevel as int?,
batteryTemperature: batteryTemperature == _unset
? this.batteryTemperature
: batteryTemperature as int?,
batteryCycles:
batteryCycles == _unset ? this.batteryCycles : batteryCycles as int?,
motorTemperature: motorTemperature == _unset
? this.motorTemperature
: motorTemperature as int?,
controllerTemperature: controllerTemperature == _unset
? this.controllerTemperature
: controllerTemperature as int?,
gear: gear == _unset ? this.gear : gear as int?,
locked: locked == _unset ? this.locked : locked as bool?,
headlight: headlight == _unset ? this.headlight : headlight as bool?,
atmosphereLight: atmosphereLight == _unset
? this.atmosphereLight
: atmosphereLight as bool?,
cruiseControl:
cruiseControl == _unset ? this.cruiseControl : cruiseControl as bool?,
leftTurnSignal: leftTurnSignal == _unset
? this.leftTurnSignal
: leftTurnSignal as bool?,
rightTurnSignal: rightTurnSignal == _unset
? this.rightTurnSignal
: rightTurnSignal as bool?,
imperial: imperial == _unset ? this.imperial : imperial as bool?,
speedLimit: speedLimit == _unset ? this.speedLimit : speedLimit as int?,
maxSpeedLimit:
maxSpeedLimit == _unset ? this.maxSpeedLimit : maxSpeedLimit as int?,
displayId: displayId == _unset ? this.displayId : displayId as String?,
displayVersion: displayVersion == _unset
? this.displayVersion
: displayVersion as String?,
);
}
}