Skip to content
Snippets Groups Projects
Commit 49dfdd26 authored by Marvin Weiler's avatar Marvin Weiler
Browse files

Added feature to show the battery level of the current obs device

parent 692b9255
Branches ci-test
Tags
No related merge requests found
......@@ -36,6 +36,9 @@
//
// buttonData: task -> main
// {"time": int}
//
// batteryLevel: task -> main
// {"level": int}
enum EventName {
requestLocationState,
......@@ -50,6 +53,7 @@ enum EventName {
disconnected,
distanceData,
buttonData,
batteryLevel,
}
enum LocationState {
......@@ -109,6 +113,8 @@ abstract class EventData {
return DistanceDataEvent.fromJson(json);
case EventName.buttonData:
return ButtonDataEvent.fromJson(json);
case EventName.batteryLevel:
return BatteryLevelEvent.fromJson(json);
default:
throw ArgumentError('Switch fallthrough:Invalid event name');
}
......@@ -331,3 +337,26 @@ class ButtonDataEvent extends EventData {
}
}
}
class BatteryLevelEvent extends EventData {
BatteryLevelEvent({required this.level})
: super(event: EventName.batteryLevel);
final int level;
@override
Map<String, dynamic> toJson() => {
'event': event.toString().split('.').last,
'level': level,
};
factory BatteryLevelEvent.fromJson(Map<String, dynamic> json) {
if (json.containsKey('level')) {
return BatteryLevelEvent(
level: json['level'],
);
} else {
throw ArgumentError('Invalid JSON data for BatteryLevelEvent');
}
}
}
......@@ -5,6 +5,9 @@ import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
// The services that should be exposed by the OpenBikeSensor
Guid batteryServiceID = Guid("0000180F-0000-1000-8000-00805F9B34FB");
Guid batteryCharacteristic = Guid("00002A19-0000-1000-8000-00805F9B34FB");
Guid deviceServiceID = Guid("0000180A-0000-1000-8000-00805F9B34FB");
Guid firmwareCharacteristic = Guid("00002a26-0000-1000-8000-00805f9b34fb");
Guid nameCharacteristic = Guid("00002a29-0000-1000-8000-00805f9b34fb");
......@@ -15,7 +18,7 @@ Guid buttonCharacteristic = Guid("1FE7FAF9-CE63-4236-0004-000000000003");
Guid offsetCharacteristic = Guid("1FE7FAF9-CE63-4236-0004-000000000004");
Guid trackidCharacteristic = Guid("1FE7FAF9-CE63-4236-0004-000000000005");
enum BluetoothEvent { distance, button, offset, trackId, firmware }
enum BluetoothEvent { distance, button, offset, trackId, firmware, battery }
typedef BluetoothCallbackFunction = Function(BluetoothEvent, CallbackResults);
typedef DiscoveredDeviceCallbackFunction = Function(List<BluetoothDevice>);
......@@ -36,6 +39,7 @@ class CallbackResults {
int distance1 = 0;
int distance2 = 0;
int clock = 0;
int batteryLevel = 0;
}
class Bluetooth {
......@@ -48,6 +52,7 @@ class Bluetooth {
// Subscription to the distance and button characteristics
StreamSubscription<List<int>>? _distanceSubscription;
StreamSubscription<List<int>>? _buttonSubscription;
StreamSubscription<List<int>>? _batterySubscription;
StreamSubscription<OnDiscoveredServicesEvent>? _servicesSubscription;
......@@ -67,6 +72,7 @@ class Bluetooth {
_servicesSubscription?.cancel();
_distanceSubscription?.cancel();
_buttonSubscription?.cancel();
_batterySubscription?.cancel();
}
static Bluetooth? _instance;
......@@ -276,6 +282,14 @@ class Bluetooth {
_notifyCharacteristicCallbacks(BluetoothEvent.trackId, data);
}
void _handleBatteryLevelCharacteristic(List<int> valueList) {
if (valueList.isEmpty) {
throw Exception("Invalid battery level characteristic");
}
final CallbackResults data = CallbackResults()..batteryLevel = valueList[0];
_notifyCharacteristicCallbacks(BluetoothEvent.battery, data);
}
// Discover all services exposed by the device
Future<void> subscribeToCharacteristics(
BluetoothCallbackFunction callback) async {
......@@ -296,6 +310,16 @@ class Bluetooth {
firmware.lastValueStream.listen(handleFirmwareCharacteristic);
await firmware.read();
// BatteryService
// https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/BAS_v1.1/out/en/index-en.html#UUID-fb902c2f-32bc-833c-8eb6-245fc3a52b81
final batteryService = services
.firstWhere((element) => element.uuid == batteryServiceID)
.characteristics;
final battery = batteryService
.firstWhere((element) => element.uuid == batteryCharacteristic);
battery.setNotifyValue(true);
battery.lastValueStream.listen(_handleBatteryLevelCharacteristic);
// Handle the obsService
final obsCharacteristics = services
.firstWhere((element) => element.uuid == obsServiceID)
......
......@@ -314,6 +314,11 @@ class ForegroundTaskHandler extends TaskHandler {
break;
case BluetoothEvent.trackId:
break; // unused
case BluetoothEvent.battery:
_sendMessageToUI(BatteryLevelEvent(
level: data.batteryLevel,
));
default:
throw Exception("Unknown event $event");
}
......
......@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:app/generated/i18n/app_localizations.dart';
import 'package:app/manager/background_manger.dart';
import 'package:app/providers/battery_provider.dart';
import 'package:app/providers/bluetooth_provider.dart';
import 'package:app/providers/button_provider.dart';
import 'package:app/providers/location_provider.dart';
......@@ -29,6 +30,7 @@ Future<void> main() async {
ChangeNotifierProvider(create: (context) => ButtonProvider()),
ChangeNotifierProvider(create: (context) => TripProvider()),
ChangeNotifierProvider(create: (context) => LocationProvider()),
ChangeNotifierProvider(create: (context) => BatteryProvider()),
],
child: const MyApp(),
);
......
import 'dart:convert';
import 'package:app/foreground_task/bluetooth.dart';
import 'package:app/foreground_task/task_handler.dart';
import 'package:app/providers/battery_provider.dart';
import 'package:app/providers/bluetooth_provider.dart';
import 'package:app/providers/button_provider.dart';
import 'package:app/providers/distance_provider.dart';
......@@ -129,6 +130,8 @@ class BackgroundManager {
Provider.of<ButtonProvider>(_context!, listen: false);
final locationProvider =
Provider.of<LocationProvider>(_context!, listen: false);
final batteryProvider =
Provider.of<BatteryProvider>(_context!, listen: false);
final event = EventData.fromJson(jsonData);
......@@ -185,6 +188,10 @@ class BackgroundManager {
case EventName.buttonData:
buttonProvider.setButton();
break;
case EventName.batteryLevel:
final batteryLevel = (event as BatteryLevelEvent).level;
batteryProvider.setBatteryLevel(batteryLevel);
break;
default:
throw Exception("Unknown event ${event.event}");
}
......
import 'package:flutter/material.dart';
class BatteryProvider extends ChangeNotifier {
int batteryLevel = -1; // No information about the battery available
BatteryProvider();
void setBatteryLevel(int level) {
batteryLevel = level;
notifyListeners();
}
}
import 'package:app/foreground_task/bluetooth.dart';
import 'package:app/providers/battery_provider.dart';
import 'package:app/providers/bluetooth_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
......@@ -19,7 +20,7 @@ class _BluetoothStatusState extends State<BluetoothStatus> {
}
// return a text widget based on the location status
Widget locationText(BleState state, ConnectionResult? device) {
Widget bluetoothStatusText(BleState state, ConnectionResult? device) {
final localizations = AppLocalizations.of(context)!;
switch (state) {
......@@ -43,9 +44,36 @@ class _BluetoothStatusState extends State<BluetoothStatus> {
}
}
Widget batteryState(int batteryLevel) {
Icon batteryIcon = Icon(Icons.battery_unknown);
if (batteryLevel < 0) {
batteryIcon = Icon(Icons.battery_unknown);
} else if (batteryLevel < 10) {
batteryIcon = Icon(Icons.battery_0_bar);
} else if (batteryLevel < 20) {
batteryIcon = Icon(Icons.battery_2_bar);
} else if (batteryLevel < 40) {
batteryIcon = Icon(Icons.battery_3_bar);
} else if (batteryLevel < 60) {
batteryIcon = Icon(Icons.battery_4_bar);
} else if (batteryLevel < 80) {
batteryIcon = Icon(Icons.battery_5_bar);
} else if (batteryLevel < 100) {
batteryIcon = Icon(Icons.battery_6_bar);
} else if (batteryLevel == 100) {
batteryIcon = Icon(Icons.battery_full);
}
return Row(children: [
Icon(Icons.battery_5_bar),
Text(batteryLevel >= 0 ? '$batteryLevel%' : '')
]);
}
@override
Widget build(BuildContext context) {
final bluetoothProvider = context.watch<BluetoothProvider>();
final batteryProvider = context.watch<BatteryProvider>();
return Card(
child: ListTile(
......@@ -56,8 +84,11 @@ class _BluetoothStatusState extends State<BluetoothStatus> {
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
locationText(bluetoothProvider.connectionState,
bluetoothStatusText(bluetoothProvider.connectionState,
bluetoothProvider.connectedDevice),
bluetoothProvider.connectionState == BleState.connected
? batteryState(batteryProvider.batteryLevel)
: SizedBox.shrink()
],
),
),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment