#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <WiFiManager.h>        //https://github.com/tzapu/WiFiManager
#include <WiFiClient.h> 
#include <OneWire.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>

#define RELAY D2 //  pinul de date (la mine am pus releu pe D2)

// Modifica cu datele tale
const char* mqttUser = "qbzesh";
const char* mqttPassword = "8219CHqbzesh";
const char* mqttSUB = "cmnd/qbzesh-1432/ESP";
const char* mqttESP = "stat/qbzesh-1432/ESP";
const char* mqttRELAY = "stat/qbzesh-1432/RELAY";
const char* mqttLWT = "tele/qbzesh-1432/LWT";
const char* espName ="qbzesh-1432";

//#######################################################################
// NU EDITA MAI JOS
//#######################################################################
const String model = "NodeMCU Releu";
const String ver = "v1.0.5";
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;
String mqttMessage;

WiFiClient espClient;
PubSubClient client(espClient);
 
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 reconnect() 
{
  while (!client.connected()) 
  {
    Serial.print("AtloopTempting MQTT connection...");
     // Incerc sa ma reconectez cu LWT din 60 in 60 secunde
    if (client.connect(espName, mqttUser, mqttPassword, mqttLWT, 1, 1, "Offline")) 
    {
        Serial.println("Releu reconectat la MQTT.");
        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 += "\", \"Version\":\"";
        netinfo += String (ver);
        netinfo += "\"}";            
        
        client.publish(mqttESP, netinfo.c_str(),TRUE);
        Serial.println(netinfo);
        client.publish(mqttESP, esp_info.c_str(),TRUE);
        Serial.println(esp_info);
        client.subscribe(mqttSUB);
    } 
    else {
        Serial.println("Reconectare MQTT esuata: ");
        Serial.println(client.state());
        Serial.println("fac o alta incercare in 60 de secunde");
        delay(60000);
    }
  }
}
 
void setup()
{
  Serial.begin(115200);
  pinMode(RELAY,OUTPUT);

  // ma conectez la AP via wifi
   WiFiManager wifi;
   wifi.setTimeout(180); // sta AP 3 minute apoi se reseteaza din nou
   if (!wifi.autoConnect("ClickHome")) {
     Serial.println("timeout - going to sleep");
  }
  delay(200);
 
  // ma conectez la mqtt server
  client.setServer(mqttServer, mqttPort);
    while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
    // fac connect cu LWT
    if (client.connect(espName, mqttUser, mqttPassword, mqttLWT, 1, 1, "Offline")) {
      // trimit starea Online pe topic LWT cand ma conectez
      client.publish(mqttLWT,"Online",TRUE);
      // trimit starea OFF cand il aprind
      digitalWrite(RELAY,LOW);
      client.publish(mqttRELAY, "{\"Relay\":\"0\"}", TRUE);
      Serial.println("connected");
      // 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 += "\", \"Version\":\"";
        netinfo += String (ver);
        netinfo += "\"}";            
        
        client.publish(mqttESP, netinfo.c_str(),TRUE);
        Serial.println(netinfo);
        client.publish(mqttESP, esp_info.c_str(),TRUE);
        Serial.println(esp_info);
      client.subscribe(mqttSUB);
    } 
      else {
        Serial.print("Conectare MQTT esuata: ");
        Serial.print(client.state());
        delay(2000);
    }
  }
  // astept mesajul de la serverul MQTT
  client.setCallback(getMessage);
 // pinMode(RELAY,OUTPUT);
}

void getMessage(char* topic, byte* payload, unsigned int length) {
  float t = 0;
  mqttMessage="";
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    mqttMessage += (char)payload[i];
  }
  Serial.println(mqttMessage);
 
  // daca a primit on
  if (mqttMessage == "ON")
  {
    Serial.println("Pornesc releu");
    digitalWrite(RELAY,HIGH);
    String json = "{\"Relay\":\"1\"}";
    client.publish(mqttRELAY, json.c_str(),TRUE);
  }  
  if (mqttMessage == "OFF")
  {
     long now = millis(); 
    Serial.println("Opresc releu");
    Serial.println(now);
    digitalWrite(RELAY,LOW);
    String json = "{\"Relay\":\"0\"}";
    client.publish(mqttRELAY, json.c_str(),TRUE);
  }
   // procedura de software update via WEB
   if (mqttMessage == "update")
   {
        String msg="Software update: ";
        t_httpUpdate_return ret; 
        //ESPhttpUpdate.rebootOnUpdate(false);
        ret = ESPhttpUpdate.update("http://update.clickhome.ro/releu/arduino.bin");  
        switch(ret) {
           case HTTP_UPDATE_FAILED:
               msg.concat(" eroare:");
               msg.concat(ESPhttpUpdate.getLastError());
               msg.concat(" motiv:");
               msg.concat(ESPhttpUpdate.getLastErrorString().c_str());
               break;
           case HTTP_UPDATE_NO_UPDATES:
               msg.concat(" no update.");
               break;
           case HTTP_UPDATE_OK:
               msg.concat(" success.");
               break;
        }
        Serial.println(msg);
   }

  if (mqttMessage == "stare")
  {
    // citesc starea si o trimit
    String json = "{\"Relay\":\"";
    json += String (digitalRead(RELAY));
    json += "\"}";
    client.publish(mqttRELAY, json.c_str(),TRUE);
  }
}
void loop()
{
  if (!client.connected()) 
  {
    reconnect();
  }
  client.loop();
  long now = millis(); 
  if (now - lastMsg > loopTimer) 
  {
    lastMsg = now;
       Serial.println("Nu fac nimic in bucla");
  }
}