From a213576bceb1125b127aafa275d5082eb9efa49d Mon Sep 17 00:00:00 2001 From: Wizznu Date: Mon, 26 Jan 2026 11:47:23 +0700 Subject: [PATCH] feat: Implement dual mode system (Smart & Fixed) and fix logout bugs - Add Smart Mode: auto-stop when target reached or timeout - Add Fixed Duration Mode: water for exact duration - Fix auth state management to prevent unexpected logout - Add error handling across all pages - Update automation service with dual mode logic - Add mode selection UI in sensor config page - Update Firebase structure: mode_sensor, durasi_sensor - Enhance logging for debugging - Add flutter_launcher_icons for app icon management - Add comprehensive documentation (3 MD files) PERUBHAAN BESAR --- BUG_FIXES_REPORT.md | 243 +++++++++ CARA_UBAH_ICON_APK.md | 163 ++++++ DUAL_MODE_IMPLEMENTATION.md | 466 ++++++++++++++++++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 9068 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 4931 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 13846 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 25716 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 40417 bytes lib/main.dart | 128 ++++- lib/screens/dashboard_page.dart | 33 ++ lib/screens/kontrol_page.dart | 147 +++--- lib/screens/pot_selection_page.dart | 27 +- lib/screens/sensor_config_page.dart | 92 +++- lib/screens/waktu_config_page.dart | 59 ++- lib/services/kontrol_automation_service.dart | 111 ++++- pubspec.lock | 88 ++++ pubspec.yaml | 12 + 17 files changed, 1444 insertions(+), 125 deletions(-) create mode 100644 BUG_FIXES_REPORT.md create mode 100644 CARA_UBAH_ICON_APK.md create mode 100644 DUAL_MODE_IMPLEMENTATION.md diff --git a/BUG_FIXES_REPORT.md b/BUG_FIXES_REPORT.md new file mode 100644 index 0000000..5e04f6f --- /dev/null +++ b/BUG_FIXES_REPORT.md @@ -0,0 +1,243 @@ +# Laporan Perbaikan Bug ApsGo + +**Tanggal**: 26 Januari 2026 +**Status**: โœ… SELESAI + +## Ringkasan Bug yang Diperbaiki + +### 1. ๐Ÿ”’ Bug Logout Tiba-tiba ke Landing Page + +**Masalah**: +- Aplikasi terkadang logout secara tiba-tiba saat klik menu Kontrol atau menu lainnya +- User ter-redirect ke Landing Page tanpa sengaja + +**Penyebab**: +- Tidak ada monitoring auth state di DashboardPage +- Ketika Firebase Auth token expired atau ada perubahan auth state, tidak ada handler yang tepat +- Navigation stack tidak terlindungi dengan baik + +**Solusi**: +โœ… Tambahkan `authStateChanges` listener di `initState()` DashboardPage +```dart +@override +void initState() { + super.initState(); + // Monitor auth state untuk mencegah logout tidak terduga + _authService.authStateChanges.listen((user) { + if (user == null && mounted) { + // User logged out, redirect to login + Navigator.of(context).pushNamedAndRemoveUntil( + '/login', + (route) => false, + ); + } + }); +} +``` + +**File yang Diubah**: +- `lib/screens/dashboard_page.dart` + +--- + +### 2. ๐Ÿ’ง Bug Pompa Tidak Langsung Aktif Saat Soil Moisture Rendah + +**Masalah**: +- Pompa terkadang tidak langsung aktif ketika soil moisture di bawah ambang bawah +- Delay response terlalu lama (10 detik) +- Cooldown period terlalu lama (5 menit) + +**Penyebab**: +- Timer check sensor berjalan setiap 10 detik โ†’ terlalu lambat +- Cooldown 5 menit antar penyiraman โ†’ terlalu lama untuk kondisi kritis +- Kurang logging untuk debugging + +**Solusi**: +โœ… **Interval Check**: Ubah dari 10 detik โ†’ **5 detik** (lebih responsif) +```dart +_sensorCheckTimer = Timer.periodic( + const Duration(seconds: 5), // Sebelumnya: 10 detik + (timer) => _checkSensorThreshold(), +); +``` + +โœ… **Cooldown Period**: Kurangi dari 5 menit โ†’ **2 menit** +```dart +if (diff.inMinutes < 2) { // Sebelumnya: 5 menit + debugPrint('โณ POT $potNumber: Cooldown active (${2 - diff.inMinutes} min remaining)'); + return; +} +``` + +โœ… **Tambah Logging Detail**: +- Log setiap check sensor dengan nilai threshold +- Log setiap trigger penyiraman +- Log progress penyiraman per 5 detik +- Log saat target tercapai + +โœ… **Pastikan Flag Reset**: Tambah delay sebelum reset flag untuk memastikan cleanup selesai +```dart +await Future.delayed(const Duration(milliseconds: 500)); +_isWateringActive[potKey] = false; +``` + +โœ… **Error Handling**: Tambahkan try-catch untuk matikan pompa saat error +```dart +} catch (e) { + debugPrint('โŒ Error watering pot by sensor: $e'); + // Matikan semua untuk safety + try { + await _dbService.setPompaAir(false); + await _dbService.setPot(potNumber, false); + } catch (cleanupError) { + debugPrint('โŒ Error during cleanup: $cleanupError'); + } + _isWateringActive[potKey] = false; +} +``` + +**File yang Diubah**: +- `lib/services/kontrol_automation_service.dart` + +--- + +### 3. ๐Ÿ”Œ Verifikasi Mapping Soil Sensor ke Mosfet + +**Masalah yang Diperiksa**: +- Apakah soil_1 bisa mempengaruhi mosvet_3 dst +- Apakah mapping soil_x ke mosvet_y sudah benar + +**Hasil Verifikasi**: +โœ… Mapping sudah **BENAR**: +``` +soil_1 (Pot 1) โ†’ mosvet_3 (Valve Pot 1) +soil_2 (Pot 2) โ†’ mosvet_4 (Valve Pot 2) +soil_3 (Pot 3) โ†’ mosvet_5 (Valve Pot 3) +soil_4 (Pot 4) โ†’ mosvet_6 (Valve Pot 4) +soil_5 (Pot 5) โ†’ mosvet_7 (Valve Pot 5) +``` + +**Catatan**: +- mosvet_1 = Pompa Air +- mosvet_2 = Pompa Nutrisi +- mosvet_3 s/d mosvet_7 = Valve Pot 1-5 + +**Perbaikan**: +โœ… Tambah komentar dan logging untuk memperjelas mapping +```dart +// pot 1 โ†’ mosvet_3, pot 2 โ†’ mosvet_4, ... pot 5 โ†’ mosvet_7 +debugPrint('๐Ÿ’ง Starting watering: POT $potNumber (mosvet_${potNumber + 2})'); +``` + +โœ… Tambah logging tracking nilai soil sensor +```dart +debugPrint('๐ŸŒก๏ธ soil_$i = $soilValue (threshold: $batasBawah)'); +debugPrint('โš ๏ธ $soilKey ($soilValue) < batasBawah ($batasBawah) โ†’ Triggering watering for POT $i'); +``` + +**File yang Diubah**: +- `lib/services/kontrol_automation_service.dart` + +--- + +## Perubahan Detail + +### File: `lib/screens/dashboard_page.dart` + +**Perubahan**: +1. Import `AuthService` +2. Tambah instance `_authService` +3. Tambah `initState()` dengan auth state listener + +### File: `lib/services/kontrol_automation_service.dart` + +**Perubahan**: +1. โœ… Timer interval: 10s โ†’ 5s (line ~183) +2. โœ… Cooldown: 5 min โ†’ 2 min (line ~248) +3. โœ… Tambah logging detail di `_checkSensorThreshold()` (line ~217) +4. โœ… Tambah logging mapping pot (line ~254) +5. โœ… Tambah logging progress watering (line ~265) +6. โœ… Tambah delay sebelum reset flag (line ~283) +7. โœ… Improve error handling dengan cleanup (line ~286) + +--- + +## Testing Checklist + +Setelah update, pastikan test hal-hal berikut: + +### Auth & Navigation +- [ ] Login dan navigate ke Dashboard +- [ ] Klik menu Kontrol โ†’ tidak logout +- [ ] Klik menu Histori โ†’ tidak logout +- [ ] Buka drawer dan klik menu โ†’ tidak logout +- [ ] Test dengan koneksi internet ON/OFF +- [ ] Test dengan token yang akan expired + +### Sensor Automation +- [ ] Aktifkan mode Sensor +- [ ] Set soil moisture di bawah batas bawah (misal: 25%) +- [ ] Cek pompa aktif dalam **< 5 detik** +- [ ] Cek valve pot yang tepat aktif (soil_1 โ†’ mosvet_3, dst) +- [ ] Cek log di console untuk tracking +- [ ] Test dengan multiple pot di bawah threshold bersamaan +- [ ] Test cooldown period (2 menit) + +### Edge Cases +- [ ] Test saat Firebase disconnect +- [ ] Test saat sensor data tidak valid (0, null, string) +- [ ] Test saat automation service crash +- [ ] Test multiple user logout/login + +--- + +## Logging untuk Debugging + +Untuk monitor aktivitas, cek console log dengan pattern: + +**Sensor Mode**: +``` +๐ŸŒก๏ธ Sensor Mode: Started +๐ŸŒก๏ธ Checking thresholds: batas_bawah=30, batas_atas=80 +๐ŸŒก๏ธ soil_1 = 25 (threshold: 30) +โš ๏ธ soil_1 (25) < batasBawah (30) โ†’ Triggering watering for POT 1 +๐Ÿ’ง Starting watering: POT 1 (mosvet_3) +๐ŸŒก๏ธ Target: soil_1 should reach >= 80 (currently: 25) +๐Ÿ’ง POT 1 watering: 5s, soil_1: 35 +๐Ÿ’ง POT 1 watering: 10s, soil_1: 50 +โœ… POT 1 reached target: 80 >= 80 +โœ… Sensor Mode: Watering completed for POT 1 (15s) +``` + +**Cooldown**: +``` +โณ POT 1: Cooldown active (1 min remaining) +``` + +**Errors**: +``` +โŒ Error watering pot by sensor: [error message] +โŒ Error during cleanup: [error message] +``` + +--- + +## Rekomendasi Lanjutan + +1. **Tambah UI Indicator**: Tampilkan status automation (active/inactive) di Dashboard +2. **Notification**: Push notification saat pompa aktif otomatis +3. **History Log**: Simpan log automation ke Firebase untuk review +4. **Manual Override**: Button untuk force stop automation +5. **Sensor Calibration**: UI untuk kalibrasi sensor soil moisture + +--- + +## Kontak + +Jika masih ada masalah setelah update ini, catat: +1. Timestamp kejadian +2. Screenshot console log +3. Nilai sensor saat kejadian +4. Action yang dilakukan user + +Happy Gardening! ๐ŸŒฑ diff --git a/CARA_UBAH_ICON_APK.md b/CARA_UBAH_ICON_APK.md new file mode 100644 index 0000000..0093f30 --- /dev/null +++ b/CARA_UBAH_ICON_APK.md @@ -0,0 +1,163 @@ +# ๐Ÿ“ฑ Panduan Mengubah Logo/Icon APK + +## โœ… Cara 1: Otomatis dengan flutter_launcher_icons (SUDAH DISETUP) + +### Langkah 1: Install Package +Saya sudah menambahkan `flutter_launcher_icons` ke `pubspec.yaml`. Sekarang jalankan: + +```bash +flutter pub get +``` + +### Langkah 2: Siapkan Logo +**Requirement:** +- Format: PNG +- Ukuran: **1024x1024 px** (recommended) atau minimal 512x512 px +- Background: Transparent atau solid color +- File: `assets/images/logo_apsgo.png` (sudah ada) + +**Jika ingin ganti logo:** +1. Siapkan file PNG 1024x1024 px +2. Replace file `assets/images/logo_apsgo.png` + +### Langkah 3: Generate Icon +Jalankan command ini di terminal: + +```bash +flutter pub run flutter_launcher_icons +``` + +### Langkah 4: Verify +Check folder ini, icon seharusnya sudah ter-generate: +- `android/app/src/main/res/mipmap-hdpi/ic_launcher.png` +- `android/app/src/main/res/mipmap-mdpi/ic_launcher.png` +- `android/app/src/main/res/mipmap-xhdpi/ic_launcher.png` +- `android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png` +- `android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png` + +### Langkah 5: Build APK +```bash +flutter build apk --release +``` + +--- + +## ๐ŸŽจ Cara 2: Manual (Advanced) + +Jika cara otomatis tidak berhasil, gunakan cara manual: + +### Langkah 1: Prepare Icon dengan Berbagai Ukuran + +| Resolusi | Ukuran | Folder | +|----------|--------|--------| +| MDPI | 48x48 px | `mipmap-mdpi/` | +| HDPI | 72x72 px | `mipmap-hdpi/` | +| XHDPI | 96x96 px | `mipmap-xhdpi/` | +| XXHDPI | 144x144 px | `mipmap-xxhdpi/` | +| XXXHDPI | 192x192 px | `mipmap-xxxhdpi/` | + +### Langkah 2: Replace File + +Replace semua file `ic_launcher.png` di folder berikut: +``` +android/app/src/main/res/mipmap-hdpi/ic_launcher.png +android/app/src/main/res/mipmap-mdpi/ic_launcher.png +android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +``` + +### Langkah 3: Build APK +```bash +flutter build apk --release +``` + +--- + +## ๐ŸŒŸ Adaptive Icon (Android 8.0+) - Optional + +Untuk icon yang lebih modern dengan adaptive background: + +### Update pubspec.yaml: +```yaml +flutter_launcher_icons: + android: true + ios: false + image_path: "assets/images/logo_apsgo.png" + adaptive_icon_background: "#4CAF50" # Warna background + adaptive_icon_foreground: "assets/images/logo_apsgo.png" +``` + +### Generate: +```bash +flutter pub run flutter_launcher_icons +``` + +--- + +## ๐Ÿ› ๏ธ Tools Online untuk Resize Icon + +Jika tidak punya tool untuk resize, gunakan: + +1. **Android Asset Studio**: https://romannurik.github.io/AndroidAssetStudio/ +2. **App Icon Generator**: https://appicon.co/ +3. **Icon Kitchen**: https://icon.kitchen/ + +Upload logo 1024x1024, download semua ukuran, copy ke folder mipmap. + +--- + +## ๐Ÿ“ Checklist + +- [x] โœ… Package `flutter_launcher_icons` sudah ditambahkan +- [x] โœ… Konfigurasi di `pubspec.yaml` sudah disetup +- [ ] โณ Jalankan `flutter pub get` +- [ ] โณ Siapkan logo 1024x1024 px di `assets/images/logo_apsgo.png` +- [ ] โณ Jalankan `flutter pub run flutter_launcher_icons` +- [ ] โณ Build APK dengan `flutter build apk --release` + +--- + +## โ“ Troubleshooting + +**Q: Error "flutter_launcher_icons not found"** +A: Jalankan `flutter pub get` dulu + +**Q: Icon tidak berubah setelah install** +A: Uninstall app dulu, baru install ulang APK baru + +**Q: Logo terlihat terpotong** +A: Gunakan logo dengan padding/margin agar tidak terpotong di adaptive icon + +**Q: Ingin icon berbeda untuk debug & release** +A: Tambahkan konfigurasi terpisah: +```yaml +flutter_launcher_icons: + android: true + image_path: "assets/images/logo_apsgo.png" + image_path_android: "assets/images/logo_debug.png" # untuk debug +``` + +--- + +## ๐Ÿš€ Quick Command + +Jalankan command ini secara berurutan: + +```bash +# 1. Install dependencies +flutter pub get + +# 2. Generate icons +flutter pub run flutter_launcher_icons + +# 3. Build APK +flutter build apk --release + +# 4. APK location +# build/app/outputs/flutter-apk/app-release.apk +``` + +--- + +**Selamat! Icon APK Anda sekarang sudah bisa diganti! ๐ŸŽ‰** diff --git a/DUAL_MODE_IMPLEMENTATION.md b/DUAL_MODE_IMPLEMENTATION.md new file mode 100644 index 0000000..75a9e12 --- /dev/null +++ b/DUAL_MODE_IMPLEMENTATION.md @@ -0,0 +1,466 @@ +# ๐ŸŽ‰ Implementasi Dual Mode System & Bug Fixes + +**Tanggal**: 26 Januari 2026 +**Status**: โœ… COMPLETED + +--- + +## ๐Ÿš€ Fitur Baru: Dual Mode System + +### Mode 1: Smart Mode (Adaptif) ๐Ÿง  + +**Konsep:** +- Pompa ON: ketika `soil < Batas Minimal` +- Pompa OFF: ketika `soil >= Batas Maksimal` ATAU timeout (durasi habis) +- Prioritas: **Mencapai target** > durasi + +**Cara Kerja:** +``` +Trigger: soil_1 = 25% < 30% (batas minimal) +โ”œโ”€ Pompa Air ON +โ”œโ”€ Valve POT 1 ON +โ”œโ”€ Loop setiap 5 detik: +โ”‚ โ”œโ”€ Check soil_1 value +โ”‚ โ”œโ”€ Jika soil_1 >= 80% โ†’ STOP (target tercapai) โœ… +โ”‚ โ””โ”€ Jika waktu >= durasi (misal 60s) โ†’ STOP (timeout) โฑ๏ธ +โ””โ”€ Pompa & Valve OFF +``` + +**Log Example:** +``` +๐Ÿง  Smart Mode: Target soil_1 >= 80 (currently: 25), max 60s +๐Ÿ’ง POT 1 watering: 5s, soil_1: 35 +๐Ÿ’ง POT 1 watering: 10s, soil_1: 55 +๐Ÿ’ง POT 1 watering: 15s, soil_1: 82 +โœ… POT 1 reached target: 82 >= 80 (Smart Mode) +โœ… Sensor Mode: Watering completed for POT 1 (15s, mode: smart) +``` + +**Use Case:** +- Tanaman sensitif terhadap over-watering +- Ingin efisiensi air maksimal +- Sensor soil moisture berfungsi baik + +--- + +### Mode 2: Fixed Duration (Durasi Tetap) โฑ๏ธ + +**Konsep:** +- Pompa ON: ketika `soil < Batas Minimal` +- Pompa OFF: setelah **tepat durasi yang ditentukan** +- Prioritas: **Durasi tetap** (tidak peduli sensor) + +**Cara Kerja:** +``` +Trigger: soil_1 = 25% < 30% (batas minimal) +โ”œโ”€ Pompa Air ON +โ”œโ”€ Valve POT 1 ON +โ”œโ”€ Loop setiap 5 detik (monitoring only): +โ”‚ โ”œโ”€ Log soil_1 value (tidak break) +โ”‚ โ””โ”€ Continue sampai durasi habis +โ””โ”€ Setelah durasi (misal 30s) โ†’ Pompa & Valve OFF +``` + +**Log Example:** +``` +โฑ๏ธ Fixed Duration Mode: Watering for exactly 30s +๐Ÿ’ง POT 1 watering: 5s/30s, soil_1: 40 +๐Ÿ’ง POT 1 watering: 10s/30s, soil_1: 60 +๐Ÿ’ง POT 1 watering: 15s/30s, soil_1: 78 +๐Ÿ’ง POT 1 watering: 20s/30s, soil_1: 88 +๐Ÿ’ง POT 1 watering: 25s/30s, soil_1: 92 +๐Ÿ’ง POT 1 watering: 30s/30s, soil_1: 95 +โœ… POT 1 fixed duration completed: 30s +โœ… Sensor Mode: Watering completed for POT 1 (30s, mode: fixed) +``` + +**Use Case:** +- Sensor tidak akurat atau rusak +- Ingin penyiraman konsisten setiap kali +- Sudah tahu berapa lama perlu menyiram + +--- + +## ๐ŸŽจ UI Changes + +### Sensor Config Page + +**Tambahan Baru:** + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Mode Penyiraman โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ— Smart Mode (Adaptif) โ”‚ +โ”‚ Siram hingga batas atas tercapai atau โ”‚ +โ”‚ timeout โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ—‹ Fixed Duration (Durasi Tetap) โ”‚ +โ”‚ Siram selama durasi yang ditentukan โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +Batas Minimal (%): 30 +Batas Maksimal (%): 80 +Durasi Penyiraman: 10 detik + +โ„น๏ธ Smart Mode: Pompa akan mati otomatis saat + mencapai batas atas atau durasi habis + sebagai safety timeout + + Fixed Duration: Pompa akan menyiram selama + durasi yang ditentukan, tidak peduli nilai + sensor +``` + +**Interaksi:** +- Radio buttons untuk pilih mode +- Info text berubah dinamis sesuai mode +- Durasi memiliki fungsi berbeda per mode: + - Smart: Max timeout (safety) + - Fixed: Durasi aktual penyiraman + +--- + +## ๐Ÿ”ง Technical Implementation + +### 1. Data Model Changes + +**Local Storage (sensor_config):** +```dart +{ + 'batasMinimal': '30', + 'batasMaksimal': '80', + 'durasi': '10', + 'durasiUnit': 'detik', + 'mode': 'smart', // โ† NEW: 'smart' or 'fixed' +} +``` + +**Firebase (kontrol):** +```json +{ + "batas_atas": 80, + "batas_bawah": 30, + "durasi_sensor": 10, // โ† NEW: dalam detik + "mode_sensor": "smart", // โ† NEW: 'smart' or 'fixed' + "otomatis": true +} +``` + +### 2. Code Changes + +**File Modified:** +1. โœ… `lib/screens/sensor_config_page.dart` + - Tambah radio buttons untuk mode selection + - Update `_sensorConfig` dengan field `mode` + - Convert durasi ke seconds sebelum save ke Firebase + - Dynamic info text berdasarkan mode + +2. โœ… `lib/services/kontrol_automation_service.dart` + - Update `_checkSensorThreshold()` untuk ambil `mode_sensor` dan `durasi_sensor` + - Update `_waterPotBySensor()` dengan parameter `mode` dan `durasiSeconds` + - Implementasi dual logic dengan if-else: + - Smart mode: break jika `soil >= batasAtas` + - Fixed mode: continue sampai durasi habis + +3. โœ… `lib/services/firebase_database_service.dart` + - Tidak perlu perubahan, sudah support `updateKontrolConfig()` + +### 3. Logic Flow + +**Smart Mode:** +```dart +if (mode == 'smart') { + while (elapsedSeconds < durasiSeconds && _isSensorModeActive) { + await Future.delayed(Duration(seconds: 5)); + elapsedSeconds += 5; + + currentSoil = await getSensorData(); + + if (currentSoil >= batasAtas) { + break; // โœ… Target tercapai + } + } + + if (elapsedSeconds >= durasiSeconds) { + // โฐ Timeout (safety) + } +} +``` + +**Fixed Mode:** +```dart +if (mode == 'fixed') { + while (elapsedSeconds < durasiSeconds && _isSensorModeActive) { + await Future.delayed(Duration(seconds: 5)); + elapsedSeconds += 5; + + currentSoil = await getSensorData(); // Log only, tidak break + } + + // โœ… Durasi selesai +} +``` + +--- + +## ๐Ÿ› Bug Fixes + +### Bug #1: Logout Tiba-tiba ke Login Page โœ… FIXED + +**Root Cause:** +- Auth state listener trigger saat first load +- Listener tidak memeriksa apakah user sudah login sebelumnya +- Bisa menyebabkan loop redirect + +**Solution:** +```dart +class _DashboardPageState extends State { + bool _isInitialized = false; + + @override + void initState() { + super.initState(); + + // Verify user on init + if (_authService.currentUser == null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + Navigator.of(context).pushNamedAndRemoveUntil( + '/login', + (route) => false, + ); + } + }); + return; + } + + _isInitialized = true; + + // Listener hanya trigger jika sudah initialized + _authService.authStateChanges.listen((user) { + if (_isInitialized && user == null && mounted) { + Navigator.of(context).pushNamedAndRemoveUntil( + '/login', + (route) => false, + ); + } + }); + } +} +``` + +**Key Changes:** +1. โœ… Tambah flag `_isInitialized` +2. โœ… Check `currentUser` di `initState()` +3. โœ… Listener hanya trigger setelah initialized +4. โœ… Always check `mounted` sebelum navigate + +### Bug #2: Crash yang Menyebabkan Logout + +**Root Cause:** +- Unhandled exceptions di Firebase calls +- setState() dipanggil setelah widget disposed +- StreamBuilder error tidak di-handle + +**Solution:** +```dart +// Always wrap await dengan try-catch +try { + final data = await _dbService.getSensorData(); + if (mounted) { // โ† Check mounted + setState(() { + // Update state + }); + } +} catch (e) { + debugPrint('Error: $e'); + if (mounted) { + // Show error tapi tidak crash + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: $e')), + ); + } +} +``` + +**Files Enhanced with Error Handling:** +1. โœ… `dashboard_page.dart` - StreamBuilder error state +2. โœ… `kontrol_page.dart` - Load Firebase state +3. โœ… `sensor_config_page.dart` - Load config +4. โœ… `waktu_config_page.dart` - Load config +5. โœ… `pot_selection_page.dart` - Load mode status + +--- + +## ๐Ÿ“Š Comparison Table + +| Feature | Smart Mode ๐Ÿง  | Fixed Duration โฑ๏ธ | +|---------|---------------|-------------------| +| **Stop Condition** | Target OR timeout | Durasi habis | +| **Sensor Dependency** | โœ… High | โŒ Low | +| **Water Efficiency** | โœ… Optimal | โš ๏ธ Depends | +| **Predictability** | โš ๏ธ Variable | โœ… Consistent | +| **If Sensor Fails** | โš ๏ธ Use timeout | โœ… Still works | +| **Over-watering Risk** | โŒ Low | โš ๏ธ Possible | +| **Best For** | Smart irrigation | Manual control | + +--- + +## ๐Ÿงช Testing Checklist + +### Dual Mode Testing + +**Smart Mode:** +- [ ] Trigger penyiraman (soil < 30%) +- [ ] Pompa mati saat mencapai target (soil >= 80%) +- [ ] Pompa mati saat timeout (durasi habis) +- [ ] Log menunjukkan "Smart Mode" dengan target +- [ ] Cooldown 2 menit bekerja + +**Fixed Duration:** +- [ ] Trigger penyiraman (soil < 30%) +- [ ] Pompa menyiram selama durasi penuh +- [ ] Pompa TIDAK mati meskipun target tercapai +- [ ] Log menunjukkan "Fixed Duration Mode" +- [ ] Progress counter: 5s/30s, 10s/30s, dst + +**Edge Cases:** +- [ ] Switch mode saat automation aktif +- [ ] Durasi 0 atau negatif (validation) +- [ ] Multiple pot trigger bersamaan +- [ ] Sensor return invalid value (null, "", 0) +- [ ] Firebase disconnected saat watering + +### Bug Fixes Testing + +**Auth & Navigation:** +- [ ] Login โ†’ Dashboard (tidak logout) +- [ ] Dashboard โ†’ Kontrol โ†’ tidak logout +- [ ] Dashboard โ†’ Histori โ†’ tidak logout +- [ ] Drawer navigation โ†’ tidak logout +- [ ] Back button โ†’ tidak logout +- [ ] Network ON/OFF โ†’ tidak logout +- [ ] Multiple tab/window โ†’ tidak logout + +**Error Handling:** +- [ ] Firebase call error โ†’ show snackbar, tidak crash +- [ ] setState after dispose โ†’ tidak crash +- [ ] StreamBuilder error โ†’ tampil error state +- [ ] Invalid sensor data โ†’ handle gracefully + +--- + +## ๐Ÿ“ Migration Guide + +### Existing Users + +**Jika sudah ada konfigurasi lama:** +1. App akan auto-migrate ke format baru +2. Default mode: `smart` +3. Default durasi unit: `detik` +4. Konfigurasi lama tetap tersimpan + +**Jika belum ada konfigurasi:** +1. Default values: + - Batas Minimal: 30% + - Batas Maksimal: 80% + - Durasi: 10 detik + - Mode: Smart + +### Firebase Update + +**Manual Steps (Optional):** +```json +// Di Firebase Console, tambahkan fields baru: +{ + "kontrol": { + "durasi_sensor": 30, // Default 30 detik + "mode_sensor": "smart" // Default smart mode + } +} +``` + +**Auto Update:** +- Fields akan otomatis ter-create saat user save config pertama kali + +--- + +## ๐Ÿ”ฎ Future Enhancements + +1. **Hybrid Mode**: Kombinasi smart + fixed (min & max duration) +2. **Per-Pot Mode**: Setiap pot bisa punya mode berbeda +3. **Schedule Mode**: Smart mode di pagi, fixed mode di sore +4. **Learning Mode**: AI adjust durasi berdasarkan history +5. **Notification**: Push notif saat watering complete +6. **History Graph**: Chart durasi vs hasil kelembaban + +--- + +## ๐Ÿ“ž Support + +**Jika ada masalah:** + +1. **Check Console Log:** + ``` + ๐Ÿง  Smart Mode: Target ... + โฑ๏ธ Fixed Duration Mode: ... + โœ… Watering completed ... + โŒ Error: ... + ``` + +2. **Verify Firebase:** + - `kontrol/mode_sensor` exist? + - `kontrol/durasi_sensor` valid number? + - `kontrol/otomatis` = true? + +3. **Reset Config:** + - Delete local storage + - Delete Firebase `/kontrol` node + - Restart app + - Setup ulang + +--- + +## ๐ŸŽฏ Summary + +### โœ… Completed + +1. **Dual Mode System** + - Smart Mode (adaptif dengan sensor) + - Fixed Duration (durasi tetap) + - UI untuk selection + - Backend logic implementation + +2. **Bug Fixes** + - Fix logout tiba-tiba + - Enhanced error handling + - Prevent crashes + - Safe navigation + +3. **Code Quality** + - Better logging + - Comprehensive error handling + - Clean separation of modes + - Well documented + +### ๐Ÿ“ˆ Improvements + +**Performance:** +- Check interval: 10s โ†’ 5s (lebih responsif) +- Cooldown: 5 min โ†’ 2 min (lebih cepat) + +**Reliability:** +- Auth state management fixed +- Error boundaries added +- Graceful degradation + +**User Experience:** +- Clear mode selection +- Dynamic info text +- Better feedback +- Predictable behavior + +--- + +**Happy Gardening! ๐ŸŒฑ** diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..d7c5e88494748d5e5c8332b61dea1f7351ca21dc 100644 GIT binary patch literal 9068 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 AQ2+n{ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..30707fe3f5dcdccb3b3ceef3d56ee81e4cde19d2 100644 GIT binary patch literal 4931 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 Y0J!#jPCWs1;s5{u07*qoM6N<$f)pyN&;S4c literal 721 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!iOI#yLg7ec#$`gxH85~pclTsBt za}(23gHjVyDhp4h+5i=O3-AeX1=1l$e`s#|#^}+&7(N@w0CIr{$Oe+Uk^K-ZP~83C zcc@hG6rikF&NPT(23>y!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..f9f5169e76da7219bcdae401b3f8b7467c5e9146 100644 GIT binary patch literal 25716 zcmV)AK*Ya^P)l|LzO;-}vty z{{ztf#%YcJ0q6una*J4W3(!&DMgHB_@}D~X2cYAOoT1rx-0iuywJR;o>JPi0-L?8) z@ZWt+|B2&&06Nadxg>pjPzpaFg3FmUm#Z(`EWH-!4AFo0b^WJ}{{iS2BX?qEf;SC6 zi8(%&Dhd)}rC*=>vsv2~|N8SCviv`7W*`6G0Ehzs0tlsM=5k$K*pZn(KDDu_&Yu?3 zx_#@GEwb^s>=CKbxE~C8Bt#=qFkpl@1PG(dU`qR+#(ihhMH}AUR%bu%)jx?(_jvsI zA}`kO-`sw*>;MTII#^4{|0g*6KMf#%Oe}J_;~f#2lrm`8gbA{oI5R9ehQ|>hol+d{ zh~N86UG@80jnBK|9C*l8X}5(?_&Od77y|`B2n4{0<6v^m*|CpRZb`hY@<2nt_)cb4 zOv2C!j=yo3cTJq_DMJJuR^-@SJ2!VO+x@k2u(DKc*|noy`L{iUZ+ZNm15id*Hg0MW z;*+u`k9Wu9%?pNI(|v-J6A1Bcj1cBAAcQbDq{x~`_2y^FE9+HRfiZ%!emZ6rP7!{E zkRW0JK%o5(V+K&Om!#AG2>>A^z@Wl$o{XZo6ONX4 zU6Py_caA8zvM55FQ>2J8Bv1mB{YMc{kQ=(2R^EMZ&o`~cF)my*CtXZao+KRdslWlM z9~-IQVm}cEu@kla@}m3ZZ2jO@D>ig@bwhJQgsl3w^MwmXI3A_8CpA7(d_n$q*1f&+D0_d|JC%*Y#!7b$8S7hu5KRH9o>V;_ z$BZc8IK=VkoSR-Nxw@~s@Opgc+^f?+-uh+So1521_HSI-6bT0ZJEH{ug#bd3l|Ry( zmUrfvjjb`4^X`;$B+SQBLXZX=%4QBg1pv||344pELP!qRy%=s;@mip(OI`hmsV+R^ zywp1cpW|voF$P3cX`^Zx?W-z)kp3V0n_F^E;N}XC7M2XU>A`ki__Ml~U2)5xk5(`4Shjj;Yh!J7 z*MF5%`mX^H$8q3`%k#UEr(PoXhu+!|c8_p*1%ZheY5+%-7}|tsIKrmnQ4~ahguV{9 ze)Q|=BV`fxNrVxMygGM|JI(bo1e}1_adg86>C#6#tN}*#=zu6jynrt)zO`_(@&r6v zy1$%sw8->}x5*#RyNN%C0TXuCpxR#$lVuoj2>UrvxHK_CzG&esQu(>p#J|4o zeeX-Jz1mO{==$%lyZ*M-W(X6)z@j~9gs3WbjEQF8 zP_>xC)K*xQL!Bj|_N6yf9@^V%9CyNHL-OK^VxL2t5W_yF2^h`uGN;|Jq(MKvb`s!0 z5JO_%zVX-R?ml!+`EvH>H(&Us$(NRQ+r&BCUj*J+#AC^!DnF{LQyt`nR035s@(k#l zz;k)nAHIL)b%E<9UXc2yO)I>wz450*4ILfffBggR$M}y1P)gb$4y2(II{C2=MZ&)6 zqC*fd!n&Fw#=M%g3S|?|-Y2R^iVz}~18HC76YD-deCUe}9jzrb# zUx1kQ(~V|Bih(Z0^doCF5!FAX;(!B*bHsL~c0E!!D{I^S6-S#CMS(xue{g$iWzpQM z!GZkZGd#0o(scnscree5bBZz5T`*H(st05dC(MqCF)!ut36aOAEbO{`!rYwupS!1Z z*_MrUk$>%z@B8?V1Q2tZ;|r!GIlU9^5~b9evB>+tTC}RP0o^cW6HS?YK~)hXb66>L z3GK@woku=Ce0c3P0kyR4t*(l4oE>f+2Avao8Fq3DIVP&8I)`9tDrB>s!8oTSL`+pl z0XYT7*x{FD{kVQ#%LC2T9ST!jZ~S?0BLFmx9w&d}bO(Mve7ul3Z9&WhqE|j!cz)>DJBydO+i)AkIw;%s5?r%F*BtQPrQ+vzW+GO}o8~OU;ovwFkcBfC2yk?=8% zQHGbV!k=fZpbMO7jZ)!iRIPU^uF&P&U13`KvE1Ths;tRJ0NK#4MX!G;_S zyry+zsyGTxEc)fF!0#tqTXaiW-)IEZ_F(bK7Kefe?hBOkohSk_eEb*hhgK*xUsfKmnx z;;C!Wr4iqxN1c)?YH!@r0MNJ4?x0C& z&Jh=8JT8i^F-%c_u0ojIgzVt4W5_Vd4Z!GzHl~(m#^i-x@MrTgrKe{#PP%mEdmEZg zbRCry4fOTr8ya5!GyG}(r5SHcogX+Wf28kvry!gO1RR6_(mYEefPg{b9S)4q4bvCG zjM0;mpZMTK{ML%4rT_Ne(Z3x)asD((OgQ&0Pu%ePFwZ$vsiWIC$S^cj!x#Yx2OBwP zev)>qdSTGjz%TjBGMB+d| zE`iMS&kio~t#E4yW$3bv}xuEk6m%DTj$(Wyn6A^zglY8Nqre#uc-=tXL`f?mwq>6!&z6y-s|`K zuE9w55=Aj%S!zHM1Q6J>a>zfv^|5&W(ziYLe(*|lb01Fet&D#QfFy?lQbwPVALPbA z6{NleJV!9qjaZ~VbgJnJB1b}7#Bj+2Wjo(l-qzZ1N(N+7VT>z(R_en%FJx#|H&Q)0 zmgCX@N_$y8pkD=m4J10asBRD0L8iW<7#tva<*eZ26BiEIx#7co)&057Kp+gSzi_y6 z!*0@nTX}eE%c-7D^7DA%FN%K{ zadH=6s)if2>Bh(uG@55;d_xBc*rg6N4Q4RqqFqv%p8}Q#6FGil>OB8rBUstS%sgQpufLMasGxU;kJ5c6}evg=e`1&t|*gi7U(op+XQdH0`@ z(5VyJ6O!W4oV$jd7Z&lo7$F{+S}|Hln!i(O`C*>?b`J}3W!H8k9bA? zC5a{VD78>yM*_9-YU-e7yxNLb12Uy2HEkjc z0u}^kEc)q`tA}j*=#j5KXKM0PMt5ff{_t?=CM7&{;fzZh&+(jaz8cYH*QQG&4nza^#ctwto$V{zU*n;23&-aZ={!mpyJrGP0=)ovH&0i|TaM{-ycw zC%9R^^8qTLLD)vkh`uZwU}=%+WnbpW|NUhs{*WP-yt^bMcWnMWxzM2yfY1(w4k zlm`4#=)PU+UVE;qweGYdt&)R>Gwv+9it|YqYpDv&YtmeH1eSMXhmA%YW0HdQ&|}B| z8rAY`VE>jq^ry#etZ1@;5Em&}wpa(d_iH4XrJ#xcp)>RZnC@TBT zVQi_aC^e-4kLcVY)0GEgdS%p9$y!pEc?Xz7SVo~6rvIRDWyM`Ji%(UR8Kd%OxBS5O z_rCe-7pF9M66;>%Fv?&EqNX&$Ac@W_n%MOE-A@d^{#Uo{T-Ma^?M1D>RREHe$7<;v2S5X2;AvF8{LhZ;I+ci&jE^_@?sa_rnyf5)IHiSCq%{>KT&ryEH$3Kk|* z2TWBl%q+t?(+~s4V{nUpNOGk>q9+YJq89`#sySMj$j}|?g4XUvXy~eemS7Wv<*=Go zAwYCH$>!SkP>CGoois&$c<1YU5qk+we^6l6}-x`3DhK@@R(u)7!a5%Ee>Ch0)2AU}*9f z$nXvVr{D#S6HP^gkYVZ*AT$CIIS9?cCMa##3)?Ez!{O!<2r9u|G!Q$Oc;FG{>5vIm z=iRpF>!bH~w}nreEmmLG0?$3z@|Dw@f6b`rv2XK8$}<>~ps6$DaCkC@&**x3=FYea zR({m@tpppt6#%)?lf}&O=RPbW=M;*G8C1c#sTm1JTH2cr|EPY`TOSa4fWU(gT>H?O zBTy{5m*;qa8CS%}*c;wE)zTvf^N{M#gc*a*hLI_g!6(J3#4ha$!A2m{?IE2@!fXu8 z^N{NGK#Dg5CJs6SDw@mTtFkX(XU#_Figbb&5Y<%~(wL1}fRx7}F?93A4@_M1-h&&y z2)3L)#fN2met%#2rXM}TT{CiO%xgRr3N%%#os;7P-UMJ2lfq0@iT z(PO{?Bqk(4V)6M`M>y#^m5PBBa}~37p~!M)Tg}cps=xf(+e8ixm|UHB$>0<*kNz16 zLLMfd2MQ4=l;{KtKVNA*SmE4)frX;vw6g2SXD^z|iE;;Bk0?Lp&sVGvU%PH^S)5 z3GmUL*P*7p0x0{gD#D0vg!4$`=-{Mjjwgoh%e{Kv#=`@W2vrmcuRL{R?TB&1Z*wO$ zzJj?(jLDx;nUii&@r3UC-aY=qzg;AM+Fai~V4m540w{j$%mN`X=Vi$yrKmh$&0JRB zlL3@KOZ_v!{a?K>kl=B+B$#&Xka=Q^bOWy=m8whMvU6fR$#CJY8=xq0G;ml@(+wdd z1Uu`t!1}V4DsTwMx`H)4DZP$NHIp?noJpewxdQ6DDqwTXmk?W;2op1=!<5Wf5aUV! z9`P_Fu^2L@Pr!c~zVR5RSnc$C4%7H&!|HHVN-`&@}c_7XS0|_8cPP&vla{Qx$;4HxU zibHzHT>(NlIrQn#m9IY(Y^@(qUgor`3sPJ;=rLa4+$w0XZg*gnVGNm`!Enj&??b9T zR}JndM23>a-LUk)VmQ>eUj+pq!??yE@FMsmKX@e{km5~;wrdD{~m*u>7 z`c=bLFL`>y?tyw1RYx1?^AEyHcm6h~z#W5bXZ4L*?o6}$B*~XiG>twnYkuOwm7g`V z55zeK1VB8`L&~_B^ROVD$6+J3rl}IPeib=X*|~S)J;Bz2rO~D5`f%cC_ib30b|q8 zgsqipp{lLRv`JL30tXI`PYVtS{Br1+oNM=OD;qH5cJGeH@ZuMpk6eCN%1B<|&Oiw3 z6mm_SaSrE<3x0sE-S}n8AG$i_)0Tb%0-%B^Q{!=L+WjJjMJ9cWC@#A?Sy8&2p_V(# z_ij8e5a%)mi}#JXAY+0fPPk3L9M*z&b_%nmG*(o62%PAKG0Br)+2IdhQ^h(6%OTa3 zXQDkbCL3nw&x4}mG2j;bD&Iz>2dKkLf&tI*kmhwmsy7R!X3c^6u4-6cwhXpaZ-9`} z4HyxqYCiz4Z~rZvTX-ELxzpg|{VzjPpiWiec3QQ@fE2*%Cl?I8D0Y7N{+h*sz`&6U zKVMwgFkyD)o%zM`63pR2ItN94K0N2-J@LvH>mKYeRSd&|lB%KaIW77mI{8sm9FoKoCOZvtCwYHk?JrBKv3?6pd3`ym4JD0 z@=i6dh%pZWazL6l2NsUJ5hmo!hGhrehO(xEsx)c|)W8S3{{UXe4|QFYs>;;D0K_U- zWYUu3Kth5eCSUpWq$>+HuX=e`#Xvn9W>i0N$JaZ4`RBnu%O4r{It7rh9&KlNK~a!0 z3#NB08a%>x`@vo9rxi~)Z2)CXojO$V#oQqY99HzQ7pngYg@fBWx2$@irLk#1%Pez7 z#0c4E`R^56uBklcO-DJ3zQagj%%oNj1W5pDJIl4GFybLAp%Bg=dL?A|3K+!Ih;mTw zghMU+U~k>|t^PA{inC=(e>UX#r^Pm@$HwIp`jT!;!#ZEm*50 zR_*>sB+|jv9Q0!3i$LNe_+-!DKx@um6a$bbevy%{SA&ZT&{RNPxAU^$3sW}N@9X$| zL(RZR>b={W!e1{(%B_pJvS)u4tLI&z)vTgk#N<9)s3gDKTiulnbRjt z?sB>>oh#`v=#p{_=E=f2=WNMoN5Kj_W=_aJWqRgxLVWK8W=fYEd^@ zx~pMN!!9UJp8>&02Yj^uP1set5yEm16e_bKGyU@z2sINLU=#o`?=C2}!N%%kP~Nr| zE-d^3WcqSc1A9m1H#+!0_tc10tKXxt!x&7ph#>GWsbo>{h1s8d{(Q^9fqY&czFb~1 zdPeb+&e+iJIK*RW-Vu*Pr+eZqH&^U$ z9I#U6hPn>=&C>d}r(Np(J}1CXZBgj;{aA8IzV5qbogcq;`I5%gJ{+#sIAs8(P8fR* zbqHs(j$h1pG)P-acso$|Z#$N+IBtR6sg4O(CJmR};hzYc6C;DSY$v!Iq<3nm2s>|? zr7IK`L?m6X=J30~vUG%MuDstF1Cugm!^E^Rz#}H;wd%<9>PDtchB^5cLrKd{SXcHB zIMPz8g0iK%4i@iyR<*ZOgIOiQ5iOQMdyv&^Mw+Q?b&Rp_73Ss3X5C!)&g*wJF6&1@ z$4STguawr0o;B);m=t)4F>Wn^%(DWAX%pxB&Ry|Q)0_RNA~D7(0jOZi2&du`?r`ud z+NtPCQq2a5guAQCR;_-frM_W6b1q4FPAPk$`+klW5)CK27jM~kY1aHvTaA~N7**7) zx)!0uSd&}@@uT3}Ay+|yJ5zJx+2=?^&AziQvDPw%_cbJbJmkcUfaN8BgAL`&RIiT8 zRQ38aOcR|Uq}x!(qC%QHBe3{HfI#9!H_7OJc*49vn>T(|K497Nj`j$A@cfa*SKgCx zqub@1re;|5=(@nU_{8k4AB>smUb1yvXJ>zpeM$iGjLe(KiFmTsT18uCEEGd1k-OXL zp03}#=d|Ms_Q4XD=iHLNkaK#@<<+ctG#yn_TNeArFj}MSB8!PgZF9&ZMPb}Xh;t4C zCXxb5P?a+K>$gBdXEhK-29F~K@)AbEsH92Yb;N=ME*P3P0XA2xU~w)j-$t#N5$N3L zs6s?rBLx#S26spoRvhBMAv(s6yDaU_ngi|kA3fAOV2kjpWsU9AFYw$~Fvjx{0_-!J zkx(AoF5iTUZpI6?eRbfS{tBUf14xSXVI1$cg2y1S^{mD>)W8@4Sq_%ee!cBY5*iqC zm@($umY3wra{f$U%@9mO)J7XLQs2~44VJdB2wsLHvcJW?xBT$CzzH76i79~3Oa2a} zExSObL9K}c^P&O>4z+KA&DG0be(p68=SqRk_Wl(VC8$aslT2o>bhV7LsJ03EnN1K` zm#C>-9&=dn%ilZu=HY+;%c3v$4Ae7Zv7t|1Zuz6O(F5BQK}ha~rTbq7w=)*nf{mK$vSsr%%*xQ*T@LTw&k(>5_i!0|CsuHRDX;;BFULS2A7RN5y`kR=2H6(2gOKC}_Vli|fo@hU6{?b#|%N zEHj`pefcmvVWN7NqiuVjq-75TNeH@?76^n|0h?(P%iFS_!wzD5yFAmVx5pn$aA_CH zV-68Ma?zOc6IX3sR{DZ04_qAflh>4C>>!HJ^a#P0}MQ0`X6LLMj76jgh4c8cs zVV6clc%y$%+r*-%nKXV9y*5ny0!zRaCX9iLhuj8kDOL+h(DPtj`4U)O@(xgB&^+|h zU<$bRsWpqIXEl-O%G-$eW=WdOtHcNdP7vh8?z?APmbY}}TL+H})HB_+simv4QGO{l zT_`4iIMb9wJZs!=$%Hdwe!qN4cb_x${gXhso?#9L9>#H2bXB$G6>%53$MA>D@2EC7qOAEP%*?(R+>UrP zD9E5GfCDCEo(o$mRzq{}s7ay+QwmuCwHu+A4m1wPvke6Zto^9^)44Zg-6vo%(;hDqd8u&RSxFO@FRA{bugB{%fCQHS2}6_5 z5(MmGnP;_Ap}vw7C48urG?y zh#5r&^;V};ht}I>NS>BUVaD(3&4=tezGfiQKC^kEG*noVDm(kd#Hnj#(1&m0g z0{Pm1lVidx$O6mACvOse3%R_;_Q9b$$ zptSrX$q^&W;yHGo#0;`Us6uI|d&S}PC8re0Jl1fy5Snsr=Jle(or?`?BFZlAp)asa z3fRCyqyJ}<7SPkwHoY`TBhVUZ0GH@By)N@KRj-807~kIWc$DmbDH@WWTzin>18}mS+s{d{^y-ZpNlLgasHk zX=b8x&C;4a(QW$-puy7%3q+?lj>m?aC0gK&L?Z1aO&!9$%;W`4y8Ac6%jh;rxk$)wbhK~#a&rSHJPVYh=r@TkXBXc)HDEQi)$ zjaGksv0S-8F5!38TC%(-qdfYi}xMsRwIf>^6z{MIC5H5gAjn1`y4J+Qxdvl^X_0wS0PJdS;3TwIUO+rtoGNgm^~C~9FK5V7d>Wp&;& z=8UXwwyho*%E!)iopp1sB^xnEvyi?ugW7wD=X_a(=nTn)w}-k<9>?qxfRgk4QW#*q z!0Y9Y`UFrKQMg66Qbim}S6!SW=n59;5w*+bAnigFw{ zoYJgm-%Yr1@1~l!LS3hyYpp!ePCDx4f5v7B7Zb{9y|yS*Vgd$HIe+4`)R(?oR&`Ra z_6b1VG%=17@Li5fAvGlz_UzIa3|hggF?^`bo;cnSGk9c7;b(8vl%MPveF9MKq{PVzA_7Jlw^w~28~~Qd!OkOX zrxZM#eL;>RAy>I4C^y6nDNg!o&!)Pd+(qECXAiHL|I5PFlo#e=VwncUg!Ggq_7SC; zY`&Or5URV|y$7{+_v|m5BVfx+uEkKSy#p_B~%%M)L5IzUJ$d3q%+)Z$LC#LCnhVN6hNGW zkxaPZ9H*Jhtkei$3alkI=~A|qZ>#KgbtU2voPSf&WKP8A@RaAKp6{ByXA}H`y;rii zC9r>4^@Af8WKG3L@I@sm?FJ`8(E&OmonU+X$gTqI8>r~gre|^$OlrOHDX;>3#_?(N zyS)Gqb)(2C=fT*t#Wbd(@ezG#^teyWB(dMKR=4I79qB{Q^4wXvHvX3KgH5O9)>j;E zq18u&o6_=lSxvziikQ)IXk=_$3MX_Of)g5CCk0T!s5D6?kzqVYDsZ&yyZStc-|gPm z*4PJAy~h|cB|-Fh23h)f9R7CzD`v25d2j+4N_ zd%(-bK}Wa=+QaodR3wVJ4$EWIcTc1|^7-8tkH0wa59N4@EAP7MP@n}=hXNS!VI%0)E3KiA9{qWu2JIO&?vZ#k zL*lZx1O=Q~QG#Uu(%EzS7g(1l^z%QkOQ*fyp&{ekXf%kSk2oXl9wZ|i9 z^gSAX(@xBR#5-dOCX$=d2gTk|Ro-%1w$Qe<<<&_;6UuljCQ)_QAazqbK!O;bkSV7C zzzOqWCk0T14`v|V7pqN{5kMmlA%QkQ=K5XB#PI+{MgEH^5Alj_&|)MCaT&p%PMSAp z<@$ddR<{ZGYVpyU;nPxo;mH+W1w>tjDlh|o#*EpbxX~k;&T0|bb@|ZK*3btv8X@Sv zSuOD_OX^s53z!~WlvNB`LPCVmsq-MkUjRna8TO`2@~UlRI0#!!&vnC}+8b7ZA<49% zZp9*@3rC&ff2!(*meWdLSC<9@5m`ATiZD^FrPGZ(gppTD>?~kihfnnVlLAO^^O+c7 z-r8bGlR$`|ny;7c=y&{Q<~i}62<^DQfnrt0CnK^etPoiAjGKEM`uV|a&U+iH1L1H; zhUI@LUwqkvWAlPS>-`8Jht(sQg3V>>1%#eicS}tfR1h2RqWU~rF1jUsjI6oc=ILob z_m+1C5Rl%SYUzPzAS1RAMx`%+IA?|`du1vc?yPxF#_>&KAK9dy?HVOsN*gxQ{k<(8 z`|qne4Uep(td0Q5u2G`yPdz2?^a`0L`~FD*lu?|LAR`&uSFGBuo1liy=3u{#xZ=sK z(W1jKQX{RiQd-o>$Y^&_=JQzdiWEgceC`6`+HFVVP zO)aMMIO0zB{SyO-2k35ZOLHW+O>wG+&hgt_7|DjbdGU@S@6MG_D> z4#J8?gWxdd^ry%V&ArLjdGKKU%fSwX?A=}wX<5|q%$1LhXh2Eaa~uUfXHq+;?cv*u z6T6Bt=^oM1VRM`$reseq9E&d;dH+J$^j#ga8r+*4vLy=F}L0uA4zCTj_4@uAwqM#v?uc7+ahmdi>Ja2n_kdK%+kn zHIT)Dz4D}sH)K7kT;4uhA+5urj^Dn917>gQxsZq4bjT%&waDYMN-J>U76r8l{ ztL7E`nhe_@N~oDqx5-7OdP)N$F4iGQ9Ki9nPBOuHybq&SN`fza^c?Y*`=B`>0dDec;2_e#{0+G-jobMin zt@$@IhX){Xf-51{d41mSxV49NH=k0Ic7=CzGEvA#iAIx7rV%jaViE?&@|9(+$BhM@ z5J1fh?Rbz7>p?v!226Mo(ys_K+u_~kiA+bVOQ50b^_Ypk0dz;e+;2jY91;?FK6TU? zj(3KR&A#`AUu=Hk@ZRR|p^|D+bzj{-?tbp<12jGKIN{KR9E-z)eV@3x3k>XVmQ0t7FIqCs}H{h zrHvZ^k+6D?#i{dPOv(ZfIR~_aj>6)-k3yJq>TUI+W9f$dX5Y@k9^ho=h2#+Dj+hpc zb7*(-DfM7-aI~8OiV$PrsV8I<55bcZA9F&Cobb({9A-;T(Dbu$PwFaT^V}~^!SIps zd_<9laMWoSBIaTedF{EVE0AhACr#?H?cu~kpuO%LP_U;kmo1E2hTXIn6+z=Jpa zvvkDN#OvnWTKFSZg0zT3h6wM@H2Ogc!SHy8Zw zELBy7NI)%|M1(>w57Tzp*sjfVQEQk1A}7TSo=9g2ui^udVE;XOH)bI@nV3OTbrq}r z81F>*cxWSbG+-ThR$FcdixHt&auC$TB%ScPCj^kbjfWANG#@^Dsc^^D z1jih?L-}~bb16}gW8JW2c7?zLgd^G(2uOEstZipiA~xnEe)gY}17-F2M zFgx!D;FHqeaMMQEQ1KRoNjoerYlJg$Zi58RAUL<^$FQ$yK0xg#-pEr zOoF;yM|;Pj%=nZxFtUY2?FOCw`fTOU#^k8GPxEN_un95oC3_nBy(^F&5V4*B)%(*@ zLL-qAn^h+S5T@$3REntzEFLgIg!HQ>Gq6Q5u_F99nY6lcRq7&%d_6y7IGaUF;yw-@oqQ*%#$qdH#<_-qsmzzZ-!14RsVv z=(6RR)ZdYbXM0rHP+Kw1G&nQ&`w;8OfvUEhu)h2?2r8}Wr`m%Ru=?<`Fe&R=$n=kd zQE77lQw}OxcEb9K4}pMH`V2N#T>C{g=xv+E8|J+(^KF~B*n*38Thy?pnKvtl34=y+ zEcMWD00n{Br3BF^V5<#MiK6r>v5guh1c4_ZhA}w0{V3 zE~w|%z<5KKuoy{%>l_LI>-sYPoP!h z0ogj2s;(->dHQpaBS!ersa8a<9R*Q~s-Twh0*IJESg}$A6pZaKkxe48lk>n)%p);! z$@D8?Mod2c%zNK>Z1<--HeBl4qw~t|F|0OMx&1W!5`72>WW6!lB0XAS*Hi zDJy-B0Oojz_Y8v8Kpg}lT2i|ww@LG)a8wH#bD0^GSL0ViqeYPIF4#gzOm`2KBmqL9 z{&A+mT^&ve4#AXjmiBDlvHZXZZK@LjXy2v-WbE}x)u4k=RR>s8D_*A~xvv-K^8xA# z(jeuXEBh+v znLQhD-Liiyr(OF~Dsrd!@2qaww>_+ME($Awbc!f4IL+1+S|vRy-v$Ht9jS0m{#`0~ zD%&@~n$o{OTcBRQ2C5p>XXf3ddSK1nWw52{eW-5V2Z2Zv$W&25I4Eucj7XXXNv^@L ztn`mi*1X27oG1|N%E7pe?9fHFy0`j7q>3GiA*;$Kz_>KqS{E@NMtfs zo%KS(oCpHvg%s~_Nb??qN=A)BvMMn%yd&Z4{GS4kJ!&Zst9CWzH?`S8`VWh1Ws5{1 zZ1ImwBh7jyM%>AC2-MDNO4j(P$&Qcyez;$04N(+RS@_f3sgY_MJV-9S0c3jWDvb4; zjQ7OVbudm5(wYz$d#!gzI@3Gk27Ih+P|uMyJR4&^oJ7qBL!#Q67>|Yl)G1&H1}PAO z_Rx!;lUd~2ZM=9XYp z7C{kxD-WGQfv~>S0lrE4g<_c-oF7PC=p`+E+mud?8C>o!YE<-mq7PWCc zr5hS)f{ZT0;~Rg0#ZyQKL|T6)M5Hr(QtC4f-dpArW0O2m zESDKK;v8!mcRix*sv0|RxgGwxfWRa3ThC%sKMF68aG_` zqzKjeFRBFxs@m0eGSnkknn`XYcB7GEIVi^l%8B#Uh6+@(=omW$A2?4oA5q`u0D|Gp zWX^?<@r)7DT0K@C>1b|gIdQ__p#0eUk@|%y!k5?>i}gf}w5< zIL>XxJ&;-7U@>>}?x7M7;N$_?L&#*fTi`Lud~_h2cBm%C74=!Ns*y0))?GarQ#33y zxoC50Oi{=w#%3YJxwGO%E88nShW#~PfD9qE_RDgwF_brNf%4`ZfH}=8WDl4nbFHL^ zR$^mzf;F0P8UUcSG9>-aoNhOofdezWudM`ZNHp_nwiYmk$8S74`|*u)fWa1}9HNSF zviY!&{DK55({LKw+k;YTRw#=B1>}=%&v{Y+(Jr9^1L{DC#u@xNb;a^1&J{<~0H6;| z71X`CG(=d%Bl?9RJ^M>dugfC=bcCqdme&9ap;X`N)Bu*v0Hv0H8Jn zjVtns3PkcLE1J-aAXSPgG~n?h9Rb*X!tb9HK=u2Z8;ces)^P~M84pW0V7+;h;)~!_ zIC*`N>JwmfwNh%+8#epL44t#&9?yE!PzXezHaW zfUyIz<0k^5%wBRr@u2TOXRsCyH+=;J!a6~XXuJF%1Vt(Hp(tgpx|2_Ls1deTEdfFU zs&|61?OOB;FbMv}`K>&g{kkGh+ z;y7N;v=+Cvm?MhPxq11)lQJDo3ZRbqa5n;40tkkJZNgY6QQ^a*IRQRUBE3RfQG@E* zmFh$&&4||DvwhP55n~IvMGD;!YNj^K$h%HAwrWz0+gCfX$s80%@s-ta$2Vox|dY3#ZL5GcEP7@%gT9dp+fx(iD;M<6 zu9nfwVzF{mAXMFdp7Eh|uOcdmYJ!MZaWA{d)6r+qKCGx8Gh0Go+ULE;BniM9!xthX zh{j2%E*2w-Qoem{%}MvrIC(yc07Pox8=S2u2xH!8Frc1C;74Z+b!YD135QN}2ybE% zp;YJ~YBwXz^N(h4BC`V`1+C|@z)|o@2tonSEezJDC;A&;Os>Ho)AxioIA)r0s;z!T z+;0oE!is~>!-8S=fL}^fL7VCu1;f)9z^=+AtXRqPlB(M`LWFb!MG7d90LZk%Oo0#* zfs*>qK@#KD9+8cK-JrGo(n2mg&?G0^N}+V13rqm=(g%&~XqBqGjqvpdq;x z2<_IWy=HLLmUN9$fl&fUmr52-8X+1+iqvOPS?b_e(VHO52gKR*9#uQ;2ilbFO6cSP z$v!1ScWCfEU1Y@jJs+u%+q+ zZLv_Ky7atg@T&<_b!>#%uFYzQfNkoFc>)6BROX^lUSm~)*!WC#Oi>!;)v2L_h|5O6TsyH5aWKH6QU$jV_XpixL~F@SW9 zpHbwWhA{k{lH=P!pMScQEd2hYO16(PBAjX?7*ST!d^BF$Y$L`KatR9QR=D>SgtF=F&G3j!{xVSFc96lj`;F%x=)?6Ft6T7ib z0IEFD5)dP385PJ#s-?H}$T5bH7@V0mEZ$YRtLeDWPdOyh<~F4!$;YcEptUUkvVjNL zkff&#SW?r8aHunwhN$oop>z?0j`8piSca)VhKf#oX!}*z4Q+K(JixHKW*HPEO@~-1 zMV*yo|8Pi;83z?Dn^ZpYwA`B^#+j`XzrZ$=_Ki}aN&5EOm7(VC@0nCyn<7F+qby2b z8}~Zwsry*p$<^***leUI=g%q_V1dNedRh_aQu;(mOiFfuJJEHP76H+p8f7DE9MiqhsoEh7OrB*xpmg943RrO**>0fP>u8paSUW=#g4@w-lgGwy+c z5w!;M9`#$Z4Ru>$pj3vs?p**_QR%HZNYDRpYNeyig||5_ zrhR0q(!jQ@5)RaFfHCO{Ait)o(6pfE`@-SK)_p6x`@Gvyp8*sO${;tumo$Oi!NR%5MkK)YR(A+v3a2Cw*}tRV zxKgOHyt_$OB8M=~rx~lp@+7pT<@U&`r3kb?VvJ&a$()!(no7a zqNh6a;}O*}GvYz^pd&Typ@^bDX~Pznnt2h3oT#ds6yGoqI0pokZqqXa<9h8VqV^3{ z422b~os5`@^@bH~j_UhKt?LAX5okM|y*@uG1FSDTmi<>KD&M%ezP>MWIpU0@7>7qX z&&tYa*RC+1unR9ga-i=!_VkO_yy?@5b!QJLr~r%y83lA~U=F+TC&r)edR^Sn-F579 z@7Yo>Q#pSR5$SZ*@FkW!r{?OlCk}OcM&}JPh{>+j>-TOt@|Rc?e{bfPthXt`E=-6P z83rwD4*Di?miGmgffAV0{C6Y5bS6)sr@}&A&n4r>|%Kn zMkYBd%CL(PHnb-e?B=?SEM`WZsdW}peW^|SWst+l8nsJ$A4bYxej=8fd1m5QXZIm5zCyWkM^N3Iz*G3~icE6b0WT$Lk0n+}Ip zBoC8rq7-w~a6_Xid63CKw5v9v^M|mA2T#2?{<@bQEPM8%{Dh}@kMkZLbDZIcP~Er3 zIxW;F?=j*=NS_F;JV_P%5J55=ZrK5uG5NYjBSDg9Ff?@THT%(mX^$D#*&`2wdSc8* z(XhnK@wzwkI)I_7DA;MNMhYXkS*J$Kmm_;u1wX5+JGtGPnC`)O6BE9N5XW10qN}fv zvZ-QQQ@=I~>=%IA8@p-cjzG1Kl1F1)Mwiyp#o-R+hhYyzlSc+OjTj zXnHdONVHdxYLeTMI>t}aH_!pW0gqD(Jh$WzcD=Fs#l4Ts{!#G=8OV7|^dN2jN_As3 zCW$aHI73W@bRfjc5kXVW=v@FnwG{H zmt<@NiJlAC)QBNQX-RZ1A2M=in@~~KCy@(f2#j#%uBIWOBwCiM`UvJ zz}l|QPIio*qu&79xvsi%+Etlv#AG{))kiqe^wCX znFJwvQk8*+82t(jPLZzO3{T8>vpdoOEulv6JL1&qaqxbSI0uAeYBpExP2$o8ik+Yx z?W*-~>3jU$#_6#)(xg#`>Jc}xozF(q`KpsS7OAO>`qXn zGc%syAKR86>32<8zX8OO(BFJ?47rw`7fO&uhhN zqQPTTP*^66wKI<$Y{%wCqv-P)@34sv(a%45qoV3$^Ol(9!*j0Bz9qs7gX}aEHNm4O z-G|mTzZwii`WA(>jZ@mSdEfe$nzL_EUPnIkbFD7YkgXW;B0u@8tJ5z2`y*X{?L0;@ zZrR62506_AzY1~a5++uV9`vF5Qdg%-e~2V1)nP>5u$n%iF7&+_mnM9$w@Rtsm%R!e?3>=zdB5KrGKQ+~wLkXIb)1y;9Ko8+!+BSyz zG1Rin!vcGN(*}Sj#cex0vY*Pfi5)d;H$(A|&Yf$O-#0ZJzkiQ0iocUOG=zoQ1uU{G zoc3gy^dT~pgXoJrYZ_0fW$-BhsG~JRw|!du%J|FDZ@?T%1=Idu{c3nFsX+Qk>R|tp zlC90X^m6@?4yEF-{O3VM;&}wXfpjJy6E%h*N@$eoH&vM7ykfO0w!=UAyo5)#uWnsX zvc96ea6= z(6vXB?Vgc&rM;|aD{Nre%{FV^&}cm~9Jb@x4Rg!&P-wUteX#VMnv+JK{pn62cTCK~ z952OiMw17GRpJ;?NU&*N*B>iOTTW>Qms0`|8!O(eEGwKA`@5J-=M${4FhY7oB?S;A zq3FEt=iKtAJ3Ai>26}G+zT}U4*5Cfr_%)n}&erAtYuig2#8^@YVI?(jX zeme51-`x1sUp{+&>&BY~FT61j6Z~t0(m0Vd?=)G9dS?-8lK^ICGpc%Mx>}(jrQ=8- z+#*v-M)q>s!TKE`WU3%@*Jbl(Z?FAWUVPN=Jye%r^xMyl%>_nT%%)?-`}NP39X#v(ibrt1s^|oa# z*~`?9T)Fg(rpIb3PHg*ld=i>|RqoB0=f_5o(hvqhBUEmqudIIk(22_~PBKmlK+Hi| z`%dErR~C70L>!$7MpH|*F%yRr61wWzpAT92)Q`5j-E%wnvaKzVP5-QYw0M5f96%)f zSdTRdG+>zxM%Z7oUpOQ?+isb0Mb5`>KVHAC=3rZJ%~KnGJmZdug9wi%t1%$Unl*XT zfUQ=%ln4;-Oag_3nwkQKw*-RKgTrzsMcC#t_mmFDkTT|V>l|)-Aj00blO78!=KR`> zaC4#P3RX4zBY~;|$&ZOQBV4(5)I z`He4)e~OiQVl#k9qaJS*h`<7o=7_2lqMp1N?k6U0$~|x6io?w%>uc-thBn=haZb|5 z5gI8_iDkCBE36foX?r441d`;1Bu6r74IcffBT&5?NarL%Lq-E)TO!uT9Yx#A=IG&m zW1CH+-2$?Hn4d*9o7(Q7+1Q6vqpVUY%Pm#%Z(n)5?A_2wdc03qm=hCM;QS56T&&SP z2IAHl6 zN_N-X)m+yV`r_5E_n++mDyX z*mgqnjGmR{nsWJ|XCk6<29HeRKo4Ok5b204|LTn+e?Nw%?DrTD039u9RW>fJdvMBk zVrBs%Ll81-8ZC{9IdS~Ni^V55Z%DZQ(B5dy?ygnUf%St^?w)#aTCS`FM`L4`Ios~b zpnFng6|4STBh^8*CQaCr==}Zhe%k4rO$3$Ia`^0MbebGHxy7({$G1n z0$$U#w%1yFpCKo+3^GT=5HUuBO2yQcnCez>(NcBM(m`pf{#$LeRn^<_>rYWb%|&Zy z#aPr(DhO$aNM#U-$SlK2&dE7@@3sDC?dj}8y7=#=RB8fRI z@I?umufCO;+rdvymQIzu!ktQ}FF!Ik0o*Z&?{U`j= zqQdf8vA^S+vzZ;D!^fIJxSc%4L5wn?tEra|pv^qWD75B1N=2#w!`fU6XAb74EBR`0vT?`;Ld)4Ca=O-Lp_VsSz{rO#*w(f2USEwAd zTNVFi-vc#iMdfH9HHZ|BwD6>kyz>m>P)u{nfN7_ueEE>7io)Y?r`I*#a#;^io)D#ZsVNHkH*bz z9mx}}uh^;(Pi7L^SI4rRZF{+{9m6#lx_18M->~KoT2)yM=DqRrzSrjT8|>E(uHvu| z35cMX;)SYusIrk&1R1nT8C}c-oh|N}8O=V6|J3QtjMQA-Tx{j{&p!2e+=nq9Mh~Yq zMKK{MdT7t<=nqSFU#_XC0grX@V*;BpeF@i4H<7brYBkomr&V2qv)#ODpFK@QhqyIc{dW=tSFT<(|p0uPY^V%Y)Krj(_ds@n- zqRBa_<>tF`v1Q!V0HQc`G{4esh4btECWP!Egm0!jrHZpqI)h(SU$nu+z2yrlKFvH{ zqd863Z5Vtteedb+&oqCg_qg!&K*v0e01t^gbVfAqbmj6AO|=@!%LfAukhsAKac_pE zEuWUZ*jB}3b8$7dZ}#!`2L7|VLFeT>2|`ShoKSPQdQ1AnJn@eRYr{6e4Bua|Pf%i| zmKp|de5yTMDI08M(|w~&l)9Ode_YOFMB}=sv|R{KI9@0`mr(V_!uPVa*lO@CGe)N# zu8pGlaLb6v5UaOBSYZW#m)}r=D3-~UWgIS>cywF#pSFeicev{TL=nF(Bcn|xzi~nB z*{uNbK2k?pA!R`bSvIs~jML_+pSOEy(aekc&6)`kK@h;9?P*!1#g)$vpWb;P)`MpO z5m+qG)Dv}uQ2OaWRaaL&iee{7r`P$}KOOO5yZoJBWo;|HT23kpEVjf|Y14Sryf zDw={IDLAw(>1XkUU;*eiJhUG~LStoizt)9?ij>SvHbGEpqp5te&XJtdVx-C6cN`R; zJkm|24R{WuTsqB;U-Vw)DO){#(%63PjmNy*Y!!0S_l1CmMkfGFECj_C2Gj`Rm@;3I zUj6=wbveh$t>f@d;hqE#eI34v*hI#N090B{eUVhZ5jOor=O5WfqFHa)CK{c#Cb+w+nv1jPb35r&BjK&r#Ew zEMuMYapGW)tbFUPhroac$gd`(x}4vXu={|6b^ zrvT;|y14{uWw!P*<`C{iqSLxT$N0AB@4vFT0zY$Xb4EJ7>-e4vw#;+6p9F=4uw^A? z@hZjjYw9ToTqL5Y2-FD$FAG<%+0TQwcBxm4QVu@7OEL$m)G- z%07($KC_^{;2ASDx^cI@WVzl&KTs60fr8wRqug+6qlJl=o;`42Q_-}n3#AQK?6_Ur zvjCzvbK;71^}qDf$4n1`0<8WA0EC4^nImTz*GGYCfE)W<_5<;#zi-r1V6nOGI4CU$Jg=#io8e7t?0-Tw6n zQMRohuTm(;OHn)7ot}*FVc2XRs>T5kD`y#0hpqv%zsWJathrx1_N=`}`MQ=by);Zn zc#cocO#vS){Va3qm8|l5sfEvu3uxMVC|nH@(+xq{dJZZBiU0^uAa*<0zkk)0mww%O zxwwIs>rdgH1`x%sJJZeAiz=qRGAqQUcVQ+YYJ9F9TjhQ^1DZtl@%X{btKIlzAE)gy zSJ-cO`=y=Ewf>QqyRviNuw9*o1dny{V@3nS(jiBN$jgMPN3RauDLpH?VzQlE#09Jz zi3X3^xbUYNobvYnbgW>=tNzyzfQ^W_;b-J01Df5+jr$)*v$oFB94eA|M6&=&nMe}h zq(_6|0UnB+6j-5<_GEaSzfLRt>NFN`s+ zG;>?OnU(c&%JGVNN%nUGe@OsQq^DNYto$f<=4*4CIRibONQadj`%-DSU>VlU)tAip z`xL*N-@L z2~!^3fj4YGji04I#J+bLfz*)MMfNS&2u^pDk_>3ot6y)8pk?fpei=7>^*8IftSp zjk-KaM##igBOKf?FL%;k6+Gat3?Pc%PvzS-e#yQ&`n?E&pqiH<0Xm>LwIDhsO)!K8 zp?B<1&plmx^q4;L)r8$8Mb?|L?l_JE`Pp2}!YMoVwP+pqORH`np+;ZfiI8ZoI6K5W z1wy>3=e-y}7|0irKwhxNx^}@EdXI@7ePlz@6(tk>5?_?xJkn=q?_s7z0Fhp_Q$`)C zwYw~h@{pr)EZ!&UtOGKExORw}o1kiaYD1!U8O)0UqO?>dWu+5l_!K4fBtcK?t~M2OG@w3cQ)~v7Rk6wXKKEGqZ zkyM*i@;+Vi*@ZvK^CO20o7ChLVkn=27~)S9yY?6 zgc{S@9m}g`?EEh0dIO&QuHr8bAd2*h#kN`F%ND=CsAH;sOT$V4nI?d6%D}NyO-AAd z+sQM)G%5Z*8S!rrQ-fjTF?(o)rhGt!Y0`#t^ zO<0G(mdJ^V;#e+BAoj-YQMSb+=fpj~dg@Oq)nw!9RP*Mwsb7wn)M{&3guZ)ZSN%W& zxEP4Bn>4^gY53H!k*xt3RzL`g7|dknq*{`)(`~7Ook`svpL;bcy^?Q8rqQBpw2z_B zv+jMOdV0JM7+pt1{a~R+xEM)GvZyMAxyrnB^w0V4o>@_pZ@Xtl^;_Y-1Q3O#nkTbg zOFlGsbkn|ZF9v)=An6H#U6DW)AHyJt2F4&5<`X6?82eAZiM{%H&DpXle@E7(>y`$+ zWCVcBlmc63N&#i?(ObO9*$5+}LUk3ka)W?)L!f)n4gDdy>aG6zVio`(t4);WdXpP* zMhF`qK$03FK|Df;7kFqFIB2i75kBo~@t>~s`Ukt90i%LC^cdj!5u-o@(_8LRrNg$Wyx_y@LSEo=@BlU;1iR z;@NuYjrSb)HGmXs`X;&X;#sekM^A5>;vK|HgOGTNOd|Ps>PX>YFN_67roT@(7c+Zh zn;*{{wlCWMLvC`$rPA9ro4ub14l$y*k-;rvp7NUHcX_-Qcpw6ZBKdS_W$~L;bH-1P+7}qff9T}O_M_$@4uu6tiXTC# zz=v4qfn4C&$NR%!(Y?azMOGD9ip50*Nj)FCEQX{K=X4j6ce6JL}#nJTL)7aV^hAKAw`0 z*rjLCh!L;(zt|`Uyu&gul%86pcblUT07V6ShK@u#vo6d~eRF2;q>qDhuw6Koe7t7; zl~l`_{X4IfnJe#qVtOp5u`#x*^Q<+P%fC`}PqUMYhXHsr zGO%4^-8?(Qx<AQLdFg+nFbcP5SM4vHPkQk~NNAc2d>~#PEc7bCL9N%aDbjykh z2?crP`@Gw^IUdLW0y4R5+nOu6`?j;w$GzKZZRg&uGY|uZut;yj0Octx64!vVAurUn8 z1{z3!C^Pj$Ra+P9NPtzQLLg#wjUhyhF~Knb&5W-Od)0}azRC#^GDb~pf3~cU%gW9) zSMeB_83t5ZEjkMVI=fNl&bvAzd$o;Wt>zLVYqUBuEOEh*xae4;%VRN~{s0P{G2kNs z1l|x5J%C6D5p-xz0Z9^Bl}FP~dO+DmfpUQe!jLd&_6q%q&EMp%j^CPH_8@GM>%#*d zKnhZRFSMsjF5J_qeWOEzMu)_->FP8du^^6R7!Qa!D0!C3Gz+K@xE!XFK;qxfs+_lijE=MwjQ z$|=i%jVm&9EYoFpe;*>6nU;L0HKOnx29ZPC^nE5Ra2W5Z0SFoq44p8K7z{BI57tni?G!PVL}f-K+8W7z zZYv9^V<{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372eebdb28e45604e46eeda8dd24651419bc0..8b630418434870a70c65c8625c3aa825b0fd641f 100644 GIT binary patch literal 40417 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

*z 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 diff --git a/lib/main.dart b/lib/main.dart index a69cca1..8a4ca9d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,11 +5,7 @@ import 'screens/landing_page.dart'; import 'screens/login_page.dart'; import 'theme/app_color.dart'; -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await Firebase.initializeApp( - options: DefaultFirebaseOptions.currentPlatform, - ); +void main() { runApp(const MyApp()); } @@ -24,9 +20,129 @@ class MyApp extends StatelessWidget { theme: ThemeData( primaryColor: AppColor.primary, scaffoldBackgroundColor: AppColor.background, + useMaterial3: true, ), - home: const LandingPage(), + home: const FirebaseInitializer(), routes: {'/login': (context) => const LoginPage()}, ); } +} + +class FirebaseInitializer extends StatefulWidget { + const FirebaseInitializer({super.key}); + + @override + State createState() => _FirebaseInitializerState(); +} + +class _FirebaseInitializerState extends State { + bool _initialized = false; + bool _error = false; + String _errorMessage = ''; + + @override + void initState() { + super.initState(); + _initializeFirebase(); + } + + Future _initializeFirebase() async { + try { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + setState(() { + _initialized = true; + }); + print('โœ… Firebase initialized successfully'); + } on FirebaseException catch (e) { + // Jika app sudah ada, anggap sudah initialized + if (e.code == 'duplicate-app') { + setState(() { + _initialized = true; + }); + print('โœ… Firebase already initialized'); + } else { + setState(() { + _error = true; + _errorMessage = e.message ?? e.toString(); + }); + print('โŒ Firebase initialization error: $e'); + } + } catch (e) { + setState(() { + _error = true; + _errorMessage = e.toString(); + }); + print('โŒ Firebase initialization error: $e'); + } + } + + @override + Widget build(BuildContext context) { + if (_error) { + return Scaffold( + backgroundColor: Colors.white, + body: Center( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error_outline, size: 80, color: Colors.red), + const SizedBox(height: 24), + const Text( + 'Error Initializing Firebase', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 12), + Text( + _errorMessage, + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey[600]), + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + setState(() { + _error = false; + _initialized = false; + }); + _initializeFirebase(); + }, + child: const Text('Retry'), + ), + ], + ), + ), + ), + ); + } + + if (!_initialized) { + return Scaffold( + backgroundColor: Colors.white, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(AppColor.primary), + ), + const SizedBox(height: 24), + Text( + 'Loading...', + style: TextStyle( + fontSize: 16, + color: AppColor.textDark.withOpacity(0.7), + ), + ), + ], + ), + ), + ); + } + + return const LandingPage(); + } } diff --git a/lib/screens/dashboard_page.dart b/lib/screens/dashboard_page.dart index 5a49eb0..cd7c8c3 100644 --- a/lib/screens/dashboard_page.dart +++ b/lib/screens/dashboard_page.dart @@ -15,6 +15,8 @@ class DashboardPage extends StatefulWidget { class _DashboardPageState extends State { int _selectedIndex = 0; final _dbService = FirebaseDatabaseService(); + final _authService = AuthService(); + bool _isInitialized = false; final List> _pages = [ {'title': 'Dashboard', 'icon': Icons.dashboard}, @@ -22,6 +24,37 @@ class _DashboardPageState extends State { {'title': 'Histori', 'icon': Icons.history}, ]; + @override + void initState() { + super.initState(); + + // Verify user is logged in on init + if (_authService.currentUser == null) { + // If no user, redirect immediately + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + Navigator.of( + context, + ).pushNamedAndRemoveUntil('/login', (route) => false); + } + }); + return; + } + + _isInitialized = true; + + // Monitor auth state untuk mencegah logout tidak terduga + // Hanya trigger jika sudah initialized dan user jadi null + _authService.authStateChanges.listen((user) { + if (_isInitialized && user == null && mounted) { + // User logged out, redirect to login + Navigator.of( + context, + ).pushNamedAndRemoveUntil('/login', (route) => false); + } + }); + } + Future _handleLogout(BuildContext context) async { final confirmed = await showDialog( context: context, diff --git a/lib/screens/kontrol_page.dart b/lib/screens/kontrol_page.dart index 974db81..86f8a68 100644 --- a/lib/screens/kontrol_page.dart +++ b/lib/screens/kontrol_page.dart @@ -121,12 +121,13 @@ class _ManualControlPageState extends State { // POT switches (5 POTs now) List _potStatus = [false, false, false, false, false]; bool _isLoading = true; + bool _hasLocalChanges = false; // Track jika ada perubahan lokal @override void initState() { super.initState(); _loadFirebaseState(); - _listenToFirebaseChanges(); + // Tidak listen real-time changes agar tidak menimpa perubahan lokal user } /// Load initial state from Firebase @@ -145,31 +146,22 @@ class _ManualControlPageState extends State { aktuatorData['mosvet_7'] ?? false, ]; _isLoading = false; + _hasLocalChanges = false; }); } } catch (e) { print('Error loading Firebase state: $e'); - setState(() => _isLoading = false); - } - } - - /// Listen to real-time Firebase changes - void _listenToFirebaseChanges() { - _dbService.getAktuatorStream().listen((aktuatorData) { if (mounted) { - setState(() { - _pompaAir = aktuatorData['mosvet_1'] ?? false; - _pompaNutrisi = aktuatorData['mosvet_2'] ?? false; - _potStatus = [ - aktuatorData['mosvet_3'] ?? false, - aktuatorData['mosvet_4'] ?? false, - aktuatorData['mosvet_5'] ?? false, - aktuatorData['mosvet_6'] ?? false, - aktuatorData['mosvet_7'] ?? false, - ]; - }); + setState(() => _isLoading = false); + // Tampilkan snackbar tapi jangan crash + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Gagal memuat data: $e'), + backgroundColor: Colors.orange, + ), + ); } - }); + } } Future _jalankan() async { @@ -192,6 +184,11 @@ class _ManualControlPageState extends State { pots: _potStatus, ); + // Reset flag perubahan lokal + setState(() { + _hasLocalChanges = false; + }); + // Show which devices are running List activeDevices = []; if (_pompaAir) activeDevices.add('Pompa Air'); @@ -210,7 +207,7 @@ class _ManualControlPageState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('โœ“ Berhasil: ${activeDevices.join(', ')}'), + content: Text('โœ“ Berhasil dijalankan: ${activeDevices.join(', ')}'), backgroundColor: AppColor.primary, duration: const Duration(seconds: 3), ), @@ -236,37 +233,21 @@ class _ManualControlPageState extends State { ControlSwitchCard( title: 'Pompa Air', isActive: _pompaAir, - onPressed: () async { - setState(() => _pompaAir = !_pompaAir); - try { - await _dbService.setPompaAir(_pompaAir); - } catch (e) { - setState(() => _pompaAir = !_pompaAir); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error: $e'), - backgroundColor: Colors.red, - ), - ); - } + onPressed: () { + setState(() { + _pompaAir = !_pompaAir; + _hasLocalChanges = true; + }); }, ), ControlSwitchCard( title: 'Pompa Nutrisi', isActive: _pompaNutrisi, - onPressed: () async { - setState(() => _pompaNutrisi = !_pompaNutrisi); - try { - await _dbService.setPompaPupuk(_pompaNutrisi); - } catch (e) { - setState(() => _pompaNutrisi = !_pompaNutrisi); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error: $e'), - backgroundColor: Colors.red, - ), - ); - } + onPressed: () { + setState(() { + _pompaNutrisi = !_pompaNutrisi; + _hasLocalChanges = true; + }); }, ), @@ -291,39 +272,77 @@ class _ManualControlPageState extends State { return ControlSwitchCard( title: 'POT ${index + 1}', isActive: _potStatus[index], - onPressed: () async { - setState(() => _potStatus[index] = !_potStatus[index]); - try { - await _dbService.setPot(index + 1, _potStatus[index]); - } catch (e) { - setState(() => _potStatus[index] = !_potStatus[index]); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error: $e'), - backgroundColor: Colors.red, - ), - ); - } + onPressed: () { + setState(() { + _potStatus[index] = !_potStatus[index]; + _hasLocalChanges = true; + }); }, ); }), const SizedBox(height: 24), + + // Info text jika ada perubahan lokal + if (_hasLocalChanges) + Container( + padding: const EdgeInsets.all(12), + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.orange.shade300), + ), + child: Row( + children: [ + Icon( + Icons.info_outline, + color: Colors.orange.shade700, + size: 20, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Ada perubahan yang belum disimpan', + style: TextStyle( + fontSize: 13, + color: Colors.orange.shade700, + ), + ), + ), + ], + ), + ), + SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _jalankan, style: ElevatedButton.styleFrom( - backgroundColor: AppColor.primary, + backgroundColor: + _hasLocalChanges ? AppColor.primary : Colors.grey, foregroundColor: Colors.white, padding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), - child: const Text( - 'Jalankan', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_hasLocalChanges) + const Padding( + padding: EdgeInsets.only(right: 8), + child: Icon(Icons.send, size: 18), + ), + Text( + _hasLocalChanges ? 'Jalankan Sekarang' : 'Jalankan', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ], ), ), ), diff --git a/lib/screens/pot_selection_page.dart b/lib/screens/pot_selection_page.dart index 9ec9cf5..41eff18 100644 --- a/lib/screens/pot_selection_page.dart +++ b/lib/screens/pot_selection_page.dart @@ -25,15 +25,26 @@ class _PotSelectionPageState extends State { } Future _loadModeStatus() async { - final isActive = - widget.mode == 'Waktu' - ? await KontrolStorage.loadWaktuModeActive() - : await KontrolStorage.loadSensorModeActive(); + try { + final isActive = + widget.mode == 'Waktu' + ? await KontrolStorage.loadWaktuModeActive() + : await KontrolStorage.loadSensorModeActive(); - setState(() { - _isModeActive = isActive; - _isLoading = false; - }); + if (mounted) { + setState(() { + _isModeActive = isActive; + _isLoading = false; + }); + } + } catch (e) { + debugPrint('Error loading mode status: $e'); + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } } Future _toggleMode(bool value) async { diff --git a/lib/screens/sensor_config_page.dart b/lib/screens/sensor_config_page.dart index 1ba1230..2d9ede0 100644 --- a/lib/screens/sensor_config_page.dart +++ b/lib/screens/sensor_config_page.dart @@ -33,7 +33,8 @@ class _SensorConfigPageState extends State { 'batasMinimal': '30', 'batasMaksimal': '80', 'durasi': '10', - 'durasiUnit': 'menit', + 'durasiUnit': 'detik', + 'mode': 'smart', // 'smart' or 'fixed' }; @override @@ -54,13 +55,18 @@ class _SensorConfigPageState extends State { final parts = durasiStr.split(' '); loadedData['durasi'] = parts[0]; // Extract number loadedData['durasiUnit'] = - parts.length > 1 ? parts[1] : 'menit'; // Extract unit + parts.length > 1 ? parts[1] : 'detik'; // Extract unit } else { // If no unit, ensure durasiUnit exists - loadedData['durasiUnit'] = loadedData['durasiUnit'] ?? 'menit'; + loadedData['durasiUnit'] = loadedData['durasiUnit'] ?? 'detik'; } } + // Ensure mode exists (default to smart) + if (!loadedData.containsKey('mode') || loadedData['mode'] == null) { + loadedData['mode'] = 'smart'; + } + setState(() { _sensorConfig = loadedData; _isLoading = false; @@ -320,6 +326,66 @@ class _SensorConfigPageState extends State { ), const SizedBox(height: 20), + // Mode Selection + Text( + 'Mode Penyiraman', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColor.textDark, + ), + ), + const SizedBox(height: 8), + + Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + RadioListTile( + title: const Text( + 'Smart Mode (Adaptif)', + style: TextStyle(fontWeight: FontWeight.w600), + ), + subtitle: const Text( + 'Siram hingga batas atas tercapai atau timeout', + style: TextStyle(fontSize: 12), + ), + value: 'smart', + groupValue: _sensorConfig['mode'], + onChanged: (value) { + setState(() { + _sensorConfig['mode'] = value!; + }); + }, + activeColor: AppColor.primary, + ), + Divider(height: 1, color: Colors.grey.shade300), + RadioListTile( + title: const Text( + 'Fixed Duration (Durasi Tetap)', + style: TextStyle(fontWeight: FontWeight.w600), + ), + subtitle: const Text( + 'Siram selama durasi yang ditentukan', + style: TextStyle(fontSize: 12), + ), + value: 'fixed', + groupValue: _sensorConfig['mode'], + onChanged: (value) { + setState(() { + _sensorConfig['mode'] = value!; + }); + }, + activeColor: AppColor.primary, + ), + ], + ), + ), + const SizedBox(height: 20), + // Batas Minimal _buildEditableField( label: 'Batas Minimal (%)', @@ -344,14 +410,14 @@ class _SensorConfigPageState extends State { _buildEditableField( label: 'Durasi Penyiraman', value: - '${_sensorConfig['durasi']} ${_sensorConfig['durasiUnit'] ?? 'menit'}', + '${_sensorConfig['durasi']} ${_sensorConfig['durasiUnit'] ?? 'detik'}', onTap: () => _editDurationField(), icon: Icons.timer, ), const SizedBox(height: 20), - // Info box + // Info box - Dynamic based on mode Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( @@ -364,7 +430,9 @@ class _SensorConfigPageState extends State { const SizedBox(width: 12), Expanded( child: Text( - 'Pompa akan aktif otomatis jika kelembaban di bawah batas minimal', + _sensorConfig['mode'] == 'smart' + ? 'Smart Mode: Pompa akan mati otomatis saat mencapai batas atas atau durasi habis sebagai safety timeout' + : 'Fixed Duration: Pompa akan menyiram selama durasi yang ditentukan, tidak peduli nilai sensor', style: TextStyle(fontSize: 12, color: AppColor.textDark), ), ), @@ -566,13 +634,23 @@ class _SensorConfigPageState extends State { // Save to local storage await KontrolStorage.saveSensorConfig(widget.potName, _sensorConfig); + // Convert durasi to seconds for Firebase + int durasiSeconds = int.tryParse(_sensorConfig['durasi']) ?? 10; + if (_sensorConfig['durasiUnit'] == 'menit') { + durasiSeconds *= 60; + } + // Update Firebase dengan konfigurasi sensor await _dbService.setThreshold( batasAtas: int.tryParse(_sensorConfig['batasMaksimal']) ?? 80, batasBawah: int.tryParse(_sensorConfig['batasMinimal']) ?? 30, ); - await _dbService.updateKontrolConfig({'otomatis': _isSensorModeActive}); + await _dbService.updateKontrolConfig({ + 'otomatis': _isSensorModeActive, + 'durasi_sensor': durasiSeconds, + 'mode_sensor': _sensorConfig['mode'] ?? 'smart', + }); setState(() { _isSaved = true; diff --git a/lib/screens/waktu_config_page.dart b/lib/screens/waktu_config_page.dart index 612651e..b56806d 100644 --- a/lib/screens/waktu_config_page.dart +++ b/lib/screens/waktu_config_page.dart @@ -54,34 +54,45 @@ class _WaktuConfigPageState extends State { } Future _loadSavedConfig() async { - final loadedData = await KontrolStorage.loadWaktuConfig(widget.potName); + try { + final loadedData = await KontrolStorage.loadWaktuConfig(widget.potName); - // Convert old format to new format if needed - for (var jadwal in loadedData) { - if (jadwal['durasi'] is String) { - final durasiStr = jadwal['durasi'] as String; - // Check if it contains unit (e.g., "10 menit") - if (durasiStr.contains(' ')) { - final parts = durasiStr.split(' '); - jadwal['durasi'] = parts[0]; // Extract number - jadwal['durasiUnit'] = - parts.length > 1 ? parts[1] : 'menit'; // Extract unit - } else { - // If no unit, ensure durasiUnit exists - jadwal['durasiUnit'] = jadwal['durasiUnit'] ?? 'menit'; + // Convert old format to new format if needed + for (var jadwal in loadedData) { + if (jadwal['durasi'] is String) { + final durasiStr = jadwal['durasi'] as String; + // Check if it contains unit (e.g., "10 menit") + if (durasiStr.contains(' ')) { + final parts = durasiStr.split(' '); + jadwal['durasi'] = parts[0]; // Extract number + jadwal['durasiUnit'] = + parts.length > 1 ? parts[1] : 'menit'; // Extract unit + } else { + // If no unit, ensure durasiUnit exists + jadwal['durasiUnit'] = jadwal['durasiUnit'] ?? 'menit'; + } } } - } - setState(() { - _jadwalPenyiraman = loadedData; - _isLoading = false; - // Check if data was previously saved - _isSaved = - loadedData[0]['jamMulai'] != '08:00' || - loadedData[0]['pompaAir'] || - loadedData[0]['pompaPupuk']; - }); + if (mounted) { + setState(() { + _jadwalPenyiraman = loadedData; + _isLoading = false; + // Check if data was previously saved + _isSaved = + loadedData[0]['jamMulai'] != '08:00' || + loadedData[0]['pompaAir'] || + loadedData[0]['pompaPupuk']; + }); + } + } catch (e) { + debugPrint('Error loading waktu config: $e'); + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } } Future _loadWaktuModeStatus() async { diff --git a/lib/services/kontrol_automation_service.dart b/lib/services/kontrol_automation_service.dart index 739a633..9c0734e 100644 --- a/lib/services/kontrol_automation_service.dart +++ b/lib/services/kontrol_automation_service.dart @@ -172,9 +172,9 @@ class KontrolAutomationService { _isSensorModeActive = true; debugPrint('๐ŸŒก๏ธ Sensor Mode: Started'); - // Monitor perubahan sensor setiap beberapa detik + // Monitor perubahan sensor lebih responsif (5 detik) _sensorCheckTimer = Timer.periodic( - const Duration(seconds: 10), + const Duration(seconds: 5), (timer) => _checkSensorThreshold(), ); @@ -209,21 +209,35 @@ class KontrolAutomationService { final batasAtas = kontrolConfig['batas_atas'] ?? 100; final batasBawah = kontrolConfig['batas_bawah'] ?? 40; + final durasiSensor = kontrolConfig['durasi_sensor'] ?? 60; // dalam detik + final modeSensor = + kontrolConfig['mode_sensor'] ?? 'smart'; // 'smart' or 'fixed' final sensorData = await _dbService.getSensorData(); // Check setiap pot (soil_1 sampai soil_5) + debugPrint( + '๐ŸŒก๏ธ Checking thresholds: batas_bawah=$batasBawah, batas_atas=$batasAtas, mode=$modeSensor, durasi=${durasiSensor}s', + ); + for (int i = 1; i <= 5; i++) { final soilKey = 'soil_$i'; final soilValue = int.tryParse(sensorData[soilKey] ?? '0') ?? 0; + debugPrint('๐ŸŒก๏ธ $soilKey = $soilValue (threshold: $batasBawah)'); + // Jika kelembapan di bawah batas bawah, siram pot tersebut if (soilValue < batasBawah) { + debugPrint( + 'โš ๏ธ $soilKey ($soilValue) < batasBawah ($batasBawah) โ†’ Triggering watering for POT $i', + ); await _waterPotBySensor( potNumber: i, soilValue: soilValue, batasBawah: batasBawah, batasAtas: batasAtas, + durasiSeconds: durasiSensor, + mode: modeSensor, ); } } @@ -244,6 +258,8 @@ class KontrolAutomationService { required int soilValue, required int batasBawah, required int batasAtas, + required int durasiSeconds, + required String mode, }) async { final potKey = 'pot_$potNumber'; @@ -253,7 +269,12 @@ class KontrolAutomationService { final lastTime = _lastWateringTime[potKey]; if (lastTime != null) { final diff = DateTime.now().difference(lastTime); - if (diff.inMinutes < 5) return; // Minimum 5 menit antar penyiraman + if (diff.inMinutes < 2) { + debugPrint( + 'โณ POT $potNumber: Cooldown active (${2 - diff.inMinutes} min remaining)', + ); + return; // Minimum 2 menit antar penyiraman + } } try { @@ -263,40 +284,98 @@ class KontrolAutomationService { debugPrint( '๐ŸŒก๏ธ Sensor Mode: Watering POT $potNumber (soil: $soilValue < $batasBawah)', ); + debugPrint('๐Ÿ”ง Mode: $mode, Durasi: ${durasiSeconds}s'); // Nyalakan pompa air dan valve pot tersebut + // pot 1 โ†’ mosvet_3, pot 2 โ†’ mosvet_4, ... pot 5 โ†’ mosvet_7 + debugPrint( + '๐Ÿ’ง Starting watering: POT $potNumber (mosvet_${potNumber + 2})', + ); await _dbService.setPompaAir(true); await _dbService.setPot(potNumber, true); - // Siram sampai mencapai batas atas atau max 60 detik int elapsedSeconds = 0; - const maxDuration = 60; - while (elapsedSeconds < maxDuration && _isSensorModeActive) { - await Future.delayed(const Duration(seconds: 5)); - elapsedSeconds += 5; + if (mode == 'smart') { + // SMART MODE: Siram sampai batas atas atau timeout + debugPrint( + '๐Ÿง  Smart Mode: Target soil_$potNumber >= $batasAtas (currently: $soilValue), max ${durasiSeconds}s', + ); - // Check sensor lagi - final currentData = await _dbService.getSensorData(); - final currentSoil = - int.tryParse(currentData['soil_$potNumber'] ?? '0') ?? 0; + while (elapsedSeconds < durasiSeconds && _isSensorModeActive) { + await Future.delayed(const Duration(seconds: 5)); + elapsedSeconds += 5; + + // Check sensor lagi + final currentData = await _dbService.getSensorData(); + final currentSoil = + int.tryParse(currentData['soil_$potNumber'] ?? '0') ?? 0; - if (currentSoil >= batasAtas) { debugPrint( - 'โœ… POT $potNumber reached target: $currentSoil >= $batasAtas', + '๐Ÿ’ง POT $potNumber watering: ${elapsedSeconds}s, soil_$potNumber: $currentSoil', ); - break; + + if (currentSoil >= batasAtas) { + debugPrint( + 'โœ… POT $potNumber reached target: $currentSoil >= $batasAtas (Smart Mode)', + ); + break; + } } + + if (elapsedSeconds >= durasiSeconds) { + debugPrint( + 'โฐ POT $potNumber timeout: ${elapsedSeconds}s (Smart Mode safety)', + ); + } + } else { + // FIXED DURATION MODE: Siram selama durasi tetap + debugPrint( + 'โฑ๏ธ Fixed Duration Mode: Watering for exactly ${durasiSeconds}s', + ); + + while (elapsedSeconds < durasiSeconds && _isSensorModeActive) { + await Future.delayed(const Duration(seconds: 5)); + elapsedSeconds += 5; + + // Check sensor untuk logging saja (tidak break) + final currentData = await _dbService.getSensorData(); + final currentSoil = + int.tryParse(currentData['soil_$potNumber'] ?? '0') ?? 0; + + debugPrint( + '๐Ÿ’ง POT $potNumber watering: ${elapsedSeconds}s/${durasiSeconds}s, soil_$potNumber: $currentSoil', + ); + } + + debugPrint( + 'โœ… POT $potNumber fixed duration completed: ${durasiSeconds}s', + ); } // Matikan pompa dan valve await _dbService.setPompaAir(false); await _dbService.setPot(potNumber, false); - debugPrint('โœ… Sensor Mode: Watering completed for POT $potNumber'); + debugPrint( + 'โœ… Sensor Mode: Watering completed for POT $potNumber (${elapsedSeconds}s, mode: $mode)', + ); + + // Pastikan flag ter-reset dengan benar + await Future.delayed(const Duration(milliseconds: 500)); _isWateringActive[potKey] = false; } catch (e) { debugPrint('โŒ Error watering pot by sensor: $e'); + + // Matikan semua untuk safety + try { + await _dbService.setPompaAir(false); + await _dbService.setPot(potNumber, false); + } catch (cleanupError) { + debugPrint('โŒ Error during cleanup: $cleanupError'); + } + + // Reset flag _isWateringActive[potKey] = false; } } diff --git a/pubspec.lock b/pubspec.lock index d67ba43..400e7e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.59" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" args: dependency: transitive description: @@ -41,6 +49,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -57,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -166,6 +198,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" flutter_lints: dependency: "direct dev" description: @@ -248,6 +288,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" + url: "https://pub.dev" + source: hosted + version: "4.7.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -344,6 +400,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.dev" + source: hosted + version: "6.1.0" platform: dependency: transitive description: @@ -360,6 +424,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" rename: dependency: "direct main" description: @@ -517,6 +589,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: dart: ">=3.7.2 <4.0.0" flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index f917843..9599a80 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,9 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 + + # Package untuk auto-generate app icons + flutter_launcher_icons: ^0.13.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -67,6 +70,15 @@ flutter: assets: - assets/images/ +# Konfigurasi untuk auto-generate app launcher icons +flutter_launcher_icons: + android: true + ios: false + image_path: "assets/images/logo_apsgo.png" + # Uncomment jika ingin adaptive icon (Android 8.0+) + # adaptive_icon_background: "#4CAF50" + # adaptive_icon_foreground: "assets/images/logo_apsgo.png" + # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images