should have push notifs as well as Battery SOC monitoring
This commit is contained in:
18
lib/BlindMasterResources/fcm_service.dart
Normal file
18
lib/BlindMasterResources/fcm_service.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
|
||||
class FcmService {
|
||||
/// Request permission, fetch the FCM token, and register it with the server.
|
||||
/// Safe to call on every login/session-restore — the server just upserts the value.
|
||||
static Future<void> register() async {
|
||||
try {
|
||||
final messaging = FirebaseMessaging.instance;
|
||||
await messaging.requestPermission(alert: true, badge: true, sound: true);
|
||||
final token = await messaging.getToken();
|
||||
if (token == null) return;
|
||||
await securePost({'token': token}, 'register_fcm_token');
|
||||
} catch (_) {
|
||||
// Non-fatal — push notifications simply won't work until the next successful registration.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:blind_master/BlindMasterResources/fcm_service.dart';
|
||||
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
||||
import 'package:blind_master/BlindMasterScreens/Startup/create_user_screen.dart';
|
||||
import 'package:blind_master/BlindMasterScreens/Startup/forgot_password_screen.dart';
|
||||
@@ -84,6 +85,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
if (token.isEmpty) throw Exception('Token Not Received');
|
||||
final storage = FlutterSecureStorage();
|
||||
await storage.write(key: 'token', value: token);
|
||||
await FcmService.register();
|
||||
|
||||
} catch(e) {
|
||||
if (!mounted) return;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:blind_master/BlindMasterResources/fcm_service.dart';
|
||||
import 'package:blind_master/BlindMasterResources/secure_transmissions.dart';
|
||||
import 'package:blind_master/BlindMasterScreens/home_screen.dart';
|
||||
import 'package:blind_master/BlindMasterScreens/Startup/login_screen.dart';
|
||||
@@ -50,6 +51,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
||||
}
|
||||
|
||||
nextScreen = HomeScreen();
|
||||
await FcmService.register();
|
||||
} else {
|
||||
nextScreen = LoginScreen();
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ abstract class BaseVerificationWaitingScreenState<T extends BaseVerificationWait
|
||||
child: ListView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
Container(
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@@ -24,6 +24,7 @@ class _DeviceScreenState extends State<DeviceScreen> {
|
||||
List occports = [];
|
||||
Widget? peripheralList;
|
||||
String deviceName = "...";
|
||||
int? batterySoc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -48,7 +49,8 @@ class _DeviceScreenState extends State<DeviceScreen> {
|
||||
final body = json.decode(response.body) as Map<String, dynamic>;
|
||||
setState(() {
|
||||
deviceName = body['device_name'];
|
||||
});
|
||||
batterySoc = body['battery_soc'] as int?;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
@@ -355,12 +357,30 @@ class _DeviceScreenState extends State<DeviceScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
deviceName,
|
||||
style: GoogleFonts.aBeeZee(),
|
||||
),
|
||||
title: Text(deviceName, style: GoogleFonts.aBeeZee()),
|
||||
backgroundColor: Theme.of(context).primaryColorLight,
|
||||
foregroundColor: Colors.white,
|
||||
actions: [
|
||||
if (batterySoc != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
batterySoc! <= 10 ? Icons.battery_alert
|
||||
: batterySoc! <= 20 ? Icons.battery_1_bar
|
||||
: batterySoc! <= 40 ? Icons.battery_2_bar
|
||||
: batterySoc! <= 60 ? Icons.battery_3_bar
|
||||
: batterySoc! <= 80 ? Icons.battery_5_bar
|
||||
: Icons.battery_full,
|
||||
color: batterySoc! <= 10 ? Colors.red : Colors.white,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text('$batterySoc%', style: const TextStyle(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: peripheralList ?? SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
|
||||
@@ -124,6 +124,27 @@ class _PeripheralScreenState extends State<PeripheralScreen> {
|
||||
}
|
||||
});
|
||||
|
||||
socket?.on("battery_alert", (data) {
|
||||
if (data is! Map<String, dynamic>) return;
|
||||
if (data['deviceId'] != widget.deviceId) return;
|
||||
if (!mounted) return;
|
||||
final type = data['type'] as String? ?? '';
|
||||
final soc = data['soc'] as int? ?? 0;
|
||||
final (String message, Color color) = switch (type) {
|
||||
'overvoltage' => ('Battery fault detected. Please check your charger.', Colors.red),
|
||||
'critical_low' => ('Battery critically low ($soc%). Device shutting down.', Colors.red),
|
||||
'low_voltage_warning' => ('Battery voltage dip detected ($soc%). Monitor closely.', Colors.orange),
|
||||
'low_20' => ('Battery low: $soc% remaining. Consider charging soon.', Colors.orange),
|
||||
'low_10' => ('Battery very low: $soc% remaining. Charge now.', Colors.deepOrange),
|
||||
_ => ('Battery alert received ($soc%).', Colors.orange),
|
||||
};
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(message),
|
||||
backgroundColor: color,
|
||||
duration: const Duration(seconds: 6),
|
||||
));
|
||||
});
|
||||
|
||||
socket?.on("calib", (periphData) {
|
||||
if (periphData is Map<String, dynamic>) {
|
||||
if (periphData['periphID'] == widget.peripheralId) {
|
||||
|
||||
88
lib/firebase_options.dart
Normal file
88
lib/firebase_options.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
// File generated by FlutterFire CLI.
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||
import 'package:flutter/foundation.dart'
|
||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||
|
||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// import 'firebase_options.dart';
|
||||
/// // ...
|
||||
/// await Firebase.initializeApp(
|
||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
||||
/// );
|
||||
/// ```
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
return web;
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
return android;
|
||||
case TargetPlatform.iOS:
|
||||
return ios;
|
||||
case TargetPlatform.macOS:
|
||||
return macos;
|
||||
case TargetPlatform.windows:
|
||||
return windows;
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
default:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions are not supported for this platform.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static const FirebaseOptions web = FirebaseOptions(
|
||||
apiKey: 'AIzaSyDnRirxd46eWwUkK9cKi-g0mKaIBY3LODM',
|
||||
appId: '1:956683546941:web:f66e35806267907c121554',
|
||||
messagingSenderId: '956683546941',
|
||||
projectId: 'blindmaster-54055',
|
||||
authDomain: 'blindmaster-54055.firebaseapp.com',
|
||||
storageBucket: 'blindmaster-54055.firebasestorage.app',
|
||||
measurementId: 'G-Y31FSGG3KP',
|
||||
);
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyBJzL-jeo4xa_rQkHymzku_2lIJ6WJ8hoI',
|
||||
appId: '1:956683546941:android:828055a1f543b75f121554',
|
||||
messagingSenderId: '956683546941',
|
||||
projectId: 'blindmaster-54055',
|
||||
storageBucket: 'blindmaster-54055.firebasestorage.app',
|
||||
);
|
||||
|
||||
static const FirebaseOptions ios = FirebaseOptions(
|
||||
apiKey: 'AIzaSyC0MWniqI8flETaT8zKwXQzBhYRljKIKvk',
|
||||
appId: '1:956683546941:ios:162a6b8aa58f1eb8121554',
|
||||
messagingSenderId: '956683546941',
|
||||
projectId: 'blindmaster-54055',
|
||||
storageBucket: 'blindmaster-54055.firebasestorage.app',
|
||||
iosBundleId: 'com.example.blindMaster',
|
||||
);
|
||||
|
||||
static const FirebaseOptions macos = FirebaseOptions(
|
||||
apiKey: 'AIzaSyC0MWniqI8flETaT8zKwXQzBhYRljKIKvk',
|
||||
appId: '1:956683546941:ios:162a6b8aa58f1eb8121554',
|
||||
messagingSenderId: '956683546941',
|
||||
projectId: 'blindmaster-54055',
|
||||
storageBucket: 'blindmaster-54055.firebasestorage.app',
|
||||
iosBundleId: 'com.example.blindMaster',
|
||||
);
|
||||
|
||||
static const FirebaseOptions windows = FirebaseOptions(
|
||||
apiKey: 'AIzaSyDnRirxd46eWwUkK9cKi-g0mKaIBY3LODM',
|
||||
appId: '1:956683546941:web:f5d94d05b6ce6bef121554',
|
||||
messagingSenderId: '956683546941',
|
||||
projectId: 'blindmaster-54055',
|
||||
authDomain: 'blindmaster-54055.firebaseapp.com',
|
||||
storageBucket: 'blindmaster-54055.firebasestorage.app',
|
||||
measurementId: 'G-WR6581J4P4',
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,21 @@
|
||||
import 'package:blind_master/BlindMasterScreens/Startup/splash_screen.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'firebase_options.dart'; // generated by: flutterfire configure
|
||||
|
||||
// Handles FCM messages that arrive when the app is terminated or in the background.
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
// The system notification tray handles display automatically — nothing to do here.
|
||||
}
|
||||
|
||||
enum DaysOfWeek {Su, M, Tu, W, Th, F, Sa}
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user