import 'package:flutter/material.dart'; import 'models/sensor_data.dart'; import 'services/firebase_service.dart'; import 'services/notification_service.dart'; import 'dart:async'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final FirebaseService _firebaseService = FirebaseService(); final TextEditingController _jamController = TextEditingController(); final TextEditingController _menitController = TextEditingController(); final TextEditingController _detikController = TextEditingController(); final TextEditingController _suhuMaksController = TextEditingController(); final TextEditingController _suhuMinController = TextEditingController(); final TextEditingController _kadarAirController = TextEditingController(); int? _lastTimer; Timer? _koneksiTimer; @override void initState() { super.initState(); NotificationService.initialize(); _firebaseService.syncPathToFirestore('sensorData', 'sensorData_firestore'); _firebaseService.listenAndMirror('sensorData', 'sensorData_firestore'); _koneksiTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _firebaseService.updateKoneksiAndroid(true); }); } @override void dispose() { _koneksiTimer?.cancel(); _jamController.dispose(); _menitController.dispose(); _detikController.dispose(); _suhuMaksController.dispose(); _suhuMinController.dispose(); _kadarAirController.dispose(); super.dispose(); } String formatWaktu(int totalDetik) { final jam = totalDetik ~/ 3600; final sisaDetik = totalDetik % 3600; final menit = sisaDetik ~/ 60; final detik = sisaDetik % 60; return '${jam.toString().padLeft(2, '0')}:' '${menit.toString().padLeft(2, '0')}:' '${detik.toString().padLeft(2, '0')}'; } void _checkTimer(int currentTimer) { if (_lastTimer != null && _lastTimer! > 0 && currentTimer == 0) { NotificationService.showNotification( title: 'Timer Selesai', body: 'Proses pengeringan jagung telah selesai!', ); } _lastTimer = currentTimer; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.blueGrey.shade50, appBar: AppBar( title: const Center(child: Text('Smartcorn Dryer')), backgroundColor: Colors.blue.shade800, foregroundColor: Colors.white, ), body: StreamBuilder( stream: _firebaseService.getSensorData(), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final data = snapshot.data!; _checkTimer(data.timer); return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Data Sensor Card Card( elevation: 4, color: Colors.white, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Data Sensor', style: Theme.of(context) .textTheme .titleLarge ?.copyWith( fontWeight: FontWeight.bold, color: Colors.blueGrey.shade800), ), const SizedBox(height: 16), Row( children: [ Icon(Icons.thermostat, color: Colors.blue.shade700), const SizedBox(width: 8), Text( 'Suhu: ${data.suhu.toStringAsFixed(2)}°C', style: Theme.of(context) .textTheme .titleMedium ?.copyWith(color: Colors.blueGrey.shade800), ), ], ), ], ), ), ), const SizedBox(height: 16), // Pengaturan Timer & Suhu Card Card( elevation: 4, color: Colors.white, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Pengaturan Timer (Otomatisasi)', style: Theme.of(context) .textTheme .titleLarge ?.copyWith( fontWeight: FontWeight.bold, color: Colors.blueGrey.shade800), ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: _jamController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Jam', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: _menitController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Menit', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: _detikController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Detik', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), Column( children: [ SizedBox( width: 110, child: ElevatedButton( onPressed: () { final jam = int.tryParse(_jamController.text) ?? 0; final menit = int.tryParse(_menitController.text) ?? 0; final detik = int.tryParse(_detikController.text) ?? 0; final totalDetik = jam * 3600 + menit * 60 + detik; _firebaseService.updateTimer(totalDetik); _jamController.clear(); _menitController.clear(); _detikController.clear(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), ), child: const Text('Atur\nTimer', textAlign: TextAlign.center), ), ), const SizedBox(height: 12), SizedBox( width: 110, child: ElevatedButton( onPressed: () { _firebaseService.resetTimer(); _firebaseService.updateManualMode(true); }, style: ElevatedButton.styleFrom( backgroundColor: const Color.fromARGB(255, 255, 0, 0), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), ), child: const Text('Reset\nTimer', textAlign: TextAlign.center), ), ), ], ), ], ), const SizedBox(height: 16), Text( 'Batas Suhu', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blueGrey.shade800), ), const SizedBox(height: 8), Row( children: [ Expanded( child: TextField( controller: _suhuMinController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Minimal (°C)', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: _suhuMaksController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Maksimal (°C)', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), SizedBox( width: 120, child: ElevatedButton( onPressed: () { _firebaseService.updateSuhuMin( int.tryParse(_suhuMinController.text) ?? 0); _firebaseService.updateSuhuMaks( int.tryParse(_suhuMaksController.text) ?? 0); _suhuMinController.clear(); _suhuMaksController.clear(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white, ), child: const Text('Atur Suhu'), ), ), ], ), const SizedBox(height: 16), Row( children: [ Icon(Icons.timer, color: Colors.blue.shade700), const SizedBox(width: 8), Text( 'Sisa Waktu: ${formatWaktu(data.timer)}', style: Theme.of(context) .textTheme .titleMedium ?.copyWith(color: Colors.blueGrey.shade800), ), ], ), const SizedBox(height: 16), Row( children: [ Icon(Icons.thermostat_outlined, color: Colors.blue.shade700), const SizedBox(width: 8), Text( 'Batas Suhu saat ini: ' '${data.suhuMin}°C - ${data.suhuMaks}°C', style: Theme.of(context) .textTheme .titleMedium ?.copyWith(color: Colors.blueGrey.shade800), ), ], ), const SizedBox(height: 16), Row( children: [ Icon(Icons.power_settings_new, color: Colors.blue.shade700), const SizedBox(width: 8), Text( 'Status Relay: ${data.elemen.toUpperCase()}', style: Theme.of(context) .textTheme .titleMedium ?.copyWith(color: Colors.blueGrey.shade800), ), ], ), const SizedBox(height: 24), ], ), ), ), const SizedBox(height: 16), // Kontrol Manual Card Card( elevation: 4, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Kontrol Manual', style: Theme.of(context) .textTheme .titleLarge ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 16), // Mode Manual ListTile( contentPadding: EdgeInsets.zero, title: const Text('Mode Manual'), subtitle: Text(data.manualMode ? 'Aktif' : 'Tidak Aktif'), trailing: SizedBox( width: 60, child: Switch( value: data.manualMode, onChanged: (value) { _firebaseService.updateManualMode(value); }, ), ), ), // Kontrol Elemen ListTile( contentPadding: EdgeInsets.zero, title: const Text('Kontrol Elemen'), subtitle: Text(data.manualButton ? 'Menyala' : 'Mati'), trailing: SizedBox( width: 60, child: Switch( value: data.manualButton, onChanged: (value) { _firebaseService.updateManualButton(value); }, ), ), ), ], ), ), ), const SizedBox(height: 16), Card( elevation: 4, color: Colors.white, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Input Kadar Air', style: Theme.of(context) .textTheme .titleLarge ?.copyWith( fontWeight: FontWeight.bold, color: Colors.blueGrey.shade800), ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: _kadarAirController, keyboardType: TextInputType.numberWithOptions( decimal: true), decoration: InputDecoration( labelText: 'Kadar Air (%)', border: OutlineInputBorder(), labelStyle: TextStyle(color: Colors.blueGrey), enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blueGrey)), focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.blue)), ), style: TextStyle(color: Colors.blueGrey.shade900), ), ), const SizedBox(width: 8), SizedBox( width: 120, child: ElevatedButton( onPressed: () { final kadarAir = double.tryParse(_kadarAirController.text); if (kadarAir != null) { _firebaseService.updateKadarAir(kadarAir); _kadarAirController.clear(); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Input tidak valid')), ); } }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade700, foregroundColor: Colors.white, ), child: const Text('Kirim'), ), ), ], ), ], ), ), ), ], ), ); }, ), ); } }