From cd2bc3e0cc0def3f1887f17b7844c80689abfbf6 Mon Sep 17 00:00:00 2001 From: kleponijo <158453666+kleponijo@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:59:19 +0700 Subject: [PATCH] vgv --- lib/screens/home/views/home_screen.dart | 241 +++++++++++++++++++++--- 1 file changed, 216 insertions(+), 25 deletions(-) diff --git a/lib/screens/home/views/home_screen.dart b/lib/screens/home/views/home_screen.dart index b7a85e1..4deb72a 100644 --- a/lib/screens/home/views/home_screen.dart +++ b/lib/screens/home/views/home_screen.dart @@ -1,14 +1,184 @@ import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'dart:async'; import '../../../blocs/authentication_bloc/authentication_bloc.dart'; import '../../monitoring/evaporasi_monitoring_screen.dart'; import '../../monitoring/wind_speed_monitoring_screen.dart'; import '../../monitoring/air_quality_monitoring_screen.dart'; -class HomeScreen extends StatelessWidget { +class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + // Firebase realtime database references + final DatabaseReference _evaporasiRef = FirebaseDatabase.instance.ref('evaporasi/realtime'); + final DatabaseReference _windSpeedRef = FirebaseDatabase.instance.ref('anemometer/realtime'); + final DatabaseReference _airQualityRef = FirebaseDatabase.instance.ref('air_quality/realtime'); + + // Stream subscriptions + StreamSubscription? _evaporasiSub; + StreamSubscription? _windSpeedSub; + StreamSubscription? _airQualitySub; + + // Realtime values + double _currentEvaporasi = 0.0; + double _currentWindSpeed = 0.0; + double _currentAirQuality = 0.0; + + DateTime? _evaporasiUpdateTime; + DateTime? _windSpeedUpdateTime; + DateTime? _airQualityUpdateTime; + + // Hourly data for charts (24 hours) + final List _evaporasiData = List.filled(24, 0.0); + final List _windSpeedData = List.filled(24, 0.0); + final List _airQualityData = List.filled(24, 0.0); + + // Hourly aggregation helpers + double _evaporasiHourlyTotal = 0.0; + int _evaporasiHourlyCount = 0; + + double _windSpeedHourlyTotal = 0.0; + int _windSpeedHourlyCount = 0; + + double _airQualityHourlyTotal = 0.0; + int _airQualityHourlyCount = 0; + + int _currentHour = DateTime.now().hour; + + @override + void initState() { + super.initState(); + _evaporasiSub = _evaporasiRef.onValue.listen(_onEvaporasiData); + _windSpeedSub = _windSpeedRef.onValue.listen(_onWindSpeedData); + _airQualitySub = _airQualityRef.onValue.listen(_onAirQualityData); + } + + void _onEvaporasiData(DatabaseEvent event) { + final data = event.snapshot.value; + if (data is Map) { + final value = (data['nilai'] ?? 0).toDouble(); + final timestamp = data['timestamp'] ?? 0; + final waktu = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); + + setState(() { + _currentEvaporasi = value; + _evaporasiUpdateTime = waktu; + + final incomingHour = waktu.hour; + + if (incomingHour != _currentHour) { + if (_evaporasiHourlyCount > 0) { + final avg = _evaporasiHourlyTotal / _evaporasiHourlyCount; + _evaporasiData[_currentHour] = avg; + } + + _evaporasiHourlyTotal = 0.0; + _evaporasiHourlyCount = 0; + + _currentHour = incomingHour; + + if (incomingHour == 0) { + for (int i = 0; i < 24; i++) { + _evaporasiData[i] = 0.0; + } + } + } + + _evaporasiHourlyTotal += value; + _evaporasiHourlyCount++; + }); + } + } + + void _onWindSpeedData(DatabaseEvent event) { + final data = event.snapshot.value; + if (data is Map) { + final value = (data['kecepatan'] ?? 0).toDouble(); + final timestamp = data['timestamp'] ?? 0; + final waktu = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); + + setState(() { + _currentWindSpeed = value; + _windSpeedUpdateTime = waktu; + + final incomingHour = waktu.hour; + + if (incomingHour != _currentHour) { + if (_windSpeedHourlyCount > 0) { + final avg = _windSpeedHourlyTotal / _windSpeedHourlyCount; + _windSpeedData[_currentHour] = avg; + } + + _windSpeedHourlyTotal = 0.0; + _windSpeedHourlyCount = 0; + + _currentHour = incomingHour; + + if (incomingHour == 0) { + for (int i = 0; i < 24; i++) { + _windSpeedData[i] = 0.0; + } + } + } + + _windSpeedHourlyTotal += value; + _windSpeedHourlyCount++; + }); + } + } + + void _onAirQualityData(DatabaseEvent event) { + final data = event.snapshot.value; + if (data is Map) { + final value = (data['aqi'] ?? 0).toDouble(); + final timestamp = data['timestamp'] ?? 0; + final waktu = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); + + setState(() { + _currentAirQuality = value; + _airQualityUpdateTime = waktu; + + final incomingHour = waktu.hour; + + if (incomingHour != _currentHour) { + if (_airQualityHourlyCount > 0) { + final avg = _airQualityHourlyTotal / _airQualityHourlyCount; + _airQualityData[_currentHour] = avg; + } + + _airQualityHourlyTotal = 0.0; + _airQualityHourlyCount = 0; + + _currentHour = incomingHour; + + if (incomingHour == 0) { + for (int i = 0; i < 24; i++) { + _airQualityData[i] = 0.0; + } + } + } + + _airQualityHourlyTotal += value; + _airQualityHourlyCount++; + }); + } + } + + @override + void dispose() { + _evaporasiSub?.cancel(); + _windSpeedSub?.cancel(); + _airQualitySub?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -142,21 +312,21 @@ class HomeScreen extends StatelessWidget { children: [ _buildDataBlock( "Evaporasi", - "45.2", + _currentEvaporasi.toStringAsFixed(1), "mm", Colors.blue, Icons.opacity, ), _buildDataBlock( "Kec. Angin", - "12.5", + _currentWindSpeed.toStringAsFixed(1), "km/h", Colors.green, Icons.air, ), _buildDataBlock( "Kualitas Udara", - "78", + _currentAirQuality.toStringAsFixed(0), "AQI", Colors.orange, Icons.cloud, @@ -461,22 +631,22 @@ class HomeScreen extends StatelessWidget { /// GET MONITORING DATA FOR 24 HOURS List _getMonitoringData(String title) { - final dataMap = { - "Evaporasi": [ - 2.1, 2.3, 2.0, 1.9, 2.2, 2.5, 3.0, 3.5, 4.0, 4.2, - 4.5, 4.8, 5.0, 5.2, 5.1, 4.9, 4.5, 4.0, 3.5, 3.0, 2.8, 2.5, 2.3, 2.2 - ], - "Kecepatan Angin": [ - 5.0, 5.2, 4.8, 4.5, 4.3, 4.6, 5.1, 6.0, 7.2, 8.5, - 9.0, 10.2, 11.0, 10.5, 10.0, 9.2, 8.0, 6.5, 5.5, 5.0, 4.8, 4.5, 5.0, 5.2 - ], - "Kualitas Udara": [ - 68.0, 70.0, 72.0, 75.0, 78.0, 80.0, 78.0, 75.0, 72.0, 70.0, - 68.0, 65.0, 60.0, 58.0, 60.0, 62.0, 65.0, 68.0, 70.0, 72.0, 75.0, 78.0, 76.0, 74.0 - ], - }; + late final List data; + + switch (title) { + case "Evaporasi": + data = _evaporasiData; + break; + case "Kecepatan Angin": + data = _windSpeedData; + break; + case "Kualitas Udara": + data = _airQualityData; + break; + default: + data = []; + } - final data = dataMap[title] ?? []; return List.generate( data.length, (index) => FlSpot(index.toDouble(), data[index]), @@ -485,12 +655,33 @@ class HomeScreen extends StatelessWidget { /// GET AVERAGE VALUE String _getAverageData(String title) { - final dataMap = { - "Evaporasi": "3.5 mm", - "Kecepatan Angin": "6.8 km/h", - "Kualitas Udara": "70 AQI", - }; - return dataMap[title] ?? "N/A"; + late final double average; + late final String unit; + + switch (title) { + case "Evaporasi": + average = _evaporasiData.isNotEmpty + ? _evaporasiData.reduce((a, b) => a + b) / _evaporasiData.length + : 0.0; + unit = "mm"; + break; + case "Kecepatan Angin": + average = _windSpeedData.isNotEmpty + ? _windSpeedData.reduce((a, b) => a + b) / _windSpeedData.length + : 0.0; + unit = "km/h"; + break; + case "Kualitas Udara": + average = _airQualityData.isNotEmpty + ? _airQualityData.reduce((a, b) => a + b) / _airQualityData.length + : 0.0; + unit = "AQI"; + break; + default: + return "N/A"; + } + + return "${average.toStringAsFixed(1)} $unit"; } /// GET COLOR BY TITLE