467 lines
12 KiB
Markdown
467 lines
12 KiB
Markdown
# 🎉 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<DashboardPage> {
|
||
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! 🌱**
|