12 KiB
🎉 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 MaksimalATAU 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):
{
'batasMinimal': '30',
'batasMaksimal': '80',
'durasi': '10',
'durasiUnit': 'detik',
'mode': 'smart', // ← NEW: 'smart' or 'fixed'
}
Firebase (kontrol):
{
"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:
-
✅
lib/screens/sensor_config_page.dart- Tambah radio buttons untuk mode selection
- Update
_sensorConfigdengan fieldmode - Convert durasi ke seconds sebelum save ke Firebase
- Dynamic info text berdasarkan mode
-
✅
lib/services/kontrol_automation_service.dart- Update
_checkSensorThreshold()untuk ambilmode_sensordandurasi_sensor - Update
_waterPotBySensor()dengan parametermodedandurasiSeconds - Implementasi dual logic dengan if-else:
- Smart mode: break jika
soil >= batasAtas - Fixed mode: continue sampai durasi habis
- Smart mode: break jika
- Update
-
✅
lib/services/firebase_database_service.dart- Tidak perlu perubahan, sudah support
updateKontrolConfig()
- Tidak perlu perubahan, sudah support
3. Logic Flow
Smart Mode:
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:
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:
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:
- ✅ Tambah flag
_isInitialized - ✅ Check
currentUserdiinitState() - ✅ Listener hanya trigger setelah initialized
- ✅ Always check
mountedsebelum 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:
// 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:
- ✅
dashboard_page.dart- StreamBuilder error state - ✅
kontrol_page.dart- Load Firebase state - ✅
sensor_config_page.dart- Load config - ✅
waktu_config_page.dart- Load config - ✅
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:
- App akan auto-migrate ke format baru
- Default mode:
smart - Default durasi unit:
detik - Konfigurasi lama tetap tersimpan
Jika belum ada konfigurasi:
- Default values:
- Batas Minimal: 30%
- Batas Maksimal: 80%
- Durasi: 10 detik
- Mode: Smart
Firebase Update
Manual Steps (Optional):
// 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
- Hybrid Mode: Kombinasi smart + fixed (min & max duration)
- Per-Pot Mode: Setiap pot bisa punya mode berbeda
- Schedule Mode: Smart mode di pagi, fixed mode di sore
- Learning Mode: AI adjust durasi berdasarkan history
- Notification: Push notif saat watering complete
- History Graph: Chart durasi vs hasil kelembaban
📞 Support
Jika ada masalah:
-
Check Console Log:
🧠 Smart Mode: Target ... ⏱️ Fixed Duration Mode: ... ✅ Watering completed ... ❌ Error: ... -
Verify Firebase:
kontrol/mode_sensorexist?kontrol/durasi_sensorvalid number?kontrol/otomatis= true?
-
Reset Config:
- Delete local storage
- Delete Firebase
/kontrolnode - Restart app
- Setup ulang
🎯 Summary
✅ Completed
-
Dual Mode System
- Smart Mode (adaptif dengan sensor)
- Fixed Duration (durasi tetap)
- UI untuk selection
- Backend logic implementation
-
Bug Fixes
- Fix logout tiba-tiba
- Enhanced error handling
- Prevent crashes
- Safe navigation
-
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! 🌱