From fd75feb34fc2c47f8b4feeb7f8299f1913d245c0 Mon Sep 17 00:00:00 2001 From: kleponijo <158453666+kleponijo@users.noreply.github.com> Date: Sun, 19 Apr 2026 12:44:37 +0700 Subject: [PATCH] joss --- images/logo_klimatologi.png | Bin 0 -> 64554 bytes lib/screens/home/views/main_drawer.dart | 36 +- .../blocs/atmospheric_conditions_bloc.dart | 69 + .../blocs/atmospheric_conditions_event.dart | 10 + .../blocs/atmospheric_conditions_state.dart | 43 + .../views/atmospheric_screen.dart | 150 +++ .../evaporasi/blocs/evaporasi_bloc.dart | 42 +- .../evaporasi/blocs/evaporasi_event.dart | 21 + .../evaporasi/blocs/evaporasi_state.dart | 54 + .../evaporasi/views/evaporasi_screen.dart | 1149 ++--------------- .../src/models/atmospheric_conditions.dart | 25 + .../lib/src/models/evaporasi.dart | 36 + .../lib/src/models/models.dart | 2 +- .../lib/src/models/pressure_sensor.dart | 0 14 files changed, 586 insertions(+), 1051 deletions(-) create mode 100644 images/logo_klimatologi.png create mode 100644 lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart create mode 100644 lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_event.dart create mode 100644 lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_state.dart create mode 100644 lib/screens/monitoring/atmospheric_conditions/views/atmospheric_screen.dart create mode 100644 packages/monitoring_repository/lib/src/models/atmospheric_conditions.dart delete mode 100644 packages/monitoring_repository/lib/src/models/pressure_sensor.dart diff --git a/images/logo_klimatologi.png b/images/logo_klimatologi.png new file mode 100644 index 0000000000000000000000000000000000000000..e127653d971d1e9ae30b52149d0da11bda229597 GIT binary patch literal 64554 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
N=O3u<8M!0!VZ37d<;cTHeJ<{|;|pdh^DBn^ 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 BlocProvider( + create: (context) => EvaporasiBloc( + repository: context.read(), + )..add(WatchEvaporasiStarted()), + child: const EvaporasiScreen(), + ), + ), + ); + }, ), ListTile( leading: Image.asset( @@ -76,8 +93,21 @@ class MainDrawer extends StatelessWidget { height: 20, width: 20, ), - title: const Text("Tekanan Udara"), - onTap: () {}, + title: const Text("Kondisi Atmosfer"), + onTap: () { + Navigator.pop(context); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => BlocProvider( + create: (context) => AtmosphericConditionsBloc( + repository: context.read(), + )..add(WatchAtmosphericConditionsStarted()), + child: AtmosphericScreen(), + ), + ), + ); + }, ), const Spacer(), // Dorong menu logout ke paling bawah const Divider(), diff --git a/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart new file mode 100644 index 0000000..a4d6cb5 --- /dev/null +++ b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart @@ -0,0 +1,69 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:monitoring_repository/monitoring_repository.dart'; + +part 'atmospheric_conditions_event.dart'; +part 'atmospheric_conditions_state.dart'; + +class AtmosphericConditionsBloc + extends Bloc { + final MonitoringRepository _repository; + StreamSubscription? _subscription; + + AtmosphericConditionsBloc({required MonitoringRepository repository}) + : _repository = repository, + super(const AtmosphericConditionsState()) { + on(_onStarted); + on<_AtmosphericConditionsUpdated>(_onUpdated); + } + + /// 🚀 START + Future _onStarted( + WatchAtmosphericConditionsStarted event, + Emitter emit, + ) async { + emit(state.copyWith(isLoading: true)); + + await _subscription?.cancel(); + + _subscription = _repository + .getSensorStream( + 'sensor/latest', + (json) => AtmosphericConditions.fromJson(json), + ) + .listen((data) { + add(_AtmosphericConditionsUpdated(data)); + }); + } + + /// ⚡ REALTIME UPDATE + void _onUpdated( + _AtmosphericConditionsUpdated event, + Emitter emit, + ) { + emit(state.copyWith( + temperature: event.data.temperature, + humidity: event.data.humidity, + pressure: event.data.pressure, + altitude: event.data.altitude, + isLoading: false, + )); + } + + @override + Future close() async { + await _subscription?.cancel(); + return super.close(); + } +} + +/// INTERNAL EVENT +class _AtmosphericConditionsUpdated extends AtmosphericConditionsEvent { + final AtmosphericConditions data; + + const _AtmosphericConditionsUpdated(this.data); + + @override + List get props => [data]; +} diff --git a/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_event.dart b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_event.dart new file mode 100644 index 0000000..e446497 --- /dev/null +++ b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_event.dart @@ -0,0 +1,10 @@ +part of 'atmospheric_conditions_bloc.dart'; + +abstract class AtmosphericConditionsEvent extends Equatable { + const AtmosphericConditionsEvent(); + + @override + List get props => []; +} + +class WatchAtmosphericConditionsStarted extends AtmosphericConditionsEvent {} diff --git a/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_state.dart b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_state.dart new file mode 100644 index 0000000..01b12ba --- /dev/null +++ b/lib/screens/monitoring/atmospheric_conditions/blocs/atmospheric_conditions_state.dart @@ -0,0 +1,43 @@ +part of 'atmospheric_conditions_bloc.dart'; + +class AtmosphericConditionsState extends Equatable { + final double temperature; + final double humidity; + final double pressure; + final double altitude; + + final bool isLoading; + + const AtmosphericConditionsState({ + this.temperature = 0.0, + this.humidity = 0.0, + this.pressure = 0.0, + this.altitude = 0.0, + this.isLoading = true, + }); + + AtmosphericConditionsState copyWith({ + double? temperature, + double? humidity, + double? pressure, + double? altitude, + bool? isLoading, + }) { + return AtmosphericConditionsState( + temperature: temperature ?? this.temperature, + humidity: humidity ?? this.humidity, + pressure: pressure ?? this.pressure, + altitude: altitude ?? this.altitude, + isLoading: isLoading ?? this.isLoading, + ); + } + + @override + List get props => [ + temperature, + humidity, + pressure, + altitude, + isLoading, + ]; +} diff --git a/lib/screens/monitoring/atmospheric_conditions/views/atmospheric_screen.dart b/lib/screens/monitoring/atmospheric_conditions/views/atmospheric_screen.dart new file mode 100644 index 0000000..611e602 --- /dev/null +++ b/lib/screens/monitoring/atmospheric_conditions/views/atmospheric_screen.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../blocs/atmospheric_conditions_bloc.dart'; + +class AtmosphericScreen extends StatelessWidget { + const AtmosphericScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.grey.shade100, + appBar: AppBar( + title: const Text( + "Kondisi Atmosfer", + style: TextStyle(fontWeight: FontWeight.bold), + ), + centerTitle: true, + backgroundColor: Colors.transparent, + elevation: 0, + foregroundColor: Colors.black, + ), + body: BlocBuilder( + builder: (context, state) { + if (state.isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + return SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + _mainTemperature(state), + const SizedBox(height: 25), + _gridInfo(state), + ], + ), + ); + }, + ), + ); + } + + /// ========================= + /// 🌡️ TEMPERATURE (HERO CARD) + /// ========================= + Widget _mainTemperature(AtmosphericConditionsState state) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 40), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + const Color.fromARGB(255, 38, 255, 222), + const Color.fromARGB(255, 53, 132, 229) + ], + ), + borderRadius: BorderRadius.circular(25), + boxShadow: [ + BoxShadow( + color: const Color.fromARGB(255, 0, 191, 255).withOpacity(0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ) + ], + ), + child: Column( + children: [ + const Icon(Icons.thermostat, color: Colors.white, size: 50), + const SizedBox(height: 10), + Text( + state.temperature.toStringAsFixed(1), + style: const TextStyle( + fontSize: 70, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const Text( + "°C", + style: TextStyle(color: Colors.white70, fontSize: 18), + ), + ], + ), + ); + } + + /// ========================= + /// 📊 INFO GRID + /// ========================= + Widget _gridInfo(AtmosphericConditionsState state) { + return GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + mainAxisSpacing: 15, + crossAxisSpacing: 15, + childAspectRatio: 1.6, + children: [ + _infoCard( + "Kelembapan", + "${state.humidity.toStringAsFixed(1)} %", + Icons.water_drop, + Colors.blue, + ), + _infoCard( + "Tekanan", + "${state.pressure.toStringAsFixed(1)} hPa", + Icons.speed, + Colors.green, + ), + _infoCard( + "Ketinggian", + "${state.altitude.toStringAsFixed(1)} m", + Icons.terrain, + Colors.brown, + ), + ], + ); + } + + Widget _infoCard(String title, String value, IconData icon, Color color) { + return Container( + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + child: Row( + children: [ + Icon(icon, color: color, size: 30), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + style: const TextStyle(color: Colors.grey, fontSize: 12), + ), + Text( + value, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ) + ], + ), + ); + } +} diff --git a/lib/screens/monitoring/evaporasi/blocs/evaporasi_bloc.dart b/lib/screens/monitoring/evaporasi/blocs/evaporasi_bloc.dart index fd8fafb..a785d90 100644 --- a/lib/screens/monitoring/evaporasi/blocs/evaporasi_bloc.dart +++ b/lib/screens/monitoring/evaporasi/blocs/evaporasi_bloc.dart @@ -3,7 +3,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:monitoring_repository/monitoring_repository.dart'; -import '../../../core/utils/time_series_mapper.dart'; +import '../../../../core/utils/time_series_mapper.dart'; part 'evaporasi_event.dart'; part 'evaporasi_state.dart'; @@ -15,7 +15,6 @@ class EvaporasiBloc extends Bloc { EvaporasiBloc({required MonitoringRepository repository}) : _repository = repository, super(const EvaporasiState()) { - on(_onStarted); on<_EvaporasiRealtimeUpdated>(_onRealtimeUpdated); on(_onPeriodChanged); @@ -25,10 +24,9 @@ class EvaporasiBloc extends Bloc { /// 🚀 START /// ========================= Future _onStarted( - WatchEvaporasiStarted event, - Emitter emit, - ) async { - + WatchEvaporasiStarted event, + Emitter emit, + ) async { emit(state.copyWith(isLoading: true)); final history = await _repository.getSensorHistory( @@ -39,7 +37,7 @@ class EvaporasiBloc extends Bloc { final dailyGraph = TimeSeriesMapper.toDaily( data: history, getTime: (e) => e.timestamp, - getValue: (e) => e.value, // ⚠️ sesuaikan nama field + getValue: (e) => e.evaporasi, // ⚠️ sesuaikan nama field ); emit(state.copyWith( @@ -52,7 +50,7 @@ class EvaporasiBloc extends Bloc { _subscription = _repository .getSensorStream( - 'evaporasi/realtime', + 'kelompok1/evaporasi', (json) => Evaporasi.fromJson(json), ) .listen((data) { @@ -64,20 +62,21 @@ class EvaporasiBloc extends Bloc { /// ⚡ REALTIME /// ========================= void _onRealtimeUpdated( - _EvaporasiRealtimeUpdated event, - Emitter emit, - ) { - + _EvaporasiRealtimeUpdated event, + Emitter emit, + ) { final updated = List.from(state.dailyValues); final index = DateTime.now().hour; if (index < updated.length) { - updated[index] = event.data.value; // ⚠️ sesuaikan field + updated[index] = event.data.evaporasi; // ⚠️ sesuaikan field } emit(state.copyWith( - currentValue: event.data.value, + currentValue: event.data.evaporasi, + temperature: event.data.suhu, + waterLevel: event.data.tinggiAir, dailyValues: updated, )); } @@ -86,10 +85,9 @@ class EvaporasiBloc extends Bloc { /// 📊 PERIOD /// ========================= Future _onPeriodChanged( - EvaporasiPeriodChanged event, - Emitter emit, - ) async { - + EvaporasiPeriodChanged event, + Emitter emit, + ) async { emit(state.copyWith(isLoading: true, selectedPeriod: event.period)); final history = state.history; @@ -100,19 +98,19 @@ class EvaporasiBloc extends Bloc { updated = TimeSeriesMapper.toWeekly( data: history, getTime: (e) => e.timestamp, - getValue: (e) => e.value, + getValue: (e) => e.evaporasi, ); } else if (event.period == "Bulan Ini") { updated = TimeSeriesMapper.toMonthly( data: history, getTime: (e) => e.timestamp, - getValue: (e) => e.value, + getValue: (e) => e.evaporasi, ); } else { updated = TimeSeriesMapper.toDaily( data: history, getTime: (e) => e.timestamp, - getValue: (e) => e.value, + getValue: (e) => e.evaporasi, ); } @@ -137,4 +135,4 @@ class _EvaporasiRealtimeUpdated extends EvaporasiEvent { @override List get props => [data]; -} \ No newline at end of file +} diff --git a/lib/screens/monitoring/evaporasi/blocs/evaporasi_event.dart b/lib/screens/monitoring/evaporasi/blocs/evaporasi_event.dart index e69de29..735954a 100644 --- a/lib/screens/monitoring/evaporasi/blocs/evaporasi_event.dart +++ b/lib/screens/monitoring/evaporasi/blocs/evaporasi_event.dart @@ -0,0 +1,21 @@ +part of 'evaporasi_bloc.dart'; + +abstract class EvaporasiEvent extends Equatable { + const EvaporasiEvent(); + + @override + List get props => []; +} + +/// 🚀 START MONITORING +class WatchEvaporasiStarted extends EvaporasiEvent {} + +/// 📊 GANTI PERIODE (Harian / Mingguan / Bulanan) +class EvaporasiPeriodChanged extends EvaporasiEvent { + final String period; + + const EvaporasiPeriodChanged(this.period); + + @override + List get props => [period]; +} diff --git a/lib/screens/monitoring/evaporasi/blocs/evaporasi_state.dart b/lib/screens/monitoring/evaporasi/blocs/evaporasi_state.dart index e69de29..b0487b7 100644 --- a/lib/screens/monitoring/evaporasi/blocs/evaporasi_state.dart +++ b/lib/screens/monitoring/evaporasi/blocs/evaporasi_state.dart @@ -0,0 +1,54 @@ +part of 'evaporasi_bloc.dart'; + +class EvaporasiState extends Equatable { + final double currentValue; // nilai evaporasi realtime + final double temperature; // suhu (opsional dari firebase) + final double waterLevel; // tinggi air + final String selectedPeriod; + + final List dailyValues; // untuk grafik + final List history; + + final bool isLoading; + + const EvaporasiState({ + this.currentValue = 0.0, + this.temperature = 0.0, + this.waterLevel = 0.0, + this.selectedPeriod = "Hari Ini", + this.dailyValues = const [], + this.history = const [], + this.isLoading = true, + }); + + EvaporasiState copyWith({ + double? currentValue, + double? temperature, + double? waterLevel, + String? selectedPeriod, + List? dailyValues, + List? history, + bool? isLoading, + }) { + return EvaporasiState( + currentValue: currentValue ?? this.currentValue, + temperature: temperature ?? this.temperature, + waterLevel: waterLevel ?? this.waterLevel, + selectedPeriod: selectedPeriod ?? this.selectedPeriod, + dailyValues: dailyValues ?? this.dailyValues, + history: history ?? this.history, + isLoading: isLoading ?? this.isLoading, + ); + } + + @override + List get props => [ + currentValue, + temperature, + waterLevel, + selectedPeriod, + dailyValues, + history, + isLoading, + ]; +} diff --git a/lib/screens/monitoring/evaporasi/views/evaporasi_screen.dart b/lib/screens/monitoring/evaporasi/views/evaporasi_screen.dart index f31a02c..a8214f4 100644 --- a/lib/screens/monitoring/evaporasi/views/evaporasi_screen.dart +++ b/lib/screens/monitoring/evaporasi/views/evaporasi_screen.dart @@ -1,1047 +1,146 @@ import 'package:flutter/material.dart'; -import 'package:fl_chart/fl_chart.dart'; -import 'package:firebase_database/firebase_database.dart'; -import 'dart:async'; -import 'package:intl/intl.dart'; -import '../../shared/widgets/monitoring_shared.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../blocs/evaporasi_bloc.dart'; -/// EVAPORASI MONITORING SCREEN -class EvaporasiMonitoringScreen extends StatefulWidget { - const EvaporasiMonitoringScreen({super.key}); - - @override - State createState() => - _EvaporasiMonitoringScreenState(); -} - -class _EvaporasiMonitoringScreenState extends State { - String _selectedPeriod = "Hari Ini"; - - final DatabaseReference _evaporationRef = - FirebaseDatabase.instance.ref('kelompok1/evaporasi'); - StreamSubscription? _evaporationSub; - - DateTime? _lastUpdateTime; - double _currentEvaporation = 0.0; - double _currentTemperature = 0.0; - double _currentWaterHeight = 0.0; - final List _dailyEvaporation = List.filled(24, 0.0); - double _hourlyTotal = 0.0; - int _hourlyCount = 0; - int _currentHour = DateTime.now().hour; - - bool get _isOnline { - if (_lastUpdateTime == null) return false; - final diff = DateTime.now().difference(_lastUpdateTime!); - return diff.inMinutes <= 5; - } - - @override - void initState() { - super.initState(); - _evaporationSub = _evaporationRef.onValue.listen(_onRealtimeData); - } - - @override - void dispose() { - _evaporationSub?.cancel(); - super.dispose(); - } - - void _onRealtimeData(DatabaseEvent event) { - final data = event.snapshot.value; - if (data is Map) { - final value = double.tryParse( - (data['evaporasi'] ?? data['nilai'] ?? 0).toString()) ?? - 0.0; - final temp = double.tryParse(data['suhu']?.toString() ?? '0') ?? 0.0; - final waterHeight = - double.tryParse(data['tinggi_air']?.toString() ?? '0') ?? 0.0; - final DateTime waktu; - - if (data.containsKey('timestamp')) { - final timestamp = - int.tryParse(data['timestamp']?.toString() ?? '0') ?? 0; - waktu = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); - } else { - final hour = int.tryParse(data['jam']?.toString() ?? '0') ?? 0; - final minute = int.tryParse(data['menit']?.toString() ?? '0') ?? 0; - final now = DateTime.now(); - waktu = DateTime(now.year, now.month, now.day, hour, minute); - } - - setState(() { - _currentEvaporation = value; - _currentTemperature = temp; - _currentWaterHeight = waterHeight; - _lastUpdateTime = waktu; - - final incomingHour = waktu.hour; - if (incomingHour != _currentHour) { - if (_hourlyCount > 0) { - _dailyEvaporation[_currentHour] = _hourlyTotal / _hourlyCount; - } - _hourlyTotal = 0.0; - _hourlyCount = 0; - _currentHour = incomingHour; - if (incomingHour == 0) { - for (int i = 0; i < 24; i++) { - _dailyEvaporation[i] = 0.0; - } - } - } - - _hourlyTotal += value; - _hourlyCount++; - _dailyEvaporation[incomingHour] = - _hourlyCount > 0 ? _hourlyTotal / _hourlyCount : value; - }); - } - } - - String _formatUpdateTime() { - if (_lastUpdateTime == null) return '-'; - return DateFormat('dd MMM yyyy • HH:mm').format(_lastUpdateTime!); - } - - bool get _hasRealtimeEvaporation => - _dailyEvaporation.any((value) => value > 0.0); - - Widget _buildDetailTile({ - required String label, - required String value, - required IconData icon, - required Color color, - }) { - return Expanded( - child: Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: color.withOpacity(0.08), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: color.withOpacity(0.2)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(icon, color: color, size: 16), - const SizedBox(width: 6), - Text( - label, - style: TextStyle( - fontSize: 11, - color: Colors.grey.shade700, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - value, - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: color, - ), - ), - ], - ), - ), - ); - } +class EvaporasiScreen extends StatelessWidget { + const EvaporasiScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Colors.grey.shade100, appBar: AppBar( - title: const Text("Monitoring Evaporasi"), + title: const Text( + "Evaporasi", + style: TextStyle(fontWeight: FontWeight.bold), + ), centerTitle: true, + backgroundColor: Colors.transparent, + elevation: 0, + foregroundColor: Colors.black, ), - body: Padding( - padding: const EdgeInsets.all(16), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Evaporasi", - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 16), - if (_lastUpdateTime != null) - Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${_currentEvaporation.toStringAsFixed(1)} mm", - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.blue, - ), - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Terakhir diperbarui", - style: TextStyle( - fontSize: 12, - color: Colors.grey, - ), - ), - const SizedBox(height: 4), - Text( - _formatUpdateTime(), - style: const TextStyle( - fontSize: 12, - color: Colors.black54, - ), - ), - ], - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - decoration: BoxDecoration( - color: _isOnline - ? Colors.green.shade100 - : Colors.red.shade100, - borderRadius: BorderRadius.circular(20), - ), - child: Text( - _isOnline ? 'ONLINE' : 'OFFLINE', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: _isOnline ? Colors.green : Colors.red, - ), - ), - ), - ], - ), - ], - ), - ), - ), - const SizedBox(height: 12), - Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Detail Sensor Evaporasi', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 14), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildDetailTile( - label: 'Jam', - value: DateFormat('HH:mm').format(_lastUpdateTime!), - icon: Icons.access_time, - color: Colors.blue, - ), - _buildDetailTile( - label: 'Suhu', - value: - '${_currentTemperature.toStringAsFixed(2)} °C', - icon: Icons.thermostat, - color: Colors.red, - ), - ], - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildDetailTile( - label: 'Tinggi Air', - value: '${_currentWaterHeight.toStringAsFixed(2)}', - icon: Icons.water_drop, - color: Colors.blueAccent, - ), - _buildDetailTile( - label: 'Evaporasi', - value: - '${_currentEvaporation.toStringAsFixed(1)} mm', - icon: Icons.opacity, - color: Colors.blue, - ), - ], - ), - ], - ), - ), - ), - const SizedBox(height: 20), + body: BlocBuilder( + builder: (context, state) { + if (state.isLoading) { + return const Center(child: CircularProgressIndicator()); + } - /// SISTEM KONTROL AIR OTOMATIS - _buildAutomaticWaterControlSystem(), - const SizedBox(height: 25), - - /// GRAFIK MONITORING - _buildMonitoringGraphBlock(), - const SizedBox(height: 25), - - /// PENJELASAN - _buildMonitoringExplanation(), - const SizedBox(height: 25), - - /// STATISTIK TAHUNAN - _buildAnnualCombinedGraphSection(), - ], - ), - ), - ), - ); - } - - /// EXPORT FUNCTIONS - void _exportDailyData() { - final StringBuffer csvContent = StringBuffer(); - csvContent.writeln("DATA MONITORING EVAPORASI - HARIAN (24 JAM)"); - csvContent.writeln("Tanggal: ${DateTime.now().toString().split(' ')[0]}"); - csvContent.writeln(""); - csvContent.writeln("Jam,Nilai (mm)"); - - final dailyData = [ - 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 - ]; - for (int i = 0; i < dailyData.length; i++) { - csvContent.writeln("$i:00,${dailyData[i]}"); - } - - csvContent.writeln(""); - csvContent.writeln("Statistik Harian"); - csvContent.writeln("Total: 79.4 mm"); - csvContent.writeln("Rata-rata: 3.3 mm"); - csvContent.writeln("Maksimal: 5.2 mm"); - csvContent.writeln("Minimal: 1.9 mm"); - - showExportPreview(context, csvContent.toString(), "Data Harian"); - } - - void _exportWeeklyData() { - final StringBuffer csvContent = StringBuffer(); - csvContent.writeln("DATA MONITORING EVAPORASI - MINGGUAN"); - csvContent.writeln("Tanggal: ${DateTime.now().toString().split(' ')[0]}"); - csvContent.writeln(""); - csvContent.writeln("Hari,Nilai (mm)"); - - final days = [ - 'Senin', - 'Selasa', - 'Rabu', - 'Kamis', - 'Jumat', - 'Sabtu', - 'Minggu' - ]; - final weeklyData = [3.5, 3.8, 4.2, 4.5, 4.0, 3.8, 3.2]; - - for (int i = 0; i < days.length; i++) { - csvContent.writeln("${days[i]},${weeklyData[i]}"); - } - - csvContent.writeln(""); - csvContent.writeln("Statistik Mingguan"); - csvContent.writeln("Total: 27.0 mm"); - csvContent.writeln("Rata-rata: 3.86 mm"); - csvContent.writeln("Maksimal: 4.5 mm"); - csvContent.writeln("Minimal: 3.2 mm"); - - showExportPreview(context, csvContent.toString(), "Data Mingguan"); - } - - void _exportMonthlyData() { - final StringBuffer csvContent = StringBuffer(); - csvContent.writeln("DATA MONITORING EVAPORASI - BULANAN"); - csvContent.writeln( - "Bulan: ${DateTime.now().toString().split(' ')[0].substring(0, 7)}"); - csvContent.writeln(""); - csvContent.writeln("Tanggal,Nilai (mm)"); - - for (int i = 1; i <= 28; i++) { - final value = (3.0 + (i % 6) * 0.5).toStringAsFixed(1); - csvContent.writeln("$i,$value"); - } - - csvContent.writeln(""); - csvContent.writeln("Statistik Bulanan"); - csvContent.writeln("Total: 99.2 mm"); - csvContent.writeln("Rata-rata: 3.54 mm"); - csvContent.writeln("Maksimal: 5.0 mm"); - csvContent.writeln("Minimal: 2.5 mm"); - - showExportPreview(context, csvContent.toString(), "Data Bulanan"); - } - - void _exportAnnualData() { - final StringBuffer csvContent = StringBuffer(); - csvContent.writeln("DATA MONITORING EVAPORASI - TAHUNAN"); - csvContent.writeln("Tahun: 2025"); - csvContent.writeln(""); - csvContent.writeln("Bulan,Pengisian (x),Rata-rata (mm)"); - - final months = [ - 'Januari', - 'Februari', - 'Maret', - 'April', - 'Mei', - 'Juni', - 'Juli', - 'Agustus', - 'September', - 'Oktober', - 'November', - 'Desember' - ]; - final fillings = [12, 13, 15, 14, 13, 12, 14, 16, 13, 14, 15, 13]; - final averages = [ - 3.5, - 3.8, - 4.2, - 4.5, - 5.0, - 5.2, - 5.5, - 5.8, - 5.0, - 4.5, - 4.0, - 3.7 - ]; - - for (int i = 0; i < months.length; i++) { - csvContent.writeln("${months[i]},${fillings[i]},${averages[i]}"); - } - - csvContent.writeln(""); - csvContent.writeln("STATISTIK TAHUNAN"); - csvContent.writeln("Total Pengisian: 156x"); - csvContent.writeln("Rata-rata Evaporasi: 4.4 mm"); - csvContent.writeln("Total Evaporasi: 52.8 mm"); - - showExportPreview(context, csvContent.toString(), "Data Tahunan"); - } - - /// DATA GENERATORS - List _getEvaporationPeriodData() { - switch (_selectedPeriod) { - case "Hari Ini": - if (_hasRealtimeEvaporation) { - return List.generate( - _dailyEvaporation.length, - (index) => FlSpot(index.toDouble(), _dailyEvaporation[index]), - ); - } - return List.generate(24, (index) => FlSpot(index.toDouble(), 0.0)); - case "Minggu Ini": - return List.generate(7, (index) => FlSpot(index.toDouble(), 0.0)); - case "Bulan Ini": - return List.generate(12, (index) => FlSpot(index.toDouble(), 0.0)); - default: - return []; - } - } - - /// UI BUILDERS - Widget _buildAutomaticWaterControlSystem() { - bool isActive = DateTime.now().hour >= 6 && DateTime.now().hour < 7; - - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.grey.shade50, - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.grey.shade300), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Sistem Kontrol Air Otomatis", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Jadwal Operasional", - style: TextStyle( - fontSize: 12, - color: Colors.grey, - ), - ), - const SizedBox(height: 4), - const Text( - "06.00 - 07.00 WIB", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 10, - ), - decoration: BoxDecoration( - color: isActive ? Colors.blue : Colors.grey.shade300, - borderRadius: BorderRadius.circular(20), - ), - child: Text( - isActive ? "AKTIF" : "TIDAK AKTIF", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: isActive ? Colors.white : Colors.grey.shade700, - ), - ), - ), - ], - ), - const SizedBox(height: 12), - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.blue.shade50, - borderRadius: BorderRadius.circular(8), - ), - child: const Text( - "Sistem otomatis akan mengisi air pada waktu yang telah ditentukan untuk menjaga tingkat evaporasi pada level optimal.", - style: TextStyle( - fontSize: 11, - color: Colors.blue, - height: 1.5, - ), - ), - ), - ], - ), - ); - } - - Widget _buildMonitoringGraphBlock() { - return Card( - elevation: 4, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "Status Evaporasi", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - decoration: BoxDecoration( - color: Colors.amber.shade100, - borderRadius: BorderRadius.circular(20), - ), - child: const Text( - "Normal", - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.amber, - ), - ), - ), - ], - ), - const SizedBox(height: 16), - - /// PERIOD SELECTOR + EXPORT - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: - ["Hari Ini", "Minggu Ini", "Bulan Ini"].map((period) { - return Padding( - padding: const EdgeInsets.only(right: 8), - child: ChoiceChip( - label: Text(period), - selected: _selectedPeriod == period, - onSelected: (selected) { - setState(() { - _selectedPeriod = period; - }); - }, - selectedColor: Colors.blue, - labelStyle: TextStyle( - color: _selectedPeriod == period - ? Colors.white - : Colors.black, - ), - ), - ); - }).toList(), - ), - ), - ), - const SizedBox(width: 8), - ElevatedButton.icon( - onPressed: () { - if (_selectedPeriod == "Hari Ini") { - _exportDailyData(); - } else if (_selectedPeriod == "Minggu Ini") { - _exportWeeklyData(); - } else if (_selectedPeriod == "Bulan Ini") { - _exportMonthlyData(); - } - }, - icon: const Icon(Icons.download, size: 16), - label: const Text("Export"), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.orange, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 6, - ), - textStyle: const TextStyle(fontSize: 11), - ), - ), - ], - ), - const SizedBox(height: 16), - - /// GRAPH - SizedBox( - height: 300, - child: LineChart( - LineChartData( - gridData: FlGridData( - show: true, - drawHorizontalLine: true, - drawVerticalLine: false, - horizontalInterval: 1, - getDrawingHorizontalLine: (value) { - return FlLine( - color: Colors.grey.shade300, - strokeWidth: 1, - ); - }, - ), - titlesData: FlTitlesData( - show: true, - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - interval: getPeriodInterval(_selectedPeriod), - getTitlesWidget: (value, meta) { - return Text( - getPeriodLabel(_selectedPeriod, value), - style: const TextStyle( - fontSize: 10, - color: Colors.grey, - ), - ); - }, - ), - ), - leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - interval: 1, - getTitlesWidget: (value, meta) { - return Text( - value.toInt().toString(), - style: const TextStyle( - fontSize: 9, - color: Colors.grey, - ), - ); - }, - reservedSize: 28, - ), - ), - topTitles: AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - rightTitles: AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - ), - borderData: FlBorderData( - show: true, - border: Border( - left: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - bottom: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - lineBarsData: [ - LineChartBarData( - spots: _getEvaporationPeriodData(), - isCurved: true, - barWidth: 2.5, - dotData: FlDotData( - show: true, - getDotPainter: (spot, percent, barData, index) { - return FlDotCirclePainter( - radius: 4, - color: Colors.blue, - strokeWidth: 0, - ); - }, - ), - color: Colors.blue, - belowBarData: BarAreaData( - show: true, - color: Colors.blue.withOpacity(0.1), - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } - - Widget _buildMonitoringExplanation() { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.blue.shade50, - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.blue.shade200), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Penjelasan Evaporasi", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.blue, - ), - ), - const SizedBox(height: 10), - const Text( - "Evaporasi adalah proses penguapan air dari permukaan tanah atau air menuju atmosfer. Tingkat evaporasi dipengaruhi oleh suhu, kelembaban udara, kecepatan angin, dan intensitas cahaya matahari. Data ini penting untuk manajemen irigasi dan perencanaan kebutuhan air.", - style: TextStyle( - fontSize: 12, - height: 1.6, - color: Colors.blue, - ), - ), - ], - ), - ); - } - - Widget _buildAnnualCombinedGraphSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Statistik Tahunan & Rata-rata", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 15), - - /// STATS - Row( - children: [ - Expanded( - child: StatCard( - title: "Total Pengisian", - value: "156x", - color: Colors.blue, - icon: Icons.water_drop, - ), - ), - const SizedBox(width: 10), - Expanded( - child: StatCard( - title: "Rata-rata", - value: "4.2 mm", - color: Colors.green, - icon: Icons.trending_up, - ), - ), - ], - ), - const SizedBox(height: 20), - - /// CHART - const Text( - "Grafik Tahunan - Pengisian Air & Rata-rata Evaporasi", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 12), - Card( - elevation: 3, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16), + return SingleChildScrollView( + padding: const EdgeInsets.all(20), child: Column( children: [ - SizedBox( - height: 320, - child: BarChart( - BarChartData( - gridData: FlGridData( - show: true, - drawHorizontalLine: true, - drawVerticalLine: false, - horizontalInterval: 2, - getDrawingHorizontalLine: (value) { - return FlLine( - color: Colors.grey.shade300, - strokeWidth: 1, - ); - }, - ), - titlesData: FlTitlesData( - show: true, - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - final months = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'Mei', - 'Jun', - 'Jul', - 'Agu', - 'Sep', - 'Okt', - 'Nov', - 'Des' - ]; - return Text( - months[value.toInt()], - style: const TextStyle( - fontSize: 10, - color: Colors.grey, - ), - ); - }, - reservedSize: 30, - ), - ), - leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - value.toInt().toString(), - style: const TextStyle( - fontSize: 9, - color: Colors.grey, - ), - ); - }, - reservedSize: 28, - ), - ), - rightTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - '${value.toInt()} mm', - style: const TextStyle( - fontSize: 9, - color: Colors.green, - ), - ); - }, - reservedSize: 45, - ), - ), - topTitles: AxisTitles( - sideTitles: SideTitles(showTitles: false), - ), - ), - borderData: FlBorderData( - show: true, - border: Border( - left: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - bottom: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - right: BorderSide( - color: Colors.grey.shade300, - width: 1, - ), - ), - ), - barGroups: List.generate( - 12, - (index) => buildBarGroup(index, 0), - ), - rangeAnnotations: RangeAnnotations( - horizontalRangeAnnotations: [ - HorizontalRangeAnnotation( - y1: 3.5, - y2: 5.8, - color: Colors.green.withOpacity(0.1), - ), - ], - ), - ), - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 16, - height: 16, - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(3), - ), - ), - const SizedBox(width: 8), - const Text( - "Pengisian Air (kali)", - style: TextStyle(fontSize: 12), - ), - const SizedBox(width: 20), - Container( - width: 16, - height: 16, - decoration: BoxDecoration( - color: Colors.green.withOpacity(0.3), - borderRadius: BorderRadius.circular(3), - ), - ), - const SizedBox(width: 8), - const Text( - "Area Rata-rata (mm)", - style: TextStyle(fontSize: 12), - ), - ], - ), + _mainCard(state), + const SizedBox(height: 25), + _infoRow(state), + const SizedBox(height: 25), + _trendSection(), ], ), - ), + ); + }, + ), + ); + } + + /// ========================= + /// 🔥 MAIN CARD (EVAPORASI) + /// ========================= + Widget _mainCard(EvaporasiState state) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 40), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.blue.shade400, Colors.blue.shade800], ), - const SizedBox(height: 15), - Center( - child: ElevatedButton.icon( - onPressed: () => _exportAnnualData(), - icon: const Icon(Icons.file_download), - label: const Text("Download Excel Tahunan"), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.green, - foregroundColor: Colors.white, + borderRadius: BorderRadius.circular(25), + ), + child: Column( + children: [ + const Icon(Icons.water_drop, color: Colors.white, size: 45), + const SizedBox(height: 10), + Text( + state.currentValue.toStringAsFixed(1), + style: const TextStyle( + fontSize: 70, + fontWeight: FontWeight.bold, + color: Colors.white, ), ), + const Text( + "mm", + style: TextStyle(color: Colors.white70, fontSize: 18), + ), + ], + ), + ); + } + + /// ========================= + /// 📊 INFO KECIL (SUHU & AIR) + /// ========================= + Widget _infoRow(EvaporasiState state) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _miniCard( + "Suhu", + "${state.temperature.toStringAsFixed(1)} °C", + Icons.thermostat, + Colors.orange, + ), + _miniCard( + "Tinggi Air", + "${state.waterLevel.toStringAsFixed(1)} cm", + Icons.water, + Colors.blue, ), ], ); } + + Widget _miniCard(String title, String value, IconData icon, Color color) { + return Container( + width: 160, + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + child: Row( + children: [ + Icon(icon, color: color), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, + style: const TextStyle(fontSize: 12, color: Colors.grey)), + Text(value, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ) + ], + ), + ); + } + + /// ========================= + /// 📈 TREND (SIMPLE PLACEHOLDER) + /// ========================= + Widget _trendSection() { + return Container( + width: double.infinity, + height: 200, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + child: const Center( + child: Text( + "Grafik Evaporasi", + style: TextStyle(color: Colors.grey), + ), + ), + ); + } } diff --git a/packages/monitoring_repository/lib/src/models/atmospheric_conditions.dart b/packages/monitoring_repository/lib/src/models/atmospheric_conditions.dart new file mode 100644 index 0000000..c0707f7 --- /dev/null +++ b/packages/monitoring_repository/lib/src/models/atmospheric_conditions.dart @@ -0,0 +1,25 @@ +class AtmosphericConditions { + final double temperature; + final double humidity; + final double pressure; + final double altitude; + final DateTime timestamp; + + AtmosphericConditions({ + required this.temperature, + required this.humidity, + required this.pressure, + required this.altitude, + required this.timestamp, + }); + + factory AtmosphericConditions.fromJson(Map json) { + return AtmosphericConditions( + temperature: (json['temperature'] ?? 0).toDouble(), + humidity: (json['humidity'] ?? 0).toDouble(), + pressure: (json['pressure'] ?? 0).toDouble(), + altitude: (json['altitude'] ?? 0).toDouble(), + timestamp: DateTime.now(), // karena kamu pakai latest + ); + } +} diff --git a/packages/monitoring_repository/lib/src/models/evaporasi.dart b/packages/monitoring_repository/lib/src/models/evaporasi.dart index e69de29..da56dd1 100644 --- a/packages/monitoring_repository/lib/src/models/evaporasi.dart +++ b/packages/monitoring_repository/lib/src/models/evaporasi.dart @@ -0,0 +1,36 @@ +class Evaporasi { + final double evaporasi; + final double suhu; + final double tinggiAir; + final DateTime timestamp; + + Evaporasi({ + required this.evaporasi, + required this.suhu, + required this.tinggiAir, + required this.timestamp, + }); + + static final empty = Evaporasi( + evaporasi: 0.0, + suhu: 0.0, + tinggiAir: 0.0, + timestamp: DateTime.fromMillisecondsSinceEpoch(0), + ); + + factory Evaporasi.fromJson(Map json) { + final int jam = (json['jam'] ?? 0) as int; + final int menit = (json['menit'] ?? 0) as int; + + final now = DateTime.now(); + + return Evaporasi( + evaporasi: (json['evaporasi'] ?? 0).toDouble(), + suhu: (json['suhu'] ?? 0).toDouble(), + tinggiAir: (json['tinggi_air'] ?? 0).toDouble(), + + /// 🔥 bikin timestamp dari jam & menit + timestamp: DateTime(now.year, now.month, now.day, jam, menit), + ); + } +} diff --git a/packages/monitoring_repository/lib/src/models/models.dart b/packages/monitoring_repository/lib/src/models/models.dart index f4c6e66..ae80da6 100644 --- a/packages/monitoring_repository/lib/src/models/models.dart +++ b/packages/monitoring_repository/lib/src/models/models.dart @@ -1,3 +1,3 @@ export 'wind_speed.dart'; export 'evaporasi.dart'; -export 'pressure_sensor.dart'; +export 'atmospheric_conditions.dart'; diff --git a/packages/monitoring_repository/lib/src/models/pressure_sensor.dart b/packages/monitoring_repository/lib/src/models/pressure_sensor.dart deleted file mode 100644 index e69de29..0000000