feat: redesign and lots of progress

This commit is contained in:
2026-04-26 22:43:22 +02:00
parent 16ac66471a
commit 82ea8125e1
24 changed files with 1095 additions and 1315 deletions

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:cbor/simple.dart';
const String universalShifterControlServiceUuid =
@ -249,6 +251,9 @@ enum ControlConnectionState {
}
if (raw is String) {
final normalized = raw.toLowerCase();
if (normalized.contains('disconnected')) {
return ControlConnectionState.disconnected;
}
if (normalized.contains('connected')) {
return ControlConnectionState.connected;
}
@ -294,32 +299,21 @@ class TrainerStatus {
static TrainerStatus fromRaw(dynamic raw) {
if (raw is int) {
switch (raw) {
case 1:
return const TrainerStatus(state: TrainerConnectionState.connecting);
case 2:
return const TrainerStatus(state: TrainerConnectionState.pairing);
case 3:
return const TrainerStatus(state: TrainerConnectionState.connected);
case 4:
return const TrainerStatus(
state: TrainerConnectionState.discoveringFtms);
case 5:
return const TrainerStatus(state: TrainerConnectionState.ftmsReady);
default:
return const TrainerStatus(state: TrainerConnectionState.idle);
}
return _trainerStatusFromVariant(raw);
}
if (raw is List && raw.isNotEmpty) {
final variant = raw.first;
final value = raw.length > 1 ? raw[1] : null;
if (variant is int && (variant == 5 || variant == 6)) {
if (variant is int && variant == 6) {
return TrainerStatus(
state: TrainerConnectionState.error,
errorCode: value is int ? value : null,
);
}
if (variant is int) {
return _trainerStatusFromVariant(variant);
}
}
if (raw is Map) {
@ -327,13 +321,16 @@ class TrainerStatus {
if (entry != null) {
final key = entry.key;
final value = entry.value;
if ((key is int && (key == 5 || key == 6)) ||
if ((key is int && key == 6) ||
(key is String && key.toLowerCase().contains('error'))) {
return TrainerStatus(
state: TrainerConnectionState.error,
errorCode: value is int ? value : null,
);
}
if (key is int) {
return _trainerStatusFromVariant(key);
}
}
}
@ -362,6 +359,26 @@ class TrainerStatus {
return const TrainerStatus(state: TrainerConnectionState.idle);
}
static TrainerStatus _trainerStatusFromVariant(int variant) {
switch (variant) {
case 1:
return const TrainerStatus(state: TrainerConnectionState.connecting);
case 2:
return const TrainerStatus(state: TrainerConnectionState.pairing);
case 3:
return const TrainerStatus(state: TrainerConnectionState.connected);
case 4:
return const TrainerStatus(
state: TrainerConnectionState.discoveringFtms);
case 5:
return const TrainerStatus(state: TrainerConnectionState.ftmsReady);
case 6:
return const TrainerStatus(state: TrainerConnectionState.error);
default:
return const TrainerStatus(state: TrainerConnectionState.idle);
}
}
}
class CentralStatus {
@ -384,16 +401,26 @@ class CentralStatus {
String get statusLine =>
'Control: ${control.name}, Trainer: ${trainer.label}${lastFailure != null ? ', Last failure: $lastFailure' : ''}';
static CentralStatus disconnected({dynamic raw}) {
return CentralStatus(
control: ControlConnectionState.disconnected,
trainer: const TrainerStatus(state: TrainerConnectionState.idle),
hasSavedBond: false,
connectedTrainerAddr: null,
lastFailure: null,
raw: raw,
);
}
static CentralStatus fromCborBytes(List<int> bytes) {
if (bytes.isEmpty) {
throw const FormatException('Status payload is empty.');
}
final decoded = cbor.decode(bytes);
if (decoded is! Map) {
return CentralStatus(
control: ControlConnectionState.disconnected,
trainer: const TrainerStatus(state: TrainerConnectionState.idle),
hasSavedBond: false,
connectedTrainerAddr: null,
lastFailure: null,
raw: decoded,
throw FormatException(
'Status payload must decode to a CBOR map, got ${decoded.runtimeType}.',
);
}
@ -428,6 +455,9 @@ List<int>? _toByteList(dynamic value) {
if (value == null) {
return null;
}
if (value is Uint8List) {
return value.toList(growable: false);
}
if (value is List) {
return value.whereType<int>().toList(growable: false);
}