Il y a 1 heure
Bonsoir à tous,
J'ai une domotique "simple" à base d'une passerelle zigbee/wifi/Ethernet TUYA, qui contrôle des volets électriques (j'en ai beaucoup), le variateur de fréquence de mon moteur de piscine, celui de mon spa, et divers capteurs. Je n'ai pas voulu mettre en oeuvre de domotique plus "compliquée".
Je souhaitais juste ajouter un "délestage de conso" les jours tempo rouge pendant les heures pleines en m'appuyant sur ma passerelle Tuya et ses scenarii plutôt que sur une multitude de switches ON/OFF ou autres relais.
Je viens poster ici un code qui crée un nouveau device simple tuya de type switch à deux états (ON/OFF), qui récupère de la trame Linky la couleur tempo du jour. Je récupère accessoirement le jour lendemain et le tarif (HC/HP) que j'utilise pour également allumer en tout trois Leds Rouge du jour, Rouge du lendemain, et HC sur le petit boitier créé dans mon garage à cet effet.
Je n'ai pas réussi à créer de switch à trois boutons, et l'IA m'a fait tourner en rond une journée en tentant plein de solutions qui faisaient du sens mais, soit plantaient, soit ne fonctionnaient pas. Comme seule la couleur du jour ROUGE m'intéressait, j'ai laissé tomber.
Ce swich, une fois créé sur la passerelle, permet d'écrire des scénarii lors des changement d'état ( si TEMPO_ROUGE='ON' ... si TEMPO_ROUGE='OFF' et la passerelle connaissant l'heure actuelle, on peut donc conjuguer avec le switch).
/* Passerelle Linky Tempo -> Tuya */
/* Couleur Rouge du jour ON/OFF remontée vers un "switch appairable Tuya" et LED rouge sur le boitier */
/* Couleur Rouge du Lendemain via LED - Orange sur le boitier chez moi */
/* HC/HP via LED - Bleu sur le boitier chez moi */
// hardware CPU ESP32 H2 pas de wifi, mais du bluetooth et surtout, du ZIGBEE !
// USB on boot Enabled
// Zigbee mode : end device ED
// il faut choisir en "partition" : 4MB Zigbee with Spiffs "
// et utiliser le port USB, ps le port UART
// l'appairage avec la passerelle TUYA se fait directement
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
// --- BROCHES LINKY --- 4 et 5 obligatoires sur ESP32 H2 pour éviter les disfonctionnements avec la pile Zigbee
#define LINKY_RX_PIN 4
#define LINKY_TX_PIN 5
#define LIGHT_ENDPOINT_NUMBER 1
// --- LES 3 BROCHES DES LEDS (GPIOs sûrs sur ESP32-H2) ---
#define LED_today_rouge 3
#define LED_tomorrow_rouge 1
#define LED_hc_bleu 2
#define LinkySerial Serial1
ZigbeeLight *zbLight = nullptr;
String currentLine = "";
bool zigbee_was_connected = false;
bool debug_trames = false;
// Variables pour la gestion du mode simulation
bool simulation_active = false;
unsigned long simulation_start_time = 0;
const unsigned long SIMULATION_DURATION = 10000; // 10 secondes
// Mémorisation des derniers états voulus par le Linky pour les restaurer après l'animation
bool target_today_rouge = false;
bool target_tomorrow_rouge = false;
bool target_hc_bleu = false;
// Variables pour l'animation "Allumage de vie" (toutes les 10s)
unsigned long last_heartbeat_time = 0;
bool heartbeat_active = false;
unsigned long heartbeat_phase_start = 0;
// Mise à jour des LEDs Tempo (stocke aussi l'état cible)
void update_tempo_leds(uint8_t today_bits, uint8_t tomorrow_bits) {
target_today_rouge = (today_bits == 3);
target_tomorrow_rouge = (tomorrow_bits == 3);
// Si aucune animation en cours, on applique directement
if (!heartbeat_active) {
digitalWrite(LED_today_rouge, target_today_rouge ? HIGH : LOW);
digitalWrite(LED_tomorrow_rouge, target_tomorrow_rouge ? HIGH : LOW);
}
}
// Mise à jour de la LED Tarif (bleu si HC)
void update_tarif_leds(bool is_hp) {
target_hc_bleu = !is_hp;
if (!heartbeat_active) {
digitalWrite(LED_hc_bleu, target_hc_bleu ? HIGH : LOW);
}
}
// Gestion de l'état Zigbee
void update_tempo_zigbee(uint8_t today_bits) {
if (today_bits == 0 || zbLight == nullptr) return;
if (!Zigbee.connected()) return;
uint8_t current_state = (today_bits == 3) ? 1 : 0;
zbLight->setLight(current_state == 1);
}
void parse_linky_line(String line) {
if (debug_trames) {
Serial.print("[TRAME LINKY] ");
Serial.println(line);
}
if (simulation_active) return;
line.trim();
if (line.length() == 0) return;
int firstTab = line.indexOf('\t');
if (firstTab == -1) return;
String label = line.substring(0, firstTab);
int secondTab = line.indexOf('\t', firstTab + 1);
String value = (secondTab != -1) ? line.substring(firstTab + 1, secondTab) : line.substring(firstTab + 1);
value.trim();
if (label == "STGE") {
uint32_t stge_val = strtoul(value.c_str(), NULL, 16);
uint8_t today_bits = (stge_val >> 24) & 0x03;
uint8_t tomorrow_bits = (stge_val >> 26) & 0x03;
update_tempo_leds(today_bits, tomorrow_bits);
update_tempo_zigbee(today_bits);
}
else if (label == "LTARF") {
if (value.startsWith("HP")) {
update_tarif_leds(true);
} else if (value.startsWith("HC")) {
update_tarif_leds(false);
}
}
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n--- DEMARRAGE LINKY TEMPO (AVEC HEARTBEAT) ---");
Serial.println("Commandes : 'T' (Trames), 'R' (Simu Rouge), 'B' (Simu Bleu), 'F' (Reset Zigbee)");
pinMode(LED_today_rouge, OUTPUT);
pinMode(LED_tomorrow_rouge, OUTPUT);
pinMode(LED_hc_bleu, OUTPUT);
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, LOW);
zbLight = new ZigbeeLight(LIGHT_ENDPOINT_NUMBER);
Zigbee.addEndpoint(zbLight);
if (!Zigbee.begin()) {
Serial.println("Erreur critique Zigbee.begin() !");
while(1);
}
LinkySerial.begin(9600, SERIAL_7E1, LINKY_RX_PIN, LINKY_TX_PIN);
Serial.println("Système prêt !");
last_heartbeat_time = millis();
}
void loop() {
unsigned long current_millis = millis();
// Gestion de la connexion Zigbee
bool connected = Zigbee.connected();
if (connected && !zigbee_was_connected) {
Serial.println("\n[ZIGBEE] Connecté à la passerelle !");
zbLight->setLight(false);
zigbee_was_connected = true;
} else if (!connected && zigbee_was_connected) {
Serial.println("\n[ZIGBEE] Perte de connexion...");
zigbee_was_connected = false;
}
// Fin du mode simulation
if (simulation_active && (current_millis - simulation_start_time > SIMULATION_DURATION)) {
simulation_active = false;
Serial.println("\n[SIMULATION] Fin de la simulation. Reprise du Linky.");
}
// --- GESTION DE L'ANIMATION "ALLUMAGE DE VIE" (Heartbeat) ---
// Uniquement si on n'est PAS en mode simulation
if (!simulation_active) {
if (!heartbeat_active) {
// Déclenchement toutes les 10 secondes (10000 ms)
if (current_millis - last_heartbeat_time >= 10000) {
heartbeat_active = true;
heartbeat_phase_start = current_millis;
// Phase 1 : Éteindre toutes les LEDs pendant 1s
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, LOW);
}
} else {
unsigned long elapsed = current_millis - heartbeat_phase_start;
if (elapsed < 1000) {
// De 0ms à 1000ms : Éteint (déjà fait)
}
else if (elapsed < 2000) {
// De 1000ms à 2000ms (soit 1s) : Allumer toutes les LEDs
digitalWrite(LED_today_rouge, HIGH);
digitalWrite(LED_tomorrow_rouge, HIGH);
digitalWrite(LED_hc_bleu, HIGH);
}
else {
// Fin de l'animation (après 2 secondes, on rétablit l'état normal)
digitalWrite(LED_today_rouge, target_today_rouge ? HIGH : LOW);
digitalWrite(LED_tomorrow_rouge, target_tomorrow_rouge ? HIGH : LOW);
digitalWrite(LED_hc_bleu, target_hc_bleu ? HIGH : LOW);
heartbeat_active = false;
last_heartbeat_time = current_millis; // On relance le compte à rebours de 10s
}
}
}
// Commandes série
if (Serial.available()) {
char cmd = Serial.read();
if (cmd == 'T' || cmd == 't') {
debug_trames = !debug_trames;
Serial.print("\n[DIAGNOSTIC] Affichage trames : ");
Serial.println(debug_trames ? "ACTIVÉ" : "DÉSACTIVÉ");
}
else if (cmd == 'R' || cmd == 'r') {
Serial.println("\n[Simulation] Auj ROUGE Demain BLEU , HC (10s)");
simulation_active = true;
simulation_start_time = current_millis;
heartbeat_active = false; // Stoppe l'animation le temps du test
digitalWrite(LED_today_rouge, HIGH);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, HIGH);
}
else if (cmd == 'B' || cmd == 'b') {
Serial.println("\n[Simulation] Auj BLANC Demain ROUGE , HP (10s)");
simulation_active = true;
simulation_start_time = current_millis;
heartbeat_active = false;
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, HIGH);
digitalWrite(LED_hc_bleu, LOW);
}
else if (cmd == 'F' || cmd == 'f') {
Zigbee.factoryReset();
}
}
// Lecture Linky
while (LinkySerial.available()) {
char c = LinkySerial.read() & 0x7F;
if (c == '\n') {
parse_linky_line(currentLine);
currentLine = "";
} else if (c != '\r' && c >= 32 && c <= 126) {
currentLine += c;
if (currentLine.length() > 100) currentLine = "";
}
}
delay(10);
}
J'ai une domotique "simple" à base d'une passerelle zigbee/wifi/Ethernet TUYA, qui contrôle des volets électriques (j'en ai beaucoup), le variateur de fréquence de mon moteur de piscine, celui de mon spa, et divers capteurs. Je n'ai pas voulu mettre en oeuvre de domotique plus "compliquée".
Je souhaitais juste ajouter un "délestage de conso" les jours tempo rouge pendant les heures pleines en m'appuyant sur ma passerelle Tuya et ses scenarii plutôt que sur une multitude de switches ON/OFF ou autres relais.
Je viens poster ici un code qui crée un nouveau device simple tuya de type switch à deux états (ON/OFF), qui récupère de la trame Linky la couleur tempo du jour. Je récupère accessoirement le jour lendemain et le tarif (HC/HP) que j'utilise pour également allumer en tout trois Leds Rouge du jour, Rouge du lendemain, et HC sur le petit boitier créé dans mon garage à cet effet.
Je n'ai pas réussi à créer de switch à trois boutons, et l'IA m'a fait tourner en rond une journée en tentant plein de solutions qui faisaient du sens mais, soit plantaient, soit ne fonctionnaient pas. Comme seule la couleur du jour ROUGE m'intéressait, j'ai laissé tomber.
Ce swich, une fois créé sur la passerelle, permet d'écrire des scénarii lors des changement d'état ( si TEMPO_ROUGE='ON' ... si TEMPO_ROUGE='OFF' et la passerelle connaissant l'heure actuelle, on peut donc conjuguer avec le switch).
/* Passerelle Linky Tempo -> Tuya */
/* Couleur Rouge du jour ON/OFF remontée vers un "switch appairable Tuya" et LED rouge sur le boitier */
/* Couleur Rouge du Lendemain via LED - Orange sur le boitier chez moi */
/* HC/HP via LED - Bleu sur le boitier chez moi */
// hardware CPU ESP32 H2 pas de wifi, mais du bluetooth et surtout, du ZIGBEE !
// USB on boot Enabled
// Zigbee mode : end device ED
// il faut choisir en "partition" : 4MB Zigbee with Spiffs "
// et utiliser le port USB, ps le port UART
// l'appairage avec la passerelle TUYA se fait directement
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
// --- BROCHES LINKY --- 4 et 5 obligatoires sur ESP32 H2 pour éviter les disfonctionnements avec la pile Zigbee
#define LINKY_RX_PIN 4
#define LINKY_TX_PIN 5
#define LIGHT_ENDPOINT_NUMBER 1
// --- LES 3 BROCHES DES LEDS (GPIOs sûrs sur ESP32-H2) ---
#define LED_today_rouge 3
#define LED_tomorrow_rouge 1
#define LED_hc_bleu 2
#define LinkySerial Serial1
ZigbeeLight *zbLight = nullptr;
String currentLine = "";
bool zigbee_was_connected = false;
bool debug_trames = false;
// Variables pour la gestion du mode simulation
bool simulation_active = false;
unsigned long simulation_start_time = 0;
const unsigned long SIMULATION_DURATION = 10000; // 10 secondes
// Mémorisation des derniers états voulus par le Linky pour les restaurer après l'animation
bool target_today_rouge = false;
bool target_tomorrow_rouge = false;
bool target_hc_bleu = false;
// Variables pour l'animation "Allumage de vie" (toutes les 10s)
unsigned long last_heartbeat_time = 0;
bool heartbeat_active = false;
unsigned long heartbeat_phase_start = 0;
// Mise à jour des LEDs Tempo (stocke aussi l'état cible)
void update_tempo_leds(uint8_t today_bits, uint8_t tomorrow_bits) {
target_today_rouge = (today_bits == 3);
target_tomorrow_rouge = (tomorrow_bits == 3);
// Si aucune animation en cours, on applique directement
if (!heartbeat_active) {
digitalWrite(LED_today_rouge, target_today_rouge ? HIGH : LOW);
digitalWrite(LED_tomorrow_rouge, target_tomorrow_rouge ? HIGH : LOW);
}
}
// Mise à jour de la LED Tarif (bleu si HC)
void update_tarif_leds(bool is_hp) {
target_hc_bleu = !is_hp;
if (!heartbeat_active) {
digitalWrite(LED_hc_bleu, target_hc_bleu ? HIGH : LOW);
}
}
// Gestion de l'état Zigbee
void update_tempo_zigbee(uint8_t today_bits) {
if (today_bits == 0 || zbLight == nullptr) return;
if (!Zigbee.connected()) return;
uint8_t current_state = (today_bits == 3) ? 1 : 0;
zbLight->setLight(current_state == 1);
}
void parse_linky_line(String line) {
if (debug_trames) {
Serial.print("[TRAME LINKY] ");
Serial.println(line);
}
if (simulation_active) return;
line.trim();
if (line.length() == 0) return;
int firstTab = line.indexOf('\t');
if (firstTab == -1) return;
String label = line.substring(0, firstTab);
int secondTab = line.indexOf('\t', firstTab + 1);
String value = (secondTab != -1) ? line.substring(firstTab + 1, secondTab) : line.substring(firstTab + 1);
value.trim();
if (label == "STGE") {
uint32_t stge_val = strtoul(value.c_str(), NULL, 16);
uint8_t today_bits = (stge_val >> 24) & 0x03;
uint8_t tomorrow_bits = (stge_val >> 26) & 0x03;
update_tempo_leds(today_bits, tomorrow_bits);
update_tempo_zigbee(today_bits);
}
else if (label == "LTARF") {
if (value.startsWith("HP")) {
update_tarif_leds(true);
} else if (value.startsWith("HC")) {
update_tarif_leds(false);
}
}
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n--- DEMARRAGE LINKY TEMPO (AVEC HEARTBEAT) ---");
Serial.println("Commandes : 'T' (Trames), 'R' (Simu Rouge), 'B' (Simu Bleu), 'F' (Reset Zigbee)");
pinMode(LED_today_rouge, OUTPUT);
pinMode(LED_tomorrow_rouge, OUTPUT);
pinMode(LED_hc_bleu, OUTPUT);
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, LOW);
zbLight = new ZigbeeLight(LIGHT_ENDPOINT_NUMBER);
Zigbee.addEndpoint(zbLight);
if (!Zigbee.begin()) {
Serial.println("Erreur critique Zigbee.begin() !");
while(1);
}
LinkySerial.begin(9600, SERIAL_7E1, LINKY_RX_PIN, LINKY_TX_PIN);
Serial.println("Système prêt !");
last_heartbeat_time = millis();
}
void loop() {
unsigned long current_millis = millis();
// Gestion de la connexion Zigbee
bool connected = Zigbee.connected();
if (connected && !zigbee_was_connected) {
Serial.println("\n[ZIGBEE] Connecté à la passerelle !");
zbLight->setLight(false);
zigbee_was_connected = true;
} else if (!connected && zigbee_was_connected) {
Serial.println("\n[ZIGBEE] Perte de connexion...");
zigbee_was_connected = false;
}
// Fin du mode simulation
if (simulation_active && (current_millis - simulation_start_time > SIMULATION_DURATION)) {
simulation_active = false;
Serial.println("\n[SIMULATION] Fin de la simulation. Reprise du Linky.");
}
// --- GESTION DE L'ANIMATION "ALLUMAGE DE VIE" (Heartbeat) ---
// Uniquement si on n'est PAS en mode simulation
if (!simulation_active) {
if (!heartbeat_active) {
// Déclenchement toutes les 10 secondes (10000 ms)
if (current_millis - last_heartbeat_time >= 10000) {
heartbeat_active = true;
heartbeat_phase_start = current_millis;
// Phase 1 : Éteindre toutes les LEDs pendant 1s
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, LOW);
}
} else {
unsigned long elapsed = current_millis - heartbeat_phase_start;
if (elapsed < 1000) {
// De 0ms à 1000ms : Éteint (déjà fait)
}
else if (elapsed < 2000) {
// De 1000ms à 2000ms (soit 1s) : Allumer toutes les LEDs
digitalWrite(LED_today_rouge, HIGH);
digitalWrite(LED_tomorrow_rouge, HIGH);
digitalWrite(LED_hc_bleu, HIGH);
}
else {
// Fin de l'animation (après 2 secondes, on rétablit l'état normal)
digitalWrite(LED_today_rouge, target_today_rouge ? HIGH : LOW);
digitalWrite(LED_tomorrow_rouge, target_tomorrow_rouge ? HIGH : LOW);
digitalWrite(LED_hc_bleu, target_hc_bleu ? HIGH : LOW);
heartbeat_active = false;
last_heartbeat_time = current_millis; // On relance le compte à rebours de 10s
}
}
}
// Commandes série
if (Serial.available()) {
char cmd = Serial.read();
if (cmd == 'T' || cmd == 't') {
debug_trames = !debug_trames;
Serial.print("\n[DIAGNOSTIC] Affichage trames : ");
Serial.println(debug_trames ? "ACTIVÉ" : "DÉSACTIVÉ");
}
else if (cmd == 'R' || cmd == 'r') {
Serial.println("\n[Simulation] Auj ROUGE Demain BLEU , HC (10s)");
simulation_active = true;
simulation_start_time = current_millis;
heartbeat_active = false; // Stoppe l'animation le temps du test
digitalWrite(LED_today_rouge, HIGH);
digitalWrite(LED_tomorrow_rouge, LOW);
digitalWrite(LED_hc_bleu, HIGH);
}
else if (cmd == 'B' || cmd == 'b') {
Serial.println("\n[Simulation] Auj BLANC Demain ROUGE , HP (10s)");
simulation_active = true;
simulation_start_time = current_millis;
heartbeat_active = false;
digitalWrite(LED_today_rouge, LOW);
digitalWrite(LED_tomorrow_rouge, HIGH);
digitalWrite(LED_hc_bleu, LOW);
}
else if (cmd == 'F' || cmd == 'f') {
Zigbee.factoryReset();
}
}
// Lecture Linky
while (LinkySerial.available()) {
char c = LinkySerial.read() & 0x7F;
if (c == '\n') {
parse_linky_line(currentLine);
currentLine = "";
} else if (c != '\r' && c >= 32 && c <= 126) {
currentLine += c;
if (currentLine.length() > 100) currentLine = "";
}
}
delay(10);
}
V17.17 serveur UxIx3 + ref Linky, client Triac CE tampon + client SSR CE tampon + client SSR sur CE tri (+ 3 clients SSR sur 3 radiateurs d'appoint). Variateurs de fréquence sur pompes tri pour Piscine et Spa. 8 panneaux (3 SE 2 S, 3 SO ) 425Wc sur 4 HM800 produisent 20kWh par jour au 16 Mars (Sud-Est France)
