Réaliser son routeur solaire, et plus encore

Routez vos surplus d'électricité solaire plutôt que de les injecter dans le réseau public. Cet article présente comment réaliser très proprement un routeur solaire évolué sous ESPHome.
Réaliser son routeur solaire, et plus encore

Sommaire

⚠️
AVERTISSEMENT

Attention : ce projet n'est pas destiné à tout le monde.
Si vous n'êtes pas confiant en vos compétences d'électricité, de soudure, n'installez pas ce dispositif.

En effet, les niveaux de puissance mis en jeu par ce dispositif sont loin d'être négligeables. Risque de blessures graves ou pire.

Je dégage toute responsabilité, tout comme le fera HACF, en cas de problème rencontré, quel qu'il soit.

Préambule

Venant de faire installer quelques panneaux solaires (2.8 kW crête), j'étais à la recherche d'une solution de routeur solaire et surtout compatible Home Assistant en DIY. J'ai donc décidé de créer mon propre routeur solaire : création d'un circuit imprimé et soudage des composants, conception et impression en 3D du boitier. Je vous présente ici mon projet.

Un routeur solaire, qu'est-ce donc ?

Le routeur solaire permet de maximiser l'utilisation de l'électricité produite par les panneaux solaires en utilisant l'électricité excédentaire pour chauffer l'eau dans un chauffe-eau électrique. Il contribue ainsi à réduire la consommation d'énergie provenant du réseau électrique traditionnel.

En d'autres termes, j'essaye d'injecter de manière automatique un maximum d'énergie issue de ma production solaire dans mon eau chaude sanitaire, tout en minimisant l'apport d'énergie issue du réseau ENEDIS.

Comment ça marche ?

Le cœur du système est un micro contrôleur de type ESP32 que l'on programmera avec ESPHome. Ainsi, le système sera compatible avec Home Assistant.

Le microcontrôleur prend en entrée la puissance consommée ou réinjectée par la maison, et en sortie pilotera en fonction le chauffe-eau :

  • Si la puissance est positive, alors on consomme ( la production solaire est insuffisante ). On n'alimente pas le chauffe-eau.
  • Si elle est négative, on injecte de l'énergie dans le circuit EDF. On va faire en sorte d'alimenter le chauffe-eau.

Le système intègre un module de mesure avec un tore pour mesurer la puissance sur la ligne d'alimentation de la maison, et un second pour mesurer ce qui est injecté dans le chauffe-eau.

Le module de sortie qui aliment le chauffe-eau dispose d'une sécurité : une sonde mesure la température du composant principal (triac avec radiateur) et si elle est trop élevé, le système s'arrête.

Pour finir il y a un petit afficheur de contrôle.

Nous verrons plus en détail dans les chapitres qui suivent le fonctionnement de chaque composant.

Pourquoi le "et plus encore" ? Mesurer c'est savoir.

Le module peut faire plus que le routage solaire. J'ai rajouté à ce projet deux autres fonctions :

  • La récupération des données de la télé-information du compteur Linky (TIC). (testé uniquement en mode historique)
  • La capacité de connecter 4 sondes de mesure de courant supplémentaires non intrusives, ce qui permet de mesurer les postes consommateurs de ma maison.
    Typiquement :
    • Chauffage (pompe à chaleur, convecteurs...),
    • Plaque de cuisson,
    • four,
    • Lave-vaisselle,

A quel appareil est destiné le routeur ?

⚠️
ATTENTION
Le routeur ne peut pas piloter tous les appareils. Il est impératif de lire les restrictions qui suivent.
  1. Le routeur n'est utilisable que sur des charges exclusivement résistives, donc adieu moteur, pompe. Dans ce cas-là, il est impératif d'utiliser un dispositif adapté.
  2. Le routeur n'est pas utilisable pour les chargeurs de batterie ou de véhicule électrique. Il y a des équipements spécifiques dont c'est la fonction.
  3. Enfin, tous les chauffe-eaux ne sont pas compatibles. Seuls les chauffe-eaux à thermostat mécanique sont compatibles avec cette solution.

Concernant les chauffe-eaux :

  • les chauffe-eaux thermodynamiques ne sont pas compatibles avec cette solution
  • les chauffe-eaux à régulation électronique ne sont pas compatibles avec cette solution
exemple de chauffe eau a thermostat electronique
Exemple de thermostat électronique
  • seul et seulement les chauffe-eaux eau à thermostat mécanique sont compatibles avec un routeur solaire.
Exemple de thermostat de chauffe-eau a thermostat mécanique

Mes sources

Je me suis inspiré pour ce projet de différentes réalisations. J'ai identifié quelques projets dont la plupart sont décrits sur le forum photovoltaïque.

Un autre projet est celui de F1ATB : routeur photovoltaïque simple à réaliser. Il nous réalise quasiment tous les 6 mois une évolution de sa solution ultra-performante et universelle Je vous laisse potasser son site très détaillé. Un excellent pédagogue. Merci aussi à lui pour sa capacité à nous faire comprendre les choses.

Ensuite, au cours de l'année 2023 je suis tombé sur une solution à 100% ESPHOME. C'est le routeur de REM81 : PV-Routeur Solaire ESP Home, alias @Remy_Crochon sur le forum et dont je me suis très largement inspiré.

😍
Encore 1000 MERCIS à REM81 pour m'autoriser à écrire cet article et à utiliser une bonne partie de sa solution. De gros lauriers doivent lui revenir.

Ma Configuration Actuelle

Afin d'expliquer un peu mon besoin et mes attentes je dois vous présenter ma configuration actuelle :

  • Un contrat EDF HC/HP en monophasé, avec 2 périodes de tarif HC (un le jour et un la nuit).
  • Compteur Linky en mode historique (important car le firmware de l'ESP va évoluer si vous êtes en mode standard, je n'ai pas testé)
  • Un chauffe-eau de 3.3 kW.
  • 2.8 kW crête de photovoltaïque orienté plein sud en région toulousaine.
  • Du tout électrique à la maison.
  • Un talon de 250 à 300 VA.

Comme vous pouvez le constater, au mieux, mon installation photovoltaïque peut produire 2.2 kW voir 2.5 kW, donc insuffisant pour alimenter exclusivement le cumulus sans faire appel à un complément EDF si je n'utilise pas de routeur.

J'ai une consommation quotidienne moyenne de 4 kWh d'eau chaude. Dont presque la moitié pour les pertes (quand je suis absent j'ai constaté un complément quotidien de 1.8 à 2 kWh) 👀 (inadmissible, oui je dois aussi travailler là-dessus).

Le postulat de base :

  • Effacer le talon de la maison,
  • "Charger" au mieux le chauffe-eau même si l'apport solaire est insuffisant de manière instantanée.

Une réalisation fiabilisée

Le petit hic de beaucoup des solutions DIY, c'est que l'assemblage des différents éléments n'est pas vraiment à mon goût. En effet, raccorder les différents sous-ensembles par des fils Dupond, c'est bien pour de l'expérimentation ou du prototype. Mais dès qu'il y a de la puissance ou du secteur je suis plus à l'aise avec une solution plus intégrée.

Si vous voulez voir des expériences électriques foireuses, allez sur la chaine YouTube d'ElectroBOOM.

Le but de ce sujet est de passer de ça :

À ça :

Le module final avec ses composants

Je me suis donc mis en tête de concevoir un circuit imprimé afin de fiabiliser un peu plus tout ça sans trop avoir un plat de spaghettis dans une boite.

L'électronique

Ce projet est constitué de plusieurs "modules" ou fonctions.

  • le module TIC,
  • le module mesure de courant.
  • le module d'acquisition dédié au routeur solaire avec le cerveau du projet
  • l'affichage,
  • le module dimmer (variateur de puissance),

Schéma du module télé-information

Ce circuit est utilisé pour la récupération de la télé-information. Attention mon installation étant en mode historique, je vous laisse adapter le code si vous êtes en mode standard. je n'ai pas testé si l'esp est capable de prendre en compte toutes ces fonctions en mode standard.

il y a ce dépôt qui donne je crois tout un tas de configurations ( mono, tri phasé, historique, standard), sinon vous avez tout un tas de sujets sur le forum HACF.

Le schéma est celui qui est largement diffusé sur le forum. Je voudrais aussi remercier Charles Hallard qui est à l'origine de ce schéma.
Merci mille fois Charles.

Je vous invite aussi à voir l'article d'Argonaute, pour des usages complémentaires (Dashboard énergie par exemple).

Schéma du module d'acquisition

Le schéma suivant présente un module d'acquisition permettant de mesurer le courant sur 4 circuits différents.

Pour ces 4 circuits de courant, j'ai fait appel à des (ADC) convertisseurs Analogiques Numérique ADS 1115 I²C sur lesquels je branche des pinces ampèremétriques SCT013-30. Cela me permet de mesurer jusqu'à 30 ampères par canal.

Bien que vous puissiez choisir d'autres modèles de sondes de courant (d'autres plages de mesures), assurez-vous de prendre des sondes renvoyant une tension et non pas un courant. Les sondes 100A/50mA ne sont pas compatibles car elles renvoient un courant. D'autre part si vous faites le choix de changer de sonde de courant, le code est à adapter ; ce sera expliqué dans le détail du code.

Les ADS 1115 ont la particularité de pouvoir fonctionner en mode différentiel, ce qui me permet de m'affranchir de créer une masse virtuelle à base de condensateurs, mais surtout élimine drastiquement les parasites (voir ce post sur le forum). ce qui est pratique dans un tableau électrique.

Afin de limiter l'effet d'antenne, les prises jack sont des modèles avec mise à la masse lorsque le canal de mesure n'est pas utilisé (pas de sonde insérée).

Schéma du module d'acquisition pour la partie Routage

le système de mesure, de commande et de visualisation est basé sur :

  • Un module d'acquisition JSY-MK-194,
  • Un ESP32,
  • Un écran LCD 20*4 ,
  • Un système de mesure de température à base de DS18b20
  • Quelques petits éléments complémentaires (alimentation, led ...).

Schéma du variateur de puissance

Le circuit du variateur de puissance (ou "dimmer") est basé sur un variateur à triac tel que ceux utilisés pour les dimmers de chez robotdyn. Attention comme précisé au début de l'article cette variante ne peut être utilisée que pour des charges résistives uniquement (en effet la partie snubber couple R9 C3 a été supprimée).

Réalisation du circuit

Le circuit imprimé

Il nous faut maintenant réaliser le circuit imprimé. J'ai choisi un format Europe (100 * 160) normalisé.

Je n'ai plus les yeux ni la patience, encore moins le matériel pour du composant en CMS (composant en montage en surface) j'utilise donc des composants traversants (truehole).

Voici le circuit :

Vous pouvez retrouver le circuit sur mon github : Fichier GERBER

Je recommande de passer par JLCPCB pour la réalisation du circuit imprimé, mais il y a d'autres fournisseurs qui permettent la réalisation de circuit imprimé par exemple : PCBWAY

PCB Prototype & PCB Fabrication Manufacturer - JLCPCB
Industry-leading PCB prototype manufacturer,offers 24 hours Quick Turn PCB prototype, PCB assembly and Reliable small-batch PCB production.
⚠️
Attention c'est généralement un lot de 5 circuit imprimé minimum à chaque fois, donc organisez-vous (par exemple en faisant une demande d'achat groupé sur le forum).
Idem pour les composants standards qui s'achètent par lot.

Pour information, je n'ai pas prévu de faire de kits. Ni HACF

Les composants

La part list est elle-aussi disponible sur mon github.

Attention :

  • Les résistances R48 et R410 (valeur 120K) doivent être IMPERATIVEMENT des résistances de 1W minimum.
  • Le module alimentation est parfois cloné et le brochage est différent (espacement différent sur les broches du secteur) le hi-link est conforme à mon circuit imprimé (les modules de AZ DELIVERY ne le sont pas) regardez bien la différence d'espacement entre les pins du haut et celles du bas. C'est seulement ce modèle qui est compatible.
  • Les prises jack femelle doivent avoir un contact à la masse quand non utilisées, pour éviter l'effet d'antenne. Voir cette référence 604267116629
  • L'ESP32 est le modèle le plus standard possible celui qui a 38 broches attention, les ESP32 avec prise USB C semble ne pas avoir le même espacement entre broches. prenez la version avec un port micro usb.
  • Le reste des composants est tout ce qu'il y a plus de standard

Renforcement des pistes de puissance

Il est impératif de doubler à l'aide d'un fil de cuivre de section 1.5mm² la partie puissance.
Voir photo ci-dessous, les deux pistes en question ne sont pas recouvertes du vernis de protection afin de rendre la soudure plus facile.

Soudage des composants

Je vous recommande d'utiliser des barrettes de connexion pour l'ESP32. car la programmation initiale (identification de l'adresse du DS18b20) sera à faire sans que l'esp soit installé sur le circuit.

💡
ATTENTION ne pas oubliez de modifier la vitesse de communication du module JSY-MK-194 avant de le souder. La procédure est décrite sur le site de profes'solaire voir cette video
💡
ATTENTION Une fois la programmation initiale, Si vous voulez mettre le firmware à jour, passez exclusivement en WIFI sinon vous allez tout endommager (PC et alimentation interne)

Commencez par les composants les plus petits, (résistances) puis allez progressivement vers les plus gros. Concernant les résistances des 1/4 de watts suffisent, SAUF pour les deux résistances (R48 et R410) de 120k qui doivent OBLIGATOIREMENT être des résistances d'1 watt minimum.

Les LEDs

3 LED sont présentes sur le circuit. Voici leurs significations :

  1. LED 3/OVER TEMP : est connectée au GPIO 25 red_led_pin elle indique un problème ( surchauffe ou température invalide )
  2. LED2 / PROD : est connectée au GPIO32 green_led_pin elle indique que le dimmer est actif
  3. LED TIC : cette dernière clignote à la réception des trames télé-information.

Je vous conseille d'utiliser des fils dupond de 20cm mini et procéder comme pour la sonde de température (voir ci-dessous)

Radiateur et triac

Le triac (module de puissance) sera refroidi par un radiateur de 40 x 40 x 100 mm

Je vous conseille de procéder dans l'ordre suivant :

  • identifier le centre du radiateur, puis faire un trou et un taraudage en 4 mm,
  • fixer le triac temporairement à l'aide d'une vis de M4,
  • présenter le tout sur le circuit imprimé ( ne pas encore souder le triac) afin de pointer les deux trous permettant la fixation du radiateur sur le circuit imprimé. Percez et taraudez toujours en 4m.

D'autre part pour assurer une meilleure isolation électrique entre le radiateur et les pistes du triac, à l'aide d'une lime faite un chanfrein tel que ci-dessous :

Vous pouvez même mettre du vernis à ongle sur les pattes du triac.

Sonde de température (DS18B20)

Pour la surveillance de la température du triac, en effet il y a de la puissance donc de la chaleur à évacuer : un circuit de protection est indispensable.

Pour information voici la courbe de température du triac quand je fais un complément de chauffage la nuit (+ 40 °C en 1 heure)

Ce circuit de protection est basé sur une sonde de température dallas DS18B20, qui sera installée dans le radiateur au plus proche du triac. En effet passé une certaine température, la température de fonctionnement max du triac, celui-ci va rendre l'âme. Dans notre cas, il est préférable de maintenir une température de jonction à une température raisonnable, ce qui fait que je limite la température du radiateur à 75°, d'autant plus que le radiateur n'est pas protégé. Donc s'il est trop chaud potentiellement brûlure.

Pour fixer la sonde, un trou du diamètre de la sonde Dallas est fait au plus proche de la fixation du triac.
Le problème des boitiers TO92 ce n'est pas un diamètre standard (4.7mm). J'ai percé à 4.5 mm puis avec une petite lime ou une petite fraise, puis j'ai adapté le trou de telle sorte que la sonde puise être installée un peu en force.

La sonde fixée avec de la Superglue ou de la colle bi-composant époxy. Avant fixation définitive, je m'assure que cette sonde fonctionne correctement (identification de l'adresse) et je soude des fils Dupont auxquels j'aurais préalablement enlevé les protections en plastique.

Avant soudure, j'aurais fait la provision de gaine thermo rétractable de longueur suffisante. la sonde sera branchée sur le connecteur à trois broches qui est au centre du circuit imprimé ayant comme repère VCC Dout GND. (j'ai fait la même chose pour les leds)

provision de graine thermo pour isolation des pates de la sonde dallas
⚠️
ATTENTION
Avant mise sous tension, à l'aide d'un ohmmètre, assurez-vous de la parfaite isolation du radiateur et des broches du triac.

Mesure du courant

Ce module contient un élément de mesure JSY-MK-194T. Il est disponible chez Aliexpress. Grâce à ce dispositif les grandeurs suivantes sont récupérées, tension, courant, puissance, "sens du courant" pour deux circuits. Voici un lien pour la documentation

La première (celle du bas) pour la mesure de la puissance récupérée, donc l'énergie injectée dans le chauffe-eau. Elle ne sert qu'à cela.

La deuxième sonde (celle du haut) sera utilisée pour la mesure du circuit EDF et participe intégralement à la régulation. Elle est à mettre sur la phase du disjoncteur principal de votre installation électrique. Attention elle a un sens, voir explications plus bas

Il en existe plusieurs modèles du module JSY-MK-194T :

Voltmètre numérique l’inventaire de moniteur d’énergie d’OEM Le compteur d’énergie bidirectionnel JSY-MK-194T prend en charge l’énergie simple d’inductance mutuelle - AliExpress 1420
Smarter Shopping, Better Living! Aliexpress.com
JSY-MK-194T bidirectionnel ouvert de la mesure PCBA de manière bidirectionnelle actuelle de CT deux - AliExpress 1420
Smarter Shopping, Better Living! Aliexpress.com

Prenez de préférence la première version.

💡
ATTENTION: Dans un premier temps, il est nécessaire de configurer le module JSY-MK-194T de telle sorte que la communication soit plus rapide. La manipulation est très bien décrite sur le site du prof’Solaire. Personnellement, j’ai utilisé le logiciel Windows disponible sur le mon github et ce convertisseur USB<->Port série

Partie logicielle

Pour le soft, comme indiqué en préambule, je me suis largement inspiré du travail de rem81, que j'ai adapté à mes besoins.

Les chapitres qui suivent présentent le code ESPHome de chaque module. Je vais essayer d'expliquer étape par étape comment cela fonctionne.

La partie téléformation

Là je n'ai rien inventé tout un tas d'articles sont présents sur le forum HACF.

Il y a juste une subtilité : je voulais afficher les valeurs de consommation journalière HP et HC. Or il n'y a pas de fonction UTILITY_METER dans ESPHOME, j'ai donc bricolé un truc, qui simule tant bien que mal cette fonction. J'aurais pu faire le calcul dans HA et le rebasculer dans l'ESP.

esphome:
  name: "router"
  friendly_name: router-tic-4ct
  on_boot:
    priority: 800
    then: 

# on initialise a zero old_hp et old_hc a chaque demarrage car il n'y a pas de sauvegarde de la valeur
      - sensor.template.publish:
          id: old_hc
          state : 0
      - sensor.template.publish:
          id: old_hp
          state : 0
substitutions:
  rxd_teleinfo_pin: GPIO3
  
uart:
teleinfo:
  id: myteleinfo
  update_interval: 5s
  historical_mode: true
  uart_id: uart_teleinformation
  
binary_sensor:
# identification du mode heure pleine heure creuse utilisé pour forcer la chauffe la nuit et en heure creuse	
  - platform: template
    name: "Sensor Heure Pleine"
    id: mode_hp

sensor:
# Simulation d'un utility meter pour la consommation heure creuse et heure pleine avec un reset a minuit; cette fonction n'existe pas dans esphome
  - platform: template
    name: "HEURE CREUSE JOUR"
    id: hcj
    unit_of_measurement: "kwh"
    lambda: return (float(id(thc).state) - float(id(old_hc).state))/1000;
    device_class: energy
    update_interval: 60s


  - platform: template
    name: "HEURE PLEINE JOUR"
    id: hpj
    unit_of_measurement: "kwh"
    lambda: return (float(id(thp).state) - float(id(old_hp).state))/1000;
    device_class: energy
    update_interval: 60s

  - platform: template
    name : "old_hc"
    id: old_hc

  - platform: template
    name : "old_hp"
    id: old_hp
    
# Declarations pour la teleinformation
  #Linky BASE value
  - platform: teleinfo
    tag_name: "HCHC"
    id: thc 
    name: "Total Heure Creuse"
    filters:
      - filter_out: 0
    unit_of_measurement: "Wh"
    icon: mdi:counter
    teleinfo_id: myteleinfo
    device_class: energy
    on_value: 

      then:
        - if:
            condition:
              lambda: 'return id(old_hc).state == 0;'
            then:
              - sensor.template.publish:
                  id: old_hc
                  state: !lambda |-
                    return id(thc).state;

  - platform: teleinfo
    tag_name: "HCHP"
    id: thp
    name: "Total Heure Pleine"
    filters:
      - filter_out: 0
    unit_of_measurement: "Wh"
    icon: mdi:counter
    teleinfo_id: myteleinfo
    device_class: energy
    on_value: 
      then:
        - if:
            condition:
              lambda: 'return id(old_hp).state == 0;'
            then:
              - sensor.template.publish:
                  id: old_hp
                  state: !lambda |-
                    return id(thp).state;

  #Linky Consumption
  - platform: teleinfo
    tag_name: "PAPP"
    name: "Linky Consommation"
    unit_of_measurement: "VA"
    icon: mdi:flash
    teleinfo_id: myteleinfo
    id: papp
    
   #Linky Intensity
  - platform: teleinfo
    tag_name: "IINST"
    name: "Linky Intensité"
    unit_of_measurement: "A"
    icon: mdi:flash
    teleinfo_id: myteleinfo

#sensor PTEC puisssance tarrifaire en cours     
text_sensor:
  - platform: teleinfo
    tag_name: "PTEC"
    name: "ptec"
    id: ptec
    teleinfo_id: myteleinfo

script:
# on initialise  old_hc et old_hp le soir a minuit 59 minutes afin de simuler un semblant de utility sensor,
  - id: reset_hphc
    mode : single
    then : 
      - sensor.template.publish:
          id: old_hc
          state: !lambda |-
            return id(thc).state;

      - sensor.template.publish:    
          id: old_hp
          state: !lambda |-
            return id(thp).state;

# test heure creuse
  - id: test_heure_creuse
    mode: single
    then:
      - if:
          condition:
            lambda: 'return id(ptec).state == "HC..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: OFF
            - logger.log:
                format: "Passage Heure Creuse"
                level: "info"
      - if:
          condition:
            lambda: 'return id(ptec).state == "HP..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: ON
            - logger.log:
                format: "Passage Heure Pleine"
                level: "info"

La partie sondes de courant

La partie sondes de courant nécessite entre autre l'initialisation du bus I²C ainsi que la déclaration des 2 modules ADS 11115.

On récupérera donc, pour chacun des circuits considéré :

  • le courant
  • la puissance
  • et l'énergie (celle-ci est remise à zéro à minuit)
substitutions:
# bus i²c
  sda_pin: GPIO21
  scl_pin: GPIO22


# activation de l'interface i²c
i2c:
  sda: ${sda_pin}
  scl: ${scl_pin}
  scan: True
  id: i2c_bus_1
  frequency: 200khz

# on active les 2 modules ADC necessaire pour les 4 sondes de courant
ads1115:
  - address: 0x48
    id : ads1
    continuous_mode: true
  - address: 0x49
    id : ads2
    continuous_mode: true

sensor:
#definition des sensors pour les 4 sondes de courant additionnelles

# channel A

  - platform: ads1115
    ads1115_id: ads2
    multiplexer: 'A0_A1'
    gain: 1.024
    name: "Channel A"
    force_update: true
    id: Channel_A
    internal: true

  - platform: ct_clamp
    sensor: Channel_A
    name: "Measured Current Channel A"
    update_interval: ${update_freq}
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 0 -> 0
          - 1 -> 30   
    id: current_channel_A

  - platform: template
    name: power channel a
    id: power_channel_a
    update_interval: ${update_freq}
    unit_of_measurement: W
    lambda: return id(current_channel_A).state * id(tension).state ;

  - platform: total_daily_energy
    name: energy channel a
    power_id: power_channel_a
    id: energy_channel_a
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

# channel B

  - platform: ads1115
    ads1115_id: ads2
    multiplexer: 'A2_A3'
    gain: 1.024
    name: "Channel A"
    force_update: true
    id: Channel_B
    internal: true

  - platform: ct_clamp
    sensor: Channel_B
    name: "Measured Current Channel B"
    update_interval: ${update_freq}
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 0 -> 0
          - 1 -> 30   
    id: current_channel_B

  - platform: template
    name: power channel b
    id: power_channel_b
    update_interval: ${update_freq}
    unit_of_measurement: W
    lambda: return id(current_channel_B).state * id(tension).state;

  - platform: total_daily_energy
    name: energy channel b
    power_id: power_channel_b
    id: energy_channel_b
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

# channel C

  - platform: ads1115
    ads1115_id: ads1
    multiplexer: 'A0_A1'
    gain: 1.024
    name: "Channel C"
    force_update: true
    id: Channel_C
    internal: true

  - platform: ct_clamp
    sensor: Channel_C
    name: "Measured Current Channel C"
    update_interval: ${update_freq}
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 0 -> 0
          - 1 -> 30   
    id: current_channel_C

  - platform: template
    name: power channel c
    id: power_channel_c
    update_interval: ${update_freq}
    unit_of_measurement: W
    lambda: return id(current_channel_C).state *  id(tension).state;

  - platform: total_daily_energy
    name: energy channel c
    power_id: power_channel_c
    id: energy_channel_c  
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

# channel D
  - platform: ads1115
    ads1115_id: ads1
    multiplexer: 'A2_A3'
    gain: 1.024
    name: "Channel D"
    force_update: true
    id: Channel_D
    internal: true

  - platform: ct_clamp
    sensor: Channel_D
    name: "Measured Current Channel D"
    update_interval: ${update_freq}
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 0 -> 0
          - 1 -> 30   
    id: current_channel_D

  - platform: template
    name: power channel d
    id: power_channel_d
    update_interval: ${update_freq}
    unit_of_measurement: W
    lambda: return id(current_channel_D).state * id(tension).state;

  - platform: total_daily_energy
    name: energy channel d
    power_id: power_channel_d
    id: energy_channel_d
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

NOTA : Si vous souhaitez changer la valeur d'une sonde de courant, modifiez la partie du code correspondante

# si channel A avec une sonde 15 Amperes modifier la ligne 20 en y mettant la valeur maximum de la sonde c'est a dire 15
# a adapter pour chacune des voies pour laquelle vous avez changé de sonde de courant, en fonction de la sonde utilisée

  - platform: ads1115
    ads1115_id: ads2
    multiplexer: 'A0_A1'
    gain: 1.024
    name: "Channel A"
    force_update: true
    id: Channel_A
    internal: true

  - platform: ct_clamp
    sensor: Channel_A
    name: "Measured Current Channel A"
    update_interval: ${update_freq}
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 0 -> 0
          - 1 -> 15 
          # modification à faire ci dessus en fonction de la sensibilité de la sonde   
    id: current_channel_A

  - platform: template
    name: power channel a
    id: power_channel_a
    update_interval: ${update_freq}
    unit_of_measurement: W
    lambda: return id(current_channel_A).state * id(tension).state ;

  - platform: total_daily_energy
    name: energy channel a
    power_id: power_channel_a
    id: energy_channel_a
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

Le variateur de puissance (dimmer)

Le code utilisé par le dimmer est celui de la doc d'ESPHome, je n'ai rien inventé.

Un gradateur à triac est simplement un circuit qui est chargé de découper le signal du secteur afin d’en réduire sa puissance moyenne. Pour faire simple, on va découper des morceaux de l’alimentation d’une charge purement résistive (le chauffe-eau) afin de pouvoir moduler la puissance injectée.

La valeur 'a' est le temps de déclenchement, plus cette valeur est petite plus longtemps le triac sera passant et du coup plus longtemps le chauffe-eau sera alimenté.

Dans notre cas la valeur de a = (100 - sortie_triac).

Voici le code du variateur :

substitutions:
  gate_pin: GPIO33
  zerocross_pin: GPIO34
  
output:
  - platform: ac_dimmer
    id: dimmer_ecs
    gate_pin: ${gate_pin}
    zero_cross_pin:
      number: ${zerocross_pin}
      mode:
        input: true
      inverted: yes
    min_power: 0.1

light:
  - platform: monochromatic
    name: "Dimmer"
    output: dimmer_ecs
    name: Dimmerized Light
    id: gradateur
    default_transition_length: 50ms
⚠️
Le système n'est pas proportionnel, et oui nous travaillons avec du courant sinusoïdal. C'est-à-dire que si la commande est à 10% la puissance injectée dans le cumulus ne sera pas 10% de la puissance de celui-ci.
⚠️
Plus la puissance injectée dans le chauffe eau est importante plus la chauffe du triac est conséquente.
Bien comprendre l'identification de la valeur PMAX sinon le routeur se mettra en défaut jusqu'à ce que la température du radiateur revienne à un niveau acceptable ( tmax -2 °) idéalement Tmax = 70°C

RÉGULATION

La partie régulation est issue du code de REM81

La solution retenue pour la régulation est une régulation proportionnelle. Le système n'est pas linéaire car on "découpe" une sinusoïde.

La régulation est faite en mesurant la puissance traversant le Linky :

  • Si elle est positive, alors on consomme (la production solaire est insuffisante).
  • Si elle est négative, alors on injecte de l'énergie dans le circuit EDF.
⚠️
Rappel
Si les mesures indiquent le contraire, inverser le sens de la pince qui est sur la phase en sortie du disjoncteur principal.

La valeur d'ouverture du triac (de 0 à pmax) est calculée de la manière suivante :

incrément= (puissance_reseau) *coef/1000*(-1)

Et ensuite :

  • Si la puissance réseau est négative (on surproduit) alors l'incrément est positif
  • Si la puissance réseau est positive (on consomme) alors l'incrément est négatif

Une fois le calcul précédent fait, on affecte à la valeur d'ouverture du triac la valeur précédente d'ouverture + l'incrément calculé.

# calcul de la valeur d'injection
  - id: calcul_injection
    mode: single
    then:
# mode jour routage si mode auto activé
      - if:
          condition:
            and:
              - binary_sensor.is_on : temperature_triac_ok
              - binary_sensor.is_off : mode_nuit
              - switch.is_on : modeauto
          then:
            - lambda: |-
                id(increment) = (id(puissance_reseau).state*id(coeff_r).state)/1000*-1;
            - lambda: |-
                id(sortie_triac) = id(sortie_triac)+id(increment);
                  if (!isnan(id(sortie_triac))) {
                    id(sortie_triac) = id(sortie_triac)+id(increment);
                  }else{
                    id(sortie_triac)=0;
                  }
                  if (id(sortie_triac) <= 0){
                    id(sortie_triac) = 0;
                  } else if(id(sortie_triac)>=id(pmax).state){
                    id(sortie_triac) = id(pmax).state;
                  }
            - light.turn_on:
                id: gradateur
                brightness: !lambda |-
                  return id(sortie_triac)/100 ;
            - output.turn_on: led_green
            - logger.log:
                format: "Log Auto OK sortie Triac %f - Increment %f"
                args: [ 'id(sortie_triac)', 'id(increment)' ]
                level: info
            - lambda: |-
                id(affichage_sortie_triac).publish_state( id(sortie_triac) );
                id(affichage_increment).publish_state( id(increment) );

# mode nuit heure pleine on desactive le gradateur
      - if: 
          condition:
            and:
              - binary_sensor.is_on: mode_nuit
              - binary_sensor.is_on : mode_hp
          then:
            - lambda: |-
                id(sortie_triac) = 0;
                id(increment) = 0;
            - light.turn_off:
                id: gradateur
            - output.turn_off: led_green
            - logger.log:
                format: "Log mode nuit heure pleine"
                level: info
# mode nuit heure creuse on active le gradateur si mode auto
      - if:
          condition:
             and:
              - binary_sensor.is_on: mode_nuit
              - binary_sensor.is_off : mode_hp
              - switch.is_on : modeauto
          then:
            - lambda: |-
                id(sortie_triac) = id(pmax).state;
                id(increment) = 0;
            - light.turn_on:
                id: gradateur
                brightness: !lambda |-
                  return id(sortie_triac)/100 ;
            - output.turn_on: led_green
            - logger.log:
                format: "Log mode nuit heure creuse sortie triac %f"
                args: [ 'id(sortie_triac)']
                level: info
# il y a un probleme on desactive le gradateur
      - if: 
          condition:
            or:
               - binary_sensor.is_off : temperature_triac_ok
               - switch.is_off: modeauto
          then:
            - lambda: |-
                id(sortie_triac) = 0;
                id(increment) = 0;
            - light.turn_off:
                id: gradateur
            - output.turn_off: led_green
            - logger.log:
                format: "Log mode manuel off ou surchauffe"
                level: info
      - lambda: |-
          id(affichage_sortie_triac).publish_state( id(sortie_triac) );
          id(affichage_increment).publish_state( id(increment) );
👍
Avec la valeur du coef de régulation de 1.5 par défaut j'ai une bonne réactivité.
De fait, j'ai très peu de moments où je dois compléter par le réseau EDF (overshoot) lorsque j'injecte dans le chauffe-eau. C'est appréciable car je suis en tout électrique à la maison.

Mesure des courants pour le routeur

Ce module communique via le protocole MODBUS via une liaison série. Il est donc nécessaire de procéder à l'initialisation de ce protocole via le code suivant.

Une des sondes sera utilisée pour mesurer le courant circulant dans la phase issue du compteur général ( sortie du Linky et avant la répartition sur les rails de disjoncteurs).

substitutions:
  rxd_pin: GPIO16
  txd_pin: GPIO17

uart:
  id: mod_bus
  tx_pin: ${txd_pin}
  rx_pin: ${rxd_pin}
  baud_rate: 38400
  stop_bits: 1

modbus:
  id: modbus1

modbus_controller:
  - id: jsymk
    address: 0x1
    modbus_id: modbus1
    update_interval: 0.75s
    command_throttle: 50ms

sensor:
# Puissance traversant la sonde 1 / ecs
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: puissance_ecs
    name: "Puissance ECS"
    address: 0x004A
    unit_of_measurement: "W"
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.0001
    register_count: 1
    response_size: 4


# Puissance traversant la sonde 2 / Reseau EDF
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: puissance_reseau_absolue
    name: "Puissance Reseau Absolue"
    address: 0x0052
    unit_of_measurement: "W"
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.0001
    register_count: 1
    response_size: 4 
    on_value:
      then:
        - lambda: |-
            if ( id(sens_pince).state == 1 ) {
              id(puissance_reseau).publish_state( id(puissance_reseau_absolue).state *-1);
            } else {
              id(puissance_reseau).publish_state( id(puissance_reseau_absolue).state );
            }



# determination si injection ou consommation : sens pince = 1 alors excedent de production  sens pince = 0 consommation sur le reseau

  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: sens_pince
    name: "Sens_Pince"
    address: 0x004E
    register_type: holding
    value_type: U_DWORD
    bitmask: 0X00010000
    filters:
      - multiply: 1
    register_count: 1
    response_size: 4

# Affichage Puissance Reseau 
  - platform: template
    name: "Puissance Reseau"
    id: puissance_reseau
    unit_of_measurement: "W"
    state_class: "measurement" 

# calcul de l'energie injectée dans le cumulus pour le jour en cours remis a zero a minuit
  - platform: total_daily_energy
    name: "Energie Injectée ECS"
    id: energy_ecs
    power_id: puissance_ecs
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

Mesure de température du triac (DS18B20)

Comme nous l'avons vu, une sonde de température mesure la température du triac pour en assurer la protection.

Le capteur DS18B20 est un capteur de température qui utilise un bus ONE-WIRE/ 1-wire fabriqué originalement par Dallas. Cette technologie de capteur permet d'empiler plusieurs capteurs à travers un bus. Donc il est nécessaire de pouvoir identifier un capteur dans cette chaine parmi les autres : cela est fait en utilisant son adresse interne.

Dans ESPHOME , il est nécessaire de déclarer l'utilisation du module 1-wire et ensuite de déclarer un sensor Dallas avec son adresse spécifique, même si nous n'utilisons qu'un seul et unique capteur.

La première étape est donc l'identification de l'adresse de la sonde

j'avais un ESP8266 sous le coude le code est quasi similaire pour un esp32, il faut juste changer le type de microcontrôleur.

esphome:
  name: temp_ds18b20
esp8266:
  board: nodemcu
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
  - platform: esphome
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
captive_portal:
one_wire:
  - platform: gpio
    pin: D6
 

En retour l'ESP donnera dans les logs quelque chose comme cela :

INFO ESPHome 2024.11.2
INFO Reading configuration /config/esphome/temp-boiler.yaml...
INFO Starting log output from 192.168.1.42 using esphome API
INFO Successfully connected to temp-boiler @ 192.168.1.42 in 0.008s
INFO Successful handshake with temp-boiler @ 192.168.1.42 in 0.018s
[21:30:01][I][app:100]: ESPHome version 2024.11.2 compiled on Nov 28 2024, 21:28:22
[21:30:01][C][wifi:600]: WiFi:
[21:30:01][C][wifi:428]:   Local MAC: AA:AA:AA:AA:AA:AA
[21:30:01][C][wifi:433]:   SSID: [redacted]
[21:30:01][C][wifi:436]:   IP Address: 192.168.1.xxx
[21:30:01][C][wifi:439]:   BSSID: [redacted]
[21:30:01][C][wifi:441]:   Hostname: 'temp_ds18b20'
[21:30:01][C][wifi:443]:   Signal strength: -59 dB ▂▄▆█
[21:30:01][C][wifi:447]:   Channel: 2
[21:30:01][C][wifi:448]:   Subnet: 255.255.255.0
[21:30:01][C][wifi:449]:   Gateway: 192.168.1.254
[21:30:01][C][wifi:450]:   DNS1: 192.168.1.254
[21:30:01][C][wifi:451]:   DNS2: 0.0.0.0
[21:30:01][C][logger:185]: Logger:
[21:30:01][C][logger:186]:   Level: DEBUG
[21:30:01][C][logger:188]:   Log Baud Rate: 115200
[21:30:01][C][logger:189]:   Hardware UART: UART0
[21:30:01][C][gpio.one_wire:020]: GPIO 1-wire bus:
[21:30:01][C][gpio.one_wire:021]:   Pin: GPIO06
[21:30:01][C][gpio.one_wire:080]:   Found devices:
[21:30:01][C][gpio.one_wire:082]:     0x9b3ce1e3814f3128 (DS18B20)

Dans ce cas l'adresse du capteur de température est 0x9b3ce1e3814f3128

Par acquis de conscience, je fais un petit essai pour vérifier le bon fonctionnement de la sonde.

substitutions:
  dallas_address: "0x9b3ce1e3814f3128"
  dallas_pin: GPIO6

esphome:
  name: temp_ds18b20
esp8266:
  board: nodemcu
logger:
api:
ota:
  - platform: esphome
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
captive_portal:
  
one_wire:
  - platform: gpio
    pin: ${dallas_pin}
    
sensor:
  - platform: dallas_temp
    address: ${dallas_address}
    name: "temperature triac"
    update_interval: 2s
    id: temperature_triac

Petite subtilité afin d'endurcir le système si le capteur de température n'est pas initialisé, faux contact, mauvaise adresse, celui-ci retourne NAN (Not A Number) cette dernière valeur sera utilisée dans un script pour dévalider le routage.

# Si Temp Triac invalide
      - if: 
          condition:
            lambda: 'return (isnan(id(temperature_triac).state));' 
          then:
            - binary_sensor.template.publish:
                id: temperature_triac_ok
                state: OFF
            - light.turn_off:
                id: gradateur                
            - logger.log:
                format: "Log Temperature triac invalide"
                level: "info" 
            - output.turn_on: led_red 
⚠️
Important
L'adresse identifiée est à utiliser dans le code final ci-dessous, et à mettre à la place du code donné pour exemple.

Gestion des heures de fonctionnement (Sun)

Pour utiliser ce module, il est nécessaire d'avoir une horloge afin de procéder aux calculs de l'azimut et de l'élévation, mais surtout du mode jour ou nuit.

Dans mon cas, je récupère l'heure de Home Assistant et je définis ma position par des substitutions. Vous trouverez facilement les deux valeurs à l'aide de Google maps. Elles seront à adapter dans le début du code.

substitutions:
  time_timezone: "Europe/Paris"
  ma_latitude: "43.14°"
  ma_longitude: "3.14°"
  
time:
- platform: homeassistant
  id: my_time

sun:
  latitude: ${ma_latitude}
  longitude: ${ma_longitude}

Grâce à un script, je définis si c'est le jour ou la nuit, et tout cela est affecté dans un binary sensor grâce au code suivant. J'en aurai besoin plus tard si je n'ai pas réussi à charger assez mon chauffe-eau.

binary_sensor:
  - platform: template
    name: "Mode-nuit"
    id: mode_nuit
    
script:
  - id: test_nuit
    mode: single
    then:
      - if:
          condition:
            sun.is_below_horizon
          then:
            - binary_sensor.template.publish:
                id: mode_nuit
                state: ON
            - logger.log:
                format: "Passage Mode Nuit"
                level: "info"
      - if:
          condition:
            sun.is_above_horizon
          then:
            - binary_sensor.template.publish:
                id: mode_nuit
                state: OFF
            - logger.log:
                format: "Passage Mode Jour"
                level: "info"

Je récupère ensuite l'heure de mon serveur home assistant. C'est indispensable pour le module SUN pour qu'il puisse identifier si le soleil est au-dessous ou au-dessus de l'horizon.

time:
- platform: homeassistant
  id: my_time

Tarification HC / HP

J'ai un abonnement avec heure pleine heure/creuse. Et comme nous avons une interface linky intégrée, nous pouvons donc récupérer la tarification en cours (paramètre PTEC) qui a pour valeur HC.. ou HP..

Pour cela, je rajoute le code ESP suivant, Bien entendu cela est à adapter en fonction du mode de votre linky et de votre contrat de fourniture d’électricité.

text_sensor:
# Puissance Tarifaire en cours

  - platform: teleinfo
    tag_name: "PTEC"
    name: "ptec"
    id: ptec
    teleinfo_id: myteleinfo

script:
  - id: test_heure_creuse
    mode: single
    then:
      - if:
          condition:
            lambda: 'return id(ptec).state == "HC..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: OFF
            - logger.log:
                format: "Passage Heure Creuse"
                level: "info"
      - if:
          condition:
            lambda: 'return id(ptec).state == "HP..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: ON
            - logger.log:
                format: "Passage Heure Pleine"
                level: "info"

Routage automatique ou non

J'ai décidé de pouvoir activer ou non la fonction routage automatique la journée ou forcée la nuit pour des raisons X ou Y. Un binary_sensor sera interne à l'ESP mais commandé par une automation HA.

Si j'injecte dans le réseau ENEDIS une certaine valeur, c'est que mon Chauffe Eau est "plein".

Dernière chose : j'ai installé forecast.solar qui me permet d'avoir une idée de la prévision de production d'énergie solaire via l'entité : sensor.energy_production_tomorrow

Donc j'ai fait dans Home Assistant 2 automations, (les puristes pourront en faire une seule et unique, mais là un choix).

La première désactive le complément de chauffe la nuit si on a réussi à injecter si j'ai injecté plus de 2kWh dans le réseau Enedis.

alias: routeur - mode auto off
description: ""
triggers:
  - trigger: time
    at: "23:59:00"
conditions:
  - condition: numeric_state
    entity_id: sensor.injection_jour
    above: 2000
  - condition: state
    entity_id: input_boolean.inter_validation_routeur
    state: "off"   
actions:
  - action: switch.turn_off
    metadata: {}
    data: {}
    target:
      entity_id:
        - switch.router_tic_4ct_mode_auto
  - action: notify.pushbullet
    metadata: {}
    data:
      message: mode auto routeur off
mode: single

Pour l'identification de la valeur d'injection je vous laisse regarder si votre systeme photovoltaique vous le propose.

Sinon à l'aide d'un template... ( merci https://forum.hacf.fr/t/panneaux-solaire-enphase-envoy/3938/8)

template: 
  - sensor: 
    - name: injection
      unit_of_measurement: "W"
      state: "{{ states('sensor.solar_power_production') | float | round(2) - states('sensor.current_power_consumption') | float | round(2)}}"


et le matin, on réactive le routeur, si le boolean inter_validation_router est activé dans Home Assistant. Ce dernier est un helper créé pour activer ou non le routage. C'est un peu comme un mode vacances. pourquoi chauffer l'eau alors que nous sommes absents.

alias: routeur mode auto on
description: ""
triggers:
  - trigger: state
    entity_id:
      - sun.sun
    from: below_horizon
    to: above_horizon
conditions:
  - condition: state
    entity_id: input_boolean.inter_validation_routeur
    state: "on"
actions:
  - action: switch.turn_on
    metadata: {}
    data: {}
    target:
      entity_id: switch.mode_auto
mode: single

En effet, si la météo a été maussade, j'ai besoin d'un complètement d'injection avec une puissance ayant une valeur prédéterminée (PMAX).

Identification de Pmax

La valeur de PMAX est à identifier manuellement de telle sorte que le circuit de protection thermique ne soit pas activé.

Je m'explique, en ce moment la température dans mon garage est d'environ 12°c, et quand j'active la marche forcée, (soit 80% d'ouverture et grosso modo 2 kWatt mesuré) la température de mon triac atteint 50-55°c, soit une élévation de température de grosso modo 40°c pour une injection à 80%.

En été la température dans mon garage est aux alentours de 30-35° la journée, donc la température du triac peut facilement atteindre 75°c/80°c. Du coup, j'arrive facilement à la limite de sécurité. A moi en été de limiter la valeur de Pmax.

Ci-dessous la partie de code ESP pour le complément de chauffe la nuit si nécessaire.

      - if:
          condition:
             and:
              - binary_sensor.is_on: mode_nuit
              - binary_sensor.is_off : mode_hp
              - switch.is_on : modeauto
          then:
            - lambda: |-
                id(sortie_triac) = id(pmax).state;
                id(increment) = 0;
            - light.turn_on:
                id: gradateur
                brightness: !lambda |-
                  return id(sortie_triac)/100 ;
            - output.turn_on: led_green
            - logger.log:
                format: "Log mode nuit heure creuse sortie triac %f"
                args: [ 'id(sortie_triac)']
                level: info

Affichage sur un écran LCD 20* 4

L'écran LCD est complètement inutile (tout est dans Home Assistant) donc rigoureusement indispensable. Il est connecté via le BUS I²C, qui exposé en bas du circuit imprimé. 4 fils Dupond permettrons de raccorder les 2

GND va sur GND ; SDA va sur SDA ; SCL va sur SCL ; VCC va sur VCC

Affichage des données sur 5 pages d'informations avec changement toutes les 5 secondes

globals:
   - id: page
     type: int
     initial_value: "1"
     
display:
  - platform: lcd_pcf8574
    id: mydisplay
    dimensions: 20x4
    address: 0x27
    lambda: |-
          switch (id(page)){
            case 1 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(ha_time).now());
            it.printf(0,1,"tarif:%s", id(ptec).state.c_str() );
            it.printf(16,1,"%s", id(mode_nuit).state ? "Nuit" : "Jour");
            it.printf(0,2,"uptime %0.f H",id(esp_uptime).state);
            it.printf(0,3,"Wifi:%0.1f dB", id(wifi_signal_db).state);
              break;
            case 2 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(ha_time).now());
            it.printf(0,1,"P Reseau=%0.0f W",id(puissance_reseau).state);
            it.printf(0,2,"P ECS=%0.0f W ",id(puissance_ecs).state);
            it.printf(0,3,"Energie ECS=%0.3f kWh",id(energy_ecs).state); 
              break;
            case 3 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(ha_time).now());
            it.printf(0,1,"Triac T=%0.1f C", id(temperature_triac).state);
            it.printf(10,2,"Ouv T:%0.0f", id(affichage_sortie_triac).state);
            it.printf(0,2,"Inc %0.1f", id(affichage_increment).state);
            it.printf(0,3,"Mode=%s", id(modeauto).state ? "Auto" : "Manu"); 
              break;
            case 4 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(ha_time).now());
            it.printf(0,1,"hpj=%0.3f kWh", id(hpj).state);
            it.printf(0,2,"hcj=%0.3f kWh", id(hcj).state);
            it.printf(0,3,"papp:%0.0f VA", id(papp).state);
            it.printf(10,3,"tarif:%s", id(ptec).state.c_str() );
              break;
            case 5 :
            it.printf(0, 0, "PA:%0.0f W",id(power_channel_a).state);
            it.printf(0, 1, "PB:%0.0f W",id(power_channel_b).state);
            it.printf(0, 2, "PC:%0.0f W",id(power_channel_c).state);
            it.printf(0, 3, "PD:%0.0f W",id(power_channel_d).state);
            it.printf(9, 0, "EA:%0.3fkWh",id(energy_channel_a).state);
            it.printf(9, 1, "EB:%0.3fkWh",id(energy_channel_b).state );
            it.printf(9, 2, "EC:%0.3fkWh",id(energy_channel_c).state );
            it.printf(9, 3, "ED:%0.3fkWh",id(energy_channel_d).state);
              break;
          } interval:
  - interval: 5s
      then:
      - lambda: |-
          id(page) = (id(page) + 1);
          if (id(page) > 3) {
            id(page) = 1;

Le script associé permet de basculer automatiquement parmi les 5 pages définies ci-dessus avec un intervalle de 5 secondes.

TIPS - sauvegarder le code de l'ESP

le code est stocké en clair dans l'ESP et lisible grâce à l'utilisation d'un external_components , Merci @tedour pour avoir trouvé cela.

Pour récupérer le fichier source http://ip_esp/config.yaml

# definition external componant
# permet de faire un backup du code dans l'ESP 
external_components:
  - source: github://dentra/esphome-components
backup:

Le code complet

Voici le code qui est actif actuellement sur mon routeur :

# definition des substitutions
# ATTENTION IL EST INDISPENSABLE D'IDENTIFIER L'ADRESSE DE SON CAPTEUR DALLAS  voir  ESPHOME.IO pour la marche a suivre
# ATTENTION LA LONGITUDE ET LA LATITUDE DOIVENT ETRE ELLES AUSSI CUSTOMISEE.
# Cela permet de definir un boolean pour le complement de chauffe la nuit.
# Attention j'utilise aussi un sensor de HA issu de la teleinfo afin de d'activer le routeur la nuit lors du passage en heure creuse

substitutions:
  friendly_name: myrouter2
  gate_pin: GPIO33
  zerocross_pin: GPIO34
  green_led_pin: GPIO32
  red_led_pin: GPIO25
  wifi_led_pin: GPIO13
  dallas_pin: GPIO27
  dallas_address: "0xbf0008008afd4a10"
  sda_pin: GPIO21
  scl_pin: GPIO22
  rxd_pin: GPIO16
  txd_pin: GPIO17
  time_timezone: "Europe/Paris"
  ma_latitude: "43.659°"
  ma_longitude: "1.310°"
  adress_ip: "192.168.1.219"


# definition du type de esp et du framework
esp32:
  board: nodemcu-32s
  framework:
    type: arduino


# desactivation des sorties au boot de l'ESP
esphome:
  name: ${friendly_name}
  on_boot:
    priority: 800
    then: 
      - binary_sensor.template.publish:
          id: temperature_triac_ok
          state: OFF

      - light.turn_off : gradateur

      - output.turn_off: led_green
      - output.turn_off: led_red


# activation de la fonction log pour deboggage
logger:
  baud_rate: 0
#  level: debug
  level: INFO

# activation de l'api de communication entre ESPHOME et HOME ASSISTANT
api:

# activation du wifi et de l'access point mode si probleme de connection
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  reboot_timeout: 5min
  ap:
    ssid: "Myrouter Fallback Hotspot"
    password: !secret wifi_password

captive_portal:

# activation de la fonction Over The Air Update	
ota:
  - platform: esphome

# activation du server web interne
web_server:
  port: 80

# activation de l'horloge interne
# necessaire pour identifier l'heure du couché du soleil, utilisé par le composant SUN
time:
- platform: homeassistant
  id: my_time

# activation du componant sun pour identifier si jour ou nuit
sun:
  latitude: ${ma_latitude}
  longitude: ${ma_longitude}

# definition external componant
# permet de faire un backup du code dans l'ESP 
external_components:
  - source: github://dentra/esphome-components
backup:


#activation du componant I²C necessaire pour l'ecran LCD
i2c:
  sda: ${sda_pin}
  scl: ${scl_pin}
  scan: True
  id: i2c_bus_1

#activation du componant UART indispensable pour le componant MODBUS  
uart:
  id: mod_bus
  tx_pin: ${txd_pin}
  rx_pin: ${rxd_pin}
  baud_rate: 38400
  stop_bits: 1

# Activation du composant modbus necessaire pour le capteur jsymk  
modbus:
  id: modbus1

# definition du controleur modubs  
modbus_controller:
  - id: jsymk
    address: 0x1
    modbus_id: modbus1
    update_interval: 0.75s
    command_throttle: 50ms

# activation du composant on_wire indispensable pour la sonde de temperature dallas	
one_wire:
  - platform: gpio
    pin: ${dallas_pin}


#Definition des variables globales
globals:
# increment est la variable qui est utilisée pour le calcul de la variation de la valeur d'ouverture du triac
   - id: increment
     type: float
     restore_value: no
     initial_value: '0'

# Sortie triac est la valeur donnee au dimmer	 
   - id: sortie_triac
     type: float
     initial_value: '0'

   - id: page
     type: int
     initial_value: "1"

# Definition des binary sensors
binary_sensor:
# donne l'etat de connection wifi
  - platform: status
    name: "Status"

# indique si la temperature du triac est inferieure a la valeur maximum telle que definie par le template "Temperature Max"
  - platform: template
    name: "Triac Temp Ok"
    id: temperature_triac_ok

# identification de la periode du jour
  - platform: template
    name: "Mode- nuit"
    id: mode_nuit

# identification du mode heure pleine heure creuse utilisé pour forcer la chauffe la nuit et en heure creuse	
  - platform: template
    name: "Sensor Heure Pleine"
    id: mode_hp

# activation du routeur necessite de creer dans home assistant un helper boolean afin d'activer ou non le routage 
  - platform: homeassistant
    name: "Validation Routeur"
    entity_id: "input_boolean.inter_validation_routeur"
    publish_initial_state: true
    id: val_routeur 

  - platform: gpio
    pin: 
      number: GPIO14
      mode: input_pullup
    name: validation_affichage
    id: val_affichage
    on_press:
      then:
        - binary_sensor.template.publish:
            id: backlight
            state: ON

        - binary_sensor.template.publish:
            id: backlight
            state: OFF  
  - platform: template
    id: backlight
    filters:
      - delayed_off: 300s
    on_press:
      then:
        - lambda: |-
            id(mydisplay).backlight();
    on_release:
      then:
        - lambda: |-
            id(mydisplay).no_backlight();

number:

# Seuil Max sortie triac
  - platform: template
    name: "Puissance Max"
    id: pmax
    optimistic: true
    restore_value: true
    mode: box
    min_value: 10
    max_value: 80
    unit_of_measurement: "%"
    step: 5

# Temperature Maxi du triac avant coupure
  - platform: template
    name: "Temperature Max"
    id: tmax
    optimistic: true
    restore_value: true
    initial_value: 40
    mode: box
    min_value: 0
    max_value: 60
    unit_of_measurement: "°C"
    step: 1

# Coefficient de reactivité du routeur
  - platform: template
    name: "Coeff Reactivite"
    id: coeff_r
    optimistic: true
    restore_value: true
    mode: box
    initial_value: 1
    min_value: 0
    max_value: 10
    unit_of_measurement: ""
    step: 0.1



text_sensor:
# Puissance Tarifaire en cours
  - platform: homeassistant
    name: "PTEC"
    entity_id: "sensor.ptec"
    id: ptec

  - platform: wifi_info
    ip_address:
      name: ESP IP Address

sensor:
  - platform: uptime
    name: "uptime"
    id: esp_uptime
    filters:
      - lambda: return x / 3600.0;
    unit_of_measurement: "hours"
    accuracy_decimals: 2    

  - platform: internal_temperature
    name: "Internal Temperature"
    id: esp_internal_temperature

  - platform: wifi_signal 
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"

  - platform: dallas_temp
    address: ${dallas_address}
    name: "temperature triac"
    update_interval: 2s
    id: temperature_triac
#    filters:
#      - filter_out: NAN

# Puissance traversant la sonde 1 / puecs
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: puissance_ecs
    name: "Puissance ECS"
    address: 0x004A
    unit_of_measurement: "W"
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.0001
    register_count: 1
    response_size: 4

# Courant traversant la sonde 1 / puecs
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: courant_ecs
    name: "courant ECS"
    address: 0x0049
    unit_of_measurement: "A"
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.0001
    register_count: 1
    response_size: 4

# Facteur de puissance traversant la sonde 1 / puecs
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: fp_ecs
    name: "fp ECS"
    address: 0x004C
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.001
    register_count: 1
    response_size: 4

# Puissance traversant la sonde 2 / Reseau
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: puissance_reseau_absolue
    name: "Puissance Reseau Absolue"
    address: 0x0052
    unit_of_measurement: "W"
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.0001
    register_count: 1
    response_size: 4 
    on_value:
      then:
        - lambda: |-
            if ( id(sens_pince).state == 1 ) {
              id(puissance_reseau).publish_state( id(puissance_reseau_absolue).state *-1);
            } else {
              id(puissance_reseau).publish_state( id(puissance_reseau_absolue).state );
            }

# Facteur de puissance traversant la sonde 2 / reseau
  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: reseau
    name: "fp reseau"
    address: 0x0054
    register_type: holding
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.001
    register_count: 1
    response_size: 4

# determination si injection ou consomation : sens pince = 1 alors excedent de production  sens pince = 0 consommation sur le reseau

  - platform: modbus_controller
    modbus_controller_id: jsymk
    id: sens_pince
    name: "Sens_Pince"
    address: 0x004E
    register_type: holding
    value_type: U_DWORD
    bitmask: 0X00010000
    filters:
      - multiply: 1
    register_count: 1
    response_size: 4

# Affichage Puissance Reseau
  - platform: template
    name: "Puissance Reseau"
    id: puissance_reseau
    unit_of_measurement: "W"
    state_class: "measurement" 

# calcul de l'energie injectée dans le cumulus pour le jour en cours remis a zero a minuit
  - platform: total_daily_energy
    name: "Energie Injectée ECS"
    id: energy_ecs
    power_id: puissance_ecs
    unit_of_measurement: kWh
    device_class: energy    
    filters:
      - multiply: .001

#Affichage valeur increment	  
  - platform: template
    name: "Increment"
    id: affichage_increment
    unit_of_measurement: ""
    accuracy_decimals: 2
    state_class: "measurement"

# affichage sortie triac	
# attention la valeur doit etre divisée par 100 pour affecter la valeur brightness du dimmer
  - platform: template
    name: "Sortie Triac"
    id: affichage_sortie_triac
    unit_of_measurement: "%"
    state_class: "measurement"  
    accuracy_decimals: 2


display:
  - platform: lcd_pcf8574
    id: mydisplay
    dimensions: 20x4
    address: 0x27
    lambda: |-
          switch (id(page)){
            case 1 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(my_time).now());
            it.printf(0,1,"tarif:%s", id(mode_hp).state ? "HP" : "HC");
            it.printf(16,1,"%s", id(mode_nuit).state ? "Nuit" : "Jour");
            it.printf(0,2,"uptime %0.f H",id(esp_uptime).state);
            it.printf(14,2,"T=%0.1fc", id(esp_internal_temperature).state);
            it.printf(0,3,"Wifi:%0.1f", id(wifi_signal_db).state);
              break;
            case 2 :
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(my_time).now());
            it.printf(0,1,"P Reseau=%0.0fW",id(puissance_reseau).state);
            it.printf(0,2,"P ECS=%0.0fW ",id(puissance_ecs).state);
            it.printf(0,3,"Energie ECS=%0.1f kWh ",id(energy_ecs).state); 
              break;
            case 3:
            it.strftime(2, 0, "%H:%M %d-%m-%Y", id(my_time).now());
            it.printf(0,1,"Triac T=%0.1fc", id(temperature_triac).state);
            it.printf(10,2,"Ouv T:%0.1f", id(affichage_sortie_triac).state);
            it.printf(0,2,"Inc %0.1f", id(affichage_increment).state);
            it.printf(0,3,"Mode=%s", id(modeauto).state ? "Auto" : "Manu"); 
              break;
          }    


switch :

  - platform: restart
    name: "${friendly_name} Restart"
    id: restart_esp

  - platform: template
    name: "Mode Auto"    
    id: modeauto
    optimistic: true
    restore_mode: always_on    
    
output:
  - platform: ac_dimmer
    id: dimmer_ecs
    gate_pin: ${gate_pin}
    method: leading
    zero_cross_pin:
      number: ${zerocross_pin}
      mode:
        input: true
      inverted: yes
    min_power: 0.1

  - id: led_red
    platform: gpio
    pin: ${red_led_pin}
#    inverted: true
    
  - id: led_green
    platform: gpio
    pin: ${green_led_pin}
#    inverted: true
light:

  - platform: monochromatic
    name: "Dimmer"
    output: dimmer_ecs
    id: gradateur
    default_transition_length: 50ms

  - platform: status_led
    id: wifi_status_led
    pin:
      number: GPIO13

interval:
 
  - interval: 30s
    then:
      if:
        condition:
          wifi.connected:
        then:
          - light.turn_on: wifi_status_led
        else:
          - light.turn_off: wifi_status_led  

  - interval: 5s
    then:
      - script.execute: test_nuit
      - script.execute: test_heure_creuse

      - lambda: |-
          id(page) = (id(page) + 1);
          if (id(page) > 3) {
            id(page) = 1;
          }
  - interval: 1s
    then:          
      - script.execute: validation_temperature      
      - script.execute: calcul_injection
      
script:

# validation temperature  
  - id: validation_temperature
    mode: single
    then:
# Si Temp triac inferieure a tmax - 2 °c alors ok
      - if:
          condition:
            lambda: 'return id(temperature_triac).state < (id(tmax).state-2);'

          then:
            - binary_sensor.template.publish:
                id: temperature_triac_ok
                state: ON
            - logger.log:
                format: "Log temperature triac ok"
                level: "info"  
            - output.turn_off: led_red
# Si Temp Triac supérieur ou égale a tmax alors NOK
      - if: 
          condition:
            lambda: 'return id(temperature_triac).state >= id(tmax).state;'
          then:
            - binary_sensor.template.publish:
                id: temperature_triac_ok
                state: OFF
            - light.turn_off:
                id: gradateur                
            - logger.log:
                format: "Log temperature triac nok"
                level: "info"
            - output.turn_on: led_red
# Si Temp Triac invalide
      - if: 
          condition:
            lambda: 'return (isnan(id(temperature_triac).state));' 
          then:
            - binary_sensor.template.publish:
                id: temperature_triac_ok
                state: OFF
            - light.turn_off:
                id: gradateur                
            - logger.log:
                format: "Log Temperature triac invalide"
                level: "info" 
            - output.turn_on: led_red 

# test nuit
  - id: test_nuit
    mode: single
    then:
      - if:
          condition:
            sun.is_below_horizon
          then:
            - binary_sensor.template.publish:
                id: mode_nuit
                state: ON
            - logger.log:
                format: "Passage Mode Nuit"
                level: "info"
      - if:
          condition:
            sun.is_above_horizon
          then:
            - binary_sensor.template.publish:
                id: mode_nuit
                state: OFF
            - logger.log:
                format: "Passage Mode Jour"
                level: "info"

# test heure creuse
  - id: test_heure_creuse
    mode: single
    then:
      - if:
          condition:
            lambda: 'return id(ptec).state == "HC..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: OFF
            - logger.log:
                format: "Passage Heure Creuse"
                level: "info"
      - if:
          condition:
            lambda: 'return id(ptec).state == "HP..";'
          then:
            - binary_sensor.template.publish:
                id: mode_hp
                state: ON
            - logger.log:
                format: "Passage Heure Pleine"
                level: "info"
# calcul de la valeur d'injection
  - id: calcul_injection
    mode: single
    then:
# mode jour routage si mode auto activé
      - if:
          condition:
            and:
              - binary_sensor.is_on : temperature_triac_ok
              - binary_sensor.is_off : mode_nuit
              - switch.is_on : modeauto
          then:
            - lambda: |-
                id(increment) = (id(puissance_reseau).state*id(coeff_r).state)/1000*-1;
            - lambda: |-
                id(sortie_triac) = id(sortie_triac)+id(increment);
                  if (!isnan(id(sortie_triac))) {
                    id(sortie_triac) = id(sortie_triac)+id(increment);
                  }else{
                    id(sortie_triac)=0;
                  }
                  if (id(sortie_triac) <= 0){
                    id(sortie_triac) = 0;
                  } else if(id(sortie_triac)>=id(pmax).state){
                    id(sortie_triac) = id(pmax).state;
                  }
            - light.turn_on:
                id: gradateur
                brightness: !lambda |-
                  return id(sortie_triac)/100 ;
            - output.turn_on: led_green
            - logger.log:
                format: "Log Auto OK sortie Triac %f - Increment %f"
                args: [ 'id(sortie_triac)', 'id(increment)' ]
                level: info
            - lambda: |-
                id(affichage_sortie_triac).publish_state( id(sortie_triac) );
                id(affichage_increment).publish_state( id(increment) );

# mode nuit heure pleine on desactive le gradateur
      - if: 
          condition:
            and:
              - binary_sensor.is_on: mode_nuit
              - binary_sensor.is_on : mode_hp
          then:
            - lambda: |-
                id(sortie_triac) = 0;
                id(increment) = 0;
            - light.turn_off:
                id: gradateur
            - output.turn_off: led_green
            - logger.log:
                format: "Log mode nuit heure pleine"
                level: info
# mode nuit heure creuse on active le gradateur
      - if:
          condition:
             and:
              - binary_sensor.is_on: mode_nuit
              - binary_sensor.is_off : mode_hp
              - switch.is_on : modeauto
          then:
            - lambda: |-
                id(sortie_triac) = id(pmax).state;
                id(increment) = 0;
            - light.turn_on:
                id: gradateur
                brightness: !lambda |-
                  return id(sortie_triac)/100 ;
            - output.turn_on: led_green
            - logger.log:
                format: "Log mode nuit heure creuse sortie triac %f"
                args: [ 'id(sortie_triac)']
                level: info
# il y a un probleme on desactive le gradateur
      - if: 
          condition:
            or:
               - binary_sensor.is_off : temperature_triac_ok
               - switch.is_off: modeauto
          then:
            - lambda: |-
                id(sortie_triac) = 0;
                id(increment) = 0;
            - light.turn_off:
                id: gradateur
            - output.turn_off: led_green
            - logger.log:
                format: "Log mode manuel ou surchauffe"
                level: info
      - lambda: |-
          id(affichage_sortie_triac).publish_state( id(sortie_triac) );
          id(affichage_increment).publish_state( id(increment) );

A noter que j'ai complètement détourné ce que faisait REM81 :

  • La LED VERTE GPIO32 est allumée quand le dimmer est actif.
  • La LED ROUGE GPIO 25 est allumée il y a surchauffe

Le boitier

Je me suis aussi forcé à faire un boitier en impression 3d (perfectible lui aussi, je ne suis pas dessinateur projeteur) ça à le mérite de rentrer dans la boîte.

Les plans sont bien entendu, eux aussi, disponibles sur le github.

Les trous pour les leds ne sont pas fait pour la simple et bonne raison que je vous laisse le choix du positionnement coté connectique ou à l'opposé.

Une fois les tests effectués, fermer le boitier avec de la colle

Rendu de l'installation :

Raccordement

⚠️
ATTENTION
Quand vous intervenez dans votre tableau électrique BIEN COUPER le différentiel d'abonné, aussi appelé disjoncteur principal.

Le routeur va en sortie du disjoncteur de 20A qui sert à protéger le chauffe-eau en remplacement du commutateur jour nuit , comme je n'utilise plus le commutateur HP/HC je l'ai laissé dans mon tableau, mais je l'ai déconnecté et je l'ai remplacé par le routeur.

La sonde de courant doit être installée sur la phase en sortie du disjoncteur général. Attention, elle a un sens.

La valeur du sensor "sens_pince" doit être à 0 quand on est consommateur. Si ce sensor est égal à 1 alors que la production solaire ne couvre pas la totalité de la consommation de la maison, alors retournez la pince.

Le bornier P12 (télé-information) doit être raccordé au compteur linky sur la sortie télé-information.

Les sondes de courant sont à installer sur le fil de phase sortant du disjoncteur du circuit que vous souhaitez mesurer.

Et dans HA alors ?

Le helper

il est indispensable de créer un helper de type boolean

inter_validation_routeur

c'est lui qui définit le mode "vacances"

utility_meter

L'utility_meter qui permettra de définir quelle source d'Energie ( soleil le jour) ou EDF la nuit

    
utility_meter:
  consommation_ecs:
    source: sensor.router_tic_4ct_energie_inject_e_ecs
    cycle: daily
    tariffs:
      - nuit
      - jour

l'automation associée pour basculer entre la charge "solaire" et le complément EDF la nuit.

alias: peak-offpeak-ecs
description: ""
triggers:
  - at: "01:08:00"
    variables:
      tariff: nuit
    trigger: time
  - at: "06:08:00"
    variables:
      tariff: jour
    trigger: time
actions:
  - target:
      entity_id: select.consommation_ecs
    data:
      option: "{{ tariff }}"
    action: select.select_option


Les automations

alias: routeur - mode auto off
description: ""
triggers:
  - trigger: time
    at: "23:59:00"
conditions:
  - condition: or
    conditions:
      - condition: numeric_state
        entity_id: sensor.injection_jour
        above: 2000
      - condition: state
        entity_id: input_boolean.inter_validation_routeur
        state: "off"  
actions:
  - action: switch.turn_off
    metadata: {}
    data: {}
    target:
      entity_id:
        - switch.router_tic_4ct_mode_auto
  - action: notify.pushbullet
    metadata: {}
    data:
      message: mode auto routeur off
mode: single

alias: routeur mode auto on
description: ""
triggers:
  - trigger: state
    entity_id:
      - sun.sun
    from: below_horizon
    to: above_horizon
conditions:
  - condition: state
    entity_id: input_boolean.inter_validation_routeur
    state: "on"
actions:
  - action: switch.turn_on
    metadata: {}
    data: {}
    target:
      entity_id:
        - switch.router_tic_4ct_mode_auto
  - action: notify.pushbullet
    metadata: {}
    data:
      message: mode auto routeur on
mode: single

alias: notification surchauffe routeur
description: ""
triggers:
  - entity_id:
      - binary_sensor.router_tic_4ct_triac_temp_ok
    for:
      hours: 0
      minutes: 0
      seconds: 10
    from: "on"
    to: "off"
    trigger: state
conditions: []
actions:
  - data:
      message: Surchauffe routeur
    action: notify.pushbullet
mode: single


Le dashboard

Le dashboard n'est pas mon fort, mais je vous propose ce que j'ai mis en place.

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - type: gauge
        max: 3000
        name: "Puissance "
        needle: true
        entity: sensor.router_tic_4ct_puissance_ecs
      - type: entities
        entities:
          - entity: sensor.consommation_ecs_jour
            name: solaire
          - entity: sensor.consommation_ecs_nuit
            name: EDF
          - entity: input_boolean.inter_validation_routeur
            name: Validation
        title: consommation
  - type: custom:apexcharts-card
    yaxis:
      - id: first
      - id: second
        opposite: true
    stacked: true
    graph_span: 7d
    update_interval: 15min
    span:
      start: day
      offset: "-6d"
    header:
      show: true
      title: ECS
      show_states: true
      colorize_states: true
    series:
      - entity: sensor.consommation_ecs_jour
        yaxis_id: first
        name: Solaire
        color: blue
        type: column
        group_by:
          duration: 1d
          func: max
      - entity: sensor.consommation_ecs_nuit
        yaxis_id: first
        color: red
        name: EDF
        type: column
        group_by:
          duration: 1d
          func: max
      - entity: sensor.energy_production_today
        yaxis_id: second
        name: forecast
        color: orange
        group_by:
          duration: 1d
          func: avg
title: ECS


type: custom:plotly-graph
entities:
  - entity: sensor.router_tic_4ct_puissance_ecs
  - entity: sensor.injection
  - entity: sensor.router_tic_4ct_energie_inject_e_ecs
hours_to_show: 2
refresh_interval: 10
type: entities
entities:
  - entity: light.router_tic_4ct_dimmer
  - entity: switch.router_tic_4ct_mode_auto
  - entity: binary_sensor.router_tic_4ct_triac_temp_ok
  - entity: binary_sensor.router_tic_4ct_mode_nuit
  - entity: sensor.router_tic_4ct_temperature_triac
  - entity: sensor.router_tic_4ct_puissance_ecs
  - entity: sensor.router_tic_4ct_puissance_reseau
  - entity: sensor.router_tic_4ct_sens_pince
  - entity: sensor.router_tic_4ct_increment
  - entity: number.router_tic_4ct_puissance_max
  - entity: number.router_tic_4ct_temperature_max
  - entity: number.router_tic_4ct_coeff_reactivite

Remerciements

Je tiens à remercier :

  • REM81, @Remy_Crochon, pour m'avoir autorisé à faire du plagiat.
  • Charles Hallard, @Hallard, pour avoir démocratisé la télé-information.
  • F1ATB pour tous les concepts qu'il a mis en place.
  • Le Forum HACF, et les relecteurs des articles
  • Le Profes'Solaire avec sa chaine youtube : www.youtube.com/@LeProfesSolaire
  • Mais aussi Docteur Cyprien, @tedour pour les tous les tutos didactiques relatif a l'ESP mis en place sur sa chaine YouTube.https://www.youtube.com/@docteurcyprien.

Conclusion

J'espère que vous aurez réussi à réaliser ce routeur solaire, ou en tout cas donné l'envie de le faire. N'hésitez pas à faire vos retours et suggestions.