#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiManager.h>        //https://github.com/tzapu/WiFiManager
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson pentru salvarea credentialelor in eprom
#include <WiFiClient.h> 
#include <OneWire.h>
#include <PietteTech_DHT.h>

//ESP8266-01 nu are suficienta memorie pt update http
const char* mqtt_user="64x35up";
const char* mqtt_devid="1790";

// senzor de temperatura DHT
#define DHTPIN 2 // pinul de date 
#define DHTTYPE DHT22 // DHT11 (albastru) DHT22 (alb)
#define DEBUG 0 //0=off 1=on

/********** NU edita mai jos ******************/

char mqttPassword[15]; //urmatoarele le va genera cu functia gen
char mqttSUB[23];
char espName[14];
char mqttESP[23];
char mqttTEMP[24];
char mqttLWT[23];
char mqttSTA[25];
char wifiID[15];

const String model = "ESP8266 ";
const String ver = "v1.1.1";
const char* mqttServer = "mqtt.clickhome.ro";
const int mqttPort = 1883;
long loopTimer = 900000; // miliseconds - by default trimite la 15 minute
long lastMsg = 0;
float loopTemp = 0;
float t = 0;
float loopHumi = 0;
float h = 0;
String mqttMessage;
bool bDHTstarted;
int acquireresult;
const unsigned long interval_update=1000*60*5; // face update la 5 min 
unsigned long t_update=0;


void dht_wrapper();
PietteTech_DHT DHT(DHTPIN, DHTTYPE, dht_wrapper);
void dht_wrapper() 
{
  DHT.isrCallback();
}
//*********************************************************************

WiFiClient espClient;
PubSubClient client(espClient);

void generate_vars(){
  strcpy (mqttPassword, "8219CH");
  strcat (mqttPassword, mqtt_user);

  strcpy (espName, mqtt_user);
  strcat (espName, "-");
  strcat (espName, mqtt_devid);

  strcpy (mqttSUB, "cmnd/");
  strcat (mqttSUB, espName);
  strcat (mqttSUB, "/ESP");

  strcpy (mqttESP, "stat/");
  strcat (mqttESP, espName);
  strcat (mqttESP, "/ESP");

  strcpy (mqttTEMP, "stat/");
  strcat (mqttTEMP, espName);
  strcat (mqttTEMP, "/TEMP");

  strcpy (mqttLWT, "tele/");
  strcat (mqttLWT, espName);
  strcat (mqttLWT, "/LWT");

  strcpy (mqttSTA, "tele/");
  strcat (mqttSTA, espName);
  strcat (mqttSTA, "/STATE");

  strcpy (wifiID, "ClickHome-");
  strcat (wifiID, mqtt_devid);
}

String ipToString(IPAddress ip){
  String s="";
  for (int i=0; i<4; i++)
    s += i  ? "." + String(ip[i]) : String(ip[i]);
  return s;
}

String getMacAddress() {
  byte mac[6];
  WiFi.macAddress(mac);
  String cMac = "";
  for (int i = 0; i < 6; ++i) {
    cMac += String(mac[i],HEX);
    if(i<5)
    cMac += "-";
  }
  cMac.toUpperCase();
  return cMac;
}

void reconectez() {
 
  // ma conectez la mqtt server
  while (!client.connected()) {
    client.setServer(mqttServer, mqttPort);
    #if DEBUG
    Serial.print("AtloopTempting MQTT connection...");
    #endif
    // Incerc sa ma reconectez cu LWT din 5 in 5 secunde
    if (client.connect(espName, mqtt_user, mqttPassword, mqttLWT, 1, 1, "Offline")) {
      #if DEBUG
      Serial.println("connected");
      #endif
      client.publish(mqttLWT,"Online",TRUE);
      // trimit informatii utile cand ma conectez
        String esp_info = "{\"ESPMac\":\"";
        esp_info += getMacAddress();
        esp_info += "\",\"IPAddress\":\"";
        esp_info += ipToString(WiFi.localIP());    
        esp_info += "\"}";       
        String netinfo = "{\"Module\":\"";
        netinfo += String (model);
        netinfo += "DHT";
        netinfo += String (DHTTYPE);
        netinfo += "\",\"Version\":\"";
        netinfo += String (ver);
        netinfo += "\"}";            
        
        client.publish(mqttESP, netinfo.c_str(),TRUE);
        #if DEBUG
        Serial.println(netinfo);
        #endif
        client.publish(mqttESP, esp_info.c_str(),TRUE);
        #if DEBUG
        Serial.println(esp_info);
        #endif
        client.publish(mqttESP, "{\"LoopInterval\":\"15 minute\"}",TRUE); // trimit intervalul default
        client.subscribe(mqttSUB);
    } else {
      #if DEBUG
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 60 seconds");
      #endif
      // Wait 60 seconds before retrying
      delay(60000);
    }
  }
}

void setup()
{
  #if DEBUG
  Serial.begin(115200);
  #endif

  generate_vars(); //genereaza topicurile de mqtt in baza mqtt_user si mqtt_devid
  
  // ma conectez la AP via wifi
   WiFiManager wifi;
   wifi.setConfigPortalTimeout(120); // a timeout so the ESP doesn't hang waiting to be configured, for instance after a power failure
   wifi.setTimeout(180); // sta AP 3 minute apoi se reseteaza din nou
   if (!wifi.autoConnect(wifiID)) {
     #if DEBUG
     Serial.println("timeout - going to sleep");
     #endif
  }
  delay(200);

  reconectez();
  client.setCallback(getMessage); //setez functia care parseaza mesajele venite prin mqtt
  update_state(); //facem update la mqtt cu Uptime
}

void update_state(){ //facem update la mqtt cu Uptime
    String json = "{\"Uptime\":";
    json += (int)millis()/3600000; //exprimat in ore
    json += "}";
    client.publish(mqttSTA, json.c_str(),FALSE);
}

void getMessage(char* topic, byte* payload, unsigned int length) {
  mqttMessage="";
  #if DEBUG
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  #endif
  for (int i = 0; i < length; i++) {
    mqttMessage += (char)payload[i];
  }
  #if DEBUG
  Serial.println(mqttMessage);
  #endif
  
  // daca a primit temp va trimite temperatura 
  if (mqttMessage == "temp")
  {
      #if DEBUG
      Serial.println("Trimit temperatura si umiditatea");
      #endif
      acquireresult = DHT.acquireAndWait(2000);
      if ( acquireresult == 0 ) 
      {
           t = DHT.getCelsius();
           h = DHT.getHumidity();
           delay(10);
           String json = "{\"Temp\":";
           json += String (t);
           json += ",\"Humi\":";
           json += String (h);
           json += "}";  
           client.publish(mqttTEMP, json.c_str(),TRUE);
           #if DEBUG
           Serial.println(json);
           #endif
      } 
      else 
      {
        t = h = 0;
        #if DEBUG
        Serial.println("Nu am putut citi temperatura si umiditatea");
        #endif
      }    
  }  

// reset la ESP
  if (mqttMessage == "reset")
  {
      String lastwords="Am fost resetat ...";
      client.publish(mqttESP, lastwords.c_str(), TRUE);
      delay (3000);
      ESP.reset();
      delay (5000);
  }

  // daca a primit valoarea in minute seteaza loopTimer si-l trimite in DB
  
   if (mqttMessage == "debug")
   {
        loopTimer=30000; // setez sa trimita valori la 30 de secunde ca sa fac debug
        client.publish(mqttESP, "{\"LoopInterval\":\"30 secunde\"}",TRUE);   
   }
   if (mqttMessage == "10")
   {
        loopTimer=600000; // setez sa trimita valori la 10 minute   
        client.publish(mqttESP, "{\"LoopInterval\":\"10 minute\"}",TRUE);
   }
   if (mqttMessage == "15")
   {
        loopTimer=900000; // setez sa trimita valori la 15 minute
        client.publish(mqttESP, "{\"LoopInterval\":\"15 minute\"}",TRUE);   
   }
   if (mqttMessage == "30")
   {
        loopTimer=1800000; // setez sa trimita valori la 30 de minute
        client.publish(mqttESP, "{\"LoopInterval\":\"30 de minute\"}",TRUE);   
   }   
   if (mqttMessage == "60")
   {
        loopTimer=3600000; // setez sa trimita valori la o ora
        client.publish(mqttESP, "{\"LoopInterval\":\"o ora\"}",TRUE);   
   }
   if (mqttMessage == "stop")
   {
        loopTimer=3600000000; // setez sa nu trimita valori
        client.publish(mqttESP, "{\"LoopInterval\":\"oprit\"}",TRUE);
   }  
}

void loop()
{
  if (!client.connected()) {
    reconectez();
  }
  client.loop();
  //facem update la $interval_update cu uptime
  if (millis() - t_update > interval_update){
    update_state();
    t_update=millis();
  }
  //end update

  if (millis() - lastMsg > loopTimer) {
     lastMsg = millis();
     acquireresult = DHT.acquireAndWait(2000);
     if ( acquireresult == 0 ) {
           loopTemp = DHT.getCelsius();
           loopHumi = DHT.getHumidity();
           delay(10);
           String val = "{\"Temp\":";
           val += String (loopTemp);
           val += ",\"Humi\":";
           val += String (loopHumi);
           val += "}";  
           client.publish(mqttESP, val.c_str(),TRUE);
      } else {
        loopTemp = loopHumi = 0;
      }
   }
}