423 lines
16 KiB
Dart
423 lines
16 KiB
Dart
//import 'package:flutter/material.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 'package:jago/services/db_helper.dart';
|
|
import 'dart:async';
|
|
|
|
class NotificationService {
|
|
static final NotificationService _instance = NotificationService._internal();
|
|
factory NotificationService() => _instance;
|
|
|
|
final DatabaseReference _database = FirebaseDatabase.instance.ref();
|
|
final DatabaseHelper dbHelper = DatabaseHelper();
|
|
int ageInWeeks = 2;
|
|
|
|
// Inisialisasi notifikasi lokal
|
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
|
|
|
|
// Daftar subscription untuk dibersihkan saat dispose
|
|
final List<StreamSubscription> _subscriptions = [];
|
|
|
|
// Flag untuk mencegah duplikasi notifikasi dalam waktu singkat
|
|
final Map<String, DateTime> _lastNotificationTimes = {};
|
|
|
|
// Flag untuk menandai apakah service sudah diinisialisasi
|
|
bool _isInitialized = false;
|
|
|
|
NotificationService._internal();
|
|
|
|
Future<void> initialize() async {
|
|
if (_isInitialized) return;
|
|
|
|
print("🚀 Menginisialisasi NotificationService...");
|
|
|
|
// Inisialisasi database
|
|
await dbHelper.checkTables();
|
|
|
|
// Inisialisasi notifikasi lokal
|
|
const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid);
|
|
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
|
|
|
|
// Setup Firebase Messaging
|
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
_showNotification(message.notification?.title ?? 'Notifikasi', message.notification?.body ?? '');
|
|
});
|
|
|
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
|
|
// Ambil umur ayam dari Firebase
|
|
_fetchAgeFromFirebase();
|
|
|
|
// Mulai memantau data
|
|
_startMonitoring();
|
|
|
|
_isInitialized = true;
|
|
print("✅ NotificationService berhasil diinisialisasi");
|
|
}
|
|
|
|
void dispose() {
|
|
for (var subscription in _subscriptions) {
|
|
subscription.cancel();
|
|
}
|
|
_subscriptions.clear();
|
|
_isInitialized = false;
|
|
print("🛑 NotificationService dihentikan");
|
|
}
|
|
|
|
static Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
print("📱 Menerima pesan di background: ${message.notification?.title}");
|
|
await NotificationService().initialize();
|
|
NotificationService()._showNotification(
|
|
message.notification?.title ?? 'Notifikasi',
|
|
message.notification?.body ?? ''
|
|
);
|
|
}
|
|
|
|
Future<void> _showNotification(String title, String body) async {
|
|
const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails(
|
|
'Jago',
|
|
'Notification',
|
|
channelDescription: 'Cek Notification!!!',
|
|
importance: Importance.max,
|
|
priority: Priority.high,
|
|
);
|
|
const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);
|
|
await flutterLocalNotificationsPlugin.show(
|
|
0,
|
|
title,
|
|
body,
|
|
platformChannelSpecifics,
|
|
);
|
|
}
|
|
|
|
void _fetchAgeFromFirebase() {
|
|
print("🐔 Memuat umur ayam dari Firebase...");
|
|
final subscription = _database.child("chicken_age").onValue.listen((event) {
|
|
final data = event.snapshot.value;
|
|
if (data != null) {
|
|
ageInWeeks = int.parse(data.toString());
|
|
print("🐔 Umur ayam diperbarui: $ageInWeeks minggu");
|
|
}
|
|
});
|
|
_subscriptions.add(subscription);
|
|
}
|
|
|
|
void _startMonitoring() {
|
|
print("👀 Mulai memantau data dari Firebase...");
|
|
|
|
// Pastikan umur ayam diambil terlebih dahulu
|
|
_fetchAgeFromFirebase();
|
|
|
|
// Delay sedikit untuk memastikan umur ayam sudah diambil
|
|
Future.delayed(Duration(milliseconds: 500), () {
|
|
_loadEmergencyData();
|
|
_loadFuzzyData();
|
|
_loadStatusData();
|
|
_loadSensorData();
|
|
});
|
|
}
|
|
|
|
// Memuat data emergency dari Firebase
|
|
void _loadEmergencyData() {
|
|
try {
|
|
print("🔄 Memuat data emergency...");
|
|
final subscription = _database.child('emergency').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
print("🔥 Data emergency diterima: ${event.snapshot.value}");
|
|
final Map<dynamic, dynamic> emergencyData = event.snapshot.value as Map;
|
|
|
|
if (emergencyData['low_temperature'] == true) {
|
|
print("🔥 Kondisi emergency: Suhu sangat rendah terdeteksi");
|
|
_addNotification('Suhu sangat rendah! Kondisi darurat terdeteksi.');
|
|
}
|
|
|
|
if (emergencyData['high_temperature'] == true) {
|
|
print("🔥 Kondisi emergency: Suhu sangat tinggi terdeteksi");
|
|
_addNotification('Suhu sangat tinggi! Kondisi darurat terdeteksi.');
|
|
}
|
|
|
|
if (emergencyData['low_humidity'] == true) {
|
|
print("🔥 Kondisi emergency: Kelembapan sangat rendah terdeteksi");
|
|
_addNotification('Kelembapan sangat rendah! Kondisi darurat terdeteksi.');
|
|
}
|
|
|
|
if (emergencyData['high_humidity'] == true) {
|
|
print("🔥 Kondisi emergency: Kelembapan sangat tinggi terdeteksi");
|
|
_addNotification('Kelembapan sangat tinggi! Kondisi darurat terdeteksi.');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data emergency: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(subscription);
|
|
|
|
// Juga dengarkan perubahan pada node emergency
|
|
final changeSubscription = _database.child('emergency').onChildChanged.listen((event) {
|
|
if (event.snapshot.value == true) {
|
|
String type = event.snapshot.key ?? "unknown";
|
|
String message = "";
|
|
|
|
print("🔥 Perubahan emergency terdeteksi: ${event.snapshot.key} = ${event.snapshot.value}");
|
|
|
|
if (type == "low_temperature") {
|
|
message = "Suhu sangat rendah! Kondisi darurat terdeteksi.";
|
|
} else if (type == "high_temperature") {
|
|
message = "Suhu sangat tinggi! Kondisi darurat terdeteksi.";
|
|
} else if (type == "low_humidity") {
|
|
message = "Kelembapan sangat rendah! Kondisi darurat terdeteksi.";
|
|
} else if (type == "high_humidity") {
|
|
message = "Kelembapan sangat tinggi! Kondisi darurat terdeteksi.";
|
|
}
|
|
|
|
if (message.isNotEmpty) {
|
|
_addNotification(message);
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(changeSubscription);
|
|
} catch (e) {
|
|
print("❌ Error saat setup listener emergency: $e");
|
|
}
|
|
}
|
|
|
|
// Memuat data fuzzy dari Firebase
|
|
void _loadFuzzyData() {
|
|
try {
|
|
print("🔄 Memuat data fuzzy...");
|
|
// Memuat data fuzzy suhu
|
|
final tempSubscription = _database.child('fuzzy/temperature').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
print("🧠 Data fuzzy temperature diterima: ${event.snapshot.value}");
|
|
final Map<dynamic, dynamic> fuzzyTemp = event.snapshot.value as Map;
|
|
|
|
if (fuzzyTemp.containsKey('veryHigh') && (fuzzyTemp['veryHigh'] as num).toDouble() > 0.7) {
|
|
double value = (fuzzyTemp['veryHigh'] as num).toDouble();
|
|
print("🧠 Fuzzy suhu sangat tinggi terdeteksi: $value");
|
|
_addNotification('Fuzzy: Suhu sangat tinggi (${value.toStringAsFixed(2)})');
|
|
}
|
|
|
|
if (fuzzyTemp.containsKey('veryLow') && (fuzzyTemp['veryLow'] as num).toDouble() > 0.7) {
|
|
double value = (fuzzyTemp['veryLow'] as num).toDouble();
|
|
print("🧠 Fuzzy suhu sangat rendah terdeteksi: $value");
|
|
_addNotification('Fuzzy: Suhu sangat rendah (${value.toStringAsFixed(2)})');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data fuzzy temperature: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(tempSubscription);
|
|
|
|
// Memuat data fuzzy kelembapan
|
|
final humSubscription = _database.child('fuzzy/humidity').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
print("🧠 Data fuzzy humidity diterima: ${event.snapshot.value}");
|
|
final Map<dynamic, dynamic> fuzzyHum = event.snapshot.value as Map;
|
|
|
|
if (fuzzyHum.containsKey('veryHigh') && (fuzzyHum['veryHigh'] as num).toDouble() > 0.7) {
|
|
double value = (fuzzyHum['veryHigh'] as num).toDouble();
|
|
print("🧠 Fuzzy kelembapan sangat tinggi terdeteksi: $value");
|
|
_addNotification('Fuzzy: Kelembapan sangat tinggi (${value.toStringAsFixed(2)})');
|
|
}
|
|
|
|
if (fuzzyHum.containsKey('veryLow') && (fuzzyHum['veryLow'] as num).toDouble() > 0.7) {
|
|
double value = (fuzzyHum['veryLow'] as num).toDouble();
|
|
print("🧠 Fuzzy kelembapan sangat rendah terdeteksi: $value");
|
|
_addNotification('Fuzzy: Kelembapan sangat rendah (${value.toStringAsFixed(2)})');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data fuzzy humidity: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(humSubscription);
|
|
} catch (e) {
|
|
print("❌ Error saat setup listener fuzzy: $e");
|
|
}
|
|
}
|
|
|
|
// Memuat data status dari Firebase
|
|
void _loadStatusData() {
|
|
try {
|
|
print("🔄 Memuat data status...");
|
|
// Memuat status suhu
|
|
final tempSubscription = _database.child('status/temperature').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
String tempStatus = event.snapshot.value.toString();
|
|
print("📊 Status suhu diterima: $tempStatus");
|
|
|
|
if (tempStatus.contains("Sangat Tinggi") || tempStatus.contains("Sangat Rendah")) {
|
|
print("📊 Status suhu kritis terdeteksi: $tempStatus");
|
|
_addNotification('Status: $tempStatus');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data status temperature: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(tempSubscription);
|
|
|
|
// Memuat status kelembapan
|
|
final humSubscription = _database.child('status/humidity').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
String humStatus = event.snapshot.value.toString();
|
|
print("📊 Status kelembapan diterima: $humStatus");
|
|
|
|
if (humStatus.contains("Sangat Tinggi") || humStatus.contains("Sangat Rendah")) {
|
|
print("📊 Status kelembapan kritis terdeteksi: $humStatus");
|
|
_addNotification('Status: $humStatus');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data status humidity: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(humSubscription);
|
|
|
|
// Mendengarkan perubahan pada node status
|
|
final statusSubscription = _database.child('status').onChildChanged.listen((event) {
|
|
String status = event.snapshot.value.toString();
|
|
String type = event.snapshot.key ?? "unknown";
|
|
print("📊 Perubahan status terdeteksi: $type = $status");
|
|
|
|
if (status.contains("Sangat Tinggi") || status.contains("Sangat Rendah")) {
|
|
print("📊 Status kritis pada perubahan terdeteksi: $type = $status");
|
|
_addNotification('Status $type: $status');
|
|
}
|
|
});
|
|
_subscriptions.add(statusSubscription);
|
|
} catch (e) {
|
|
print("❌ Error saat setup listener status: $e");
|
|
}
|
|
}
|
|
|
|
void _loadSensorData() {
|
|
print("🔄 Memuat data sensor...");
|
|
final subscription = _database.child('sensor').onValue.listen((event) {
|
|
if (event.snapshot.value != null) {
|
|
try {
|
|
final Map<dynamic, dynamic> sensorData = event.snapshot.value as Map;
|
|
double temperature = (sensorData['temperature'] as num).toDouble();
|
|
double humidity = (sensorData['Humidity'] as num).toDouble();
|
|
|
|
print("🌡️ Data sensor diterima: Suhu=$temperature°C, Kelembapan=$humidity%");
|
|
|
|
// Tentukan rentang suhu berdasarkan umur ayam
|
|
double tempMin, tempMax, humMin, humMax;
|
|
switch (ageInWeeks) {
|
|
case 1:
|
|
tempMin = 33.0;
|
|
tempMax = 35.0;
|
|
humMin = 60.0;
|
|
humMax = 70.0;
|
|
break;
|
|
case 2:
|
|
tempMin = 30.0;
|
|
tempMax = 33.0;
|
|
humMin = 60.0;
|
|
humMax = 65.0;
|
|
break;
|
|
case 3:
|
|
tempMin = 28.0;
|
|
tempMax = 30.0;
|
|
humMin = 60.0;
|
|
humMax = 65.0;
|
|
break;
|
|
case 4:
|
|
tempMin = 25.0;
|
|
tempMax = 28.0;
|
|
humMin = 55.0;
|
|
humMax = 60.0;
|
|
break;
|
|
default:
|
|
tempMin = 25.0;
|
|
tempMax = 28.0;
|
|
humMin = 55.0;
|
|
humMax = 60.0;
|
|
}
|
|
|
|
// Buat notifikasi berdasarkan kondisi suhu
|
|
if (temperature < tempMin - 2) {
|
|
print("🌡️ Suhu sangat rendah terdeteksi: $temperature°C");
|
|
_addNotification('Suhu sangat rendah: $temperature°C (Kritis)');
|
|
} else if (temperature < tempMin) {
|
|
print("🌡️ Suhu rendah terdeteksi: $temperature°C");
|
|
_addNotification('Suhu rendah: $temperature°C');
|
|
} else if (temperature > tempMax + 2) {
|
|
print("🌡️ Suhu sangat tinggi terdeteksi: $temperature°C");
|
|
_addNotification('Suhu sangat tinggi: $temperature°C (Kritis)');
|
|
} else if (temperature > tempMax) {
|
|
print("🌡️ Suhu tinggi terdeteksi: $temperature°C");
|
|
_addNotification('Suhu tinggi: $temperature°C');
|
|
} else if (temperature >= tempMin && temperature <= tempMax) {
|
|
print("🌡️ Suhu normal terdeteksi: $temperature°C");
|
|
_addNotification('Suhu normal: $temperature°C (Optimal)');
|
|
}
|
|
|
|
// Buat notifikasi berdasarkan kondisi kelembapan
|
|
if (humidity < humMin - 5) {
|
|
print("💧 Kelembapan sangat rendah terdeteksi: $humidity%");
|
|
_addNotification('Kelembapan sangat rendah: $humidity% (Kritis)');
|
|
} else if (humidity < humMin) {
|
|
print("💧 Kelembapan rendah terdeteksi: $humidity%");
|
|
_addNotification('Kelembapan rendah: $humidity%');
|
|
} else if (humidity > humMax + 5) {
|
|
print("💧 Kelembapan sangat tinggi terdeteksi: $humidity%");
|
|
_addNotification('Kelembapan sangat tinggi: $humidity% (Kritis)');
|
|
} else if (humidity > humMax) {
|
|
print("💧 Kelembapan tinggi terdeteksi: $humidity%");
|
|
_addNotification('Kelembapan tinggi: $humidity%');
|
|
} else if (humidity >= humMin && humidity <= humMax) {
|
|
print("💧 Kelembapan normal terdeteksi: $humidity%");
|
|
_addNotification('Kelembapan normal: $humidity% (Optimal)');
|
|
}
|
|
} catch (e) {
|
|
print("❌ Error saat memproses data sensor: $e");
|
|
}
|
|
}
|
|
});
|
|
_subscriptions.add(subscription);
|
|
}
|
|
|
|
// Fungsi untuk menambahkan notifikasi baru
|
|
void _addNotification(String event) {
|
|
print("🔍 Mencoba menambahkan notifikasi: $event");
|
|
|
|
// Cek apakah notifikasi serupa sudah ditambahkan dalam 5 menit terakhir
|
|
if (_lastNotificationTimes.containsKey(event)) {
|
|
DateTime lastTime = _lastNotificationTimes[event]!;
|
|
Duration difference = DateTime.now().difference(lastTime);
|
|
|
|
// Jika notifikasi serupa sudah ditambahkan dalam 5 menit terakhir, abaikan
|
|
if (difference.inMinutes < 5) {
|
|
print("⏱️ Notifikasi serupa sudah ditambahkan ${difference.inSeconds} detik yang lalu, diabaikan: $event");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Catat waktu notifikasi ini ditambahkan
|
|
_lastNotificationTimes[event] = DateTime.now();
|
|
|
|
// Tambahkan informasi umur ayam ke event
|
|
String eventWithAge = "$event [Umur: $ageInWeeks minggu]";
|
|
|
|
// Simpan ke database lokal
|
|
dbHelper.insertNotification(eventWithAge).then((_) {
|
|
print("✅ Berhasil menyimpan notifikasi: $eventWithAge");
|
|
|
|
// Tampilkan notifikasi lokal
|
|
_showNotification('Peringatan Kandang Ayam', event);
|
|
}).catchError((error) {
|
|
print("❌ Gagal menyimpan notifikasi: $error");
|
|
});
|
|
}
|
|
} |