TKK_E32230814/railway-worker/lib/services/firebase_database_service.dart

455 lines
12 KiB
Dart

import 'package:firebase_database/firebase_database.dart';
class FirebaseDatabaseService {
static final FirebaseDatabaseService _instance =
FirebaseDatabaseService._internal();
factory FirebaseDatabaseService() => _instance;
FirebaseDatabaseService._internal();
final DatabaseReference _database = FirebaseDatabase.instance.ref();
// ==================== SENSOR DATA ====================
Stream<Map<String, dynamic>> getSensorDataStream() {
return _database.child('data').onValue.map((event) {
if (event.snapshot.value != null) {
final data = Map<String, dynamic>.from(event.snapshot.value as Map);
return {
'suhu': _parseValue(data['suhu']),
'kelembapan': _parseValue(data['kelembapan']),
'ldr': _parseValue(data['ldr']),
'water_flow': data['water_flow'] ?? 0,
'soil_1': _parseValue(data['soil_1']),
'soil_2': _parseValue(data['soil_2']),
'soil_3': _parseValue(data['soil_3']),
'soil_4': _parseValue(data['soil_4']),
'soil_5': _parseValue(data['soil_5']),
};
}
return {};
});
}
Future<Map<String, dynamic>> getSensorData() async {
try {
final snapshot = await _database.child('data').get();
if (snapshot.exists) {
final data = Map<String, dynamic>.from(snapshot.value as Map);
return {
'suhu': _parseValue(data['suhu']),
'kelembapan': _parseValue(data['kelembapan']),
'ldr': _parseValue(data['ldr']),
'water_flow': data['water_flow'] ?? 0,
'soil_1': _parseValue(data['soil_1']),
'soil_2': _parseValue(data['soil_2']),
'soil_3': _parseValue(data['soil_3']),
'soil_4': _parseValue(data['soil_4']),
'soil_5': _parseValue(data['soil_5']),
};
}
} catch (e) {
print('Error getting sensor data: $e');
}
return {};
}
// ==================== AKTUATOR CONTROL ====================
Stream<Map<String, bool>> getAktuatorStream() {
return _database.child('aktuator').onValue.map((event) {
if (event.snapshot.value != null) {
final data = Map<String, dynamic>.from(event.snapshot.value as Map);
return {
'mosvet_1': data['mosvet_1'] ?? false,
'mosvet_2': data['mosvet_2'] ?? false,
'mosvet_3': data['mosvet_3'] ?? false,
'mosvet_4': data['mosvet_4'] ?? false,
'mosvet_5': data['mosvet_5'] ?? false,
'mosvet_6': data['mosvet_6'] ?? false,
'mosvet_7': data['mosvet_7'] ?? false,
'mosvet_8': data['mosvet_8'] ?? false,
};
}
return {};
});
}
Future<void> setAktuator(String mosfetName, bool value) async {
try {
await _database.child('aktuator/$mosfetName').set(value);
} catch (e) {
print('Error setting aktuator: $e');
rethrow;
}
}
Future<void> setPompaAir(bool value) async {
await setAktuator('mosvet_1', value);
}
Future<void> setPompaPupuk(bool value) async {
await setAktuator('mosvet_2', value);
}
Future<void> setPengaduk(bool value) async {
await setAktuator('mosvet_8', value);
}
Future<void> setPot(int potNumber, bool value) async {
if (potNumber < 1 || potNumber > 5) {
throw ArgumentError('Pot number must be between 1 and 5');
}
await setAktuator('mosvet_${potNumber + 2}', value);
}
Future<void> setMultipleAktuator(Map<String, bool> aktuatorStates) async {
try {
await _database.child('aktuator').update(aktuatorStates);
} catch (e) {
print('Error setting multiple aktuator: $e');
rethrow;
}
}
Future<void> turnOffAllAktuator() async {
await setMultipleAktuator({
'mosvet_1': false,
'mosvet_2': false,
'mosvet_3': false,
'mosvet_4': false,
'mosvet_5': false,
'mosvet_6': false,
'mosvet_7': false,
'mosvet_8': false,
});
}
// ==================== KONTROL CONFIG ====================
Stream<Map<String, dynamic>> getKontrolStream() {
return _database.child('kontrol').onValue.map((event) {
if (event.snapshot.value != null) {
return Map<String, dynamic>.from(event.snapshot.value as Map);
}
return {};
});
}
Future<Map<String, dynamic>> getKontrolConfig() async {
try {
final snapshot = await _database.child('kontrol').get();
if (snapshot.exists) {
return Map<String, dynamic>.from(snapshot.value as Map);
}
} catch (e) {
print('Error getting kontrol config: $e');
}
return {};
}
Future<void> updateKontrolConfig(Map<String, dynamic> config) async {
try {
await _database.child('kontrol').update(config);
} catch (e) {
print('Error updating kontrol config: $e');
rethrow;
}
}
Future<void> setThreshold({int? batasAtas, int? batasBawah}) async {
try {
final updates = <String, dynamic>{};
if (batasAtas != null) updates['batas_atas'] = batasAtas;
if (batasBawah != null) updates['batas_bawah'] = batasBawah;
await _database.child('kontrol').update(updates);
} catch (e) {
print('Error setting threshold: $e');
rethrow;
}
}
Future<void> setWaktuPenyiraman({
String? waktu1,
String? waktu2,
bool? waktuEnabled,
}) async {
try {
final updates = <String, dynamic>{};
if (waktu1 != null) updates['waktu_1'] = waktu1;
if (waktu2 != null) updates['waktu_2'] = waktu2;
if (waktuEnabled != null) updates['waktu'] = waktuEnabled;
await _database.child('kontrol').update(updates);
} catch (e) {
print('Error setting waktu: $e');
rethrow;
}
}
Future<void> setDurasi({int? durasi1, int? durasi2}) async {
try {
final updates = <String, dynamic>{};
if (durasi1 != null) updates['durasi_1'] = durasi1;
if (durasi2 != null) updates['durasi_2'] = durasi2;
await _database.child('kontrol').update(updates);
} catch (e) {
print('Error setting durasi: $e');
rethrow;
}
}
Future<void> setOtomatis(bool value) async {
try {
await _database.child('kontrol/otomatis').set(value);
} catch (e) {
print('Error setting otomatis: $e');
rethrow;
}
}
// ==================== HISTORY LOGGING BARU ====================
Future<void> saveHistory(Map<String, dynamic> sensorData) async {
try {
final now = DateTime.now();
for (int pot = 1; pot <= 5; pot++) {
await _database.child('history').push().set({
'timestamp': now.millisecondsSinceEpoch,
'tanggal': '${now.day}/${now.month}/${now.year}',
'waktu':
'${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}',
'jenis_histori': 'monitoring',
'source': 'flutter_app',
'mode': '-',
'type': 'auto_log',
'pot': pot,
'nama_pot': 'Pot $pot',
'suhu': _toDouble(sensorData['suhu']),
'kelembapan': _toDouble(sensorData['kelembapan']),
'ldr': _toDouble(sensorData['ldr']),
'soil': _toDouble(sensorData['soil_$pot']),
'durasi': 0,
});
}
} catch (e) {
print('Error saving history: $e');
}
}
Future<void> saveManualWateringHistory({
required List<int> pots,
int duration = 0,
}) async {
try {
if (pots.isEmpty) return;
final now = DateTime.now();
final sensorData = await getSensorData();
for (final pot in pots) {
await _database.child('history').push().set({
'timestamp': now.millisecondsSinceEpoch,
'tanggal': '${now.day}/${now.month}/${now.year}',
'waktu':
'${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}',
'jenis_histori': 'penyiraman',
'source': 'flutter_app',
'mode': 'manual',
'type': 'manual',
'pot': pot,
'nama_pot': 'Pot $pot',
'suhu': _toDouble(sensorData['suhu']),
'kelembapan': _toDouble(sensorData['kelembapan']),
'ldr': _toDouble(sensorData['ldr']),
'soil': _toDouble(sensorData['soil_$pot']),
'durasi': duration,
});
}
} catch (e) {
print('Error saving manual watering history: $e');
}
}
Future<void> saveMonitoringHistory() async {
try {
final sensorData = await getSensorData();
await saveHistory(sensorData);
} catch (e) {
print('Error saving monitoring history: $e');
}
}
Future<List<Map<String, dynamic>>> getHistoryListByDateRange(
DateTime startDate,
DateTime endDate,
) async {
try {
final start =
DateTime(
startDate.year,
startDate.month,
startDate.day,
).millisecondsSinceEpoch;
final end =
DateTime(
endDate.year,
endDate.month,
endDate.day,
23,
59,
59,
999,
).millisecondsSinceEpoch;
final snapshot =
await _database
.child('history')
.orderByChild('timestamp')
.startAt(start)
.endAt(end)
.get();
if (!snapshot.exists) return [];
final rawData = Map<String, dynamic>.from(snapshot.value as Map);
final result = <Map<String, dynamic>>[];
rawData.forEach((key, value) {
if (value is Map) {
final item = Map<String, dynamic>.from(value);
item['id'] = key;
result.add(item);
}
});
result.sort((a, b) {
final ta = int.tryParse(a['timestamp']?.toString() ?? '0') ?? 0;
final tb = int.tryParse(b['timestamp']?.toString() ?? '0') ?? 0;
return tb.compareTo(ta);
});
return result;
} catch (e) {
print('Error getting history list: $e');
return [];
}
}
Future<List<Map<String, dynamic>>> getLatestHistoryList({
int limit = 100,
}) async {
try {
final snapshot =
await _database
.child('history')
.orderByChild('timestamp')
.limitToLast(limit)
.get();
if (!snapshot.exists) return [];
final rawData = Map<String, dynamic>.from(snapshot.value as Map);
final result = <Map<String, dynamic>>[];
rawData.forEach((key, value) {
if (value is Map) {
final item = Map<String, dynamic>.from(value);
item['id'] = key;
result.add(item);
}
});
result.sort((a, b) {
final ta = int.tryParse(a['timestamp']?.toString() ?? '0') ?? 0;
final tb = int.tryParse(b['timestamp']?.toString() ?? '0') ?? 0;
return tb.compareTo(ta);
});
return result;
} catch (e) {
print('Error getting latest history list: $e');
return [];
}
}
Future<void> clearOldHistory({int daysToKeep = 30}) async {
try {
final cutoff =
DateTime.now()
.subtract(Duration(days: daysToKeep))
.millisecondsSinceEpoch;
final snapshot = await _database.child('history').get();
if (!snapshot.exists) return;
final data = Map<String, dynamic>.from(snapshot.value as Map);
for (final key in data.keys) {
final item = data[key];
if (item is Map) {
final timestamp =
int.tryParse(item['timestamp']?.toString() ?? '0') ?? 0;
if (timestamp > 0 && timestamp < cutoff) {
await _database.child('history/$key').remove();
}
}
}
} catch (e) {
print('Error clearing old history: $e');
}
}
// ==================== HELPER FUNCTIONS ====================
String _parseValue(dynamic value) {
if (value == null || value == '') {
return '0';
}
return value.toString();
}
double _toDouble(dynamic value) {
return double.tryParse(value?.toString() ?? '0') ?? 0;
}
DatabaseReference get databaseRef => _database;
Stream<bool> getConnectionStatus() {
return _database.child('.info/connected').onValue.map((event) {
return event.snapshot.value as bool? ?? false;
});
}
}