
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <PubSubClient.h>
#include <ArduinoOTA.h>     //ArduinoOTA is now included with the ArduinoIDE

//start user config
#define USER_SSID                 "USER_SSID "
#define USER_PASSWORD             "USER_PASSWORD  "
#define USER_MQTT_SERVER          "192.168.86.145"
#define USER_MQTT_PORT            1883
#define USER_MQTT_USERNAME        "MQTT_USERNAME"
#define USER_MQTT_PASSWORD        "MQTT_PASSWORD  "
#define USER_MQTT_CLIENT_NAME     "DiningMQTTLamp"           // Used to define MQTT topics, MQTT Client ID, and ArduinoOTA
#define USER_MQTT_TOPIC           "mymosquitto/home"  
#define MQTTOH_SUBTOPIC1          "home"
#define MQTTOH_SUBTOPIC2          "DiningRoom" //may just use the client name here instead
#define MQTTOH_SUBTOPIC3          "Relay"
#define MQTTOH_SUBTOPIC4          "Brightness"
/*
#define MQTTOH_SUBTOPIC5          "Motion"
#define MQTTOH_SUBTOPIC6          "GDoorSwitch"
#define MQTTOH_SUBTOPIC7          "ButtonIN"
*/
#define MQTTOH_SUBTOPIC8          "UpdateSwitch"

//Define pins
#define relayPin 13 //d7
#define brightnessPin 15 //D8 Lamp - LED - GPIO 15=d8 on ESP-12E NodeMCU board
//Define entire topic strings
const char* DiningRelay = USER_MQTT_TOPIC "/" USER_MQTT_CLIENT_NAME "/" MQTTOH_SUBTOPIC3; //Relay state to recieve
const char* DiningBrightness = USER_MQTT_TOPIC "/" USER_MQTT_CLIENT_NAME "/" MQTTOH_SUBTOPIC4; //Brightness Data to recieve
const char* DiningLEDUpdate = USER_MQTT_TOPIC "/" USER_MQTT_CLIENT_NAME "/" MQTTOH_SUBTOPIC8; //switch for setting ota update state


//Variables to use for later
int dimLevelReal = 0; //output PWM level to use after conversion
int dimLevelReceived = 0; //capture input from mqtt to convert to 0-1023
String relayState = "OFF";





//Do not change, refer to setup at top instead

const char* ssid = USER_SSID ; 
const char* password = USER_PASSWORD ;
const char* mqtt_server = USER_MQTT_SERVER ;
const int mqtt_port = USER_MQTT_PORT ;
const char *mqtt_user = USER_MQTT_USERNAME ;
const char *mqtt_pass = USER_MQTT_PASSWORD ;
const char *mqtt_client_name = USER_MQTT_CLIENT_NAME ; 

// Initializes the espClient. You should change the espClient name if you have multiple ESPs running in your home automation system
WiFiClient espClient;
PubSubClient client(espClient);


//general variables
bool boot = true;
bool otaUpgrade = false; //variable to hold in case ota update is needed
int SendCommand = 0;
int TempCount = 0;

// Timers auxiliar variables
long now = millis();
long lastMeasure = 0;



// Don't change the function below. This functions connects your ESP8266 to your router
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.hostname(USER_MQTT_CLIENT_NAME);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
  digitalWrite(relayPin, HIGH); //make sure light is off if booting
//  delay(500);
//  digitalWrite(lamp, LOW);
}

// This functions is executed when some device publishes a message to a topic that your ESP8266 is subscribed to
// Change the function below to add logic to your program, so when a device publishes a message to a topic that 
// your ESP8266 is subscribed you can actually do something
void callback(String topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic, you check if the message is either on or off. Turns the lamp GPIO according to the message
  if(topic == DiningRelay){
      
      if(messageTemp == "ON" or messageTemp == "on"){
      Serial.print("Turning ON the OUTPUT for relay ");
      digitalWrite(relayPin, LOW); //Test if relay is triggered by low signal
      relayState = "ON";
      Serial.println();
      Serial.print("Relay On");
      }
      else if (messageTemp == "OFF" or messageTemp == "off"){
        Serial.print("Turning OFF the OUTPUT for relay ");
        digitalWrite(relayPin, HIGH);
        relayState = "OFF";
        Serial.println();
        Serial.print("Relay Off");
      }
  }
  
  if(topic == DiningBrightness){
      Serial.print("Changing the brightness of the LED strip ");
      Serial.println();
      dimLevelReceived = messageTemp.toInt();
      dimLevelReal = map(dimLevelReceived,0,100,0,1023);
      //dimLevelReal = dimLevelReceived; //going to use the dimLevelReal to store a value to go to when a light comes back on without goign to 100%
      if(relayState == "ON"){
        Serial.println("LED Strip is ON and Brightness is set to ");
        Serial.print(dimLevelReal);
        analogWrite(brightnessPin, dimLevelReal);
        Serial.println();
        
      }
      else if(relayState == "OFF"){
      Serial.print("Turning ON the OUTPUT for relay ");
      Serial.println();
      digitalWrite(relayPin, LOW); //Test if relay is triggered by low signal
      relayState = "ON";
      Serial.println("LED Strip now turned ON and Brightness is set to ");
      Serial.print(dimLevelReal);
      analogWrite(brightnessPin, dimLevelReal);
      Serial.println();
      }
  }
  if(topic == DiningLEDUpdate){
      Serial.println();
      Serial.print("Check OTA Status ");
      if(messageTemp == "ON" or messageTemp == "on"){
        otaUpgrade = true;
        Serial.println();
        Serial.print("OTA set to true ");
        Serial.println();
      }
      else{
        if(messageTemp == "OFF" or messageTemp == "off"){
        otaUpgrade = false;
        Serial.println();
        Serial.print("OTA set to false ");
        Serial.println();
      }
      }
  }
  
  
  Serial.println();
}

// This functions reconnects your ESP8266 to your MQTT broker
// Change the function below if you want to subscribe to more topics with your ESP8266 
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    /*
     YOU MIGHT NEED TO CHANGE THIS LINE, IF YOU'RE HAVING PROBLEMS WITH MQTT MULTIPLE CONNECTIONS
     To change the ESP device ID, you will have to give a new name to the ESP8266.
     Here's how it looks:
       if (client.connect("ESP8266Client")) {
     You can do it like this:
       if (client.connect("ESP1_Office")) {
     Then, for the other ESP:
       if (client.connect("ESP2_Garage")) {
      That should solve your MQTT multiple connections problem
    */
      if (client.connect(mqtt_client_name, mqtt_user, mqtt_pass)) 
      {
        Serial.println("connected");
        // Once connected, publish an announcement...
        if(boot == true)
        {
          client.publish(USER_MQTT_TOPIC "/" USER_MQTT_CLIENT_NAME"/checkIn","Rebooted");
          boot = false;
        }
        if(boot == false)
        {
          client.publish(USER_MQTT_TOPIC "/" USER_MQTT_CLIENT_NAME"/checkIn","Reconnected"); 
        }
        // ... and resubscribe
        client.subscribe(DiningRelay);
        client.subscribe(DiningBrightness);
        client.subscribe(DiningLEDUpdate);
        

    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// The setup function sets your ESP GPIOs to Outputs, starts the serial communication at a baud rate of 115200
// Sets your mqtt broker and sets the callback function
// The callback function is what receives messages and actually controls the LEDs
void setup() {
  pinMode(brightnessPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  
  //dht.begin();
  
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  //ArduinoOTA.setHostname(USER_MQTT_CLIENT_NAME);
    // Hostname defaults to esp8266-[ChipID]
   ArduinoOTA.setHostname("DiningLEDLamp");

  // No authentication by default
   ArduinoOTA.setPassword((const char *)"usersRgr8!!"); //sets the password
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP()); 

}

// For this project, you don't need to change anything in the loop function. Basically it ensures that you ESP is connected to your broker
void loop() {

  if (!client.connected()) {
    reconnect();
  }
  if(!client.loop())
    client.connect(mqtt_client_name ,mqtt_user, mqtt_pass);
  if(otaUpgrade == true)
  {
    ArduinoOTA.handle();  
  }
  //check state of switch input and update state on server
 now = millis(); //set up to check state every set amount of time

/* If using physical inputs on board, this section will do that work
   if (now - lastMeasure > 1000) {
    lastMeasure = now;
   //read switch pin
 Button1Read = digitalRead(Button1);  

  if (Button1Read == HIGH && Button1Last != 1)
    {
    // Publishes Switch Value
    Button1Last = 1;
    client.publish(Garageinput1, "closed");
    Serial.print("Garage is closed,input reading is HIGH");
    Serial.println();
    }
    else
    if (Button1Read == LOW && Button1Last != 0)
    {
      Button1Last = 0;
      client.publish(Garageinput1, "open");
      Serial.print("Garage is open,input reading is LOW");
      Serial.println();
    }
   }

    */



  
} 
