/* import des librairies */
#include <ESP8266WiFi.h>                // pour la gestion du wifi  
#include <ESPAsyncTCP.h>                // pour la gestion des connections TCP asynchrones 
#include <ESPAsyncWebServer.h>          // pour un serveur web asynchrone 
#include "fauxmoESP.h"                  // pour se connecter a Alexa 
#include <ArduinoOTA.h>                 // pour mettre a jour la carte à distance

/* déclaration des constantes */
#define MON_SSID        "XXXXXXX"                 // votre réseau wifi
#define MA_CLE_WIFI     "XXXXXXXXXXXXXXXXXX"      // votre clé wifi 
#define PORT_SERVEUR    8080                      // le port du serveur web  
#define PORT_FAUXMO     80                        // le port pour communiquer avec Alexa
#define HOST            "TEST_fauxmo"             // le nom pour le port réseau (OTA)
#define ID_LED          "ma led"                  // le nom pour Alexa 
#define BOUTON_PIN      D3

/* déclaration des variables */
bool flag_ledflag_bouton false;      // 2 flags, un pour le bouton et un pour l'état de la led

/* déclaration des objets */
AsyncWebServer serveur(PORT_SERVEUR);    // on définie un serveur web nommé serveur
fauxmoESP fauxmo;                        // on définie l'objet fauxmo

/* déclaration des fonctions */
// fonction qui créer la page web
String SendHTML() {
  String couleuretat;                  // on déclare 2 variables vide
  if (flag_led) {                        // si flag_led est activé
    couleur "green";                   // couleur vaut vert
    etat "ON";                         // etat vaut ON
  }
  else {                                 // si flag_led est désactivé
    couleur "red";                     // couleur vaut rouge
    etat "OFF";                        // etat vaut OFF
  }
  // on créer la page HTML avec nos variable qui se mettra à jour en temps réel
  String page "<!DOCTYPE html>\n";
  page += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  page += "<title>TEST fauxmo, serveur web et OTA</title>\n";
  page += "<script>\n";
  page += "setInterval(loadDoc,200);\n";
  page += "function loadDoc() {\n";
  page += "var xhttp = new XMLHttpRequest();\n";
  page += "xhttp.onreadystatechange = function() {\n";
  page += "if (this.readyState == 4 && this.status == 200) {\n";
  page += "document.getElementById(\"webpage\").innerHTML =this.responseText}\n";
  page += "};\n";
  page += "xhttp.open(\"GET\", \"/\", true);\n";
  page += "xhttp.send();\n";
  page += "}\n";
  page += "</script>\n";
  page += "</head>\n";
  page += "<body style='background:#C7C4C4; margin-top: 50px; text-align: center;'>\n";
  page += "<div id=\"webpage\">\n";
  page += "<h1>TEST fauxmo,serveur web et OTA</h1>\n";
  page += "<p><a href=\"on\"><button style='background:#90EE90;border:2px solid black;font-weight: bold;font-size: 20px;padding: 20px 20px;border-radius: 25px;'>LED ON</button></a>\n";
  page += "&nbsp;&nbsp;\n";
  page += "<a href=\"off\"><button style='background:#F78A8A;border:2px solid black;font-weight: bold;font-size: 20px;padding: 20px 20px;border-radius: 25px;'>LED OFF</button></a></p>\n";
  page += "<p style='font-size:2em; font-weight:bold;'>Etat de la LED : ";
  page += "<span style='color:";
  page += couleur;
  page += ";'> ";
  page += etat;
  page += "</span></p>\n";
  page += "</div>\n";
  page += "</body>\n";
  page += "</html>\n";
  return page;
}

// on crée la fonction qui donne l'état de la led
String envoyer_etat_led() {
  String etat;
  flag_led ?  etat "ON" etat "OFF";
  return etat;
}

// on crée la fonction si la page n'existe pas
void notFound(AsyncWebServerRequest *request) {
  request->send(404"text/plain""Essaye IP:8080/on ou off");
}

// on crée la fonction d'interruption
ICACHE_RAM_ATTR void handleInterrupt() {
  // on active le flag
  flag_bouton true;
  // un délai pour la sensibilité du bouton
  delay(100);
}

/* le setup */
void setup() {
  // on démarre la com série
  Serial.begin(115200);
  delay(250);
  Serial.println();
  // on déclare broche de la led interne en sortie
  pinMode(LED_BUILTINOUTPUT);
  // on déclare broche du bouton en entée avec un pullup
  pinMode(BOUTON_PININPUT_PULLUP);
  // on paramètre le wifi en mode station (connecté sur la box)
  WiFi.mode(WIFI_STA);
  // on démarre le wifi
  WiFi.begin(MON_SSIDMA_CLE_WIFI);
  // tant qu'on est pas connecté la led clignote
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    digitalWrite(LED_BUILTINLOW);
    delay(250);
    digitalWrite(LED_BUILTINHIGH);
    delay(250);
  }
  Serial.println();
  // on informe l'IP
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  // on éteint la led interne (etat inverse pour la led interne)
  digitalWrite(LED_BUILTINHIGH);
  // on envoie la fonction SendHTML() si on se connecte à la racine de l'URL (IP:8080)
  serveur.on("/"HTTP_GET, [](AsyncWebServerRequest request) {
    request->send(200"text/html"SendHTML());
  });
  // on envoie la fonction envoyer_etat_led() si on se connecte à l'onglet /etat de l'URL (IP:8080/etat)
  serveur.on("/etat"HTTP_GET, [](AsyncWebServerRequest request) {
    request->send(200"text/html"envoyer_etat_led());
  });
  // on allume la led et envoi la fonction SendHTML() à l'onglet /on de l'URL (IP:8080/on)
  serveur.on("/on"HTTP_GET, [] (AsyncWebServerRequest request) {
    flag_led true;
    fauxmo.setState(ID_LEDflag_led254);
    digitalWrite(LED_BUILTIN, !flag_led);
    Serial.printf("[WEB] Device (%s) state: %s \n"ID_LED"ON");
    request->send(200"text/html"SendHTML());
  });
  // on éteint la led et envoi la fonction SendHTML() à l'onglet /off de l'URL (IP:8080/on)
  serveur.on("/off"HTTP_GET, [] (AsyncWebServerRequest request) {
    flag_led false;
    fauxmo.setState(ID_LEDflag_led254);
    digitalWrite(LED_BUILTIN, !flag_led);
    Serial.printf("[WEB] Device (%s) state: %s \n"ID_LED"OFF");
    request->send(200"text/html"SendHTML());
  });
  // on envoie la fonction notFound si l'onglet n'existe pas
  serveur.onNotFound(notFound);
  // on démarre le serveur web
  serveur.begin();
  // on crée un serveur fauxmo
  fauxmo.createServer(true);
  // on paramètre le port
  fauxmo.setPort(PORT_FAUXMO);
  // on active le serveur
  fauxmo.enable(true);
  // on ajoute notre led comme appareil 
  fauxmo.addDevice(ID_LED);
  // la fonction de callbak quand Alexa communique
  fauxmo.onSetState([](unsigned char device_idconst char device_namebool stateunsigned char value) {
    // on affiche la commande reçu
    Serial.printf("[ALEXA] Device #%d (%s) state: %s value: %d\n"device_iddevice_namestate "ON" "OFF"value);
    // si le nom de l'appareil correspond à mon appareil (ma led)
    if (strcmp(device_nameID_LED) == 0) {
      // flag_led prend la valeur de l'état demandé
      flag_led state;
      // on allume la led en fonction de la valeur de flag_led (état inverse pour la led interne: LOW allume, HIGH éteint)
      digitalWrite(LED_BUILTINflag_led LOW HIGH);
    }
  });
  // on envoie l'info à Alexa que la led est éteinte ( valeur de 0 à 254, ici on laisse à 100% ) 
  fauxmo.setState(ID_LEDflag_led254);
  // on configure le port réseau pour l' OTA
  ArduinoOTA.setPort(8266);
  // on configure le nom du host pour l' OTA
  ArduinoOTA.setHostname(HOST);
  // on démarre le serveur OTA
  ArduinoOTA.begin();
  // on met une interruption sur le bouton de type descendante associé à la fonction handleInterrupt
  attachInterrupt(digitalPinToInterrupt(BOUTON_PIN), handleInterruptFALLING);
}

/* le loop */
void loop() {
  // on écoute le serveur fauxmo
  fauxmo.handle();                              
  // on écoute le serveur OTA  
  ArduinoOTA.handle();                         
  // si le bouton a été activé
  if (flag_bouton) {                            
    // on change l'état de la led
    flag_led = !flag_led;
    // on applique ce changement
    digitalWrite(LED_BUILTIN, !flag_led);
    // on informe que le bouton a été activé et l'état de la led
    Serial.printf("[BOUTON] interruption détécté --> %s\n"flag_led "LED ON" "LED OFF");
    // on envoie à Alexa le nouvel état de la led
    fauxmo.setState(ID_LEDflag_led254);
    // on désactive le flag
    flag_bouton false;
  }
}