#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiManager.h>
#include <EEPROM.h>
#include <DHTesp.h>
#include <FS.h>

// Structures de données
struct ParVal
{
  String param;
  String value;
};

struct Conf_WHP {
  char      devname[16] = "";
  char      ip_ipx[16] = "";
  char      api_key[16] = "";
};

// Version du firmware
const String FIRMWARE_VERSION = "PatLeHibou's WiFi Heater Pilot v.2";
String FilePath = __FILE__; // Récupère le chemin et le nom du fichier source
String FileName; // Recevra le nom du fichier seul, sans extension, permettant d'avoir la version précise de la release

// Suivi de version
// v.1.0 : première version complètement opérationnelle basée sur la version v.0.6d de développement
//         l'état du fil pilote, la luminosité, la présence ou non du capteur DHT ainsi que le nom du device sont conservés dans l'EEPROM et rechargé en cas de reboot (après coupure par ex.)
// V.2.0 : deuxième version intégrant les remontées d'informations vers l'IPX
//         2.0a : prend en charge les paramètres de connexion à l'IPX et pour la surveillance de la T° et l'hygrométrie, et envoie les requêtes HTTP vers l'IPX
//         2.1a : version faisant appel à SPIFFS pour solliciter moins l'EEPROM lors des changements d'état
//         2.1b : utilisation de la librairie DHTesp
//         2.2b : modification de la structure du JSON pour avoir une arborescence des paramètres de th_control et introduction d'un sous-paramètre "status" (même modif qu'en 2.1a et 2.2a)
//         2.2c : si la valeur de DeviceConfig lue au démarrage est 0xFF (valeur usuelle avec une carte WeMos neuve) DeviceConfig est forcé à 0
//         2.5  : évolution majeure dans la gestion du Wifi en tirant parti de la mémorisation du SSID et du Password directement par l'ESP
//                suppression de ces informations dans la structure de données rebaptisée Conf_WHP (pour WiFi Heater Pilot)
//                suppression de la mémorisation d'un accès dans le byte DeviceConfig réduit à gérer la présence du capteur DHT
//                suppression du byte qui jusqu'à la version 2.0a servait à mémoriser l'état du Fil Pilote
//                nettoyage du code (suppression des commentaires inutiles, ajouts, corrections de commentaires...)
//         2.5a : ajout de la surveillance de la connexion WiFi (extinction LED Link si perte du réseau)
//                avec auto-reconnexion en cas de perte du réseau
//         2.6  : remplacement de la procédure d'initialisation via requêtes HTTP par celle de WiFiManager
//         2.6a : modification du réglage de luminosité des LEDs pour éviter le scintillement
//                luminosité fixée à 1000 si valeur demandée supérieure
//                ajout d'un Wipe de l'EEPROM si bouton Hard Reset enfoncé au démarrage (les LEDs Link et Rouge cligntotent alternativement). Attention : luminosité fixée à 0 après
//                mise en commentaire de la ligne "if (Etat.substring(0, 3).equalsIgnoreCase("EP=")) {Val=9;}" pour désactiver les fonctions de Dump, Wipe et Fill sur l'EEPROM
//         2.7  : ajout du retour de la température et de l'humidité dans des entrées virtuelles analogiques de l'IPX
//         2.7a : correction d'un bug empêchant de désactiver le capteur DHT (oubli de modifier un test suite au passage en 2.6) et d'un autre dans la réponse en JSON lors de l'entrée de la configuration de l'IPX (virgule manquante)
//         2.7b : modification de la gestion de la luminosité des LEDs : gestion individuelle des 3 valeurs R, V et B et comptage pour 256 tours de boucle au lieu de 1000 (et modifications relatives)
//
//-------------------------------------------------------//
// LISTE DES COMMANDES VALIDES POUR LE PILOTAGE VIA HTTP //
//-------------------------------------------------------//
// CF : Mode 0 fait passer le Fil Pilote en mode Confort (pas de tension)
// EC : Mode 1 fait passer le Fil Pilote en mode Eco (alternance complète)
// HG : Mode 2 fait passer le Fil Pilote en mode Hors-Gel (1/2 alternance négative)
// AR : Mode 3 fait passer le Fil Pilote en mode Arrêt ou Délestage (1/2 alternance positive)
// ?  : retourne l'état du dispositif, l'état du DHT avec T° et Hygrométrie s'il est activé, la valeur de la luminosité
// VE : retourne la version du Firmware et celle du fichier source
// LUM : suivi de '?' et un ou trois paramètres sous la forme
//   - soit RVB=Valeur : luminosité identique pour les 3 LEDs, valeur entre 0 (extinction complète) et 255 (luminosité max)
//   - soit R=Valeur_R&V=Valeur_V&B=Valeur_B : luminosité différenciée pour les 3 LEDs, valeurs entre 0 (extinction complète) et 255 (luminosité max)
// NM : suivi de '=' et un mot, renomme le dispositif (éviter les espaces qui seront traduits en %22)
// TH : suivi de '=' et ON pour activer le capteur DHT s'il est présent, OFF (ou n'importe quoi d'autre) pour le désactiver
// A partir de v.2.0
// EP : suivi de '=' et DUMP pour voir le contenu de l'EEPROM en Hexa et en ASCII, WIPE pour vider l'EEPROM (mise à 0 de toutes les cellules)
// IPX_CFG : suivi de '?' et une suite de paramètres sous la forme IP_IPX=AddrIP_IPX&APIKey=apikey, si APIKey est omis elle est fixée à "" et l'APIkey n'est pas utilisée
// CTRL_TH : suivi de '?' et une suite de paramètres sous la forme PERIOD=Périodicité_en_Minutes&T_MINI=Val_T°_mini&T_MAXI=Val_T°_maxi&SV_TE=Num_SV_T°&H_MINI=Val_Hum_mini&H_MAXI=Val_Hum_maxi&SV_HU=Num_SV_Hum
//   - Si PERIOD=0, inhibe la surveillance et les autres paramètres peuvent être omis (ne sont pas évalués)
//   - PERIOD doit être inférieure à 1440 soit une journée
//   - T_MINI doit être compris entre 0 et 20
//   - T_MAXI doit être compris entre 0 et 30
//   - T_MINI doit être inférieur à T_MAXI
//   - H_MINI doit être compris entre 0 et 99
//   - H_MAXI doit être compris entre 0 et 99
//   - H_MINI doit être inférieur à H_MAXI
//   - SV_TE et SV_HU doivent être comprises entre 1 et 128, si 0, n'est pas utilisée, les 2 peuvent être égales pour conduire au même comportement
// RTRN_TH : suivi de '?' et une suite de paramètres sous la forme PERIOD=Périodicité_en_Minutes&VA_TE=Num_VA_T°&VA_HU=Num_VA_Hum
//   - Si PERIOD=0, inhibe la surveillance et les autres paramètres peuvent être omis (ne sont pas évalués)
//   - PERIOD doit être inférieure à 1440 soit une journée
//   - VA_TE et VA_HU doivent être comprises entre 1 et 32, si 0, n'est pas utilisée, les 2 ne peuvent être égales (conflit de remontées)

// toutes ces commandes retournent une réponse en JSON
// toute autre valeur retournera un message "status" : "error"

// Constantes

// Structure du contenu de l'EEPROM
const uint16_t EEPROM_SIZE=512; // Taille utilisée de l'EEPROM
const uint8_t FLAG_DEVICECONFIG=0; // Byte 0 Configuration du device 1er bit  = DHT actif ou non
const uint8_t LUM_VALUE=1; // Byte 1, 2 et 3 Valeur des luminosité Rouge, Verte et Bleue (3 x uint8_t : 3 x 1 octet)
const uint8_t PERIODE_CTRL=4; // Byte 4 et 5 Valeur de la périodicité de contrôle de franchissement des seuils de T° et d'Humidité
const uint8_t T_MIN=6; // Byte 6 Valeur de T° mini
const uint8_t T_MAX=7; // Byte 7 Valeur de T° maxi
const uint8_t SV_TE=8; // Byte 8 N° de la Sortie Virtuelle de l'IPX recevant les changements d'état liés à la T° (1 si T° < seuil mini, 0 si T° > seuil maxi)
const uint8_t H_MIN=9; // Byte 9 Valeur d'humidité mini
const uint8_t H_MAX=10; // Byte 10 Valeur d'humidité maxi
const uint8_t SV_HU=11; // Byte 11 N° de la Sortie Virtuelle de l'IPX recevant les changements d'état liés à l'humidité (0 si Humi < seuil mini, 1 si Humi > seuil maxi)
const uint8_t PERIODE_RTRN=12; // Byte 12 et 13 Valeur de la périodicité de mesure de la T° et de l'Humidité
const uint8_t VA_TE=14; // Byte 14 N° de l'Entrée Virtuelle Analogique de l'IPX recevant la température (doit être différente de VA_HU)
const uint8_t VA_HU=15; // Byte 15 N° de l'Entrée Virtuelle Analogique de l'IPX recevant la température (doit être différente de VA_TE)

// Les bytes 16 à 31 sont réservés pour un usage ultérieur
const uint8_t POS_DEVICECONFIG=32; // Byte 32 et au-delà : Structure de donnée mémorisant la configuration du device (Nom, Infos IPX...)

// Nom du fichier contenant le statut du radiateur
const String STATUS_FILENAME = "/status.dat";

// Commandes de configuration du boitier
// Nb max de paramètres dans les commandes
const uint8_t MAX_PARAMS = 10; // Nb de paramètres maximum pour dimensionnement du tableau (7 utilisés pour CTRL_TH)

// Statut par défaut du fil pilote à l'initialisation de la connexion au WiFi (0 = Confort, 1 = Eco, 2 = Hors Gel, 3 = Arrêt)
const uint8_t DEFAULT_PILOTE_STATUS = 1; // Mode par défaut Eco de manière à ce que la LED passe en orange après entrée d'une configuration valide
                                         // Cycle à l'initialisation du dispositif :
                                         // Clignotement lent rouge : attend qu'une station se connecte sur l'adresse IP par défaut
                                         // Clignotement lent vert  : une station est connecté sur l'AP, attend une commande de configuration
                                         // Vert fixe               : commande reçue en cours d'interprétation (la LED bleue clignote pour indiquer les tentatives d'accès au WiFi, 20 clignotements rapides = réseau rejoint)
                                         // Rouge de courte durée   : le réseau n'a pu être rejoint, réinitialisation du dispositif
                                         // Orange (et bleue) fixe  : l'appareil est connecté au réseau WiFi et peut être piloté

// Constantes d'affectation des broches aux fonctions (à modifier en cas de changement d'utilisation des broches)
const uint16_t ALT_POS = D8; // Broche pilotant l'alternance positive
const uint16_t ALT_NEG = D7; // Broche pilotant l'alternance négative
const uint16_t LED_ROUGE = D1; // Broche pilotant la LED rouge de la LED Bicolore
const uint16_t LED_VERTE = D2; // Broche pilotant la LED verte de la LED Bicolore
const uint16_t LED_LINK = D4; // Broche pilotant la LED Link WiFi bleue
const uint16_t MODE_BUTTON = D6; // Broche recevant le bouton de changement de mode
const uint16_t HARD_RESET = D5; // Broche recevant le bouton de Hard Reset
const uint16_t SENSOR_PIN = D3; // Broche sur laquelle le capteur de T° et d'humidité est branché (s'il est présent)

// Autres constantes de base
const uint16_t LUMIN_BASE = 10; // Valeur entre 0 et 1000 spécifiant le ‰ de temps que les LEDs doivent être allumées par boucle loop (si 0 : complètement éteintes, mode discret)
const uint16_t NB_TENTATIVES = 20; // Nombre de tentatives de lecture de la T° et de l'hygrométrie
const String SEPARATEUR= "------------------------------------------------------------------------------------------------------------";

// Création de l'instance dht
DHTesp dht;

// Paramètres du périphérique
// MAC adress *** ATTENTION la WiFi.softAPmacAddress() n'a pas la même valeur que celle de connexion au réseau ***
const String  MACaddress = WiFi.macAddress();

// Nom du réseau Ad Hoc de configuration : Pilote_ les 6 derniers octets de l'adresse Mac de la carte en hexa
const String  AP_Network = "Pilote_" + MACaddress.substring(9, 11) + MACaddress.substring(12, 14) + MACaddress.substring(15, 17);

// Port d'écoute du serveur Web
WiFiServer server(80);

// Variables de gestion du WiFi et des échanges HTTP
boolean     WiFiConnected; // Booléen mémorisant l'état de la conexion WiFi
char        ssid[32] = ""; // SSID
char        pass[64] = ""; // Mot de passe d'accès au WiFi
char        devname[16] = ""; // Nom donné au device
char        ip_ipx[16] = ""; // Adresse Ip de l'IPX
char        api_key[16] = ""; // APIkey de l'IPX
String      ClientRequest;
String      RequeteAnalyse;
String      Method;
String      Command;
ParVal      Params[MAX_PARAMS];
boolean     CdeOK;
uint16_t    DeviceConfig = 0; // 1er bit : Flag pour indiquer la présence ou non du capteur DHT
Conf_WHP    ConfWHP; // Infos de config du boitier en EEPROM
File        Status_File;
String      Nom_Dispositif;
String      Commande;
short       Num_Cde, Num_Cde_Recue;
String      HttpResponse;

// Variables pour le capteur DHT
boolean     Capteur_DHT; // Vrai si actif (suppose qu'il soit présent)
float       Temp;
float       Humi;
uint16_t    Num_Tentative; // Numéro de la tentative de lecture

// Variables pour la gestion de la luminosité des LEDs
uint8_t     Luminosite_R = 4; // Valeur de luminosité rouge en cours
uint8_t     Luminosite_V = 4; // Valeur de luminosité verte en cours
uint8_t     Luminosite_B = 4; // Valeur de luminosité bleue en cours
uint16_t    Val_Lum_Dde; // Valeur de luminosité demandée
uint16_t    Cpt_Lum; // Compteur de boucle pour la luminosité des LEDs

// Variables intermédiaires
char        Char_Tab[16]; // Tableau de char pour conversion String to int

// Variables pour la connexion à un IPX800
String      IP_IPX = "";
String      API_Key = "";
String      IPX_API_Prefix = "";
String      IPX_API_Command = "";

// Variables pour la surveillance de la T° et de l'Humidité
int         T_min = 10;
int         T_Max = 12;
int         H_min = 70;
int         H_Max = 80;
int         SV_Te = 0;
int         SV_Hu = 0;
short       Periode_Ctrl = 0;

// Variables pour la remontée périodique de la T° et de l'Humidité
int         VA_Te = 0;
int         VA_Hu = 0;
short       Periode_Rtrn = 0;
short       CAN_Te, CAN_Hu; // Variable recevant la conversion de la T° et de l'humidité mesurée sur 16 bits
                            // CAN_Te = (Temp+40)/120*65535
                            // CAN_Hu = Humi/100*65535

// Variables recevant les nouvelles valeurs demandées
int         New_T_min, New_T_Max, New_H_min, New_H_Max, New_SV_Te, New_SV_Hu, New_VA_Te, New_VA_Hu;
int         New_Periode_Ctrl, New_Periode_Rtrn; // Plantage du module lors du sscanf dans le case CTRL_TH si short au lieu de int !!!

// Fonction de requête HTTP GET
String GetWithAnswer(String IP_Addr, String Commande) {
  String      httpurl;
  String      ClientAnswer;
  HTTPClient  http;
  httpurl = "http://";
  httpurl += IP_Addr;
//  httpurl+="/"; supprimé car le massage contient un "/" en début de chaîne
  httpurl += Commande;
  Serial.println(httpurl);
  http.begin(httpurl);
  http.GET();
  ClientAnswer = (http.getString());
  http.end();
  return ClientAnswer;
}

// Cette fonction extrait les caractères d'une chaine jusqu'à un symbole de séparation spécifié, une espace ou la fin de la chaine
String ExtractStr(String Entree, char Separateur) {
  uint16_t i = 0;
  boolean finChaine = false;
  String Sortie = "";
  while (Entree[i] != Separateur && Entree[i] != ' ' && i != Entree.length()) {
    Sortie += Entree[i];
    i++;
  }
  return Sortie;
}

// Cette fonction extrait les couples (Paramètre, Valeur) présents dans une chaîne de la forme param1=value1&param2&value2&... à concurrence de NB_PARAMS
//void ExtractParamsValues (String Requete, ParVal ParsVals[NB_PARAMS]) {
void ExtractParamsValues (String Requete, ParVal ParsVals[], uint8_t Nb_Params) {
  uint8_t i;
  // Initialisation à vide du tableau des paramètres
  for (i = 0; i < Nb_Params ; i++) {
    ParsVals[i].param = "";
    ParsVals[i].value = "";
  }
  // Extrait les paramètres et les valeurs séparés par = et &
  String ChAnalyse = Requete;
  i = 0;
  while ((ChAnalyse != "") && (i < Nb_Params)) {
    ParsVals[i].param = ExtractStr (ChAnalyse, '=');
    ChAnalyse = ChAnalyse.substring(ParsVals[i].param.length() + 1);
    ParsVals[i].value = ExtractStr (ChAnalyse, '&');
    ChAnalyse = ChAnalyse.substring(ParsVals[i].value.length() + 1);
    // Suppression de la " éventuelle en début de chaîne
    if (ParsVals[i].value[0] == '"') {
      ParsVals[i].value = ParsVals[i].value.substring(1);
    }
    // Suppression du %22 (") éventuel en début de chaîne
    if (ParsVals[i].value.substring(0, 3) == "%22") {
      ParsVals[i].value = ParsVals[i].value.substring(3);
    }
    // Suppression de la " éventuelle en fin de chaîne
    if (ParsVals[i].value[ParsVals[i].value.length() - 1] == '"') {
      ParsVals[i].value = ParsVals[i].value.substring(0, ParsVals[i].value.length() - 1);
    }
    // Suppression du %22 (") éventuel en fin de chaîne
    if (ParsVals[i].value.substring(ParsVals[i].value.length() - 3) == "%22") {
      ParsVals[i].value = ParsVals[i].value.substring(0, ParsVals[i].value.length() - 3);
    }
    i++;
  }
}

// Cette fonction teste l'appui sur le bouton Hard Reset
void Test_Hard_Reset () {
  if (digitalRead(HARD_RESET) == 0) {
    Serial.println();
    Serial.println(F("**************************************"));
    Serial.println(F("* HARD RESET, REINITIALISATION EN AP *"));
    Serial.println(F("**************************************"));
    // Lors d'un Hard Reset, déconnecte d'un éventuel réseau WiFi et efface un SSID précédemment mémorisé
    Serial.print(F("Lecture du SSID mémorisé : "));
    Serial.println(WiFi.SSID());
    WiFi.disconnect();
    // Réinitialisation des paramètres
    DeviceConfig = 0; // Pas de capteur DHT
    Luminosite_R = 4; // Luminosité rouge à 4/256
    Luminosite_V = 4; // Luminosité verte à 4/256
    Luminosite_B = 4; // Luminosité bleue à 4/256
    Nom_Dispositif = AP_Network; // Nom du dispositif au nom par défaut du boitier
    Nom_Dispositif.toCharArray(devname, 16);
    for (uint8_t i = 0; i < 16 ; i++) ConfWHP.devname[i] = devname[i];
    IP_IPX = ""; // Mise à "" de l'adresse IP de l'IPX
    IP_IPX.toCharArray(ip_ipx, 16);
    for (uint8_t i = 0; i < 16 ; i++) ConfWHP.ip_ipx[i] = ip_ipx[i];
    API_Key = ""; // Mise à "" de l'APIkey de l'IPX
    API_Key.toCharArray(api_key, 16);
    for (uint8_t i = 0; i < 16 ; i++) ConfWHP.api_key[i] = api_key[i];
    Periode_Ctrl = 0; // Mise à 0 de la périodicité de contrôle du climat (la désactive)
    Periode_Rtrn = 0; // Mise à 0 de la périodicité de remontée du climat (la désactive)

    // Mémorisation dans l'EEPROM
    EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig);
    EEPROM.write(LUM_VALUE,Luminosite_R);
    EEPROM.write(LUM_VALUE+1,Luminosite_V);
    EEPROM.write(LUM_VALUE+2,Luminosite_B);
    EEPROM.put(POS_DEVICECONFIG,ConfWHP);
    EEPROM.put(PERIODE_CTRL, Periode_Ctrl);
    EEPROM.put(PERIODE_RTRN, Periode_Rtrn);
    EEPROM.end();
    
    delay(1000); // délai pour la mémorisation de l'effacement
    Serial.print(F("Lecture du SSID après réinitialisation : "));
    Serial.println(WiFi.SSID());
    // Fait rebooter l'équipement
    ESP.restart();
  }
}

// Cette fonction fait clignoter la led additionnelle sur la GPIO 2 (broche D4) et la led intégrée à l'inverse l'une de l'autre.
// La durée d'allumage et d'extinction est donnée par delai en 1000ème de seconde
void ClignoteLED(uint8_t nbre, long delai) {
  uint8_t i;
  nbre = abs (nbre);
  for (i = 1; i <= nbre ; i++) {
    digitalWrite(LED_LINK, LOW);
    delay(delai);
    digitalWrite(LED_LINK, HIGH);
    delay(delai);
  }
}

// **************************************************************
// ******** Fonctions propres au pilotage d'un radiateur ********
// **************************************************************

// Cette fonction donne le n° correspondant à l'état demandé : 0=Confort, 1=Eco, 2=Hors-Gel, 3=Arrêt
// ou à la commande passée : 4=Status, 5=Version de Firmware, 6=Réglage de la luminosité, 7=Renommage du device,
// 8=activation/désactivation DHT, 9=Dump/Wipe, 10=Configuration IPX, 11=Paramètres de surveillance de la T° et de l'hygrométrie
int NumEtat(String Etat) {
  int Val = -1;
  if (Etat.equalsIgnoreCase("CF")) {Val=0;}
  if (Etat.equalsIgnoreCase("EC")) {Val=1;}
  if (Etat.equalsIgnoreCase("HG")) {Val=2;}
  if (Etat.equalsIgnoreCase("AR")) {Val=3;}
  if (Etat.equalsIgnoreCase("?"))  {Val=4;}
  if (Etat.equalsIgnoreCase("VE")) {Val=5;}
  if (Etat.substring(0, 4).equalsIgnoreCase("LUM?")) {Val=6;}
  if (Etat.substring(0, 3).equalsIgnoreCase("NM=")) {Val=7;}
  if (Etat.substring(0, 3).equalsIgnoreCase("TH=")) {Val=8;}
//  if (Etat.substring(0, 3).equalsIgnoreCase("EP=")) {Val=9;}
  if (Etat.substring(0, 8).equalsIgnoreCase("IPX_CFG?")) {Val=10;}
  if (Etat.substring(0, 8).equalsIgnoreCase("CTRL_TH?")) {Val=11;}
  if (Etat.substring(0, 8).equalsIgnoreCase("RTRN_TH?")) {Val=12;}

  return Val;
}

// Cette fonction pilote le changement d'état des sorties, de la LED bicolore d'état et le mémorise dans le fichier d'état
void Pilote (int Status) {
    switch (Status) {
      case 0 : // aucune alternance en sortie, LED allumée en rouge : Confort
        digitalWrite(ALT_NEG,LOW);
        digitalWrite(ALT_POS,LOW);
        digitalWrite(LED_ROUGE,HIGH);
        digitalWrite(LED_VERTE,LOW);
        break;
      case 1 : // pleine alternance en sortie, LED allumée en orange (rouge+vert) : Eco
        digitalWrite(ALT_NEG,HIGH);
        digitalWrite(ALT_POS,HIGH);
        digitalWrite(LED_ROUGE,HIGH);
        digitalWrite(LED_VERTE,HIGH);
        break;
      case 2 : // demie alternance négative en sortie, LED allumée en vert : Hors Gel
        digitalWrite(ALT_NEG,HIGH);
        digitalWrite(ALT_POS,LOW);
        digitalWrite(LED_ROUGE,LOW);
        digitalWrite(LED_VERTE,HIGH);
        break;
      case 3 : // demie alternance positive en sortie, LED éteinte : Arrêt
        digitalWrite(ALT_NEG,LOW);
        digitalWrite(ALT_POS,HIGH);
        digitalWrite(LED_ROUGE,LOW);
        digitalWrite(LED_VERTE,LOW);
        break;
   }
    // Mémorisation dans le fichier
    Serial.print(F("Status écrit : "));
    Serial.println(Status);
    Status_File = SPIFFS.open(STATUS_FILENAME, "w+");
    Status_File.write(Status);
    Status_File.seek(0, SeekSet);
    Serial.print(F("Status relu : "));
    Serial.println(Status_File.read());
    Status_File.close();
}

//******************************************************************************************************************//
//                                                  Setup                                                           //
//******************************************************************************************************************//

void setup()
{
  // Affiche les communications sur le port série
  Serial.begin(9600);

  Serial.println();
  Serial.println(F("Initialisation du système"));

  // Initialisation du WiFiManager
  WiFiManager wifiManager;

  // Extraction du nom du fichier source en tant que version de Firmware
  reverse(FilePath.begin(),FilePath.end());
  FileName = ExtractStr(FilePath.substring(4),'/');
  reverse(FileName.begin(),FileName.end());
  
  // Déclaration des broches utilisées
  // LED Link
  pinMode(LED_LINK, OUTPUT);
  // Alternance -
  pinMode(ALT_NEG, OUTPUT);
  // Alternance +
  pinMode(ALT_POS, OUTPUT);
  // LED bicolore côté Rouge
  pinMode(LED_ROUGE, OUTPUT);
  // LED bicolore côté Vert
  pinMode(LED_VERTE, OUTPUT);
  // Bouton de changement de mode
  pinMode(MODE_BUTTON, INPUT);
  // Bouton Hard Reset
  pinMode(HARD_RESET, INPUT);

  // Charge l'EEPROM en RAM et lit le premier octet pour savoir si un capteur DHT est déclaré présent
  EEPROM.begin(EEPROM_SIZE);

  // Si bouton Hard Reses appuyé, Wipe l'EEPROM
  if (digitalRead(HARD_RESET) == 0) { 
    Serial.println(F("Hard Reset appuyé, relachez pour poursuivre."));
    while (digitalRead(HARD_RESET) == 0) { // Attend que Hard Reset soit relâché pour éviter un reset de la carte
      digitalWrite(LED_LINK,LOW);
      digitalWrite(LED_ROUGE,HIGH);
      delay(250);
      digitalWrite(LED_LINK,HIGH);
      digitalWrite(LED_ROUGE,LOW);
      delay(250);      
    }
    Serial.println(F("Wipe de l'EEPROM"));
    for (uint16_t x = 0; x < EEPROM_SIZE; x++) {    //Loop end of EEPROM address
      if (!(EEPROM.read(x) == 0)) {
        EEPROM.write(x, 0);       // if not 0 write 0 to clear, it takes 3.3mS ?
      }
      yield();
    }
    EEPROM.commit();
    Serial.println(F("Wipe terminé, vérification..."));
    // Dump en Hexa et en ASCII
    Serial.println(F("Dump de l'EEPROM"));
    for (uint16_t i = 0 ; i < (EEPROM_SIZE/16) ; i++) {
      for (uint8_t j = 0; j < 16 ; j++) {
        Serial.print(EEPROM.read(i*16+j) < 0x10 ? "0" : "");
        Serial.print(EEPROM.read(i*16+j), HEX);
        Serial.print(F(" "));
      }
      for (uint8_t j = 0; j < 16 ; j++) {
        Serial.print((EEPROM.read(i*16+j) < 0x20) || (EEPROM.read(i*16+j) > 0x7F) ? '^' : char(EEPROM.read(i*16+j)));
      }            
      Serial.println(F(""));
      yield();
    }
  }
  
  // lit le premier octet pour savoir si un capteur DHT est déclaré présent
  DeviceConfig = EEPROM.read(FLAG_DEVICECONFIG);
  Serial.print(F("Lecture du DeviceConfig dans l'EEPROM : "));
  Serial.println(DeviceConfig);

  // Lecture du SSID mémorisé par l'ESP8266
  Serial.print(F("Lecture du SSID mémorisé : "));
  Serial.println(WiFi.SSID());
  
  //Démarrage du File System
  SPIFFS.begin();
  Serial.println(F("Démarrage du File System"));
  Serial.println(SEPARATEUR);

  // Initialisation de la connexion si pas de SSID connu 
  if (WiFi.SSID() == "") {
    digitalWrite(LED_LINK,HIGH);
    digitalWrite(LED_ROUGE,HIGH);
    AP_Network.toCharArray(Char_Tab,14);
    wifiManager.autoConnect(Char_Tab);
  }
 
  //------------------------------------------------------------------------//
  // Une configuration valide a été entrée et l'accès au WiFi a été vérifié //
  //------------------------------------------------------------------------//

  // Récupère les infos de luminosité et de configuration du boitier
  Luminosite_R = EEPROM.read(LUM_VALUE);
  Luminosite_V = EEPROM.read(LUM_VALUE+1);
  Luminosite_B = EEPROM.read(LUM_VALUE+2);
  EEPROM.get(POS_DEVICECONFIG, ConfWHP);
  Serial.print(F("Device Config : "));
  Serial.println(EEPROM.read(FLAG_DEVICECONFIG));
  // Initialisation dans le mode précédemment mémorisé
  // Vérification de l'existence du fichier status.dat
  if (!SPIFFS.exists(STATUS_FILENAME)) {
    // Le crée et l'initialise avec la valeur par défaut s'il n'existe pas
    Status_File = SPIFFS.open(STATUS_FILENAME, "w");
    Status_File.write(DEFAULT_PILOTE_STATUS);
    Status_File.close();
  }
  Status_File = SPIFFS.open(STATUS_FILENAME, "r");
  Num_Cde = Status_File.read();
  Status_File.close();
  Serial.print(F("Statut du radiateur (valeur lue dans le fichier Status) : "));
  Serial.println(Num_Cde);

  // Bascule du radiateur dans son état antérieur
  Pilote(Num_Cde);

  // Définit les paramètres du capteur et positionne le booléen Capteur_DHT en fonction de la valeur du 1er bit de DeviceConfig
  dht.setup(SENSOR_PIN,DHTesp::DHT22);
  Capteur_DHT = DeviceConfig%2;
  if (Capteur_DHT) {
    // Effectue une première lecture de T° et d'Humidité pour que la première en surveillance ne soit pas à 0
    Temp = dht.getTemperature( );
    Humi = dht.getHumidity();
  }

  // Nomme le périphérique
  Nom_Dispositif = ConfWHP.devname;
  if (Nom_Dispositif != "") WiFi.hostname(Nom_Dispositif);
  Nom_Dispositif = WiFi.hostname();

  // Connecte au WiFi
  Serial.print(F("Recherche réseau WiFi : "));
  Serial.println(WiFi.SSID());
  WiFi.begin();
  while ((!(WiFi.status() == WL_CONNECTED))) {
    delay(300);
    Serial.print(F("."));
    ClignoteLED(2, 30);
    digitalWrite(LED_LINK, LOW);
    Test_Hard_Reset();
  }
  WiFiConnected = true; // Passe le booléen à vrai
  WiFi.setAutoReconnect(true); // Permet la reconnexion automatique en cas de perte du réseau
  Serial.println();
  Serial.print(F("Connecté au WIFI : "));
  Serial.println(WiFi.SSID());
  Serial.print(F("Puissance du signal "));
  Serial.print(WiFi.RSSI());
  Serial.println(F(" dBm"));
  ClignoteLED(5, 250);

  // Démarrage du Serveur Web
  server.begin();

  // Affichage des informations sur le port série
  Serial.println(F("Serveur Web démarré"));
  Serial.print(F("L\'adresse IP du dispositif est "));
  Serial.println((WiFi.localIP().toString()));
  Serial.print(F("Le nom du dispositif est "));
  Serial.println((WiFi.hostname()));
  Serial.print(F("La version du firmware est "));
  Serial.println(FIRMWARE_VERSION);
  Serial.print(F("Le fichier source est "));
  Serial.println(FileName);
  Serial.print(F("La luminosité RVB est "));
  Serial.print(Luminosite_R);
  Serial.print(F(", "));
  Serial.print(Luminosite_V);
  Serial.print(F(", "));
  Serial.println(Luminosite_B);
  
  Serial.print(F("Le capteur DHT est à "));
  Serial.println(Capteur_DHT);
  
  // Charge et affiche les paramètres de surveillance de la T° et de l'humidité
  IP_IPX = ConfWHP.ip_ipx;
  if (IP_IPX != "") {
    Serial.print(F("IP IPX800 : "));
    Serial.println(IP_IPX);
    IPX_API_Prefix = "/api/xdevices.json?";
    if (ConfWHP.api_key != "") {
      IPX_API_Prefix = IPX_API_Prefix+"key="+ConfWHP.api_key+"&";
    }
    Serial.print(F("Préfixe des requêtes HTTP pour IPX : http://"));
    Serial.println(IP_IPX+IPX_API_Prefix);
  }
  T_min = EEPROM.read(T_MIN);
  T_Max = EEPROM.read(T_MAX);
  H_min = EEPROM.read(H_MIN);
  H_Max = EEPROM.read(H_MAX);
  SV_Te = EEPROM.read(SV_TE);
  SV_Hu = EEPROM.read(SV_HU);
  Periode_Ctrl = EEPROM.read(PERIODE_CTRL);
  if (Periode_Ctrl == 0) {
    Serial.println(F("Surveillance T° et Hygrométrie désactivée, périodicité à 0"));
  } else {
    Serial.println(F("Surveillance T° et Hygrométrie activée"));
    Serial.print(F("Périodicité : "));
    Serial.print(Periode_Ctrl);
    Serial.println(F(" minutes"));
    Serial.print(F("T° mini : "));
    Serial.print(T_min);
    Serial.println(F(" °C"));
    Serial.print(F("T° Maxi : "));
    Serial.print(T_Max);
    Serial.println(F(" °C"));
    Serial.print(F("La sortie virtuelle "));
    Serial.print(SV_Te);
    Serial.println(F(" est sélectionnée pour la surveillance de la T°"));
    Serial.print(F("Hygro mini : "));
    Serial.print(H_min);
    Serial.println(F(" %"));
    Serial.print(F("Hygro Maxi : "));
    Serial.print(H_Max);
    Serial.println(F(" %"));
    Serial.print(F("La sortie virtuelle "));
    Serial.print(SV_Hu);
    Serial.println(F(" est sélectionnée pour la surveillance de l'hygrométrie"));
  }
  VA_Te = EEPROM.read(VA_TE);
  VA_Hu = EEPROM.read(VA_HU);
  Periode_Rtrn = EEPROM.read(PERIODE_RTRN);
  if (Periode_Ctrl == 0) {
    Serial.println(F("Remontée T° et Hygrométrie désactivée, périodicité à 0"));
  } else {
    Serial.println(F("Remontée T° et Hygrométrie activée"));
    Serial.print(F("Périodicité : "));
    Serial.print(Periode_Rtrn);
    Serial.println(F(" minutes"));
    Serial.print(F("L'entrée analogique virtuelle "));
    Serial.print(VA_Te);
    Serial.println(F(" est sélectionnée pour la remontée de la T°"));
    Serial.print(F("L'entrée analogique virtuelle "));
    Serial.print(VA_Hu);
    Serial.println(F(" est sélectionnée pour la remontée de l'hygrométrie"));
  }
  Serial.println(F("Pilote correctement démarré, en attente de commande..."));
  Serial.println(SEPARATEUR);
}

//******************************************************************************************************************//
//******************************************************************************************************************//
//                                                  Loop                                                           //
//******************************************************************************************************************//
//******************************************************************************************************************//

void loop()
{
  // Test du bouton Hard Reset
  Test_Hard_Reset();
  
  // Test du bouton de changement d'état
  if (digitalRead(MODE_BUTTON) == 0) {
      delay(10);// Attend 10 ms que l'état du bouton soit stabilisé (pour éviter les commandes multiples en cas de rebond du poussoir)
      Serial.println();
      Serial.print(F("Passage d'état "));
      Serial.print(Num_Cde);
      Num_Cde = (Num_Cde+1)%4;
      Serial.print(F(" à "));
      Serial.println(Num_Cde);
      Pilote(Num_Cde);
      while (digitalRead(MODE_BUTTON) == 0) { // Attend que le bouton soit relâché
        delay(1);
      }
      delay(10);// Attend 10 ms que l'état du bouton soit stabilisé (pour éviter les commandes multiples en cas de rebond du poussoir lors de sa réouverture)
  }

  // Teste une fois toutes les 10 secondes si le WiFi est bien connecté
  if ((millis()%10000) == 0) {
    WiFiConnected = WiFi.isConnected();
  }

  // Clignotement des LEDs pour réduire l'intensité
  if ((Luminosite_R+Luminosite_V+Luminosite_B) != 0) { // Vérifie si toutes les luminosités ne sont pas à 0
    Cpt_Lum=(Cpt_Lum+1)%256;
    // Luminosité de la LED Link (bleue)
    if (Luminosite_B != 0) {
      if (((Cpt_Lum%(256/Luminosite_B)) == 0) && (WiFiConnected)) {
        digitalWrite(LED_LINK,HIGH); // Allume la LED Link uniquement si le WiFi est connecté
      } else {
        digitalWrite(LED_LINK,LOW);
      }
    } else {
      digitalWrite(LED_LINK,LOW);
    }
    // Luminosité de la LED rouge
    if (Luminosite_R != 0) {
      if (((Cpt_Lum%(256/Luminosite_R)) == 0) && (Num_Cde <=1)) {
        digitalWrite(LED_ROUGE,HIGH); // Allume la LED rouge pour les modes 0 et 1
      } else {
        digitalWrite(LED_ROUGE,LOW);
      }
    } else {
      digitalWrite(LED_ROUGE,LOW);     
    }
    // Luminosité de la LED verte
    if (Luminosite_V != 0) {
      if (((Cpt_Lum%(256/Luminosite_V) == 0) && (((Num_Cde+3)%4) <=1))) {
        digitalWrite(LED_VERTE,HIGH); // Allume la LED verte pour les modes 0 et 1
      } else {
        digitalWrite(LED_VERTE,LOW);
      }
    } else {
      digitalWrite(LED_VERTE,LOW);
    }
  } else {
    digitalWrite(LED_LINK,LOW);
    digitalWrite(LED_ROUGE,LOW);
    digitalWrite(LED_VERTE,LOW);
  }

  // Surveillance de la T° et de l'hygrométrie
  if ((IPX_API_Prefix != "") && (Periode_Ctrl != 0) && (Capteur_DHT)) { // Les paramètres de surveillance sont configurés et le capteur DHT est présent, on remonte les infos vers l'IPX à la périodicité demandée
    if ((millis()-30000) % (Periode_Ctrl * 60000) == 0) { // Diffère la mesure de 30 secondes pour éviter la simultanéité avec celle de remontée
      Serial.println(F("Mesure de la T° et de l'hygrométrie"));
      delay(1); // Pour s'assurer qu'il n'y aura pas de double déclenchement
      // Mesure de la T° et de l'hygrométrie
      Num_Tentative=0;
      Temp = dht.getTemperature( );
      Humi = dht.getHumidity();
      while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { 
        delay(100);
        Temp = dht.getTemperature( );
        Humi = dht.getHumidity();
        Num_Tentative++;
      }
      if (!(isnan(Temp)) && (SV_TE != 0) ) { // Une T° a bien été mesurée et la sortie virtuelle n'est pas 0
        Serial.print(F("La T° mesurée est de : "));
        Serial.print(Temp);
        Serial.println(F(" °C"));
        if (Temp < T_min) { // La T° est inférieure au seuil mini : on place la sortie virtuelle à 1 (Set)
          Serial.print(F("Elle est inférieure au seuil minimal de "));
          Serial.print(T_min);
          Serial.print(F(" °C, la sortie virtuelle "));
          Serial.print(SV_Te);
          Serial.println(F(" passe à 1"));
          IPX_API_Command = IPX_API_Prefix+"SetVO="+SV_Te;
          Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
          Serial.println(IP_IPX+IPX_API_Command);
          HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
          Serial.println(HttpResponse);          
        }
        if (Temp > T_Max) { // La T° est supérieure au seuil maxi : on place la sortie virtuelle à 0 (Clear)
          Serial.print(F("Elle est supérieure au seuil maximal de "));
          Serial.print(T_Max);
          Serial.print(F(" °C, la sortie virtuelle "));
          Serial.print(SV_Te);
          Serial.println(F(" passe à 0"));
          IPX_API_Command = IPX_API_Prefix+"ClearVO="+SV_Te;
          Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
          Serial.println(IP_IPX+IPX_API_Command);
          HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
          Serial.println(HttpResponse);         
        }
      }
      if (!(isnan(Humi)) && (SV_HU != 0)) { // Une humidité a bien été mesurée et la sortie virtuelle n'est pas 0
        Serial.print(F("L'humidité mesurée est de : "));
        Serial.print(Humi);
        Serial.println(F(" %"));
        if (Humi < H_min) { // L'humidité est inférieure au seuil mini : on place la sortie virtuelle à 0 (Clear)
          Serial.print(F("Elle est inférieure au seuil minimal de "));
          Serial.print(H_min);
          Serial.print(F(" %, la sortie virtuelle "));
          Serial.print(SV_Hu);
          Serial.println(F(" passe à 0"));
          IPX_API_Command = IPX_API_Prefix+"ClearVO="+SV_Hu;
          Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
          Serial.println(IP_IPX+IPX_API_Command);
          HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
          Serial.println(HttpResponse);
        }
        if (Humi > H_Max) { // L'humidité est supérieure au seuil maxi : on place la sortie virtuelle à 1 (Set)
          Serial.print(F("Elle est supérieure au seuil maximal de "));
          Serial.print(H_Max);
          Serial.print(F(" %, la sortie virtuelle "));
          Serial.print(SV_Hu);
          Serial.println(F(" passe à 1"));
          IPX_API_Command = IPX_API_Prefix+"SetVO="+SV_Hu;
          Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
          Serial.println(IP_IPX+IPX_API_Command);
          HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
          Serial.println(HttpResponse);
        }
      }
      Serial.println(SEPARATEUR);
    }
  }

  // Remontée de la T° et de l'hygrométrie
  if ((IPX_API_Prefix != "") && (Periode_Rtrn != 0) && (Capteur_DHT)) { // Les paramètres de remontée sont configurés et le capteur DHT est présent, on remonte les infos vers l'IPX à la périodicité demandée
    if (millis() % (Periode_Rtrn * 60000) == 0) {
      Serial.println(F("Remontée de la T° et de l'hygrométrie"));
      delay(1); // Pour s'assurer qu'il n'y aura pas de double déclenchement
      // Mesure de la T° et de l'hygrométrie
      Num_Tentative=0;
      Temp = dht.getTemperature( );
      Humi = dht.getHumidity();
      while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { 
        delay(100);
        Temp = dht.getTemperature( );
        Humi = dht.getHumidity();
        Num_Tentative++;
      }
      if (!(isnan(Temp)) && (VA_TE !=0)) { // Une T° a bien été mesurée et l'entrée analogique virtuelle n'est pas 0
        Serial.print(F("La T° remontée est de : "));
        Serial.print(Temp);
        Serial.println(F(" °C"));
        CAN_Te = (Temp+40)/120*65535;
        Serial.print(F("soit "));
        Serial.print(CAN_Te);
        Serial.println(F(" sur 16 bits"));
        if (VA_Te < 10) {
          IPX_API_Command = IPX_API_Prefix+"SetVA0"+VA_Te+"="+CAN_Te;
        } else {
          IPX_API_Command = IPX_API_Prefix+"SetVA"+VA_Te+"="+CAN_Te;         
        }
        Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
        Serial.println(IP_IPX+IPX_API_Command);
        HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
        Serial.println(HttpResponse);          
      }
      if (!(isnan(Humi)) && (VA_HU !=0)) { // Une humidité a bien été mesurée et l'entrée analogique virtuelle n'est pas 0
        Serial.print(F("L'humidité remontée est de : "));
        Serial.print(Humi);
        Serial.println(F(" %"));
        CAN_Hu = Humi/100*65535;
        Serial.print(F("soit "));
        Serial.print(CAN_Hu);
        Serial.println(F(" sur 16 bits"));
        if (VA_Hu < 10) {
          IPX_API_Command = IPX_API_Prefix+"SetVA0"+VA_Hu+"="+CAN_Hu;
        } else {
          IPX_API_Command = IPX_API_Prefix+"SetVA"+VA_Hu+"="+CAN_Hu;         
        }
        Serial.print(F("Requête HTTP envoyée à l'IPX : http://"));
        Serial.println(IP_IPX+IPX_API_Command);
        HttpResponse = (GetWithAnswer(IP_IPX, IPX_API_Command));
        Serial.println(HttpResponse);
      }
      Serial.println(SEPARATEUR);
    }
  }
  // Attente d'une connexion sur le serveur Web
  WiFiClient client = server.available();
  if (!client) { return; }
  Serial.println(F("Client connecté sur le serveur web embarqué"));
  Serial.print(F("Adresse IP du client :"));
  Serial.println((client.remoteIP()));
  ClientRequest = (client.readStringUntil('\r'));
  Serial.println(ClientRequest);
  Commande=ClientRequest.substring(5,ClientRequest.length()-9);
  Serial.print("Commande \""+Commande+"\" : "); 
  Num_Cde_Recue=NumEtat(Commande);
  Serial.println(Num_Cde_Recue);
  if (Num_Cde_Recue >= 4) {
    Serial.print(F("Status : "));
    Serial.println(Num_Cde);  
  } 
  client.println(F("HTTP/1.1 200 OK"));
  client.println(F("Cache-Control: no-cache"));
  client.println(F("Connection: close"));
  client.println(F("Content-Type: application/json"));
  client.println(F("access-control-allow-origin: *"));
  client.println(F(""));
  client.println(F("{"));
  client.println("    \"device\" : \""+Nom_Dispositif+"\",");

  if (Num_Cde_Recue == -1) {
    ClignoteLED(5, 60);
    client.println(F("    \"status\" : \"error\""));
  } else {
    ClignoteLED(1, 60);
    client.println("    \"command\" : \""+Commande+"\",");
    switch (Num_Cde_Recue) {
      case 4 : // Lecture de l'état
        client.print(F("    \"status\" : \""));
        client.print((digitalRead(ALT_POS)*3+(digitalRead(ALT_NEG)*2))%4);
        client.println(F("\","));
        client.print(F("    \"dht\" : \""));
        client.print(Capteur_DHT);
        client.println(F("\","));
        // Si présence du capteur DHT, mesure de la T° et de l'humidité
        if (Capteur_DHT) {
          Temp = dht.getTemperature( );
          Humi = dht.getHumidity();
          Serial.println(F("Lecture T° et Humidité"));
          Num_Tentative=0;
          while ((isnan(Temp) || isnan(Humi)) && Num_Tentative < NB_TENTATIVES) { 
            delay(100);
            Temp = dht.getTemperature( );
            Humi = dht.getHumidity();
            Serial.print(Temp);
            Serial.print(F(" °C / "));
            Serial.print(Humi);
            Serial.println(F("%"));
            Num_Tentative++;
          }
          Serial.print(F("Température : "));
          Serial.print(Temp);
          Serial.println(F(" °C"));
          Serial.print(F("Humidité : "));
          Serial.print(Humi);
          Serial.println(F(" %"));
          client.print(F("    \"temp\" : "));
          if (!isnan(Temp)) {
            client.print(Temp);
          } else {
            client.print("\"nan\"");
          }        
          client.println(F(","));
          client.print(F("    \"humi\" : "));
          if (!isnan(Humi)) {
            client.print(Humi);
          } else {
            client.print("\"nan\"");
          }      
          client.println(F(","));
        }
        client.print(F("    \"IP_IPX\" : \""));
        client.print(IP_IPX);
        client.println(F("\","));
        client.print(F("    \"IPX_API_Prefix\" : \""));
        client.print(IPX_API_Prefix);
        client.println(F("\","));
        client.println(F("    \"th_control\" : {"));
        if (Periode_Ctrl == 0) {
          client.println(F("      \"status\" : \"Off\""));
        } else {
          client.println(F("      \"status\" : \"On\","));
          client.print(F("      \"periodicity\" : \""));
          client.print(Periode_Ctrl);
          client.println(F("\","));        
          client.print(F("      \"t_min\" : \""));
          client.print(T_min);
          client.println(F("\","));        
          client.print(F("      \"t_max\" : \""));
          client.print(T_Max);
          client.println(F("\","));        
          client.print(F("      \"sv_te\" : \""));
          client.print(SV_Te);
          client.println(F("\","));        
          client.print(F("      \"h_min\" : \""));
          client.print(H_min);
          client.println(F("\","));        
          client.print(F("      \"h_max\" : \""));
          client.print(H_Max);
          client.println(F("\","));        
          client.print(F("      \"sv_hu\" : \""));
          client.print(SV_Hu);
          client.println(F("\""));        
        }
        client.println(F("    },"));
        client.println(F("    \"th_return\" : {"));
        if (Periode_Rtrn == 0) {
          client.println(F("      \"status\" : \"Off\""));
        } else {
          client.println(F("      \"status\" : \"On\","));
          client.print(F("      \"periodicity\" : \""));
          client.print(Periode_Rtrn);
          client.println(F("\","));        
          client.print(F("      \"va_te\" : \""));
          client.print(VA_Te);
          client.println(F("\","));        
          client.print(F("      \"va_hu\" : \""));
          client.print(VA_Hu);
          client.println(F("\""));        
        }
        client.println(F("    },"));
        client.print(F("    \"blue_luminosity\" : \""));
        client.print(Luminosite_B);
        client.println(F("\","));
        client.print(F("    \"red_luminosity\" : \""));
        client.print(Luminosite_R);
        client.println(F("\","));
        client.print(F("    \"green_luminosity\" : \""));
        client.print(Luminosite_V);
        client.println(F("\","));
        client.print(F("    \"RSSI\" : \""));
        client.print(WiFi.RSSI());
        break;
      case 5 : // Lecture de la version du firmware
        client.println("    \"version\" : \""+FIRMWARE_VERSION+"\",");
        client.print("    \"source\" : \""+FileName);
        break;
      case 6 : // Changement de luminosité des LEDs
        ExtractParamsValues(Commande.substring(4), Params, 3); // Extrait les 3 paramètres attendus : RVB ou R, V et B
        CdeOK = false;
        if (Params[0].param.equalsIgnoreCase("RVB")) { // Le paramètre est RVB : on fixe la même valeur pour les 3 luminosités
          Params[0].value.toCharArray(Char_Tab,16);
          int Value;
          sscanf(Char_Tab, "%i", &Value);
          Val_Lum_Dde=Value;
          Serial.print(F("Luminosité RVB demandée : "));
          Serial.println(Val_Lum_Dde);
          if (Val_Lum_Dde <= 255) {
            Luminosite_R = Val_Lum_Dde;
            Luminosite_V = Val_Lum_Dde;
            Luminosite_B = Val_Lum_Dde;
          } else {
            Luminosite_R = 255;
            Luminosite_V = 255;
            Luminosite_B = 255;
           }
           CdeOK=true;
        }

        if ((Params[0].param.equalsIgnoreCase("R")) && (Params[1].param.equalsIgnoreCase("V")) && (Params[2].param.equalsIgnoreCase("B"))) { // Les 3 paramètres sont R, V et B (dans l'ordre) : on fixe des valeurs différenciées pour les 3 luminosités
          // Valeur de luminosité rouge
          Params[0].value.toCharArray(Char_Tab,16);
          int Value;
          sscanf(Char_Tab, "%i", &Value);
          Val_Lum_Dde=Value;
          Serial.print(F("Luminosité R demandée : "));
          Serial.println(Val_Lum_Dde);
          if (Val_Lum_Dde <= 255) {
            Luminosite_R = Val_Lum_Dde;
          } else {
            Luminosite_R = 255;
           }         
          // Valeur de luminosité verte
          Params[1].value.toCharArray(Char_Tab,16);
          sscanf(Char_Tab, "%i", &Value);
          Val_Lum_Dde=Value;
          Serial.print(F("Luminosité V demandée : "));
          Serial.println(Val_Lum_Dde);
          if (Val_Lum_Dde <= 255) {
            Luminosite_V = Val_Lum_Dde;
          } else {
            Luminosite_V = 255;
           }         
          // Valeur de luminosité bleue
          Params[2].value.toCharArray(Char_Tab,16);
          sscanf(Char_Tab, "%i", &Value);
          Val_Lum_Dde=Value;
          Serial.print(F("Luminosité B demandée : "));
          Serial.println(Val_Lum_Dde);
          if (Val_Lum_Dde <= 255) {
            Luminosite_B = Val_Lum_Dde;
          } else {
            Luminosite_B = 255;
           }         
           CdeOK=true;
        }

        // Indique le résultat
        if (CdeOK) {         
          // Mémorisation des luminosités dans l'EEPROM
          EEPROM.write(LUM_VALUE,Luminosite_R);
          EEPROM.write(LUM_VALUE+1,Luminosite_V);
          EEPROM.write(LUM_VALUE+2,Luminosite_B);
          EEPROM.commit();
          client.print(F("    \"blue_luminosity\" : \""));
          client.print(Luminosite_B);
          client.println(F("\","));
          client.print(F("    \"red_luminosity\" : \""));
          client.print(Luminosite_R);
          client.println(F("\","));
          client.print(F("    \"green_luminosity\" : \""));
          client.print(Luminosite_V);
        } else {
          client.print(F("    \"status\" : \"LUMI Command error, incorrect or missing parameter(s)"));
        }
        break;
      case 7 : // Renommage du device
        Nom_Dispositif = Commande.substring(3,18);
        //----------------------------------------------------
        Nom_Dispositif.toCharArray(devname, 16);
        for (uint8_t i = 0; i < 16 ; i++) ConfWHP.devname[i] = devname[i];
        // Mémorisation du nouveau nom dans l'EEPROM
        EEPROM.put(POS_DEVICECONFIG,ConfWHP);
        EEPROM.commit();
        Serial.print(F("Nouveau nom du device : "));
        Serial.println(Nom_Dispositif);
        client.print(F("    \"newname\" : \""));
        client.print(Nom_Dispositif);
        break;
      case 8 : // Activation/désactivation du DHT
        if (Commande.substring(3).equalsIgnoreCase("ON")) {
          if (!(DeviceConfig%2)) {
            DeviceConfig = DeviceConfig+1; // Positionne à 1 le 1er bit de DeviceConfig si "ON"
            Capteur_DHT = true; // Place Capteur_DHT à vrai
            }
        } else {
          if (DeviceConfig%2) {
            DeviceConfig = DeviceConfig-1; // Positionne à 0 le 1er bit de DeviceConfig si "OFF"
            Capteur_DHT = false; // Place Capteur_DHT à faux
            }
        }
        Serial.print(F("Config écrite : "));
        Serial.println(DeviceConfig);
        EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig);
        Serial.print(F("Config relue : "));
        Serial.println(EEPROM.read(FLAG_DEVICECONFIG));
        EEPROM.commit();
        Serial.print(F("Le capteur DHT est à "));
        Serial.println(Capteur_DHT);
        client.print(F("    \"dht\" : \""));
        client.print(Capteur_DHT);
        break;
      case 9 : // Dump, Wipe (remplissage avec des 0) ou Fill (remplissage avec des 0xFF) de l'EEPROM
        if (Commande.substring(3).equalsIgnoreCase("WIPE")) {
          Serial.println(F("Wipe de l'EEPROM"));
          for (uint16_t x = 0; x < EEPROM_SIZE; x++) {    //Loop end of EEPROM address
            if (!(EEPROM.read(x) == 0)) {
              EEPROM.write(x, 0);       // if not 0 write 0 to clear, it takes 3.3mS ?
            }
            yield();
          }
          EEPROM.commit();
          Serial.println(F("Wipe terminé, vérification..."));
          Commande="EP=DUMP"; // Force un Dump de l'EEPROM après Wipe pour contrôle
        }
        if (Commande.substring(3).equalsIgnoreCase("FILL")) {
          Serial.println(F("Fill de l'EEPROM"));
          for (uint16_t x = 0; x < EEPROM_SIZE; x++) {    //Loop end of EEPROM address
            if (!(EEPROM.read(x) == 0xFF)) {
              EEPROM.write(x, 0xFF);       // if not 0 write 0 to clear, it takes 3.3mS ?
            }
            yield();
          }
          DeviceConfig = 0; // Force le device Config à 0
          EEPROM.write(FLAG_DEVICECONFIG,DeviceConfig);
          EEPROM.commit();
          Serial.println(F("Fill terminé, vérification..."));
          Commande="EP=DUMP"; // Force un Dump de l'EEPROM après Fill pour contrôle
        }
        if (Commande.substring(3).equalsIgnoreCase("DUMP")) {
          // Dump en Hexa et en ASCII
          Serial.println(F("Dump de l'EEPROM"));
          for (uint16_t i = 0 ; i < (EEPROM_SIZE/16) ; i++) {
            for (uint8_t j = 0; j < 16 ; j++) {
              Serial.print(EEPROM.read(i*16+j) < 0x10 ? "0" : "");
              Serial.print(EEPROM.read(i*16+j), HEX);
              Serial.print(F(" "));
            }
            for (uint8_t j = 0; j < 16 ; j++) {
              Serial.print((EEPROM.read(i*16+j) < 0x20) || (EEPROM.read(i*16+j) > 0x7F) ? '^' : char(EEPROM.read(i*16+j)));
            }            
            Serial.println(F(""));
            yield();
          }
          client.print(F("    \"status\" : \"Successfull"));
          break;
        }
        Serial.println(F("Commande non reconnue"));
        client.print(F("    \"status\" : \"EP Command error"));
        break;
      case 10 : // Paramètres de l'IPX
        Serial.println(F("Configuration des paramètres de l'IPX"));
        ExtractParamsValues(Commande.substring(8), Params, 2); // Extrait les 2 paramètres attendus : Ip et, éventuellement, APIkey de l'IPX
        CdeOK = true;
        // Teste si le 1er paramètre est bien IP_IPX
        if (!Params[0].param.equalsIgnoreCase("IP_IPX")) {
          CdeOK = false;
          Serial.println(F("Le premier paramètre n'est pas IP_IPX"));
          Serial.println(F("Configuration non modifiée"));
          client.print(F("    \"status\" : \"IPX_CFG Command error, 1st param not IP_IPX"));
          Params[1].param = ""; // Efface l'éventuel deuxième paramètre pour ne pas rentrer dans le test suivant
        } else {
          IP_IPX = Params[0].value;
        }
        // Teste si le 2ème paramètre existe et est bien APIkey
        //API_Key = "";
        if (Params[1].param != "") {
          if (!Params[1].param.equalsIgnoreCase("APIkey")) {
          CdeOK = false;    
          Serial.println(F("Une second paramètre existe mais n'est pas APIkey"));
          Serial.println(F("Configuration non modifiée"));
          client.print(F("    \"status\" : \"IPX_CFG Command error, 2nd param present but not APIkey"));
          IP_IPX = ConfWHP.ip_ipx; // Restaure l'ancienne valeur de l'IP de l'IPX
          } else {
            API_Key = Params[1].value;
          }           
        }
        IPX_API_Prefix = "";
        if (IP_IPX != "") {
          Serial.print(F("IP IPX800 : "));
          Serial.println(IP_IPX);
          IPX_API_Prefix = "/api/xdevices.json?";
          if (API_Key != "") {
            IPX_API_Prefix = IPX_API_Prefix+"key="+API_Key+"&";
          }
        }
        Serial.print(F("Préfixe des requêtes HTTP pour IPX : "));
        Serial.println((IPX_API_Prefix=="") ? "Effacée" : "http://"+IP_IPX+IPX_API_Prefix);
        // Mémorisation des paramètres dans l'EEPROM
        if (CdeOK) {
          client.println(F("    \"status\" : \"IPX_CFG Success\","));
          client.print(F("    \"IP_IPX\" : \""));
          client.print(IP_IPX);
          client.println(F("\","));
          client.print(F("    \"IPX_API_Prefix\" : \""));
          client.print(IPX_API_Prefix);
          IP_IPX.toCharArray(ip_ipx, 16);
          for (uint8_t i = 0; i < 16 ; i++) ConfWHP.ip_ipx[i] = ip_ipx[i];
          API_Key.toCharArray(api_key, 16);
          for (uint8_t i = 0; i < 16 ; i++) ConfWHP.api_key[i] = api_key[i];
          EEPROM.put(POS_DEVICECONFIG,ConfWHP);
          EEPROM.commit();        
        }
        break;
      case 11 : // Paramètres de surveillance du climat
         Serial.println(F("Configuration des paramètres pour la surveillance du climat"));
         ExtractParamsValues(Commande.substring(8), Params, 7); // Extrait les 7 paramètres attendus : PERIOD, T_MINI, T_MAXI, SV_TE et optionnellement H_MINI, H_MAXI et SV_HU
         CdeOK = true;
        if (!Params[0].param.equalsIgnoreCase("PERIOD")) {
          CdeOK = false;
          Serial.println(F("Le premier paramètre n'est pas PERIOD"));
          Serial.println(F("Configuration non modifiée"));
          client.print(F("    \"status\" : \"CTRL_TH Command error, 1st param not PERIOD"));
        } else {
          Serial.println(F("Le premier paramètre est bien PERIOD"));
          // Duplication des anciennes valeurs
          New_T_min = T_min;
          New_T_Max = T_Max;
          New_H_min = H_min;
          New_H_Max = H_Max;
          New_SV_Te = SV_Te;
          New_SV_Hu = SV_Hu;
          New_Periode_Ctrl = Periode_Ctrl;
          // Evaluation de la périodicité demandée
          Params[0].value.toCharArray(Char_Tab,16);
          sscanf(Char_Tab, "%i", &New_Periode_Ctrl);
          if (New_Periode_Ctrl >= 1440) { // Périodicité supérieure à 24h refusée
            CdeOK = false;
            Serial.println(F("La périodicité demandée est supérieure à 24h"));
            Serial.println(F("Configuration non modifiée"));
            client.print(F("    \"status\" : \"CTRL_TH Command error, PERIOD more than 24h"));           
          } else {
            if (New_Periode_Ctrl == 0) { // La périodicité est fixée à 0, inutile d'évaluer le reste
              Serial.println(F("Périodicité à 0, fonction de surveillance désactivée"));
            } else {
              Serial.println(F("La périodicité est comprise entre 0 et 24h"));
              if ((!Params[1].param.equalsIgnoreCase("T_MINI")) || (!Params[2].param.equalsIgnoreCase("T_MAXI")) || (!Params[3].param.equalsIgnoreCase("SV_TE"))) {
                CdeOK = false;
                Serial.println(F("Un des paramètres pour la T° est faux ou manquant"));
                Serial.println(F("Configuration non modifiée"));
                client.print(F("    \"status\" : \"CTRL_TH Command error, incorrect or missing Temperature parameter"));           
              } else {
                Serial.println(F("Les paramètres de T° sont bien tous présents"));
                // Evaluation des paramètres de T°
                Params[1].value.toCharArray(Char_Tab,16);
                sscanf(Char_Tab, "%i", &New_T_min);
                Serial.print(F("T min = "));
                Serial.println(New_T_min);
                Params[2].value.toCharArray(Char_Tab,16);
                sscanf(Char_Tab, "%i", &New_T_Max);
                Serial.print(F("T Max = "));
                Serial.println(New_T_Max);
                if ((New_T_min <0) || (New_T_min > 20) || (New_T_Max <0) || (New_T_Max > 30) || (New_T_min >= New_T_Max)) {
                  CdeOK = false;
                  Serial.println(F("Les valeurs de T° min et Max sont incorrectes, vérifiez la consigne demandée"));
                  Serial.println(F("Configuration non modifiée"));
                  client.print(F("    \"status\" : \"CTRL_TH Command error, incorrect Temperature value(s)"));
                } else {
                   Serial.println(F("Les températures mini et Maxi sont bien cohérentes"));
                  Params[3].value.toCharArray(Char_Tab,16);
                  sscanf(Char_Tab, "%i", &New_SV_Te);
                  if (New_SV_Te > 128) {
                    CdeOK = false;
                    Serial.println(F("La sortie virtuelle demandée pour la température est au delà de 128"));
                    Serial.println(F("Configuration non modifiée"));
                    client.print(F("    \"status\" : \"CTRL_TH Command error, incorrect temperature VO value"));
                  } else {
                    Serial.print(F("La sortie virtuelle "));
                    Serial.print(New_SV_Te);
                    Serial.println(F(" est sélectionnée pour la surveillance de la T°"));
                    if ((Params[4].value != "") && (Params[4].param.equalsIgnoreCase("H_MINI")) && (Params[5].param.equalsIgnoreCase("H_MAXI")) && (Params[6].param.equalsIgnoreCase("SV_HU"))) {
                      // Des paramètres d'hygrométrie sont présents on vérifie leur cohérence
                      Serial.println(F("Les paramètres d'hygrométrie sont bien tous présents"));
                      // Evaluation des paramètres d'hygrométrie
                      Params[4].value.toCharArray(Char_Tab,16);
                      sscanf(Char_Tab, "%i", &New_H_min);
                      Serial.print(F("H min = "));
                      Serial.println(New_H_min);
                      Params[5].value.toCharArray(Char_Tab,16);
                      sscanf(Char_Tab, "%i", &New_H_Max);
                      Serial.print(F("H Max = "));
                      Serial.println(New_H_Max);
                      if ((New_H_min <0) || (New_H_min > 100) || (New_H_Max <0) || (New_H_Max > 100) || (New_H_min >= New_H_Max)) {
                        CdeOK = false;
                        Serial.println(F("Les valeurs d'humidité min et Max sont incorrectes, vérifiez la consigne demandée"));
                        Serial.println(F("Configuration non modifiée"));
                        client.print(F("    \"status\" : \"CTRL_TH Command error, incorrect Hygrometry value(s)"));
                      } else {
                        Serial.println(F("Les pourcentages d'hygrométrie mini et Maxi sont bien cohérents"));
                        Params[6].value.toCharArray(Char_Tab,16);
                        sscanf(Char_Tab, "%i", &New_SV_Hu);
                        if (New_SV_Hu > 128) {
                          CdeOK = false;
                          Serial.println(F("La sortie virtuelle demandée pour l'hygrométrie est au delà de 128"));
                          Serial.println(F("Configuration non modifiée"));
                          client.print(F("    \"status\" : \"CTRL_TH Command error, incorrect hygrometry VO value"));
                        } else {
                          Serial.print(F("La sortie virtuelle "));
                          Serial.print(New_SV_Hu);
                          Serial.println(F(" est sélectionnée pour la surveillance de l'hygrométrie"));
                        } // endif Vérification SV Hygrométrie <=128
                      } // endif Contrôle des paramètres d'hygrométrie
                    } else {
                      // Réinitialisation des paramètres d'hygrométrie
                      New_H_min = 0;
                      New_H_Max = 100;
                      New_SV_Hu = 0;
                    } // endif présence des paramètres d'hygrométrie
                  } // endif Vérification SV Température <=128
                } // endif Contrôle des T° demandées
              } // endif Contrôle des paramètres de T°
            } // endif PERIOD à 0, désactivation
          } // endif PERIOD > 24h
        } // endif Premier paramètre non égal à PERIOD
        // Mémorisation des paramètres dans l'EEPROM
        if (CdeOK) { // La commande est Ok
          Serial.println(F("Configuration OK"));
          // Recopie des nouvelles valeurs demandées dans les variables correspondantes
          T_min = New_T_min;
          T_Max = New_T_Max;
          H_min = New_H_min;
          H_Max = New_H_Max;
          SV_Te = New_SV_Te;
          SV_Hu = New_SV_Hu;
          Periode_Ctrl = New_Periode_Ctrl;
          // Mémorisation dans l'EEPROM
          EEPROM.put(PERIODE_CTRL, Periode_Ctrl);
          if (Periode_Ctrl == 0) {
            client.print(F("    \"status\" : \"CTRL_TH Successfully deactivated"));            
          } else {
            client.print(F("    \"status\" : \"CTRL_TH Successfull"));
            EEPROM.write(T_MIN, T_min);
            EEPROM.write(T_MAX, T_Max);
            EEPROM.write(SV_TE, SV_Te);
            EEPROM.write(H_MIN, H_min);
            EEPROM.write(H_MAX, H_Max);
            EEPROM.write(SV_HU, SV_Hu);           
          }
          EEPROM.commit();        
        }
        break;
      case 12 : // Paramètres de remontée du climat
        Serial.println(F("Configuration des paramètres pour la remontée du climat vers l'IPX"));
        ExtractParamsValues(Commande.substring(8), Params, 3); // Extrait les 3 paramètres attendus : PERIOD, VA_TE et optionnellement VA_HU
        CdeOK = true;
        if (!Params[0].param.equalsIgnoreCase("PERIOD")) {
          CdeOK = false;
          Serial.println(F("Le premier paramètre n'est pas PERIOD"));
          Serial.println(F("Configuration non modifiée"));
          client.print(F("    \"status\" : \"RTRN_TH Command error, 1st param not PERIOD"));
        } else {
          Serial.println(F("Le premier paramètre est bien PERIOD"));
          // Evaluation de la périodicité demandée
          Params[0].value.toCharArray(Char_Tab,16);
          sscanf(Char_Tab, "%i", &New_Periode_Rtrn);
          if (New_Periode_Rtrn >= 1440) { // Périodicité supérieure à 24h refusée
            CdeOK = false;
            Serial.println(F("La périodicité demandée est supérieure à 24h"));
            Serial.println(F("Configuration non modifiée"));
            client.print(F("    \"status\" : \"RTRN_TH Command error, PERIOD more than 24h"));           
          } else {
            if (New_Periode_Rtrn == 0) { // La périodicité est fixée à 0, inutile d'évaluer le reste
              Serial.println(F("Périodicité à 0, fonction de remontée désactivée"));
            } else {
              Serial.println(F("La périodicité est comprise entre 0 et 24h"));
              if (!Params[1].param.equalsIgnoreCase("VA_TE")) {
                CdeOK = false;
                Serial.println(F("Le paramètre du n° d'entrée virtuelle analogique est manquant"));
                Serial.println(F("Configuration non modifiée"));
                client.print(F("    \"status\" : \"RTRN_TH Command error, incorrect or missing temperature VA argument"));           
              } else {
                Serial.println(F("Le paramètre du n° d'entrée virtuelle analogique est bien présent"));
                // Evaluation des paramètres de T°
                Params[1].value.toCharArray(Char_Tab,16);
                sscanf(Char_Tab, "%i", &New_VA_Te);
                if (New_VA_Te > 32) {
                  CdeOK = false;
                  Serial.println(F("L'entrée virtuelle analogique demandée pour la température est au delà de 32"));
                  Serial.println(F("Configuration non modifiée"));
                  client.print(F("    \"status\" : \"RTRN_TH Command error, incorrect temperature VA number (max 32)"));
                } else {
                  Serial.print(F("L'entrée virtuelle analogique "));
                  Serial.print(New_VA_Te);
                  Serial.println(F(" est sélectionnée pour la remontée de la T°"));
                  if (Params[2].param.equalsIgnoreCase("VA_HU")) {
                    // Une entrée virtuelle analogique est indiquée pour l'humidité
                    Params[2].value.toCharArray(Char_Tab,16);
                    sscanf(Char_Tab, "%i", &New_VA_Hu);
                    if ((New_VA_Hu > 32) || (New_VA_Hu == New_VA_Te)) {
                      CdeOK = false;
                      Serial.println(F("L'entrée virtuelle analogique demandée pour l'hygrométrie est au delà de 32 ou égale à celle de la température"));
                      Serial.println(F("Configuration non modifiée"));
                      if (New_VA_Hu > 32) {
                        client.print(F("    \"status\" : \"RTRN_TH Command error, incorrect hygrometry VA number (max 32)"));
                      } else {
                        client.print(F("    \"status\" : \"RTRN_TH Command error, hygrometry VA number same as temperature (must be different)"));
                      } // endif Vérification VA Hygrométrie <=32 pour message
                    } else {
                      Serial.print(F("L'entrée virtuelle analogique "));
                      Serial.print(New_VA_Hu);
                      Serial.println(F(" est sélectionnée pour la remontée de l'hygrométrie"));
                    } // endif Vérification VA Hygrométrie <=32 et != VA T°
                  } else { // Pas de troisième paramètre ou il n'est pas égal à VA_HU, mise à 0
                  New_VA_Hu = 0;
                  } // endif Contrôle de la présence de VA d'hygrométrie
                } // endif Vérification VA Température <=32
              } // endif Contrôle de la VA de T°
            } // endif PERIOD à 0, désactivation
          } // endif PERIOD > 24h
        } // endif Premier paramètre non égal à PERIOD
        // Mémorisation des paramètres dans l'EEPROM
        if (CdeOK) { // La commande est Ok
          Serial.println(F("Configuration OK"));
          // Recopie des nouvelles valeurs demandées dans les variables correspondantes
          VA_Te = New_VA_Te;
          VA_Hu = New_VA_Hu;
          Periode_Rtrn = New_Periode_Rtrn;
          // Mémorisation dans l'EEPROM
          EEPROM.put(PERIODE_RTRN, Periode_Rtrn);
          if (Periode_Rtrn == 0) {
            client.print(F("    \"status\" : \"RTRN_TH Successfully deactivated"));            
          } else {
            client.print(F("    \"status\" : \"RTRN_TH Successfull"));
            EEPROM.write(VA_TE, VA_Te);
            EEPROM.write(VA_HU, VA_Hu);           
          }
          EEPROM.commit();        
        }
        break;
      default : // Gestion d'un changement d'état
        Num_Cde=Num_Cde_Recue;
        client.print(F("    \"status\" : \""));
        client.print(Num_Cde);
        Pilote(Num_Cde);
    }
    client.println(F("\""));
  }  
  client.println(F("}"));
  client.flush();
  Serial.println(SEPARATEUR);

}
