diff --git a/images/logo_klimatologi.png b/images/logo_klimatologi.png new file mode 100644 index 0000000..e127653 Binary files /dev/null and b/images/logo_klimatologi.png differ diff --git a/lib/screens/home/views/main_drawer.dart b/lib/screens/home/views/main_drawer.dart index 9c6b4a7..4e08d0a 100644 --- a/lib/screens/home/views/main_drawer.dart +++ b/lib/screens/home/views/main_drawer.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:klimatologiot/screens/monitoring/evaporasi/blocs/evaporasi_bloc.dart'; import 'package:monitoring_repository/monitoring_repository.dart'; import '../../../blocs/authentication_bloc/authentication_bloc.dart'; import '../../monitoring/wind_speed/views/wind_speed_screen.dart'; import '../../monitoring/wind_speed/blocs/wind_speed_bloc.dart'; +import '../../monitoring/evaporasi/views/evaporasi_screen.dart'; +import '../../monitoring/atmospheric_conditions/blocs/atmospheric_conditions_bloc.dart'; +import '../../monitoring/atmospheric_conditions/views/atmospheric_screen.dart'; class MainDrawer extends StatelessWidget { const MainDrawer({super.key}); @@ -68,7 +72,20 @@ class MainDrawer extends StatelessWidget { ListTile( leading: const Icon(Icons.water_drop), title: const Text("Evaporasi"), - onTap: () {}, + onTap: () { + Navigator.pop(context); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => 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