Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Bike Battery #47

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
Binary file added assets/Screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Screenshot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Screenshot3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 82 additions & 7 deletions lib/bike.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Bike extends _$Bike {
_resetReadTimer();
var data = await ref
.read(bluetoothRepositoryProvider)
.readCurrentState(state.id);
.readCurrentState(state.id, 'SETTING');
if (data == null || data.isEmpty) {
return;
}
Expand All @@ -87,10 +87,27 @@ class Bike extends _$Bike {
newState = newState.copyWith(assist: state.assist);
}
writeStateData(newState);

// Read the ride state to update the battery level
var rideData = await ref
.read(bluetoothRepositoryProvider)
.readCurrentState(state.id, 'RIDE');
if (rideData != null && rideData.isNotEmpty) {
//update Battery State
state = newState.updateRideFromData(rideData);
}
// Read the ride state to update the battery level
var totalData = await ref
.read(bluetoothRepositoryProvider)
.readCurrentState(state.id, 'TOTAL');
if (totalData != null && totalData.isNotEmpty) {
//update Battery State
state = newState.updateTotalFromData(totalData);
}
});
}

void writeStateData(BikeState newState, {saveToBike = true}) async {
void writeStateData(BikeState newState, {bool saveToBike = true}) async {
_resetDebounce();
if (state.id != newState.id) {
throw Exception('Bike id mismatch');
Expand Down Expand Up @@ -235,9 +252,8 @@ class BikePageState extends ConsumerState<BikePage> {
mainAxisAlignment: MainAxisAlignment.end,
children: [ConnectionWidget(bike: bike)],
),
LightControlWidget(
bike: bike,
),
BatteryODOControlWidget(bike: bike),
LightControlWidget(bike: bike),
ModeControlWidget(bike: bike),
AssistControlWidget(bike: bike),
if (Platform.isAndroid)
Expand Down Expand Up @@ -292,8 +308,7 @@ class ConnectionWidget extends ConsumerWidget {
if (connectionStatus == BluetoothConnectionState.connected) {
text = 'Connected';
disabled = true;
} else if (connectionStatus == BluetoothConnectionState.disconnected &&
!isScanning) {
} else if (connectionStatus == BluetoothConnectionState.disconnected && !isScanning) {
text = 'Connect';
disabled = false;
}
Expand Down Expand Up @@ -327,6 +342,66 @@ class LockWidget extends StatelessWidget {
);
}
}
class BatteryODOControlWidget extends ConsumerWidget {
const BatteryODOControlWidget({super.key, required this.bike});
final BikeState bike;

@override
Widget build(BuildContext context, WidgetRef ref) {
var bikeControl = ref.watch(bikeProvider(bike.id).notifier);
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: DiscoverCard(
colorIndex: bike.color,
title: "",
metric: bike.speedMetric == 'metric'
? '${bike.odometer.toStringAsFixed(0)} km'
: '${(bike.odometer * 0.621371).toStringAsFixed(0)} mi',
titleIcon: Icons.pedal_bike,
selected: false,
onTap: () {
final newMetric = bike.speedMetric == 'metric' ? 'imperial' : 'metric';
bikeControl.writeStateData(bike.copyWith(speedMetric: newMetric), saveToBike: false);
},
),
),
SizedBox(width: 16.0), // Add padding between the cards
Expanded(
child: DiscoverCard(
height: 2,
colorIndex: bike.color,
title: "",
metric: '${bike.battery.toDouble()} %',
titleIcon: _getBatteryIcon(bike.battery),
selected: false,
onTap: () {
//todo: show battery info
},
),
),
],
),
);
}
}

IconData _getBatteryIcon(double batteryPercentage) {
return switch (batteryPercentage) {
>= 95 => Icons.battery_full,
>= 80 => Icons.battery_6_bar,
>= 60 => Icons.battery_5_bar,
>= 50 => Icons.battery_4_bar,
>= 35 => Icons.battery_3_bar,
>= 20 => Icons.battery_2_bar,
>= 5 => Icons.battery_1_bar,
>= 0 => Icons.battery_0_bar,
_ => Icons.battery_alert,
};
}

class LightControlWidget extends ConsumerWidget {
const LightControlWidget({super.key, required this.bike});
Expand Down
2 changes: 1 addition & 1 deletion lib/bike.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion lib/debug.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:superduper/help.dart';
import 'dart:math';

import 'bike.dart';
import 'edit_bike.dart';
Expand Down Expand Up @@ -41,7 +42,7 @@ class DebugPage extends StatelessWidget {
context: context,
builder: (BuildContext context) {
return BikeSettingsWidget(
bike: BikeState.defaultState('1'));
bike: BikeState.defaultState(generateRandomMacAddress()));
});
},
child: const Text('Form'),
Expand All @@ -50,4 +51,14 @@ class DebugPage extends StatelessWidget {
),
);
}

String generateRandomMacAddress() {
final Random random = Random();
return '${_randomHex(random)}:${_randomHex(random)}:${_randomHex(random)}:${_randomHex(random)}:${_randomHex(random)}:${_randomHex(random)}';
}

String _randomHex(Random random) {
return random.nextInt(16).toRadixString(16).padLeft(2, '0');
}

}
36 changes: 34 additions & 2 deletions lib/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class BikeState with _$BikeState {
@Assert('assist >= 0')
@Assert('assist <= 4')
@Assert('color >= 0')
@Assert('battery >= 0')
@Assert('battery <= 100')
@Assert('odometer >= 0')
@Assert('speedMetric == "metric" || speedMetric == "imperial"')
const factory BikeState(
{required String id,
required int mode,
Expand All @@ -35,14 +39,18 @@ class BikeState with _$BikeState {
required String name,
BikeRegion? region,
@Default(false) bool modeLock,
@Default(0) int color}) = _BikeState;
@Default(0) int color,
@Default(0.0) double battery,
@Default(0) double odometer,
@Default('metric') String speedMetric
}) = _BikeState;

factory BikeState.fromJson(Map<String, Object?> json) =>
_$BikeStateFromJson(json);

factory BikeState.defaultState(String id) {
return BikeState(
id: id, mode: 0, light: false, assist: 0, name: getName(seed: id));
id: id, mode: 0, light: false, assist: 0, name: getName(seed: id), battery: 0.0, odometer: 0);
}

BikeState updateFromData(List<int> data) {
Expand All @@ -58,6 +66,20 @@ class BikeState with _$BikeState {
region: region);
}

BikeState updateRideFromData(List<int> data) {
const cadenceIdx = 3;
const rangeIdx = 8;
final batteryPercentage = _batteryPercentage(data[rangeIdx]);
return copyWith(battery: batteryPercentage);
}

BikeState updateTotalFromData(List<int> data) {
const total1Idx = 6;
const total2Idx = 7;
final totalodometer = _getodometer(data[total1Idx], data[total2Idx]);
return copyWith(odometer: totalodometer);
}

BikeRegion _guessRegion(int mode) {
if (region != null) {
return region!;
Expand Down Expand Up @@ -90,6 +112,16 @@ class BikeState with _$BikeState {
return (mode + 1) % 4;
}

double _getodometer(int total1, int total2) {
return (((total2 * 256) + total1) / 10);
}

double _batteryPercentage(int range) {
// All current Super73 max range is 60km
double batteryPercentage = range / 60.0 * 100;
return double.parse(batteryPercentage.toStringAsFixed(1));
}

List<int> toWriteData() {
return [0, 209, light ? 1 : 0, assist, _modeToWrite(), 0, 0, 0, 0, 0];
}
Expand Down
Loading