247 lines
4.9 KiB
Dart
247 lines
4.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:firebase_core/firebase_core.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
|
|
import 'firebase_options.dart';
|
|
import 'dashboard.dart';
|
|
|
|
// 🔔 LOCAL NOTIFICATION
|
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
|
FlutterLocalNotificationsPlugin();
|
|
|
|
// 🔥 REALTIME DATABASE
|
|
final dbRef = FirebaseDatabase.instance.ref("sensor");
|
|
|
|
// 🔥 ANTI SPAM
|
|
bool notifHabisSudahMuncul = false;
|
|
bool notifKosongSudahMuncul = false;
|
|
bool notifFullSudahMuncul = false;
|
|
bool notifBerhasilSudahMuncul = false;
|
|
|
|
final statusRef = FirebaseDatabase.instance.ref("status");
|
|
|
|
int lastHeartbeat = 0;
|
|
DateTime lastHeartbeatTime = DateTime.now();
|
|
|
|
bool notifOfflineSudahMuncul = false;
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
await Firebase.initializeApp(
|
|
options: DefaultFirebaseOptions.currentPlatform,
|
|
);
|
|
|
|
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
|
|
|
await messaging.requestPermission(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
);
|
|
|
|
const AndroidInitializationSettings androidSettings =
|
|
AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
|
|
const InitializationSettings settings =
|
|
InitializationSettings(
|
|
android: androidSettings,
|
|
);
|
|
|
|
await flutterLocalNotificationsPlugin.initialize(settings);
|
|
|
|
startRealtimeNotif();
|
|
monitorHeartbeat();
|
|
|
|
runApp(const MyApp());
|
|
}
|
|
|
|
// 🔥 MONITOR HEARTBEAT
|
|
void monitorHeartbeat() {
|
|
|
|
statusRef.child("heartbeat").onValue.listen((event) {
|
|
|
|
int heartbeat = 0;
|
|
|
|
if (event.snapshot.value != null) {
|
|
heartbeat = (event.snapshot.value as num).toInt();
|
|
}
|
|
|
|
if (heartbeat != lastHeartbeat) {
|
|
|
|
lastHeartbeat = heartbeat;
|
|
lastHeartbeatTime = DateTime.now();
|
|
|
|
notifOfflineSudahMuncul = false;
|
|
}
|
|
});
|
|
|
|
Stream.periodic(
|
|
const Duration(seconds: 1),
|
|
).listen((_) {
|
|
|
|
int selisih = DateTime.now()
|
|
.difference(lastHeartbeatTime)
|
|
.inSeconds;
|
|
|
|
if (selisih >= 5 &&
|
|
!notifOfflineSudahMuncul) {
|
|
|
|
notifOfflineSudahMuncul = true;
|
|
|
|
showNotif(
|
|
"⚠️ Perangkat Offline",
|
|
"Jaringan lemah atau perangkat tidak terhubung.",
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 🔥 REALTIME LISTENER
|
|
void startRealtimeNotif() {
|
|
|
|
dbRef.onValue.listen((event) {
|
|
|
|
final data = event.snapshot.value as Map?;
|
|
|
|
if (data != null) {
|
|
|
|
double jarak =
|
|
(data['jarak'] ?? 0).toDouble();
|
|
|
|
double berat1 =
|
|
(data['berat1'] ?? 0).toDouble();
|
|
|
|
double berat2 =
|
|
(data['berat2'] ?? 0).toDouble();
|
|
|
|
// 🔥 HITUNG PERSEN
|
|
double persen =
|
|
((12 - jarak) / 8) * 100;
|
|
|
|
if (persen < 0) persen = 0;
|
|
if (persen > 100) persen = 100;
|
|
|
|
// ================================
|
|
// 🔔 PAKAN FULL (100%)
|
|
// ================================
|
|
if (persen >= 100 &&
|
|
!notifFullSudahMuncul) {
|
|
|
|
notifFullSudahMuncul = true;
|
|
|
|
showNotif(
|
|
"Pakan Full",
|
|
"Wadah penampung pakan penuh.",
|
|
);
|
|
}
|
|
|
|
// RESET
|
|
if (persen < 100) {
|
|
notifFullSudahMuncul = false;
|
|
}
|
|
|
|
// ================================
|
|
// 🔔 PAKAN HAMPIR HABIS (25%)
|
|
// ================================
|
|
if (persen <= 25 &&
|
|
persen > 0 &&
|
|
!notifHabisSudahMuncul) {
|
|
|
|
notifHabisSudahMuncul = true;
|
|
|
|
showNotif(
|
|
"Pakan Hampir Habis",
|
|
"Segera isi ulang wadah penampung pakan.",
|
|
);
|
|
}
|
|
|
|
// RESET
|
|
if (persen > 25 || persen <= 0) {
|
|
notifHabisSudahMuncul = false;
|
|
}
|
|
|
|
// ================================
|
|
// 🔔 PAKAN HABIS (0%)
|
|
// ================================
|
|
if (persen <= 0 &&
|
|
!notifKosongSudahMuncul) {
|
|
|
|
notifKosongSudahMuncul = true;
|
|
|
|
showNotif(
|
|
"Pakan Habis",
|
|
"Wadah penampung pakan sudah kosong.",
|
|
);
|
|
}
|
|
|
|
// RESET
|
|
if (persen > 0) {
|
|
notifKosongSudahMuncul = false;
|
|
}
|
|
|
|
// ================================
|
|
// 🔔 PEMBERIAN PAKAN BERHASIL
|
|
// ================================
|
|
bool pakanBerhasil =
|
|
berat1 > 0 || berat2 > 0;
|
|
|
|
if (pakanBerhasil &&
|
|
!notifBerhasilSudahMuncul) {
|
|
|
|
notifBerhasilSudahMuncul = true;
|
|
|
|
showNotif(
|
|
"Pemberian Pakan Berhasil",
|
|
"Pakan berhasil diberikan ke wadah pakan.",
|
|
);
|
|
}
|
|
|
|
// RESET
|
|
if (!pakanBerhasil) {
|
|
notifBerhasilSudahMuncul = false;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 🔔 FUNGSI TAMPILKAN NOTIFIKASI
|
|
Future<void> showNotif(
|
|
String title,
|
|
String body,
|
|
) async {
|
|
|
|
const AndroidNotificationDetails androidDetails =
|
|
AndroidNotificationDetails(
|
|
'pakan_channel',
|
|
'Notifikasi Pakan',
|
|
importance: Importance.max,
|
|
priority: Priority.high,
|
|
);
|
|
|
|
const NotificationDetails details =
|
|
NotificationDetails(
|
|
android: androidDetails,
|
|
);
|
|
|
|
await flutterLocalNotificationsPlugin.show(
|
|
0,
|
|
title,
|
|
body,
|
|
details,
|
|
);
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
const MyApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
home: Dashboard(),
|
|
);
|
|
}
|
|
} |