Bienvenue, Visiteur
Vous devez vous enregistrer avant de pouvoir poster.

Nom d’utilisateur
  

Mot de passe
  





Rechercher dans les forums

(Recherche avancée)

Derniers sujets
V17
Dernier message : Sgb31
Il y a 7 minutes
Pas de remontée de donnée...
Dernier message : mulraf
Il y a 7 minutes
parametrage boxsfr ipv6
Dernier message : jph64
Il y a 13 minutes
V17 - Modification ON/OFF...
Dernier message : Mike
Il y a 15 minutes
Communication du JSY ou i...
Dernier message : grostoto
Il y a 32 minutes
UI2 sans wifi.
Dernier message : grostoto
Il y a 36 minutes
Valeurs de page d'accueil...
Dernier message : Letus
Il y a 38 minutes
Sortie de la version V17
Dernier message : grostoto
Il y a 45 minutes
Reset ESP32 à 6h02
Dernier message : Sgb31
Il y a 1 heure
Analyseur de TIC
Dernier message : pvrout
Il y a 2 heures

Statistiques du Forum
» Membres : 2,442,   » Dernier membre : mulraf,   » Sujets du forum : 1,991,   » Messages du forum : 17,999,  
Statistiques complètes

  Chargeur simple vehicule électrique
Posté par : ghoudart - 24-01-2026, 08:15 PM - Forum : Evolutions faites, à faire, dont vous rêvez... - Réponses (4)

J'ai installé le routeur solaire qui marche parfaitement pour chauffer le ballon d'eau chaude avec le surplus solaire depuis plus d'un an. Merci André !

Je souhaiterai savoir si je peux utiliser le relais 2 en mode ON/OFF pour charger mon véhicule électrique sur une prise domestique classique. 

J'ai bien conscience que ce serait un dispositif très simplifié: pas de variation de la charge, charge bloquée à 10A et 2200W. 

Mais je souhaiterais avoir confirmation que c'est possible et que le on/off est compatible avec une charge de vehicule electrique sur prise domestique (je me dis que oui puisque ca peut piloter une pompe de piscine).

merci bcp

Imprimer cet élément

  RMS et Shelly Pro 3EM 3CT 63 ?
Posté par : Rakibou - 24-01-2026, 08:06 PM - Forum : Routeur Photovoltaïque - Pas de réponse

Bonjour,

Je m'apprête à installer un RMS F1ATB chez un ami.
C'est une installation PV triphasée.

Le linky étant trop loin, et compte tenu de l'absence de place dans le tableau, on envisage d'installer un
Shelly Pro 3EM 3CT 63 sous le disjoncteur d'abonné (seul endroit où on peut installer le bloc 3CT).
https://www.shelly.com/fr/products/shelly-pro-3em-3ct63

Quelqu'un a-t-il déjà utilisé ce modèle avec le RMS ?

Merci d'avance de vos infos et de vos avis.

Imprimer cet élément

  lektrico
Posté par : Dann - 24-01-2026, 06:31 PM - Forum : Routeur Photovoltaïque - Réponses (2)

Bonjour j'ai un souci avec l'appli de chez lektrico mon shelly em ne s'intègre pas avez vous eu ce problème
merci pour vos retours

Imprimer cet élément

  Forçage MQTT via Jeedom et jmqtt (RÉSOLU)
Posté par : LuciusTerror - 24-01-2026, 02:41 AM - Forum : Routeur Photovoltaïque - Réponses (3)

Hello,

---------------------------------------------------------------------------
RÉSOLU : https://f1atb.fr/forum_f1atb/thread-2189...l#pid19349
---------------------------------------------------------------------------

Ca fait 5h que j'essaie de commander le forçage du triac F1ATB en v16.10 via une requête MQTT depuis jmqtt dans jeedom.
J'ai épongé des 10aines de topic sur le forum F1ATB et même sur celui de la commu jeedom ...
En désespoir j'ai même fait appel à GPT .... mais il m'a soulé, il est 1h30 passé du mat' et ses réponses changeantes m'agacent lol.


Voici ma config routeur :

   

Préfixe = f1atb
Device Name = routeur

Forçage MQTT = activé

   

Nom action = TRIAC


Et voici ma config jmqtt :

   

Equipement : Topic = f1atb/#

   

Commande : Topic = routeur/TRIAC
value = {"tOnOff":30}


Quand je teste, je vois vois bien le message passer sur MQTTX :
   

Mais rien ne se passe dans l'IHM du routeur, dans la page d'accueil au niveau de la zone de forçage....

Si vous pouvez m'aider et me dire ou j'ai bugé ?
J'ai des des 10aines de combinaisons différentes ....

Merci.

Imprimer cet élément

  Module JSY-MK-194G
Posté par : jeanmi58 - 23-01-2026, 01:06 PM - Forum : Routeur Photovoltaïque - Réponses (4)

Bonjour

Je ne sais pas si le sujet a déjà été évoquer mais voila ma constatation.
Sur ce type de module il faut croiser TX/RX : Le fil TX du module doit impérativement aller sur GPIO RX de l'ESP32, et le fil RX du module sur un GPIOl TX   de l'ESP32.
Exemple pour GPIO 16 et 17
TXGPIO 16 (RX2)Transmission vers Réception
RXGPIO 17 (TX2)Réception depuis Transmission

Sinon module parfait connectique sur bornier.

   

Imprimer cet élément

  Modification Graphe Puissances
Posté par : Lolo69 - 23-01-2026, 10:18 AM - Forum : Evolutions faites, à faire, dont vous rêvez... - Réponses (4)

Pour observer le comportement du régulateur de puissance sur le RMS il est utile de pouvoir superposer la courbe Sortie ( Triac ou SSR) sur la courbe de puissance, sur un periode de 10 min plutot que quelques secondes.
j'ai donc mis cette courbe de sortie à la place de la puissance VA ( qui ne sert pas à grand chose) sur le premier graphe de la page d'accueil.
La problematique etant la différence d'echelle entre la puissance qui s'auto ajuste et la sortie qui est limitée entre 0 et 100%
J'ai donc mis deux echelles différentes pour les deux courbes , la courbe Sortie regulateur entre 0 et 100 qui est aussi possible de rendre auto ajustable en cochant la case auto pour "zoomer" si besoin
Le resultat ici
https://f1atb.fr/forum_f1atb/attachment.php?aid=4934

Pour ceux qui le souhaite je decris ci dessous ce qu'il faut faire sur la base d'une version 16.09 , par contre je ne peux pas vous mettre vraiement les numeros de lignes car j'ai fait d'autres modifications mais il vous suffira de faire les recherches avec les mots clés que je donne.


1) rajouter la variable globale pidout quelques part dans le fichier Solar_Router_V16_09.ino

Code :
int pidout=0;


2) dans le fichier JS_Accueil.h remplacer entierement la function Plot(SVG, Tab, couleur1... par celle ci-dessous. ne vous inquietez pas elle reste compatible par les différents appels car tout ce que j'ai rajouté est en optionnel
Code PHP :
function Plot(SVGTabcouleur1titre1couleur2titre2echelleAutoY2memeEchelle) {
  var Vmax 0;
  var Vmin 0;
  var Vmax2 0// Max pour la deuxième courbe
  var Vmin2 0// Min pour la deuxième courbe
  var TabY0 = [];
  var TabY1 = [];
  couleur1 "#" couleur1;
  couleur2 "#" couleur2;
  var dX 900 Tab.length;
  const = new Date();
  var dI 1;
  var label 'heure';
  var pixelTic 72;
  var dTextTic 4;
  var moduloText 24;
  var H0 d.getHours() + d.getMinutes() / 60;
  var H00 Math.floor(H0 4);
  var X0 18 * (H00 H0);
  var Y0 250;
  var Yamp 230;
  var dy 2;
  var dispVA false;
  var echellePID 100// Échelle fixe pour le PID par défaut
  
  
// Si echelleAutoY2 n'est pas défini, on utilise false par défaut (échelle fixe 0-100)
  if (echelleAutoY2 === undefined) {
    echelleAutoY2 false;
  }
  
  
// Si memeEchelle n'est pas défini, on utilise false par défaut
  if (memeEchelle === undefined) {
    memeEchelle false;
  }
  
  
switch (SVG) {
    case 'SVG_PW48hM':
      break;
    case 'SVG_PW48hT':
      break;
    case 'SVG_Temp48h':
      Y0 450;
      Yamp 430;
      dy 1;
      break;
    case 'SVG_PW2sM':
      label 'mn';
      pixelTic 90;
      X0 0;
      dTextTic 1;
      moduloText = -100;
      H00 0;
      dI 2//2 courbes PW et PVA
      GID(SVG '_L').style 'color:' couleur2 ';display:block;';
      dispVA GID(SVG '_C').checked//Plot courbe VA
      localStorage.setItem(SVG '_LS'dispVA);
      break;
    case 'SVG_PW2sT':
      label 'mn';
      pixelTic 90;
      X0 0;
      dTextTic 1;
      moduloText = -100;
      H00 0;
      dI 2//2 courbes PW et PVA
      GID(SVG '_L').style 'color:' couleur2 ';display:block;';
      dispVA GID(SVG '_C').checked//Plot courbe VA
      localStorage.setItem(SVG '_LS'dispVA);
      break;
    case 'SVG_Wh1an':
      label 'Mois';
      pixelTic dX 30.4375;//Mois moyen
      var dTextTic 1;
      moduloText 12;
      H00 d.getMonth();
      X0 dX * (d.getDate());
      var Mois = ['Jan''Fev''Mars''Avril''Mai''Juin''Juil''Août''Sept''Oct''Nov''Dec'];
      break;
  }
  
  
// Calcul du max pour la courbe principale (indices pairs)
  for (var 0Tab.lengthi++) {
    if (== || dI == 1) {
      Tab[i] = Math.min(Tab[i], 10000000);
      Tab[i] = Math.max(Tab[i], -10000000);
      Vmax Math.max(Math.abs(Tab[i]), Vmax);
    }
  }
  
  
// Si même échelle, calculer le max des DEUX courbes ensemble
  if (dI == && memeEchelle && dispVA) {
    for (var 1Tab.length2) {
      Tab[i] = Math.min(Tab[i], 10000000);
      Tab[i] = Math.max(Tab[i], -10000000);
      Vmax Math.max(Math.abs(Tab[i]), Vmax); // Utilise Vmax pour les deux courbes
    }
  }
  
  
// Calcul du max pour la deuxième courbe (indices impairs) si échelle auto demandée et pas même échelle
  if (dI == && echelleAutoY2 && !memeEchelle && dispVA) {
    for (var 1Tab.length2) {
      Tab[i] = Math.min(Tab[i], 10000000);
      Tab[i] = Math.max(Tab[i], -10000000);
      Vmax2 Math.max(Math.abs(Tab[i]), Vmax2);
    }
  }
  
  
var cadrageMax 1;
  var cadrage1 1000000;
  var cadrage2 = [1085421];
  for (var 07m++) {
    for (var 0cadrage2.lengthi++) {
      var cadrage1 cadrage2[i];
      if ((Vmax) <= XcadrageMax X;
    }
    cadrage1 cadrage1 10;
  }
  
  
// Calcul du cadrage pour la deuxième courbe
  var cadrageMax2 100// Par défaut échelle fixe 0-100
  
  
if (memeEchelle) {
    // Si même échelle demandée, utiliser la même que la courbe 1
    cadrageMax2 cadrageMax;
    echellePID cadrageMax;
  } else if (echelleAutoY2) {
    // Si échelle auto demandée (et pas même échelle)
    cadrageMax2 1;
    cadrage1 1000000;
    for (var 07m++) {
      for (var 0cadrage2.lengthi++) {
        var cadrage1 cadrage2[i];
        if ((Vmax2) <= XcadrageMax2 X;
      }
      cadrage1 cadrage1 10;
    }
    echellePID cadrageMax2// Utilise l'échelle calculée
  }

  var c1 '"' couleur1 '"';
  var c2 '"' couleur2 '"';
  var cT "#" Koul[Coul_Graphe][1];
  var style 'background:linear-gradient( #' Koul[Coul_Graphe][5] + ',#' Koul[Coul_Graphe][3] + ',#' Koul[Coul_Graphe][5] + ');border-color:#' Koul[Coul_Tab][5] + ';';
  var "<svg viewbox='0 0 1030 500' style='" style "' height='500' width='100%' id='S_" SVG "' onmousemove ='DispVal(this,event);' >";
  
  
// Axe vertical gauche (Puissance Active)
  S += "<line x1='100' y1='20' x2='100' y2='480' style='stroke:" cT ";stroke-width:2' />";
  S += "<line x1='100' y1='" Y0 "' x2='1000' y2='" Y0 "' style='stroke:" cT ";stroke-width:2' />";

  // Graduations horizontales (temps)
  for (var 1000 X0100pixelTic) {
    var x;
    var Y2 Y0 6;
    S += "<line x1='" "' y1='" Y0 "' x2='" "' y2='" Y2 "' style='stroke:" cT ";stroke-width:2' />";
    X 8;
    Y2 Y0 22;
    if (SVG == 'SVG_Wh1an') {
      X 8;
      S += "<text x='" "' y='" Y2 "' style='font-size:16px;fill:" cT ";'>" Mois[H00] + "</text>";
    } else {
      S += "<text x='" "' y='" Y2 "' style='font-size:16px;fill:" cT ";'>" H00 "</text>";
    }
    H00 = (H00 dTextTic moduloText) % moduloText;
  }
  Y2 Y0 3;
  S += "<text x='980' y='" Y2 "' style='font-size:14px;fill:" cT ";'>" label "</text>";
  
  
// Graduations verticales gauche (Puissance Active)
  for (var = -10<= 10dy) {
    Y2 Y0 Yamp 10;
    if (Y2 <= 480) {
      S += "<line x1='100' y1='" Y2 "' x2='1000' y2='" Y2 "' style='stroke:" cT ";stroke-width:1;stroke-dasharray:2 10;' />";
      Y2 Y2 7;
      var cadrageMax 10T.toString();
      var 90 T.length;
      S += "<text x='" "' y='" Y2 "' style='font-size:16px;fill:" cT ";'>" "</text>";
    }
  }
  
  
// Axe vertical pour PID (à côté de l'axe principal) seulement si pas même échelle
  if (dI == && Pva_valide && dispVA && !memeEchelle) {
    S += "<line x1='150' y1='20' x2='150' y2='480' style='stroke:" couleur2 ";stroke-width:2;stroke-dasharray:5 5;' />";
    
    
// Graduations verticales pour le PID
    for (var 0<= 10dy) {
      Y2 Y0 Yamp 10;
      if (Y2 <= 480) {
        var echellePID 10;
        T T.toString();
        S += "<text x='155' y='" + (Y2 7) + "' style='font-size:14px;fill:" couleur2 ";'>" "</text>";
      }
    }
  }
  
  
// Courbe PID
  if (dI == && Pva_valide && dispVA) {
    S += "<text x='450' y='40' style='font-size:18px;fill:" couleur2 ";'>" titre2 "</text>";
    S += "<polyline points='";
    var 0;
    for (var 1Tab.lengthdI) {
      var valeurPID Tab[i];
      if (!echelleAutoY2 && !memeEchelle) {
        // Échelle fixe : limiter entre 0 et 100
        valeurPID Math.max(0Math.min(100valeurPID));
      }
      var Y0 Yamp valeurPID echellePID;
      var 100 dX i;
      S += "," " ";
      TabY1[j] = parseFloat(Tab[i]);
      j++;
    }
    S += "' style='fill:none;stroke:" couleur2 ";stroke-width:2' />";
  }
  
  
// Courbe Puissance Active (avec échelle automatique)
  S += "<text x='450' y='18' style='font-size:18px;fill:" couleur1 ";'>" titre1 "</text>";
  S += "<polyline points='";
  var 0;
  for (var 0Tab.lengthdI) {
    var Y0 Yamp Tab[i] / cadrageMax;
    var 100 dX i;
    S += "," " ";
    TabY0[j] = parseFloat(Tab[i]);
    j++;
  }
  S += "' style='fill:none;stroke:" couleur1 ";stroke-width:2' />";
  S += "</svg>";
  
  GID
(SVG).innerHTML S;
  TabVal["S_" SVG] = [TabY0TabY1];
  TabCoul["S_" SVG] = [couleur1couleur2];


3) dans le fichier Server.ino remplacer la VA par pidout , pour simplifier les explication remplacer la void handleAjaxData() par celle ci-dessous
Code :
{ // Données page d'accueil
    String DateLast = "Attente d'une mise à l'heure par internet";
    if (Horloge == 1)
      DateLast = "Attente d'une mise à l'heure par le Linky";
    if (ModeReseau == 0 && WiFi.getMode() != WIFI_STA)
      DateLast = "Sélectionnez un réseau <a href='/Wifi'>Wifi</a>";
    if (Horloge > 1 && Horloge < 5)
      DateLast = "Attente d'une mise à l'heure  <a href='/Heure' >manuellement</a> ";
    if (Horloge == 5)
      DateLast = "Attente d'une mise à l'heure un ESP externe (maître)";
    if (HeureValide)
    {
      DateLast = DATE;
    }
    int pva_temps_reel =  pidout;  // LBE
    String S = LesTemperatures();
    S = "Deb" + RS + DateLast + RS + Source_data + RS + LTARF + RS + STGEt + RS + S + RS + String(Pva_valide);
    // LBE remplacée par la ligne suivante S += GS + String(PuissanceS_M) + RS + String(PuissanceI_M) + RS + String(PVAS_M) + RS + String(PVAI_M);
    S += GS + String(PuissanceS_M) + RS + String(PuissanceI_M) + RS + String(pva_temps_reel) + RS + "0";
    S += RS + String(EnergieJour_M_Soutiree) + RS + String(EnergieJour_M_Injectee) + RS + String(Energie_M_Soutiree) + RS + String(Energie_M_Injectee);
    if (Source_data == "UxIx2" || ((Source_data == "ShellyEm" || Source_data == "ShellyPro") && EnphaseSerial.toInt() != 3))
    { // UxIx2 ou Shelly monophasé avec 2 sondes
      S += GS + String(PuissanceS_T) + RS + String(PuissanceI_T) + RS + String(PVAS_T) + RS + String(PVAI_T);
      S += RS + String(EnergieJour_T_Soutiree) + RS + String(EnergieJour_T_Injectee) + RS + String(Energie_T_Soutiree) + RS + String(Energie_T_Injectee);
    }
    S += GS + "Fin\r";
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", S);
}

4) retour sur  dans le fichier Solar_Router_V16_09.ino pour préremplir les tableau avec pidout à la place de la VA
rechercher les lignes 
if (tps - previousTimer2sMillis > 2000) {
      unsigned long dt = tps - previousTimer2sMillis;
      previousTimer2sMillis += 2000;  //Pou caler exactement à 2s
      tabPw_Maison_2s[IdxStock2s] = PuissanceS_M - PuissanceI_M;
      tabPw_Triac_2s[IdxStock2s] = PuissanceS_T - PuissanceI_T;

et juste apres modifier la ligne
      tabPva_Maison_2s[IdxStock2s] = PVAS_M - PVAI_M;
par
      tabPva_Maison_2s[IdxStock2s] = pidout;//PVAS_M - PVAI_M; //LBE

5) et pour finir toujour dans le fichier  Solar_Router_V16_09
chercher la ligne 
  Retard[i] = round(RetardF[i]);           //Valeure entiere pour piloter le Triac et les relais

et juste apres rajouter
pidout=1*(100-Retard[i]);

Voilà ca fait pas mal de petits endroits à modifier mais qui ne sera pas un obstacle vule niveau de compétence croisé ici 

Je vous proposerai prochainement de modifier l'algorithme PID d 'André pour le rendre beaucop plus réactif lors des changements brusques sans le passer en autooscillation. Le probleme du PID d'André est que pour anticiper il faudrait mettre de l action Dérivée, mais pour éviter les sauts brusques de la Dérivée provoqués par l’échantillonnage, André a été obligé de mettre un filtrage "fort" de cette action ce qui introduit un retard, et donc anticiper avec du retard ca fonctionne pas top.
Sans dérivée le régulateur ne bouge pas sa sortie tant que l erreur ne change pas de signe ( intégrale et proportionnel) ce qui conduit à de forts dépassements lors des changement de production ( Nuages) ou de consommation ( arrêt marche d'un gros consommateur) , je vous expliquerai le moment venu le principe du nouvel algorithme mis en place par la technique de l anti-windup avec back-calculation , qui permet de desaturer l intégrale avant le changement de signe de l erreur

Imprimer cet élément

  Nouveauté
Posté par : tupolev89 - 22-01-2026, 04:01 PM - Forum : Routeur Photovoltaïque - Réponses (1)

Bonjour, pour les développeurs avez vous entendu parler de cette nouveauté?

https://www.igen.fr/domotique/2026/01/le...261-154409

Cordialement 
Tupolev

Imprimer cet élément

Question Vérification configuration
Posté par : Jean31 - 22-01-2026, 01:32 PM - Forum : Evolutions faites, à faire, dont vous rêvez... - Réponses (20)

Bonjour,

Un ami, indisponible actuellement, m'a installé un routeur pour optimiser la consommation de mon chauffe eau.
Pas d'anomalies de chauffe sur cet été mais l'hiver arrivant, il m'est arrivé de manquer d'eau chaude à la douche du matin.

J'ai donc décidé de forcer manuellement la chauffe pendant les heures creuses (00:00 à 08:00) pendant 2 heures. C'est concluant mais pas simple à piloter en raison de l'heure tardive de déclenchement.

Le seuil a été réglé initialement à -50 W, dois-je le laisser tel quel ? La réactivité est à 80.
J'ai une installation photovoltaïque 3 KW et un chauffe eau de 1800 W

Je suis allé  sur la chaine youtube, j'ai fait les modifs suivantes
- Réactivité passée à 20. J'ai pas bien compris les effets de ce paramètre
- J'ai inséré une condition on de 04:00 à 06:00 pour éviter les désagréments rencontrés.

Quelqu'un peut-il valider ces modifications ? Je ne suis pas très à l'aise avec toutes ces notions électriques.

Merci par avance



Pièces jointes Miniature(s)
       
Imprimer cet élément

  Modification page action
Posté par : 59jag - 21-01-2026, 11:42 PM - Forum : Evolutions faites, à faire, dont vous rêvez... - Réponses (13)

J ai fait quelques modifications de la page action.
Car sur smartphone tres difficile de ne pas modifier les plage horaire qui sont trop sensible.

le bin

https://drive.google.com/file/d/1aADt1Ao...p=drivesdk

dans le binaire il y a aussi la possibilite de lancer  des actions avec des interrupteur ou poussoir branche sur gpio

la modif de js action

Code :
const char * ActionsJS1 = R"====(
var LesActions = [];
var mouseClick = false;
var blockEvent = false;
var draggedHandle = null;    
var LesTemperatures = [];
var NomTemperatures = [];
var ListeActions = [];
var SelectActions = "";
var LTARFbin = 0;
var pTriac = 0;
var ReacCACSI = 1;
var PlotIdx = -1;
var TabIdx = 0;
var MesuresPwAct = [];
var Ecart = [];
var Ouvert = [];
var Retard = [];
var Prop = [];
var Integ = [];
var Deriv = [];
var Pause = false;
var IS = "|";
var BordsInverse = [".Bactions"];
function Init() {
    LoadActions();
    LoadCouleurs();
    ShowAction();
}
function creerAction(aActif, aTitre, aHost, aPort, aOrdreOn, aOrdreOff, aRepet, aTempo, aKp, aKi, aKd, aPID, aPeriodes) {
    if (aTitre == "") aTitre = "Titre";
    var S = {
        Actif: aActif,
        Titre: aTitre,
        Host: aHost,
        Port: aPort,
        OrdreOn: aOrdreOn,
        OrdreOff: aOrdreOff,
        Repet: aRepet,
        Tempo: aTempo,
        Kp: aKp,
        Ki: aKi,
        Kd: aKd,
        PID: aPID,
        Periodes: aPeriodes
    }
    return S;
}
function TracePlanning(iAct) {
    var Radio0 = "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-0' onclick='checkDisabled();'>Inactif</div>";
    var Radio1 = "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-1'  onclick='checkDisabled();'>Découpe sinus</div>";
    if (iAct > 0) { Radio1 = "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-1'  onclick='checkDisabled();'>On/Off</div>"; }
    Radio1 += "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-5'  onclick='checkDisabled();'>Demi-sinus</div>";
    Radio1 += "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-2'  onclick='checkDisabled();'>Multi-sinus</div>";
    Radio1 += "<div ><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-3'  onclick='checkDisabled();'>Train de sinus</div>";
    Radio1 += "<div id='Pwm" + iAct + "'><input type='radio' name='modeactif" + iAct + "' id='radio" + iAct + "-4'  onclick='checkDisabled();'>PWM</div>";
    var SelectPin = "<div>Gpio <select id='selectPin" + iAct + "' onchange='checkDisabled();'  title='Choix broche (GPIO) de commande'>";
    for (var i = 0; i < Pins.length; i++) {
        var v = "gpio:" + Pins[i];
        if (Pins[i] == 0) v = "";
        if (Pins[i] == -1) v = "Externe";
        SelectPin += "<option value=" + Pins[i] + ">" + v + "</option>";
    }
    SelectPin += "</select></div>";
    var SelectOut = "<div id='SelectOut" + iAct + "'>Sortie 'On' <select id='selectOut" + iAct + "'  title='Dépend du relais. En général +3.3V'><option value=0>0V</option><option value=1 selected>3.3V</option></select></div>";

    var S = "<div class='titre'><span id ='titre" + iAct + "'  onclick='editTitre(" + iAct + ")' title='Donnez un nom'>Titre</span></div>";
    S += "<div class='visu' onclick='Plot(" + iAct + ")' id='visu" + iAct + "' title='Zoom sur la régulation en temps réel. Les réglages peuvent être modifiés. Ne pas oublier de sauvegarder'>?</div>";
    S += "<div  id='mode' ><div class='TitZone' title='Choix du mode de découpe du secteur'>Mode</div>" + Radio0 + Radio1 + "</div>";
    S += "<div id='blocPlanning" + iAct + "' >";
    S += "<div class='les_select' id='sortie" + iAct + "'>";
    S += "<div class='TitZone' title='Définir la broche (GPIO) ou commande' >Sortie</div>" + SelectPin + SelectOut;
    S += "<div><span id='Tempo" + iAct + "'>Temporisation(s) <input type='number' class='tm' id='tempo" + iAct + "'  title='Temporisation entre chaque changement d&apos;état pour éviter des oscillations quand un appareil dans la maison consomme en dents de scie (Ex: un four).'></span></div>";
    S += "</div><div class='les_select' id='ligne_bas" + iAct + "'><div class='TitZone'>Externe</div>";
    S += "<div><span id='Host" + iAct + "'>Host<br><input type='text' id='host" + iAct + "'  onchange='checkDisabled();' title='Adresse IP machine sur réseau LAN, nom de domaine ou rien.'></span></div>";
    S += "<div><span id='Port" + iAct + "'>Port<br><input type='number' class='tm' id='port" + iAct + "'  title='Port d&apos;acc&egrave;s via le protocole http , uniquement pour machine distante. En g&eacute;n&eacute;ral <b>80</b>.'></span></div>";
    S += "<div><span id='ordreon" + iAct + "'>Ordre On<br><input type='text' id='ordreOn" + iAct + "'  title='Ordre à passer à la machine distante.'></span></div>";
    S += "<div><span id='ordreoff" + iAct + "'>Ordre Off<br><input type='text' id='ordreOff" + iAct + "' ></span></div>";
    S += "<div><span id='Repet" + iAct + "'>Répétition(s)<br><input type='number' id='repet" + iAct + "' class='tm'  title='P&eacute;riode en s de r&eacute;p&eacute;tition/rafra&icirc;chissement de la commande. Uniquement pour les commandes vers l&apos;extérieur. 0= pas de répétition.'></span></div>";
    S += "</div>";

    S += "<div  class='bouton_curseur' ><div class='boutons'><input id='adds' type='button' value='-' class='tbut'  onclick='AddSub(-1," + iAct + ")'  title='Retrait d&apos;une p&eacute;riode horaire.'>";
    S += "<input id='adds' type='button' value='+' class='tbut' onclick='AddSub(1," + iAct + ")' title='Ajout d&apos;une p&eacute;riode horaire.'></div>";
    S += "<div class='slideTriac' id='fen_slide" + iAct + "'>";
    S += "<div class='slideTriacIn' id='Propor" + iAct + "'>";
    S += "<div class='Tcell1'>Coef. Proportionnel</div>";
    S += "<div class='Tcell2'><input type='range' min='0' max='100' value='50' title='Correction proportionnelle à l&apos;écart en W' id='sliderKp" + iAct + "' style='width:100%;max-width:none;' oninput=\"GH('sensiKp" + iAct + "',Math.floor(this.value));UpdateK(" + iAct + ");\"  ></div>";
    S += "<div class='Tcell3'><strong id='sensiKp" + iAct + "'></strong></div>";
    S += "</div>";
    S += "<div class='slideTriacIn'>";
    S += "<div class='Tcell1'>Coef. Intégral ou R&eacute;activit&eacute; </div>";
    S += "<div class='Tcell2'><input type='range' min='0' max='100' value='50' title='Correction suivant l&apos;intégrale de l&apos;écart en W' id='sliderKi" + iAct + "'  style='width:100%;max-width:none;' oninput=\"GH('sensiKi" + iAct + "',Math.floor(this.value));UpdateK(" + iAct + ");\"  ></div>";
    S += "<div class='Tcell3'><strong id='sensiKi" + iAct + "'></strong></div>";
    S += "</div>";
    S += "<div class='slideTriacIn' title='Correction suivant la derivée de l&apos;écart en W' id='Derive" + iAct + "'>";
    S += "<div class='Tcell1'>Coef. Dérivé</div>";
    S += "<div class='Tcell2'><input type='range' min='0' max='100' value='50' id='sliderKd" + iAct + "' style='width:100%;max-width:none;' oninput=\"GH('sensiKd" + iAct + "',Math.floor(this.value));UpdateK(" + iAct + ");\"  ></div>";
    S += "<div class='Tcell3'><strong id='sensiKd" + iAct + "'></strong></div>";
    S += "</div>";
    S += "</div>";
    S += "<div id='PIDbox" + iAct + "'><label> PID On</label><input type='checkbox' id='PID" + iAct + "' onclick='checkDisabled();'></div>";
    S += "</div>";
    S += "<did id='graphAction" + iAct + "' class='graphAction'><div id='graphSVG" + iAct + "' class='graphSVG'></div><div class='GraphSVG' onclick='Pause=!Pause;'>&#x23EF;</div></div>";
    S += "<div style='margin:4px;'>";
    S += "<div id='infoAction" + iAct + "' class='infoAction'></div>";
    S += "<div id='curseurs" + iAct + "' class='curseur' onmousemove='dragHandle(event," + iAct + ");' onmouseup='stopDrag();' ontouchmove='dragHandle(event," + iAct + ");' ontouchend='stopDrag();'></div>";
    S += "</div>";
    S += "</div>";

    GH("planning" + iAct, S);
    GID("radio" + iAct + "-" + LesActions[iAct].Actif).checked = true;
    GH("titre" + iAct, LesActions[iAct].Titre);
    GV("host" + iAct, LesActions[iAct].Host);
    GV("port" + iAct, LesActions[iAct].Port);
    GV("ordreOn" + iAct, LesActions[iAct].OrdreOn);
    GV("ordreOff" + iAct, LesActions[iAct].OrdreOff);
    GV("repet" + iAct, LesActions[iAct].Repet);
    GV("tempo" + iAct, LesActions[iAct].Tempo);
    GV("sliderKp" + iAct, LesActions[iAct].Kp);
    GH("sensiKp" + iAct, LesActions[iAct].Kp);
    GV("sliderKi" + iAct, LesActions[iAct].Ki);
    GH("sensiKi" + iAct, LesActions[iAct].Ki);
    GV("sliderKd" + iAct, LesActions[iAct].Kd);
    GH("sensiKd" + iAct, LesActions[iAct].Kd);
    GID("PID" + iAct).checked = LesActions[iAct].PID == 1 ? true : false;
    if (LesActions[iAct].OrdreOn.indexOf(IS) > 0) {
        var vals = LesActions[iAct].OrdreOn.split(IS);
        GID("selectPin" + iAct).value = vals[0];
        GID("selectOut" + iAct).value = vals[1];
    } else {
        GID("selectPin" + iAct).value = -1;
        GID("selectOut" + iAct).value = 1;
        if (LesActions[iAct].OrdreOn == "") GID("selectPin" + iAct).value = 0;
    }
    TracePeriodes(iAct);

}


function TracePeriodes(iAct) {
    var S = "";
    var Sinfo = "";
    var SinfoClick = "";
    var left = 0;
    var H0 = 0;
    var colors = ["#666", "#66f", "#f66", "#6f6", "#cc4"]; //NO,OFF,ON,PW,Triac
    blockEvent = false;
    for (var i = 0; i < LesActions[iAct].Periodes.length; i++) {
        var w = (LesActions[iAct].Periodes[i].Hfin - H0) / 24;
        left = H0 / 24;
        H0 = LesActions[iAct].Periodes[i].Hfin;
        var Type = LesActions[iAct].Periodes[i].Type;
        var color = colors[Type];
        var temperature = "";
        if (LesActions[iAct].Periodes[i].CanalTemp >= 0) {
            if (LesTemperatures[LesActions[iAct].Periodes[i].CanalTemp] > -100) { // La sonde de température fonctionne         
                var Tsup = LesActions[iAct].Periodes[i].Tsup;
                if (Tsup >= 0 && Tsup <= 1000) temperature += "<div>T &ge;" + Tsup / 10 + "°</div>";
                var Tinf = LesActions[iAct].Periodes[i].Tinf;
                if (Tinf >= 0 && Tinf <= 1000) temperature += "<div>T &le;" + Tinf / 10 + "°</div>";
            }
        }
        var H_Ouvert = "";
        if (LesActions[iAct].Periodes[i].SelAct != 255) {
            if (LesActions[iAct].Periodes[i].Hmin > 0) H_Ouvert += "<div>H<span class='fsize8'>ouverture</span> &ge;" + Hdeci2Hmn(LesActions[iAct].Periodes[i].Hmin) + "</div>";
            if (LesActions[iAct].Periodes[i].Hmax > 0) H_Ouvert += "<div>H<span class='fsize8'>ouverture</span> &le;" + Hdeci2Hmn(LesActions[iAct].Periodes[i].Hmax) + "</div>";
            if (LesActions[iAct].Periodes[i].Ooff > 0) H_Ouvert += "<div>On à Off si &le;" + LesActions[iAct].Periodes[i].Ooff + "%</div>";
            if (LesActions[iAct].Periodes[i].O_on > 0) H_Ouvert += "<div>Off à On si &ge;" + LesActions[iAct].Periodes[i].O_on + "%</div>";
        }
        var TxtTarif = "";
        if (LTARFbin > 0) {
            TxtTarif = "Tarif : ";
            var Tarif_ = LesActions[iAct].Periodes[i].Tarif;
            if (LTARFbin <= 3) {
                TxtTarif += (Tarif_ & 1) ? "<span style='color:red;'>H. Pleine</span>" : "";
                TxtTarif += (Tarif_ & 2) ? "<span style='color:green;'> H. Creuse</span>" : "";
            } else {
                TxtTarif += (Tarif_ & 4) ? "Tempo<span style='color:blue;'>Bleu</span>" : "";
                TxtTarif += (Tarif_ & 8) ? "<span style='color:white;'> Blanc</span>" : "";
                TxtTarif += (Tarif_ & 16) ? "<span style='color:red;'> Rouge</span>" : "";
            }
            TxtTarif = "<div>" + TxtTarif + "</div>";
        }
        let TexteMinMax = "";
        var condition = (temperature != "" || H_Ouvert != "" || TxtTarif != "") ? "<div>Condition(s) :</div>" + temperature + H_Ouvert + TxtTarif : "";
        if (LesActions[iAct].Actif <= 1 && iAct > 0) {
            LesActions[iAct].Periodes[i].Vmax = Math.max(LesActions[iAct].Periodes[i].Vmin, LesActions[iAct].Periodes[i].Vmax);
            TexteMinMax = "<div>Off si Pw&gt;" + LesActions[iAct].Periodes[i].Vmax + "W</div><div>On si Pw&lt;" + LesActions[iAct].Periodes[i].Vmin + "W</div>" + condition;
        } else {
            LesActions[iAct].Periodes[i].Vmax = Math.max(0, LesActions[iAct].Periodes[i].Vmax);
            LesActions[iAct].Periodes[i].Vmax = Math.min(100, LesActions[iAct].Periodes[i].Vmax);
            TexteMinMax = "<div>Seuil Pw : " + LesActions[iAct].Periodes[i].Vmin + "W</div>" + "<div>Ouvre Max : " + LesActions[iAct].Periodes[i].Vmax + "%</div>" + condition;
        }
        var TexteTriac = "<div>Seuil Pw : " + LesActions[iAct].Periodes[i].Vmin + "W</div>" + "<div>Ouvre Max : " + LesActions[iAct].Periodes[i].Vmax + "%</div>" + condition;
        var paras = ["Pas de contr&ocirc;le", "OFF", "<div>ON</div>" + condition, TexteMinMax, TexteTriac];
        var para = paras[Type];
       
        // Créer la période
        S += "<div id='zone" + iAct + "_" + i + "' class='periode' data-idx='" + i + "' style='width:" + w + "%;left:" + left + "%;background-color:" + color + ";'>";
       
        // Ajouter une poignée de déplacement uniquement si ce n'est pas la dernière période
        if (i < LesActions[iAct].Periodes.length - 1) {
            var handleStyle = "position:absolute;right:-15px;top:50%;transform:translateY(-50%);width:30px;height:60px;";
            handleStyle += "background-color:rgba(255,255,255,0.8);border:2px solid #333;border-radius:8px;";
            handleStyle += "cursor:ew-resize;display:flex;align-items:center;justify-content:center;";
            handleStyle += "font-size:14px;font-weight:bold;color:#333;z-index:10;";
            handleStyle += "box-shadow:0 2px 5px rgba(0,0,0,0.3);touch-action:none;user-select:none;";
           
            S += "<div class='handle' style='" + handleStyle + "' data-action='" + iAct + "' data-periode='" + i + "' ";
            S += "onmousedown='startDrag(this,event," + iAct + "," + i + ");' ";
            S += "ontouchstart='startDrag(this,event," + iAct + "," + i + ");'>|||</div>";
        }
       
        S += "</div>";
       
        Hmn = Hdeci2Hmn(H0);
        fs = Math.max(8, Math.min(16, w / 2)) + "px";
        Sinfo += "<div class='infoZone' style='width:" + w + "%;border-color:" + color + ";font-size:" + fs + "'  onclick='infoZclicK(" + i + "," + iAct + ")'  >"
        Sinfo += "<div class='Hfin'>" + Hmn + "</div>" + para + "</div>";
        SinfoClick += "<div id='info" + iAct + "Z" + i + "' class='infoZ' ></div>";
    }
    GH("curseurs" + iAct, S);
    GH("infoAction" + iAct, SinfoClick + Sinfo);
}

)====";

const char * ActionsJS2 = R"====(
   
function startDrag(handleElement, ev, iAct, iPeriode) {
   if (ev.preventDefault) ev.preventDefault();
    if (ev.stopPropagation) ev.stopPropagation();
     draggedHandle = {
        action: iAct,
        periode: iPeriode,
        element: handleElement,
        originalStyle: handleElement.style.cssText
    };
    var dragStyle = "position:absolute;right:-15px;top:50%;transform:translateY(-50%) scale(1.2);width:30px;height:60px;";
    dragStyle += "background-color:#ffeb3b;border:2px solid #f57c00;border-radius:8px;";
    dragStyle += "cursor:ew-resize;display:flex;align-items:center;justify-content:center;";
    dragStyle += "font-size:14px;font-weight:bold;color:#333;z-index:10;";
    dragStyle += "box-shadow:0 4px 8px rgba(0,0,0,0.5);touch-action:none;user-select:none;";
    handleElement.style.cssText = dragStyle;
}

function dragHandle(ev, iAct) {
    if (!draggedHandle || draggedHandle.action !== iAct) return;
   
    if (ev.preventDefault) ev.preventDefault();
   
    var curseur = GID('curseurs' + iAct);
    var leftPos;
   
      if (ev.touches && ev.touches.length > 0) {
        leftPos = ev.touches[0].clientX - curseur.getBoundingClientRect().left;
    } else {
        leftPos = ev.clientX - curseur.getBoundingClientRect().left;
    }
   var width = curseur.getBoundingClientRect().width;
    var HeureMouse = leftPos * 2420 / width;
    var iPeriode = draggedHandle.periode;
   
    var NewHfin = Math.max(0, Math.min(HeureMouse, 2400));
   
  if (iPeriode < LesActions[iAct].Periodes.length - 1) {
        NewHfin = Math.min(NewHfin, LesActions[iAct].Periodes[iPeriode + 1].Hfin);
    }
    if (iPeriode > 0) {
        NewHfin = Math.max(NewHfin, LesActions[iAct].Periodes[iPeriode - 1].Hfin);
    }
   
   LesActions[iAct].Periodes[iPeriode].Hfin = Math.floor(NewHfin);
   var H0 = (iPeriode > 0) ? LesActions[iAct].Periodes[iPeriode - 1].Hfin : 0;
    var left = H0 / 24;
    var w = (NewHfin - H0) / 24;
   
   var zone = GID('zone' + iAct + '_' + iPeriode);
    if (zone) {
        zone.style.width = w + '%';
    }
   
     if (iPeriode < LesActions[iAct].Periodes.length - 1) {
        var nextZone = GID('zone' + iAct + '_' + (iPeriode + 1));
        var nextLeft = NewHfin / 24;
        var nextW = (LesActions[iAct].Periodes[iPeriode + 1].Hfin - NewHfin) / 24;
        if (nextZone) {
            nextZone.style.left = nextLeft + '%';
            nextZone.style.width = nextW + '%';
        }
    }
}

function stopDrag() {
    if (draggedHandle && draggedHandle.element) {
      draggedHandle.element.style.cssText = draggedHandle.originalStyle;
       
      var iAct = draggedHandle.action;
        TracePeriodes(iAct);
    }
    draggedHandle = null;
}

function AddSub(v, iAct) {
    if (v == 1) {
        if (LesActions[iAct].Periodes.length < 8) {
            LesActions[iAct].Periodes.push({
                Hfin: 2400,
                Type: 1,
                Vmin: 0,
                Vmax: 100,
                Tinf: 1600,
                Tsup: 1600,
                Hmin: 0,
                Hmax: 0,
                CanalTemp: -1,
                SelAct: 255,
                Ooff: 0,
                O_on: 0,
                Tarif: 31
            }); //Tarif codé en bits
            var Hbas = 0;
            if (LesActions[iAct].Periodes.length > 2) {
                Hbas = parseInt(LesActions[iAct].Periodes[LesActions[iAct].Periodes.length - 3].Hfin);
            }
            if (LesActions[iAct].Periodes.length > 1) {
                LesActions[iAct].Periodes[LesActions[iAct].Periodes.length - 2].Hfin = Math.floor((Hbas + 2400) / 2);
            }
        }
    } else {
        if (LesActions[iAct].Periodes.length > 1) {
            LesActions[iAct].Periodes.pop();
            if (LesActions[iAct].Periodes.length > 0)
                LesActions[iAct].Periodes[LesActions[iAct].Periodes.length - 1].Hfin = 2400;
        }
    }
    TracePeriodes(iAct);

}
function infoZclicK(i, iAct) {
    var capteurT = false;
    if (!blockEvent) {
        blockEvent = true;
        var Type = LesActions[iAct].Periodes[i].Type;
        var idZ = "info" + iAct + "Z" + i;
        var S = "<div class='selectZ'> S&eacute;lection Action<div class='closeZ' onclick='infoZclose(\"" + idZ + "\")'>X</div></div>";
        var check = (Type == 1) ? "checked" : "";
        S += "<div class='zOff'  ><div class='radioC' ><input type='radio'  name='R" + idZ + "' onclick='selectZ(1," + i + "," + iAct + ");' " + check + " title='Off forcé'>OFF</div></div>";
        S += "<div class='fcontainer'><div class='fcontleft'>";
        check = (Type == 2) ? "checked" : "";
        S += "<div  class='zOn'   ><div class='radioC' ><input type='radio'  name='R" + idZ + "' onclick='selectZ(2," + i + "," + iAct + ");' " + check + " 'title='On forcé (si conditions optionnelles valides)'>ON <small>100%</small></div></div>";
        check = (Type > 2) ? "checked" : "";
        var Vmin = LesActions[iAct].Periodes[i].Vmin;
        var Vmax = LesActions[iAct].Periodes[i].Vmax;
        var Tinf = LesActions[iAct].Periodes[i].Tinf;
        var Tsup = LesActions[iAct].Periodes[i].Tsup;
        var TinfC = Tinf / 10;
        var TsupC = Tsup / 10;
        var Hmin = (LesActions[iAct].Periodes[i].Hmin > 0) ? Hdeci2Hmn(LesActions[iAct].Periodes[i].Hmin) : "";
        var Hmax = (LesActions[iAct].Periodes[i].Hmax > 0) ? Hdeci2Hmn(LesActions[iAct].Periodes[i].Hmax) : "";
        var Ooff = (LesActions[iAct].Periodes[i].Ooff > 0) ? LesActions[iAct].Periodes[i].Ooff : "";
        var O_on = (LesActions[iAct].Periodes[i].O_on > 0) ? LesActions[iAct].Periodes[i].O_on : "";
        if (Tinf > 1500 || Tinf < -500) TinfC = ""; //Temperature entre -50 et 150° représenté en dixième
        if (Tsup > 1500 || Tsup < -500) TsupC = ""; //Temperature entre -50 et 150
        if (iAct > 0) {
            var Routage = ["", "Routage ON/Off", "Routage Multi-sinus", "Routage Train de Sinus", "PWM", "Routage Demi-Sinus"];
            S += "<div class='zPw' ><div class='radioC' ><input type='radio'  name='R" + idZ + "' onclick='selectZ(3," + i + "," + iAct + ");' " + check + ">" + Routage[LesActions[iAct].Actif] + "</div>";
            if (LesActions[iAct].Actif <= 1) {
                S += "<div><small>On : &nbsp;</small>Pw &lt;<input id='Pw_min_" + idZ + "'  type='number' value='" + Vmin + "' onchange='NewVal(this)' title='Seuil de puissance pour activer ou désactiver le routage. Attention, en cas de mode On/Off la diff&eacute;rence, seuil sup&eacute;rieur moins  seuil inf&eacute;rieur doit &ecirc;tre sup&eacute;rieure &agrave; la consommation du dipositif pour &eacute;viter l&apos;oscillation du relais de commande.'>W</div>";
                S += "<div><small>Off : </small>Pw &gt;<input id='Pw_max_" + idZ + "'  type='number' value='" + Vmax + "' onchange='NewVal(this)'>W</div>";
                S += "<div><small>Puissance active en entrée de maison</small></div></div>";
            } else {
                S += "<div><small>Seuil Pw : &nbsp;</small><input id='Pw_min_" + idZ + "'  type='number' value='" + Vmin + "' onchange='NewVal(this)' title='Seuil de puissance pour activer ou désactiver le routage.' >W</div>";
                S += "<div><small>Puissance active en entrée de maison</small></div>";
                S += "<div><small>Ouvre Max : </small><input id='Pw_max_" + idZ + "'   type='number' value='" + Vmax + "' onchange='NewVal(this)' title='Ouverture maximum du  SSR. Valeur typique : 100%'>%</div></div>";
            }

        } else {
            var Routage = ["", "Routage Découpe Sinus", "Routage Multi-sinus", "Routage Train de Sinus", "", "Routage Demi-Sinus"];
            S += "<div  class='zTriac' ><div class='radioC' ><input type='radio'  name='R" + idZ + "' onclick='selectZ(4," + i + "," + iAct + ");' " + check + ">" + Routage[LesActions[iAct].Actif] + "</div>";
            S += "<div>Seuil Pw &nbsp;<input id='Pw_min_" + idZ + "'  type='number' value='" + Vmin + "' onchange='NewVal(this)' title='Seuil en W de r&eacute;gulation par le Triac de la puissance mesur&eacute;e Pw en entrée de la maison. Valeur typique : 0.'>W</div>";
            S += "<div><small>Puissance active en entrée de maison</small></div>";
            S += "<div>Ouvre Max <input id='Pw_max_" + idZ + "' type='number' value='" + Vmax + "' onchange='NewVal(this)' title='Ouverture maximum du triac. Valeur typique : 100%'>%</div></div>";
        }
        S += "</div>";
        var SelectT = "<div>Canal de Température <select id='CanalTemp" + idZ + "'  onchange='NewVal(this)'><option value=-1 selected>Non exploité</option>";
        for (var c = 0; c < 4; c++) {
            if (LesTemperatures[c] > -100) {
                var Temper = parseFloat(LesTemperatures[c]).toFixed(1);
                SelectT += "<option value=" + c + " >" + NomTemperatures[c] + " (" + Temper + "°)" + "</option>";
                capteurT = true;
            }
        }
        SelectT += "</select></div>";
        var style = (ModePara == 0) ? "none" : "block";
        style = "style='display:" + style + "';";
        S += "<div>";
        S += "<div class='TitZone' " + style + ">&nbsp;&nbsp;&nbsp;Conditions optionnelles pour activer</div>";
        if (capteurT) {
            S += "<div  class='bord1px' " + style + ">";
            S += SelectT;
            S += "<div class='minmax'><div>T &ge;<input id='T_sup_" + idZ + "'  type='number' value='" + TsupC + "' onchange='NewVal(this)' title='Définir la ou les températures qui permettent l&apos;activation de la fonction On ou Routage.'>°</div>";
            S += "<div>T &le;<input id='T_inf_" + idZ + "'  type='number' value='" + TinfC + "' onchange='NewVal(this)' >°</div></div>";
            S += "<div><small>T en degré (-50.0 à 150.0) ou laisser vide</small></div>";
            S += "</div>";
        }
        S += "<div  class='bord1px' " + style + " >";
        S += "<div>Etat d'une Action <select id='SelAct" + idZ + "' onchange='NewVal(this)' >" + SelectActions + "</select></div>";
        S += "<div class='minmax'><div>Durée : </div><div>H &ge;<input id='H_min_" + idZ + "'  type='text' value='" + Hmin + "' onchange='NewVal(this)' >h:mn</div>";
        S += "<div>H &le;<input id='H_max_" + idZ + "'  type='text' value='" + Hmax + "' onchange='NewVal(this)' >h:mn</div></div>";
        S += "<div class='minmax'><div>Seuil : </div><div>On à Off si &le;<input id='O_min_" + idZ + "'  type='number' value='" + Ooff + "' onchange='NewVal(this)' >%</div>";
        S += "<div>Off à On si &ge;<input id='O_max_" + idZ + "'  type='number' value='" + O_on + "' onchange='NewVal(this)' >%</div></div>";
        S += "<div><small>h:mn ou % ou laisser vide</small></div>";
        S += "</div>";


        if (LTARFbin > 0) {
            S += "<div  class='bord1px' >";
            S += "<div title='Condition d&apos;activation suivant la tarification. Sinon ordre Off envoyé ou Triac/SSR se ferme.'>Actif si tarif :</div>";
            if (LTARFbin <= 3) {
                S += "<div id='PleineCreuse'><span style='color:red;'>Heure Pleine</span><input type='checkbox' checked id='TarifPl_" + idZ + "' onchange='NewVal(this)'> <span style='color:green;'>Heure Creuse</span><input type='checkbox' checked id='TarifCr_" + idZ + "' onchange='NewVal(this)'></div>";
            } else {
                S += "<div id='Tempo'>Tempo <span style='color:blue;'>Bleu</span><input type='checkbox' checked id='TarifBe_" + idZ + "' onchange='NewVal(this)'><span style='color:white;'> Blanc</span><input type='checkbox' checked id='TarifBa_" + idZ + "' onchange='NewVal(this)'><span style='color:red;'> Rouge</span><input type='checkbox' checked id='TarifRo_" + idZ + "' onchange='NewVal(this)'></div>";
            }
            S += "</div>";
        }
        S += "</div>";

        S += "</div>";
        GH(idZ, S);
        if (capteurT) GID("CanalTemp" + idZ).value = LesActions[iAct].Periodes[i].CanalTemp;
        GID("SelAct" + idZ).value = LesActions[iAct].Periodes[i].SelAct;
        var Tarif_ = LesActions[iAct].Periodes[i].Tarif;
        if (LTARFbin > 0) {
            if (LTARFbin <= 3) {
                GID("TarifPl_" + idZ).checked = (Tarif_ & 1) ? 1 : 0; // H Pleine
                GID("TarifCr_" + idZ).checked = (Tarif_ & 2) ? 1 : 0;
            } else {
                GID("TarifBe_" + idZ).checked = (Tarif_ & 4) ? 1 : 0;
                GID("TarifBa_" + idZ).checked = (Tarif_ & 8) ? 1 : 0;
                GID("TarifRo_" + idZ).checked = (Tarif_ & 16) ? 1 : 0; //Rouge
            }
        }
        GID(idZ).style.display = "block";
    }
}

)====";

const char * ActionsJS3 = R"====(
function infoZclose(idx) {
    var champs = idx.split("info");
    var idx = champs[1].split("Z");
    S = "TracePeriodes(" + idx[0] + ");";
    setTimeout(S, 100);
}
function selectZ(T, i, iAct) {
    if (LesActions[iAct].Periodes[i].Type != T) {
        LesActions[iAct].Periodes[i].Type = T;
        var idZ = "info" + iAct + "Z" + i;
        if (T <= 1) {
            infoZclose(idZ);
            TracePeriodes(iAct);
        }
    }
}

function NewVal(t) {
    var champs = t.id.split("info");
    var idx = champs[1].split("Z");   //Num Action, Num période
    if (champs[0].indexOf("Pw_min") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].Vmin = Math.floor(GID(t.id).value);
    }
    if (champs[0].indexOf("Pw_max") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].Vmax = Math.floor(GID(t.id).value);
        if (idx[0] == 0) {
            LesActions[idx[0]].Periodes[idx[1]].Vmax = Math.max(LesActions[idx[0]].Periodes[idx[1]].Vmax, 5);
            LesActions[idx[0]].Periodes[idx[1]].Vmax = Math.min(LesActions[idx[0]].Periodes[idx[1]].Vmax, 100);
        }
    }
    if (champs[0].indexOf("inf") > 0) {
        var V = GID(t.id).value;
        if (V == "") V = 158;
        LesActions[idx[0]].Periodes[idx[1]].Tinf = Math.floor(V * 10);
    }
    if (champs[0].indexOf("sup") > 0) {
        var V = GID(t.id).value;
        if (V == "") V = 158;
        LesActions[idx[0]].Periodes[idx[1]].Tsup = Math.floor(V * 10);
    }
    if (champs[0].indexOf("H_min") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].Hmin = Hmn2Hdeci(GID(t.id).value);
    }
    if (champs[0].indexOf("H_max") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].Hmax = Hmn2Hdeci(GID(t.id).value);
    }
    if (champs[0].indexOf("O_min") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].Ooff = Math.max(0, Math.min(100, Math.floor(GID(t.id).value)));
    }
    if (champs[0].indexOf("O_max") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].O_on = Math.max(0, Math.min(100, Math.floor(GID(t.id).value)));
    }
    if (champs[0].indexOf("Tarif") >= 0) {
        var idZ = "info" + champs[1];
        var Tarif_ = 0;
        if (LTARFbin <= 3) {
            Tarif_ += GID("TarifPl_" + idZ).checked ? 1 : 0; //H pleine
            Tarif_ += GID("TarifCr_" + idZ).checked ? 2 : 0;
        } else {
            Tarif_ += GID("TarifBe_" + idZ).checked ? 4 : 0; //Bleu
            Tarif_ += GID("TarifBa_" + idZ).checked ? 8 : 0;
            Tarif_ += GID("TarifRo_" + idZ).checked ? 16 : 0; //Rouge
        }
        LesActions[idx[0]].Periodes[idx[1]].Tarif = Tarif_;
    }
    if (champs[0].indexOf("CanalTemp") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].CanalTemp = GID(t.id).value;
    }
    if (champs[0].indexOf("SelAct") >= 0) {
        LesActions[idx[0]].Periodes[idx[1]].SelAct = GID(t.id).value;
    }
}


function editTitre(iAct) {
    if (GID("titre" + iAct).innerHTML.indexOf("<input") == -1) {
        GH("titre" + iAct, "<input type='text' value='" + GID("titre" + iAct).innerHTML + "' id='Etitre" + iAct + "'  onblur='TitreValid(" + iAct + ")' >");
        GID("Etitre" + iAct).focus();
    }
}
function TitreValid(iAct) {
    LesActions[iAct].Titre = GID("Etitre" + iAct).value.trim();
    GH("titre" + iAct, LesActions[iAct].Titre);
}
function checkDisabled() {
    GID("sortie0").style.display = "none";
    GID("Freq_PWM").style.display = "none";
    GID("commun").style.display = (ModePara > 0 && ReacCACSI < 100) ? "block" : "none";
    for (var iAct = 0; iAct < LesActions.length; iAct++) {
        for (var i = 0; i <= 5; i++) {
            if (GID("radio" + iAct + "-" + i).checked) { LesActions[iAct].Actif = i; } //0=Inactif,1=Decoupe ou On/Off, 2=Multi, 3= Train, 4= PWM, 5=Demi-Sinus
        }
        if (GID("selectPin" + iAct).value == -1 && LesActions[iAct].Actif > 1 && iAct > 0) { LesActions[iAct].Actif = 1; GID("radio" + iAct + "-" + LesActions[iAct].Actif).checked = true; }
        TracePeriodes(iAct);
        GID("planning0").style.display = (pTriac > 0) ? "block" : "none";  // Si Pas de Triac
        GID("TitrTriac").style.display = (pTriac > 0) ? "block" : "none";
        GID("blocPlanning" + iAct).style.display = (LesActions[iAct].Actif > 0) ? "block" : "none";
        GID("visu" + iAct).style.display = (LesActions[iAct].Actif > 0) ? "block" : "none";
        if (LesActions[iAct].Actif == 1 && iAct>0){
             GID('graphAction' + iAct).style.display ="none";
             GID("visu" + iAct).style.display ="none";
        }
        var visible = (LesActions[iAct].Actif == 1) ? "block" : "none";
        GID("Tempo" + iAct).style.display = visible;
        var disable = true;
        var disp = "block";
        if (GID("selectPin" + iAct).value >= 0) { visible = "none"; disable = false; disp = "none"; }
        GID("SelectOut" + iAct).style.display = (GID("selectPin" + iAct).value <= 0) ? "none" : "inline-block";
        GID("Host" + iAct).style.display = visible;
        GID("Port" + iAct).style.display = visible;
        GID("Repet" + iAct).style.display = visible;
        GID("radio" + iAct + "-2").disabled = disable;
        GID("radio" + iAct + "-3").disabled = disable;
        GID("radio" + iAct + "-4").disabled = disable;
        GID("radio" + iAct + "-5").disabled = disable;
        GID("ordreoff" + iAct).style.display = disp;
        GID("ordreon" + iAct).style.display = disp;
        if (GID("selectPin" + iAct).value == -1 && GID("ordreOn" + iAct).value.indexOf(IS) > 0) GID("ordreOn" + iAct).value = "";
        GID("ligne_bas" + iAct).style.display = (LesActions[iAct].Actif == 1 && GID("selectPin" + iAct).value <= 0 && iAct > 0) ? "flex" : "none";
        GID("fen_slide" + iAct).style.display = (LesActions[iAct].Actif == 1 && iAct > 0) ? "none" : "table";
        if (GID("radio" + iAct + "-4").checked) {
            GID("Freq_PWM").style.display = "block";
            GID("commun").style.display = "block";
        }
        if (ModePara == 0) {
            GID("PID" + iAct).checked = false;
        }
        GID("PIDbox" + iAct).style.display = (ModePara == 0 || (GID("selectPin" + iAct).value <= 0 && iAct > 0) || (LesActions[iAct].Actif == 1 && iAct > 0)) ? "none" : "block";
        GID("Propor" + iAct).style.display = (GID("PID" + iAct).checked) ? "table-row" : "none";
        GID("Derive" + iAct).style.display = (GID("PID" + iAct).checked) ? "table-row" : "none";

    }
    GID("Pwm0").style.display = "none"; //Pas de PWM sur la sortie Triac
}

function LoadActions() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            var LeRetour = this.responseText;
            var Les_ACTIONS = LeRetour.split(GS);
            var LesParas = Les_ACTIONS[0].split(RS);
            LesTemperatures = LesParas[0].split("|");
            NomTemperatures = LesParas[1].split(US);
            LTARFbin = parseInt(LesParas[2]);
            pTriac = parseInt(LesParas[3]);
            ReacCACSI = LesParas[4]; //1,24 8 ou 100 pour EstimCACSI
            if (ReacCACSI < 100) {
                GID("CACSI" + ReacCACSI).checked = true; //Reactivité Ki CACSI et non Estimation
                GID("CACSI").style = "display:block;";
            }
            GID("Fpwm" + LesParas[5]).checked = true;
            LesActions.splice(0, LesActions.length);
            for (var iAct = 1; iAct < Les_ACTIONS.length - 1; iAct++) {
                var champs = Les_ACTIONS[iAct].split(RS);
                var NbPeriodes = champs[12];
                var Periodes = [];
                var j = 13;
                for (var i = 0; i < NbPeriodes; i++) {
                    Periodes[i] = { Type: champs[j], Hfin: champs[j + 1], Vmin: champs[j + 2], Vmax: champs[j + 3], Tinf: champs[j + 4], Tsup: champs[j + 5], Hmin: champs[j + 6], Hmax: champs[j + 7], CanalTemp: champs[j + 8], SelAct: champs[j + 9], Ooff: champs[j + 10], O_on: champs[j + 11], Tarif: champs[j + 12] };
                    j = j + 13;
                }
                LesActions[iAct - 1] = creerAction(champs[0], champs[1], champs[2], champs[3], champs[4], champs[5], champs[6], champs[7], champs[8], champs[9], champs[10], champs[11], Periodes);
            }
            if (LesActions.length == 0) {  //Action Triac
                LesActions.push(creerAction(0, "Titre Triac", "", 50, "", "", "", 0, 10, 10, 10, 0, [{
                    Hfin: 2400,
                    Type: 4,
                    Vmin: 0,
                    Vmax: 100,
                    Tinf: 1600,
                    Tsup: 1600,
                    Hmin: 0,
                    Hmax: 0,
                    CanalTemp: -1,
                    SelAct: 255,
                    Ooff: 0,
                    O_on: 0,
                    Tarif: 31
                }
                ]));
            }
            LesActions.push(creerAction(0, "Titre Relais " + LesActions.length, "", 80, "", "", 240, 0, 10, 10, 10, 0, [{
                Hfin: 2400,
                Type: 3,
                Vmin: 0,
                Vmax: 100,
                Tinf: 1600,
                Tsup: 1600,
                Hmin: 0,
                Hmax: 0,
                CanalTemp: -1,
                SelAct: 255,
                Ooff: 0,
                O_on: 0,
                Tarif: 31
            }
            ]));
            var S = "";
            for (var i = 1; i < LesActions.length; i++) {
                S += "<div id='planning" + i + "' class='planning' ></div>";
            }
            let imax = LesActions.length - 1;

            S += "<input id='butR' type='button'  class='tbut' value='+' onclick='this.style.display=\"none\";GID(\"planning" + imax + "\").style.display=\"block\";' title='Rajouter un relais d&apos;action.'>";
            GH("plannings", S);
            GID("planning" + imax).style.display = "none";
            for (var iAct = 0; iAct < LesActions.length; iAct++) {
                TracePlanning(iAct);
            }
            checkDisabled();
            LoadParaRouteur();
        }
    };
    xhttp.open('GET', '/ActionsAjax', true);
    xhttp.send();
}
function UpdateK(iAct) {
    if (PlotIdx >= 0) { //envoi direct des coef si on visualise en temps réel la régulation. Ils ne sont pas enregistrés.
        let Kp = Math.floor(GID("sliderKp" + iAct).value);
        let Ki = Math.floor(GID("sliderKi" + iAct).value);
        let Kd = Math.floor(GID("sliderKd" + iAct).value);

        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                var retour = this.responseText;
            }
        };
        xhttp.open('GET', '/UpdateK?iAct=' + iAct + '&Kp=' + Kp + '&Ki=' + Ki + '&Kd=' + Kd, true);
        xhttp.send();
    }

}
)====";

const char * ActionsJS4 = R"====(
function SendValues() {
    GID("attente").style = "visibility: visible;";
    for (var iAct = 0; iAct < LesActions.length; iAct++) {
        for (var i = 0; i <= 4; i++) {
            if (GID("radio" + iAct + "-" + i).checked) { LesActions[iAct].Actif = i; }
        }
        LesActions[iAct].Titre = GID("titre" + iAct).innerHTML.trim();
        LesActions[iAct].Host = GID("host" + iAct).value.trim();
        LesActions[iAct].Port = GID("port" + iAct).value;
        LesActions[iAct].OrdreOn = GID("ordreOn" + iAct).value.trim();
        LesActions[iAct].OrdreOff = GID("ordreOff" + iAct).value.trim();
        LesActions[iAct].Repet = GID("repet" + iAct).value;
        LesActions[iAct].Tempo = GID("tempo" + iAct).value;
        LesActions[iAct].Kp = GID("sliderKp" + iAct).value;
        LesActions[iAct].Ki = GID("sliderKi" + iAct).value;
        LesActions[iAct].Kd = GID("sliderKd" + iAct).value;
        LesActions[iAct].PID = GID("PID" + iAct).checked ? 1 : 0;
        if (GID("selectPin" + iAct).value >= 0) LesActions[iAct].OrdreOn = GID("selectPin" + iAct).value + IS + GID("selectOut" + iAct).value;
        if (iAct > 0 && (GID("selectPin" + iAct).value == 0 || LesActions[iAct].Titre == "")) LesActions[iAct].Actif = -1; //Action à effacer
    }
    var S = "";
    for (var iAct = 0; iAct < LesActions.length; iAct++) {
        if (LesActions[iAct].Actif >= 0) {
            S += LesActions[iAct].Actif + RS + LesActions[iAct].Titre + RS;
            S += LesActions[iAct].Host + RS + LesActions[iAct].Port + RS;
            S += LesActions[iAct].OrdreOn + RS + LesActions[iAct].OrdreOff + RS + LesActions[iAct].Repet + RS + LesActions[iAct].Tempo + RS;
            S += LesActions[iAct].Kp + RS + LesActions[iAct].Ki + RS + LesActions[iAct].Kd + RS + LesActions[iAct].PID + RS + LesActions[iAct].Periodes.length + RS;
            for (var i = 0; i < LesActions[iAct].Periodes.length; i++) {
                if (ModePara == 0) { //Standard
                    LesActions[iAct].Periodes[i].CanalTemp = -1;
                    LesActions[iAct].Periodes[i].SelAct = 255;
                }
                S += LesActions[iAct].Periodes[i].Type + RS + Math.floor(LesActions[iAct].Periodes[i].Hfin) + RS;
                S += Math.floor(LesActions[iAct].Periodes[i].Vmin) + RS + Math.floor(LesActions[iAct].Periodes[i].Vmax) + RS;
                S += Math.floor(LesActions[iAct].Periodes[i].Tinf) + RS + Math.floor(LesActions[iAct].Periodes[i].Tsup) + RS;
                S += Math.floor(LesActions[iAct].Periodes[i].Hmin) + RS + Math.floor(LesActions[iAct].Periodes[i].Hmax) + RS;
                S += Math.floor(LesActions[iAct].Periodes[i].CanalTemp) + RS + Math.floor(LesActions[iAct].Periodes[i].SelAct) + RS;
                S += Math.floor(LesActions[iAct].Periodes[i].Ooff) + RS + Math.floor(LesActions[iAct].Periodes[i].O_on) + RS;
                S += LesActions[iAct].Periodes[i].Tarif + RS;
            }
            S += GS;
        }
    }
    if (ReacCACSI < 100) ReacCACSI = document.querySelector('input[name="ReacCACSI"]:checked').value; //Pas d'estimation
    var Fpwm = document.querySelector('input[name="Fpwm"]:checked').value;
    S = encodeURIComponent(S);
    S = "?ReacCACSI=" + ReacCACSI + "&Fpwm=" + Fpwm + "&actions=" + S + "|"; //On ne peut pas terminer par GS

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            var retour = this.responseText;
            location.reload();
        }
    };
    xhttp.open('GET', '/ActionsUpdate' + S, true);
    xhttp.send();

}
function Plot(iAct) {
    for (i = 0; i < LesActions.length; i++) {
        GID('graphAction' + i).style.display = "none";
    }
    if (PlotIdx != iAct) {
        PlotIdx = iAct;
        GID('graphAction' + iAct).style.display = "block";
        PlotR(iAct);
    } else {
        PlotIdx = -1;
    }


}
function PlotR(iAct) { // Courbes détaillées PID
    if (PlotIdx >= 0) {
        if (!Pause) {
            TabIdx = (TabIdx + 1) % 150;
            Ecart[TabIdx] = parseInt(MesuresPwAct[0]);
            Prop[TabIdx] = parseFloat(MesuresPwAct[1]);
            Integ[TabIdx] = parseFloat(MesuresPwAct[2]);
            Deriv[TabIdx] = parseFloat(MesuresPwAct[3]);
            let retard = Prop[TabIdx] + Integ[TabIdx] + Deriv[TabIdx];
            Retard[TabIdx] = Math.floor(Math.max(Math.min(100, retard), 0));
            Ouvert[TabIdx] = 100 - Retard[TabIdx];
            var cW = "#" + Koul[Coul_W][3];
            var cOuvre = "#" + Koul[Coul_Ouvre][3];
            var cRetard = "#" + Koul[Coul_VA][3];
            var cT = "#" + Koul[Coul_Graphe][1];
            var cP = "#" + Koul[Coul_Temp][3];
            var cI = "#" + Koul[Coul_Temp + 1][3];
            var cD = "#" + Koul[Coul_Temp + 3][3];
            var style = 'background:linear-gradient(#' + Koul[Coul_Graphe][5] + ',#' + Koul[Coul_Graphe][3] + ',#' + Koul[Coul_Graphe][5] + ');border-color:#' + Koul[Coul_Tab][5] + ';';
            var S = "<svg viewbox='0 0 970 240' style='" + style + "' height='240'  >"; //  
            S += "<line x1='50' y1='20' x2='50' y2='220' style='stroke:" + cW + ";stroke-width:2' />";
            S += "<line x1='800' y1='20' x2='800' y2='220' style='stroke:" + cOuvre + ";stroke-width:2' />";
            S += "<line x1='50' y1='220' x2='800' y2='220' style='stroke:" + cT + ";stroke-width:2' />";
            for (var t = 0; t < 75; t = t + 5) {
                let x = 800 - t * 10;
                S += "<text x='" + x + "' y='237' style='font-size:16px;fill:" + cT + ";'>" + t + "</text>";
                S += "<line x1='" + x + "' y1='220' x2='" + x + "' y2='224' style='stroke:" + cT + ";stroke-width:2' />";
            }
            S += "<text x='790' y='218' style='font-size:12px;fill:" + cT + ";'>s</text>";
            S += "<text x='804' y='220' style='font-size:12px;fill:" + cOuvre + ";'>0%</text>";
            S += "<text x='804' y='20' style='font-size:12px;fill:" + cOuvre + ";'>100%</text>";
            S += "<line x1='50' y1='20' x2='800' y2='20' style='stroke:" + cOuvre + ";stroke-width:2' stroke-dasharray='1 4' />";
            S += "<line x1='50' y1='120' x2='800' y2='120' style='stroke:" + cW + ";stroke-width:2' stroke-dasharray='1 4' />";
            S += "<text x='20' y='120' style='font-size:12px;fill:" + cW + ";'>0 W</text>";
            S += "<text x='5' y='20' style='font-size:12px;fill:" + cW + ";'>100 W</text>";
            S += "<text x='1' y='220' style='font-size:12px;fill:" + cW + ";'>-100 W</text>";


            S += "<text x='830' y='40' style='font-size:16px;fill:" + cW + ";'>Ecart..........." + Ecart[TabIdx] + " W</text>";
            S += "<text x='830' y='60' style='font-size:16px;fill:" + cOuvre + ";'>Ouverture....." + Ouvert[TabIdx] + " %</text>";
            S += "<text x='860' y='76' style='font-size:12px;fill:" + cT + ";'>= 100 - Retard</text>";
            S += "<text x='830' y='96' style='font-size:16px;fill:" + cRetard + ";'>Retard........." + Retard[TabIdx] + " %</text>";           
            S += "<text x='830' y='168' style='font-size:16px;fill:" + cI + ";'>Intégral....." + Integ[TabIdx] + "</text>";
           
            if (ModePara == 1 && GID("PID" + iAct).checked ){
                S += "<text x='860' y='112' style='font-size:12px;fill:" + cT + ";'>=</text>";
                S += "<text x='830' y='132' style='font-size:16px;fill:" + cP + ";'>Proportion.." + Prop[TabIdx] + "</text>";
                S += "<text x='860' y='148' style='font-size:12px;fill:" + cT + ";'>+</text>";
                S += "<text x='860' y='184' style='font-size:12px;fill:" + cT + ";'>+</text>";
                S += "<text x='830' y='204' style='font-size:16px;fill:" + cD + ";'>Dérivée........" + Deriv[TabIdx] + "</text>";
            }

            let SE = "<polyline points='";
            let SO = "<polyline points='";
            let SR = "<polyline points='";
            let SP = "<polyline points='";
            let SI = "<polyline points='";
            let SD = "<polyline points='";
            for (let i = 0; i < 150; i++) { //toutes les 0.5s
                let j = (TabIdx - i + 150) % 150;
                let x = 800 - i * 5;
                let y = 120 - Ecart[j];
                y = Math.min(y, 220); y = Math.max(y, 20);
                SE += x + "," + y + " ";
                y = 220 - 2 * Ouvert[j];
                SO += x + "," + y + " ";
                y = 220 - 2 * Retard[j];
                SR += x + "," + y + " ";
                y = 120 - Prop[j];
                y = Math.min(y, 220); y = Math.max(y, 20);
                SP += x + "," + y + " ";
                y = 220 - 2 * Integ[j];
                SI += x + "," + y + " ";
                y = 120 - Deriv[j];
                y = Math.min(y, 220); y = Math.max(y, 20);
                SD += x + "," + y + " ";
            }
            SE += "' style='fill:none;stroke:" + cW + ";stroke-width:2' />";
            SO += "' style='fill:none;stroke:" + cOuvre + ";stroke-width:2' />";
            SR += "' style='fill:none;stroke:" + cRetard + ";stroke-width:2' />";
            SP += "' style='fill:none;stroke:" + cP + ";stroke-width:2' />";
            SI += "' style='fill:none;stroke:" + cI + ";stroke-width:2' />";
            SD += "' style='fill:none;stroke:" + cD + ";stroke-width:2' />";
             if (ModePara == 1 && GID("PID" + iAct).checked){
                    S += SD  + SP;
             }
            S +=  SI  + SR + SO + SE + "</svg>";
            GID('graphSVG' + iAct).innerHTML = S;

        }
        let P = "PlotR(" + iAct + ");"
        setTimeout(P, 500);
    }
}

function ShowAction() {
    let Dt = 200; //ms
    if (PlotIdx >= 0) {
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                var retour = this.responseText;
                MesuresPwAct.length = 0;
                MesuresPwAct = retour.split(RS);

            }
        };
        xhttp.open('GET', '/ShowAction?NumAction=' + PlotIdx, true);
        xhttp.send();
    } else {
        for (var i = 0; i < 4; i++) {
            MesuresPwAct[i] = 0;
        }
        for (var i = 0; i < 150; i++) {
            Ecart[i] = 0;
            Ouvert[i] = 0;
            Retard[i] = 0;
            Prop[i] = 0;
            Integ[i] = 0;
            Deriv[i] = 0;
        }
        Dt = 2000;
    }
    setTimeout("ShowAction();", Dt);
}

function FinParaRouteur() {
    SelectActions = "<option value=255>Non exploité</option>";
    for (esp = 0; esp < nomRMS.length; esp++) { //Liste des actions par routeur
        for (var iAct = 0; iAct < nomActions[esp].length; iAct++) {
            var v = esp * 10 + parseInt(nomActions[esp][iAct][0]); //Nombre refletant la référence esp et action
            var T = (esp == 0) ? "" : nomRMS[esp] + " / ";
            SelectActions += "<option value=" + v + ">" + T + nomActions[esp][iAct][1] + "</option>";
            ListeActions[v] = T + nomActions[esp][iAct][1];
        }
    }
    for (var iAct = 0; iAct < LesActions.length; iAct++) {
        GID("PID" + iAct).checked = LesActions[iAct].PID == 1 ? true : false;
    }
    checkDisabled();
}



function AdaptationSource() {

}
)====";

Imprimer cet élément

  perte connexion a la passerelle Envoy suite a passage a version 16.09
Posté par : Jessidy - 21-01-2026, 04:56 PM - Forum : Routeur Photovoltaïque - Réponses (1)

Bonjour a tous,
j'utilise avec bonheur ce routeur solaire depuis la version V8.06, en utilisant ma passerelle Enphase - Envoy comme capteur de mesure de puissance. Tout s'était parfaitement bien passe jusqu'a present (installation - configuration - utilisation) grace a ce site et au Forum.

Merci a André pour ce travail remarquable d'efficacité, et a vous tous pour vos questions / réponses qui m'ont souvent été d'une aide précieuse.

une panne récente (qui s'est révélée être un faux contact), m'a amené a évoluer dimanche dernier vers la version 15.08 (j'en ai profité), car il semblait que cette version était tres stable. Apres quelques difficultés avec le wifi, tout est rentre dans l'ordre :

mes 2 routeurs en réseau (un principal proche du tableau électrique et dont le Triac est connecte a mon cumulus, et un secondaire, sur le cumulus, possédant une sonde de temperature ds18b20) communiquent parfaitement

la connexion a la passerelle enphase se faisait sans problème, et les mesures de puissance / consommation remontaient bien au routeur RMS, permettant l'envoi du surplus solaire vers le cumulus.

mes 2 routeurs ainsi que la passerelle ont des IP fixes déclarées sur dans Livebox.

Ce matin, pas de routage solaire et les donnees maison (venant de la passerelle Enphase) etaient toutes a zero. pas de puissance ni de consommation.

je suis passé a la version 16.09, sans succes.

lorsque je regarde les logs du routeur principal, j'obtiens le message suivant :

"21/01/2026 15:44:59 : connection to client clientFirmV5 failed (call to Envoy-S)
21/01/2026 15:45:59 : connection to client clientFirmV5 failed (call to Envoy-S)
21/01/2026 15:46:05 : connection to client clientFirmV5 failed (call to Envoy-S)
21/01/2026 15:46:59 : connection to client clientFirmV5 failed (call to Envoy-S)
Note échanges entre routeurs
Routeur - DS18b20 (192.168.1.47)"

La connexion entre le routeur et la passerelle ne se fait donc plus.

le firmware de ma passerelle est en D8.3.5167

pour resumer :

routeur RMS V16.09 + Envoy D8.3.5167
Log: "connection to client clientFirmV5 failed"
IP stable 192.168.1.53, WiFi -27dBm

j'ai réussi jusqu'a aujourd'hui a surmonter toutes les difficultés, mais là, je sèche ...

quelqu'un a t il une idée d'ou vient le problème ?

Imprimer cet élément


Utilisateurs en ligne
Il y a actuellement 197 utilisateurs connectés. » 8 Membre(s) | 186 Visiteur(s)
Applebot, Bing, Google, glu3, Guepin, jlcflo, mulraf, scannj1, Sgb31, Specot

Moteur MyBB, © 2002-2026 Melroy van den Berg.