#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#include <Adafruit_Sensor.h>
#include "DHT.h"

const char *ssid = "Bill1389";
const char *password = "B4cvxw43bt";

#define GH_INCLUDE_PORTAL
#include <GyverHub.h>
GyverHub hub("MyDevices", "ESP", "");

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");

#define LED_PIN 2 // Указывайте пин, на котором подключен светодиод
#define FAN_RELAY 22 // Указывайте пин, на котором подключен светодиод

#define DHTPIN 23           // пин (D2) подключения контакта DATA
#define DHTTYPE DHT22      // датчик DHT 22 
#define INTERVAL_GET_DATA 2000  // интервала измерений, мс
// создание экземпляра объекта DHT
DHT dht(DHTPIN, DHTTYPE);
// переменная для интервала измерений 
unsigned long millis_int1=0;

int switchLightState;
int switchLightStateOld;
int currentTimeSec;
int startTime;
int stopTime;
int startTimeOld;
int stopTimeOld;

int setHumidity;
int setTemp;
int setHumidityOld;
int setTempOld;
int currentHumidity;
float currentTemp;

  bool lampRelayState = 0; // Реле освещения
  bool fanCoolingRelayState = 0; // Реле вентилятора отопления
  bool fanCirculationRelayState = 0; // Реле вентилятора циркуляции воздуха
  bool fanOutdoorAirRelayState = 0; // Реле вентилятора вытяжки
  bool waterPumpRelayState = 0; // Реле циркуляционного насоса
  bool TENRelayState = 0; // Реле ТЭНа
  bool fitoLampRelayState = 0; // Реле фитоламп
  bool bit7 = 0; // резерв

// для работы периодов
int statePeriodLamp;
int minutePeriodLamp;
int minuteOnPeriodLamp;


int statePeriodFanCooling;
int minutePeriodFanCooling;
int minuteOnPeriodFanCooling;

int statePeriodFanCirculation;
int minutePeriodFanCirculation;
int minuteOnPeriodFanCirculation;

int statePeriodFanOutdoorAir;
int minutePeriodFanOutdoorAir;
int minuteOnPeriodFanOutdoorAir;

int statePeriodWaterPump;
int minutePeriodWaterPump;
int minuteOnPeriodWaterPump;


int statePeriodTEN;
int minutePeriodTEN;
int minuteOnPeriodTEN;


int statePeriodFitoLamp;
int minutePeriodFitoLamp;
int minuteOnPeriodFitoLamp;


int statePeriod_Reserv;
int minutePeriod_Reserv;
int minuteOnPeriod_Reserv;

bool statusTest = 0;


char currentTime[10]; // Выделение памяти под строку
gh::Log hlog;





void writeIntToEEPROM(int address, int value)
{
  // EEPROM.begin(sizeof(value));
  EEPROM.put(address, value);
  EEPROM.commit();
  // EEPROM.end();
}

int readIntFromEEPROM(int address)
{
  int value;
  // EEPROM.begin(sizeof(value));
  EEPROM.get(address, value);
  // EEPROM.end();
  return value;
}


void checkLight()
{
  // Получаем текущее время
  int currentHour = timeClient.getHours() + 3;
  int currentMinute = timeClient.getMinutes();
  int currentSeconds = timeClient.getSeconds();

  if (startTimeOld != startTime)
  {
    writeIntToEEPROM(sizeof(int), startTime);
    Serial.println(startTime);

    startTimeOld = startTime;
  }

  if (stopTimeOld != stopTime)
  {

    writeIntToEEPROM(2 * sizeof(int), stopTime);
    Serial.println(stopTime);
    stopTimeOld = stopTime;
  }

  snprintf(currentTime, sizeof(currentTime), "%02d:%02d", currentHour, currentMinute);
  currentTimeSec = currentHour * 3600 + currentMinute * 60 + currentSeconds;
  // Проверяем условие освещения

  if (switchLightState != switchLightStateOld)
  {
    writeIntToEEPROM(0, switchLightState);
    switchLightStateOld = switchLightState;
  }

  switch (switchLightState)
  {
  case 0:
    digitalWrite(LED_PIN, LOW); // Включаем светодиод
    break;
  case 1:
    digitalWrite(LED_PIN, HIGH); // Выключаем светодиод
    break;
  case 2:
    //if (((currentHour >= startHours) and (currentMinute >= startMinutes)) and ((currentHour <= stopHours) and (currentMinute < stopMinutes)))
    if((startTime <= currentTimeSec) and ( currentTimeSec <= stopTime))
    {
      digitalWrite(LED_PIN, LOW); // Включаем светодиод
    }
    else
    {
      digitalWrite(LED_PIN, HIGH); // Выключаем светодиод
    }
    break;

  default:
    digitalWrite(LED_PIN, HIGH); // Выключаем светодиод
    break;
  }
}


int get_data_humidity() {
   int h = dht.readHumidity();
   return h;
}
float get_data_temperature() {
   float h = dht.readTemperature();
   return h;
}

void manageClimate()
{
  currentHumidity = get_data_humidity();
  currentTemp = get_data_temperature();

    if (setTemp != setTempOld)
  {
    writeIntToEEPROM(3 * sizeof(int), setTemp);
    setTempOld = setTemp;
  }


  if (setHumidity != setHumidityOld)
  {
    writeIntToEEPROM(4 * sizeof(int), setHumidity);
    setHumidityOld = setHumidity;
  }

  if(currentTemp < setTemp)
  {
    digitalWrite(FAN_RELAY, LOW); // Включаем светодиод
  } else {
    digitalWrite(FAN_RELAY, HIGH); // Выключаем светодиод
  }



}









void build_main(gh::Builder &b)
{

  b.Title_(F("title1")).value("Освещение");
  if (b.beginRow())
  {
    b.Time_("startTime", &startTime).label("Время включения").size(2);
    b.Label_(F("currentTime")).label("Текущее время").color(0xd55f30).size(2);

    b.Time_("stopTime", &stopTime).label("Время отключения").size(2);
    b.endRow();
  }

  {
    gh::Row r(b);
    b.Tabs_("tab", &switchLightState).text("Включить;Выключить;По таймеру").label("Выключатель света").size(3);
  }

   
    b.Title_(F("title2")).value("Настройка климата");
  if (b.beginRow())
  {
    b.Spinner_("setTemp", &setTemp).label("Поддерживать температуру").size(3);
    b.Spinner_("setHumidity", &setHumidity).label("Задать максимальную влажость").size(3);
    b.endRow();
  }
    b.Title_(F("title3")).value("Данные датчиков");
  if (b.beginRow())
  {
    b.Label_(F("currentTemp")).label("Температура").color(0xd55f30).size(2);
    b.Label_(F("currentHumidity")).label("Влажность").color(0xd55f30).size(2);
    b.endRow();
  }

}


void build_period(gh::Builder &b)
{
  b.Title_(F("title3")).value("Освещение");
  if (b.beginRow())
  {
         b.Switch(&statePeriodLamp).label("Активировать").size(1);
         b.Input(&minutePeriodLamp).label("Каждые __ минут");
         b.Input(&minuteOnPeriodLamp).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title4")).value("Вентилятор отопления");
  if (b.beginRow())
  {
         b.Switch(&statePeriodFanCooling).label("Активировать").size(1);
         b.Input(&minutePeriodFanCooling).label("Каждые __ минут");
         b.Input(&minuteOnPeriodFanCooling).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title5")).value("Вентилятор циркуляции");
  if (b.beginRow())
  {
         b.Switch(&statePeriodFanCirculation).label("Активировать").size(1);
         b.Input(&minutePeriodFanCirculation).label("Каждые __ минут");
         b.Input(&minuteOnPeriodFanCirculation).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title6")).value("Вентиляция");
  if (b.beginRow())
  {
         b.Switch(&statePeriodFanOutdoorAir).label("Активировать").size(1);
         b.Input(&minutePeriodFanOutdoorAir).label("Каждые __ минут");
         b.Input(&minuteOnPeriodFanOutdoorAir).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title7")).value("Насос котла");
  if (b.beginRow())
  {
         b.Switch(&statePeriodWaterPump).label("Активировать").size(1);
         b.Input(&minutePeriodWaterPump).label("Каждые __ минут");
         b.Input(&minuteOnPeriodWaterPump).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title8")).value("ТЭН");
  if (b.beginRow())
  {
         b.Switch(&statePeriodTEN).label("Активировать").size(1);
         b.Input(&minutePeriodTEN).label("Каждые __ минут");
         b.Input(&minuteOnPeriodTEN).label("включить на __ минут");
    b.endRow();
  }

  b.Title_(F("title9")).value("Фитолампы");
  if (b.beginRow())
  {
         b.Switch(&statePeriodFitoLamp).label("Активировать").size(1);
         b.Input(&minutePeriodFitoLamp).label("Каждые __ минут");
         b.Input(&minuteOnPeriodFitoLamp).label("включить на __ минут");
    b.endRow();
  }

  if (b.Button().label("Сохранить изменения").size(3).click()){
    writeIntToEEPROM(5 * sizeof(int), statePeriodLamp);
    writeIntToEEPROM(6 * sizeof(int), minutePeriodLamp);
    writeIntToEEPROM(7 * sizeof(int), minuteOnPeriodLamp);
    writeIntToEEPROM(8 * sizeof(int), statePeriodFanCooling);
    writeIntToEEPROM(9 * sizeof(int), minutePeriodFanCooling);
    writeIntToEEPROM(10 * sizeof(int), minuteOnPeriodFanCooling);
    writeIntToEEPROM(11 * sizeof(int), statePeriodFanCirculation);
    writeIntToEEPROM(12 * sizeof(int), minutePeriodFanCirculation);
    writeIntToEEPROM(13 * sizeof(int), minuteOnPeriodFanCirculation);
    writeIntToEEPROM(14 * sizeof(int), statePeriodFanOutdoorAir);
    writeIntToEEPROM(15 * sizeof(int), minutePeriodFanOutdoorAir);
    writeIntToEEPROM(16 * sizeof(int), minuteOnPeriodFanOutdoorAir);
    writeIntToEEPROM(17 * sizeof(int), statePeriodWaterPump);
    writeIntToEEPROM(18 * sizeof(int), minutePeriodWaterPump);
    writeIntToEEPROM(19 * sizeof(int), minuteOnPeriodWaterPump);
    writeIntToEEPROM(20 * sizeof(int), statePeriodTEN);
    writeIntToEEPROM(21 * sizeof(int), minutePeriodTEN);
    writeIntToEEPROM(22 * sizeof(int), minuteOnPeriodTEN);
    writeIntToEEPROM(23 * sizeof(int), statePeriodFitoLamp);
    writeIntToEEPROM(24 * sizeof(int), minutePeriodFitoLamp);
    writeIntToEEPROM(25 * sizeof(int), minuteOnPeriodFitoLamp);
    writeIntToEEPROM(26 * sizeof(int), statePeriod_Reserv);
    writeIntToEEPROM(27 * sizeof(int), minutePeriod_Reserv);
    writeIntToEEPROM(28 * sizeof(int), minuteOnPeriod_Reserv);

  }

  
}


void build(gh::Builder& b) {
    if (b.build.isSet()) hlog.println(b.build.name);
    b.Menu(F("Главная;Периоды"));

    switch (hub.menu) {
        case 0:
            build_main(b);
            
            break;
        case 1:
            build_period(b);
            break;
        
    }
}
bool checkPeriod(int startMinute, unsigned long &previousMillis) {
    const unsigned long interval = 60000UL * startMinute; // Интервал в миллисекундах для выполнения действия каждые startMinute минут

    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
        previousMillis = currentMillis; // Обновляем время последнего выполнения
        return true;
    } else {
        return false;
    }
}

unsigned long previousMillisPeriodLamp = 0;
unsigned long previousMillisStopPeriodLamp = 0;

void checkPeriodLight() {
    if (statePeriodLamp == 1) {
        if (checkPeriod(minutePeriodLamp, previousMillisPeriodLamp)) {
            Serial.println("ON");
            Serial.println(previousMillisPeriodLamp);
            previousMillisStopPeriodLamp = previousMillisPeriodLamp;
        }

        if (checkPeriod(minuteOnPeriodLamp, previousMillisStopPeriodLamp)) {
            Serial.println("OFF");
        }
    }
}

void periodLight() {

    //Serial.println(statePeriodLamp);
if(statePeriodLamp == 1){
  Serial.println(statePeriodLamp);
  static gh::Timer tmrLampStart(minutePeriodLamp * 60000); // период минут
  
  if (tmrLampStart)
  {
   static gh::Timer tmrLampStop(minuteOnPeriodLamp * 60000); // период минут
statusTest = !statusTest;
      
  }
  if(statusTest == 1){
  Serial.println("ON"); 
} else {
    Serial.println("OFF"); 
}
}
  
}

void codingStateReley(){

  // Кодируем 8 битов в одно число
  uint8_t encodedNumber = (bit7 << 7) | (fitoLampRelayState << 6) | (TENRelayState << 5) | (waterPumpRelayState << 4) |
                          (fanOutdoorAirRelayState << 3) | (fanCirculationRelayState << 2) | (fanCoolingRelayState << 1) | lampRelayState;

}





void setup()
{
  Serial.begin(9600);
  EEPROM.begin(128);
  dht.begin();         // запуск DHT  
  switchLightState = readIntFromEEPROM(0);

  startTime = readIntFromEEPROM(sizeof(int));
  stopTime = readIntFromEEPROM(2 * sizeof(int));
  setTemp = readIntFromEEPROM(3 * sizeof(int));
  setHumidity = readIntFromEEPROM(4 * sizeof(int));

  // для работы периодов
statePeriodLamp = readIntFromEEPROM(5 * sizeof(int));
minutePeriodLamp = readIntFromEEPROM(6 * sizeof(int));
minuteOnPeriodLamp = readIntFromEEPROM(7 * sizeof(int));
statePeriodFanCooling = readIntFromEEPROM(8 * sizeof(int));
minutePeriodFanCooling = readIntFromEEPROM(9 * sizeof(int));
minuteOnPeriodFanCooling = readIntFromEEPROM(10 * sizeof(int));
statePeriodFanCirculation = readIntFromEEPROM(11 * sizeof(int));
minutePeriodFanCirculation = readIntFromEEPROM(12 * sizeof(int));
minuteOnPeriodFanCirculation = readIntFromEEPROM(13 * sizeof(int));
statePeriodFanOutdoorAir = readIntFromEEPROM(14 * sizeof(int));
minutePeriodFanOutdoorAir = readIntFromEEPROM(15 * sizeof(int));
minuteOnPeriodFanOutdoorAir = readIntFromEEPROM(16 * sizeof(int));
statePeriodWaterPump = readIntFromEEPROM(17 * sizeof(int));
minutePeriodWaterPump = readIntFromEEPROM(18 * sizeof(int));
minuteOnPeriodWaterPump = readIntFromEEPROM(19 * sizeof(int));
statePeriodTEN = readIntFromEEPROM(20 * sizeof(int));
minutePeriodTEN = readIntFromEEPROM(21 * sizeof(int));
minuteOnPeriodTEN = readIntFromEEPROM(22 * sizeof(int));
statePeriodFitoLamp = readIntFromEEPROM(23 * sizeof(int));
minutePeriodFitoLamp = readIntFromEEPROM(24 * sizeof(int));
minuteOnPeriodFitoLamp = readIntFromEEPROM(25 * sizeof(int));
statePeriod_Reserv = readIntFromEEPROM(26 * sizeof(int));
minutePeriod_Reserv = readIntFromEEPROM(27 * sizeof(int));
minuteOnPeriod_Reserv = readIntFromEEPROM(28 * sizeof(int));






  // Подключение к WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");

  // Инициализация светодиода
  pinMode(LED_PIN, OUTPUT);
  pinMode(FAN_RELAY, OUTPUT);

  // Настройка NTP
  timeClient.begin();

  hub.config(F("MyDevices"), F("ESP"), F(""));
  //hub.mqtt
  // подключить билдер
  hub.onBuild(build);

  // запуск!
  hub.begin();
}

void loop()
{
  timeClient.update(); // Обновляем время
  checkLight();

  hub.tick();

  checkPeriodLight();

  static gh::Timer tmr(1000); // период 1 секунда
  // каждую секунду будем обновлять заголовок
  if (tmr)
  {
    manageClimate();
    hub.update(F("currentTime")).value(String(currentTime));
    hub.update(F("currentHumidity")).value(String(currentHumidity)+'%');
    hub.update(F("currentTemp")).value(String(currentTemp)+ "C");
    
      
  }
}