import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:mqtt_client/mqtt_client.dart'; import '../services/mqtt_service.dart'; import '../services/supabase_service.dart'; class MonitoringScreen extends StatefulWidget { const MonitoringScreen({super.key}); @override State createState() => _MonitoringScreenState(); } class _MonitoringScreenState extends State { bool _isInitialLoading = true; @override void initState() { super.initState(); _initData(); } Future _initData() async { setState(() => _isInitialLoading = true); await _loadLimits(); await _loadLastSensorData(); if (mounted) setState(() => _isInitialLoading = false); } Future _loadLastSensorData() async { try { final lastLog = await SupabaseService().getLatestLog(); if (lastLog != null && mounted) { final mqtt = Provider.of(context, listen: false); // Update local MQTT service state with last DB values if currently "0" or default // This ensures the user sees the last known data immediately mqtt.updateFromLastLog( lastLog['suhu'].toString(), lastLog['kelembapan'].toString(), lastLog['intensitas'].toString(), lastLog['kipas1'] == 'ON', lastLog['kipas2'] == 'ON' ); } } catch (e) { debugPrint('Error loading last log: $e'); } } Future _loadLimits() async { try { final data = await SupabaseService().getBatasSensor(); if (data != null && mounted) { Provider.of(context, listen: false).setLimits( double.parse(data['suhu_max'].toString()), double.parse(data['rh_max'].toString()), ); } } catch (e) { debugPrint('Error loading limits: $e'); } } @override Widget build(BuildContext context) { final mqtt = Provider.of(context); final curTemp = double.tryParse(mqtt.temp) ?? 0; final curHum = double.tryParse(mqtt.humidity) ?? 0; return Scaffold( backgroundColor: Colors.black, body: SafeArea( child: _isInitialLoading ? const Center(child: CircularProgressIndicator(color: Colors.green)) : RefreshIndicator( onRefresh: _initData, color: Colors.green, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Monitoring', style: TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold), ), const Text( 'Sistem Pengeringan Kopi', style: TextStyle(color: Colors.grey, fontSize: 16), ), const SizedBox(height: 5), Row( children: [ Container( width: 8, height: 8, decoration: BoxDecoration( shape: BoxShape.circle, color: mqtt.client?.connectionStatus?.state == MqttConnectionState.connected ? Colors.green : Colors.red, ), ), const SizedBox(width: 8), Text( mqtt.client?.connectionStatus?.state == MqttConnectionState.connected ? 'Connected' : 'Disconnected', style: TextStyle( color: mqtt.client?.connectionStatus?.state == MqttConnectionState.connected ? Colors.green : Colors.red, fontSize: 12, ), ), ], ), ], ), const CircleAvatar( backgroundColor: Color(0xFF1A1A1A), radius: 25, child: Icon(Icons.person, color: Colors.green), ), ], ), const SizedBox(height: 30), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, crossAxisSpacing: 15, mainAxisSpacing: 15, children: [ _buildSensorCard( 'Suhu', '${mqtt.temp}°C', Icons.thermostat, curTemp > mqtt.maxTemp ? Colors.red : Colors.redAccent, isAlert: curTemp > mqtt.maxTemp, onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Suhu (°C)', 'color': Colors.redAccent, 'value': '${mqtt.temp}°C'}), ), _buildSensorCard( 'Kelembaban', '${mqtt.humidity}%', Icons.water_drop, curHum > mqtt.maxHum ? Colors.red : Colors.cyan, isAlert: curHum > mqtt.maxHum, onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Kelembaban (%)', 'color': Colors.cyan, 'value': '${mqtt.humidity}%'}), ), _buildSensorCard( 'Cahaya', '${mqtt.light} Lux', Icons.wb_sunny, Colors.orange, onTap: () => Navigator.pushNamed(context, '/analytics', arguments: {'title': 'Grafik Cahaya (Lux)', 'color': Colors.orange, 'value': '${mqtt.light} Lux'}), ), _buildControlCard( 'Intake', mqtt.isIntakeOn, (val) async { final status = val ? 'ON' : 'OFF'; mqtt.publish('coffee/intake', status); await SupabaseService().logFanAction(status, mqtt.isExhaustOn ? 'ON' : 'OFF', curTemp, curHum, double.tryParse(mqtt.light) ?? 0); }, mqtt, ), _buildControlCard( 'Exhaust', mqtt.isExhaustOn, (val) async { final status = val ? 'ON' : 'OFF'; mqtt.publish('coffee/exhaust', status); await SupabaseService().logFanAction(mqtt.isIntakeOn ? 'ON' : 'OFF', status, curTemp, curHum, double.tryParse(mqtt.light) ?? 0); }, mqtt, ), ], ), if (curTemp > mqtt.maxTemp || curHum > mqtt.maxHum) Container( margin: const EdgeInsets.only(top: 20), padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.red.withOpacity(0.2), borderRadius: BorderRadius.circular(15), border: Border.all(color: Colors.red), ), child: Row( children: const [ Icon(Icons.warning_amber_rounded, color: Colors.red), SizedBox(width: 10), Expanded( child: Text( 'Peringatan: Parameter pengeringan melewati batas aman!', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold, fontSize: 12), ), ), ], ), ), ], ), ), ), ), ), ); } Widget _buildSensorCard(String title, String value, IconData icon, Color color, {bool isAlert = false, VoidCallback? onTap}) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: isAlert ? Colors.red.withOpacity(0.1) : const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(20), border: isAlert ? Border.all(color: Colors.red, width: 2) : null, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color, size: 40), const SizedBox(height: 10), Text(title, style: const TextStyle(color: Colors.grey, fontSize: 14)), const SizedBox(height: 5), Text(value, style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold)), ], ), ), ); } Widget _buildControlCard(String title, bool isOn, Function(bool) onChanged, MqttService mqtt) { return Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(20), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.settings_input_component, color: Colors.green, size: 20), const SizedBox(width: 8), Text(title, style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold)), ], ), const Spacer(), Text('Mode: ${mqtt.currentMode}', style: const TextStyle(color: Colors.green, fontSize: 9, fontWeight: FontWeight.bold)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(isOn ? 'ON' : 'OFF', style: const TextStyle(color: Colors.grey, fontSize: 12)), SizedBox( height: 30, child: Switch(value: isOn, onChanged: onChanged, activeColor: Colors.green), ), ], ), ], ), ); } }