feat: ui rework and gear generator

This commit is contained in:
2026-04-28 17:13:30 +02:00
parent 82ea8125e1
commit 57a14134a6
300 changed files with 2901 additions and 135 deletions

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:abawo_bt_app/controller/bluetooth.dart';
import 'package:abawo_bt_app/model/gear_ratio_codec.dart';
import 'package:abawo_bt_app/model/shifter_types.dart';
import 'package:anyhow/anyhow.dart';
import 'package:logging/logging.dart';
@ -43,7 +44,6 @@ class ShifterService {
static const int _gearRatioSlots = 32;
static const int _defaultGearIndexOffset = _gearRatioSlots;
static const int _gearRatioPayloadBytes = _gearRatioSlots + 1;
static const double _maxGearRatio = 255 / 64;
static const int _gearRatioWriteMtu = 64;
Future<Result<void>> writeConnectToAddress(String bikeDeviceId) async {
@ -166,12 +166,61 @@ class ShifterService {
}
try {
return Ok(CentralStatus.fromCborBytes(readRes.unwrap()));
return Ok(CentralStatus.fromBytes(readRes.unwrap()));
} catch (e) {
return bail('Failed to decode status payload: $e');
}
}
Future<Result<ShifterDeviceTelemetry>> readDeviceTelemetry() async {
int? batteryPercent;
String? firmwareRevision;
final errors = <String>[];
final batteryResult = await _requireBluetooth.readCharacteristic(
buttonDeviceId,
batteryServiceUuid,
batteryLevelCharacteristicUuid,
);
if (batteryResult.isOk()) {
try {
batteryPercent = parseBatteryLevelPercent(batteryResult.unwrap());
} catch (error) {
errors.add('battery parse failed: $error');
}
} else {
errors.add('battery read failed: ${batteryResult.unwrapErr()}');
}
final firmwareResult = await _requireBluetooth.readCharacteristic(
buttonDeviceId,
deviceInformationServiceUuid,
firmwareRevisionCharacteristicUuid,
);
if (firmwareResult.isOk()) {
try {
firmwareRevision = parseGattUtf8String(firmwareResult.unwrap());
} catch (error) {
errors.add('firmware parse failed: $error');
}
} else {
errors.add('firmware read failed: ${firmwareResult.unwrapErr()}');
}
if (batteryPercent == null &&
firmwareRevision == null &&
errors.isNotEmpty) {
return bail('Could not read battery or firmware: ${errors.join('; ')}');
}
return Ok(
ShifterDeviceTelemetry(
batteryPercent: batteryPercent,
firmwareRevision: firmwareRevision,
),
);
}
Future<Result<DfuPreflightResult>> runDfuPreflight({
int requestedMtu = universalShifterDfuPreferredMtu,
}) async {
@ -253,7 +302,7 @@ class ShifterService {
.listen(
(data) {
try {
final status = CentralStatus.fromCborBytes(data);
final status = CentralStatus.fromBytes(data);
_statusController.add(status);
} catch (error, st) {
_log.warning(
@ -284,19 +333,11 @@ class ShifterService {
}
int _encodeGearRatio(double value) {
if (value <= 0) {
return 0;
}
final clamped = value.clamp(0, _maxGearRatio);
final scaled = (clamped * 64).round();
if (scaled <= 0) {
return 1;
}
return scaled.clamp(1, 255);
return encodeGearRatioByte(value);
}
double _decodeGearRatio(int raw) {
return raw / 64.0;
return decodeGearRatioByte(raw);
}
String _formatBytes(List<int> bytes) {