#include <ESP8266WiFi.h>
#include <FirebaseESP8266.h>
#include <DHT.h>
#include <Wire.h>
#include <BH1750.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

// Konfigurasi WiFi
#define WIFI_SSID "didinganteng"
#define WIFI_PASSWORD "didin123"

// Konfigurasi Firebase
#define FIREBASE_HOST "https://sensoranggrek-3d9ac-default-rtdb.firebaseio.com/"
#define FIREBASE_AUTH "suOXlb5gENTmy6PbRNWsbpWiFOeUVDQoWq61GhRu"

#define DHTPIN D4      // Pin data DHT11 terhubung ke D4
#define DHTTYPE DHT11  // Jenis sensor DHT

// Definisi pin relay
#define RELAY_FAN D5    // Kipas pada relay channel 1
#define RELAY_LIGHT D6  // Lampu pada relay channel 2
#define RELAY_PUMP D7   // Water pump pada relay channel 3
#define RELAY_CAMERA_TRIGGER D3  // Relay untuk mengaktifkan kamera pada jadwal tertentu

// PERBAIKAN 1: Ubah ke pin analog jika sensor mendukung output analog
#define SOIL_MOISTURE_PIN A0  // Ganti ke A0 untuk sensor analog

// Nilai threshold kelembapan tanah untuk media tanam anggrek
const int SOIL_DRY_THRESHOLD = 48;     // Di bawah nilai ini dianggap kering (perlu penyiraman)
const int SOIL_OPTIMAL_MIN = 50;       // Batas bawah optimal
const int SOIL_OPTIMAL_THRESHOLD = 60; // Di atas nilai ini dianggap optimal
const int SOIL_WET_THRESHOLD = 70;     // Di atas nilai ini dianggap terlalu basah

// Nilai threshold intensitas cahaya
const int LIGHT_LOW_THRESHOLD = 8000;    // Di bawah nilai ini dianggap rendah (perlu lampu)
const int LIGHT_OPTIMAL_MAX = 11000;    // Di atas nilai ini dianggap tinggi

// Konfigurasi Watchdog dan Power Management
#define WATCHDOG_TIMEOUT 60 // 60 detik timeout
#define POWER_STABILIZATION_DELAY 500 // Delay stabilisasi daya
#define MAX_SENSOR_INIT_ATTEMPTS 3
#define MAX_WIFI_CONNECT_ATTEMPTS 30

DHT dht(DHTPIN, DHTTYPE);
BH1750 lightMeter; // Inisialisasi objek BH1750
FirebaseData firebaseData;
FirebaseConfig config;
FirebaseAuth auth;

// Untuk NTP (Network Time Protocol)
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7 * 3600, 60000);

// Variabel untuk sensor dan perangkat
unsigned long pumpStartTime = 0;
unsigned long pumpCycleTime = 0;
bool pumpCycleOn = false;
const unsigned long PUMP_ON_DURATION = 10000;  // 10 detik ON
const unsigned long PUMP_OFF_DURATION = 10000; // 10 detik OFF

// Variabel untuk pengaktifan kamera terjadwal
unsigned long cameraTriggerStartTime = 0;
bool cameraTriggerOn = false;
const unsigned long CAMERA_TRIGGER_DURATION = 300000; // 5 menit (5 * 60 * 1000 ms)

// Variabel untuk melacak status perangkat terakhir
bool lastKipasStatus = false;
bool lastLampuStatus = false;
bool lastPumpStatus = false;
bool lastCamStatus = false;
bool lastCameraTriggerStatus = false;
String lastLampReason = "";

// Variabel untuk melacak status sensor
bool dht11Status = false;
bool bh1750Status = false;
bool soilMoistureStatus = false;

// Variabel untuk throttling pengiriman data
unsigned long lastSensorUpdateTime = 0;
unsigned long lastDeviceControlTime = 0;
unsigned long lastFirebaseUpdateTime = 0;
const unsigned long sensorUpdateInterval = 5000; // Update setiap 5 detik
const unsigned long deviceControlInterval = 2000; // Kontrol perangkat setiap 2 detik

// Variabel untuk heartbeat status koneksi
unsigned long lastHeartbeatTime = 0;
const unsigned long heartbeatInterval = 30000; // Update status koneksi setiap 30 detik

// Deklarasi fungsi
void checkRestartStatus();
void updateSensorStatus();
void updateSensorData(float temp, float humidity, float lux, int soilMoisture);
void updateHeartbeat();
void checkWiFiConnection();
void checkRestartCommand();
void controlDevices();
void controlPump();
void controlCameraTrigger();  // Fungsi untuk mengontrol pengaktifan kamera terjadwal
void updateDeviceStatus();
String getCurrentTime();

/// Fungsi pembacaan sensor yang ditingkatkan dengan penanganan error
int readSoilMoisture() {
    int rawValue;
    int readAttempts = 0;
    const int MAX_READ_ATTEMPTS = 3;
    
    // Coba baca sensor beberapa kali jika nilainya tidak valid
    do {
        rawValue = analogRead(SOIL_MOISTURE_PIN);
        readAttempts++;
        
        if (rawValue < 0 || rawValue > 1023) {
            Serial.println("Error: Pembacaan sensor kelembapan tidak valid!");
            delay(100); // Tunggu sebentar sebelum coba lagi
        }
    } while ((rawValue < 0 || rawValue > 1023) && readAttempts < MAX_READ_ATTEMPTS);
    
    // Jika setelah beberapa percobaan masih error, catat error dan kembalikan nilai default
    if (rawValue < 0 || rawValue > 1023) {
        Serial.println("Error: Gagal membaca sensor kelembapan tanah setelah beberapa percobaan!");
        Firebase.setString(firebaseData, "/logs/soil_moisture/error", "Pembacaan tidak valid setelah " + String(MAX_READ_ATTEMPTS) + " percobaan");
        return -1; // Nilai error
    }
    
    // Debugging: Tampilkan nilai raw di Serial Monitor
    Serial.print("Nilai Raw Soil Moisture: ");
    Serial.println(rawValue);
    
    // Kalibrasi nilai (sesuaikan dengan sensor Anda)
    // Contoh kalibrasi:
    // Nilai 1023 = sangat kering (0% kelembapan)
    // Nilai 300 = sangat basah (100% kelembapan)
    int moisturePercentage = map(rawValue, 1023, 300, 0, 100);
    
    // Pastikan nilai dalam rentang 0-100%
    moisturePercentage = constrain(moisturePercentage, 0, 100);
    
    // Debugging: Tampilkan nilai hasil konversi
    Serial.print("Kelembapan Tanah: ");
    Serial.print(moisturePercentage);
    Serial.println("%");
    
    return moisturePercentage;
}

// Fungsi Tambahan untuk Penanganan Error
void handleError(const String& errorMessage) {
    Serial.println("Error: " + errorMessage);
    
    // Log error ke Firebase
    Firebase.setString(firebaseData, "/logs/system/error", errorMessage);
    
    delay(1000);
}

void setupPowerManagement() {
    system_update_cpu_freq(80);
    WiFi.setSleepMode(WIFI_NONE_SLEEP);
    WiFi.setPhyMode(WIFI_PHY_MODE_11N);
}

// PERBAIKAN: Implementasi fungsi checkRestartStatus yang lebih baik
void checkRestartStatus() {
    if (Firebase.ready() && Firebase.getBool(firebaseData, "/system/restart")) {
        if (firebaseData.dataType() == "boolean" && firebaseData.boolData() == true) {
            Serial.println("Restart command detected on startup");
            Firebase.setString(firebaseData, "/logs/system", "System baru saja di-restart");
            Firebase.setBool(firebaseData, "/system/restart", false);
        }
    }
    Firebase.setString(firebaseData, "/logs/system", "System starting up");
}

void setup() {
    setupPowerManagement();
    ESP.wdtDisable();
    delay(POWER_STABILIZATION_DELAY);
    Serial.begin(115200);
    
    // Koneksi WiFi
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("Menghubungkan ke WiFi");
    
    int wifiAttempts = 0;
    while (WiFi.status() != WL_CONNECTED && wifiAttempts < MAX_WIFI_CONNECT_ATTEMPTS) {
        Serial.print(".");
        delay(500);
        wifiAttempts++;
        ESP.wdtFeed();
    }
    
    if (WiFi.status() != WL_CONNECTED) {
        handleError("Gagal terhubung ke WiFi");
        delay(5000);
        ESP.restart();
    }
    
    Serial.println("\nTerhubung ke WiFi");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());

    // Konfigurasi Firebase
    config.host = FIREBASE_HOST;
    config.signer.tokens.legacy_token = FIREBASE_AUTH;
    
    Firebase.begin(&config, &auth);
    if (!Firebase.ready()) {
        handleError("Gagal inisialisasi Firebase");
        delay(5000);
        ESP.restart();
    }
    
    Firebase.reconnectWiFi(true);
    Firebase.setMaxRetry(firebaseData, 3);
    Firebase.setMaxErrorQueue(firebaseData, 10);
    Firebase.setReadTimeout(firebaseData, 1000);
    Firebase.setwriteSizeLimit(firebaseData, "tiny");

    // Inisialisasi I2C
    Wire.begin(4, 5);
    Wire.setClock(100000);
    
    // Inisialisasi Sensor BH1750
    int bh1750Attempts = 0;
    while (!lightMeter.begin() && bh1750Attempts < MAX_SENSOR_INIT_ATTEMPTS) {
        Serial.println("Inisialisasi BH1750 gagal, mencoba ulang...");
        delay(500);
        bh1750Attempts++;
    }
    
    bh1750Status = lightMeter.begin();
    Firebase.setString(firebaseData, "/logs/bh1750/status", 
                       bh1750Status ? "Terhubung" : "Gagal terhubung");
    
    // Setup relay pins
    pinMode(RELAY_FAN, OUTPUT);
    pinMode(RELAY_LIGHT, OUTPUT);
    pinMode(RELAY_PUMP, OUTPUT);
    pinMode(RELAY_CAMERA_TRIGGER, OUTPUT);  // Inisialisasi pin trigger kamera
    
    // PERBAIKAN 3: Tidak perlu set pinMode untuk A0 karena sudah analog input secara default
    // Kondisi default relay (HIGH = OFF)
    digitalWrite(RELAY_FAN, HIGH);
    digitalWrite(RELAY_LIGHT, HIGH);
    digitalWrite(RELAY_PUMP, HIGH);
    digitalWrite(RELAY_CAMERA_TRIGGER, HIGH);  // Matikan relay kamera saat inisialisasi
        
    // Inisialisasi NTP Client
    timeClient.begin();
    timeClient.setTimeOffset(25200); // GMT+7 (WIB)
    
    while(!timeClient.update()) {
        timeClient.forceUpdate();
        delay(500);
    }

    // PERBAIKAN: Panggil checkRestartStatus tanpa mengatur restart ke false lagi
    checkRestartStatus();
    // Baris berikut dihapus karena sudah diatur dalam checkRestartStatus
    // Firebase.setBool(firebaseData, "/system/restart", false);

    // Inisialisasi DHT11
    dht.begin();
    
    // Periksa DHT11
    float testHumidity = dht.readHumidity();
    float testTemperature = dht.readTemperature();
    if (!isnan(testHumidity) && !isnan(testTemperature)) {
        dht11Status = true;
        Firebase.setString(firebaseData, "/logs/dht11/message", "Perangkat terhubung");
    } else {
        dht11Status = false;
        Firebase.setString(firebaseData, "/logs/dht11/message", "Sensor tidak terhubung");
    }
    
    // PERBAIKAN 4: Pengecekan status soil moisture sensor yang lebih baik
    int testSoilMoisture = readSoilMoisture();
    if (testSoilMoisture >= 0 && testSoilMoisture <= 100) {
        soilMoistureStatus = true;
        Firebase.setString(firebaseData, "/logs/soil_moisture/message", "Perangkat terhubung");
        Firebase.setInt(firebaseData, "/sensor/kelembapan_tanah", testSoilMoisture);
    } else {
        soilMoistureStatus = false;
        Firebase.setString(firebaseData, "/logs/soil_moisture/message", "Sensor tidak terhubung");
    }
    
    // Kirim status awal
    Firebase.setBool(firebaseData, "/status/connected", true);
    Firebase.setString(firebaseData, "/status/last_seen", getCurrentTime());
    
    // Inisialisasi waktu
    lastDeviceControlTime = millis();
    lastFirebaseUpdateTime = millis();
    lastHeartbeatTime = millis();

    ESP.wdtEnable(WATCHDOG_TIMEOUT * 1000);
}

void loop() {
    ESP.wdtFeed();
    unsigned long currentMillis = millis();

    if (currentMillis - lastFirebaseUpdateTime >= sensorUpdateInterval) {
        lastFirebaseUpdateTime = currentMillis;
        
        // Baca sensor
        float humidity = dht.readHumidity();
        float temperature = dht.readTemperature();
        float lux = lightMeter.readLightLevel();
        int soilMoisture = readSoilMoisture();
        
        if (!isnan(temperature) && !isnan(humidity) && lux >= 0 && soilMoisture != -1) {
            updateSensorData(temperature, humidity, lux, soilMoisture);
        }
        
              if (currentMillis - lastDeviceControlTime >= deviceControlInterval) {
          lastDeviceControlTime = currentMillis;
          controlDevices();
          controlPump();
          controlCameraTrigger();
          ESP.wdtFeed();
     }
      
          updateDeviceStatus();
        updateSensorStatus(); // PERBAIKAN: Menambahkan panggilan ke fungsi updateSensorStatus()
        updateHeartbeat();
        ESP.wdtFeed();
    }
    
    timeClient.update();
    
    // PERBAIKAN: Panggil checkRestartCommand setiap iterasi loop
    checkRestartCommand();
    delay(50);
}

void controlDevices() {
    float temperature = dht.readTemperature();
    float humidity = dht.readHumidity();
    float lightIntensity = lightMeter.readLightLevel();
    
    if (isnan(temperature) || isnan(humidity)) {
        Serial.println("Gagal membaca sensor DHT11");
        return;
    }
    
    timeClient.update();
    int currentHour = timeClient.getHours();
    
    bool shouldFanBeOn = false;
    String fanReason = "";
    
    if (temperature > 27) {
        shouldFanBeOn = true;
        fanReason = "Suhu tinggi";
    } else if (humidity < 60) {
        shouldFanBeOn = true;
        fanReason = "Kelembaban rendah";
    }
    
    digitalWrite(RELAY_FAN, shouldFanBeOn ? LOW : HIGH);
    
    if (shouldFanBeOn != lastKipasStatus) {
        lastKipasStatus = shouldFanBeOn;
        Serial.println("Kipas " + String(shouldFanBeOn ? "ON: " + fanReason : "OFF: Kondisi normal"));
    }
    
    // Logika baru untuk lampu berdasarkan sensor cahaya BH1750
    bool shouldLightBeOn = false;
    String lightReason = "";
    
    // Prioritas 1: Mode malam (jam 17:00 - 06:00)
    if (currentHour >= 17 || currentHour < 6) {
        shouldLightBeOn = true;
        lightReason = "Mode malam";
    }
    // Prioritas 2: Berdasarkan intensitas cahaya
    else if (lightIntensity < LIGHT_LOW_THRESHOLD) {
        shouldLightBeOn = true;
        lightReason = "Intensitas cahaya rendah";
    }
    
    // Update status intensitas cahaya ke Firebase
    String lightStatus = "";
    if (lightIntensity < LIGHT_LOW_THRESHOLD) {
        lightStatus = "Rendah";
    } else if (lightIntensity <= LIGHT_OPTIMAL_MAX) {
        lightStatus = "Optimal";
    } else {
        lightStatus = "Tinggi";
    }
    Firebase.setString(firebaseData, "sensor/status_cahaya", lightStatus);
    
    digitalWrite(RELAY_LIGHT, shouldLightBeOn ? LOW : HIGH);
    
    if (shouldLightBeOn != lastLampuStatus || (shouldLightBeOn && lightReason != lastLampReason)) {
        lastLampuStatus = shouldLightBeOn;
        lastLampReason = lightReason;
        Serial.println("Lampu " + String(shouldLightBeOn ? "ON: " + lightReason : "OFF: Kondisi normal"));
        
        if (shouldLightBeOn) {
            Firebase.setString(firebaseData, "devices/lampu_reason", lightReason);
        }
    }
}

void controlPump() {
    int soilMoistureValue = readSoilMoisture();
    unsigned long currentMillis = millis();
    
    // Jika pembacaan sensor error (-1), jangan lakukan kontrol pompa
    if (soilMoistureValue == -1) {
        Serial.println("Tidak dapat mengontrol pompa: error pembacaan sensor");
        return;
    }
    
    // Tentukan status kelembapan tanah
    String soilStatus = "";
    if (soilMoistureValue < SOIL_DRY_THRESHOLD) {
        soilStatus = "Kering";
    } else if (soilMoistureValue < SOIL_OPTIMAL_MIN) {
        soilStatus = "Cukup";
    } else if (soilMoistureValue <= SOIL_OPTIMAL_THRESHOLD) {
        soilStatus = "Optimal";
    } else if (soilMoistureValue < SOIL_WET_THRESHOLD) {
        soilStatus = "Lembab";
    } else {
        soilStatus = "Terlalu Basah";
    }
    
    // Update status ke Firebase
    Firebase.setString(firebaseData, "sensor/status_kelembapan_tanah", soilStatus);
    
    // Fungsi helper untuk menghitung elapsed time dengan penanganan overflow
    auto calculateElapsedTime = [](unsigned long startTime, unsigned long currentTime) -> unsigned long {
        if (currentTime >= startTime) {
            return currentTime - startTime;
        } else {
            // Overflow terjadi
            return (ULONG_MAX - startTime) + currentTime + 1;
        }
    };
    
    // Logika kontrol pompa yang diperbaiki
    if (soilMoistureValue < SOIL_DRY_THRESHOLD) {
        // Tanah kering - perlu penyiraman dengan siklus
        handleDrySoil(currentMillis, soilStatus, calculateElapsedTime);
    } 
    else if (soilMoistureValue < SOIL_OPTIMAL_MIN) {
        // Tanah cukup - penyiraman ringan atau hentikan siklus
        handleModeratelySoil(currentMillis, soilStatus, calculateElapsedTime);
    }
    else {
        // Tanah sudah optimal/lembab/terlalu basah - matikan pompa
        handleWetSoil(soilStatus);
    }
    
    // Update status pompa ke Firebase
    updatePumpStatus();
}

// Fungsi helper untuk menangani tanah kering
void handleDrySoil(unsigned long currentMillis, String soilStatus, 
                   std::function<unsigned long(unsigned long, unsigned long)> calculateElapsedTime) {
    
    // Jika pompa belum dalam mode siklus, mulai siklus penyiraman
    if (pumpCycleTime == 0) {
        pumpCycleTime = currentMillis;
        pumpCycleOn = true;
        digitalWrite(RELAY_PUMP, LOW); // Nyalakan pompa
        Firebase.setString(firebaseData, "devices/pump_reason", "Penyiraman otomatis - " + soilStatus);
        Serial.println("Pompa ON - Penyiraman dimulai untuk status: " + soilStatus);
    }
    // Jika dalam siklus, atur ON/OFF sesuai timing
    else {
        unsigned long elapsedTime = calculateElapsedTime(pumpCycleTime, currentMillis);
        
        // Jika dalam fase ON dan waktunya sudah lewat, ganti ke OFF
        if (pumpCycleOn && (elapsedTime >= PUMP_ON_DURATION)) {
            pumpCycleTime = currentMillis;
            pumpCycleOn = false;
            digitalWrite(RELAY_PUMP, HIGH); // Matikan pompa untuk sementara
            Serial.println("Pompa OFF - Jeda sementara dalam siklus penyiraman");
        }
        // Jika dalam fase OFF dan waktunya sudah lewat, ganti ke ON
        else if (!pumpCycleOn && (elapsedTime >= PUMP_OFF_DURATION)) {
            pumpCycleTime = currentMillis;
            pumpCycleOn = true;
            digitalWrite(RELAY_PUMP, LOW); // Nyalakan pompa lagi
            Serial.println("Pompa ON - Melanjutkan siklus penyiraman");
        }
    }
}

// Fungsi helper untuk menangani tanah cukup
void handleModeratelySoil(unsigned long currentMillis, String soilStatus,
                         std::function<unsigned long(unsigned long, unsigned long)> calculateElapsedTime) {
    
    // Jika pompa sedang dalam siklus penyiraman
    if (pumpCycleTime != 0) {
        unsigned long elapsedTime = calculateElapsedTime(pumpCycleTime, currentMillis);
        
        // Jika sedang dalam fase ON, biarkan selesai dulu satu siklus ON
        if (pumpCycleOn && (elapsedTime >= PUMP_ON_DURATION)) {
            // Selesaikan fase ON dan langsung hentikan siklus
            digitalWrite(RELAY_PUMP, HIGH); // Matikan pompa
            pumpCycleTime = 0; // Reset siklus
            pumpCycleOn = false;
            Firebase.setString(firebaseData, "devices/pump_reason", "Kelembapan tanah sudah " + soilStatus);
            Serial.println("Pompa OFF - Siklus dihentikan, kelembapan tanah sudah " + soilStatus);
        }
        // Jika dalam fase OFF, langsung hentikan siklus
        else if (!pumpCycleOn) {
            pumpCycleTime = 0; // Reset siklus
            // Pompa sudah OFF, cukup reset status
            Firebase.setString(firebaseData, "devices/pump_reason", "Kelembapan tanah sudah " + soilStatus);
            Serial.println("Siklus pompa dihentikan - Kelembapan tanah sudah " + soilStatus);
        }
        // Jika sedang ON tapi belum waktunya OFF, biarkan lanjut
    }
    // Jika pompa tidak dalam siklus dan sedang OFF, biarkan tetap OFF
    else if (digitalRead(RELAY_PUMP) == HIGH) {
        // Tidak perlu action, pompa sudah OFF
    }
    // Jika pompa tidak dalam siklus tapi sedang ON (kondisi aneh), matikan
    else {
        digitalWrite(RELAY_PUMP, HIGH);
        Firebase.setString(firebaseData, "devices/pump_reason", "Kelembapan tanah sudah " + soilStatus);
        Serial.println("Pompa OFF - Kelembapan tanah sudah " + soilStatus);
    }
}

// Fungsi helper untuk menangani tanah basah
void handleWetSoil(String soilStatus) {
    // Tanah sudah optimal/lembab/terlalu basah - matikan pompa
    if (digitalRead(RELAY_PUMP) != HIGH) {
        digitalWrite(RELAY_PUMP, HIGH); // Matikan pompa
        pumpCycleTime = 0; // Reset siklus penyiraman
        pumpCycleOn = false;
        Firebase.setString(firebaseData, "devices/pump_reason", "Kelembapan tanah sudah " + soilStatus);
        Serial.println("Pompa OFF - Kelembapan tanah sudah " + soilStatus);
    }
}

// Fungsi helper untuk update status pompa
void updatePumpStatus() {
    bool currentPumpStatus = (digitalRead(RELAY_PUMP) == LOW);
    if (currentPumpStatus != lastPumpStatus) {
        lastPumpStatus = currentPumpStatus;
        
        // Retry mechanism untuk Firebase
        if (!Firebase.setBool(firebaseData, "devices/pump", currentPumpStatus)) {
            Serial.println("Gagal update status pump ke Firebase, mencoba lagi...");
            delay(1000);
            Firebase.setBool(firebaseData, "devices/pump", currentPumpStatus);
        }
        
        if (!Firebase.setString(firebaseData, "devices/pump_timestamp", getCurrentTime())) {
            Serial.println("Gagal update pump timestamp ke Firebase");
        }
    }
}
void controlCameraTrigger() {
    // Update waktu NTP dengan penanganan error
    if (!timeClient.update()) {
        Serial.println("Gagal update NTP, menggunakan waktu terakhir");
        // Tetap lanjutkan dengan waktu terakhir yang tersedia
    }
    
    // Dapatkan jam saat ini (format 24 jam)
    int currentHour = timeClient.getHours();
    int currentMinute = timeClient.getMinutes();
    int currentSecond = timeClient.getSeconds();
    
    // Konversi ke total detik dalam satu hari untuk perbandingan yang lebih mudah
    int currentTimeInSeconds = currentHour * 3600 + currentMinute * 60 + currentSecond;
    
    // Waktu aktivasi kamera pagi: 06:00:00 (6 jam * 3600 detik)
    int morningTriggerTime = 6 * 3600;
    
    // Waktu aktivasi kamera sore: 16:00:00 (16 jam * 3600 detik)
    int afternoonTriggerTime = 16 * 3600;
    
    // Cek apakah kamera sedang aktif
    if (cameraTriggerOn) {
        // Penanganan overflow millis() - gunakan unsigned long dan hitung selisih dengan benar
        unsigned long currentTime = millis();
        unsigned long elapsedTime;
        
        // Hitung elapsed time dengan mempertimbangkan kemungkinan overflow
        if (currentTime >= cameraTriggerStartTime) {
            elapsedTime = currentTime - cameraTriggerStartTime;
        } else {
            // Overflow terjadi
            elapsedTime = (ULONG_MAX - cameraTriggerStartTime) + currentTime + 1;
        }
        
        // Jika kamera sedang aktif, cek apakah sudah waktunya mematikan
        if (elapsedTime >= CAMERA_TRIGGER_DURATION) {
            // Matikan relay kamera setelah durasi yang ditentukan
            digitalWrite(RELAY_CAMERA_TRIGGER, HIGH); // HIGH = OFF
            cameraTriggerOn = false;
            
            // Log ke Serial dan Firebase
            Serial.println("Kamera dimatikan setelah 5 menit");
            Firebase.setString(firebaseData, "devices/camera_trigger_reason", "Pengaktifan kamera selesai");
        }
    } else {
        // Jika kamera tidak aktif, cek apakah sudah waktunya menyalakan (pagi atau sore)
        // Perluas toleransi waktu menjadi 60 detik untuk memastikan tidak terlewat
        bool shouldTriggerMorning = (currentTimeInSeconds >= morningTriggerTime && 
                                   currentTimeInSeconds < morningTriggerTime + 60);
        bool shouldTriggerAfternoon = (currentTimeInSeconds >= afternoonTriggerTime && 
                                     currentTimeInSeconds < afternoonTriggerTime + 60);
        
        // Tambahan: Cek juga apakah sudah lewat 24 jam sejak aktivasi terakhir
        // untuk mencegah kamera tidak diaktifkan karena masalah timing
        static unsigned long lastTriggerDay = 0;
        unsigned long currentDay = currentTimeInSeconds / 86400; // Hari ke-n sejak epoch
        
        if (shouldTriggerMorning || shouldTriggerAfternoon || 
            (currentDay > lastTriggerDay && (currentHour == 6 || currentHour == 16))) {
            
            // Aktifkan relay kamera
            digitalWrite(RELAY_CAMERA_TRIGGER, LOW); // LOW = ON
            cameraTriggerStartTime = millis();
            cameraTriggerOn = true;
            lastTriggerDay = currentDay;
            
            // Log informasi ke Serial dan Firebase
            String triggerReason;
            if (shouldTriggerMorning || (currentHour == 6 && currentDay > lastTriggerDay)) {
                Serial.println("Aktivasi kamera pagi (06:00) dimulai");
                triggerReason = "Aktivasi kamera pagi";
            } else {
                Serial.println("Aktivasi kamera sore (16:00) dimulai");
                triggerReason = "Aktivasi kamera sore";
            }
            
            // Tambahkan timestamp untuk debugging
            char timeStr[64];
            sprintf(timeStr, "%s - %02d:%02d:%02d", triggerReason.c_str(), currentHour, currentMinute, currentSecond);
            Firebase.setString(firebaseData, "devices/camera_trigger_reason", timeStr);
        }
    }
    
    // Update status aktivasi kamera jika ada perubahan
    bool currentCameraTriggerStatus = (digitalRead(RELAY_CAMERA_TRIGGER) == LOW);
    if (currentCameraTriggerStatus != lastCameraTriggerStatus) {
        lastCameraTriggerStatus = currentCameraTriggerStatus;
        
        // Tambahkan retry untuk Firebase jika gagal
        if (!Firebase.setBool(firebaseData, "devices/camera_trigger", currentCameraTriggerStatus)) {
            Serial.println("Gagal update status camera_trigger ke Firebase");
            // Coba lagi setelah delay singkat
            delay(1000);
            Firebase.setBool(firebaseData, "devices/camera_trigger", currentCameraTriggerStatus);
        }
        
        if (!Firebase.setString(firebaseData, "devices/camera_trigger_timestamp", getCurrentTime())) {
            Serial.println("Gagal update timestamp ke Firebase");
        }
    }
    
    // Tambahkan debugging info setiap 1 jam
    static unsigned long lastDebugTime = 0;
    if (millis() - lastDebugTime > 3600000) { // 1 jam
        lastDebugTime = millis();
        Serial.printf("Debug: Waktu sekarang %02d:%02d:%02d, Camera aktif: %s\n", 
                     currentHour, currentMinute, currentSecond, 
                     cameraTriggerOn ? "Ya" : "Tidak");
    }

}void updateHeartbeat() {
    unsigned long currentMillis = millis();
    if (currentMillis - lastHeartbeatTime >= heartbeatInterval) {
        lastHeartbeatTime = currentMillis;
        Firebase.setBool(firebaseData, "/status/connected", true);
        Firebase.setString(firebaseData, "/status/last_seen", getCurrentTime());
        Serial.println("Heartbeat: Status koneksi diperbarui");
    }
}

void updateSensorStatus() {
    float testHumidity = dht.readHumidity();
    float testTemperature = dht.readTemperature();
    bool currentDht11Status = !isnan(testHumidity) && !isnan(testTemperature);
    
    if (currentDht11Status != dht11Status) {
        dht11Status = currentDht11Status;
        Firebase.setString(firebaseData, "/logs/dht11/message", 
                          dht11Status ? "Perangkat terhubung" : "Sensor tidak terhubung");
    }
    
    float testLux = lightMeter.readLightLevel();
    bool currentBh1750Status = testLux >= 0;
    
    if (currentBh1750Status != bh1750Status) {
        bh1750Status = currentBh1750Status;
        Firebase.setString(firebaseData, "/logs/bh1750/message", 
                          bh1750Status ? "Perangkat terhubung" : "Sensor tidak terhubung");
    }
    
    int testSoilMoisture = readSoilMoisture();
    bool currentSoilMoistureStatus = testSoilMoisture >= 0 && testSoilMoisture <= 100;
    
    if (currentSoilMoistureStatus != soilMoistureStatus) {
        soilMoistureStatus = currentSoilMoistureStatus;
        Firebase.setString(firebaseData, "/logs/soil_moisture/message", 
                          soilMoistureStatus ? "Perangkat terhubung" : "Sensor tidak terhubung");
    }
}

// Perbarui updateSensorData untuk tidak mengirim data kelembapan tanah dua kali
void updateSensorData(float temp, float humidity, float lux, int soilMoisture) {
    Firebase.setFloat(firebaseData, "sensor/suhu", temp);
    Firebase.setFloat(firebaseData, "sensor/kelembaban", humidity);
    Firebase.setFloat(firebaseData, "sensor/cahaya", lux);
    Firebase.setInt(firebaseData, "sensor/kelembapan_tanah", soilMoisture);
    // Status kelembapan tanah sekarang ditangani di controlPump()
}

void updateDeviceStatus() {
    bool fanStatus = (digitalRead(RELAY_FAN) == LOW);
    bool lightStatus = (digitalRead(RELAY_LIGHT) == LOW);
    bool pumpStatus = (digitalRead(RELAY_PUMP) == LOW);
    bool cameraTriggerStatus = (digitalRead(RELAY_CAMERA_TRIGGER) == LOW);
   
    Firebase.setBool(firebaseData, "devices/kipas", fanStatus);
    Firebase.setBool(firebaseData, "devices/lampu", lightStatus);
    Firebase.setBool(firebaseData, "devices/pump", pumpStatus);
    Firebase.setBool(firebaseData, "devices/camera_trigger", cameraTriggerStatus);
    
    if (fanStatus != lastKipasStatus) {
        lastKipasStatus = fanStatus;
        Firebase.setString(firebaseData, "devices/kipas_timestamp", getCurrentTime());
    }
    
    if (lightStatus != lastLampuStatus) {
        lastLampuStatus = lightStatus;
        Firebase.setString(firebaseData, "devices/lampu_timestamp", getCurrentTime());
    }
    
    if (pumpStatus != lastPumpStatus) {
        lastPumpStatus = pumpStatus;
        Firebase.setString(firebaseData, "devices/pump_timestamp", getCurrentTime());
    }
    
    if (cameraTriggerStatus != lastCameraTriggerStatus) {
        lastCameraTriggerStatus = cameraTriggerStatus;
        Firebase.setString(firebaseData, "devices/camera_trigger_timestamp", getCurrentTime());
    }
}

String getCurrentTime() {
  timeClient.update();
  return timeClient.getFormattedTime();
}

// PERBAIKAN: Implementasi fungsi checkRestartCommand yang lebih robust
void checkRestartCommand() {
    if (Firebase.ready() && Firebase.getBool(firebaseData, "/system/restart")) {
        // Check if we got a valid response and the value is true
        if (firebaseData.dataType() == "boolean" && firebaseData.boolData() == true) {
            Serial.println("Restart command detected, restarting system...");
            // Log the restart event
            Firebase.setString(firebaseData, "/logs/system", "System sedang di restart");
            // Set the restart flag back to false
            Firebase.setBool(firebaseData, "/system/restart", false);
            delay(1000); // Give Firebase some time to update
            ESP.restart();
        }
    }
}
