Skip to content

feat: Enhance Badge with Adjustable Brightness Support #1271

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

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:badgemagic/providers/BadgeBrightnessProvider.dart';
import 'package:badgemagic/providers/getitlocator.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:badgemagic/services/BleBrightnessService.dart';
import 'package:badgemagic/view/about_us_screen.dart';
import 'package:badgemagic/view/draw_badge_screen.dart';
import 'package:badgemagic/view/homescreen.dart';
Expand All @@ -8,16 +10,25 @@ import 'package:badgemagic/view/saved_clipart.dart';
import 'package:badgemagic/view/settings_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart';
import 'globals/globals.dart' as globals;

void main() {
final getIt = GetIt.instance;
getIt.registerSingleton<BleBrightnessService>(BleBrightnessService());

setupLocator();
WidgetsFlutterBinding.ensureInitialized();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider<InlineImageProvider>(
create: (context) => getIt<InlineImageProvider>()),
ChangeNotifierProvider<BadgeBrightnessProvider>(
create: (_) => BadgeBrightnessProvider(
GetIt.instance<BleBrightnessService>(),
),
)
],
child: const MyApp(),
));
Expand Down
91 changes: 91 additions & 0 deletions lib/providers/BadgeBrightnessProvider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:badgemagic/services/BleBrightnessService.dart';

class BadgeBrightnessProvider extends ChangeNotifier {
final BleBrightnessService _bleBrightnessService;
double _brightness = 50.0; // Default brightness level
bool _isConnected = false;
String _errorMessage = "";
bool _isBrightnessVisible = false;

// Getters
double get brightness => _brightness;
bool get isConnected => _isConnected;
String get errorMessage => _errorMessage;
bool get isBrightnessVisible => _isBrightnessVisible;

BadgeBrightnessProvider(this._bleBrightnessService) {
_initialize();
}

Future<void> _initialize() async {
await _bleBrightnessService.initialize();

_bleBrightnessService.connectionStatus.listen((connected) {
_isConnected = connected;
notifyListeners();

if (connected) {
getCurrentBrightness();
}
});

_bleBrightnessService.errorStream.listen((error) {
_errorMessage = error;
notifyListeners();
});
}

void toggleBrightnessVisibility(bool value) {
_isBrightnessVisible = value;
notifyListeners();
}

Future<bool> connectToDevice(String deviceId) async {
bool result = await _bleBrightnessService.connectToDevice(deviceId);
if (result) {
_errorMessage = "";
}
notifyListeners();
return result;
}

Future<bool> setBrightness(double brightnessLevel) async {
_brightness = brightnessLevel;
notifyListeners();

if (_isConnected) {
bool success =
await _bleBrightnessService.setBrightness(brightnessLevel.round());

if (!success && _errorMessage.isEmpty) {
_errorMessage = "Failed to update brightness";
notifyListeners();
}

return success;
}

// If not connected, return true since we updated the local state
return true;
}

Future<void> getCurrentBrightness() async {
final currentBrightness =
await _bleBrightnessService.getCurrentBrightness();
if (currentBrightness != null) {
_brightness = currentBrightness.toDouble();
notifyListeners();
}
}

Future<void> disconnect() async {
await _bleBrightnessService.disconnect();
}

@override
void dispose() {
_bleBrightnessService.dispose();
super.dispose();
}
}
1 change: 1 addition & 0 deletions lib/providers/brightness_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO Implement this library.
180 changes: 180 additions & 0 deletions lib/services/BleBrightnessService.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import 'dart:async';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:logger/logger.dart';

class BleBrightnessService {
final Logger logger = Logger();
BluetoothDevice? connectedDevice;
BluetoothCharacteristic? brightnessCharacteristic;

final String badgeServiceUuid = "0000fee0-0000-1000-8000-00805f9b34fb";
final String brightnessCharacteristicUuid =
"0000fee1-0000-1000-8000-00805f9b34fb";

final _connectionStatus = StreamController<bool>.broadcast();
Stream<bool> get connectionStatus => _connectionStatus.stream;

final _errorStream = StreamController<String>.broadcast();
Stream<String> get errorStream => _errorStream.stream;

bool simulationMode = false;
int simulatedBrightness = 50;

bool get isConnected =>
simulationMode ||
(connectedDevice != null && brightnessCharacteristic != null);

Future<void> initialize({bool enableSimulation = false}) async {
simulationMode = enableSimulation;
if (simulationMode) {
logger.i("Simulation mode enabled");
_connectionStatus.add(true);
return;
}

try {
await FlutterBluePlus.turnOn();
if (!await FlutterBluePlus.isAvailable) {
_errorStream.add("Bluetooth is not available on this device");
return;
}
} catch (e) {
logger.e("Error initializing Bluetooth: $e");
_errorStream.add("Error initializing Bluetooth: $e");
}
}

Future<bool> connectToDevice(String deviceId) async {
if (simulationMode) {
logger.i("Simulated connection to device: $deviceId");
_connectionStatus.add(true);
return true;
}
try {
List<BluetoothDevice> connectedDevices = FlutterBluePlus.connectedDevices;
for (var device in connectedDevices) {
if (device.remoteId.toString() == deviceId) {
return await _connectToDevice(device);
}
}
} catch (e) {
logger.e("Connection error: $e");
_errorStream.add("Connection error: $e");
}
return false;
}

Future<bool> _connectToDevice(BluetoothDevice device) async {
if (simulationMode) return true;
try {
await device.connect(autoConnect: false);
connectedDevice = device;
_connectionStatus.add(true);
await _discoverServices();
return true;
} catch (e) {
logger.e("Failed to connect: $e");
_errorStream.add("Failed to connect: $e");
return false;
}
}

Future<void> _discoverServices() async {
if (simulationMode) return;
if (connectedDevice == null) return;
try {
List<BluetoothService> services =
await connectedDevice!.discoverServices();
for (var service in services) {
if (service.uuid.toString() == badgeServiceUuid) {
for (var characteristic in service.characteristics) {
if (characteristic.uuid.toString() ==
brightnessCharacteristicUuid) {
brightnessCharacteristic = characteristic;
logger.i("Found brightness characteristic");
break;
}
}
}
}
if (brightnessCharacteristic == null) {
_errorStream.add("Brightness characteristic not found");
}
} catch (e) {
logger.e("Error discovering services: $e");
_errorStream.add("Error discovering services: $e");
}
}

Future<bool> setBrightness(int brightnessLevel) async {
if (!isConnected) {
_errorStream.add("Not connected to device");
return false;
}
final validBrightness = brightnessLevel.clamp(0, 100);
if (simulationMode) {
simulatedBrightness = validBrightness;
logger.i("Simulated brightness set to $validBrightness%");
return true;
}
try {
int scaledBrightness = (validBrightness * 2.55).round();
List<int> payload = [0x01, scaledBrightness];
await brightnessCharacteristic!.write(payload, withoutResponse: false);
logger.i("Brightness set to $validBrightness%");
return true;
} catch (e) {
logger.e("Failed to set brightness: $e");
_errorStream.add("Failed to set brightness: $e");
return false;
}
}

Future<int?> getCurrentBrightness() async {
if (!isConnected) {
_errorStream.add("Not connected to device");
return null;
}
if (simulationMode) {
return simulatedBrightness;
}
try {
List<int> value = await brightnessCharacteristic!.read();
if (value.length > 1) {
return (value[1] / 2.55).round();
}
return null;
} catch (e) {
logger.e("Failed to read brightness: $e");
_errorStream.add("Failed to read brightness: $e");
return null;
}
}

Future<void> disconnect() async {
if (simulationMode) {
logger.i("Simulated disconnection");
_connectionStatus.add(false);
return;
}
if (connectedDevice != null) {
try {
await connectedDevice!.disconnect();
logger.i("Disconnected from device");
} catch (e) {
logger.e("Error disconnecting: $e");
_errorStream.add("Error disconnecting: $e");
} finally {
connectedDevice = null;
brightnessCharacteristic = null;
_connectionStatus.add(false);
}
}
}

void dispose() {
disconnect();
_connectionStatus.close();
_errorStream.close();
}
}
Loading
Loading