Electrolyseur E-Pool Technologie Justsalt

état d’avancement

  • Reboot esp par HA : KO
  • Lecture des Valeurs : OK
  • Découpage Binaire : Bug
  • send au justsalt des paramètres via HA :
    – Consigne PH : OK
    – Consigne ORP : OK
    – Consigne ORP Alarme : OK
    – Volume eau : OK
    – taux Acide : OK
    – Production ORP : OK
    – Inversion : OK

il y a encore des problèmes de stabilité du Bluetooth du a des scan qui ne ce fond pas au bon moment !
J attend la stabilité du BT pour vous poster le code , puis je ferais une explication pour la mise en œuvre !

état d’avancement

  • Reboot esp par HA : OK
  • Lecture des Valeurs : OK
  • Découpage Binaire : OK mais il faut en ajouter
  • send au justsalt des paramètres via HA :
    – Consigne PH : OK
    – Consigne ORP : OK
    – Consigne ORP Alarme : OK
    – Volume eau : OK
    – taux Acide : OK
    – Production ORP : OK
    – Inversion : OK

il reste encore bcp de chose a optimiser et a rendre plus propre (volume piscine par exemple) mais c est intégrable :slight_smile:
il y a les commande de boost , les alarmes , l injection manuel de PH … a trouver

en attendant voici le code brut
je vais le mettre sur un github , pour partager aussi les bibliothèques utilisé

#define ARDUINOHA_DEBUG
#include <Arduino.h>


/** Electolyseur JustSalt & co
 *
 *  Passerelle Bt Justsalt to Mqtt
 *
 *  Created: Mai 2025
 *      Author: Ricky
 *
*/


#include <NimBLEDevice.h>
#include <ArduinoHA.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
#include <TaskScheduler.h>


const char* version = "0.0.1";
// Update these with values suitable for your wifi network.
const char* ssid = "xxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxxx";

//Home Assistant integration
// configure here your HA params for connection to MQTT server
#define BROKER_ADDR IPAddress(192,168,50,11)
#define BROKER_USERNAME     "" // replace with your credentials
#define BROKER_PASSWORD     ""

//Unique device for HA integration (Adresse Mac du justsalt)
const byte deviceUniqID[] = { 0x94, 0xDE, 0xB8, 0xA1, 0x1A, 0xAC };

//justsalt service and char UUID
// The remote service we wish to connect to.
static  BLEUUID serviceUUID("09912756-7b32-4629-aeb1-b309d9a338ae");
// The characteristic of the remote service we are interested in.
static  BLEUUID    charUUID("ef785c24-22bb-463d-b651-0b7445ba091c");
static  BLEUUID    charUUIDwrite("4d32c5e5-2bb5-45c6-8c89-6f59bb3930d2");

static NimBLEAdvertisedDevice* advDevice;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static int countertrameold = 0;
static int countertrame = 5;
static int countertrametry = 0;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static NimBLERemoteCharacteristic* pRemoteCharWrite;

static NimBLEClient* pClient;
static NimBLERemoteService* pSvc ;
static NimBLERemoteCharacteristic* pChr ;
static NimBLERemoteDescriptor* pDsc ;

WiFiClient wifiMQTT;
WiFiClient telnet;
WiFiServer telnetServer(23);

Scheduler timeScheduler;
AsyncWebServer webServer(80);


unsigned long previousMillis = 0;
unsigned long interval = 30000;

void scanEndedCB(NimBLEScanResults results);
void setup_wifi();
void setup_glogal();
void reconnect_wifi();
void setupHaIntegration();
void setup_telnet();
void cb_handleTelnet();
void cb_loopHaIntegration();
void cb_loopElegantOTA();
void cb_loopAvaibilityMQTT();
void cb_setupAndScan_ble();
void cb_connectBleServer();
void onValueConsignePhChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender);
void onValueConsignevolChanged( HANumeric number, HANumber* sender);
void onValueConsigneacideChanged( HANumeric number, HANumber* sender);
void onValueConsigneprodChanged( HANumeric number, HANumber* sender);
void onValueConsigneinversionChanged( HANumeric number, HANumber* sender);
void onwriteble (int ValueID ,float numberset,int ValueSizea);

bool connectToServer();


Task taskSetup(5000,TASK_FOREVER,&setup_wifi);
Task taskReconnectWifi(interval,TASK_FOREVER,&reconnect_wifi);
Task taskTelnet(5000,TASK_FOREVER,&cb_handleTelnet);
Task taskloopElegantOTA(5000,TASK_FOREVER,&cb_loopElegantOTA);
Task taskBleSetupAndScan(30000, TASK_FOREVER, &cb_setupAndScan_ble);
Task taskConnectBleServer(50000, TASK_FOREVER, &cb_connectBleServer);
Task taskloopHaIntegration(5000, TASK_FOREVER,&cb_loopHaIntegration);
Task taskloopAvaibilityMQTT(3000, TASK_FOREVER, &cb_loopAvaibilityMQTT);

//static BLEAdvertisedDevice* advDevice;

static uint32_t scanTime = 0; /** 0 = scan forever */

struct trame_value {
    uint8_t     V_id_00;
    float       V_id_01;    // PH
    float       V_id_02;
    float       V_id_03;
    uint16_t    V_id_06;    // ORP
    float       V_id_08;
    float       V_id_09;    // temp Eau
    float       V_id_0A;    // Sel
    float       V_id_0B;
    float       V_id_0C;
    float       V_id_0D;
    float       V_id_0E;
    float       V_id_0F;    // minutes de fonctionement
    float       V_id_10;
    float       V_id_11;    // Volume eau
    float       V_id_12;
    float       V_id_13;
    float       V_id_1F;
    float       V_id_28;
    float       V_id_29;
    float       V_id_2A;
    float       V_id_30;    // Consigne PH
    float       V_id_31;
    float       V_id_32;    // % Acide
    float       V_id_33;    // Production ?
    float       V_id_35;    // Consigne ORP
    float       V_id_37;    // Seuil alarme ORP
    float       V_id_39;    // Inversion
    float       V_id_50;
    float       V_id_51;
    float       V_id_5F;
    float       V_id_69;
    float       V_id_6A;
    float       V_id_8F;
    uint64_t    V_id_90;
    uint64_t    V_id_91;
    float       V_id_92;
    uint16_t     V_id_93;
    bool        V_id_93_b00;
    bool        V_id_93_b01;
    bool        V_id_93_b02;
    bool        V_id_93_b03;
    bool        V_id_93_b04;
    bool        V_id_93_b05;
    bool        V_id_93_b06;
    bool        V_id_93_b07;
    bool        V_id_93_b10;
    bool        V_id_93_b11;
    bool        V_id_93_b12;
    bool        V_id_93_b13;
    bool        V_id_93_b14;
    bool        V_id_93_b15;
    bool        V_id_93_b16;
    bool        V_id_93_b17;
    float       V_id_94;
    uint32_t    V_id_95;    // ID Code
    String      V_id_96;    // Version
    String      V_id_97;    // Slave
    String      V_id_99;    // Nom
    String      V_id_9A;    // SN
    float       V_id_9B;
    float       V_id_9C;
    String      V_id_9D;
    String      V_id_A3;
    float       V_id_B0;
    String      V_id_B1;    // Mac adresse
    String      V_id_D0;
    float       V_id_D1;
    String      V_id_E1;
    String      V_id_E2;
    String      V_id_E4;
    float       V_id_FE;
};

struct trame_value Electrovalue;

HADevice deviceHA;
// dernier paramètre pour le nombre de sensorMQTT à lister
HAMqtt  mqtt(wifiMQTT, deviceHA, 90);

// Command ESP
HASwitch rebootesp("rebootESP");
//List of sensor for HA
HABinarySensor bluetoothConnected("pool_bluetooth_connected");
HASensorNumber wifiStrength("pool_wifi_strength", HASensorNumber::PrecisionP2);
HASensor justsaltIp("pool_ip");
HASensorNumber tempeau("pool_temp",HASensorNumber::PrecisionP1) ; 
HASensorNumber ph("pool_ph",HASensorNumber::PrecisionP1);
HASensorNumber orp("pool_orp",HASensorNumber::PrecisionP0);
HASensorNumber sel("pool_sel",HASensorNumber::PrecisionP1);
HANumber vol("pool_vol",HASensorNumber::PrecisionP0);
HANumber phconsigne("pool_ph_consigne",HASensorNumber::PrecisionP1);
HANumber acide("pool_acide",HASensorNumber::PrecisionP1);
HANumber prod("pool_prod",HASensorNumber::PrecisionP0);
HANumber orpconsigne("pool_orp_consigne",HASensorNumber::PrecisionP0);
HANumber orpalarme("pool_orp_alarme",HASensorNumber::PrecisionP0);
HANumber inversion("pool_inversion",HASensorNumber::PrecisionP0);
HASensorNumber temp02("pool_temp02",HASensorNumber::PrecisionP0);
HASensorNumber temp03("pool_temp03",HASensorNumber::PrecisionP0);
HASensorNumber temp08("pool_temp08",HASensorNumber::PrecisionP0);
HASensorNumber temp0B("pool_temp0B",HASensorNumber::PrecisionP0);
HASensorNumber temp0C("pool_temp0C",HASensorNumber::PrecisionP0);
HASensorNumber temp0D("pool_temp0D",HASensorNumber::PrecisionP0);
HASensorNumber temp0E("pool_temp0E",HASensorNumber::PrecisionP0);
HASensorNumber temp0F("pool_temp0F",HASensorNumber::PrecisionP0);
HASensorNumber temp10("pool_temp10",HASensorNumber::PrecisionP0);
HASensorNumber temp12("pool_temp12",HASensorNumber::PrecisionP0);
HASensorNumber temp13("pool_temp13",HASensorNumber::PrecisionP0);
HASensorNumber temp1F("pool_temp1F",HASensorNumber::PrecisionP0);
HASensorNumber temp28("pool_temp28",HASensorNumber::PrecisionP0);
HASensorNumber temp29("pool_temp29",HASensorNumber::PrecisionP0);
HASensorNumber temp2A("pool_temp2A",HASensorNumber::PrecisionP0);
HASensorNumber temp31("pool_temp31",HASensorNumber::PrecisionP0);
HASensorNumber temp50("pool_temp50",HASensorNumber::PrecisionP0);
HASensorNumber temp51("pool_temp51",HASensorNumber::PrecisionP0);
HASensorNumber temp5F("pool_temp5F",HASensorNumber::PrecisionP0);
HASensorNumber temp69("pool_temp69",HASensorNumber::PrecisionP0);
HASensorNumber temp6A("pool_temp6A",HASensorNumber::PrecisionP0);
HASensorNumber temp8F("pool_temp8F",HASensorNumber::PrecisionP0);
HASensorNumber temp90("pool_temp90",HASensorNumber::PrecisionP0);
HASensorNumber temp91("pool_temp91",HASensorNumber::PrecisionP0);
HASensorNumber temp92("pool_temp92",HASensorNumber::PrecisionP0);
HASensorNumber temp93("pool_temp93",HASensorNumber::PrecisionP0);
HABinarySensor temp93B00("false93B00");
HABinarySensor temp93B01("false93B01");
HABinarySensor temp93B02("false93B02");
HABinarySensor temp93B03("false93B03");
HABinarySensor temp93B04("false93B04");
HABinarySensor temp93B05("false93B05");
HABinarySensor temp93B06("false93B06");
HABinarySensor temp93B07("false93B07");
HABinarySensor temp93B10("false93B10");
HABinarySensor temp93B11("false93B11");
HABinarySensor temp93B12("false93B12");
HABinarySensor temp93B13("false93B13");
HABinarySensor temp93B14("false93B14");
HABinarySensor temp93B15("false93B15");
HABinarySensor temp93B16("false93B16");
HABinarySensor temp93B17("false93B17");
HASensorNumber temp94("pool_temp94",HASensorNumber::PrecisionP0);
HASensorNumber temp95("pool_temp95",HASensorNumber::PrecisionP0);
HASensor temp96("pool_temp96");
HASensor temp97("pool_temp97");
HASensor temp99("pool_temp99");
HASensor temp9A("pool_temp9A");
HASensorNumber temp9B("pool_temp9B",HASensorNumber::PrecisionP0);
HASensorNumber temp9C("pool_temp9C",HASensorNumber::PrecisionP0);
HASensor temp9D("pool_temp9D");
HASensor tempA3("pool_tempA3");
HASensorNumber tempB0("pool_tempB0",HASensorNumber::PrecisionP0);
HASensor tempB1("pool_tempB1");
HASensor tempD0("pool_tempD0");
HASensorNumber tempD1("pool_tempD1",HASensorNumber::PrecisionP0);
HASensor tempE1("pool_tempE1");
HASensor tempE2("pool_tempE2");
HASensor tempE4("pool_tempE4");
HASensorNumber tempFE("pool_tempFE",HASensorNumber::PrecisionP0);

void cb_handleTelnet() {
  if (telnetServer.hasClient()) {
    if (!telnet || !telnet.connected()) {
      if (telnet) telnet.stop();
      telnet = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}

void cb_loopElegantOTA(){
  ElegantOTA.loop();
}


void cb_loopAvaibilityMQTT(){
  mqtt.loop();
  //remove setAvaibility to use native check of Ha integration Shared availability
  deviceHA.setAvailability(true);

  //savoir si la connexion bluetooth est OK ou si le justsalt n'est pas sous tension.
  bluetoothConnected.setState(connected);

}

void cb_loopHaIntegration(){
    taskloopHaIntegration.disable();
    mqtt.loop();
    //remove setAvaibility to use native check of Ha integration Shared availability
    //deviceHA.setAvailability(true);
    wifiStrength.setValue(WiFi.RSSI());
    justsaltIp.setValue(WiFi.localIP().toString().c_str());

    ph.setValue(Electrovalue.V_id_01);
    temp02.setValue(Electrovalue.V_id_02);
    temp03.setValue(Electrovalue.V_id_03);
    orp.setValue(Electrovalue.V_id_06);
    temp08.setValue(Electrovalue.V_id_08);
    tempeau.setValue(Electrovalue.V_id_09);
    sel.setValue(Electrovalue.V_id_0A);
    temp0B.setValue(Electrovalue.V_id_0B);
    temp0C.setValue(Electrovalue.V_id_0C);
    temp0D.setValue(Electrovalue.V_id_0D);
    temp0E.setValue(Electrovalue.V_id_0E);
    temp0F.setValue(Electrovalue.V_id_0F);
    temp10.setValue(Electrovalue.V_id_10);
    //vol.setState(Electrovalue.V_id_11);
    temp12.setValue(Electrovalue.V_id_12);
    temp13.setValue(Electrovalue.V_id_13);
    temp1F.setValue(Electrovalue.V_id_1F);
    temp28.setValue(Electrovalue.V_id_28);
    temp29.setValue(Electrovalue.V_id_29);
    //phconsigne.setState(Electrovalue.V_id_30);
    temp31.setValue(Electrovalue.V_id_31);
    //acide.setCurrentState(Electrovalue.V_id_32);
    //prod.setState(Electrovalue.V_id_33);
    //orpconsigne.setState(Electrovalue.V_id_35);
    //orpalarme.setState(Electrovalue.V_id_37);
    //inversion.setState(Electrovalue.V_id_39);
    temp50.setValue(Electrovalue.V_id_50);
    temp51.setValue(Electrovalue.V_id_51);
    temp5F.setValue(Electrovalue.V_id_5F);
    temp69.setValue(Electrovalue.V_id_69);
    temp6A.setValue(Electrovalue.V_id_6A);
    temp8F.setValue(Electrovalue.V_id_8F);
    temp92.setValue(Electrovalue.V_id_92);
    temp93.setValue(Electrovalue.V_id_93);
    temp93B00.setCurrentState(Electrovalue.V_id_93_b00);
    temp93B01.setCurrentState(Electrovalue.V_id_93_b01);
    temp93B02.setCurrentState(Electrovalue.V_id_93_b02);
    temp93B03.setCurrentState(Electrovalue.V_id_93_b03);
    temp93B04.setCurrentState(Electrovalue.V_id_93_b04);
    temp93B05.setCurrentState(Electrovalue.V_id_93_b05);
    temp93B06.setCurrentState(Electrovalue.V_id_93_b06);
    temp93B07.setCurrentState(Electrovalue.V_id_93_b07);
    temp93B10.setCurrentState(Electrovalue.V_id_93_b10);
    temp93B11.setCurrentState(Electrovalue.V_id_93_b11);
    temp93B12.setCurrentState(Electrovalue.V_id_93_b12);
    temp93B13.setCurrentState(Electrovalue.V_id_93_b13);
    temp93B14.setCurrentState(Electrovalue.V_id_93_b14);
    temp93B15.setCurrentState(Electrovalue.V_id_93_b15);
    temp93B16.setCurrentState(Electrovalue.V_id_93_b16);
    temp93B17.setCurrentState(Electrovalue.V_id_93_b17);
    temp94.setValue(Electrovalue.V_id_94);
    temp95.setValue(Electrovalue.V_id_95);
    temp9B.setValue(Electrovalue.V_id_9B);
    temp9C.setValue(Electrovalue.V_id_9C);
    tempFE.setValue(Electrovalue.V_id_FE);

//    tempB1.setValue(Electrovalue.V_id_B1);
//    tempD0.setValue(Electrovalue.V_id_D0);
    // Valeur des selecteurs



}

void onStateChangedrebootesp (bool state, HASwitch* s){
    if (state == true){
        //lancer le reboot
        state =false;
        //esp_restart();
        //timeScheduler.disable() ;
        esp_restart();
     
        
    }
}


void setupHaIntegration(){
    //HA integration
    deviceHA.setUniqueId(deviceUniqID, sizeof(deviceUniqID));
    deviceHA.setName("justsalt");
    deviceHA.setSoftwareVersion(version);
    deviceHA.setModel("Electolyseur");
    deviceHA.setManufacturer("ricky");

    // This method enables availability for all device types registered on the device.
    // For example, if you have 5 sensors on the same device, you can enable
    // shared availability and change availability state of all sensors using
    // single method call "device.setAvailability(false|true)"
    deviceHA.enableSharedAvailability();

    // Optionally, you can enable MQTT LWT feature. If device will lose connection
    // to the broker, all device types related to it will be marked as offline in
    // the Home Assistant Panel.
    deviceHA.enableLastWill();

    rebootesp.setName("Reboot ESP");
    rebootesp.setIcon("mdi:restart");
    rebootesp.onCommand(onStateChangedrebootesp);

    wifiStrength.setName("Pool wifi Strength");
    wifiStrength.setDeviceClass("signal_strength");
    wifiStrength.setUnitOfMeasurement("dB");

    justsaltIp.setName("ESP justsalt IP");
    justsaltIp.setIcon("mdi:ip-network");

        // HA integration List of Sensor
    tempeau.setName("Water temp");
    tempeau.setUnitOfMeasurement("°C");
    tempeau.setDeviceClass("temperature");
    tempeau.setIcon("mdi:thermometer");
    
    orp.setName("orp");
    orp.setUnitOfMeasurement("mV");
    orp.setIcon("mdi:flash-triangle-outline");

    ph.setName("PH");
    ph.setIcon("mdi:ph");
    ph.setUnitOfMeasurement("ph");

    sel.setName("Sel");
    sel.setUnitOfMeasurement("g/L");
    sel.setIcon("mdi:water-opacity");

    phconsigne.setName("PH Consigne");
    phconsigne.setIcon("mdi:ph");
    phconsigne.setUnitOfMeasurement("ph");
    phconsigne.setStep(0.1);
    phconsigne.setMin(6.8);
    phconsigne.setMax(7.6);
    phconsigne.onCommand(onValueConsignePhChanged);

    orpconsigne.setName("orp Consigne");
    orpconsigne.setUnitOfMeasurement("mV");
    orpconsigne.setIcon("mdi:flash-triangle-outline");
    orpconsigne.setStep(10);
    orpconsigne.setMin(200);
    orpconsigne.setMax(900);
    orpconsigne.onCommand(onValueConsigneorpChanged);
    
    orpalarme.setName("orp Alarme");
    orpalarme.setUnitOfMeasurement("h");
    orpalarme.setIcon("mdi:alarme");
    orpalarme.setStep(6);
    orpalarme.setMin(12);
    orpalarme.setMax(96);
    orpalarme.onCommand(onValueConsigneorpalarmeChanged);
  
    vol.setName("volume piscine");
    vol.setUnitOfMeasurement("m3");
    vol.setIcon("mdi:cup-water");
    vol.setStep(10);
    vol.setMin(10);
    vol.setMax(200);
    vol.onCommand(onValueConsignevolChanged);

    acide.setName("taux Acide");
    acide.setUnitOfMeasurement("%");
    acide.setIcon("mdi:skull-crossbones-outline");
    acide.setStep(1);
    acide.setMin(5);
    acide.setMax(55);
    acide.onCommand(onValueConsigneacideChanged);
    
    prod.setName("production");
    prod.setUnitOfMeasurement("%");
    prod.setIcon("mdi:cog-outline");
    prod.setStep(1);
    prod.setMin(10);
    prod.setMax(100);
    prod.onCommand(onValueConsigneprodChanged);

    inversion.setName("inversion");
    inversion.setUnitOfMeasurement("h");
    inversion.setIcon("mdi:alarme");
    inversion.setStep(1);
    inversion.setMin(2);
    inversion.setMax(24);
    inversion.onCommand(onValueConsigneinversionChanged);
    
    temp02.setName("temp02");
    temp03.setName("temp03");
    temp08.setName("temp08");
    temp0B.setName("temp0B");
    temp0C.setName("temp0C");
    temp0D.setName("temp0D");
    temp0E.setName("temp0E");
    temp0F.setName("minutes de fonctionement");
    temp0F.setUnitOfMeasurement("Min");
    temp0F.setIcon("mdi:counter");

    temp10.setName("temp10");
    temp12.setName("temp12");
    temp13.setName("temp13");
    temp1F.setName("temp1F");
    temp28.setName("temp28");
    temp29.setName("temp29");
    temp31.setName("temp31");
    temp50.setName("temp50");
    temp51.setName("temp51");
    temp5F.setName("temp5F");
    temp69.setName("temp69");
    temp6A.setName("temp6A");
    temp8F.setName("temp8F");
    temp90.setName("temp90");
    temp91.setName("temp91");
    temp92.setName("temp92");
    temp93.setName("temp93");
    temp93B00.setName("temp93B00");
    temp93B01.setName("temp93B01");
    temp93B02.setName("temp93B02");
    temp93B03.setName("temp93B03");
    temp93B04.setName("temp93B04");
    temp93B05.setName("temp93B05");
    temp93B06.setName("temp93B06");
    temp93B07.setName("temp93B07");
    temp93B10.setName("temp93B10");
    temp93B11.setName("temp93B11");
    temp93B12.setName("temp93B12");
    temp93B13.setName("temp93B13");
    temp93B14.setName("temp93B14");
    temp93B15.setName("temp93B15");
    temp93B16.setName("temp93B16");
    temp93B17.setName("temp93B17");
    temp94.setName("temp94");
    temp95.setName("ID code");
    temp95.setIcon("mdi:barcode");

    temp96.setName("version");
    temp96.setIcon("mdi:qrcode");

    temp97.setName("slave");
    temp97.setIcon("mdi:qrcode-edit");

    temp99.setName("nom");
    temp99.setIcon("mdi:card-account-details-outline");

    temp9A.setName("SN");
    temp9A.setIcon("mdi:barcode");

    temp9B.setName("temp9B");
    temp9C.setName("temp9C");
    temp9D.setName("temp9D");
    tempA3.setName("tempA3");
    tempB0.setName("tempB0");
    tempB1.setName("Mac adr");
    tempB1.setIcon("mdi:network-outline");

    tempD0.setName("tempD0");
    tempD1.setName("tempD1");
    tempE1.setName("tempE1");
    tempE2.setName("tempE2");
    tempE4.setName("tempE4");
    tempFE.setName("tempFE");

    bluetoothConnected.setName("Bluetooth Status");

  mqtt.begin( BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD );

}

void setup_telnet(){
  telnetServer.begin();
  telnetServer.setNoDelay(true); 

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.localIP());
  Serial.println(" 23' to connect");
  
  timeScheduler.addTask(taskTelnet);
  taskTelnet.enable();
  Serial.println("Add Task telnet handle");
}

void reconnect_wifi(){
   unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    #ifdef SYSLOG_SERVER
      syslog.log(LOG_INFO, "WIFI lost and reconnect automatically");
    #endif
    previousMillis = currentMillis;
  }
}

void setup_wifi() {
    taskSetup.disable();

    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");

    WiFi.mode(WIFI_STA);
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    //Reconnect wifi Task
    timeScheduler.addTask(taskReconnectWifi);
    taskReconnectWifi.enable();
    Serial.print("Add task to monitor and reconnect wifi");
    
    //Elegant OTA
    webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "Hi! I am ESP32 justsalt to update use http://[yourIP]/update.");
     });
    ElegantOTA.begin(&webServer);    // Start ElegantOTA
    webServer.begin();
    Serial.println("HTTP server started");

    //elegant OTA loop
    timeScheduler.addTask(taskloopElegantOTA);
    taskloopElegantOTA.enable();
    Serial.println("Add task for loop of Elegant OTA");

    //telnet setup
    setup_telnet();
    //launch handle telnet
    timeScheduler.addTask(taskTelnet);
    taskTelnet.enable();
    Serial.print("Add task to Handle telnet");

    //setup mqtt
    setupHaIntegration();
    //loop avaibility for mqtt
    timeScheduler.addTask(taskloopAvaibilityMQTT);
    taskloopAvaibilityMQTT.enable();
    telnet.print("Add task for loop of MQTT");
    
    //cb_setupAndScan_ble();
    doScan = true;
    timeScheduler.addTask(taskConnectBleServer);
    taskConnectBleServer.enable();
    
    Serial.print("Add task to Connec Ble server!!!!!!");
 
}


/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println("Connected");
        /** After connection we should change the parameters if we don't need fast response times.
         *  These settings are 150ms interval, 0 latency, 450ms timout.
         *  Timeout should be a multiple of the interval, minimum is 100ms.
         *  I find a multiple of 3-5 * the interval works best for quick response/reconnect.
         *  Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
         */
        pClient->updateConnParams(120,120,0,60);
    };

    void onDisconnect(NimBLEClient* pClient) {
        Serial.print(pClient->getPeerAddress().toString().c_str());
        Serial.println(" Disconnected - Starting scan");
        NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
    };

    /** Called when the peripheral requests a change to the connection parameters.
     *  Return true to accept and apply them or false to reject and keep
     *  the currently used parameters. Default will return true.
     */
    bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
        if(params->itvl_min < 24) { /** 1.25ms units */
            return false;
        } else if(params->itvl_max > 40) { /** 1.25ms units */
            return false;
        } else if(params->latency > 2) { /** Number of intervals allowed to skip */
            return false;
        } else if(params->supervision_timeout > 100) { /** 10ms units */
            return false;
        }

        return true;
    };

    /********************* Security handled here **********************
    ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest(){
        Serial.println("Client Passkey Request");
        /** return the passkey to send to the server */
        return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key){
        Serial.print("The passkey YES/NO number: ");
        Serial.println(pass_key);
    /** Return false if passkeys don't match. */
        return true;
    };

    /** Pairing process complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc* desc){
        if(!desc->sec_state.encrypted) {
            Serial.println("Encrypt connection failed - disconnecting");
            /** Find the client with the connection handle provided in desc */
            NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
            return;
        }
    };
};


/** Define a class to handle the callbacks when advertisments are received */
// class MyAdvertisedDeviceCallbacks: public NimBLEMyAdvertisedDeviceCallbacks {

//     void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
//         Serial.print("Advertised Device found: ");
//         Serial.println(advertisedDevice->toString().c_str());
//         telnet.println("BLE Advertised Device found: ");
//         telnet.println(advertisedDevice->toString().c_str());

//         char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
//         Serial.println(manufacturerdata);
//         if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
//         //if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
//         //{
//             Serial.println("Found Our device");
//             /** stop scan before connecting */
//             NimBLEDevice::getScan()->stop();
//             /** Save the device reference in a global for the client to use*/
//             advDevice = advertisedDevice;
//             /** Ready to connect now */
//             doConnect = true;
//             doScan = false;
//         }
//     };
// };

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
        std::string str = "------------------------------- \r\n" ;
        str += "BLE Advertised Device found: \r\n";
        str += ": Name = " + std::string(advertisedDevice->getName().c_str());
        str += ", adr = " + std::string(advertisedDevice->getAddress().toString().c_str());
        
        Serial.println(str.c_str());
        telnet.println(str.c_str());

        char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
        Serial.println(manufacturerdata);
        telnet.println(manufacturerdata);

        if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
        // if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)) {

            BLEDevice::getScan()->stop();
            advDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
            
            Serial.print("Found our device!  address: ");

            doConnect = true;
            doScan = false;

            // timeScheduler.addTask(taskConnectBleServer);
            taskConnectBleServer.forceNextIteration();
            Serial.println("add Task Connect Ble server");

        } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks



/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
    std::string str = (isNotify == true) ? "Notification" : "Indication";
    str += " from ";
    /** NimBLEAddress and NimBLEUUID have std::string operators */
    str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
    str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
    str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
    str += ", Countertrame = " + std::to_string(countertrame);
    str += ", Value = " + std::string((char*)pData, length);
    countertrame++;
    if (countertrame > 65000){
        countertrame=5;
        countertrameold=0;
    } 
    Serial.println(str.c_str());
    Serial.print("recep chaine taille =");
    Serial.println(length);
    telnet.print("recep chaine taille = ");
    telnet.println(length);
    if (length > 5){
        int index =3;
        while (index +2 < length) {
            uint8_t idvaleur = pData[index];
            int taillevaleur = pData[index + 1 ];
            String Valuetrame = ""; 
            for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                String converttrame = String(pData[i],HEX) ;
                if (converttrame.length() == 1 ){ 
                    converttrame = "0" + converttrame ;
                    }
                Valuetrame += converttrame;
                if (i != index + 1 + taillevaleur){
                    Valuetrame += "." ; 
                }
            }
            Valuetrame.toUpperCase();

            // Serial.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            // telnet.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            std::string str = "Trame " + std::to_string(countertrame);
            str += ": Values ID  = " + std::to_string(idvaleur);
            str += ", taille = " + std::to_string(taillevaleur);
            str += ", Value = " + std::string(Valuetrame.c_str());

            Serial.println(str.c_str());
            telnet.println(str.c_str());

            switch (idvaleur) {
            case 0x00: { 
                Electrovalue.V_id_00 = 0;
                }break;
        
            case 0x01: {
                uint8_t phhex = pData[index + 2];
                float ph = static_cast<float>(phhex)/10;
                Electrovalue.V_id_01 = ph;
                Serial.print("PH : " );
                Serial.println(ph);
                }break;

            case 0x02: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_02 = temp;
                }break;

            case 0x03: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_03 = temp; 
                }break;

            case 0x06: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index + 3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_06 = temphex;
                }break;

            case 0x08: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_08 = temp;
                }break;

            case 0x09: {
                uint16_t tempeauhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float tempeauf = static_cast<float>(tempeauhex)/10;
                tempeau.setValue(tempeauf);
                }break;
            
            case 0x0A: {
                uint8_t selhex = pData[index + 2] ;
                float sel = static_cast<float>(selhex)/10;
                Electrovalue.V_id_0A = sel;
                }break;

            case 0x0B: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_0B = temp;
                }break;
            
            case 0x0C: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_0C = temp;
                }break;

            case 0x0D: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_0D = temp;
                }break;

            case 0x0E: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_0E = temp;
                }break;

            case 0x0F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_0F = temp;
                }break;
            
            case 0x10: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_10 = temp;
                }break;

            case 0x11: {
                uint16_t volhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float volf = static_cast<float>(volhex);
                vol.setState(volf);
                }break;
            
            case 0x12: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_12 = temp;
                }break;

            case 0x13: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_13 = temp;
                }break;

            case 0x1F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_1F = temp;
                }break;
            
            case 0x28: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_28 = temp;
                }break;
            
            case 0x29: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_29 = temp;
                }break;
            
            case 0x2A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_2A = temp;
                }break;
            
            case 0x30: {
                uint8_t phchex = pData[index + 2] ;
                float phcf = static_cast<float>(phchex)/10;
                phconsigne.setState(phcf);
                }break;
            
            case 0x31: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_31 = temp;
                }break;         

            case 0x32: {
                uint8_t acidehex = pData[index + 2] ;
                float acidef = static_cast<float>(acidehex);
                //Electrovalue.V_id_32 = acidef;
                acide.setState(acidef);
                }break;
            
            case 0x33: {
                uint8_t prodhex = pData[index + 2] ;
                float prodf = static_cast<float>(prodhex);
                //Electrovalue.V_id_33 = prodf;
                prod.setState(prodf);
                }break;

            case 0x35: {
                uint8_t orpchex = pData[index + 2] ;
                float orpcf = static_cast<float>(orpchex)*10;
                //Electrovalue.V_id_35 = orpcf;
                orpconsigne.setState(orpcf);
                }break;
            
            case 0x37: {
                uint8_t alarmeorphex = pData[index + 2] ;
                float alarmeorpf = static_cast<float>(alarmeorphex);
                //Electrovalue.V_id_37 = alarmeorpf;
                orpalarme.setState(alarmeorpf);
                }break;
    
            case 0x39: {
                uint8_t inversionhex = pData[index + 2] ;
                float inversionvalf = static_cast<float>(inversionhex);
                //Electrovalue.V_id_39 = inversionvalf;
                inversion.setState(inversionvalf);
                }break;

            case 0x50: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_50 = temp;
                }break;

            case 0x51: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_51 = temp;
                }break;

            case 0x5F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_5F = temp;
                }break;
            
            case 0x69: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_69 = temp;
                }break;

            case 0x6A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_6A = temp;
                }break;

            case 0x8F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_8F = temp;
                }break;
                
            case 0x90: {
                // 90.08.00.00.00.00.00.00.00.14.
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp90.setValue(temp);
                //Electrovalue.V_id_90 = temp;
                }break;

            case 0x91: {
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp91.setValue(temp);
                //temp91.setValue(tempstring.c_str());
                }break;
            
            case 0x92: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_92 = temp;
                }break;

            case 0x93: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_93 = temp;
                temp93B00.setCurrentState((bool)((temphex & 0x0001) ));
                temp93B01.setCurrentState((bool)((temphex & 0x0002) ));
                temp93B02.setCurrentState((bool)((temphex & 0x0004) ));
                temp93B03.setCurrentState((bool)((temphex & 0x0008) ));
                temp93B04.setCurrentState((bool)((temphex & 0x0010) ));
                temp93B05.setCurrentState((bool)((temphex & 0x0020) ));
                temp93B06.setCurrentState((bool)((temphex & 0x0040) ));
                temp93B07.setCurrentState((bool)((temphex & 0x0080) ));
                temp93B10.setCurrentState((bool)((temphex & 0x0100) ));
                temp93B11.setCurrentState((bool)((temphex & 0x0200) ));
                temp93B12.setCurrentState((bool)((temphex & 0x0400) ));
                temp93B13.setCurrentState((bool)((temphex & 0x0800) ));
                temp93B14.setCurrentState((bool)((temphex & 0x1000) ));
                temp93B15.setCurrentState((bool)((temphex & 0x2000) ));
                temp93B16.setCurrentState((bool)((temphex & 0x4000) ));
                temp93B17.setCurrentState((bool)((temphex & 0x8000) ));
                
                
                }break;

            case 0x94: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_94 = temp;
                }break;

            case 0x95: {
                uint32_t idcodehex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float idcodefloat = static_cast<float>(idcodehex);
                Electrovalue.V_id_95 = idcodefloat;
                }break;
            
            case 0x96: {
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                // telnet.println( tempstring.c_str());
                temp96.setValue(Valuetrame.c_str());
                }break;
            
            case 0x97: {
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                // telnet.println( tempstring.c_str());
                temp97.setValue(Valuetrame.c_str());
                }break;

            case 0x99: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp99.setValue(tempstring.c_str());
                }break;

            case 0x9A: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp9A.setValue(tempstring.c_str());
                }break;

            case 0x9B: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_9B = temp;
                }break;

            case 0x9C: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_9C = temp;
                }break;

            case 0x9D: {
                //  9D.08.00.00.00.00.00.00.01.FF
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                temp9D.setValue(Valuetrame.c_str());
                }break;
            
            case 0xA3: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                // telnet.println( tempstring.c_str());
                tempA3.setValue(Valuetrame.c_str());
                }break;
            
            case 0xB0: {

                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_B0 = temp;
                }break;
                
    
            case 0xB1: {
            //     std::string tempstring = rawhex.substr((index + 2 )* 2, 12);
            //     id(JustSalt_macbt_textsensor).publish_state(tempstring);
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += String(pData[i],HEX) ;
                    if (i != index + 1 + taillevaleur){
                        tempstring += ":" ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempB1.setValue(tempstring.c_str());
                }break;
            
            case 0xD0: {
            //     //D0.06.30.00.00.00.00.00
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    String convert = String(pData[i],HEX) ;
                    if (convert.length() == 1 ){ 
                        convert = "0" + convert ;
                        }
                    tempstring += convert;
                    if (i != index + 1 + taillevaleur){
                        tempstring += "." ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempD0.setValue(tempstring.c_str());
                }break;

            case 0xD1: {
            //     uint8_t temphex = pData[index + 2];
            //     float temp = static_cast<float>(temphex);
            //     id(JustSalt_tempd1_sensor).publish_state(temp);
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_D1 = temp;
                }break;
            
            

            case 0xE1: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                tempE1.setValue(Valuetrame.c_str());
                }break;
                
            case 0xE2: {
                // E2.0F.18.04.16.0A.22.10.00.00.00.41.3E.DC.00.00.00
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                tempE2.setValue(Valuetrame.c_str());
                }break;

            case 0xE4: {
                // E4.0F.18.04.12.15.00.00.46.00.0B.13.00.00.00.00.00
                // String tempstring = ""; 
                // for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                //     String convert = String(pData[i],HEX) ;
                //     if (convert.length() == 1 ){ 
                //         convert = "0" + convert ;
                //         }
                //     tempstring += convert;
                //     if (i != index + 1 + taillevaleur){
                //         tempstring += "." ; 
                //     }
                // }
                // tempstring.toUpperCase();
                tempE4.setValue(Valuetrame.c_str());
                }break;
    
            case 0xFE: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                Electrovalue.V_id_FE = temp;
                }break;
    
            default: {
                Serial.print( "idvaleur Non implementee " );
                Serial.println(idvaleur);
                };
            }
            index = index + 2 + taillevaleur ;

        }
        Serial.println("fin traitement trame");
        //cb_loopHaIntegration();
        Serial.println("fin cb_loopHaIntegration");
        
    }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}


/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(advDevice->toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  // Connect to the remove BLE Server.
  pClient->connect(advDevice); 
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  //il faut s'abonner la la charactisitic "indication" afin de recevoir les notification de publication
  if(pRemoteCharacteristic->canIndicate())
    pRemoteCharacteristic->subscribe(false, notifyCB);

    pRemoteCharWrite = pRemoteService->getCharacteristic(charUUIDwrite);
    if (pRemoteCharWrite == nullptr) {
        Serial.print("Failed to find our characteristic UUID: ");
        Serial.println(charUUIDwrite.toString().c_str());
        pClient->disconnect();
        return false;
    } else {
        Serial.println("caracteristic Write - Found our characteristic");
    }


  connected = true;
  return true;
  
}

void cb_setupAndScan_ble() {
    taskBleSetupAndScan.disable();
    Serial.println("Starting NimBLE Client");
    /** Initialize NimBLE, no device name spcified as we are not advertising */
    NimBLEDevice::init("");
    /** Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /** 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, secure connections.
     *
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power, default is 3db */
    #ifdef ESP_PLATFORM
        NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
    #else
        NimBLEDevice::setPower(9); /** +9db */
    #endif

    /** Optional: set any devices you don't want to get advertisments from */
    // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));

    /** create new scan */
    NimBLEScan* pScan = NimBLEDevice::getScan();
     Serial.println("avant callback");
    /** create a callback that gets called when advertisers are found */
    pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(45);
    pScan->setWindow(15);

    /** Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    Serial.println("avant set active scan");
    pScan->setActiveScan(true);
    /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
     *  Optional callback for when scanning stops.
     */
    //pScan->start(scanTime, scanEndedCB);
    pScan->start(10, false);
    Serial.println("Fin de cb_setupAndScan_ble");
    
}

void onValueConsignePhChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of PH Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(48,numberFloat * 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}
void onValueConsigneorpChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(53,numberFloat / 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}

void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Alarme ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x37,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsignevolChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Volume Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x11,numberFloat ,2);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneacideChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Acide Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x32,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneprodChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Production Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); ;
        onwriteble(0x33,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneinversionChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Version Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str());        
        onwriteble(0x39,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}
void onwriteble (int ValueID ,float numberset,int ValueSizea){
    Serial.println("debut onwriteble : ");
    static uint8_t trame[80] = {0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //union float2bytes { float f; char b[sizeof(float)]; } ;
    typedef union _data {
        int f;
        char  s[2];
    } myData;

    //float2bytes myvalue;
    myData myvalue;
    myvalue.f =  static_cast<int>(numberset);
    trame[3]= ValueID ;
    trame[4]= ValueSizea;
    switch (ValueSizea){
    case 1:
        //uint8_t hexnumberset = (numberset) & 0xff;
        trame[5]= myvalue.s[0];
        break;

    case 2:
        trame[5]= 0x00;
        trame[6]= myvalue.s[0];
        break;
    
    default:
        break;
    }
    //Serial.println(sizeof(trame));
    std::string str = "send des valeur ";
    str += ": Number a set = " + std::to_string(numberset);
    str += ": Myvalue f = " + std::to_string(myvalue.f);
    str += ": Myvalue s 0 = " + std::to_string(myvalue.s[0]);
    str += ": Myvalue s 1 = " + std::to_string(myvalue.s[1]);
    str += " : value ID = " + std::to_string(trame[3]);
    str += ", taille = " + std::to_string(trame[4]);
    str += ", Value ch 5= " + std::to_string(trame[5]);
    str += ", Value ch 6= " + std::to_string(trame[6]);

    Serial.println(str.c_str());
    telnet.println(str.c_str());
    
    bool writeResponse;
    writeResponse = pRemoteCharWrite->writeValue(trame, 80, true);
    Serial.println(writeResponse);
    str = "onwriteble  : ";
    str += "send trame result = " + std::to_string(writeResponse);
    if (writeResponse == 1) {
        str += " OK ";
    }else {
        str += " KO ";
    }
    Serial.println(str.c_str());
    telnet.println(str.c_str());
}



void setup (){
    Serial.begin(115200);
    timeScheduler.init();

    timeScheduler.addTask(taskSetup);
    taskSetup.enable();
    Serial.println("add Task to setup justsalt");
}


void loop (){
    timeScheduler.execute();
}


void cb_connectBleServer(){
  //connection au serveur Ble
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
    Serial.println("debut cb_connectBleServer");
   
    if (connected == true ){
        Serial.println("connected = true");

        // check si on est connecté BLE (evolution des trames notifié ) 
        if (countertrame != countertrameold){
            //Serial.println("recepion notification ok donc conecté ");
            std::string str = "recepion notification ok donc conecté ";
            str += ": countertrame  = " + std::to_string(countertrame);
            str += ", countertrameold = " + std::to_string(countertrameold);
            
            Serial.println(str.c_str());
            telnet.println(str.c_str());

            countertrameold=countertrame;
            delay(50);
        } else {
            Serial.println("recepion notification KO donc liaison BT HS ");
            connected = false;
            doConnect = true;
        } 
        Serial.println("apres check connection");
        
    }else{
        if (doConnect == true) {
            Serial.println("doConnect is true");
            if (connectToServer()) {
                Serial.println("We are now connected to the BLE Server.");
                //write sur la prochaine itération de la task
                taskConnectBleServer.forceNextIteration();
            } else {
                Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                doScan = true;
            }
            doConnect = false;
            Serial.println("doConnect ********************* to false ");
            //on relance un scan
        }
        if (doScan == true){
            Serial.println( "add Task to Scan Ble devices");
            timeScheduler.addTask(taskBleSetupAndScan);
            taskBleSetupAndScan.enable();
        }
        Serial.println("connected = false");
    } 
}

pour faire fonctionner le code
il faut sur HA un serveur MQTT en Auto-discover

pour compiler le code (j’ai utilisé):
VSCode (Microsoft)
Puthon 3.11
et ensuite installer dans VSCode le module Platforme IO ,PIO pour les intimes

puis telecharger
Ricky197503/haJustsalt (github.com)
et vous ouvrez le projet depuis VSC\pio le projet est le repertoir ESP32BT
vous modifier les infos , Wifi , mqtt serveur et l address mac du justsalt pour la creation de l object MQTT , compilation upload et c est fini :slight_smile:

1 « J'aime »

Ah super boulot !
J’ai une 404 qd je clique sur le lien github

En fait ton repo n’est pas public.

Petite question sur la version esphome tu pense que je peux utiliser ton code pour qui fonctionne comme Bluetooth proxy ? J’ai des xiaomi plant à côté

Bonjour, j’ai installé le code de mon électrolyseur ePool pour récupérer les données, au début tout va bien et au bout de 5min, perte de connexion. J’ai essayé plein de trucs mais rien ne fonctionne:

  • esp à 4m de la borne wifi
  • ip fixe
  • allumage extinction pour réactiver « 5 minutes » l’ESP mais après ça se coupe
  • changement de câble
  • changement de chargeur
    Si je lance le log pendant le blocage, j’ai ça
INFO ESPHome 2024.4.2
INFO Reading configuration /config/esphome/justsalt.yaml...
INFO Detected timezone 'Europe/Paris'
INFO Starting log output from 192.168.1.240 using esphome API
WARNING Can't connect to ESPHome API for justsalt @ 192.168.1.240: Timeout while connecting to [AddrInfo(family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, proto=6, sockaddr=IPv4Sockaddr(address='192.168.1.240', port=6053))] (TimeoutAPIError)
INFO Trying to connect to justsalt @ 192.168.1.240 in the background``

Est-ce que c’est à cause de la saturation de l’ESP ?

salut
effectivement ca manque de stabilité
je m en suis aperçu hier et je suis dessus
je pense je suis dessus :slight_smile:
j essaye plusieurs truc mais du coup les test sont plus long :slight_smile:
je viens d ajouter un bouton pour couper le BLE , je vais essayer en cassant la connexion pour voir si ca rattrape le coup
mais merci du retour :slight_smile:

Aloès j’ai eu pas mal de souci aussi avec l alim j’ai pris une 1.5A

Pour info moi cest super stable

je teste et je vous dis

Bon avec une alimentation de 3A, même bazar. Et si dans le code j’enlevais tout qui ne me sert à rien (tous les temp par exemple, est-ce que cela allégerait le code ?) Si oui comment faire ,

je vous poste ma dernière version (plus stable)
Mais pour ma part les valeurs temp0B et temp0C change toutes les secondes
je vais voir pour les limités
je me demande si ce n est pas un retour sur un courant je vais mesurer le voltage de l électrolyseur pour voir

#define ARDUINOHA_DEBUG
#include <Arduino.h>


/** Electolyseur JustSalt & co
 *
 *  Passerelle Bt Justsalt to Mqtt
 *
 *  Created: Mai 2025
 *      Author: Ricky
 *
*/


#include <NimBLEDevice.h>
#include <ArduinoHA.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
#include <TaskScheduler.h>


const char* version = "0.0.1";
// Update these with values suitable for your wifi network.
const char* ssid = "xxxxxxxxxxxxx-wifi";
const char* password = "xxxxxxxxxxxxxxxxxxx";

//Home Assistant integration
// configure here your HA params for connection to MQTT server
#define BROKER_ADDR IPAddress(192,168,50,11)
#define BROKER_USERNAME     "" // replace with your credentials
#define BROKER_PASSWORD     ""

//Unique device for HA integration
const byte deviceUniqID[] = { 0x94, 0xDE, 0xB8, 0xA1, 0x1A, 0xAC };

//justsalt service and char UUID
// The remote service we wish to connect to.
static  BLEUUID serviceUUID("09912756-7b32-4629-aeb1-b309d9a338ae");
// The characteristic of the remote service we are interested in.
static  BLEUUID    charUUID("ef785c24-22bb-463d-b651-0b7445ba091c");
static  BLEUUID    charUUIDwrite("4d32c5e5-2bb5-45c6-8c89-6f59bb3930d2");

static NimBLEAdvertisedDevice* advDevice;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static int countertrameold = 0;
static int countertrame = 5;
static int countertrametry = 0;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static NimBLERemoteCharacteristic* pRemoteCharWrite;

static NimBLEClient* pClient;
static NimBLERemoteService* pSvc ;
static NimBLERemoteCharacteristic* pChr ;
static NimBLERemoteDescriptor* pDsc ;

WiFiClient wifiMQTT;
WiFiClient telnet;
WiFiServer telnetServer(23);

Scheduler timeScheduler;
AsyncWebServer webServer(80);


unsigned long previousMillis = 0;
unsigned long interval = 30000;

void scanEndedCB(NimBLEScanResults results);
void setup_wifi();
void setup_glogal();
void reconnect_wifi();
void setupHaIntegration();
void setup_telnet();
void cb_handleTelnet();
void cb_loopHaIntegration();
void cb_loopElegantOTA();
void cb_loopAvaibilityMQTT();
void cb_setupAndScan_ble();
void cb_connectBleServer();
void onValueConsignePhChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender);
void onValueConsignevolChanged( HANumeric number, HANumber* sender);
void onValueConsigneacideChanged( HANumeric number, HANumber* sender);
void onValueConsigneprodChanged( HANumeric number, HANumber* sender);
void onValueConsigneinversionChanged( HANumeric number, HANumber* sender);
void onwriteble (int ValueID ,float numberset,int ValueSizea);

bool connectToServer();


Task taskSetup(5000,TASK_FOREVER,&setup_wifi);
Task taskReconnectWifi(interval,TASK_FOREVER,&reconnect_wifi);
Task taskTelnet(5000,TASK_FOREVER,&cb_handleTelnet);
Task taskloopElegantOTA(5000,TASK_FOREVER,&cb_loopElegantOTA);
Task taskBleSetupAndScan(30000, TASK_FOREVER, &cb_setupAndScan_ble);
Task taskConnectBleServer(50000, TASK_FOREVER, &cb_connectBleServer);
Task taskloopHaIntegration(5000, TASK_FOREVER,&cb_loopHaIntegration);
Task taskloopAvaibilityMQTT(3000, TASK_FOREVER, &cb_loopAvaibilityMQTT);

//static BLEAdvertisedDevice* advDevice;

static uint32_t scanTime = 0; /** 0 = scan forever */

struct trame_value {
    uint8_t     V_id_00;
    float       V_id_01;    // PH
    float       V_id_02;
    float       V_id_03;
    uint16_t    V_id_06;    // ORP
    float       V_id_08;
    float       V_id_09;    // temp Eau
    float       V_id_0A;    // Sel
    float       V_id_0B;
    float       V_id_0C;
    float       V_id_0D;
    float       V_id_0E;
    float       V_id_0F;    // minutes de fonctionement
    float       V_id_10;
    float       V_id_11;    // Volume eau
    float       V_id_12;
    float       V_id_13;
    float       V_id_1F;
    float       V_id_28;
    float       V_id_29;
    float       V_id_2A;
    float       V_id_30;    // Consigne PH
    float       V_id_31;
    float       V_id_32;    // % Acide
    float       V_id_33;    // Production ?
    float       V_id_35;    // Consigne ORP
    float       V_id_37;    // Seuil alarme ORP
    float       V_id_39;    // Inversion
    float       V_id_50;
    float       V_id_51;
    float       V_id_5F;
    float       V_id_69;
    float       V_id_6A;
    float       V_id_8F;
    uint64_t    V_id_90;
    uint64_t    V_id_91;
    float       V_id_92;
    uint16_t     V_id_93;
    bool        V_id_93_b00;
    bool        V_id_93_b01;
    bool        V_id_93_b02;
    bool        V_id_93_b03;
    bool        V_id_93_b04;
    bool        V_id_93_b05;
    bool        V_id_93_b06;
    bool        V_id_93_b07;
    bool        V_id_93_b10;
    bool        V_id_93_b11;
    bool        V_id_93_b12;
    bool        V_id_93_b13;
    bool        V_id_93_b14;
    bool        V_id_93_b15;
    bool        V_id_93_b16;
    bool        V_id_93_b17;
    float       V_id_94;
    uint32_t    V_id_95;    // ID Code
    String      V_id_96;    // Version
    String      V_id_97;    // Slave
    String      V_id_99;    // Nom
    String      V_id_9A;    // SN
    float       V_id_9B;
    float       V_id_9C;
    String      V_id_9D;
    String      V_id_A3;
    float       V_id_B0;
    String      V_id_B1;    // Mac adresse
    String      V_id_D0;
    float       V_id_D1;
    String      V_id_E1;
    String      V_id_E2;
    String      V_id_E4;
    float       V_id_FE;
};

struct trame_value Electrovalue;

HADevice deviceHA;
// dernier paramètre pour le nombre de sensorMQTT à lister
HAMqtt  mqtt(wifiMQTT, deviceHA, 90);

// Command ESP
HASwitch rebootesp("rebootESP");
HASwitch bluetoothesp("bluetoothESP");

//List of sensor for HA
HABinarySensor bluetoothConnected("pool_bluetooth_connected");
HASensorNumber wifiStrength("pool_wifi_strength", HASensorNumber::PrecisionP2);
HASensor justsaltIp("pool_ip");
HASensorNumber tempeau("pool_temp",HASensorNumber::PrecisionP1) ; 
HASensorNumber ph("pool_ph",HASensorNumber::PrecisionP1);
HASensorNumber orp("pool_orp",HASensorNumber::PrecisionP0);
HASensorNumber sel("pool_sel",HASensorNumber::PrecisionP1);
HANumber vol("pool_vol",HASensorNumber::PrecisionP0);
HANumber phconsigne("pool_ph_consigne",HASensorNumber::PrecisionP1);
HANumber acide("pool_acide",HASensorNumber::PrecisionP1);
HANumber prod("pool_prod",HASensorNumber::PrecisionP0);
HANumber orpconsigne("pool_orp_consigne",HASensorNumber::PrecisionP0);
HANumber orpalarme("pool_orp_alarme",HASensorNumber::PrecisionP0);
HANumber inversion("pool_inversion",HASensorNumber::PrecisionP0);
HASensorNumber temp02("pool_temp02",HASensorNumber::PrecisionP0);
HASensorNumber temp03("pool_temp03",HASensorNumber::PrecisionP0);
HASensorNumber temp08("pool_temp08",HASensorNumber::PrecisionP0);
HASensorNumber temp0B("pool_temp0B",HASensorNumber::PrecisionP0);
HASensorNumber temp0C("pool_temp0C",HASensorNumber::PrecisionP0);
HASensorNumber temp0D("pool_temp0D",HASensorNumber::PrecisionP0);
HASensorNumber temp0E("pool_temp0E",HASensorNumber::PrecisionP0);
HASensorNumber temp0F("pool_temp0F",HASensorNumber::PrecisionP0);
HASensorNumber temp10("pool_temp10",HASensorNumber::PrecisionP0);
HASensorNumber temp12("pool_temp12",HASensorNumber::PrecisionP0);
HASensorNumber temp13("pool_temp13",HASensorNumber::PrecisionP0);
HASensorNumber temp1F("pool_temp1F",HASensorNumber::PrecisionP0);
HASensorNumber temp28("pool_temp28",HASensorNumber::PrecisionP0);
HASensorNumber temp29("pool_temp29",HASensorNumber::PrecisionP0);
HASensorNumber temp2A("pool_temp2A",HASensorNumber::PrecisionP0);
HASensorNumber temp31("pool_temp31",HASensorNumber::PrecisionP0);
HASensorNumber temp50("pool_temp50",HASensorNumber::PrecisionP0);
HASensorNumber temp51("pool_temp51",HASensorNumber::PrecisionP0);
HASensorNumber temp5F("pool_temp5F",HASensorNumber::PrecisionP0);
HASensorNumber temp69("pool_temp69",HASensorNumber::PrecisionP0);
HASensorNumber temp6A("pool_temp6A",HASensorNumber::PrecisionP0);
HASensorNumber temp8F("pool_temp8F",HASensorNumber::PrecisionP0);
HASensorNumber temp90("pool_temp90",HASensorNumber::PrecisionP0);
HASensorNumber temp91("pool_temp91",HASensorNumber::PrecisionP0);
HASensorNumber temp92("pool_temp92",HASensorNumber::PrecisionP0);
HASensorNumber temp93("pool_temp93",HASensorNumber::PrecisionP0);
HABinarySensor temp93B00("false93B00");
HABinarySensor temp93B01("false93B01");
HABinarySensor temp93B02("false93B02");
HABinarySensor temp93B03("false93B03");
HABinarySensor temp93B04("false93B04");
HABinarySensor temp93B05("false93B05");
HABinarySensor temp93B06("false93B06");
HABinarySensor temp93B07("false93B07");
HABinarySensor temp93B10("false93B10");
HABinarySensor temp93B11("false93B11");
HABinarySensor temp93B12("false93B12");
HABinarySensor temp93B13("false93B13");
HABinarySensor temp93B14("false93B14");
HABinarySensor temp93B15("false93B15");
HABinarySensor temp93B16("false93B16");
HABinarySensor temp93B17("false93B17");
HASensorNumber temp94("pool_temp94",HASensorNumber::PrecisionP0);
HASensorNumber temp95("pool_temp95",HASensorNumber::PrecisionP0);
HASensor temp96("pool_temp96");
HASensor temp97("pool_temp97");
HASensor temp99("pool_temp99");
HASensor temp9A("pool_temp9A");
HASensorNumber temp9B("pool_temp9B",HASensorNumber::PrecisionP0);
HASensorNumber temp9C("pool_temp9C",HASensorNumber::PrecisionP0);
HASensor temp9D("pool_temp9D");
HASensor tempA3("pool_tempA3");
HASensorNumber tempB0("pool_tempB0",HASensorNumber::PrecisionP0);
HASensor tempB1("pool_tempB1");
HASensor tempD0("pool_tempD0");
HASensorNumber tempD1("pool_tempD1",HASensorNumber::PrecisionP0);
HASensor tempE1("pool_tempE1");
HASensorNumber tempE1O8("pool_tempE1O8",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O9("pool_tempE1O9",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O10("pool_tempE1O10",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O11("pool_tempE1O11",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O12("pool_tempE1O12",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O13("pool_tempE1O13",HASensorNumber::PrecisionP0);


// HABinarySensor tempE1O9B00("falseE1O9B00");
// HABinarySensor tempE1O9B01("falseE1O9B01");
// HABinarySensor tempE1O9B02("falseE1O9B02");
// HABinarySensor tempE1O9B03("falseE1O9B03");
// HABinarySensor tempE1O9B04("falseE1O9B04");
// HABinarySensor tempE1O9B05("falseE1O9B05");
// HABinarySensor tempE1O9B06("falseE1O9B06");
// HABinarySensor tempE1O9B07("falseE1O9B07");
// HABinarySensor tempE1O10B00("falseE1O10B00");
// HABinarySensor tempE1O10B01("falseE1O10B01");
// HABinarySensor tempE1O10B02("falseE1O10B02");
// HABinarySensor tempE1O10B03("falseE1O10B03");
// HABinarySensor tempE1O10B04("falseE1O10B04");
// HABinarySensor tempE1O10B05("falseE1O10B05");
// HABinarySensor tempE1O10B06("falseE1O10B06");
// HABinarySensor tempE1O10B07("falseE1O10B07");
// HABinarySensor tempE1O11B00("falseE1O11B00");
// HABinarySensor tempE1O11B01("falseE1O11B01");
// HABinarySensor tempE1O11B02("falseE1O11B02");
// HABinarySensor tempE1O11B03("falseE1O11B03");
// HABinarySensor tempE1O11B04("falseE1O11B04");
// HABinarySensor tempE1O11B05("falseE1O11B05");
// HABinarySensor tempE1O11B06("falseE1O11B06");
// HABinarySensor tempE1O11B07("falseE1O11B07");
// HABinarySensor tempE1O12B00("falseE1O12B00");
// HABinarySensor tempE1O12B01("falseE1O12B01");
// HABinarySensor tempE1O12B02("falseE1O12B02");
// HABinarySensor tempE1O12B03("falseE1O12B03");
// HABinarySensor tempE1O12B04("falseE1O12B04");
// HABinarySensor tempE1O12B05("falseE1O12B05");
// HABinarySensor tempE1O12B06("falseE1O12B06");
// HABinarySensor tempE1O12B07("falseE1O12B07");

HASensor tempE2("pool_tempE2");
HASensor tempE4("pool_tempE4");
HASensorNumber tempFE("pool_tempFE",HASensorNumber::PrecisionP0);

void cb_handleTelnet() {
  if (telnetServer.hasClient()) {
    if (!telnet || !telnet.connected()) {
      if (telnet) telnet.stop();
      telnet = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}

void cb_loopElegantOTA(){
  ElegantOTA.loop();
}


void cb_loopAvaibilityMQTT(){
  mqtt.loop();
  //remove setAvaibility to use native check of Ha integration Shared availability
  //deviceHA.setAvailability(true);

  //savoir si la connexion bluetooth est OK ou si le justsalt n'est pas sous tension.
  bluetoothConnected.setState(connected);

}

void cb_loopHaIntegration(){
    //Serial.println("cb_loopHaIntegration");
    taskloopHaIntegration.disable();
    mqtt.loop();
    //remove setAvaibility to use native check of Ha integration Shared availability
    deviceHA.setAvailability(true);

    wifiStrength.setValue(WiFi.RSSI());
    justsaltIp.setValue(WiFi.localIP().toString().c_str());
    //Serial.print("E1 =");
    //Serial.println(Electrovalue.V_id_E1);
    //Serial.print("E1[8] =");
    //Serial.println(Electrovalue.V_id_E1[8]);
    //Serial.println((static_cast<int>(Electrovalue.V_id_E1[8]) ));

    
    // tempE1O9B00.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x01) ));
    // tempE1O9B01.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x02) ));
    // tempE1O9B02.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x04) ));
    // tempE1O9B03.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x08) ));
    // tempE1O9B04.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x10) ));
    // tempE1O9B05.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x20) ));
    // tempE1O9B06.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x40) ));
    // tempE1O9B07.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x80) ));

    // tempE1O10B00.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x01) ));
    // tempE1O10B01.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x02) ));
    // tempE1O10B02.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x04) ));
    // tempE1O10B03.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x08) ));
    // tempE1O10B04.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x10) ));
    // tempE1O10B05.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x20) ));
    // tempE1O10B06.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x40) ));
    // tempE1O10B07.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x80) ));

    // tempE1O11B00.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x01) ));
    // tempE1O11B01.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x02) ));
    // tempE1O11B02.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x04) ));
    // tempE1O11B03.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x08) ));
    // tempE1O11B04.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x10) ));
    // tempE1O11B05.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x20) ));
    // tempE1O11B06.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x40) ));
    // tempE1O11B07.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x80) ));

    // tempE1O12B00.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x01) ));
    // tempE1O12B01.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x02) ));
    // tempE1O12B02.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x04) ));
    // tempE1O12B03.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x08) ));
    // tempE1O12B04.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x10) ));
    // tempE1O12B05.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x20) ));
    // tempE1O12B06.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x40) ));
    // tempE1O12B07.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x80) ));
    //Serial.println("FIN cb_loopHaIntegration");
}

void onStateChangedrebootesp (bool state, HASwitch* s){
    if (state == true){
        //lancer le reboot
        state =false;
        //esp_restart();
        //timeScheduler.disable() ;
        esp_restart();
     
        
    }
}


void onStateChangedbluetoothesp (bool state, HASwitch* s){
    if (state == true){
        btStart();    
        
    } else {
        btStop();
    }
}

void setupHaIntegration(){
    //HA integration
    deviceHA.setUniqueId(deviceUniqID, sizeof(deviceUniqID));
    deviceHA.setName("justsalt");
    deviceHA.setSoftwareVersion(version);
    deviceHA.setModel("Electolyseur");
    deviceHA.setManufacturer("ricky");

    // This method enables availability for all device types registered on the device.
    // For example, if you have 5 sensors on the same device, you can enable
    // shared availability and change availability state of all sensors using
    // single method call "device.setAvailability(false|true)"
    deviceHA.enableSharedAvailability();

    // Optionally, you can enable MQTT LWT feature. If device will lose connection
    // to the broker, all device types related to it will be marked as offline in
    // the Home Assistant Panel.
    deviceHA.enableLastWill();

    rebootesp.setName("Reboot ESP");
    rebootesp.setIcon("mdi:restart");
    rebootesp.onCommand(onStateChangedrebootesp);

    bluetoothesp.setName("Bluetooth ESP");
    bluetoothesp.setIcon("mdi:bluetooth");
    bluetoothesp.onCommand(onStateChangedbluetoothesp);

    wifiStrength.setName("Pool wifi Strength");
    wifiStrength.setDeviceClass("signal_strength");
    wifiStrength.setUnitOfMeasurement("dB");

    justsaltIp.setName("ESP justsalt IP");
    justsaltIp.setIcon("mdi:ip-network");

        // HA integration List of Sensor
    tempeau.setName("Water temp");
    tempeau.setUnitOfMeasurement("°C");
    tempeau.setDeviceClass("temperature");
    tempeau.setIcon("mdi:thermometer");
    
    orp.setName("orp");
    orp.setUnitOfMeasurement("mV");
    orp.setIcon("mdi:flash-triangle-outline");

    ph.setName("PH");
    ph.setIcon("mdi:ph");
    ph.setUnitOfMeasurement("ph");

    sel.setName("Sel");
    sel.setUnitOfMeasurement("g/L");
    sel.setIcon("mdi:water-opacity");

    phconsigne.setName("PH Consigne");
    phconsigne.setIcon("mdi:ph");
    phconsigne.setUnitOfMeasurement("ph");
    phconsigne.setStep(0.1);
    phconsigne.setMin(6.8);
    phconsigne.setMax(7.6);
    phconsigne.onCommand(onValueConsignePhChanged);

    orpconsigne.setName("orp Consigne");
    orpconsigne.setUnitOfMeasurement("mV");
    orpconsigne.setIcon("mdi:flash-triangle-outline");
    orpconsigne.setStep(10);
    orpconsigne.setMin(200);
    orpconsigne.setMax(900);
    orpconsigne.onCommand(onValueConsigneorpChanged);
    
    orpalarme.setName("orp Alarme");
    orpalarme.setUnitOfMeasurement("h");
    orpalarme.setIcon("mdi:alarm");
    orpalarme.setStep(6);
    orpalarme.setMin(12);
    orpalarme.setMax(96);
    orpalarme.onCommand(onValueConsigneorpalarmeChanged);
  
    vol.setName("volume piscine");
    vol.setUnitOfMeasurement("m3");
    vol.setIcon("mdi:cup-water");
    vol.setStep(10);
    vol.setMin(10);
    vol.setMax(200);
    vol.onCommand(onValueConsignevolChanged);

    acide.setName("taux Acide");
    acide.setUnitOfMeasurement("%");
    acide.setIcon("mdi:skull-crossbones-outline");
    acide.setStep(1);
    acide.setMin(5);
    acide.setMax(55);
    acide.onCommand(onValueConsigneacideChanged);
    
    prod.setName("production");
    prod.setUnitOfMeasurement("%");
    prod.setIcon("mdi:cog-outline");
    prod.setStep(1);
    prod.setMin(10);
    prod.setMax(100);
    prod.onCommand(onValueConsigneprodChanged);

    inversion.setName("inversion");
    inversion.setUnitOfMeasurement("h");
    inversion.setIcon("mdi:alarm");
    inversion.setStep(1);
    inversion.setMin(2);
    inversion.setMax(24);
    inversion.onCommand(onValueConsigneinversionChanged);
    
    temp02.setName("temp02");
    temp03.setName("temp03");
    temp08.setName("temp08");
    temp0B.setName("temp0B");
    temp0C.setName("temp0C");
    temp0D.setName("temp0D");
    temp0E.setName("temp0E");
    temp0F.setName("minutes de fonctionement");
    temp0F.setUnitOfMeasurement("Min");
    temp0F.setIcon("mdi:counter");

    temp10.setName("temp10");
    temp12.setName("temp12");
    temp13.setName("temp13");
    temp1F.setName("temp1F");
    temp28.setName("temp28");
    temp29.setName("temp29");
    temp31.setName("temp31");
    temp50.setName("temp50");
    temp51.setName("temp51");
    temp5F.setName("temp5F");
    temp69.setName("temp69");
    temp6A.setName("temp6A");
    temp8F.setName("temp8F");
    temp90.setName("temp90");
    temp91.setName("temp91");
    temp92.setName("temp92");
    temp93.setName("temp93");
    temp93B00.setName("temp93B00");
    temp93B01.setName("temp93B01");
    temp93B02.setName("temp93B02");
    temp93B03.setName("temp93B03");
    temp93B04.setName("temp93B04");
    temp93B05.setName("temp93B05");
    temp93B06.setName("temp93B06");
    temp93B07.setName("temp93B07");
    temp93B10.setName("temp93B10");
    temp93B11.setName("temp93B11");
    temp93B12.setName("temp93B12");
    temp93B13.setName("temp93B13");
    temp93B14.setName("temp93B14");
    temp93B15.setName("temp93B15");
    temp93B16.setName("temp93B16");
    temp93B17.setName("temp93B17");
    temp94.setName("temp94");
    temp95.setName("ID code");
    temp95.setIcon("mdi:barcode");

    temp96.setName("version");
    temp96.setIcon("mdi:qrcode");

    temp97.setName("slave");
    temp97.setIcon("mdi:qrcode-edit");

    temp99.setName("nom");
    temp99.setIcon("mdi:card-account-details-outline");

    temp9A.setName("SN");
    temp9A.setIcon("mdi:barcode");

    temp9B.setName("temp9B");
    temp9C.setName("temp9C");
    temp9D.setName("temp9D");
    tempA3.setName("tempA3");
    tempB0.setName("tempB0");
    tempB1.setName("Mac adr");
    tempB1.setIcon("mdi:network-outline");

    tempD0.setName("tempD0");
    tempD1.setName("tempD1");
    tempE1.setName("tempE1");

    tempE1O8.setName("tempE1O8");
    tempE1O9.setName("tempE1O9");
    tempE1O10.setName("tempE1O10");
    tempE1O11.setName("tempE1O11");
    tempE1O12.setName("tempE1O12");
    tempE1O13.setName("tempE1O13");
        
    // tempE1O9B00.setName("tempE1O9B00");
    // tempE1O9B01.setName("tempE1O9B01");
    // tempE1O9B02.setName("tempE1O9B02");
    // tempE1O9B03.setName("tempE1O9B03");
    // tempE1O9B04.setName("tempE1O9B04");
    // tempE1O9B05.setName("tempE1O9B05");
    // tempE1O9B06.setName("tempE1O9B06");
    // tempE1O9B07.setName("tempE1O9B07");
    // tempE1O10B00.setName("tempE1O10B00");
    // tempE1O10B01.setName("tempE1O10B01");
    // tempE1O10B02.setName("tempE1O10B02");
    // tempE1O10B03.setName("tempE1O10B03");
    // tempE1O10B04.setName("tempE1O10B04");
    // tempE1O10B05.setName("tempE1O10B05");
    // tempE1O10B06.setName("tempE1O10B06");
    // tempE1O10B07.setName("tempE1O10B07");
    // tempE1O11B00.setName("tempE1O11B00");
    // tempE1O11B01.setName("tempE1O11B01");
    // tempE1O11B02.setName("tempE1O11B02");
    // tempE1O11B03.setName("tempE1O11B03");
    // tempE1O11B04.setName("tempE1O11B04");
    // tempE1O11B05.setName("tempE1O11B05");
    // tempE1O11B06.setName("tempE1O11B06");
    // tempE1O11B07.setName("tempE1O11B07");
    // tempE1O12B00.setName("tempE1O12B00");
    // tempE1O12B01.setName("tempE1O12B01");
    // tempE1O12B02.setName("tempE1O12B02");
    // tempE1O12B03.setName("tempE1O12B03");
    // tempE1O12B04.setName("tempE1O12B04");
    // tempE1O12B05.setName("tempE1O12B05");
    // tempE1O12B06.setName("tempE1O12B06");
    // tempE1O12B07.setName("tempE1O12B07");

    tempE2.setName("tempE2");
    tempE4.setName("tempE4");
    tempFE.setName("tempFE");

    bluetoothConnected.setName("Bluetooth Status");

  mqtt.begin( BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD );

}

void setup_telnet(){
  telnetServer.begin();
  telnetServer.setNoDelay(true); 

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.localIP());
  Serial.println(" 23' to connect");
  
  timeScheduler.addTask(taskTelnet);
  taskTelnet.enable();
  Serial.println("Add Task telnet handle");
}

void reconnect_wifi(){
   unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    #ifdef SYSLOG_SERVER
      syslog.log(LOG_INFO, "WIFI lost and reconnect automatically");
    #endif
    previousMillis = currentMillis;
  }
}

void setup_wifi() {
    taskSetup.disable();

    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");

    WiFi.mode(WIFI_STA);
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    //Reconnect wifi Task
    timeScheduler.addTask(taskReconnectWifi);
    taskReconnectWifi.enable();
    Serial.print("Add task to monitor and reconnect wifi");
    
    //Elegant OTA
    webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "Hi! I am ESP32 justsalt to update use http://[yourIP]/update.");
     });
    ElegantOTA.begin(&webServer);    // Start ElegantOTA
    webServer.begin();
    Serial.println("HTTP server started");

    //elegant OTA loop
    timeScheduler.addTask(taskloopElegantOTA);
    taskloopElegantOTA.enable();
    Serial.println("Add task for loop of Elegant OTA");

    //telnet setup
    setup_telnet();
    //launch handle telnet
    timeScheduler.addTask(taskTelnet);
    taskTelnet.enable();
    Serial.print("Add task to Handle telnet");

    //setup mqtt
    setupHaIntegration();
    //loop avaibility for mqtt
    timeScheduler.addTask(taskloopAvaibilityMQTT);
    taskloopAvaibilityMQTT.enable();
    telnet.print("Add task for loop of MQTT");
    
    //cb_setupAndScan_ble();
    doScan = true;
    timeScheduler.addTask(taskConnectBleServer);
    taskConnectBleServer.enable();
    
    Serial.print("Add task to Connec Ble server!!!!!!");
 
}


/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println("Connected");
        /** After connection we should change the parameters if we don't need fast response times.
         *  These settings are 150ms interval, 0 latency, 450ms timout.
         *  Timeout should be a multiple of the interval, minimum is 100ms.
         *  I find a multiple of 3-5 * the interval works best for quick response/reconnect.
         *  Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
         */
        pClient->updateConnParams(120,120,0,60);
    };

    void onDisconnect(NimBLEClient* pClient) {
        Serial.print(pClient->getPeerAddress().toString().c_str());
        Serial.println(" Disconnected - Starting scan");
        NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
    };

    /** Called when the peripheral requests a change to the connection parameters.
     *  Return true to accept and apply them or false to reject and keep
     *  the currently used parameters. Default will return true.
     */
    bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
        if(params->itvl_min < 24) { /** 1.25ms units */
            return false;
        } else if(params->itvl_max > 40) { /** 1.25ms units */
            return false;
        } else if(params->latency > 2) { /** Number of intervals allowed to skip */
            return false;
        } else if(params->supervision_timeout > 100) { /** 10ms units */
            return false;
        }

        return true;
    };

    /********************* Security handled here **********************
    ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest(){
        Serial.println("Client Passkey Request");
        /** return the passkey to send to the server */
        return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key){
        Serial.print("The passkey YES/NO number: ");
        Serial.println(pass_key);
    /** Return false if passkeys don't match. */
        return true;
    };

    /** Pairing process complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc* desc){
        if(!desc->sec_state.encrypted) {
            Serial.println("Encrypt connection failed - disconnecting");
            /** Find the client with the connection handle provided in desc */
            NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
            return;
        }
    };
};



class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
        std::string str = "------------------------------- \r\n" ;
        str += "BLE Advertised Device found: \r\n";
        str += ": Name = " + std::string(advertisedDevice->getName().c_str());
        str += ", adr = " + std::string(advertisedDevice->getAddress().toString().c_str());
        
        Serial.println(str.c_str());
        telnet.println(str.c_str());

        char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
        Serial.println(manufacturerdata);
        telnet.println(manufacturerdata);

        if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
        // if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)) {

            BLEDevice::getScan()->stop();
            advDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
            
            Serial.print("Found our device!  address: ");

            doConnect = true;
            doScan = false;

            // timeScheduler.addTask(taskConnectBleServer);
            taskConnectBleServer.forceNextIteration();
            Serial.println("add Task Connect Ble server");

        } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks



/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
    std::string str = (isNotify == true) ? "Notification" : "Indication";
    str += " from ";
    /** NimBLEAddress and NimBLEUUID have std::string operators */
    str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
    str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
    str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
    str += ", Countertrame = " + std::to_string(countertrame);
    str += ", Value = " + std::string((char*)pData, length);
    countertrame++;
    if (countertrame > 65000){
        countertrame=5;
        countertrameold=0;
    } 
    Serial.println(str.c_str());
    Serial.print("recep chaine taille =");
    Serial.println(length);
    telnet.print("recep chaine taille = ");
    telnet.println(length);
    if (length > 5){
        int index =3;
        while (index +2 < length) {
            uint8_t idvaleur = pData[index];
            int taillevaleur = pData[index + 1 ];
            String Valuetrame = ""; 
            for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                String converttrame = String(pData[i],HEX) ;
                if (converttrame.length() == 1 ){ 
                    converttrame = "0" + converttrame ;
                    }
                Valuetrame += converttrame;
                if (i != index + 1 + taillevaleur){
                    Valuetrame += "." ; 
                }
            }
            Valuetrame.toUpperCase();

            // Serial.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            // telnet.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            std::string str = "Trame " + std::to_string(countertrame);
            str += ": Values ID  = " + std::to_string(idvaleur);
            str += ", taille = " + std::to_string(taillevaleur);
            str += ", Value = " + std::string(Valuetrame.c_str());

            Serial.println(str.c_str());
            telnet.println(str.c_str());

            switch (idvaleur) {
            case 0x00: { 
                //Electrovalue.V_id_00 = 0;
                }break;
        
            case 0x01: {
                uint8_t phhex = pData[index + 2];
                float phf = static_cast<float>(phhex)/10;
                ph.setValue(phf);

                }break;

            case 0x02: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp02.setValue(temp);
                }break;

            case 0x03: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_03 = temp; 
                temp03.setValue(temp);
                }break;

            case 0x06: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index + 3]));
                //float temp = static_cast<float>(temphex);
                orp.setValue(temphex);;
                }break;

            case 0x08: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_08 = temp;
                temp08.setValue(temp);
                }break;

            case 0x09: {
                uint16_t tempeauhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float tempeauf = static_cast<float>(tempeauhex)/10;
                tempeau.setValue(tempeauf);
                }break;
            
            case 0x0A: {
                uint8_t selhex = pData[index + 2] ;
                float self = static_cast<float>(selhex)/10;
                sel.setValue(self);
                }break;

            case 0x0B: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp0B.setValue(temp);
                
                //Electrovalue.V_id_0B = temp;
                }break;
            
            case 0x0C: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp0C.setValue(temp);
                }break;

            case 0x0D: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0D.setValue(temp);
                }break;

            case 0x0E: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0E.setValue(temp);
                }break;

            case 0x0F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0F.setValue(temp);
                }break;
            
            case 0x10: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_10 = temp;
                temp10.setValue(temp);
                }break;

            case 0x11: {
                uint16_t volhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float volf = static_cast<float>(volhex);
                vol.setState(volf);
                }break;
            
            case 0x12: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_12 = temp;
                temp12.setValue(temp);
                
                }break;

            case 0x13: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_13 = temp;
                temp13.setValue(temp);
                
                }break;

            case 0x1F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_1F = temp;
                temp1F.setValue(temp);
                }break;
            
            case 0x28: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_28 = temp;
                temp28.setValue(temp);
                
                }break;
            
            case 0x29: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_29 = temp;
                temp29.setValue(temp);
                }break;
            
            case 0x2A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_2A = temp;
                temp2A.setValue(temp);
                }break;
            
            case 0x30: {
                uint8_t phchex = pData[index + 2] ;
                float phcf = static_cast<float>(phchex)/10;
                phconsigne.setState(phcf);
                }break;
            
            case 0x31: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp31.setValue(temp);
                //Electrovalue.V_id_31 = temp;
                }break;         

            case 0x32: {
                uint8_t acidehex = pData[index + 2] ;
                float acidef = static_cast<float>(acidehex);
                //Electrovalue.V_id_32 = acidef;
                acide.setState(acidef);
                }break;
            
            case 0x33: {
                uint8_t prodhex = pData[index + 2] ;
                float prodf = static_cast<float>(prodhex);
                //Electrovalue.V_id_33 = prodf;
                prod.setState(prodf);
                }break;

            case 0x35: {
                uint8_t orpchex = pData[index + 2] ;
                float orpcf = static_cast<float>(orpchex)*10;
                //Electrovalue.V_id_35 = orpcf;
                orpconsigne.setState(orpcf);
                }break;
            
            case 0x37: {
                uint8_t alarmeorphex = pData[index + 2] ;
                float alarmeorpf = static_cast<float>(alarmeorphex);
                //Electrovalue.V_id_37 = alarmeorpf;
                orpalarme.setState(alarmeorpf);
                }break;
    
            case 0x39: {
                uint8_t inversionhex = pData[index + 2] ;
                float inversionvalf = static_cast<float>(inversionhex);
                //Electrovalue.V_id_39 = inversionvalf;
                inversion.setState(inversionvalf);
                }break;

            case 0x50: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_50 = temp;
                temp50.setValue(temp);
                }break;

            case 0x51: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_51 = temp;
                temp51.setValue(temp);
                }break;

            case 0x5F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_5F = temp;
                temp5F.setValue(temp);
                }break;
            
            case 0x69: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_69 = temp;
                temp69.setValue(temp);
                }break;

            case 0x6A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_6A = temp;
                temp6A.setValue(temp);
                }break;

            case 0x8F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_8F = temp;
                temp8F.setValue(temp);
                }break;
                
            case 0x90: {
                // 90.08.00.00.00.00.00.00.00.14.
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp90.setValue(temp);
                //Electrovalue.V_id_90 = temp;
                }break;

            case 0x91: {
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp91.setValue(temp);
                //temp91.setValue(tempstring.c_str());
                }break;
            
            case 0x92: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp92.setValue(temp);
                }break;

            case 0x93: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp93.setValue(temp);
                temp93B00.setCurrentState((bool)((temphex & 0x0001) ));
                temp93B01.setCurrentState((bool)((temphex & 0x0002) ));
                temp93B02.setCurrentState((bool)((temphex & 0x0004) ));
                temp93B03.setCurrentState((bool)((temphex & 0x0008) ));
                temp93B04.setCurrentState((bool)((temphex & 0x0010) ));
                temp93B05.setCurrentState((bool)((temphex & 0x0020) ));
                temp93B06.setCurrentState((bool)((temphex & 0x0040) ));
                temp93B07.setCurrentState((bool)((temphex & 0x0080) ));
                temp93B10.setCurrentState((bool)((temphex & 0x0100) ));
                temp93B11.setCurrentState((bool)((temphex & 0x0200) ));
                temp93B12.setCurrentState((bool)((temphex & 0x0400) ));
                temp93B13.setCurrentState((bool)((temphex & 0x0800) ));
                temp93B14.setCurrentState((bool)((temphex & 0x1000) ));
                temp93B15.setCurrentState((bool)((temphex & 0x2000) ));
                temp93B16.setCurrentState((bool)((temphex & 0x4000) ));
                temp93B17.setCurrentState((bool)((temphex & 0x8000) ));
                
                
                }break;

            case 0x94: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_94 = temp;
                temp94.setValue(temp);
                }break;

            case 0x95: {
                uint32_t idcodehex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float idcodefloat = static_cast<float>(idcodehex);
                //Electrovalue.V_id_95 = idcodefloat;
                temp95.setValue(idcodefloat);
                
                }break;
            
            case 0x96: {
                temp96.setValue(Valuetrame.c_str());
                }break;
            
            case 0x97: {
                temp97.setValue(Valuetrame.c_str());
                }break;

            case 0x99: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp99.setValue(tempstring.c_str());
                }break;

            case 0x9A: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp9A.setValue(tempstring.c_str());
                }break;

            case 0x9B: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9B = temp;
                temp9B.setValue(temp);
                }break;

            case 0x9C: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9C = temp;
                temp9C.setValue(temp);
                }break;

            case 0x9D: {
                temp9D.setValue(Valuetrame.c_str());
                }break;
            
            case 0xA3: {
                tempA3.setValue(Valuetrame.c_str());
                }break;
            
            case 0xB0: {

                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_B0 = temp;
                tempB0.setValue(temp);
                
                }break;
                
    
            case 0xB1: {
            //     std::string tempstring = rawhex.substr((index + 2 )* 2, 12);
            //     id(JustSalt_macbt_textsensor).publish_state(tempstring);
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += String(pData[i],HEX) ;
                    if (i != index + 1 + taillevaleur){
                        tempstring += ":" ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempB1.setValue(tempstring.c_str());
                }break;
            
            case 0xD0: {
            //     //D0.06.30.00.00.00.00.00
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    String convert = String(pData[i],HEX) ;
                    if (convert.length() == 1 ){ 
                        convert = "0" + convert ;
                        }
                    tempstring += convert;
                    if (i != index + 1 + taillevaleur){
                        tempstring += "." ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempD0.setValue(tempstring.c_str());
                }break;

            case 0xD1: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_D1 = temp;
                tempD1.setValue(temp);
                }break;
            
            

            case 0xE1: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                tempE1.setValue(Valuetrame.c_str());
                Electrovalue.V_id_E1 = Valuetrame.c_str();
                 //9-11
                tempE1O8.setValue(static_cast<int>((pData[index +2 +8])) );
                tempE1O9.setValue(static_cast<int>((pData[index +2 +9])) );
                tempE1O10.setValue(static_cast<int>((pData[index +2 +10])) );
                tempE1O11.setValue(static_cast<int>((pData[index +2 +11])) );
                tempE1O12.setValue(static_cast<int>((pData[index +2 +12]) ));
                tempE1O13.setValue(static_cast<int>((pData[index +2 +13]) ));
                }break;
                
            case 0xE2: {
                // E2.0F.18.04.16.0A.22.10.00.00.00.41.3E.DC.00.00.00
                tempE2.setValue(Valuetrame.c_str());
                }break;

            case 0xE4: {
                // E4.0F.18.04.12.15.00.00.46.00.0B.13.00.00.00.00.00
                tempE4.setValue(Valuetrame.c_str());
                }break;
    
            case 0xFE: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_FE = temp;
                tempFE.setValue(temp);
                
                }break;
    
            default: {
                Serial.print( "idvaleur Non implementee " );
                Serial.println(idvaleur);
                };
            }
            index = index + 2 + taillevaleur ;

        }
        timeScheduler.addTask(taskloopHaIntegration);
        taskloopHaIntegration.enable();
        //Serial.println("fin traitement trame");
       
    }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}


/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(advDevice->toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  // Connect to the remove BLE Server.
  pClient->connect(advDevice); 
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  //il faut s'abonner la la charactisitic "indication" afin de recevoir les notification de publication
  if(pRemoteCharacteristic->canIndicate())
    pRemoteCharacteristic->subscribe(false, notifyCB);

    pRemoteCharWrite = pRemoteService->getCharacteristic(charUUIDwrite);
    if (pRemoteCharWrite == nullptr) {
        Serial.print("Failed to find our characteristic UUID: ");
        Serial.println(charUUIDwrite.toString().c_str());
        pClient->disconnect();
        return false;
    } else {
        Serial.println("caracteristic Write - Found our characteristic");
    }


  connected = true;
  return true;
  
}

void cb_setupAndScan_ble() {
    taskBleSetupAndScan.disable();
    Serial.println("Starting NimBLE Client");
    /** Initialize NimBLE, no device name spcified as we are not advertising */
    NimBLEDevice::init("");
    /** Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /** 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, secure connections.
     *
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power, default is 3db */
    #ifdef ESP_PLATFORM
        NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
    #else
        NimBLEDevice::setPower(9); /** +9db */
    #endif

    /** Optional: set any devices you don't want to get advertisments from */
    // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));

    /** create new scan */
    NimBLEScan* pScan = NimBLEDevice::getScan();
     Serial.println("avant callback");
    /** create a callback that gets called when advertisers are found */
    pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(45);
    pScan->setWindow(15);

    /** Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    Serial.println("avant set active scan");
    pScan->setActiveScan(true);
    /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
     *  Optional callback for when scanning stops.
     */
    //pScan->start(scanTime, scanEndedCB);
    pScan->start(10, false);
    Serial.println("Fin de cb_setupAndScan_ble");
    
}

void onValueConsignePhChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of PH Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(48,numberFloat * 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}
void onValueConsigneorpChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(53,numberFloat / 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}

void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Alarme ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x37,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsignevolChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Volume Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x11,numberFloat ,2);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneacideChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Acide Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x32,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneprodChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Production Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); ;
        onwriteble(0x33,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneinversionChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Version Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str());        
        onwriteble(0x39,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}
void onwriteble (int ValueID ,float numberset,int ValueSizea){
    Serial.println("debut onwriteble : ");
    static uint8_t trame[80] = {0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //union float2bytes { float f; char b[sizeof(float)]; } ;
    typedef union _data {
        int f;
        char  s[2];
    } myData;

    //float2bytes myvalue;
    myData myvalue;
    myvalue.f =  static_cast<int>(numberset);
    trame[3]= ValueID ;
    trame[4]= ValueSizea;
    switch (ValueSizea){
    case 1:
        //uint8_t hexnumberset = (numberset) & 0xff;
        trame[5]= myvalue.s[0];
        break;

    case 2:
        trame[5]= 0x00;
        trame[6]= myvalue.s[0];
        break;
    
    default:
        break;
    }
    //Serial.println(sizeof(trame));
    std::string str = "send des valeur ";
    str += ": Number a set = " + std::to_string(numberset);
    str += ": Myvalue f = " + std::to_string(myvalue.f);
    str += ": Myvalue s 0 = " + std::to_string(myvalue.s[0]);
    str += ": Myvalue s 1 = " + std::to_string(myvalue.s[1]);
    str += " : value ID = " + std::to_string(trame[3]);
    str += ", taille = " + std::to_string(trame[4]);
    str += ", Value ch 5= " + std::to_string(trame[5]);
    str += ", Value ch 6= " + std::to_string(trame[6]);

    Serial.println(str.c_str());
    telnet.println(str.c_str());
    
    bool writeResponse;
    writeResponse = pRemoteCharWrite->writeValue(trame, 80, true);
    Serial.println(writeResponse);
    str = "onwriteble  : ";
    str += "send trame result = " + std::to_string(writeResponse);
    if (writeResponse == 1) {
        str += " OK ";
    }else {
        str += " KO ";
    }
    Serial.println(str.c_str());
    telnet.println(str.c_str());
}



void setup (){
    Serial.begin(115200);
    timeScheduler.init();

    timeScheduler.addTask(taskSetup);
    taskSetup.enable();
    Serial.println("add Task to setup justsalt");
}


void loop (){
    timeScheduler.execute();
}


void cb_connectBleServer(){
  //connection au serveur Ble
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
    Serial.println("debut cb_connectBleServer");
   
    if (connected == true ){
        Serial.println("connected = true");

        // check si on est connecté BLE (evolution des trames notifié ) 
        if (countertrame != countertrameold){
            //Serial.println("recepion notification ok donc conecté ");
            std::string str = "recepion notification ok donc conecté ";
            str += ": countertrame  = " + std::to_string(countertrame);
            str += ", countertrameold = " + std::to_string(countertrameold);
            
            Serial.println(str.c_str());
            telnet.println(str.c_str());

            countertrameold=countertrame;
            delay(50);
        } else {
            Serial.println("recepion notification KO donc liaison BT HS ");
            connected = false;
            doConnect = true;
        } 
        Serial.println("apres check connection");
        
    }else{
        if (doConnect == true) {
            Serial.println("doConnect is true");
            if (connectToServer()) {
                Serial.println("We are now connected to the BLE Server.");
                //write sur la prochaine itération de la task
                taskConnectBleServer.forceNextIteration();
            } else {
                Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                doScan = true;
            }
            doConnect = false;
            Serial.println("doConnect ********************* to false ");
            //on relance un scan
        }
        if (doScan == true){
            Serial.println( "add Task to Scan Ble devices");
            timeScheduler.addTask(taskBleSetupAndScan);
            taskBleSetupAndScan.enable();
        }
        Serial.println("connected = false");
    } 
}

je ne retrouve pas mon voltmètres mais
je viens d ajouter le faite que j envois les valeur 0B , 0C et B0 que toutes les 20 trames
ca devrait diminuer la charge

#define ARDUINOHA_DEBUG
#include <Arduino.h>


/** Electolyseur JustSalt & co
 *
 *  Passerelle Bt Justsalt to Mqtt
 *
 *  Created: Mai 2025
 *      Author: Ricky
 *
*/


#include <NimBLEDevice.h>
#include <ArduinoHA.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
#include <TaskScheduler.h>


const char* version = "0.0.1";
// Update these with values suitable for your wifi network.
const char* ssid = "xxxx-wifi";
const char* password = "xxxxxxxxxxxxx";

//Home Assistant integration
// configure here your HA params for connection to MQTT server
#define BROKER_ADDR IPAddress(192,168,50,11)
#define BROKER_USERNAME     "" // replace with your credentials
#define BROKER_PASSWORD     ""

//Unique device for HA integration
const byte deviceUniqID[] = { 0x94, 0xDE, 0xB8, 0xA1, 0x1A, 0xAC };

//justsalt service and char UUID
// The remote service we wish to connect to.
static  BLEUUID serviceUUID("09912756-7b32-4629-aeb1-b309d9a338ae");
// The characteristic of the remote service we are interested in.
static  BLEUUID    charUUID("ef785c24-22bb-463d-b651-0b7445ba091c");
static  BLEUUID    charUUIDwrite("4d32c5e5-2bb5-45c6-8c89-6f59bb3930d2");

static NimBLEAdvertisedDevice* advDevice;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static int countertrameold = 0;
static int countertrame = 5;
static int countertry = 0;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static NimBLERemoteCharacteristic* pRemoteCharWrite;

static NimBLEClient* pClient;
static NimBLERemoteService* pSvc ;
static NimBLERemoteCharacteristic* pChr ;
static NimBLERemoteDescriptor* pDsc ;

WiFiClient wifiMQTT;
WiFiClient telnet;
WiFiServer telnetServer(23);

Scheduler timeScheduler;
AsyncWebServer webServer(80);


unsigned long previousMillis = 0;
unsigned long interval = 30000;

void scanEndedCB(NimBLEScanResults results);
void setup_wifi();
void setup_glogal();
void reconnect_wifi();
void setupHaIntegration();
void setup_telnet();
void cb_handleTelnet();
void cb_loopHaIntegration();
void cb_loopElegantOTA();
void cb_loopAvaibilityMQTT();
void cb_setupAndScan_ble();
void cb_connectBleServer();
void onValueConsignePhChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender);
void onValueConsignevolChanged( HANumeric number, HANumber* sender);
void onValueConsigneacideChanged( HANumeric number, HANumber* sender);
void onValueConsigneprodChanged( HANumeric number, HANumber* sender);
void onValueConsigneinversionChanged( HANumeric number, HANumber* sender);
void onwriteble (int ValueID ,float numberset,int ValueSizea);

bool connectToServer();


Task taskSetup(5000,TASK_FOREVER,&setup_wifi);
Task taskReconnectWifi(interval,TASK_FOREVER,&reconnect_wifi);
Task taskTelnet(5000,TASK_FOREVER,&cb_handleTelnet);
Task taskloopElegantOTA(5000,TASK_FOREVER,&cb_loopElegantOTA);
Task taskBleSetupAndScan(30000, TASK_FOREVER, &cb_setupAndScan_ble);
Task taskConnectBleServer(50000, TASK_FOREVER, &cb_connectBleServer);
Task taskloopHaIntegration(5000, TASK_FOREVER,&cb_loopHaIntegration);
Task taskloopAvaibilityMQTT(3000, TASK_FOREVER, &cb_loopAvaibilityMQTT);

//static BLEAdvertisedDevice* advDevice;

static uint32_t scanTime = 0; /** 0 = scan forever */

struct trame_value {
    uint8_t     V_id_00;
    float       V_id_01;    // PH
    float       V_id_02;
    float       V_id_03;
    uint16_t    V_id_06;    // ORP
    float       V_id_08;
    float       V_id_09;    // temp Eau
    float       V_id_0A;    // Sel
    float       V_id_0B;
    float       V_id_0C;
    float       V_id_0D;
    float       V_id_0E;
    float       V_id_0F;    // minutes de fonctionement
    float       V_id_10;
    float       V_id_11;    // Volume eau
    float       V_id_12;
    float       V_id_13;
    float       V_id_1F;
    float       V_id_28;
    float       V_id_29;
    float       V_id_2A;
    float       V_id_30;    // Consigne PH
    float       V_id_31;
    float       V_id_32;    // % Acide
    float       V_id_33;    // Production ?
    float       V_id_35;    // Consigne ORP
    float       V_id_37;    // Seuil alarme ORP
    float       V_id_39;    // Inversion
    float       V_id_50;
    float       V_id_51;
    float       V_id_5F;
    float       V_id_69;
    float       V_id_6A;
    float       V_id_8F;
    uint64_t    V_id_90;
    uint64_t    V_id_91;
    float       V_id_92;
    uint16_t     V_id_93;
    bool        V_id_93_b00;
    bool        V_id_93_b01;
    bool        V_id_93_b02;
    bool        V_id_93_b03;
    bool        V_id_93_b04;
    bool        V_id_93_b05;
    bool        V_id_93_b06;
    bool        V_id_93_b07;
    bool        V_id_93_b10;
    bool        V_id_93_b11;
    bool        V_id_93_b12;
    bool        V_id_93_b13;
    bool        V_id_93_b14;
    bool        V_id_93_b15;
    bool        V_id_93_b16;
    bool        V_id_93_b17;
    float       V_id_94;
    uint32_t    V_id_95;    // ID Code
    String      V_id_96;    // Version
    String      V_id_97;    // Slave
    String      V_id_99;    // Nom
    String      V_id_9A;    // SN
    float       V_id_9B;
    float       V_id_9C;
    String      V_id_9D;
    String      V_id_A3;
    float       V_id_B0;
    String      V_id_B1;    // Mac adresse
    String      V_id_D0;
    float       V_id_D1;
    String      V_id_E1;
    String      V_id_E2;
    String      V_id_E4;
    float       V_id_FE;
};

struct trame_value Electrovalue;

HADevice deviceHA;
// dernier paramètre pour le nombre de sensorMQTT à lister
HAMqtt  mqtt(wifiMQTT, deviceHA, 90);

// Command ESP
HASwitch rebootesp("rebootESP");
HASwitch bluetoothesp("bluetoothESP");

//List of sensor for HA
HABinarySensor bluetoothConnected("pool_bluetooth_connected");
HASensorNumber wifiStrength("pool_wifi_strength", HASensorNumber::PrecisionP2);
HASensor justsaltIp("pool_ip");
HASensorNumber tempeau("pool_temp",HASensorNumber::PrecisionP1) ; 
HASensorNumber ph("pool_ph",HASensorNumber::PrecisionP1);
HASensorNumber orp("pool_orp",HASensorNumber::PrecisionP0);
HASensorNumber sel("pool_sel",HASensorNumber::PrecisionP1);
HANumber vol("pool_vol",HASensorNumber::PrecisionP0);
HANumber phconsigne("pool_ph_consigne",HASensorNumber::PrecisionP1);
HANumber acide("pool_acide",HASensorNumber::PrecisionP1);
HANumber prod("pool_prod",HASensorNumber::PrecisionP0);
HANumber orpconsigne("pool_orp_consigne",HASensorNumber::PrecisionP0);
HANumber orpalarme("pool_orp_alarme",HASensorNumber::PrecisionP0);
HANumber inversion("pool_inversion",HASensorNumber::PrecisionP0);
HASensorNumber temp02("pool_temp02",HASensorNumber::PrecisionP0);
HASensorNumber temp03("pool_temp03",HASensorNumber::PrecisionP0);
HASensorNumber temp08("pool_temp08",HASensorNumber::PrecisionP0);
HASensorNumber temp0B("pool_temp0B",HASensorNumber::PrecisionP0);
HASensorNumber temp0C("pool_temp0C",HASensorNumber::PrecisionP0);
HASensorNumber temp0D("pool_temp0D",HASensorNumber::PrecisionP0);
HASensorNumber temp0E("pool_temp0E",HASensorNumber::PrecisionP0);
HASensorNumber temp0F("pool_temp0F",HASensorNumber::PrecisionP0);
HASensorNumber temp10("pool_temp10",HASensorNumber::PrecisionP0);
HASensorNumber temp12("pool_temp12",HASensorNumber::PrecisionP0);
HASensorNumber temp13("pool_temp13",HASensorNumber::PrecisionP0);
HASensorNumber temp1F("pool_temp1F",HASensorNumber::PrecisionP0);
HASensorNumber temp28("pool_temp28",HASensorNumber::PrecisionP0);
HASensorNumber temp29("pool_temp29",HASensorNumber::PrecisionP0);
HASensorNumber temp2A("pool_temp2A",HASensorNumber::PrecisionP0);
HASensorNumber temp31("pool_temp31",HASensorNumber::PrecisionP0);
HASensorNumber temp50("pool_temp50",HASensorNumber::PrecisionP0);
HASensorNumber temp51("pool_temp51",HASensorNumber::PrecisionP0);
HASensorNumber temp5F("pool_temp5F",HASensorNumber::PrecisionP0);
HASensorNumber temp69("pool_temp69",HASensorNumber::PrecisionP0);
HASensorNumber temp6A("pool_temp6A",HASensorNumber::PrecisionP0);
HASensorNumber temp8F("pool_temp8F",HASensorNumber::PrecisionP0);
HASensorNumber temp90("pool_temp90",HASensorNumber::PrecisionP0);
HASensorNumber temp91("pool_temp91",HASensorNumber::PrecisionP0);
HASensorNumber temp92("pool_temp92",HASensorNumber::PrecisionP0);
HASensorNumber temp93("pool_temp93",HASensorNumber::PrecisionP0);
HABinarySensor temp93B00("false93B00");
HABinarySensor temp93B01("false93B01");
HABinarySensor temp93B02("false93B02");
HABinarySensor temp93B03("false93B03");
HABinarySensor temp93B04("false93B04");
HABinarySensor temp93B05("false93B05");
HABinarySensor temp93B06("false93B06");
HABinarySensor temp93B07("false93B07");
HABinarySensor temp93B10("false93B10");
HABinarySensor temp93B11("false93B11");
HABinarySensor temp93B12("false93B12");
HABinarySensor temp93B13("false93B13");
HABinarySensor temp93B14("false93B14");
HABinarySensor temp93B15("false93B15");
HABinarySensor temp93B16("false93B16");
HABinarySensor temp93B17("false93B17");
HASensorNumber temp94("pool_temp94",HASensorNumber::PrecisionP0);
HASensorNumber temp95("pool_temp95",HASensorNumber::PrecisionP0);
HASensor temp96("pool_temp96");
HASensor temp97("pool_temp97");
HASensor temp99("pool_temp99");
HASensor temp9A("pool_temp9A");
HASensorNumber temp9B("pool_temp9B",HASensorNumber::PrecisionP0);
HASensorNumber temp9C("pool_temp9C",HASensorNumber::PrecisionP0);
HASensor temp9D("pool_temp9D");
HASensor tempA3("pool_tempA3");
HASensorNumber tempB0("pool_tempB0",HASensorNumber::PrecisionP0);
HASensor tempB1("pool_tempB1");
HASensor tempD0("pool_tempD0");
HASensorNumber tempD1("pool_tempD1",HASensorNumber::PrecisionP0);
HASensor tempE1("pool_tempE1");
HASensorNumber tempE1O8("pool_tempE1O8",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O9("pool_tempE1O9",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O10("pool_tempE1O10",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O11("pool_tempE1O11",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O12("pool_tempE1O12",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O13("pool_tempE1O13",HASensorNumber::PrecisionP0);


// HABinarySensor tempE1O9B00("falseE1O9B00");
// HABinarySensor tempE1O9B01("falseE1O9B01");
// HABinarySensor tempE1O9B02("falseE1O9B02");
// HABinarySensor tempE1O9B03("falseE1O9B03");
// HABinarySensor tempE1O9B04("falseE1O9B04");
// HABinarySensor tempE1O9B05("falseE1O9B05");
// HABinarySensor tempE1O9B06("falseE1O9B06");
// HABinarySensor tempE1O9B07("falseE1O9B07");
// HABinarySensor tempE1O10B00("falseE1O10B00");
// HABinarySensor tempE1O10B01("falseE1O10B01");
// HABinarySensor tempE1O10B02("falseE1O10B02");
// HABinarySensor tempE1O10B03("falseE1O10B03");
// HABinarySensor tempE1O10B04("falseE1O10B04");
// HABinarySensor tempE1O10B05("falseE1O10B05");
// HABinarySensor tempE1O10B06("falseE1O10B06");
// HABinarySensor tempE1O10B07("falseE1O10B07");
// HABinarySensor tempE1O11B00("falseE1O11B00");
// HABinarySensor tempE1O11B01("falseE1O11B01");
// HABinarySensor tempE1O11B02("falseE1O11B02");
// HABinarySensor tempE1O11B03("falseE1O11B03");
// HABinarySensor tempE1O11B04("falseE1O11B04");
// HABinarySensor tempE1O11B05("falseE1O11B05");
// HABinarySensor tempE1O11B06("falseE1O11B06");
// HABinarySensor tempE1O11B07("falseE1O11B07");
// HABinarySensor tempE1O12B00("falseE1O12B00");
// HABinarySensor tempE1O12B01("falseE1O12B01");
// HABinarySensor tempE1O12B02("falseE1O12B02");
// HABinarySensor tempE1O12B03("falseE1O12B03");
// HABinarySensor tempE1O12B04("falseE1O12B04");
// HABinarySensor tempE1O12B05("falseE1O12B05");
// HABinarySensor tempE1O12B06("falseE1O12B06");
// HABinarySensor tempE1O12B07("falseE1O12B07");

HASensor tempE2("pool_tempE2");
HASensor tempE4("pool_tempE4");
HASensorNumber tempFE("pool_tempFE",HASensorNumber::PrecisionP0);

void cb_handleTelnet() {
  if (telnetServer.hasClient()) {
    if (!telnet || !telnet.connected()) {
      if (telnet) telnet.stop();
      telnet = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}

void cb_loopElegantOTA(){
  ElegantOTA.loop();
}


void cb_loopAvaibilityMQTT(){
  mqtt.loop();
  //remove setAvaibility to use native check of Ha integration Shared availability
  //deviceHA.setAvailability(true);

  //savoir si la connexion bluetooth est OK ou si le justsalt n'est pas sous tension.
  bluetoothConnected.setState(connected);

}

void cb_loopHaIntegration(){
    //Serial.println("cb_loopHaIntegration");
    taskloopHaIntegration.disable();
    mqtt.loop();
    //remove setAvaibility to use native check of Ha integration Shared availability
    deviceHA.setAvailability(true);

    wifiStrength.setValue(WiFi.RSSI());
    justsaltIp.setValue(WiFi.localIP().toString().c_str());
    //Serial.print("E1 =");
    //Serial.println(Electrovalue.V_id_E1);
    //Serial.print("E1[8] =");
    //Serial.println(Electrovalue.V_id_E1[8]);
    //Serial.println((static_cast<int>(Electrovalue.V_id_E1[8]) ));

    
    // tempE1O9B00.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x01) ));
    // tempE1O9B01.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x02) ));
    // tempE1O9B02.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x04) ));
    // tempE1O9B03.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x08) ));
    // tempE1O9B04.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x10) ));
    // tempE1O9B05.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x20) ));
    // tempE1O9B06.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x40) ));
    // tempE1O9B07.setCurrentState((bool)(((Electrovalue.V_id_E1[9]) & 0x80) ));

    // tempE1O10B00.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x01) ));
    // tempE1O10B01.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x02) ));
    // tempE1O10B02.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x04) ));
    // tempE1O10B03.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x08) ));
    // tempE1O10B04.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x10) ));
    // tempE1O10B05.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x20) ));
    // tempE1O10B06.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x40) ));
    // tempE1O10B07.setCurrentState((bool)(((Electrovalue.V_id_E1[10]) & 0x80) ));

    // tempE1O11B00.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x01) ));
    // tempE1O11B01.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x02) ));
    // tempE1O11B02.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x04) ));
    // tempE1O11B03.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x08) ));
    // tempE1O11B04.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x10) ));
    // tempE1O11B05.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x20) ));
    // tempE1O11B06.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x40) ));
    // tempE1O11B07.setCurrentState((bool)(((Electrovalue.V_id_E1[11]) & 0x80) ));

    // tempE1O12B00.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x01) ));
    // tempE1O12B01.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x02) ));
    // tempE1O12B02.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x04) ));
    // tempE1O12B03.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x08) ));
    // tempE1O12B04.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x10) ));
    // tempE1O12B05.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x20) ));
    // tempE1O12B06.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x40) ));
    // tempE1O12B07.setCurrentState((bool)(((Electrovalue.V_id_E1[12]) & 0x80) ));
    //Serial.println("FIN cb_loopHaIntegration");
}

void onStateChangedrebootesp (bool state, HASwitch* s){
    if (state == true){
        //lancer le reboot
        state =false;
        //esp_restart();
        //timeScheduler.disable() ;
        esp_restart();
     
        
    }
}


void onStateChangedbluetoothesp (bool state, HASwitch* s){
    if (state == true){
        Serial.println("BT start");
        btStart();    
    } else {
        Serial.println("BT stop");
        btStop();
    }
}

void setupHaIntegration(){
    //HA integration
    deviceHA.setUniqueId(deviceUniqID, sizeof(deviceUniqID));
    deviceHA.setName("justsalt");
    deviceHA.setSoftwareVersion(version);
    deviceHA.setModel("Electolyseur");
    deviceHA.setManufacturer("ricky");

    // This method enables availability for all device types registered on the device.
    // For example, if you have 5 sensors on the same device, you can enable
    // shared availability and change availability state of all sensors using
    // single method call "device.setAvailability(false|true)"
    deviceHA.enableSharedAvailability();

    // Optionally, you can enable MQTT LWT feature. If device will lose connection
    // to the broker, all device types related to it will be marked as offline in
    // the Home Assistant Panel.
    deviceHA.enableLastWill();

    rebootesp.setName("Reboot ESP");
    rebootesp.setIcon("mdi:restart");
    rebootesp.onCommand(onStateChangedrebootesp);

    bluetoothesp.setName("Bluetooth ESP");
    bluetoothesp.setIcon("mdi:bluetooth");
    bluetoothesp.onCommand(onStateChangedbluetoothesp);

    wifiStrength.setName("Pool wifi Strength");
    wifiStrength.setDeviceClass("signal_strength");
    wifiStrength.setUnitOfMeasurement("dB");

    justsaltIp.setName("ESP justsalt IP");
    justsaltIp.setIcon("mdi:ip-network");

        // HA integration List of Sensor
    tempeau.setName("Water temp");
    tempeau.setUnitOfMeasurement("°C");
    tempeau.setDeviceClass("temperature");
    tempeau.setIcon("mdi:thermometer");
    
    orp.setName("orp");
    orp.setUnitOfMeasurement("mV");
    orp.setIcon("mdi:flash-triangle-outline");

    ph.setName("PH");
    ph.setIcon("mdi:ph");
    ph.setUnitOfMeasurement("ph");

    sel.setName("Sel");
    sel.setUnitOfMeasurement("g/L");
    sel.setIcon("mdi:water-opacity");

    phconsigne.setName("PH Consigne");
    phconsigne.setIcon("mdi:ph");
    phconsigne.setUnitOfMeasurement("ph");
    phconsigne.setStep(0.1);
    phconsigne.setMin(6.8);
    phconsigne.setMax(7.6);
    phconsigne.onCommand(onValueConsignePhChanged);

    orpconsigne.setName("orp Consigne");
    orpconsigne.setUnitOfMeasurement("mV");
    orpconsigne.setIcon("mdi:flash-triangle-outline");
    orpconsigne.setStep(10);
    orpconsigne.setMin(200);
    orpconsigne.setMax(900);
    orpconsigne.onCommand(onValueConsigneorpChanged);
    
    orpalarme.setName("orp Alarme");
    orpalarme.setUnitOfMeasurement("h");
    orpalarme.setIcon("mdi:alarm");
    orpalarme.setStep(6);
    orpalarme.setMin(12);
    orpalarme.setMax(96);
    orpalarme.onCommand(onValueConsigneorpalarmeChanged);
  
    vol.setName("volume piscine");
    vol.setUnitOfMeasurement("m3");
    vol.setIcon("mdi:cup-water");
    vol.setStep(10);
    vol.setMin(10);
    vol.setMax(200);
    vol.onCommand(onValueConsignevolChanged);

    acide.setName("taux Acide");
    acide.setUnitOfMeasurement("%");
    acide.setIcon("mdi:skull-crossbones-outline");
    acide.setStep(1);
    acide.setMin(5);
    acide.setMax(55);
    acide.onCommand(onValueConsigneacideChanged);
    
    prod.setName("production");
    prod.setUnitOfMeasurement("%");
    prod.setIcon("mdi:cog-outline");
    prod.setStep(1);
    prod.setMin(10);
    prod.setMax(100);
    prod.onCommand(onValueConsigneprodChanged);

    inversion.setName("inversion");
    inversion.setUnitOfMeasurement("h");
    inversion.setIcon("mdi:alarm");
    inversion.setStep(1);
    inversion.setMin(2);
    inversion.setMax(24);
    inversion.onCommand(onValueConsigneinversionChanged);
    
    temp02.setName("temp02");
    temp03.setName("temp03");
    temp08.setName("temp08");
    temp0B.setName("temp0B");
    temp0C.setName("temp0C");
    temp0D.setName("temp0D");
    temp0E.setName("temp0E");
    temp0F.setName("minutes de fonctionement");
    temp0F.setUnitOfMeasurement("Min");
    temp0F.setIcon("mdi:counter");

    temp10.setName("temp10");
    temp12.setName("temp12");
    temp13.setName("temp13");
    temp1F.setName("temp1F");
    temp28.setName("temp28");
    temp29.setName("temp29");
    temp31.setName("temp31");
    temp50.setName("temp50");
    temp51.setName("temp51");
    temp5F.setName("temp5F");
    temp69.setName("temp69");
    temp6A.setName("temp6A");
    temp8F.setName("temp8F");
    temp90.setName("temp90");
    temp91.setName("temp91");
    temp92.setName("temp92");
    temp93.setName("temp93");
    temp93B00.setName("temp93B00");
    temp93B01.setName("temp93B01");
    temp93B02.setName("temp93B02");
    temp93B03.setName("temp93B03");
    temp93B04.setName("temp93B04");
    temp93B05.setName("temp93B05");
    temp93B06.setName("temp93B06");
    temp93B07.setName("temp93B07");
    temp93B10.setName("temp93B10");
    temp93B11.setName("temp93B11");
    temp93B12.setName("temp93B12");
    temp93B13.setName("temp93B13");
    temp93B14.setName("temp93B14");
    temp93B15.setName("temp93B15");
    temp93B16.setName("temp93B16");
    temp93B17.setName("temp93B17");
    temp94.setName("temp94");
    temp95.setName("ID code");
    temp95.setIcon("mdi:barcode");

    temp96.setName("version");
    temp96.setIcon("mdi:qrcode");

    temp97.setName("slave");
    temp97.setIcon("mdi:qrcode-edit");

    temp99.setName("nom");
    temp99.setIcon("mdi:card-account-details-outline");

    temp9A.setName("SN");
    temp9A.setIcon("mdi:barcode");

    temp9B.setName("temp9B");
    temp9C.setName("temp9C");
    temp9D.setName("temp9D");
    tempA3.setName("tempA3");
    tempB0.setName("tempB0");
    tempB1.setName("Mac adr");
    tempB1.setIcon("mdi:network-outline");

    tempD0.setName("tempD0");
    tempD1.setName("tempD1");
    tempE1.setName("tempE1");

    tempE1O8.setName("tempE1O8");
    tempE1O9.setName("tempE1O9");
    tempE1O10.setName("tempE1O10");
    tempE1O11.setName("tempE1O11");
    tempE1O12.setName("tempE1O12");
    tempE1O13.setName("tempE1O13");
        
    // tempE1O9B00.setName("tempE1O9B00");
    // tempE1O9B01.setName("tempE1O9B01");
    // tempE1O9B02.setName("tempE1O9B02");
    // tempE1O9B03.setName("tempE1O9B03");
    // tempE1O9B04.setName("tempE1O9B04");
    // tempE1O9B05.setName("tempE1O9B05");
    // tempE1O9B06.setName("tempE1O9B06");
    // tempE1O9B07.setName("tempE1O9B07");
    // tempE1O10B00.setName("tempE1O10B00");
    // tempE1O10B01.setName("tempE1O10B01");
    // tempE1O10B02.setName("tempE1O10B02");
    // tempE1O10B03.setName("tempE1O10B03");
    // tempE1O10B04.setName("tempE1O10B04");
    // tempE1O10B05.setName("tempE1O10B05");
    // tempE1O10B06.setName("tempE1O10B06");
    // tempE1O10B07.setName("tempE1O10B07");
    // tempE1O11B00.setName("tempE1O11B00");
    // tempE1O11B01.setName("tempE1O11B01");
    // tempE1O11B02.setName("tempE1O11B02");
    // tempE1O11B03.setName("tempE1O11B03");
    // tempE1O11B04.setName("tempE1O11B04");
    // tempE1O11B05.setName("tempE1O11B05");
    // tempE1O11B06.setName("tempE1O11B06");
    // tempE1O11B07.setName("tempE1O11B07");
    // tempE1O12B00.setName("tempE1O12B00");
    // tempE1O12B01.setName("tempE1O12B01");
    // tempE1O12B02.setName("tempE1O12B02");
    // tempE1O12B03.setName("tempE1O12B03");
    // tempE1O12B04.setName("tempE1O12B04");
    // tempE1O12B05.setName("tempE1O12B05");
    // tempE1O12B06.setName("tempE1O12B06");
    // tempE1O12B07.setName("tempE1O12B07");

    tempE2.setName("tempE2");
    tempE4.setName("tempE4");
    tempFE.setName("tempFE");

    bluetoothConnected.setName("Bluetooth Status");

  mqtt.begin( BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD );

}

void setup_telnet(){
  telnetServer.begin();
  telnetServer.setNoDelay(true); 

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.localIP());
  Serial.println(" 23' to connect");
  
  timeScheduler.addTask(taskTelnet);
  taskTelnet.enable();
  Serial.println("Add Task telnet handle");
}

void reconnect_wifi(){
   unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    #ifdef SYSLOG_SERVER
      syslog.log(LOG_INFO, "WIFI lost and reconnect automatically");
    #endif
    previousMillis = currentMillis;
  }
}

void setup_wifi() {
    taskSetup.disable();

    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");

    WiFi.mode(WIFI_STA);
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    //Reconnect wifi Task
    timeScheduler.addTask(taskReconnectWifi);
    taskReconnectWifi.enable();
    Serial.print("Add task to monitor and reconnect wifi");
    
    //Elegant OTA
    webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "Hi! I am ESP32 justsalt to update use http://[yourIP]/update.");
     });
    ElegantOTA.begin(&webServer);    // Start ElegantOTA
    webServer.begin();
    Serial.println("HTTP server started");

    //elegant OTA loop
    timeScheduler.addTask(taskloopElegantOTA);
    taskloopElegantOTA.enable();
    Serial.println("Add task for loop of Elegant OTA");

    //telnet setup
    setup_telnet();
    //launch handle telnet
    timeScheduler.addTask(taskTelnet);
    taskTelnet.enable();
    Serial.print("Add task to Handle telnet");

    //setup mqtt
    setupHaIntegration();
    //loop avaibility for mqtt
    timeScheduler.addTask(taskloopAvaibilityMQTT);
    taskloopAvaibilityMQTT.enable();
    telnet.print("Add task for loop of MQTT");
    
    //cb_setupAndScan_ble();
    doScan = true;
    timeScheduler.addTask(taskConnectBleServer);
    taskConnectBleServer.enable();
    
    Serial.print("Add task to Connec Ble server!!!!!!");
 
}


/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println("Connected");
        /** After connection we should change the parameters if we don't need fast response times.
         *  These settings are 150ms interval, 0 latency, 450ms timout.
         *  Timeout should be a multiple of the interval, minimum is 100ms.
         *  I find a multiple of 3-5 * the interval works best for quick response/reconnect.
         *  Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
         */
        pClient->updateConnParams(120,120,0,60);
    };

    void onDisconnect(NimBLEClient* pClient) {
        Serial.print(pClient->getPeerAddress().toString().c_str());
        Serial.println(" Disconnected - Starting scan");
        NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
    };

    /** Called when the peripheral requests a change to the connection parameters.
     *  Return true to accept and apply them or false to reject and keep
     *  the currently used parameters. Default will return true.
     */
    bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
        if(params->itvl_min < 24) { /** 1.25ms units */
            return false;
        } else if(params->itvl_max > 40) { /** 1.25ms units */
            return false;
        } else if(params->latency > 2) { /** Number of intervals allowed to skip */
            return false;
        } else if(params->supervision_timeout > 100) { /** 10ms units */
            return false;
        }

        return true;
    };

    /********************* Security handled here **********************
    ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest(){
        Serial.println("Client Passkey Request");
        /** return the passkey to send to the server */
        return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key){
        Serial.print("The passkey YES/NO number: ");
        Serial.println(pass_key);
    /** Return false if passkeys don't match. */
        return true;
    };

    /** Pairing process complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc* desc){
        if(!desc->sec_state.encrypted) {
            Serial.println("Encrypt connection failed - disconnecting");
            /** Find the client with the connection handle provided in desc */
            NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
            return;
        }
    };
};



class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
        std::string str = "------------------------------- \r\n" ;
        str += "BLE Advertised Device found: \r\n";
        str += ": Name = " + std::string(advertisedDevice->getName().c_str());
        str += ", adr = " + std::string(advertisedDevice->getAddress().toString().c_str());
        
        Serial.println(str.c_str());
        telnet.println(str.c_str());

        char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
        Serial.println(manufacturerdata);
        telnet.println(manufacturerdata);

        if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
        // if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)) {

            BLEDevice::getScan()->stop();
            advDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
            
            Serial.print("Found our device!  address: ");

            doConnect = true;
            doScan = false;

            // timeScheduler.addTask(taskConnectBleServer);
            taskConnectBleServer.forceNextIteration();
            Serial.println("add Task Connect Ble server");

        } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks



/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
    std::string str = (isNotify == true) ? "Notification" : "Indication";
    str += " from ";
    /** NimBLEAddress and NimBLEUUID have std::string operators */
    str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
    str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
    str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
    str += ", Countertrame = " + std::to_string(countertrame);
    str += ", Value = " + std::string((char*)pData, length);
    countertrame++;
    if (countertrame > 65000){
        countertrame=5;
        countertrameold=0;
    } 
    Serial.println(str.c_str());
    Serial.print("recep chaine taille =");
    Serial.println(length);
    telnet.print("recep chaine taille = ");
    telnet.println(length);
    if (length > 5){
        int index =3;
        while (index +2 < length) {
            uint8_t idvaleur = pData[index];
            int taillevaleur = pData[index + 1 ];
            String Valuetrame = ""; 
            for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                String converttrame = String(pData[i],HEX) ;
                if (converttrame.length() == 1 ){ 
                    converttrame = "0" + converttrame ;
                    }
                Valuetrame += converttrame;
                if (i != index + 1 + taillevaleur){
                    Valuetrame += "." ; 
                }
            }
            Valuetrame.toUpperCase();

            // Serial.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            // telnet.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            std::string str = "Trame " + std::to_string(countertrame);
            str += ": Values ID  = " + std::to_string(idvaleur);
            str += ", taille = " + std::to_string(taillevaleur);
            str += ", Value = " + std::string(Valuetrame.c_str());

            Serial.println(str.c_str());
            telnet.println(str.c_str());

            switch (idvaleur) {
            case 0x00: { 
                //Electrovalue.V_id_00 = 0;
                }break;
        
            case 0x01: {
                uint8_t phhex = pData[index + 2];
                float phf = static_cast<float>(phhex)/10;
                ph.setValue(phf);

                }break;

            case 0x02: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp02.setValue(temp);
                }break;

            case 0x03: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_03 = temp; 
                temp03.setValue(temp);
                }break;

            case 0x06: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index + 3]));
                //float temp = static_cast<float>(temphex);
                orp.setValue(temphex);;
                }break;

            case 0x08: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_08 = temp;
                temp08.setValue(temp);
                }break;

            case 0x09: {
                uint16_t tempeauhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float tempeauf = static_cast<float>(tempeauhex)/10;
                tempeau.setValue(tempeauf);
                }break;
            
            case 0x0A: {
                uint8_t selhex = pData[index + 2] ;
                float self = static_cast<float>(selhex)/10;
                sel.setValue(self);
                }break;

            case 0x0B: {
                if(countertry == 20) {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp0B.setValue(temp);
                }
                //Electrovalue.V_id_0B = temp;
                }break;
            
            case 0x0C: {
                if(countertry == 20) {
                    uint8_t temphex = pData[index + 2];
                    float temp = static_cast<float>(temphex);
                    temp0C.setValue(temp);
                }
                }break;

            case 0x0D: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0D.setValue(temp);
                }break;

            case 0x0E: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0E.setValue(temp);
                }break;

            case 0x0F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0F.setValue(temp);
                }break;
            
            case 0x10: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_10 = temp;
                temp10.setValue(temp);
                }break;

            case 0x11: {
                uint16_t volhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float volf = static_cast<float>(volhex);
                vol.setState(volf);
                }break;
            
            case 0x12: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_12 = temp;
                temp12.setValue(temp);
                
                }break;

            case 0x13: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_13 = temp;
                temp13.setValue(temp);
                
                }break;

            case 0x1F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_1F = temp;
                temp1F.setValue(temp);
                }break;
            
            case 0x28: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_28 = temp;
                temp28.setValue(temp);
                
                }break;
            
            case 0x29: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_29 = temp;
                temp29.setValue(temp);
                }break;
            
            case 0x2A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_2A = temp;
                temp2A.setValue(temp);
                }break;
            
            case 0x30: {
                uint8_t phchex = pData[index + 2] ;
                float phcf = static_cast<float>(phchex)/10;
                phconsigne.setState(phcf);
                }break;
            
            case 0x31: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp31.setValue(temp);
                //Electrovalue.V_id_31 = temp;
                }break;         

            case 0x32: {
                uint8_t acidehex = pData[index + 2] ;
                float acidef = static_cast<float>(acidehex);
                //Electrovalue.V_id_32 = acidef;
                acide.setState(acidef);
                }break;
            
            case 0x33: {
                uint8_t prodhex = pData[index + 2] ;
                float prodf = static_cast<float>(prodhex);
                //Electrovalue.V_id_33 = prodf;
                prod.setState(prodf);
                }break;

            case 0x35: {
                uint8_t orpchex = pData[index + 2] ;
                float orpcf = static_cast<float>(orpchex)*10;
                //Electrovalue.V_id_35 = orpcf;
                orpconsigne.setState(orpcf);
                }break;
            
            case 0x37: {
                uint8_t alarmeorphex = pData[index + 2] ;
                float alarmeorpf = static_cast<float>(alarmeorphex);
                //Electrovalue.V_id_37 = alarmeorpf;
                orpalarme.setState(alarmeorpf);
                }break;
    
            case 0x39: {
                uint8_t inversionhex = pData[index + 2] ;
                float inversionvalf = static_cast<float>(inversionhex);
                //Electrovalue.V_id_39 = inversionvalf;
                inversion.setState(inversionvalf);
                }break;

            case 0x50: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_50 = temp;
                temp50.setValue(temp);
                }break;

            case 0x51: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_51 = temp;
                temp51.setValue(temp);
                }break;

            case 0x5F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_5F = temp;
                temp5F.setValue(temp);
                }break;
            
            case 0x69: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_69 = temp;
                temp69.setValue(temp);
                }break;

            case 0x6A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_6A = temp;
                temp6A.setValue(temp);
                }break;

            case 0x8F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_8F = temp;
                temp8F.setValue(temp);
                }break;
                
            case 0x90: {
                // 90.08.00.00.00.00.00.00.00.14.
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp90.setValue(temp);
                //Electrovalue.V_id_90 = temp;
                }break;

            case 0x91: {
                uint64_t temphex =  (pData[index + 6]<< 24) + (pData[index + 7]<< 16)+ (pData[index + 8]<< 8) + pData[index + 9];
                float temp = static_cast<float>(temphex);
                telnet.printf( "Temp : %ld - tempex : %ld \r\n", temp, temphex );
                temp91.setValue(temp);
                //temp91.setValue(tempstring.c_str());
                }break;
            
            case 0x92: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp92.setValue(temp);
                }break;

            case 0x93: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp93.setValue(temp);
                temp93B00.setCurrentState((bool)((temphex & 0x0001) ));
                temp93B01.setCurrentState((bool)((temphex & 0x0002) ));
                temp93B02.setCurrentState((bool)((temphex & 0x0004) ));
                temp93B03.setCurrentState((bool)((temphex & 0x0008) ));
                temp93B04.setCurrentState((bool)((temphex & 0x0010) ));
                temp93B05.setCurrentState((bool)((temphex & 0x0020) ));
                temp93B06.setCurrentState((bool)((temphex & 0x0040) ));
                temp93B07.setCurrentState((bool)((temphex & 0x0080) ));
                temp93B10.setCurrentState((bool)((temphex & 0x0100) ));
                temp93B11.setCurrentState((bool)((temphex & 0x0200) ));
                temp93B12.setCurrentState((bool)((temphex & 0x0400) ));
                temp93B13.setCurrentState((bool)((temphex & 0x0800) ));
                temp93B14.setCurrentState((bool)((temphex & 0x1000) ));
                temp93B15.setCurrentState((bool)((temphex & 0x2000) ));
                temp93B16.setCurrentState((bool)((temphex & 0x4000) ));
                temp93B17.setCurrentState((bool)((temphex & 0x8000) ));
                
                
                }break;

            case 0x94: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_94 = temp;
                temp94.setValue(temp);
                }break;

            case 0x95: {
                uint32_t idcodehex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float idcodefloat = static_cast<float>(idcodehex);
                //Electrovalue.V_id_95 = idcodefloat;
                temp95.setValue(idcodefloat);
                
                }break;
            
            case 0x96: {
                temp96.setValue(Valuetrame.c_str());
                }break;
            
            case 0x97: {
                temp97.setValue(Valuetrame.c_str());
                }break;

            case 0x99: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp99.setValue(tempstring.c_str());
                }break;

            case 0x9A: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp9A.setValue(tempstring.c_str());
                }break;

            case 0x9B: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9B = temp;
                temp9B.setValue(temp);
                }break;

            case 0x9C: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9C = temp;
                temp9C.setValue(temp);
                }break;

            case 0x9D: {
                temp9D.setValue(Valuetrame.c_str());
                }break;
            
            case 0xA3: {
                tempA3.setValue(Valuetrame.c_str());
                }break;
            
            case 0xB0: {
                if(countertry == 20) {

                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_8F = temp;
                tempB0.setValue(temp);
                }
                
                }break;
                
    
            case 0xB1: {
            //     std::string tempstring = rawhex.substr((index + 2 )* 2, 12);
            //     id(JustSalt_macbt_textsensor).publish_state(tempstring);
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += String(pData[i],HEX) ;
                    if (i != index + 1 + taillevaleur){
                        tempstring += ":" ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempB1.setValue(tempstring.c_str());
                }break;
            
            case 0xD0: {
            //     //D0.06.30.00.00.00.00.00
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    String convert = String(pData[i],HEX) ;
                    if (convert.length() == 1 ){ 
                        convert = "0" + convert ;
                        }
                    tempstring += convert;
                    if (i != index + 1 + taillevaleur){
                        tempstring += "." ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempD0.setValue(tempstring.c_str());
                }break;

            case 0xD1: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_D1 = temp;
                tempD1.setValue(temp);
                }break;
            
            

            case 0xE1: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                tempE1.setValue(Valuetrame.c_str());
                Electrovalue.V_id_E1 = Valuetrame.c_str();
                 //9-11
                tempE1O8.setValue(static_cast<int>((pData[index +2 +8])) );
                tempE1O9.setValue(static_cast<int>((pData[index +2 +9])) );
                tempE1O10.setValue(static_cast<int>((pData[index +2 +10])) );
                tempE1O11.setValue(static_cast<int>((pData[index +2 +11])) );
                tempE1O12.setValue(static_cast<int>((pData[index +2 +12]) ));
                tempE1O13.setValue(static_cast<int>((pData[index +2 +13]) ));
                }break;
                
            case 0xE2: {
                // E2.0F.18.04.16.0A.22.10.00.00.00.41.3E.DC.00.00.00
                tempE2.setValue(Valuetrame.c_str());
                }break;

            case 0xE4: {
                // E4.0F.18.04.12.15.00.00.46.00.0B.13.00.00.00.00.00
                tempE4.setValue(Valuetrame.c_str());
                }break;
    
            case 0xFE: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_FE = temp;
                tempFE.setValue(temp);
                
                }break;
    
            default: {
                Serial.print( "idvaleur Non implementee " );
                Serial.println(idvaleur);
                };
            }
            countertry ++;
            if (countertry == 21) {
                countertry = 0;
            }
            index = index + 2 + taillevaleur ;

        }
        timeScheduler.addTask(taskloopHaIntegration);
        taskloopHaIntegration.enable();
        //Serial.println("fin traitement trame");
       
    }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}


/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(advDevice->toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  // Connect to the remove BLE Server.
  pClient->connect(advDevice); 
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  //il faut s'abonner la la charactisitic "indication" afin de recevoir les notification de publication
  if(pRemoteCharacteristic->canIndicate())
    pRemoteCharacteristic->subscribe(false, notifyCB);

    pRemoteCharWrite = pRemoteService->getCharacteristic(charUUIDwrite);
    if (pRemoteCharWrite == nullptr) {
        Serial.print("Failed to find our characteristic UUID: ");
        Serial.println(charUUIDwrite.toString().c_str());
        pClient->disconnect();
        return false;
    } else {
        Serial.println("caracteristic Write - Found our characteristic");
    }


  connected = true;
  return true;
  
}

void cb_setupAndScan_ble() {
    taskBleSetupAndScan.disable();
    Serial.println("Starting NimBLE Client");
    /** Initialize NimBLE, no device name spcified as we are not advertising */
    NimBLEDevice::init("");
    /** Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /** 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, secure connections.
     *
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power, default is 3db */
    #ifdef ESP_PLATFORM
        NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
    #else
        NimBLEDevice::setPower(9); /** +9db */
    #endif

    /** Optional: set any devices you don't want to get advertisments from */
    // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));

    /** create new scan */
    NimBLEScan* pScan = NimBLEDevice::getScan();
     Serial.println("avant callback");
    /** create a callback that gets called when advertisers are found */
    pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(45);
    pScan->setWindow(15);

    /** Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    Serial.println("avant set active scan");
    pScan->setActiveScan(true);
    /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
     *  Optional callback for when scanning stops.
     */
    //pScan->start(scanTime, scanEndedCB);
    pScan->start(10, false);
    Serial.println("Fin de cb_setupAndScan_ble");
    
}

void onValueConsignePhChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of PH Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(48,numberFloat * 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}
void onValueConsigneorpChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(53,numberFloat / 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}

void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Alarme ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x37,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsignevolChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Volume Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x11,numberFloat ,2);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneacideChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Acide Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x32,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneprodChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Production Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); ;
        onwriteble(0x33,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneinversionChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Version Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str());        
        onwriteble(0x39,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}
void onwriteble (int ValueID ,float numberset,int ValueSizea){
    Serial.println("debut onwriteble : ");
    static uint8_t trame[80] = {0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //union float2bytes { float f; char b[sizeof(float)]; } ;
    typedef union _data {
        int f;
        char  s[2];
    } myData;

    //float2bytes myvalue;
    myData myvalue;
    myvalue.f =  static_cast<int>(numberset);
    trame[3]= ValueID ;
    trame[4]= ValueSizea;
    switch (ValueSizea){
    case 1:
        //uint8_t hexnumberset = (numberset) & 0xff;
        trame[5]= myvalue.s[0];
        break;

    case 2:
        trame[5]= 0x00;
        trame[6]= myvalue.s[0];
        break;
    
    default:
        break;
    }
    //Serial.println(sizeof(trame));
    std::string str = "send des valeur ";
    str += ": Number a set = " + std::to_string(numberset);
    str += ": Myvalue f = " + std::to_string(myvalue.f);
    str += ": Myvalue s 0 = " + std::to_string(myvalue.s[0]);
    str += ": Myvalue s 1 = " + std::to_string(myvalue.s[1]);
    str += " : value ID = " + std::to_string(trame[3]);
    str += ", taille = " + std::to_string(trame[4]);
    str += ", Value ch 5= " + std::to_string(trame[5]);
    str += ", Value ch 6= " + std::to_string(trame[6]);

    Serial.println(str.c_str());
    telnet.println(str.c_str());
    
    bool writeResponse;
    writeResponse = pRemoteCharWrite->writeValue(trame, 80, true);
    Serial.println(writeResponse);
    str = "onwriteble  : ";
    str += "send trame result = " + std::to_string(writeResponse);
    if (writeResponse == 1) {
        str += " OK ";
    }else {
        str += " KO ";
    }
    Serial.println(str.c_str());
    telnet.println(str.c_str());
}



void setup (){
    Serial.begin(115200);
    timeScheduler.init();

    timeScheduler.addTask(taskSetup);
    taskSetup.enable();
    Serial.println("add Task to setup justsalt");
    //bluetoothesp.setState(true);
}


void loop (){
    timeScheduler.execute();
}


void cb_connectBleServer(){
  //connection au serveur Ble
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
    Serial.println("debut cb_connectBleServer");
   
    if (connected == true ){
        Serial.println("connected = true");

        // check si on est connecté BLE (evolution des trames notifié ) 
        if (countertrame != countertrameold){
            //Serial.println("recepion notification ok donc conecté ");
            std::string str = "recepion notification ok donc conecté ";
            str += ": countertrame  = " + std::to_string(countertrame);
            str += ", countertrameold = " + std::to_string(countertrameold);
            
            Serial.println(str.c_str());
            telnet.println(str.c_str());

            countertrameold=countertrame;
            delay(50);
        } else {
            Serial.println("recepion notification KO donc liaison BT HS ");
            connected = false;
            doConnect = true;
        } 
        Serial.println("apres check connection");
        
    }else{
        if (doConnect == true) {
            Serial.println("doConnect is true");
            if (connectToServer()) {
                Serial.println("We are now connected to the BLE Server.");
                //write sur la prochaine itération de la task
                taskConnectBleServer.forceNextIteration();
            } else {
                Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                doScan = true;
            }
            doConnect = false;
            Serial.println("doConnect ********************* to false ");
            //on relance un scan
        }
        if (doScan == true){
            Serial.println( "add Task to Scan Ble devices");
            timeScheduler.addTask(taskBleSetupAndScan);
            taskBleSetupAndScan.enable();
        }
        Serial.println("connected = false");
    } 
}
1 « J'aime »

@Ricky_D, content de voir que tu avances sur ton projet ! je vois de la ressemblance dans le code :wink: c’est cool que mon code serve aussi a un autre matériel !

Tu pourras stp me redonner ton GitHub, je crois que le lien plus haut ne fonctionne pas.

mon git etait en mode « privacy » je l ai passé en mode « Public »

bonjour Messieurs
je me demande si la valeur temp 0E ne serait pas des heures de fonctionnement je ne sais pas de quoi par contre
pouvez vous me dire si pour vous aussi elle s incrémente de 1 toutes les heure ?
et ne repart pas de 0 ?

pouvez vous aussi me confirmer le changement de valeur de la temp02 pendant l injection de PH
perso
Injection PH- => 218
pas d injection => 208

ce qui donnerai un truc du genre
1101 0000 pas inj ph-
1101 1010 inj PH

je vais vous laisser faire de l analyse :slight_smile:
car je voudrais travailler sur l exclusion des données dans le code
je vais essayer de mettre en œuvre Wifi-Manager dans la prochaine version
c est un portail captif qui permettra de déclarer le Wifi et le serveur Mqtt ainsi je pourrais fournir pour ceux qu’ils ne veulent pas s’embêter uniquement le firmeware a pousser sur l’esp !

le « code ID » / « code de configuration » dans epool change en fonction des parametre
exemple le reglage durée boost agit dessus

Temp0e ne S incrémente pas chez moi: je reste à 1.
Info c’est que mon electroliseur est allumé mais n’est pas en fonctionnement le circuit d injection et d’analyse est fermé. Mais mises en services demain donc peut être que jaurai un changement.


Électroliseur en service depuis ce matin 9h
Donc ça confirme ce que tu as dis

juste pour vous informez que je travaille toujours sur le projet
mais je me confronte a des problèmes avec Wifi manager
le but et de créer un point accès wifi , offrant un portail captif ou nous pourrions saisir le réseau wifi et les paramètre du serveur Mqtt

pour le moment ,et je ne comprend pas pourquoi , les point acces sont pas visible a partir d un téléphone Android ou d’un PC, mais il est visible avec des scan wifi d’un autre ESP32 !

quand au dev principale je dois traiter toutes les valeur temp différemment nous passons a coter d information !
ex: date du dernier étalonnage ORP ,date du dernier étalonnage PH , heure total de fonctionnement electolyseur, version tlv

Bonjour ,
pour la détection des réseaux Wifi j’ai mon PC portable et mon tel qui choissisent en priorité les réseaux 5 Ghz .
Lorsque je programme un esp8266 ou esp32 , je ne vois pas le point d’accès , je suis obligé de forcer le wifi 2,4 Ghz sur le PC pour voir le point d’accès créé par l’esp (portail captif)

Chez moi j’ai ma box et mon routeur en 5 Ghz prioritaire .
C’est une idée , qui n’a peut etre rien à voir avec Wifi manager (problème dans le code )

Salut et merci des infos
malheureusement j ai déjà fait des tests dans ce sens , j ai mis une application « Wifi analyser » qui est « sensé » trouver les réseau 2.4G , elle en trouve mais pas mon esp32

je viens de me commander 2 * ESP32-C6 avec 8M de Flash pour tester , je les reçoit fin du moi en principe ! (ca ma couter 12€ les 2 frais de port inclus) donc si ca marche ! il va falloir faire des frais désolé !

2 « J'aime »

bonjour
en préparant la refonte complète du code j’en ai profiter pour corriger quelques coquille et implémenter les nouvelles valeurs trouvé :slight_smile: !

il est nécessaires de supprimer le device mqtt avant de mettre le nouveau code en place !

j ai personnellement étalonné mon ORP et le PH le même jour donc si pour l un d’entre vous ce n est pas le cas merci de me confirmer que les dates sont sur les bon champs !

pour les curieux les logs en on bcp changé , dans la version V0.2.X je vais tous passer dans une structure de donné et j ai commencer a la travailler dans cette V0.0.2

voila

#define ARDUINOHA_DEBUG
#include <Arduino.h>


/** Electolyseur JustSalt & co
 *
 *  Passerelle Bt Justsalt to Mqtt
 *
 *  Created: Mai 2025
 *      Author: Ricky
 *
*/


#include <NimBLEDevice.h>
#include <ArduinoHA.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
#include <TaskScheduler.h>

//#include <FS.h>         
//#include <DNSServer.h>
//#include <WiFiManager.h>   
//#include <ArduinoJson.h>   

const char* version = "0.0.2";
// Update these with values suitable for your wifi network.
const char* ssid = "xxxxxxxx-wifi";
const char* password = "xxxxxxxxxxxxxxxxxxxx";

//Home Assistant integration
// configure here your HA params for connection to MQTT server
#define BROKER_ADDR IPAddress(192,168,50,11)
#define BROKER_USERNAME     "" // replace with your credentials
#define BROKER_PASSWORD     ""

//Unique device for HA integration
const byte deviceUniqID[] = { 0x94, 0xDE, 0xB8, 0xA1, 0x1A, 0xAC };

//justsalt service and char UUID
// The remote service we wish to connect to.
static  BLEUUID serviceUUID("09912756-7b32-4629-aeb1-b309d9a338ae");
// The characteristic of the remote service we are interested in.
static  BLEUUID    charUUID("ef785c24-22bb-463d-b651-0b7445ba091c");
static  BLEUUID    charUUIDwrite("4d32c5e5-2bb5-45c6-8c89-6f59bb3930d2");

static NimBLEAdvertisedDevice* advDevice;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static int countertrameold = 0;
static int countertrame = 5;
static int countertry = 0;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static NimBLERemoteCharacteristic* pRemoteCharWrite;

static NimBLEClient* pClient;
static NimBLERemoteService* pSvc ;
static NimBLERemoteCharacteristic* pChr ;
static NimBLERemoteDescriptor* pDsc ;

WiFiClient wifiMQTT;
WiFiClient telnet;
WiFiServer telnetServer(23);

Scheduler timeScheduler;
//AsyncWebServer webServer(80);


unsigned long previousMillis = 0;
unsigned long interval = 30000;

void scanEndedCB(NimBLEScanResults results);
void setup_wifi();
void setup_glogal();
void reconnect_wifi();
void setupHaIntegration();
void setup_telnet();
void cb_handleTelnet();
void cb_loopHaIntegration();
//void cb_loopElegantOTA();
void cb_loopAvaibilityMQTT();
void cb_setupAndScan_ble();
void cb_connectBleServer();
void onValueConsignePhChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender);
void onValueConsignevolChanged( HANumeric number, HANumber* sender);
void onValueConsigneacideChanged( HANumeric number, HANumber* sender);
void onValueConsigneprodChanged( HANumeric number, HANumber* sender);
void onValueConsigneinversionChanged( HANumeric number, HANumber* sender);
void onwriteble (int ValueID ,float numberset,int ValueSizea);

bool connectToServer();


Task taskSetup(5000,TASK_FOREVER,&setup_wifi);
Task taskReconnectWifi(interval,TASK_FOREVER,&reconnect_wifi);
Task taskTelnet(5000,TASK_FOREVER,&cb_handleTelnet);
//Task taskloopElegantOTA(5000,TASK_FOREVER,&cb_loopElegantOTA);
Task taskBleSetupAndScan(30000, TASK_FOREVER, &cb_setupAndScan_ble);
Task taskConnectBleServer(50000, TASK_FOREVER, &cb_connectBleServer);
Task taskloopHaIntegration(5000, TASK_FOREVER,&cb_loopHaIntegration);
Task taskloopAvaibilityMQTT(3000, TASK_FOREVER, &cb_loopAvaibilityMQTT);

//static BLEAdvertisedDevice* advDevice;

static uint32_t scanTime = 0; /** 0 = scan forever */

struct trame_value {
    uint8_t     V_id_00;
    float       V_id_01;    // PH
    float       V_id_02;
    float       V_id_03;
    uint16_t    V_id_06;    // ORP
    float       V_id_08;
    float       V_id_09;    // temp Eau
    float       V_id_0A;    // Sel
    float       V_id_0B;
    float       V_id_0C;
    float       V_id_0D;
    float       V_id_0E;    // compteur ? en heure
    float       V_id_0F;    // minutes de fonctionement
    float       V_id_10;
    float       V_id_11;    // Volume eau
    float       V_id_12;
    float       V_id_13;
    float       V_id_1F;
    float       V_id_28;
    float       V_id_29;
    float       V_id_2A;
    float       V_id_30;    // Consigne PH
    float       V_id_31;
    float       V_id_32;    // % Acide
    float       V_id_33;    // Production ?
    float       V_id_35;    // Consigne ORP
    float       V_id_37;    // Seuil alarme ORP
    float       V_id_39;    // Inversion
    float       V_id_50;
    float       V_id_51;
    float       V_id_5F;
    float       V_id_69;
    float       V_id_6A;
    float       V_id_8F;
    uint64_t    V_id_90;
    uint64_t    V_id_91;
    float       V_id_92;
    uint16_t     V_id_93;
    bool        V_id_93_b00;
    bool        V_id_93_b01;
    bool        V_id_93_b02;
    bool        V_id_93_b03;
    bool        V_id_93_b04;
    bool        V_id_93_b05;
    bool        V_id_93_b06;
    bool        V_id_93_b07;
    bool        V_id_93_b10;
    bool        V_id_93_b11;
    bool        V_id_93_b12;
    bool        V_id_93_b13;
    bool        V_id_93_b14;
    bool        V_id_93_b15;
    bool        V_id_93_b16;
    bool        V_id_93_b17;
    float       V_id_94;
    uint32_t    V_id_95;    // ID Code
    String      V_id_96;    // Version
    String      V_id_97;    // Slave
    String      V_id_99;    // Nom
    String      V_id_9A;    // SN
    float       V_id_9B;
    float       V_id_9C;
    String      V_id_9D;
    String      V_id_A3;
    float       V_id_B0;
    String      V_id_B1;    // Mac adresse
    String      V_id_D0;
    float       V_id_D1;
    String      V_id_E1;
    String      V_id_E2;
    String      V_id_E4;
    float       V_id_FE;
};


struct detailvalue {
  int ID;
  int taille;
  String valeurdetrame;
};


struct detailvalue Electrovaluefull[255];


struct trame_value Electrovalue;

HADevice deviceHA;
// dernier paramètre pour le nombre de sensorMQTT à lister
HAMqtt  mqtt(wifiMQTT, deviceHA, 90);

// Command ESP
HASwitch rebootesp("rebootESP");
HASwitch bluetoothesp("bluetoothESP");

//List of sensor for HA
HABinarySensor bluetoothConnected("pool_bluetooth_connected");
HASensorNumber wifiStrength("pool_wifi_strength", HASensorNumber::PrecisionP2);
HASensor justsaltIp("pool_ip");
HASensorNumber tempeau("pool_temp",HASensorNumber::PrecisionP1) ; 
HASensorNumber ph("pool_ph",HASensorNumber::PrecisionP1);
HASensorNumber orp("pool_orp",HASensorNumber::PrecisionP0);
HASensorNumber sel("pool_sel",HASensorNumber::PrecisionP1);
HANumber vol("pool_vol",HASensorNumber::PrecisionP0);
HANumber phconsigne("pool_ph_consigne",HASensorNumber::PrecisionP1);
HANumber acide("pool_acide",HASensorNumber::PrecisionP1);
HANumber prod("pool_prod",HASensorNumber::PrecisionP0);
HANumber orpconsigne("pool_orp_consigne",HASensorNumber::PrecisionP0);
HANumber orpalarme("pool_orp_alarme",HASensorNumber::PrecisionP0);
HANumber inversion("pool_inversion",HASensorNumber::PrecisionP0);
HASensorNumber temp02("pool_temp02",HASensorNumber::PrecisionP0);
HASensorNumber temp03("pool_temp03",HASensorNumber::PrecisionP0);
HASensorNumber temp08("pool_temp08",HASensorNumber::PrecisionP0);
HASensorNumber temp0B("pool_temp0B",HASensorNumber::PrecisionP0);
HASensorNumber temp0C("pool_temp0C",HASensorNumber::PrecisionP0);
HASensorNumber temp0D("pool_temp0D",HASensorNumber::PrecisionP0);
HASensorNumber temp0E("pool_temp0E",HASensorNumber::PrecisionP0);
HASensorNumber temp0F("pool_temp0F",HASensorNumber::PrecisionP0);
HASensorNumber temp10("pool_temp10",HASensorNumber::PrecisionP0);
HASensor temp12("pool_temp12");
HASensor temp13("pool_temp13");
HASensorNumber temp1F("pool_temp1F",HASensorNumber::PrecisionP0);
HASensorNumber temp28("pool_temp28",HASensorNumber::PrecisionP0);
HASensorNumber temp29("pool_temp29",HASensorNumber::PrecisionP0);
HASensorNumber temp2A("pool_temp2A",HASensorNumber::PrecisionP0);
HASensorNumber temp31("pool_temp31",HASensorNumber::PrecisionP0);
HASensorNumber temp50("pool_temp50",HASensorNumber::PrecisionP0);
HASensorNumber temp51("pool_temp51",HASensorNumber::PrecisionP0);
HASensorNumber temp5F("pool_temp5F",HASensorNumber::PrecisionP0);
HASensorNumber temp69("pool_temp69",HASensorNumber::PrecisionP0);
HASensorNumber temp6A("pool_temp6A",HASensorNumber::PrecisionP0);
HASensorNumber temp8F("pool_temp8F",HASensorNumber::PrecisionP0);
HASensor temp90("pool_temp90");
HASensor temp91("pool_temp91");
HASensorNumber temp92("pool_temp92",HASensorNumber::PrecisionP0);
HASensorNumber temp93("pool_temp93",HASensorNumber::PrecisionP0);
HABinarySensor temp93B00("false93B00");
HABinarySensor temp93B01("false93B01");
HABinarySensor temp93B02("false93B02");
HABinarySensor temp93B03("false93B03");
HABinarySensor temp93B04("false93B04");
HABinarySensor temp93B05("false93B05");
HABinarySensor temp93B06("false93B06");
HABinarySensor temp93B07("false93B07");
HABinarySensor temp93B10("false93B10");
HABinarySensor temp93B11("false93B11");
HABinarySensor temp93B12("false93B12");
HABinarySensor temp93B13("false93B13");
HABinarySensor temp93B14("false93B14");
HABinarySensor temp93B15("false93B15");
HABinarySensor temp93B16("false93B16");
HABinarySensor temp93B17("false93B17");
HASensorNumber temp94("pool_temp94",HASensorNumber::PrecisionP0);
HASensorNumber temp95("pool_temp95",HASensorNumber::PrecisionP0);
HASensor temp96("pool_temp96");
HASensor temp97("pool_temp97");
HASensor temp99("pool_temp99");
HASensor temp9A("pool_temp9A");
HASensorNumber temp9B("pool_temp9B",HASensorNumber::PrecisionP0);
HASensorNumber temp9C("pool_temp9C",HASensorNumber::PrecisionP0);
HASensor temp9D("pool_temp9D");
HASensor tempA3("pool_tempA3");
HASensorNumber tempB0("pool_tempB0",HASensorNumber::PrecisionP0);
HASensor tempB1("pool_tempB1");
HASensor tempD0("pool_tempD0");
HASensorNumber tempD1("pool_tempD1",HASensorNumber::PrecisionP0);
HASensor tempE1("pool_tempE1");
HASensorNumber tempE1O8("pool_tempE1O8",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O9("pool_tempE1O9",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O10("pool_tempE1O10",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O11("pool_tempE1O11",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O12("pool_tempE1O12",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O13("pool_tempE1O13",HASensorNumber::PrecisionP0);

HASensor tempE2("pool_tempE2");
HASensor tempE4("pool_tempE4");
HASensorNumber tempFE("pool_tempFE",HASensorNumber::PrecisionP0);

void cb_handleTelnet() {
  if (telnetServer.hasClient()) {
    if (!telnet || !telnet.connected()) {
      if (telnet) telnet.stop();
      telnet = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}

// void cb_loopElegantOTA(){
//   ElegantOTA.loop();
// }


void cb_loopAvaibilityMQTT(){
  mqtt.loop();
  //remove setAvaibility to use native check of Ha integration Shared availability
  //deviceHA.setAvailability(true);

  //savoir si la connexion bluetooth est OK ou si le justsalt n'est pas sous tension.
    bluetoothConnected.setState(connected);
  

}

void cb_loopHaIntegration(){
    //Serial.println("cb_loopHaIntegration");
    taskloopHaIntegration.disable();
    mqtt.loop();
    //remove setAvaibility to use native check of Ha integration Shared availability
    deviceHA.setAvailability(true);

    wifiStrength.setValue(WiFi.RSSI());
    justsaltIp.setValue(WiFi.localIP().toString().c_str());
    Serial.println("----------------------------- debut");
  telnet.println("----------------------------- debut");

  for (size_t i = 0; i < 254; i++)
  {
    if(Electrovaluefull[i].valeurdetrame != NULL ){
     
        std::string str = "Valeur ";
        str += ": ID = " + std::to_string(Electrovaluefull[i].ID);
        str += ": taille = " + std::to_string(Electrovaluefull[i].taille);
        str += ": Value = " + std::string(Electrovaluefull[i].valeurdetrame.c_str());

        Serial.println(str.c_str());
        telnet.println(str.c_str());
    } 
  }
  Serial.println("----------------------------- Fin");
  telnet.println("----------------------------- Fin");
  

}

void onStateChangedrebootesp (bool state, HASwitch* s){
    if (state == true){
        //lancer le reboot
        state =false;
        //esp_restart();
        //timeScheduler.disable() ;
        esp_restart();
     
        
    }
}


void onStateChangedbluetoothesp (bool state, HASwitch* s){
    if (state == true){
        Serial.println("BT start");
        btStart();    
    } else {
        Serial.println("BT stop");
        btStop();
    }
}

void setupHaIntegration(){
    //HA integration
    //deviceUniqID =WiFi.macAddress();
    deviceHA.setUniqueId(deviceUniqID, sizeof(deviceUniqID));
    
    //deviceHA.setUniqueId(WiFi.macAddress(), sizeof(WiFi.macAddress()));
    
    deviceHA.setName("justsalt");
    deviceHA.setSoftwareVersion(version);
    deviceHA.setModel("Electolyseur");
    deviceHA.setManufacturer("ricky");

    // This method enables availability for all device types registered on the device.
    // For example, if you have 5 sensors on the same device, you can enable
    // shared availability and change availability state of all sensors using
    // single method call "device.setAvailability(false|true)"
    deviceHA.enableSharedAvailability();

    // Optionally, you can enable MQTT LWT feature. If device will lose connection
    // to the broker, all device types related to it will be marked as offline in
    // the Home Assistant Panel.
    deviceHA.enableLastWill();

    rebootesp.setName("Reboot ESP");
    rebootesp.setIcon("mdi:restart");
    rebootesp.onCommand(onStateChangedrebootesp);

    bluetoothesp.setName("Bluetooth ESP");
    bluetoothesp.setIcon("mdi:bluetooth");
    bluetoothesp.onCommand(onStateChangedbluetoothesp);

    wifiStrength.setName("Pool wifi Strength");
    wifiStrength.setDeviceClass("signal_strength");
    wifiStrength.setUnitOfMeasurement("dB");

    justsaltIp.setName("ESP justsalt IP");
    justsaltIp.setIcon("mdi:ip-network");

        // HA integration List of Sensor
    tempeau.setName("Water temp");
    tempeau.setUnitOfMeasurement("°C");
    tempeau.setDeviceClass("temperature");
    tempeau.setIcon("mdi:thermometer");
    
    orp.setName("orp");
    orp.setUnitOfMeasurement("mV");
    orp.setIcon("mdi:flash-triangle-outline");

    ph.setName("PH");
    ph.setIcon("mdi:ph");
    ph.setUnitOfMeasurement("ph");

    sel.setName("Sel");
    sel.setUnitOfMeasurement("g/L");
    sel.setIcon("mdi:water-opacity");

    phconsigne.setName("PH Consigne");
    phconsigne.setIcon("mdi:ph");
    phconsigne.setUnitOfMeasurement("ph");
    phconsigne.setStep(0.1);
    phconsigne.setMin(6.8);
    phconsigne.setMax(7.6);
    phconsigne.onCommand(onValueConsignePhChanged);

    orpconsigne.setName("orp Consigne");
    orpconsigne.setUnitOfMeasurement("mV");
    orpconsigne.setIcon("mdi:flash-triangle-outline");
    orpconsigne.setStep(10);
    orpconsigne.setMin(200);
    orpconsigne.setMax(900);
    orpconsigne.onCommand(onValueConsigneorpChanged);
    
    orpalarme.setName("orp Alarme");
    orpalarme.setUnitOfMeasurement("h");
    orpalarme.setIcon("mdi:alarm");
    orpalarme.setStep(6);
    orpalarme.setMin(12);
    orpalarme.setMax(96);
    orpalarme.onCommand(onValueConsigneorpalarmeChanged);
  
    vol.setName("volume piscine");
    vol.setUnitOfMeasurement("m3");
    vol.setIcon("mdi:cup-water");
    vol.setStep(10);
    vol.setMin(10);
    vol.setMax(200);
    vol.onCommand(onValueConsignevolChanged);

    acide.setName("taux Acide");
    acide.setUnitOfMeasurement("%");
    acide.setIcon("mdi:skull-crossbones-outline");
    acide.setStep(1);
    acide.setMin(5);
    acide.setMax(55);
    acide.onCommand(onValueConsigneacideChanged);
    
    prod.setName("production");
    prod.setUnitOfMeasurement("%");
    prod.setIcon("mdi:cog-outline");
    prod.setStep(1);
    prod.setMin(10);
    prod.setMax(100);
    prod.onCommand(onValueConsigneprodChanged);

    inversion.setName("inversion");
    inversion.setUnitOfMeasurement("h");
    inversion.setIcon("mdi:alarm");
    inversion.setStep(1);
    inversion.setMin(2);
    inversion.setMax(24);
    inversion.onCommand(onValueConsigneinversionChanged);
    
    temp02.setName("temp02");
    temp03.setName("temp03");
    temp08.setName("temp08");
    temp0B.setName("temp0B");
    temp0C.setName("temp0C");
    temp0D.setName("temp0D");
    temp0E.setName("durée total electrolyseur");
    temp0E.setUnitOfMeasurement("h");
    temp0E.setIcon("mdi:timer-sand");

    temp0F.setName("minutes de fonctionement");
    temp0F.setUnitOfMeasurement("Min");
    temp0F.setIcon("mdi:counter");

    temp10.setName("temp10");
    temp12.setName("date etalonage PH?");
    temp12.setIcon("mdi:calendar-star-four-points");

    temp13.setName("date etalonage ORP?");
    temp13.setIcon("mdi:calendar-star-four-points");
    
    temp1F.setName("temp1F");
    temp28.setName("temp28");
    temp29.setName("temp29");
    temp2A.setName("temp2A");
    temp31.setName("temp31");
    temp50.setName("temp50");
    temp51.setName("temp51");
    temp5F.setName("temp5F");
    temp69.setName("temp69");
    temp6A.setName("temp6A");
    temp8F.setName("temp8F");
    temp90.setName("temp90");
    temp91.setName("temp91");
    temp92.setName("temp92");
    temp93.setName("temp93");
    temp93B00.setName("temp93B00");
    temp93B01.setName("temp93B01");
    temp93B02.setName("temp93B02");
    temp93B03.setName("temp93B03");
    temp93B04.setName("temp93B04");
    temp93B05.setName("temp93B05");
    temp93B06.setName("temp93B06");
    temp93B07.setName("temp93B07");
    temp93B10.setName("temp93B10");
    temp93B11.setName("temp93B11");
    temp93B12.setName("temp93B12");
    temp93B13.setName("temp93B13");
    temp93B14.setName("temp93B14");
    temp93B15.setName("temp93B15");
    temp93B16.setName("temp93B16");
    temp93B17.setName("temp93B17");
    temp94.setName("temp94");
    temp95.setName("ID code");
    temp95.setIcon("mdi:barcode");

    temp96.setName("version");
    temp96.setIcon("mdi:qrcode");

    temp97.setName("slave");
    temp97.setIcon("mdi:qrcode-edit");

    temp99.setName("nom");
    temp99.setIcon("mdi:card-account-details-outline");

    temp9A.setName("SN");
    temp9A.setIcon("mdi:barcode");

    temp9B.setName("temp9B");
    temp9C.setName("temp9C");
    temp9D.setName("temp9D");
    tempA3.setName("tempA3");
    tempB0.setName("tempB0");
    tempB1.setName("Mac adr");
    tempB1.setIcon("mdi:network-outline");

    tempD0.setName("tempD0");
    tempD1.setName("TLV");
    tempE1.setName("tempE1");

    tempE1O8.setName("tempE1O8");
    tempE1O9.setName("tempE1O9");
    tempE1O10.setName("tempE1O10");
    tempE1O11.setName("tempE1O11");
    tempE1O12.setName("tempE1O12");
    tempE1O13.setName("tempE1O13");
   
    tempE2.setName("tempE2");
    tempE4.setName("tempE4");
    tempFE.setName("tempFE");

    bluetoothConnected.setName("Bluetooth Status");

  mqtt.begin( BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD );

}

void setup_telnet(){
  telnetServer.begin();
  telnetServer.setNoDelay(true); 

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.localIP());
  Serial.println(" 23' to connect");
  
  timeScheduler.addTask(taskTelnet);
  taskTelnet.enable();
  Serial.println("Add Task telnet handle");
}

void reconnect_wifi(){
   unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    #ifdef SYSLOG_SERVER
      syslog.log(LOG_INFO, "WIFI lost and reconnect automatically");
    #endif
    previousMillis = currentMillis;
  }
}

void setup_wifi() {
    taskSetup.disable();

    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");

    WiFi.mode(WIFI_STA);
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    //Reconnect wifi Task
    timeScheduler.addTask(taskReconnectWifi);
    taskReconnectWifi.enable();
    Serial.print("Add task to monitor and reconnect wifi");
    
    //Elegant OTA
    // webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    // request->send(200, "text/plain", "Hi! I am ESP32 justsalt to update use http://[yourIP]/update.");
    //  });
    // ElegantOTA.begin(&webServer);    // Start ElegantOTA
    // webServer.begin();
    // Serial.println("HTTP server started");

    // //elegant OTA loop
    // timeScheduler.addTask(taskloopElegantOTA);
    // taskloopElegantOTA.enable();
    // Serial.println("Add task for loop of Elegant OTA");

    //telnet setup
    setup_telnet();
    //launch handle telnet
    timeScheduler.addTask(taskTelnet);
    taskTelnet.enable();
    Serial.print("Add task to Handle telnet");

    //setup mqtt
    setupHaIntegration();
    //loop avaibility for mqtt
    timeScheduler.addTask(taskloopAvaibilityMQTT);
    taskloopAvaibilityMQTT.enable();
    telnet.print("Add task for loop of MQTT");
    
    //cb_setupAndScan_ble();
    doScan = true;
    timeScheduler.addTask(taskConnectBleServer);
    taskConnectBleServer.enable();
    
    Serial.print("Add task to Connec Ble server!!!!!!");
 
}


/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println("Connected");
        /** After connection we should change the parameters if we don't need fast response times.
         *  These settings are 150ms interval, 0 latency, 450ms timout.
         *  Timeout should be a multiple of the interval, minimum is 100ms.
         *  I find a multiple of 3-5 * the interval works best for quick response/reconnect.
         *  Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
         */
        pClient->updateConnParams(120,120,0,60);
    };

    void onDisconnect(NimBLEClient* pClient) {
        Serial.print(pClient->getPeerAddress().toString().c_str());
        Serial.println(" Disconnected - Starting scan");
        NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
    };

    /** Called when the peripheral requests a change to the connection parameters.
     *  Return true to accept and apply them or false to reject and keep
     *  the currently used parameters. Default will return true.
     */
    bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
        if(params->itvl_min < 24) { /** 1.25ms units */
            return false;
        } else if(params->itvl_max > 40) { /** 1.25ms units */
            return false;
        } else if(params->latency > 2) { /** Number of intervals allowed to skip */
            return false;
        } else if(params->supervision_timeout > 100) { /** 10ms units */
            return false;
        }

        return true;
    };

    /********************* Security handled here **********************
    ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest(){
        Serial.println("Client Passkey Request");
        /** return the passkey to send to the server */
        return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key){
        Serial.print("The passkey YES/NO number: ");
        Serial.println(pass_key);
    /** Return false if passkeys don't match. */
        return true;
    };

    /** Pairing process complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc* desc){
        if(!desc->sec_state.encrypted) {
            Serial.println("Encrypt connection failed - disconnecting");
            /** Find the client with the connection handle provided in desc */
            NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
            return;
        }
    };
};



class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
        std::string str = "------------------------------- \r\n" ;
        str += "BLE Advertised Device found: \r\n";
        str += ": Name = " + std::string(advertisedDevice->getName().c_str());
        str += ", adr = " + std::string(advertisedDevice->getAddress().toString().c_str());
        
        Serial.println(str.c_str());
        telnet.println(str.c_str());

        char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
        Serial.println(manufacturerdata);
        telnet.println(manufacturerdata);

        if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
        // if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)) {

            BLEDevice::getScan()->stop();
            advDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
            
            Serial.print("Found our device!  address: ");

            doConnect = true;
            doScan = false;

            // timeScheduler.addTask(taskConnectBleServer);
            taskConnectBleServer.forceNextIteration();
            Serial.println("add Task Connect Ble server");

        } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks



/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
    std::string str = (isNotify == true) ? "Notification" : "Indication";
    str += " from ";
    /** NimBLEAddress and NimBLEUUID have std::string operators */
    str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
    str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
    str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
    str += ", Countertrame = " + std::to_string(countertrame);
    str += ", Value = " + std::string((char*)pData, length);
    countertrame++;
    if (countertrame > 65000){
        countertrame=5;
        countertrameold=0;
    } 
    Serial.println(str.c_str());
    telnet.println(str.c_str());

    Serial.print("recep chaine taille =");
    Serial.println(length);

    telnet.println("recep chaine");
    //telnet.println(std::to_string(length));
    if (length > 5){
        int index =3;
        while (index +2 < length) {
            uint8_t idvaleur = pData[index];
            int taillevaleur = pData[index + 1 ];
            String Valuetrame = ""; 
            for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                String converttrame = String(pData[i],HEX) ;
                if (converttrame.length() == 1 ){ 
                    converttrame = "0" + converttrame ;
                    }
                Valuetrame += converttrame;
                if (i != index + 1 + taillevaleur){
                    Valuetrame += "." ; 
                }
            }
            Valuetrame.toUpperCase();

            // Serial.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            // telnet.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            std::string str = "Trame " + std::to_string(countertrame);
            str += ": Values ID  = " + std::to_string(idvaleur);
            str += ", taille = " + std::to_string(taillevaleur);
            str += ", Value = " + std::string(Valuetrame.c_str());

            Serial.println(str.c_str());
            telnet.println(str.c_str());

            Electrovaluefull[idvaleur].ID=idvaleur;
            Electrovaluefull[idvaleur].taille=taillevaleur;
            Electrovaluefull[idvaleur].valeurdetrame=Valuetrame.c_str();

            switch (idvaleur) {
            case 0x00: { 
                //Electrovalue.V_id_00 = 0;
                }break;
        
            case 0x01: {
                uint8_t phhex = pData[index + 2];
                float phf = static_cast<float>(phhex)/10;
                ph.setValue(phf);

                }break;

            case 0x02: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp02.setValue(temp);
                }break;

            case 0x03: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_03 = temp; 
                temp03.setValue(temp);
                }break;

            case 0x06: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index + 3]));
                //float temp = static_cast<float>(temphex);
                orp.setValue(temphex);;
                }break;

            case 0x08: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_08 = temp;
                temp08.setValue(temp);
                }break;

            case 0x09: {
                uint16_t tempeauhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float tempeauf = static_cast<float>(tempeauhex)/10;
                tempeau.setValue(tempeauf);
                }break;
            
            case 0x0A: {
                uint8_t selhex = pData[index + 2] ;
                float self = static_cast<float>(selhex)/10;
                sel.setValue(self);
                }break;

            case 0x0B: {
                if(countertry == 20) {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp0B.setValue(temp);
                }
                //Electrovalue.V_id_0B = temp;
                }break;
            
            case 0x0C: {
                if(countertry == 20) {
                    uint8_t temphex = pData[index + 2];
                    float temp = static_cast<float>(temphex);
                    temp0C.setValue(temp);
                }
                }break;

            case 0x0D: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0D.setValue(temp);
                }break;

            case 0x0E: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0E.setValue(temp);
                }break;

            case 0x0F: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0F.setValue(temp);
                }break;
            
            case 0x10: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_10 = temp;
                temp10.setValue(temp);
                }break;

            case 0x11: {
                uint16_t volhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float volf = static_cast<float>(volhex);
                vol.setState(volf);
                }break;
            
            case 0x12: {
                uint8_t dayphex = (pData[index + 3]);
                uint8_t monthphex = (pData[index + 4]);
                uint8_t yearphex = (pData[index +5]);
                int dayf = static_cast<int>(dayphex);
                int monthf = static_cast<int>(monthphex);
                int yearf = static_cast<int>(yearphex);
                //String daystr = String(dayf);
                //String monthstr = String(monthf);
                //String yearstr = String(yearf);

                std::string datestr =  std::to_string(dayf) + "/" + std::to_string(monthf) + "/20" + std::to_string(yearf);
                temp12.setValue(datestr.c_str()) ;
                
                }break;

            case 0x13: {
                uint8_t dayphex = (pData[index + 3]);
                uint8_t monthphex = (pData[index + 4]);
                uint8_t yearphex = (pData[index +5]);
                int dayf = static_cast<float>(dayphex);
                int monthf = static_cast<float>(monthphex);
                int yearf = static_cast<float>(yearphex);
                std::string datestr =  std::to_string(dayf) + "/" + std::to_string(monthf) + "/20" + std::to_string(yearf);
                temp13.setValue(datestr.c_str()) ;
                
                }break;

            case 0x1F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_1F = temp;
                temp1F.setValue(temp);
                }break;
            
            case 0x28: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_28 = temp;
                temp28.setValue(temp);
                
                }break;
            
            case 0x29: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_29 = temp;
                temp29.setValue(temp);
                }break;
            
            case 0x2A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_2A = temp;
                temp2A.setValue(temp);
                }break;
            
            case 0x30: {
                uint8_t phchex = pData[index + 2] ;
                float phcf = static_cast<float>(phchex)/10;
                phconsigne.setState(phcf);
                }break;
            
            case 0x31: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp31.setValue(temp);
                //Electrovalue.V_id_31 = temp;
                }break;         

            case 0x32: {
                uint8_t acidehex = pData[index + 2] ;
                float acidef = static_cast<float>(acidehex);
                //Electrovalue.V_id_32 = acidef;
                acide.setState(acidef);
                }break;
            
            case 0x33: {
                uint8_t prodhex = pData[index + 2] ;
                float prodf = static_cast<float>(prodhex);
                //Electrovalue.V_id_33 = prodf;
                prod.setState(prodf);
                }break;

            case 0x35: {
                uint8_t orpchex = pData[index + 2] ;
                float orpcf = static_cast<float>(orpchex)*10;
                //Electrovalue.V_id_35 = orpcf;
                orpconsigne.setState(orpcf);
                }break;
            
            case 0x37: {
                uint8_t alarmeorphex = pData[index + 2] ;
                float alarmeorpf = static_cast<float>(alarmeorphex);
                //Electrovalue.V_id_37 = alarmeorpf;
                orpalarme.setState(alarmeorpf);
                }break;
    
            case 0x39: {
                uint8_t inversionhex = pData[index + 2] ;
                float inversionvalf = static_cast<float>(inversionhex);
                //Electrovalue.V_id_39 = inversionvalf;
                inversion.setState(inversionvalf);
                }break;

            case 0x50: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_50 = temp;
                temp50.setValue(temp);
                }break;

            case 0x51: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_51 = temp;
                temp51.setValue(temp);
                }break;

            case 0x5F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_5F = temp;
                temp5F.setValue(temp);
                }break;
            
            case 0x69: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_69 = temp;
                temp69.setValue(temp);
                }break;

            case 0x6A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_6A = temp;
                temp6A.setValue(temp);
                }break;

            case 0x8F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_8F = temp;
                temp8F.setValue(temp);
                }break;
                
            case 0x90: {
                // 90.08.00.00.00.00.00.00.00.14.
                temp90.setValue(Valuetrame.c_str());
                }break;

            case 0x91: {
                temp91.setValue(Valuetrame.c_str());
                }break;
            
            case 0x92: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp92.setValue(temp);
                }break;

            case 0x93: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp93.setValue(temp);
                temp93B00.setCurrentState((bool)((temphex & 0x0001) ));
                temp93B01.setCurrentState((bool)((temphex & 0x0002) ));
                temp93B02.setCurrentState((bool)((temphex & 0x0004) ));
                temp93B03.setCurrentState((bool)((temphex & 0x0008) ));
                temp93B04.setCurrentState((bool)((temphex & 0x0010) ));
                temp93B05.setCurrentState((bool)((temphex & 0x0020) ));
                temp93B06.setCurrentState((bool)((temphex & 0x0040) ));
                temp93B07.setCurrentState((bool)((temphex & 0x0080) ));
                temp93B10.setCurrentState((bool)((temphex & 0x0100) ));
                temp93B11.setCurrentState((bool)((temphex & 0x0200) ));
                temp93B12.setCurrentState((bool)((temphex & 0x0400) ));
                temp93B13.setCurrentState((bool)((temphex & 0x0800) ));
                temp93B14.setCurrentState((bool)((temphex & 0x1000) ));
                temp93B15.setCurrentState((bool)((temphex & 0x2000) ));
                temp93B16.setCurrentState((bool)((temphex & 0x4000) ));
                temp93B17.setCurrentState((bool)((temphex & 0x8000) ));
                
                
                }break;

            case 0x94: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_94 = temp;
                temp94.setValue(temp);
                }break;

            case 0x95: {
                uint32_t idcodehex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float idcodefloat = static_cast<float>(idcodehex);
                //Electrovalue.V_id_95 = idcodefloat;
                temp95.setValue(idcodefloat);
                
                }break;
            
            case 0x96: {
                temp96.setValue(Valuetrame.c_str());
                }break;
            
            case 0x97: {
                temp97.setValue(Valuetrame.c_str());
                }break;

            case 0x99: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp99.setValue(tempstring.c_str());
                }break;

            case 0x9A: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp9A.setValue(tempstring.c_str());
                }break;

            case 0x9B: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9B = temp;
                temp9B.setValue(temp);
                }break;

            case 0x9C: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_9C = temp;
                temp9C.setValue(temp);
                }break;

            case 0x9D: {
                temp9D.setValue(Valuetrame.c_str());
                }break;
            
            case 0xA3: {
                tempA3.setValue(Valuetrame.c_str());
                }break;
            
            case 0xB0: {
                if(countertry == 20) {

                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_8F = temp;
                tempB0.setValue(temp);
                }
                
                }break;
                
    
            case 0xB1: {
            //     std::string tempstring = rawhex.substr((index + 2 )* 2, 12);
            //     id(JustSalt_macbt_textsensor).publish_state(tempstring);
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += String(pData[i],HEX) ;
                    if (i != index + 1 + taillevaleur){
                        tempstring += ":" ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempB1.setValue(tempstring.c_str());
                }break;
            
            case 0xD0: {
            //     //D0.06.30.00.00.00.00.00
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    String convert = String(pData[i],HEX) ;
                    if (convert.length() == 1 ){ 
                        convert = "0" + convert ;
                        }
                    tempstring += convert;
                    if (i != index + 1 + taillevaleur){
                        tempstring += "." ; 
                    }
                }
                tempstring.toUpperCase();
                telnet.println( tempstring.c_str());
                tempD0.setValue(tempstring.c_str());
                }break;

            case 0xD1: {
                uint8_t temphex = (pData[index + 2]) ;
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_D1 = temp;
                tempD1.setValue(temp);
                }break;
            
            

            case 0xE1: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                tempE1.setValue(Valuetrame.c_str());
                Electrovalue.V_id_E1 = Valuetrame.c_str();
                 //9-11
                tempE1O8.setValue(static_cast<int>((pData[index +2 +8])) );
                tempE1O9.setValue(static_cast<int>((pData[index +2 +9])) );
                tempE1O10.setValue(static_cast<int>((pData[index +2 +10])) );
                tempE1O11.setValue(static_cast<int>((pData[index +2 +11])) );
                tempE1O12.setValue(static_cast<int>((pData[index +2 +12]) ));
                tempE1O13.setValue(static_cast<int>((pData[index +2 +13]) ));
                }break;
                
            case 0xE2: {
                // E2.0F.18.04.16.0A.22.10.00.00.00.41.3E.DC.00.00.00
                tempE2.setValue(Valuetrame.c_str());
                }break;

            case 0xE4: {
                // E4.0F.18.04.12.15.00.00.46.00.0B.13.00.00.00.00.00
                tempE4.setValue(Valuetrame.c_str());
                }break;
    
            case 0xFE: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                //Electrovalue.V_id_FE = temp;
                tempFE.setValue(temp);
                
                }break;
    
            default: {
                Serial.print( "idvaleur Non implementee " );
                Serial.println(idvaleur);
                };
            }
            countertry ++;
            if (countertry == 21) {
                countertry = 0;
            }
            index = index + 2 + taillevaleur ;

        }
        timeScheduler.addTask(taskloopHaIntegration);
        taskloopHaIntegration.enable();
        //Serial.println("fin traitement trame");
       
    }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}


/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(advDevice->toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  // Connect to the remove BLE Server.
  pClient->connect(advDevice); 
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  //il faut s'abonner la la charactisitic "indication" afin de recevoir les notification de publication
  if(pRemoteCharacteristic->canIndicate())
    pRemoteCharacteristic->subscribe(false, notifyCB);

    pRemoteCharWrite = pRemoteService->getCharacteristic(charUUIDwrite);
    if (pRemoteCharWrite == nullptr) {
        Serial.print("Failed to find our characteristic UUID: ");
        Serial.println(charUUIDwrite.toString().c_str());
        pClient->disconnect();
        return false;
    } else {
        Serial.println("caracteristic Write - Found our characteristic");
    }


  connected = true;
  return true;
  
}

void cb_setupAndScan_ble() {
    taskBleSetupAndScan.disable();
    Serial.println("Starting NimBLE Client");
    /** Initialize NimBLE, no device name spcified as we are not advertising */
    NimBLEDevice::init("");
    /** Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /** 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, secure connections.
     *
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power, default is 3db */
    #ifdef ESP_PLATFORM
        NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
    #else
        NimBLEDevice::setPower(9); /** +9db */
    #endif

    /** Optional: set any devices you don't want to get advertisments from */
    // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));

    /** create new scan */
    NimBLEScan* pScan = NimBLEDevice::getScan();
     Serial.println("avant callback");
    /** create a callback that gets called when advertisers are found */
    pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(45);
    pScan->setWindow(15);

    /** Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    Serial.println("avant set active scan");
    pScan->setActiveScan(true);
    /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
     *  Optional callback for when scanning stops.
     */
    //pScan->start(scanTime, scanEndedCB);
    pScan->start(10, false);
    Serial.println("Fin de cb_setupAndScan_ble");
    
}

void onValueConsignePhChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of PH Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(48,numberFloat * 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}
void onValueConsigneorpChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(53,numberFloat / 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}

void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Alarme ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x37,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsignevolChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Volume Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x11,numberFloat ,2);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneacideChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Acide Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x32,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneprodChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Production Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); ;
        onwriteble(0x33,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneinversionChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Version Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str());        
        onwriteble(0x39,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}
void onwriteble (int ValueID ,float numberset,int ValueSizea){
    Serial.println("debut onwriteble : ");
    static uint8_t trame[80] = {0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //union float2bytes { float f; char b[sizeof(float)]; } ;
    typedef union _data {
        int f;
        char  s[2];
    } myData;

    //float2bytes myvalue;
    myData myvalue;
    myvalue.f =  static_cast<int>(numberset);
    trame[3]= ValueID ;
    trame[4]= ValueSizea;
    switch (ValueSizea){
    case 1:
        //uint8_t hexnumberset = (numberset) & 0xff;
        trame[5]= myvalue.s[0];
        break;

    case 2:
        trame[5]= 0x00;
        trame[6]= myvalue.s[0];
        break;
    
    default:
        break;
    }
    //Serial.println(sizeof(trame));
    std::string str = "send des valeur ";
    str += ": Number a set = " + std::to_string(numberset);
    str += ": Myvalue f = " + std::to_string(myvalue.f);
    str += ": Myvalue s 0 = " + std::to_string(myvalue.s[0]);
    str += ": Myvalue s 1 = " + std::to_string(myvalue.s[1]);
    str += " : value ID = " + std::to_string(trame[3]);
    str += ", taille = " + std::to_string(trame[4]);
    str += ", Value ch 5= " + std::to_string(trame[5]);
    str += ", Value ch 6= " + std::to_string(trame[6]);

    Serial.println(str.c_str());
    telnet.println(str.c_str());
    
    bool writeResponse;
    writeResponse = pRemoteCharWrite->writeValue(trame, 80, true);
    Serial.println(writeResponse);
    str = "onwriteble  : ";
    str += "send trame result = " + std::to_string(writeResponse);
    if (writeResponse == 1) {
        str += " OK ";
    }else {
        str += " KO ";
    }
    Serial.println(str.c_str());
    telnet.println(str.c_str());
}



void setup (){
    Serial.begin(115200);
    // Wifi Manager & wait?

    timeScheduler.init();
    timeScheduler.addTask(taskSetup);
    taskSetup.enable();
    Serial.println("add Task to setup justsalt");
    //bluetoothesp.setState(true);
}


void loop (){
    timeScheduler.execute();
}


void cb_connectBleServer(){
  //connection au serveur Ble
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
    Serial.println("debut cb_connectBleServer");
   
    if (connected == true ){
        Serial.println("connected = true");

        // check si on est connecté BLE (evolution des trames notifié ) 
        if (countertrame != countertrameold){
            //Serial.println("recepion notification ok donc conecté ");
            std::string str = "recepion notification ok donc conecté ";
            str += ": countertrame  = " + std::to_string(countertrame);
            str += ", countertrameold = " + std::to_string(countertrameold);
            
            Serial.println(str.c_str());
            telnet.println(str.c_str());

            countertrameold=countertrame;
            delay(50);
        } else {
            Serial.println("recepion notification KO donc liaison BT HS ");
            connected = false;
            doConnect = true;
        } 
        Serial.println("apres check connection");
        
    }else{
        if (doConnect == true) {
            Serial.println("doConnect is true");
            if (connectToServer()) {
                Serial.println("We are now connected to the BLE Server.");
                //write sur la prochaine itération de la task
                taskConnectBleServer.forceNextIteration();
            } else {
                Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                doScan = true;
            }
            doConnect = false;
            Serial.println("doConnect ********************* to false ");
            //on relance un scan
        }
        if (doScan == true){
            Serial.println( "add Task to Scan Ble devices");
            timeScheduler.addTask(taskBleSetupAndScan);
            taskBleSetupAndScan.enable();
        }
        Serial.println("connected = false");
    } 
}

Bonjour

en attendant de recevoir mais ESP-C6
j’ ai travaillé sur le code et la structure de donnée, et le repérage des valeurs !
j’ai du cout refait une relecture du topic !

je devrais bientôt implémenter un indicateur de l’injection PH !
mais j’aimerais, intégrer la gestion de volet , du cout @starpom je vais avoir besoin que tu face des relever, et me les transmettre.
du cout je vous fait par d’une version de code intermédiaire qui vous permet d’exporter plus facilement !
en effet cette nouvelle version vous permet d’avoir un serveur Web avec les donnés , suffit de mettre ip de votre ESP dans votre browser Web

il me faut dans toutes les valeurs , Pas décodé ,Partiellement décodé ,Value inconnu
pour ton Volet Fermer , ouvert a 20% et completement ouvert

#define ARDUINOHA_DEBUG
#include <Arduino.h>


/** Electolyseur JustSalt & co
 *
 *  Passerelle Bt Justsalt to Mqtt
 *
 *  Created: Mai 2025
 *      Author: Ricky
 *
*/


#include <NimBLEDevice.h>
#include <ArduinoHA.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
#include <TaskScheduler.h>

//#include <FS.h>         
//#include <DNSServer.h>
//#include <WiFiManager.h>   
//#include <ArduinoJson.h>   

const char* version = "0.1.1";
// Update these with values suitable for your wifi network.
const char* ssid = "Durand-wifi";
const char* password = "1975198020132017";

//Home Assistant integration
// configure here your HA params for connection to MQTT server
#define BROKER_ADDR IPAddress(192,168,50,11)
#define BROKER_USERNAME     "" // replace with your credentials
#define BROKER_PASSWORD     ""

//Unique device for HA integration
const byte deviceUniqID[] = { 0x94, 0xDE, 0xB8, 0xA1, 0x1A, 0xAC };

//justsalt service and char UUID
// The remote service we wish to connect to.
static  BLEUUID serviceUUID("09912756-7b32-4629-aeb1-b309d9a338ae");
// The characteristic of the remote service we are interested in.
static  BLEUUID    charUUID("ef785c24-22bb-463d-b651-0b7445ba091c");
static  BLEUUID    charUUIDwrite("4d32c5e5-2bb5-45c6-8c89-6f59bb3930d2");

static NimBLEAdvertisedDevice* advDevice;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static int countertrameold = 0;
static int countertrame = 5;
static int countertry = 0;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static NimBLERemoteCharacteristic* pRemoteCharWrite;

static NimBLEClient* pClient;
static NimBLERemoteService* pSvc ;
static NimBLERemoteCharacteristic* pChr ;
static NimBLERemoteDescriptor* pDsc ;

WiFiClient wifiMQTT;
WiFiClient telnet;
WiFiServer telnetServer(23);

Scheduler timeScheduler;
AsyncWebServer webServer(80);
//WebServer webServer(80);

unsigned long previousMillis = 0;
unsigned long interval = 30000;

void scanEndedCB(NimBLEScanResults results);
void setup_wifi();
void setup_glogal();
void reconnect_wifi();
void setupHaIntegration();
void setup_telnet();
void cb_handleTelnet();
void cb_loopHaIntegration();
void cb_loopElegantOTA();
void cb_loopAvaibilityMQTT();
void cb_setupAndScan_ble();
void cb_connectBleServer();
void onValueConsignePhChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpChanged( HANumeric number, HANumber* sender);
void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender);
void onValueConsignevolChanged( HANumeric number, HANumber* sender);
void onValueConsigneacideChanged( HANumeric number, HANumber* sender);
void onValueConsigneprodChanged( HANumeric number, HANumber* sender);
void onValueConsigneinversionChanged( HANumeric number, HANumber* sender);
void onwriteble (int ValueID ,float numberset,int ValueSizea);
void handleRoot();

bool connectToServer();


Task taskSetup(5000,TASK_FOREVER,&setup_wifi);
Task taskReconnectWifi(interval,TASK_FOREVER,&reconnect_wifi);
Task taskTelnet(5000,TASK_FOREVER,&cb_handleTelnet);
Task taskloopElegantOTA(5000,TASK_FOREVER,&cb_loopElegantOTA);
Task taskBleSetupAndScan(30000, TASK_FOREVER, &cb_setupAndScan_ble);
Task taskConnectBleServer(50000, TASK_FOREVER, &cb_connectBleServer);
Task taskloopHaIntegration(5000, TASK_FOREVER,&cb_loopHaIntegration);
Task taskloopAvaibilityMQTT(3000, TASK_FOREVER, &cb_loopAvaibilityMQTT);

//static BLEAdvertisedDevice* advDevice;

static uint32_t scanTime = 0; /** 0 = scan forever */


struct detailvalue {
  int ID;
  int taille;
  String valeurdetrame;
  String valeurconverted;
  int status; // 0: pas decodé ; 1: decodé ; 2 partielement decodé  ; 3: inconnu (non exporter dans Mqtt) 
  String Description;
};


struct detailvalue Electrovaluefull[255];


HADevice deviceHA;
// dernier paramètre pour le nombre de sensorMQTT à lister
HAMqtt  mqtt(wifiMQTT, deviceHA, 90);

// Command ESP
HASwitch rebootesp("rebootESP");
HASwitch bluetoothesp("bluetoothESP");

//List of sensor for HA
HABinarySensor bluetoothConnected("pool_bluetooth_connected");
HASensorNumber wifiStrength("pool_wifi_strength", HASensorNumber::PrecisionP2);
HASensor justsaltIp("pool_ip");
HASensorNumber tempeau("pool_temp",HASensorNumber::PrecisionP1) ; 
HASensorNumber ph("pool_ph",HASensorNumber::PrecisionP1);
HASensorNumber orp("pool_orp",HASensorNumber::PrecisionP0);
HASensorNumber sel("pool_sel",HASensorNumber::PrecisionP1);
HANumber vol("pool_vol",HASensorNumber::PrecisionP0);
HANumber phconsigne("pool_ph_consigne",HASensorNumber::PrecisionP1);
HANumber acide("pool_acide",HASensorNumber::PrecisionP1);
HANumber prod("pool_prod",HASensorNumber::PrecisionP0);
HANumber orpconsigne("pool_orp_consigne",HASensorNumber::PrecisionP0);
HANumber orpalarme("pool_orp_alarme",HASensorNumber::PrecisionP0);
HANumber inversion("pool_inversion",HASensorNumber::PrecisionP0);
HASensorNumber temp02("pool_temp02",HASensorNumber::PrecisionP0);
HASensorNumber temp03("pool_temp03",HASensorNumber::PrecisionP0);
HASensorNumber temp08("pool_temp08",HASensorNumber::PrecisionP0);
HASensorNumber temp0B("pool_temp0B",HASensorNumber::PrecisionP0);
HASensorNumber temp0C("pool_temp0C",HASensorNumber::PrecisionP0);
HASensorNumber temp0D("pool_temp0D",HASensorNumber::PrecisionP0);
HASensorNumber temp0E("pool_temp0E",HASensorNumber::PrecisionP0);
HASensorNumber temp0F("pool_temp0F",HASensorNumber::PrecisionP0);
HASensorNumber temp10("pool_temp10",HASensorNumber::PrecisionP0);
HASensor temp12("pool_temp12");
HASensor temp13("pool_temp13");
HASensorNumber temp1F("pool_temp1F",HASensorNumber::PrecisionP0);
HASensorNumber temp28("pool_temp28",HASensorNumber::PrecisionP0);
HASensorNumber temp29("pool_temp29",HASensorNumber::PrecisionP0);
HASensorNumber temp2A("pool_temp2A",HASensorNumber::PrecisionP0);
HASensorNumber temp31("pool_temp31",HASensorNumber::PrecisionP0);
HASensorNumber temp50("pool_temp50",HASensorNumber::PrecisionP0);
HASensorNumber temp51("pool_temp51",HASensorNumber::PrecisionP0);
HASensorNumber temp5F("pool_temp5F",HASensorNumber::PrecisionP0);
HASensorNumber temp69("pool_temp69",HASensorNumber::PrecisionP0);
HASensorNumber temp6A("pool_temp6A",HASensorNumber::PrecisionP0);
HASensorNumber temp8F("pool_temp8F",HASensorNumber::PrecisionP0);
HASensor temp90("pool_temp90");
HASensor temp91("pool_temp91");
HASensorNumber temp92("pool_temp92",HASensorNumber::PrecisionP0);
HASensorNumber temp93("pool_temp93",HASensorNumber::PrecisionP0);
HABinarySensor temp93B00("false93B00");
HABinarySensor temp93B01("false93B01");
HABinarySensor temp93B02("false93B02");
HABinarySensor temp93B03("false93B03");
HABinarySensor temp93B04("false93B04");
HABinarySensor temp93B05("false93B05");
HABinarySensor temp93B06("false93B06");
HABinarySensor temp93B07("false93B07");
HABinarySensor temp93B10("false93B10");
HABinarySensor temp93B11("false93B11");
HABinarySensor temp93B12("false93B12");
HABinarySensor temp93B13("false93B13");
HABinarySensor temp93B14("false93B14");
HABinarySensor temp93B15("false93B15");
HABinarySensor temp93B16("false93B16");
HABinarySensor temp93B17("false93B17");
HASensorNumber temp94("pool_temp94",HASensorNumber::PrecisionP0);
HASensorNumber temp95("pool_temp95",HASensorNumber::PrecisionP0);
HASensor temp96("pool_temp96");
HASensor temp97("pool_temp97");
HASensor temp99("pool_temp99");
HASensor temp9A("pool_temp9A");
HASensorNumber temp9B("pool_temp9B",HASensorNumber::PrecisionP0);
HASensorNumber temp9C("pool_temp9C",HASensorNumber::PrecisionP0);
HASensor temp9D("pool_temp9D");
HASensor tempA3("pool_tempA3");
HASensorNumber tempB0("pool_tempB0",HASensorNumber::PrecisionP0);
HASensor tempB1("pool_tempB1");
HASensor tempD0("pool_tempD0");
HASensorNumber tempD1("pool_tempD1",HASensorNumber::PrecisionP0);
HASensor tempE1("pool_tempE1");
HASensorNumber tempE1O8("pool_tempE1O8",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O9("pool_tempE1O9",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O10("pool_tempE1O10",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O11("pool_tempE1O11",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O12("pool_tempE1O12",HASensorNumber::PrecisionP0);
HASensorNumber tempE1O13("pool_tempE1O13",HASensorNumber::PrecisionP0);

HASensor tempE2("pool_tempE2");
HASensor tempE4("pool_tempE4");
HASensorNumber tempFE("pool_tempFE",HASensorNumber::PrecisionP0);


static String htmltabdecode = "";
static String htmltabpasdecode = "";
static String htmltabpartieldecode = "";
static String htmltabinconnu = "";
static String page = "" ;

void handleRoot(){   // Début de la page HTML
    page = "<!DOCTYPE html>";
    page += "<html lang='fr'>";
    
    page += "<head>";
    page += "    <title>Passerel ESP32 / Electolyseur</title>";
    page += "    <meta http-equiv='refresh' content='60' name='viewport' content='width=device-width, initial-scale=1' charset='UTF-8'/>";
    page += "    <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";  // Utilisation du css 
    page += "</head>";

    page += "<body>";
    page += "    <div class='w3-panel w3-padding-small w3-blue w3-center'>";
    page += "        <p>Décodé</p>";
    page += "    </div>";
    page += "   <table class='w3-table-all'>";
    page += "   <thead><tr class='w3-green'><th scope='col'>id hex</th><th scope='col'>id</th><th scope='col'>Taille</th><th scope='col'>Valeur</th><th scope='col'>Valeur exploité</th><th scope='col'>Description</th><th scope='col'>Status</th></tr>";
    page += "   </thead><tbody>";
    page +=  htmltabdecode;
    page += "   </table>";

    page += "    <div class='w3-panel w3-padding-small w3-blue w3-center'>";
    page += "        <p>Pas décodé</p>";
    page += "    </div>";
    page += "   <table class='w3-table-all'>";
    page += "   <thead><tr class='w3-orange'><th scope='col'>id hex</th><th scope='col'>id</th><th scope='col'>Taille</th><th scope='col'>Valeur</th><th scope='col'>Valeur exploité</th><th scope='col'>Description</th><th scope='col'>Status</th></tr>";
    page += "   </thead><tbody>";
    page +=  htmltabpasdecode;
    page += "   </table>";

    page += "    <div class='w3-panel w3-padding-small w3-blue w3-center'>";
    page += "        <p>Partiellement décodé</p>";
    page += "    </div>";
    page += "   <table class='w3-table-all'>";
    page += "   <thead><tr class='w3-orange'><th scope='col'>id hex</th><th scope='col'>id</th><th scope='col'>Taille</th><th scope='col'>Valeur</th><th scope='col'>Valeur exploité</th><th scope='col'>Description</th><th scope='col'>Status</th></tr>";
    page += "   </thead><tbody>";
    page +=  htmltabpartieldecode;
    page += "   </table>";

    page += "    <div class='w3-panel w3-padding-small w3-blue w3-center'>";
    page += "        <p>Value inconnu</p>";
    page += "    </div>";
    page += "   <table class='w3-table-all'>";
    page += "   <thead><tr class='w3-red'><th scope='col'>id hex</th><th scope='col'>id</th><th scope='col'>Taille</th><th scope='col'>Valeur</th><th scope='col'>Valeur exploité</th><th scope='col'>Description</th><th scope='col'>Status</th></tr>";
    page += "   </thead><tbody>";
    page +=  htmltabinconnu;
    page += "   </table>";


    page += "    <div class='w3-center w3-padding-16'>";
    page += "        <p>Ce serveur est hébergé sur un ESP32</p>";
    page += "        <i>Créé par Richard Durand</i>";
    page += "    </div>";

    page += "</body>";
    page += "</html>";  // Fin de la page HTML

    //webServer.setContentLength(page.length());  // Permet l'affichage plus rapide après chaque clic sur les boutons
    //webServer.send(200, "text/html", page);
    webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send(200, "text/html", page);
    });
}

void cb_handleTelnet() {
  if (telnetServer.hasClient()) {
    if (!telnet || !telnet.connected()) {
      if (telnet) telnet.stop();
      telnet = telnetServer.available();
    } else {
      telnetServer.available().stop();
    }
  }
}

void cb_loopElegantOTA(){
  ElegantOTA.loop();
}


void cb_loopAvaibilityMQTT(){
  mqtt.loop();
  //remove setAvaibility to use native check of Ha integration Shared availability
  //deviceHA.setAvailability(true);

  //savoir si la connexion bluetooth est OK ou si le justsalt n'est pas sous tension.
    bluetoothConnected.setState(connected);
  

}

void cb_loopHaIntegration(){
    //Serial.println("cb_loopHaIntegration");
    taskloopHaIntegration.disable();
    mqtt.loop();
    //remove setAvaibility to use native check of Ha integration Shared availability
    deviceHA.setAvailability(true);


    wifiStrength.setValue(WiFi.RSSI());
    justsaltIp.setValue(WiFi.localIP().toString().c_str());
    Serial.println("----------------------------- debut");
    telnet.println("----------------------------- debut");

    htmltabdecode = "";
    htmltabpasdecode = "";
    htmltabpartieldecode = "";
    htmltabinconnu = "";

  for (size_t i = 0; i < 254; i++)
  {
    if(Electrovaluefull[i].valeurdetrame != NULL ){
     
        std::string str = "Valeur ";
        str += ": ID = " + std::to_string(Electrovaluefull[i].ID);
        str += ": taille = " + std::to_string(Electrovaluefull[i].taille);
        str += ": Value = " + std::string(Electrovaluefull[i].valeurdetrame.c_str());

        Serial.println(str.c_str());
        telnet.println(str.c_str());

        String convertid = String(Electrovaluefull[i].ID,HEX) ;
        if (convertid.length() == 1 ){ 
            convertid = "0" + convertid ;
            }
        convertid.toUpperCase();


// 0: pas decodé ; 1: decodé ; 2 partielement decodé  ; 3: inconnu (non exporter dans Mqtt) 

        switch (Electrovaluefull[i].status){
            case 0:
                htmltabpasdecode += "<tr><th scope=''row''>" + convertid + "</th><th scope=''row''>" + String(Electrovaluefull[i].ID) + "</th><td>" + String(Electrovaluefull[i].taille) +"</td><td>" + String(Electrovaluefull[i].valeurdetrame.c_str()) +"</td><td>" + String(Electrovaluefull[i].valeurconverted.c_str()) +"</td><td>" + String(Electrovaluefull[i].Description) +"</td><td>" + String(Electrovaluefull[i].status) +"</td></tr>";
                break;
            case 1:
                htmltabdecode += "<tr><th scope=''row''>" + convertid + "</th><th scope=''row''>" + String(Electrovaluefull[i].ID) + "</th><td>" + String(Electrovaluefull[i].taille) +"</td><td>" + String(Electrovaluefull[i].valeurdetrame.c_str()) +"</td><td>" + String(Electrovaluefull[i].valeurconverted.c_str()) +"</td><td>" + String(Electrovaluefull[i].Description) +"</td><td>" + String(Electrovaluefull[i].status) +"</td></tr>";
                break;
            case 2:
                htmltabpartieldecode += "<tr><th scope=''row''>" + convertid + "</th><th scope=''row''>" + String(Electrovaluefull[i].ID) + "</th><td>" + String(Electrovaluefull[i].taille) +"</td><td>" + String(Electrovaluefull[i].valeurdetrame.c_str()) +"</td><td>" + String(Electrovaluefull[i].valeurconverted.c_str()) +"</td><td>" + String(Electrovaluefull[i].Description) +"</td><td>" + String(Electrovaluefull[i].status) +"</td></tr>";
                break;
            case 3:
                htmltabinconnu += "<tr><th scope=''row''>" + convertid + "</th><th scope=''row''>" + String(Electrovaluefull[i].ID) + "</th><td>" + String(Electrovaluefull[i].taille) +"</td><td>" + String(Electrovaluefull[i].valeurdetrame.c_str()) +"</td><td>" + String(Electrovaluefull[i].valeurconverted.c_str()) +"</td><td>" + String(Electrovaluefull[i].Description) +"</td><td>" + String(Electrovaluefull[i].status) +"</td></tr>";
                break;
        }
    } 
  }
  htmltabdecode += "</table>";
  htmltabinconnu += "</table>";
  htmltabpartieldecode += "</table>";
  htmltabpasdecode += "</table>";

  Serial.println("----------------------------- Fin");
  telnet.println("----------------------------- Fin");
  handleRoot();

}

void onStateChangedrebootesp (bool state, HASwitch* s){
    if (state == true){
        //lancer le reboot
        state =false;
        //esp_restart();
        //timeScheduler.disable() ;
        esp_restart();       
    }
}


void onStateChangedbluetoothesp (bool state, HASwitch* s){
    if (state == true){
        Serial.println("BT start");
        btStart();    
    } else {
        Serial.println("BT stop");
        btStop();
    }
}

void setupHaIntegration(){
    //HA integration
    //deviceUniqID =WiFi.macAddress();
    deviceHA.setUniqueId(deviceUniqID, sizeof(deviceUniqID));
    
    //deviceHA.setUniqueId(WiFi.macAddress(), sizeof(WiFi.macAddress()));
    
    deviceHA.setName("justsalt");
    deviceHA.setSoftwareVersion(version);
    deviceHA.setModel("Electolyseur");
    deviceHA.setManufacturer("ricky");

    // This method enables availability for all device types registered on the device.
    // For example, if you have 5 sensors on the same device, you can enable
    // shared availability and change availability state of all sensors using
    // single method call "device.setAvailability(false|true)"
    deviceHA.enableSharedAvailability();

    // Optionally, you can enable MQTT LWT feature. If device will lose connection
    // to the broker, all device types related to it will be marked as offline in
    // the Home Assistant Panel.
    deviceHA.enableLastWill();

    rebootesp.setName("Reboot ESP");
    rebootesp.setIcon("mdi:restart");
    rebootesp.onCommand(onStateChangedrebootesp);

    bluetoothesp.setName("Bluetooth ESP");
    bluetoothesp.setIcon("mdi:bluetooth");
    bluetoothesp.onCommand(onStateChangedbluetoothesp);

    wifiStrength.setName("Pool wifi Strength");
    wifiStrength.setDeviceClass("signal_strength");
    wifiStrength.setUnitOfMeasurement("dB");

    justsaltIp.setName("ESP justsalt IP");
    justsaltIp.setIcon("mdi:ip-network");

        // HA integration List of Sensor
    tempeau.setName("Water temp");
    tempeau.setUnitOfMeasurement("°C");
    tempeau.setDeviceClass("temperature");
    tempeau.setIcon("mdi:thermometer");
    
    orp.setName("orp");
    orp.setUnitOfMeasurement("mV");
    orp.setIcon("mdi:flash-triangle-outline");

    ph.setName("PH");
    ph.setIcon("mdi:ph");
    ph.setUnitOfMeasurement("ph");

    sel.setName("Sel");
    sel.setUnitOfMeasurement("g/L");
    sel.setIcon("mdi:water-opacity");

    phconsigne.setName("PH Consigne");
    phconsigne.setIcon("mdi:ph");
    phconsigne.setUnitOfMeasurement("ph");
    phconsigne.setStep(0.1);
    phconsigne.setMin(6.8);
    phconsigne.setMax(7.6);
    phconsigne.onCommand(onValueConsignePhChanged);

    orpconsigne.setName("orp Consigne");
    orpconsigne.setUnitOfMeasurement("mV");
    orpconsigne.setIcon("mdi:flash-triangle-outline");
    orpconsigne.setStep(10);
    orpconsigne.setMin(200);
    orpconsigne.setMax(900);
    orpconsigne.onCommand(onValueConsigneorpChanged);
    
    orpalarme.setName("orp Alarme");
    orpalarme.setUnitOfMeasurement("h");
    orpalarme.setIcon("mdi:alarm");
    orpalarme.setStep(6);
    orpalarme.setMin(12);
    orpalarme.setMax(96);
    orpalarme.onCommand(onValueConsigneorpalarmeChanged);
  
    vol.setName("volume piscine");
    vol.setUnitOfMeasurement("m3");
    vol.setIcon("mdi:cup-water");
    vol.setStep(10);
    vol.setMin(10);
    vol.setMax(200);
    vol.onCommand(onValueConsignevolChanged);

    acide.setName("taux Acide");
    acide.setUnitOfMeasurement("%");
    acide.setIcon("mdi:skull-crossbones-outline");
    acide.setStep(1);
    acide.setMin(5);
    acide.setMax(55);
    acide.onCommand(onValueConsigneacideChanged);
    
    prod.setName("production");
    prod.setUnitOfMeasurement("%");
    prod.setIcon("mdi:cog-outline");
    prod.setStep(1);
    prod.setMin(10);
    prod.setMax(100);
    prod.onCommand(onValueConsigneprodChanged);

    inversion.setName("inversion");
    inversion.setUnitOfMeasurement("h");
    inversion.setIcon("mdi:alarm");
    inversion.setStep(1);
    inversion.setMin(2);
    inversion.setMax(24);
    inversion.onCommand(onValueConsigneinversionChanged);
    
    temp02.setName("temp02");
    temp03.setName("temp03");
    temp08.setName("temp08");
    temp0B.setName("temp0B");
    temp0C.setName("temp0C");
    temp0D.setName("temp0D");
    temp0E.setName("durée total electrolyseur");
    temp0E.setUnitOfMeasurement("h");
    temp0E.setIcon("mdi:timer-sand");

    temp0F.setName("minutes de fonctionement");
    temp0F.setUnitOfMeasurement("Min");
    temp0F.setIcon("mdi:counter");

    temp10.setName("temp10");
    temp12.setName("date etalonage PH?");
    temp12.setIcon("mdi:calendar-star-four-points");

    temp13.setName("date etalonage ORP?");
    temp13.setIcon("mdi:calendar-star-four-points");
    
    temp1F.setName("temp1F");
    temp28.setName("temp28");
    temp29.setName("temp29");
    temp2A.setName("temp2A");
    temp31.setName("temp31");
    temp50.setName("temp50");
    temp51.setName("temp51");
    temp5F.setName("temp5F");
    temp69.setName("temp69");
    temp6A.setName("temp6A");
    temp8F.setName("temp8F");
    temp90.setName("temp90");
    temp91.setName("temp91");
    temp92.setName("temp92");
    temp93.setName("temp93");
    temp93B00.setName("temp93B00");
    temp93B01.setName("temp93B01");
    temp93B02.setName("temp93B02");
    temp93B03.setName("temp93B03");
    temp93B04.setName("temp93B04");
    temp93B05.setName("temp93B05");
    temp93B06.setName("temp93B06");
    temp93B07.setName("temp93B07");
    temp93B10.setName("temp93B10");
    temp93B11.setName("temp93B11");
    temp93B12.setName("temp93B12");
    temp93B13.setName("temp93B13");
    temp93B14.setName("temp93B14");
    temp93B15.setName("temp93B15");
    temp93B16.setName("temp93B16");
    temp93B17.setName("temp93B17");
    temp94.setName("temp94");
    temp95.setName("ID code");
    temp95.setIcon("mdi:barcode");

    temp96.setName("version");
    temp96.setIcon("mdi:qrcode");

    temp97.setName("slave");
    temp97.setIcon("mdi:qrcode-edit");

    temp99.setName("nom");
    temp99.setIcon("mdi:card-account-details-outline");

    temp9A.setName("SN");
    temp9A.setIcon("mdi:barcode");

    temp9B.setName("temp9B");
    temp9C.setName("temp9C");
    temp9D.setName("temp9D");
    tempA3.setName("tempA3");
    tempB0.setName("tempB0");
    tempB1.setName("Mac adr");
    tempB1.setIcon("mdi:network-outline");

    tempD0.setName("tempD0");
    tempD1.setName("TLV");
    tempE1.setName("tempE1");

    tempE1O8.setName("tempE1O8");
    tempE1O9.setName("tempE1O9");
    tempE1O10.setName("tempE1O10");
    tempE1O11.setName("tempE1O11");
    tempE1O12.setName("tempE1O12");
    tempE1O13.setName("tempE1O13");
   
    tempE2.setName("tempE2");
    tempE4.setName("tempE4");
    tempFE.setName("tempFE");

    bluetoothConnected.setName("Bluetooth Status");

  mqtt.begin( BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD );

}

void setup_telnet(){
  telnetServer.begin();
  telnetServer.setNoDelay(true); 

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.localIP());
  Serial.println(" 23' to connect");
  
  timeScheduler.addTask(taskTelnet);
  taskTelnet.enable();
  Serial.println("Add Task telnet handle");
}

void reconnect_wifi(){
   unsigned long currentMillis = millis();
  // if WiFi is down, try reconnecting every CHECK_WIFI_TIME seconds
  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    WiFi.disconnect();
    WiFi.reconnect();
    #ifdef SYSLOG_SERVER
      syslog.log(LOG_INFO, "WIFI lost and reconnect automatically");
    #endif
    previousMillis = currentMillis;
  }
}

void setup_wifi() {
    taskSetup.disable();

    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");

    //WiFi.mode(WIFI_STA);
    Serial.println(ssid);
    Serial.println(password);
    

    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    //Reconnect wifi Task
    timeScheduler.addTask(taskReconnectWifi);
    taskReconnectWifi.enable();
    Serial.print("Add task to monitor and reconnect wifi");
    
    //Elegant OTA
    // webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    // request->send(200, "text/plain", "Hi! I am ESP32 justsalt to update use http://[yourIP]/update.");
    //  });
    handleRoot();
    // webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    //     request->send_P(200, "text/html", index_html);
    // });
    ElegantOTA.begin(&webServer);    // Start ElegantOTA
    webServer.begin();
    Serial.println("HTTP server started");

    //elegant OTA loop
    timeScheduler.addTask(taskloopElegantOTA);
    taskloopElegantOTA.enable();
    Serial.println("Add task for loop of Elegant OTA");

    //telnet setup
    setup_telnet();
    //launch handle telnet
    timeScheduler.addTask(taskTelnet);
    taskTelnet.enable();
    Serial.print("Add task to Handle telnet");

    //setup mqtt
    setupHaIntegration();
    //loop avaibility for mqtt
    timeScheduler.addTask(taskloopAvaibilityMQTT);
    taskloopAvaibilityMQTT.enable();
    telnet.print("Add task for loop of MQTT");
    
    //cb_setupAndScan_ble();
    doScan = true;
    timeScheduler.addTask(taskConnectBleServer);
    taskConnectBleServer.enable();
    
    Serial.print("Add task to Connec Ble server!!!!!!");



}


/**  None of these are required as they will be handled by the library with defaults. **
 **                       Remove as you see fit for your needs                        */
class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) {
        Serial.println("Connected");
        /** After connection we should change the parameters if we don't need fast response times.
         *  These settings are 150ms interval, 0 latency, 450ms timout.
         *  Timeout should be a multiple of the interval, minimum is 100ms.
         *  I find a multiple of 3-5 * the interval works best for quick response/reconnect.
         *  Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
         */
        pClient->updateConnParams(120,120,0,60);
    };

    void onDisconnect(NimBLEClient* pClient) {
        Serial.print(pClient->getPeerAddress().toString().c_str());
        Serial.println(" Disconnected - Starting scan");
        NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
    };

    /** Called when the peripheral requests a change to the connection parameters.
     *  Return true to accept and apply them or false to reject and keep
     *  the currently used parameters. Default will return true.
     */
    bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
        if(params->itvl_min < 24) { /** 1.25ms units */
            return false;
        } else if(params->itvl_max > 40) { /** 1.25ms units */
            return false;
        } else if(params->latency > 2) { /** Number of intervals allowed to skip */
            return false;
        } else if(params->supervision_timeout > 100) { /** 10ms units */
            return false;
        }

        return true;
    };

    /********************* Security handled here **********************
    ****** Note: these are the same return values as defaults ********/
    uint32_t onPassKeyRequest(){
        Serial.println("Client Passkey Request");
        /** return the passkey to send to the server */
        return 123456;
    };

    bool onConfirmPIN(uint32_t pass_key){
        Serial.print("The passkey YES/NO number: ");
        Serial.println(pass_key);
    /** Return false if passkeys don't match. */
        return true;
    };

    /** Pairing process complete, we can check the results in ble_gap_conn_desc */
    void onAuthenticationComplete(ble_gap_conn_desc* desc){
        if(!desc->sec_state.encrypted) {
            Serial.println("Encrypt connection failed - disconnecting");
            /** Find the client with the connection handle provided in desc */
            NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
            return;
        }
    };
};

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    /**
        Called for each advertising BLE server.
    */
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
        std::string str = "------------------------------- \r\n" ;
        str += "BLE Advertised Device found: \r\n";
        str += ": Name = " + std::string(advertisedDevice->getName().c_str());
        str += ", adr = " + std::string(advertisedDevice->getAddress().toString().c_str());
        
        Serial.println(str.c_str());
        telnet.println(str.c_str());

        char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
        Serial.println(manufacturerdata);
        telnet.println(manufacturerdata);

        if ((strcmp(manufacturerdata , "ffff00202020202020202020202020202020202020") == 0) or (strcmp(manufacturerdata , "ffff01202020202020202020202020202020202020") == 0))  {
        // if (advertisedDevice->haveServiceUUID() && advertisedDevice->getServiceUUID().equals(serviceUUID)) {

            BLEDevice::getScan()->stop();
            advDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */
            
            Serial.print("Found our device!  address: ");

            doConnect = true;
            doScan = false;

            // timeScheduler.addTask(taskConnectBleServer);
            taskConnectBleServer.forceNextIteration();
            Serial.println("add Task Connect Ble server");

        } // Found our server
    } // onResult
}; // MyAdvertisedDeviceCallbacks



/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
    std::string str = (isNotify == true) ? "Notification" : "Indication";
    str += " from ";
    /** NimBLEAddress and NimBLEUUID have std::string operators */
    str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
    str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
    str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
    str += ", Countertrame = " + std::to_string(countertrame);
    str += ", Value = " + std::string((char*)pData, length);
    countertrame++;
    if (countertrame > 65000){
        countertrame=5;
        countertrameold=0;
    } 
    Serial.println(str.c_str());
    telnet.println(str.c_str());

    Serial.print("recep chaine taille =");
    Serial.println(length);

    telnet.println("recep chaine");
    //telnet.println(std::to_string(length));
    if (length > 5){
        int index =3;
        while (index +2 < length) {
            uint8_t idvaleur = pData[index];
            int taillevaleur = pData[index + 1 ];
            String Valuetrame = ""; 
            for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                String converttrame = String(pData[i],HEX) ;
                if (converttrame.length() == 1 ){ 
                    converttrame = "0" + converttrame ;
                    }
                Valuetrame += converttrame;
                if (i != index + 1 + taillevaleur){
                    Valuetrame += "." ; 
                }
            }
            Valuetrame.toUpperCase();

            // Serial.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            // telnet.printf( "Values ID : %ld - taille : %ld \r\n", idvaleur, taillevaleur );
            std::string str = "Trame " + std::to_string(countertrame);
            str += ": Values ID  = " + std::to_string(idvaleur);
            str += ", taille = " + std::to_string(taillevaleur);
            str += ", Value = " + std::string(Valuetrame.c_str());

            Serial.println(str.c_str());
            telnet.println(str.c_str());

            Electrovaluefull[idvaleur].ID=idvaleur;
            Electrovaluefull[idvaleur].taille=taillevaleur;
            Electrovaluefull[idvaleur].valeurdetrame=Valuetrame.c_str();
            Electrovaluefull[idvaleur].Description="";
            Electrovaluefull[idvaleur].status=0;

            switch (idvaleur) {
            case 0x00: { 

                }break;
        
            case 0x01: {
                uint8_t phhex = pData[index + 2];
                float phf = static_cast<float>(phhex)/10;
                ph.setValue(phf);
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(phf)).c_str();
                Electrovaluefull[idvaleur].Description="PH"; 
                Electrovaluefull[idvaleur].status=1;

                }break;

            case 0x02: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp02.setValue(temp);
    
                }break;

            case 0x03: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp03.setValue(temp);
                }break;

            case 0x06: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index + 3]));
                //float temp = static_cast<float>(temphex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(temphex)).c_str();
                Electrovaluefull[idvaleur].Description="ORP"; 
                orp.setValue(temphex);;
                }break;

            case 0x08: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp08.setValue(temp);
                }break;

            case 0x09: {
                uint16_t tempeauhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float tempeauf = static_cast<float>(tempeauhex)/10;
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(tempeauf)).c_str();
                Electrovaluefull[idvaleur].Description="temperature eau"; 
                tempeau.setValue(tempeauf);
                }break;
            
            case 0x0A: {
                uint8_t selhex = pData[index + 2] ;
                float self = static_cast<float>(selhex)/10;
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(self)).c_str();
                Electrovaluefull[idvaleur].Description="taux de sel"; 
                sel.setValue(self);
                }break;

            case 0x0B: {
                if(countertry == 20) {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp0B.setValue(temp);
                }
                }break;
            
            case 0x0C: {
                if(countertry == 20) {
                    uint8_t temphex = pData[index + 2];
                    float temp = static_cast<float>(temphex);
                    temp0C.setValue(temp);
                }
                }break;

            case 0x0D: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp0D.setValue(temp);
                }break;

            case 0x0E: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(temp)).c_str();
                Electrovaluefull[idvaleur].Description="Compteur Heure total de fonctionnement"; 
                temp0E.setValue(temp);
                }break;

            case 0x0F: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(temp)).c_str();
                Electrovaluefull[idvaleur].Description="Minutes de fonctionnement"; 
                temp0F.setValue(temp);
                }break;
            
            case 0x10: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp10.setValue(temp);
                }break;

            case 0x11: {
                uint16_t volhex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float volf = static_cast<float>(volhex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(volf)).c_str();
                Electrovaluefull[idvaleur].Description="Volume de la piscine"; 
                vol.setState(volf);
                }break;
            
            case 0x12: {
                uint8_t dayphex = (pData[index + 3]);
                uint8_t monthphex = (pData[index + 4]);
                uint8_t yearphex = (pData[index +5]);
                int dayf = static_cast<int>(dayphex);
                int monthf = static_cast<int>(monthphex);
                int yearf = static_cast<int>(yearphex);

                std::string datestr =  std::to_string(dayf) + "/" + std::to_string(monthf) + "/20" + std::to_string(yearf);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (datestr.c_str());
                Electrovaluefull[idvaleur].Description="Date etalonage PH"; 
                temp12.setValue(datestr.c_str()) ;
                
                }break;

            case 0x13: {
                uint8_t dayphex = (pData[index + 3]);
                uint8_t monthphex = (pData[index + 4]);
                uint8_t yearphex = (pData[index +5]);
                int dayf = static_cast<float>(dayphex);
                int monthf = static_cast<float>(monthphex);
                int yearf = static_cast<float>(yearphex);
                std::string datestr =  std::to_string(dayf) + "/" + std::to_string(monthf) + "/20" + std::to_string(yearf);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (datestr.c_str());
                Electrovaluefull[idvaleur].Description="Date etalonage ORP"; 
                temp13.setValue(datestr.c_str()) ;
                }break;

            case 0x1F: {
                uint8_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp1F.setValue(temp);
                }break;
            
            case 0x28: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp28.setValue(temp);
                
                }break;
            
            case 0x29: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp29.setValue(temp);
                }break;
            
            case 0x2A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp2A.setValue(temp);
                }break;
            
            case 0x30: {
                uint8_t phchex = pData[index + 2] ;
                float phcf = static_cast<float>(phchex)/10;
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(phcf)).c_str();
                Electrovaluefull[idvaleur].Description="Consigne PH"; 
                phconsigne.setState(phcf);
                }break;
            
            case 0x31: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp31.setValue(temp);
                }break;         

            case 0x32: {
                uint8_t acidehex = pData[index + 2] ;
                float acidef = static_cast<float>(acidehex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(acidef)).c_str();
                Electrovaluefull[idvaleur].Description="taux acide"; 
                acide.setState(acidef);
                }break;
            
            case 0x33: {
                uint8_t prodhex = pData[index + 2] ;
                float prodf = static_cast<float>(prodhex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(prodf)).c_str();
                Electrovaluefull[idvaleur].Description="production"; 
                prod.setState(prodf);
                }break;

            case 0x35: {
                uint8_t orpchex = pData[index + 2] ;
                float orpcf = static_cast<float>(orpchex)*10;
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(orpcf)).c_str();
                Electrovaluefull[idvaleur].Description="Consigne ORP"; 
                orpconsigne.setState(orpcf);
                }break;
            
            case 0x37: {
                uint8_t alarmeorphex = pData[index + 2] ;
                float alarmeorpf = static_cast<float>(alarmeorphex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(alarmeorpf)).c_str();
                Electrovaluefull[idvaleur].Description="seuil Alarme ORP"; 
                orpalarme.setState(alarmeorpf);
                }break;
    
            case 0x39: {
                uint8_t inversionhex = pData[index + 2] ;
                float inversionvalf = static_cast<float>(inversionhex);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(inversionvalf)).c_str();
                Electrovaluefull[idvaleur].Description="Inversion"; 
                inversion.setState(inversionvalf);
                }break;

            case 0x50: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp50.setValue(temp);
                }break;

            case 0x51: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp51.setValue(temp);
                }break;

            case 0x5F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp5F.setValue(temp);
                }break;
            
            case 0x69: {
                uint32_t temphex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float temp = static_cast<float>(temphex);
                temp69.setValue(temp);
                }break;

            case 0x6A: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp6A.setValue(temp);
                }break;

            case 0x8F: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp8F.setValue(temp);
                }break;
                
            case 0x90: {
                temp90.setValue(Valuetrame.c_str());
                }break;

            case 0x91: {
                temp91.setValue(Valuetrame.c_str());
                }break;
            
            case 0x92: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                temp92.setValue(temp);
                }break;

            case 0x93: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp93.setValue(temp);
                temp93B00.setCurrentState((bool)((temphex & 0x0001) ));
                temp93B01.setCurrentState((bool)((temphex & 0x0002) ));
                temp93B02.setCurrentState((bool)((temphex & 0x0004) ));
                temp93B03.setCurrentState((bool)((temphex & 0x0008) ));
                temp93B04.setCurrentState((bool)((temphex & 0x0010) ));
                temp93B05.setCurrentState((bool)((temphex & 0x0020) ));
                temp93B06.setCurrentState((bool)((temphex & 0x0040) ));
                temp93B07.setCurrentState((bool)((temphex & 0x0080) ));
                temp93B10.setCurrentState((bool)((temphex & 0x0100) ));
                temp93B11.setCurrentState((bool)((temphex & 0x0200) ));
                temp93B12.setCurrentState((bool)((temphex & 0x0400) ));
                temp93B13.setCurrentState((bool)((temphex & 0x0800) ));
                temp93B14.setCurrentState((bool)((temphex & 0x1000) ));
                temp93B15.setCurrentState((bool)((temphex & 0x2000) ));
                temp93B16.setCurrentState((bool)((temphex & 0x4000) ));
                temp93B17.setCurrentState((bool)((temphex & 0x8000) ));
                }break;

            case 0x94: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp94.setValue(temp);
                }break;

            case 0x95: {
                uint32_t idcodehex = (pData[index + 2]<< 24) + (pData[index + 3]<< 16)+ (pData[index + 4]<< 8) + (pData[index +5]);
                float idcodefloat = static_cast<float>(idcodehex);
                Electrovaluefull[idvaleur].status=2;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(idcodefloat)).c_str();
                Electrovaluefull[idvaleur].Description="ID Code"; 
                temp95.setValue(idcodefloat);
                
                }break;
            
            case 0x96: {
                temp96.setValue(Valuetrame.c_str());
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = Valuetrame.c_str();
                Electrovaluefull[idvaleur].Description="version"; 
                }break;
            
            case 0x97: {
                temp97.setValue(Valuetrame.c_str());
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = Valuetrame.c_str();
                Electrovaluefull[idvaleur].Description="Slave (version hardware)";
                }break;

            case 0x99: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp99.setValue(tempstring.c_str());
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = tempstring.c_str();
                Electrovaluefull[idvaleur].Description="Nom";
                
                }break;

            case 0x9A: {
                std::string tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += pData[i];
                }
                temp9A.setValue(tempstring.c_str());
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = tempstring.c_str();
                Electrovaluefull[idvaleur].Description="SN";
                }break;

            case 0x9B: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp9B.setValue(temp);
                }break;

            case 0x9C: {
                uint16_t temphex = ((pData[index + 2]<< 8) + (pData[index +3]));
                float temp = static_cast<float>(temphex);
                temp9C.setValue(temp);
                }break;

            case 0x9D: {
                temp9D.setValue(Valuetrame.c_str());
                }break;
            
            case 0xA3: {
                tempA3.setValue(Valuetrame.c_str());
                }break;
            
            case 0xB0: {
                if(countertry == 20) {

                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                tempB0.setValue(temp);
                }
                
                }break;
                
    
            case 0xB1: {
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    tempstring += String(pData[i],HEX) ;
                    if (i != index + 1 + taillevaleur){
                        tempstring += ":" ; 
                    }
                }
                tempstring.toUpperCase();
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = tempstring.c_str();
                Electrovaluefull[idvaleur].Description="Mac adress";
                tempB1.setValue(tempstring.c_str());
                }break;
            
            case 0xD0: {
            //     //D0.06.30.00.00.00.00.00
                String tempstring = ""; 
                for ( int i = index + 2; i < index + 2 + taillevaleur ; i++ ) {
                    String convert = String(pData[i],HEX) ;
                    if (convert.length() == 1 ){ 
                        convert = "0" + convert ;
                        }
                    tempstring += convert;
                    if (i != index + 1 + taillevaleur){
                        tempstring += "." ; 
                    }
                }
                tempstring.toUpperCase();
                tempD0.setValue(tempstring.c_str());
                }break;

            case 0xD1: {
                uint8_t temphex = (pData[index + 2]) ;
                float temp = static_cast<float>(temphex);
                tempD1.setValue(temp);
                Electrovaluefull[idvaleur].status=1;
                Electrovaluefull[idvaleur].valeurconverted = (std::to_string(temp)).c_str();
                Electrovaluefull[idvaleur].Description="TLV";
                }break;
            
            

            case 0xE1: {
                //  E1.0F.18.04.11.11.00.33.00.00.88.00.F8.00.00.00.00
                tempE1.setValue(Valuetrame.c_str());
                tempE1O8.setValue(static_cast<int>((pData[index +2 +8])) );
                tempE1O9.setValue(static_cast<int>((pData[index +2 +9])) );
                tempE1O10.setValue(static_cast<int>((pData[index +2 +10])) );
                tempE1O11.setValue(static_cast<int>((pData[index +2 +11])) );
                tempE1O12.setValue(static_cast<int>((pData[index +2 +12]) ));
                tempE1O13.setValue(static_cast<int>((pData[index +2 +13]) ));
                }break;
                
            case 0xE2: {
                // E2.0F.18.04.16.0A.22.10.00.00.00.41.3E.DC.00.00.00
                tempE2.setValue(Valuetrame.c_str());
                }break;

            case 0xE4: {
                // E4.0F.18.04.12.15.00.00.46.00.0B.13.00.00.00.00.00
                tempE4.setValue(Valuetrame.c_str());
                }break;
    
            case 0xFE: {
                uint8_t temphex = pData[index + 2];
                float temp = static_cast<float>(temphex);
                tempFE.setValue(temp);
                }break;
    
            default: {
                Serial.print( "idvaleur Non implementee " );
                Serial.println(idvaleur);
                Electrovaluefull[idvaleur].status=3;
                };
            }
            countertry ++;
            if (countertry == 21) {
                countertry = 0;
            }
            index = index + 2 + taillevaleur ;

        }
        timeScheduler.addTask(taskloopHaIntegration);
        taskloopHaIntegration.enable();
        //Serial.println("fin traitement trame");
       
    }
}

/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
    Serial.println("Scan Ended");
}


/** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB;

bool connectToServer() {
  Serial.print("Forming a connection to ");
  Serial.println(advDevice->toString().c_str());

  BLEClient*  pClient  = BLEDevice::createClient();
  Serial.println(" - Created client");

  // Connect to the remove BLE Server.
  pClient->connect(advDevice); 
  Serial.println(" - Connected to server");

  // Obtain a reference to the service we are after in the remote BLE server.
  BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
  if (pRemoteService == nullptr) {
    Serial.print("Failed to find our service UUID: ");
    Serial.println(serviceUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our service");


  // Obtain a reference to the characteristic in the service of the remote BLE server.
  pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
  if (pRemoteCharacteristic == nullptr) {
    Serial.print("Failed to find our characteristic UUID: ");
    Serial.println(charUUID.toString().c_str());
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found our characteristic");

  // Read the value of the characteristic.
  if(pRemoteCharacteristic->canRead()) {
    std::string value = pRemoteCharacteristic->readValue();
    Serial.print("The characteristic value was: ");
    Serial.println(value.c_str());
  }

  //il faut s'abonner la la charactisitic "indication" afin de recevoir les notification de publication
  if(pRemoteCharacteristic->canIndicate())
    pRemoteCharacteristic->subscribe(false, notifyCB);

    pRemoteCharWrite = pRemoteService->getCharacteristic(charUUIDwrite);
    if (pRemoteCharWrite == nullptr) {
        Serial.print("Failed to find our characteristic UUID: ");
        Serial.println(charUUIDwrite.toString().c_str());
        pClient->disconnect();
        return false;
    } else {
        Serial.println("caracteristic Write - Found our characteristic");
    }


  connected = true;
  return true;
  
}

void cb_setupAndScan_ble() {
    taskBleSetupAndScan.disable();
    Serial.println("Starting NimBLE Client");
    /** Initialize NimBLE, no device name spcified as we are not advertising */
    NimBLEDevice::init("");
    /** Set the IO capabilities of the device, each option will trigger a different pairing method.
     *  BLE_HS_IO_KEYBOARD_ONLY    - Passkey pairing
     *  BLE_HS_IO_DISPLAY_YESNO   - Numeric comparison pairing
     *  BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
     */
    //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
    NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison

    /** 2 different ways to set security - both calls achieve the same result.
     *  no bonding, no man in the middle protection, secure connections.
     *
     *  These are the default values, only shown here for demonstration.
     */
    NimBLEDevice::setSecurityAuth(true, true, true);
    NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);

    /** Optional: set the transmit power, default is 3db */
    #ifdef ESP_PLATFORM
        NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
    #else
        NimBLEDevice::setPower(9); /** +9db */
    #endif

    /** Optional: set any devices you don't want to get advertisments from */
    // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));

    /** create new scan */
    NimBLEScan* pScan = NimBLEDevice::getScan();
     Serial.println("avant callback");
    /** create a callback that gets called when advertisers are found */
    pScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

    /** Set scan interval (how often) and window (how long) in milliseconds */
    pScan->setInterval(45);
    pScan->setWindow(15);

    /** Active scan will gather scan response data from advertisers
     *  but will use more energy from both devices
     */
    Serial.println("avant set active scan");
    pScan->setActiveScan(true);
    /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
     *  Optional callback for when scanning stops.
     */
    //pScan->start(scanTime, scanEndedCB);
    pScan->start(10, false);
    Serial.println("Fin de cb_setupAndScan_ble");
    
}

void onValueConsignePhChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of PH Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(48,numberFloat * 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}
void onValueConsigneorpChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(53,numberFloat / 10,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
  
}

void onValueConsigneorpalarmeChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Alarme ORP Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x37,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsignevolChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Volume Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x11,numberFloat ,2);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneacideChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Acide Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); 
        onwriteble(0x32,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneprodChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Production Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str()); ;
        onwriteble(0x33,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}

void onValueConsigneinversionChanged( HANumeric number, HANumber* sender){
  if (!number.isSet()) {
        // the reset command was send by Home Assistant
    } else {
        float numberFloat = number.toFloat();
        std::string str = "Value of Version Consigne changed: ";
        str += ": New valeur = " + std::to_string(numberFloat);

        Serial.println(str.c_str());
        telnet.println(str.c_str());        
        onwriteble(0x39,numberFloat ,1);
    }
    sender->setState(number); // report the selected option back to the HA panel
}
void onwriteble (int ValueID ,float numberset,int ValueSizea){
    Serial.println("debut onwriteble : ");
    static uint8_t trame[80] = {0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    //union float2bytes { float f; char b[sizeof(float)]; } ;
    typedef union _data {
        int f;
        char  s[2];
    } myData;

    //float2bytes myvalue;
    myData myvalue;
    myvalue.f =  static_cast<int>(numberset);
    trame[3]= ValueID ;
    trame[4]= ValueSizea;
    switch (ValueSizea){
    case 1:
        //uint8_t hexnumberset = (numberset) & 0xff;
        trame[5]= myvalue.s[0];
        break;

    case 2:
        trame[5]= 0x00;
        trame[6]= myvalue.s[0];
        break;
    
    default:
        break;
    }
    //Serial.println(sizeof(trame));
    std::string str = "send des valeur ";
    str += ": Number a set = " + std::to_string(numberset);
    str += ": Myvalue f = " + std::to_string(myvalue.f);
    str += ": Myvalue s 0 = " + std::to_string(myvalue.s[0]);
    str += ": Myvalue s 1 = " + std::to_string(myvalue.s[1]);
    str += ": value ID = " + std::to_string(trame[3]);
    str += ", taille = " + std::to_string(trame[4]);
    str += ", Value ch 5= " + std::to_string(trame[5]);
    str += ", Value ch 6= " + std::to_string(trame[6]);

    Serial.println(str.c_str());
    telnet.println(str.c_str());
    
    bool writeResponse;
    writeResponse = pRemoteCharWrite->writeValue(trame, 80, true);
    Serial.println(writeResponse);
    str = "onwriteble  : ";
    str += "send trame result = " + std::to_string(writeResponse);
    if (writeResponse == 1) {
        str += " OK ";
    }else {
        str += " KO ";
    }
    Serial.println(str.c_str());
    telnet.println(str.c_str());
}



void setup (){
    Serial.begin(115200);
    // Wifi Manager & wait?

    timeScheduler.init();
    timeScheduler.addTask(taskSetup);
    taskSetup.enable();
    Serial.println("add Task to setup justsalt");
    //bluetoothesp.setState(true);
}


void loop (){
    timeScheduler.execute();
    
}


void cb_connectBleServer(){
  //connection au serveur Ble
  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are
  // connected we set the connected flag to be true.
    Serial.println("debut cb_connectBleServer");
   
    if (connected == true ){
        Serial.println("connected = true");

        // check si on est connecté BLE (evolution des trames notifié ) 
        if (countertrame != countertrameold){
            //Serial.println("recepion notification ok donc conecté ");
            std::string str = "recepion notification ok donc conecté ";
            str += ": countertrame  = " + std::to_string(countertrame);
            str += ", countertrameold = " + std::to_string(countertrameold);
            
            Serial.println(str.c_str());
            telnet.println(str.c_str());

            countertrameold=countertrame;
            delay(50);
        } else {
            Serial.println("recepion notification KO donc liaison BT HS ");
            connected = false;
            doConnect = true;
        } 
        Serial.println("apres check connection");
        
    }else{
        if (doConnect == true) {
            Serial.println("doConnect is true");
            if (connectToServer()) {
                Serial.println("We are now connected to the BLE Server.");
                //write sur la prochaine itération de la task
                taskConnectBleServer.forceNextIteration();
            } else {
                Serial.println("We have failed to connect to the server; there is nothin more we will do.");
                doScan = true;
            }
            doConnect = false;
            Serial.println("doConnect ********************* to false ");
            //on relance un scan
        }
        if (doScan == true){
            Serial.println( "add Task to Scan Ble devices");
            timeScheduler.addTask(taskBleSetupAndScan);
            taskBleSetupAndScan.enable();
        }
        Serial.println("connected = false");
    } 
}