{"id":4189,"date":"2026-01-09T11:18:25","date_gmt":"2026-01-09T10:18:25","guid":{"rendered":"https:\/\/domo.rem81.com\/?p=4189"},"modified":"2026-03-24T09:20:01","modified_gmt":"2026-03-24T08:20:01","slug":"ha-routeur-solaire-photovoltaique-avec-esphome-v2026-hp-hc-calibration","status":"publish","type":"post","link":"https:\/\/domo.rem81.com\/index.php\/2026\/01\/09\/ha-routeur-solaire-photovoltaique-avec-esphome-v2026-hp-hc-calibration\/","title":{"rendered":"HA-Routeur Solaire Photovolta\u00efque avec ESPHome \u2014 V2026"},"content":{"rendered":"\n\n\n\n<h1 class=\"wp-block-heading\">Intro<\/h1>\n\n\n\n<p>Dans un article pr\u00e9c\u00e9dent <a href=\"https:\/\/domo.rem81.com\/index.php\/2025\/06\/02\/routeur-solaire-photovoltaique-avec-esphome-une-solution-diy-pour-optimiser-votre-autoconsommation\/\">https:\/\/domo.rem81.com\/index.php\/2025\/06\/02\/routeur-solaire-photovoltaique-avec-esphome-une-solution-diy-pour-optimiser-votre-autoconsommation\/<\/a> , je d\u00e9crivais une version V2 de mon routeur PV d\u00e9velopp\u00e9 sous ESPHome et totalement int\u00e9gr\u00e9 \u00e0 Home Assistant.<br>Depuis, le routeur a continu\u00e9 d\u2019\u00e9voluer \u201cen production\u201d, avec une contrainte principale : <strong>optimiser l\u2019autoconsommation<\/strong> tout en <strong>prot\u00e9geant les batteries<\/strong>, et en tirant parti d\u2019une logique tarifaire <strong>HP\/HC<\/strong>.<\/p>\n\n\n\n<p>Cette V2026 apporte plusieurs nouveaut\u00e9s structurantes :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Bascule HP\/HC automatique<\/strong> via SNTP (HP 06:00\u201321:59 \/ HC 22:00\u201305:59).<\/li>\n\n\n\n<li>Deux strat\u00e9gies distinctes :\n<ul class=\"wp-block-list\">\n<li><strong>HP<\/strong> : routeur pilot\u00e9 par production PV + SOC + \u00e9tat Victron (Bulk\/Absorption\/Float) + r\u00e9serve batteries.<\/li>\n\n\n\n<li><strong>HC<\/strong> : chauffe ECS pilot\u00e9e par <strong>hyst\u00e9r\u00e9sis sur volume d\u2019eau chaude<\/strong> + \u201ccible\u201d de puissance r\u00e9seau.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Une <strong>r\u00e9gulation unique<\/strong> (HP + HC) via <strong>interpolation sur table de puissance<\/strong>.<\/li>\n\n\n\n<li>Un mode <strong>\u00c9talonnage<\/strong> am\u00e9lior\u00e9 pour g\u00e9n\u00e9rer les points <code>{%, W}<\/code> et construire la table.<\/li>\n\n\n\n<li>Une <strong>r\u00e9serve batteries automatique<\/strong> (optionnelle) qui ajuste dynamiquement la r\u00e9serve selon SOC\/PV\/\u00e9tat VE.Bus.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Upgrade:<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>09\/01\/2026 : ajout HP\/HC + hyst\u00e9r\u00e9sis ECS sur volume + r\u00e9gulation par interpolation + r\u00e9serve batteries auto.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Pourquoi un Routeur Solaire ?<\/h1>\n\n\n\n<p>Depuis l\u2019installation de mes panneaux photovolta\u00efques, je cherchais une solution pour maximiser l\u2019utilisation de l\u2019\u00e9nergie produite, plut\u00f4t que de l\u2019injecter dans le r\u00e9seau \u00e0 un tarif peu int\u00e9ressant.<br>L\u2019objectif reste le m\u00eame : <strong>rediriger le surplus PV vers l\u2019ECS<\/strong>, mais avec une logique plus fine, tenant compte :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>du contexte Victron (Bulk\/Absorption\/Float),<\/li>\n\n\n\n<li>de la charge\/d\u00e9charge batterie,<\/li>\n\n\n\n<li>du SOC,<\/li>\n\n\n\n<li>du tarif HP\/HC,<\/li>\n\n\n\n<li>et du besoin r\u00e9el en ECS (volume disponible).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Configuration Solaire<\/h2>\n\n\n\n<p>La configuration g\u00e9n\u00e9rale ne change pas : production PV, onduleur Victron MultiPlus II + Cerbo GX (MQTT), batteries, et un chauffe-eau \u00e9lectrique pilot\u00e9 par gradateur AC.<\/p>\n\n\n\n<p>L\u2019\u00e9volution V2026 porte surtout sur la <strong>strat\u00e9gie de r\u00e9gulation<\/strong>, plus \u201csyst\u00e8me \u00e9nerg\u00e9tique\u201d que simple routeur.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Mat\u00e9riel du Routeur Solaire<\/h2>\n\n\n\n<p>Le mat\u00e9riel reste identique dans l\u2019esprit (ESP32 + dimmer + DS18B20 + LCD I2C + relais + LEDs).<br>Le c\u00e2blage GPIO est clairement pos\u00e9 via substitutions :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DS18B20 : GPIO27<\/li>\n\n\n\n<li>I2C LCD : GPIO21\/22<\/li>\n\n\n\n<li>Dimmer : GPIO33 (gate) + GPIO34 (ZC)<\/li>\n\n\n\n<li>LED jaune : GPIO26 \/ LED rouge : GPIO25<\/li>\n\n\n\n<li>Relais : GPIO5<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Programme ESPHome : une r\u00e9gulation \u201cintelligente\u201d V2026<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Bascule HP\/HC via SNTP<\/h2>\n\n\n\n<p>Toutes les minutes, l\u2019ESP calcule l\u2019heure et met \u00e0 jour <code>is_hp_hours<\/code> :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>HP : 06:00 \u2192 21:59<\/li>\n\n\n\n<li>HC : 22:00 \u2192 05:59<\/li>\n<\/ul>\n\n\n\n<p>Ensuite, toutes les secondes :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>si HP \u2192 <code>calcul_injection_hp<\/code><\/li>\n\n\n\n<li>sinon \u2192 <code>calcul_injection_hc<\/code><\/li>\n<\/ul>\n\n\n\n<p>Ce point est central : on garde une boucle tr\u00e8s r\u00e9active (1s), mais la strat\u00e9gie change totalement selon le tarif.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Les quatre modes de fonctionnement<\/h2>\n\n\n\n<p>Comme en V2, on conserve un <code>select<\/code> Home Assistant :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Auto<\/strong> : routeur autonome (HP\/HC + s\u00e9curit\u00e9s)<\/li>\n\n\n\n<li><strong>Manu<\/strong> : triac forc\u00e9 via <code>ctriac_manu<\/code> (0\u2013100%)<\/li>\n\n\n\n<li><strong>Arr\u00eat<\/strong> : triac OFF<\/li>\n\n\n\n<li><strong>\u00c9talonnage<\/strong> : balayage 0\u2192100% et log des points de puissance<\/li>\n<\/ul>\n\n\n\n<p>\u00c0 chaque changement de mode :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>log envoy\u00e9 \u00e0 HA (<code>notify.log_esp176<\/code>)<\/li>\n\n\n\n<li>triac remis \u00e0 0 + dimmer OFF (s\u00e9curit\u00e9)<\/li>\n\n\n\n<li>en \u00c9talonnage : lancement du script d\u00e9di\u00e9<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">S\u00e9curit\u00e9s et contr\u00f4les (HP)<\/h2>\n\n\n\n<p>En HP, la chauffe est autoris\u00e9e uniquement si :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>validrouteur<\/code> est ON<\/li>\n\n\n\n<li>production PV &gt; <code>seuil_prod<\/code><\/li>\n\n\n\n<li>SOC &gt; <code>seuil_soc<\/code> (hyst\u00e9r\u00e9sis 2%)<\/li>\n\n\n\n<li>temp\u00e9rature dissipateur triac &lt; (<code>tmax - 2<\/code>)<\/li>\n<\/ul>\n\n\n\n<p>Si une condition tombe : triac \u00e0 0, dimmer OFF, publication d\u2019un \u00e9tat \u201cOFF\u201d.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Strat\u00e9gie HC : hyst\u00e9r\u00e9sis ECS bas\u00e9e sur le volume<\/h2>\n\n\n\n<p>En HC, la logique change : on ne chauffe pas \u201cjuste parce que c\u2019est HC\u201d.<br>On chauffe parce que <strong>le ballon en a besoin<\/strong>, mesur\u00e9 via un <strong>volume d\u2019eau chaude disponible<\/strong> remont\u00e9 dans HA (ex : <code>sensor.esp139_ecs_ballon_volume_eau_chaude<\/code>).<\/p>\n\n\n\n<p>Deux seuils pilotent l\u2019hyst\u00e9r\u00e9sis :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>SB<\/strong> = <code>Seuil Bas ECS (L)<\/code><\/li>\n\n\n\n<li><strong>SH<\/strong> = <code>Seuil Haut ECS (L)<\/code><\/li>\n<\/ul>\n\n\n\n<p>R\u00e8gles :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>si volume &lt; SB \u2192 <strong>ON forc\u00e9<\/strong><\/li>\n\n\n\n<li>si volume \u2265 SH \u2192 <strong>OFF forc\u00e9<\/strong><\/li>\n\n\n\n<li>si SB \u2264 volume &lt; SH \u2192 <strong>maintien de l\u2019\u00e9tat pr\u00e9c\u00e9dent<\/strong><\/li>\n<\/ul>\n\n\n\n<p>On \u00e9vite ainsi :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>les cycles courts,<\/li>\n\n\n\n<li>les oscillations,<\/li>\n\n\n\n<li>les micro-d\u00e9marrages.<\/li>\n<\/ul>\n\n\n\n<p>S\u00e9curit\u00e9s HC suppl\u00e9mentaires :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>temp\u00e9rature dissipateur OK<\/li>\n\n\n\n<li>et un garde-fou \u201canti-injection\u201d (<code>p_reseau &gt;= -200W<\/code>) avant d\u2019autoriser la chauffe.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Le c\u0153ur commun : <code>regulation_interpolation<\/code> (HP + HC)<\/h2>\n\n\n\n<p>Cette version est plus propre : <strong>un seul script<\/strong> calcule la puissance disponible <code>pdispo<\/code>, puis la convertit en <strong>% triac<\/strong> via une <strong>table de puissance calibr\u00e9e<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Calcul de <code>pdispo<\/code> en HP selon l\u2019\u00e9tat VE.Bus<\/h3>\n\n\n\n<p>L\u2019\u00e9tat Victron (Bulk\/Absorption\/Float) est r\u00e9cup\u00e9r\u00e9 via HA (capteur <code>vebus_inverter_state<\/code>).<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Bulk<\/strong> : on conserve une r\u00e9serve batteries <code>res_pubatt<\/code> avant de chauffer l\u2019ECS<\/li>\n\n\n\n<li><strong>Absorption \/ Float<\/strong> : on int\u00e8gre la puissance batteries pour \u00e9viter de \u201csubir\u201d un Float trop t\u00f4t et mieux valoriser le surplus<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Calcul de <code>pdispo<\/code> en HC avec une \u201ccible r\u00e9seau\u201d<\/h3>\n\n\n\n<p>En HC, tu utilises une logique de plafonnement :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>RESEAU_CIBLE_HC = 6000W<\/code><\/li>\n\n\n\n<li><code>pdispo = cible - conso - clim - pac - batt<\/code><\/li>\n<\/ul>\n\n\n\n<p>Cela revient \u00e0 \u201cremplir\u201d jusqu\u2019\u00e0 la cible, sans d\u00e9passer, en tenant compte de la maison et de l\u2019\u00e9tat batterie.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conversion <code>pdispo (W)<\/code> \u2192 <code>% triac<\/code> via table + interpolation<\/h3>\n\n\n\n<p>Une fois <code>pdispo<\/code> calcul\u00e9e :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>limitation \u00e0 <code>pmax<\/code><\/li>\n\n\n\n<li>conversion en % triac en parcourant <code>table_puissance<\/code> et en interpolant lin\u00e9airement entre deux points<\/li>\n<\/ul>\n\n\n\n<p>R\u00e9sultat : r\u00e9gulation plus stable, plus lin\u00e9aire, et surtout <strong>adapt\u00e9e \u00e0 la r\u00e9sistance r\u00e9elle<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Affichage LCD<\/h1>\n\n\n\n<p>L\u2019\u00e9cran LCD 20\u00d74 conserve son r\u00f4le de supervision locale (tr\u00e8s utile en cas de souci r\u00e9seau) :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pr = puissance r\u00e9seau \/ Pe = puissance ECS<\/li>\n\n\n\n<li>Tr = % triac \/ Valid = OK\/NOK<\/li>\n\n\n\n<li>Tp = temp\u00e9rature dissipateur \/ Temp OK = OK\/NOK<\/li>\n\n\n\n<li>Mode : Auto\/Manu\/Arr\u00eat\/\u00c9talonnage<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Int\u00e9gration Home Assistant<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">R\u00e9glages expos\u00e9s<\/h2>\n\n\n\n<p>Cette V2026 expose proprement tous les param\u00e8tres cl\u00e9s :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Seuil Production Val Routeur<\/code><\/li>\n\n\n\n<li><code>Seuil SOC<\/code><\/li>\n\n\n\n<li><code>Reserve Charge Batteries<\/code><\/li>\n\n\n\n<li><code>Auto Reserve Batteries<\/code><\/li>\n\n\n\n<li><code>T Max<\/code><\/li>\n\n\n\n<li><code>Puissance Max Triac<\/code><\/li>\n\n\n\n<li><code>Seuil Bas ECS<\/code> \/ <code>Seuil Haut ECS<\/code><\/li>\n\n\n\n<li><code>Mode_Fonctionnement_routeur<\/code> + <code>Valid Routeur<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Capteurs importants<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>production PV, conso maison, batteries, SOC (via capteurs HA)<\/li>\n\n\n\n<li>volume ECS HA (cl\u00e9 pour la strat\u00e9gie HC)<\/li>\n\n\n\n<li>puissance r\u00e9seau et ECS (template c\u00f4t\u00e9 ESP)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Alimentation de l\u2019ECS<\/h1>\n\n\n\n<p>Le principe \u00e9lectrique ne change pas : le gradateur module l\u2019alimentation de la r\u00e9sistance ECS.<br>La nouveaut\u00e9 V2026 se situe dans l\u2019arbitrage :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>HP<\/strong> : autoconsommation + protection batterie<\/li>\n\n\n\n<li><strong>HC<\/strong> : chauffe \u201cutile\u201d bas\u00e9e sur le volume et plafonn\u00e9e par une cible r\u00e9seau<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Installation et mise en route (V2026)<\/h1>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Assemblage et c\u00e2blage identiques \u00e0 la V2.<\/li>\n\n\n\n<li>Flasher le YAML (IP, secrets Wi-Fi, etc.).<\/li>\n\n\n\n<li>V\u00e9rifier que toutes les entit\u00e9s HA sources existent (Victron + volume ECS).<\/li>\n\n\n\n<li>R\u00e9gler <code>pmax<\/code>, <code>tmax<\/code>, <code>seuil_soc<\/code>, <code>seuil_prod<\/code>, <code>res_pubatt<\/code>.<\/li>\n\n\n\n<li><strong>Faire l\u2019\u00e9talonnage<\/strong> et construire la table.<\/li>\n\n\n\n<li>Passer en <strong>Auto<\/strong>.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">R\u00e9sultats et perspectives<\/h1>\n\n\n\n<p>Dans la version V2, j\u2019avais d\u00e9j\u00e0 une excellente r\u00e9activit\u00e9 sur la puissance ECS.<br>La V2026 apporte surtout :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>un comportement plus \u201clogique\u201d en HC (chauffe pilot\u00e9e par besoin r\u00e9el),<\/li>\n\n\n\n<li>une meilleure ma\u00eetrise batterie via la r\u00e9serve auto,<\/li>\n\n\n\n<li>une r\u00e9gulation plus propre gr\u00e2ce \u00e0 l\u2019interpolation sur table calibr\u00e9e.<\/li>\n<\/ul>\n\n\n\n<p>Pistes restantes (toujours pertinentes) :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>int\u00e9gration temp\u00e9rature d\u2019eau,<\/li>\n\n\n\n<li>pr\u00e9visions m\u00e9t\u00e9o,<\/li>\n\n\n\n<li>second gradateur (si besoin).<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Tableau de Bord:<\/h1>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"397\" height=\"350\" src=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-1.png\" alt=\"\" class=\"wp-image-4193\" srcset=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-1.png 397w, https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-1-300x264.png 300w\" sizes=\"auto, (max-width: 397px) 100vw, 397px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: markdown\ntitle: CONDITIONS VALID ROUTEUR\ncontent: &gt;-\n  --------------------------------\ud83d\udd25 \u00c9tat Routage ---------------------------\n\n  Status Bus VE: {{\n  states(\"sensor.victron_mqtt_c0619ab1db0d_vebus_276_vebus_inverter_state\") }} \n\n  Switch Validation Routeur: {% if\n  is_state('switch.esp176_routeur_valid_routeur', 'on') %} \u2705 OK {% else %} \u274c NOK\n  {% endif %}\n\n  Pu Prod &gt; Seuil Production: {% if\n  is_state('binary_sensor.esp176_routeur_seuil_prod_ok', 'on') %} \u2705 OK {% else\n  %} \u274c NOK {% endif %}\n\n  Seuil SOC: {% if is_state('binary_sensor.esp176_routeur_seuil_soc_ok', 'on')\n  %} \u2705 OK {% else %} \u274c NOK {% endif %}\n\n  Temp Triac &lt; Tmax: {% if is_state('binary_sensor.esp176_routeur_temp_ok',\n  'on') %} \u2705 OK {% else %} \u274c NOK {% endif %}\n\n  Mode Routeur: {{ states(\"select.esp176_routeur_mode_fonctionnement_routeur\")\n  }}\n\n  Mode de R\u00e9gulation: {{ states(\"sensor.esp176_routeur_mode_regulation\") }}\n\n  ----------------------------------\ud83d\udd25 \u00c9tat ECS --------------------------------\n\n  S Bas : {{ states(\"number.esp176_routeur_seuil_bas_ecs_l\") }} L&lt;Volume ECS: {{\n  states(\"sensor.esp139_ecs_ballon_volume_eau_chaude\") | float(0) | round(1) }}\n  L&lt;S Haut:{{ states(\"number.esp176_routeur_seuil_haut_ecs_l\") }} L{% set vol =\n  states(\"sensor.esp139_ecs_ballon_volume_eau_chaude\") | float(0) %} {% set bas\n  = states(\"number.esp176_routeur_seuil_bas_ecs_l\") | float(0) %}{% set haut =\n  states(\"number.esp176_routeur_seuil_haut_ecs_l\") | float(0) %}\n\n  {% if vol &lt; bas %} \ud83d\udfe5 Vol Bas \u2192 chauffe demand\u00e9e {% elif vol &gt;= haut %} \ud83d\udfe9 Vol\n  Haut \u2192 chauffe coup\u00e9e {% else %} \ud83d\udfe8 ECS en plage neutre {% endif %}\n\n  Puissance R\u00e9seau: {{\n  states(\"sensor.victron_mqtt_c0619ab1db0d_grid_31_grid_power\") | float(0) |\n  round(0) }} W - S Triac: {{ states(\"sensor.esp176_routeur_sortie_triac\") |\n  float(0) | round(1) }} %\n\n  Derni\u00e8re mise \u00e0 jour : {{ now().strftime('%H:%M:%S') }}\ngrid_options:\n  columns: 12\n  rows: 6\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"408\" height=\"280\" src=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-2.png\" alt=\"\" class=\"wp-image-4194\" srcset=\"https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-2.png 408w, https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-2-300x206.png 300w, https:\/\/domo.rem81.com\/wp-content\/uploads\/2026\/01\/image-2-135x93.png 135w\" sizes=\"auto, (max-width: 408px) 100vw, 408px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>type: picture-elements\nelements:\n  - entity: sensor.esp176_routeur_mode_regulation\n    prefix: \"Regul \"\n    style:\n      background: null\n      color: white\n      font-size: 120%\n      left: 5%\n      top: 0%\n      transform: none\n    type: state-label\n  - entity: sensor.victron_mqtt_c0619ab1db0d_vebus_276_vebus_inverter_state\n    prefix: \"Bus Ve= \"\n    style:\n      background: null\n      color: white\n      font-size: 120%\n      left: 5%\n      top: 10%\n      transform: none\n    type: state-label\n  - entity: sensor.esp176_esp32_routeur_1r_pu_disponible\n    style:\n      background: none\n      color: white\n      font-size: 100%\n      left: 47%\n      top: 31%\n      transform: none\n    type: state-label\n    prefix: \"P= \"\n  - entity: sensor.ecocompteur_pac\n    prefix: \"PAC= \"\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 0%\n      top: 56%\n      transform: none\n    type: state-label\n  - entity: sensor.ecocompteur_clim\n    prefix: \"Clim= \"\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 0%\n      top: 46%\n      transform: none\n    type: state-label\n  - entity: sensor.esp176_routeur_sortie_triac\n    prefix: \"\"\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 82%\n      top: 31%\n      transform: none\n    type: state-label\n  - entity: sensor.esp176_esp32_routeur_1r_p_ecs_jsymk\n    prefix: \"ECS: \"\n    type: state-label\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 47%\n      top: 48%\n      transform: none\n  - entity: sensor.mp2_production_solaire_totale\n    prefix: \"Prod= \"\n    type: state-label\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 0%\n      top: 26%\n      transform: none\n  - entity: sensor.victron_mqtt_c0619ab1db0d_vebus_276_vebus_inverter_output_power_l1\n    prefix: \"Mais= \"\n    type: state-label\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 0%\n      top: 36%\n      transform: none\n  - entity: sensor.esp176_routeur_temp_triac\n    prefix: \"T\u00b0 Triac= \"\n    type: state-label\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 50%\n      top: 60%\n      transform: none\n  - entity: sensor.esp139_ecs_ballon_volume_eau_chaude\n    prefix: \"Vol ECS= \"\n    type: state-label\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 50%\n      top: 70%\n      transform: none\n  - entity: sensor.esp176_esp32_routeur_1r_cons_batt_en_cours\n    prefix: \"Bat= \"\n    style:\n      background: null\n      color: white\n      font-size: 100%\n      left: 0%\n      top: 66%\n      transform: none\n    type: state-label\nimage: \/local\/images\/pid_routeur_v4.png\ngrid_options:\n  columns: 12\n  rows: 5\n<\/code><\/pre>\n\n\n\n<p>Le fichier \u00ab\u00a0pid_routeur_v4.png\u00a0\u00bb est t\u00e9l\u00e9chargeable ici: <a href=\"https:\/\/github.com\/remycrochon\/domo.rem81\/blob\/main\/pid_routeur_v4.png\">domo.rem81\/pid_routeur_v4.png at main \u00b7 remycrochon\/domo.rem81<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion<\/h1>\n\n\n\n<p>Cette V2026 n\u2019est plus seulement un routeur PV \u201canti-injection\u201d.<br>C\u2019est un <strong>contr\u00f4leur \u00e9nerg\u00e9tique<\/strong> :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>conscient du tarif HP\/HC,<\/li>\n\n\n\n<li>conscient de l\u2019\u00e9tat Victron (Bulk\/Abs\/Float),<\/li>\n\n\n\n<li>conscient du besoin ECS via volume,<\/li>\n\n\n\n<li>et qui module le triac avec une conversion <strong>W \u2192 %<\/strong> fid\u00e8le gr\u00e2ce \u00e0 l\u2019\u00e9talonnage.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Liens vers des routeurs \u00e0 monter soi-m\u00eame:<\/h1>\n\n\n\n<p>Bien entendu cette liste est non exhaustive, si vous avez d&rsquo;autres liens, n&rsquo;h\u00e9siter pas \u00e0 me les transmettre, je les ajouterai.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.hacf.fr\/realisation-routeur-solaire\/\" data-type=\"link\" data-id=\"https:\/\/www.hacf.fr\/realisation-routeur-solaire\/\" target=\"_blank\" rel=\"noreferrer noopener\">Routeur ESPHome Complet<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/sites.google.com\/view\/le-professolaire\/routeur-professolaire\" data-type=\"URL\" data-id=\"https:\/\/sites.google.com\/view\/le-professolaire\/routeur-professolaire\" target=\"_blank\">Le Prof&rsquo;Solaire<\/a>. <\/li>\n\n\n\n<li><a href=\"https:\/\/ptiwatt.kyna.eu\/post\/2022\/07\/09\/Construction-d-un-Power-Router-pas-%C3%A0-pas\" data-type=\"URL\" data-id=\"https:\/\/ptiwatt.kyna.eu\/post\/2022\/07\/09\/Construction-d-un-Power-Router-pas-%C3%A0-pas\" target=\"_blank\" rel=\"noreferrer noopener\">Association P&rsquo;TIWATT<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/pvrouteur.apper-solaire.org\/\" data-type=\"URL\" data-id=\"https:\/\/pvrouteur.apper-solaire.org\/\" target=\"_blank\">Le Routeur\/Dimmer de Cyril<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/bidouilleurs\/routeur_solaire\" data-type=\"URL\" data-id=\"https:\/\/github.com\/bidouilleurs\/routeur_solaire\" target=\"_blank\" rel=\"noreferrer noopener\">Les bidouilleurs Solaire<\/a><\/li>\n\n\n\n<li><a href=\"http:\/\/pvrouteur.free.fr\/\" data-type=\"URL\" data-id=\"http:\/\/pvrouteur.free.fr\/\" target=\"_blank\" rel=\"noreferrer noopener\">pvrouteur pour les nuls<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/f1atb.fr\/fr\/realisation-dun-routeur-photovoltaique-multi-sources-multi-modes-et-modulaire\/\" target=\"_blank\" rel=\"noopener\" title=\"\">F1ATB<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/Jetblack31\/MaxPV\" data-type=\"URL\" data-id=\"https:\/\/github.com\/Jetblack31\/MaxPV\" target=\"_blank\" rel=\"noreferrer noopener\">MaxPv<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/mk2pvrouter.co.uk\/shop.html\" data-type=\"URL\" data-id=\"https:\/\/mk2pvrouter.co.uk\/shop.html\" target=\"_blank\">Mk2PVrouter<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/xavierberger.github.io\/Solar-Router-for-ESPHome\/\">https:\/\/xavierberger.github.io\/Solar-Router-for-ESPHome\/<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/yasolr.carbou.me\/\">https:\/\/yasolr.carbou.me\/<\/a><\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Annexes<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Calibration : pourquoi et comment<\/h2>\n\n\n\n<p>Le triac (commande en %) n\u2019est <strong>pas lin\u00e9aire<\/strong> en puissance. Sans calibration :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>zones mortes,<\/li>\n\n\n\n<li>saturation,<\/li>\n\n\n\n<li>oscillations.<\/li>\n<\/ul>\n\n\n\n<p>Le but est de construire une table r\u00e9elle :<br><strong>% triac \u2192 W ECS<\/strong>, puis d\u2019utiliser l\u2019inverse : <strong>W disponible \u2192 % triac<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Mode \u00c9talonnage : g\u00e9n\u00e9ration des points <code>{%, W}<\/code><\/h2>\n\n\n\n<p>Quand on passe en mode <strong>\u00c9talonnage<\/strong>, le script :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>incr\u00e9mente <code>striac<\/code> de 1% jusqu\u2019\u00e0 100%<\/li>\n\n\n\n<li>applique le dimmer<\/li>\n\n\n\n<li>attend 20 secondes (stabilisation)<\/li>\n\n\n\n<li>lit la puissance ECS<\/li>\n\n\n\n<li>envoie un message <code>{%,W}<\/code> \u00e0 HA via <code>notify.etalonnage_routeur<\/code><\/li>\n<\/ul>\n\n\n\n<p>Extrait (principe) :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>########################################################################\n  # Mode Etalonnage Increment S Triac\n  ########################################################################\n  - id: etalonnage_striac\n    mode: restart  \n    then:\n      - lambda: |-\n          id(striac) = 0.0;\n      - while:\n          condition:\n            lambda: 'return id(striac) &lt; 100.0;'  # S'arr\u00eate apr\u00e8s striac = 100\n          then:\n            - lambda: |-\n                id(striac) += 1.0; \/\/ Incr\u00e9mente striac\n                ESP_LOGI(\"striac\", \"Valeur striac: %.2f\", id(striac));\n            - light.turn_on:\n                id: gradateur\n                brightness: !lambda 'return id(striac) \/ 100.0;'  # Normalise entre 0.0 et 1.0\n\n            - delay: 20s  # Temporisation\n\n            - lambda: |-\n                ESP_LOGI(\"striac\", \"Valeur striac: %.2f Pu ECS %.0f\", id(striac), id(puecs).state);            \n\n            - lambda: |-\n                std::string mess = \"{\";\n                mess += std::to_string(id(striac)) + \",\";\n                mess += std::to_string(id(puecs).state)+\"}\";\n                ESP_LOGI(\"fichier\", \"Message: %s\", mess.c_str());\n                id(_log_etalonnage).execute(mess);\n\n      - lambda: |-\n          ESP_LOGI(\"striac\", \"Fin de l'\u00e9talonnage, striac = %.2f\", id(striac));<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Construction de la table de puissance (<code>table_pu.yaml<\/code>)<\/h2>\n\n\n\n<p>Bonnes pratiques :<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Toujours commencer par :<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>0% \u2192 0W<\/li>\n<\/ul>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>Table monotone :<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>W doit cro\u00eetre avec %<\/li>\n<\/ul>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Temporisation entre chaque point :<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>laisse le temps \u00e0 la puissance de se stabiliser<\/li>\n<\/ul>\n\n\n\n<p>Ensuite, ton script d\u2019interpolation fait le reste : pdispo (W) devient un % triac stable.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Code ESPHome<\/h2>\n\n\n\n<p>Le code complet (YAML principal + includes) est disponible ici: <a href=\"https:\/\/github.com\/remycrochon\/home-assistant\/tree\/master\/esphome\" target=\"_blank\" rel=\"noreferrer noopener\">home-assistant\/esphome at master \u00b7 remycrochon\/home-assistant<\/a><\/p>\n\n\n\n<p>T\u00e9l\u00e9charger le code de l&rsquo;ESP 176 et les includes ESP176.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Affichage Status bus VE\n# 0=Off;1=Low Power;2=Fault;3=Bulk;4=Absorption;5=Float;6=Storage;\n# 7=Equalize;8=Passthru;9=Inverting;10=Power assist;11=Power supply;252=External control\n\nsubstitutions:\n  device_name: \"esp176-routeur\"\n  friendly_name: esp176\n  adress_ip: \"192.168.0.176\"\n  time_timezone: \"Europe\/Paris\"\n  # Affectation des GPIO\n  GPIO_onewire: \"GPIO27\"\n  GPIO_sda: \"GPIO21\"\n  GPIO_scl: \"GPIO22\"\n  GPIO_tx: \"GPIO17\"\n  GPIO_rx: \"GPIO16\"  \n  GPIO_Led_jaune: \"GPIO26\"\n  GPIO_Led_rouge: \"GPIO25\"\n  GPIO_Led_status: \"GPIO32\"\n  GPIO_Relais: \"GPIO5\"\n  # Dimmer\n  GPIO_Gate_pin: \"GPIO33\"\n  GPIO_ZC_pin: \"GPIO34\"\n\npackages:\n  jsk: !include pack_esp176\/jsk.yaml\n  table_pu: !include pack_esp176\/table_pu.yaml\n\nesphome:\n  name: ${device_name}\n  on_boot:\n    priority: -100\n    # Force mode auto et tempok au demarrage\n    then: \n      - binary_sensor.template.publish:\n          id: temperatureok\n          state: ON\n\nesp32:\n  board: esp32dev\n  framework:\n    type: arduino\n    \nwifi:\n  networks:\n    - ssid: !secret wifi_esp\n      password: !secret mdpwifi_esp\n  reboot_timeout: 5min\n  min_auth_mode: WPA2\n  manual_ip:\n    static_ip: ${adress_ip}\n    gateway: 192.168.0.254\n    subnet: 255.255.255.0\n    dns1: !secret dns1\n    dns2: !secret dns2\n    \n    \n# Utilisez la LED bleue de l'appareil comme LED d'\u00e9tat, qui clignotera s'il y a des avertissements (lent) ou des erreurs (rapide)\nstatus_led:\n  pin:\n    number: ${GPIO_Led_status} # led jaune\n    inverted: true\n\n# Enable logging\nlogger:\n  baud_rate: 0\n  level: info\n\n# Enable Home Assistant API\napi:\n\nota:\n  platform: esphome\n\nweb_server:\n  port: 80\n  version: 3\n\ntime:\n  - platform: sntp\n    id: sntp_time\n    timezone: Europe\/Paris\n    servers:\n      - 0.pool.ntp.org\n      - 1.pool.ntp.org\n      - 2.pool.ntp.org  \n    on_time:\n      # Mise \u00e0 jour toutes les minutes du mode HP\/HC\n      - seconds: 0\n        minutes: \/1\n        then:\n          - lambda: |-\n              auto now = id(sntp_time).now();\n              if (!now.is_valid()) {\n                return;\n              }\n              int h = now.hour;   \/\/ 0..23\n\n              \/\/ HP : de 06:00 \u00e0 21:59\n              \/\/ HC : de 22:00 \u00e0 05:59\n              bool hp = (h &gt;= 6 &amp;&amp; h &lt; 22);\n              id(is_hp_hours) = hp; \/\/ En production\n              \/\/id(is_hp_hours) = !hp; \/\/ pour tester\n\n              ESP_LOGD(\"horaire\", \"Heure = %02d:%02d \u2192 %s\", h, now.minute, hp ? \"HP\" : \"HC\");\n\n# Protocole I2C\ni2c:\n  sda: ${GPIO_sda}\n  scl: ${GPIO_scl}\n  scan: True\n  id: bus_a\n  frequency: 400kHz\n  \nglobals:\n  - id: p_dispo\n    type: float\n    restore_value: no\n    initial_value: '0'\n\n  - id: regul\n    type: std::string\n    restore_value: no\n    initial_value: '\"Pas de r\u00e9gulation\"'\n\n  - id: striac\n    type: float\n    restore_value: no\n    initial_value: '0'\n\n  - id: hc_chauffe          # m\u00e9morise l'\u00e9tat chauffe HC (hyst\u00e9r\u00e9sis)\n    type: bool\n    restore_value: true\n    initial_value: 'false'\n\n  # \ud83d\udd39 Indique si on est en heures pleines (HP) ou heures creuses (HC)\n  # HP : 06:00 \u2192 22:00  ;  HC : 22:00 \u2192 06:00\n  - id: is_hp_hours\n    type: bool\n    restore_value: no\n    initial_value: 'true'\n\n  # stocke temporairement le message \u00e0 envoyer \u00e0 telegram\n  - id: telegram_msg_buffer\n    type: std::string\n    restore_value: no\n    initial_value: '\"\"'\n\n# Sonde Temperature Dallas\none_wire:\n  - platform: gpio  \n    pin: ${GPIO_onewire}\n\n# d\u00e9claration des modes de fonctionnement dans des \"input select\"\nselect:\n  - platform: template\n    name: \"Mode_Fonctionnement_routeur\"\n    optimistic: true\n    restore_value: true\n    options:\n      - Auto\n      - Manu\n      - Arret\n      - Etalonnage\n    id: _Mode_Fonctionnement_routeur\n    on_value: \n      then:\n        - lambda: |-\n            char mess&#091;128];\n            snprintf(mess, sizeof(mess), \"Mode Fonctionnement Routeur: %s\", id(_Mode_Fonctionnement_routeur).current_option());\n            ESP_LOGI(\"fichier\", \"Message: %s\", mess);\n            id(_log_fichier).execute(mess);  \/\/ Appelle le script _log_fichier avec le param\u00e8tre mess      \n        # Passage en mode \u00e9talonnage\n        - if:\n            condition:\n              - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option() == \"Etalonnage\";'\n            then:\n              - script.execute: etalonnage_striac\n        # Passage en mode Manu on remet \u00e0 Zero le valid Routeur et la consigne Manu\n        - if:\n            condition:\n              - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option()  == \"Manu\";'\n            then:\n              - lambda: |-\n                  id(ctriac_manu).publish_state(0);\n              - switch.turn_off: validrouteur\n        # Passage dans tous les modes on met \u00e0 z\u00e9ro le triac\n        - lambda: |-\n            id(striac) = 0;\n        - light.turn_off:\n            id: gradateur\n        - script.execute: calcul_injection_hp     \n\nbinary_sensor:\n  #Etat de la connection\n  - platform: status\n    name: \"Status\"\n  \n  - platform: template\n    name: \"Temp Ok\"\n    id: temperatureok\n  \n  - platform: template\n    name: \"Seuil Prod Ok\"\n    id: seuil_prod_ok\n\n  - platform: template\n    name: \"Seuil SOC Ok\"\n    id: seuil_soc_ok\n  \n# Input Number\nnumber:\n  # seuil SOC validation routeur\n  - platform: template\n    name: \"Consigne Triac en manu\"\n    id: ctriac_manu\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 100\n    unit_of_measurement: \"%\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n\n  # Max sortie triac\n  - platform: template\n    name: \"Puissance Max Triac\"\n    id: pmax\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 10\n    max_value: 3000\n    unit_of_measurement: \"W\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n\n  # Seuil MAX temperature\n  - platform: template\n    name: \"T Max\"\n    id: tmax\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 75\n    unit_of_measurement: \"C\u00b0\"\n    step: 0.1\n    icon: mdi:arrow-collapse-vertical\n\n  # Consigne R\u00e9gul sur Puissance Batteries en mode Bulk\n  - platform: template\n    name: \"Reserve Charge Batteries\"\n    id: res_pubatt\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 2500\n    unit_of_measurement: \"W\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n\n  # seuil SOC validation routeur\n  - platform: template\n    name: \"Seuil SOC\"\n    id: seuil_soc\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 100\n    unit_of_measurement: \"%\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n\n  # seuil Production Photovoltaique de validation routeur \n  - platform: template\n    name: \"Seuil Production Val Routeur\"\n    id: seuil_prod\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 100\n    max_value: 3000\n    unit_of_measurement: \"W\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n\n  # Simul Vol ECS\n  - platform: template\n    name: \"Simul Vol ECS\"\n    id: volume_ecs_ha_simule\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 200\n    unit_of_measurement: \"L\"\n    step: 1\n    icon: mdi:arrow-collapse-vertical\n    \n  - platform: template\n    name: \"Seuil Bas ECS (L)\"\n    id: seuil_bas_ha\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 200       # ballon 200L \u2192 \u00e0 adapter si besoin\n    unit_of_measurement: \"L\"\n    step: 5\n    icon: mdi:water\n\n  - platform: template\n    name: \"Seuil Haut ECS (L)\"\n    id: seuil_haut_ha\n    optimistic: true\n    restore_value: true\n    mode: box\n    min_value: 0\n    max_value: 200\n    unit_of_measurement: \"L\"\n    step: 5\n    icon: mdi:water\n\nsensor:\n  - platform: wifi_signal # Reports the WiFi signal strength\/RSSI in dB\n    name: \"WiFi Signal dB\"\n    id: wifi_signal_db\n    update_interval: 60s\n    entity_category: \"diagnostic\"\n\n  - platform: copy # Reports the WiFi signal strength in %\n    source_id: wifi_signal_db\n    name: \"WiFi Signal Percent\"\n    filters:\n      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);\n    unit_of_measurement: \"Signal %\"\n    entity_category: \"diagnostic\"\n    device_class: \"\"\n    \n  ############### TEMPLATE  ######################\"\n  # Affichage dans HA et sur l'afficheur\n  # Puissance lue par le JSk- N\u00e9gative en injection\/Positive en soutirage\n  - platform: template\n    name: \"Pu Reseau\"\n    id: pureseau1\n    unit_of_measurement: \"W\"\n    state_class: \"measurement\"\n    accuracy_decimals: 0\n\n  # Sortie triac de 0\u00e0100%\n  - platform: template\n    name: \"Sortie Triac\"\n    id: afstriac\n    unit_of_measurement: \"%\"\n    state_class: \"measurement\"  \n    accuracy_decimals: 2\n\n  # Pu disponible\n  - platform: template\n    name: \"Pu Disponible\"\n    id: afpdispo\n    unit_of_measurement: \"W\"\n    state_class: \"measurement\"\n    accuracy_decimals: 0\n\n  # Sensor Intermediaire pour synoptique\n  - platform: template\n    name: \"Cons batt en Cours\"\n    id: cons_batt_cours\n    state_class: \"measurement\"\n    unit_of_measurement: \"W\"\n    accuracy_decimals: 0\n\n  # Lecture dans HA\n\n  - platform: homeassistant\n    entity_id: sensor.victron_mqtt_c0619ab1db0d_vebus_276_vebus_inverter_output_power_l1\n    id: conso_maison\n    internal: true\n    filters:\n      - sliding_window_moving_average:\n          window_size: 10\n          send_every: 1\n\n  - platform: homeassistant\n    entity_id: sensor.victron_mqtt_c0619ab1db0d_battery_277_battery_power\n    id: pu_batteries\n    internal: true\n    filters:\n      - sliding_window_moving_average:\n          window_size: 10\n          send_every: 1\n \n  - platform: homeassistant\n    entity_id: sensor.mp2_production_solaire_totale\n    id: pu_prod\n    internal: true\n    filters:\n      - sliding_window_moving_average:\n          window_size: 10\n          send_every: 1\n\n  - platform: homeassistant\n    entity_id: sensor.victron_mqtt_c0619ab1db0d_battery_277_battery_soc\n    id: soc\n    internal: true\n    filters:\n      - sliding_window_moving_average:\n          window_size: 10\n          send_every: 1\n\n  - platform: homeassistant\n    entity_id: sensor.ecocompteur_clim\n    id: pu_clim\n    internal: true\n\n  - platform: homeassistant\n    entity_id: sensor.ecocompteur_pac\n    id: pu_pac\n    internal: true\n\n  # Sonde Temperature radiateur\n  - platform: dallas_temp\n    address: 0xeb012112e461b128\n    name: \"Temp triac\"\n    id: temp_triac\n    update_interval: 60s\n    filters:\n      - filter_out: NAN\n\n  # Temp\u00e9rature ECS HA\n  #- platform: homeassistant\n  #  id: temp_ecs_ha9\n  #  entity_id: sensor.esp139_ecs_temp_ecs\n  #  name: \"Temp\u00e9rature ECS HA\"\n\n  # Volume d'eau chaude dispo (en L) mesur\u00e9 par ESP126\n  - platform: homeassistant\n    id: volume_ecs_ha\n    entity_id: sensor.esp139_ecs_ballon_volume_eau_chaude\n    name: \"Volume ECS HA\"\n\n# d\u00e9claration des \"text_sensors\"\ntext_sensor:\n  - platform: template\n    name: \"Mode Regulation\"\n    id: moderegul\n\n  - platform: homeassistant\n    entity_id: sensor.victron_mqtt_c0619ab1db0d_vebus_276_vebus_inverter_state\n    id: etatbus_ve\n    internal: true\n\nswitch:\n  - platform: gpio\n    name: \"Relais\"\n    pin: ${GPIO_Relais}\n    id: relais\n\n  - platform: template\n    name: \"Valid Routeur\"    \n    id: validrouteur\n    optimistic: true\n    restore_mode: always_on\n\n  - platform: template\n    name: \"Auto Reserve Batteries\"\n    id: auto_reserve_batt\n    optimistic: true\n    restore_mode: RESTORE_DEFAULT_ON\n\n  - platform: restart\n    name: \"Restart\"\n\noutput:\n  #LEDS --------------------------------------\n  - id: led_jaune\n    platform: gpio\n    pin: ${GPIO_Led_jaune} \n\n  - id: led_rouge\n    platform: gpio\n    pin: ${GPIO_Led_rouge}\n\n  # Pilotage du Dimmer\n  - platform: ac_dimmer \n    id: ecs\n    gate_pin: ${GPIO_Gate_pin}\n    method: leading\n    zero_cross_pin:\n      number: ${GPIO_ZC_pin} \n      mode:\n        input: true\n      inverted: yes\n    min_power: 5%\n\nlight:\n  - platform: monochromatic\n    name: \"STriac\"\n    output: ecs\n    id: gradateur\n    default_transition_length: 50ms\n\n# Affichage\ndisplay:\n  - platform: lcd_pcf8574\n    dimensions: 20x4\n    address: 0x27\n    update_interval: 20s  # Plus espac\u00e9 pour all\u00e9ger la charge CPU\n    lambda: |-\n      char ligne0&#091;21];\n      char ligne1&#091;21];\n      char ligne2&#091;21];\n      char ligne3&#091;21];\n\n      snprintf(ligne0, sizeof(ligne0), \"Pr=%0.0fW Pe=%0.0fW\", id(pureseau1).state, id(puecs).state);\n      snprintf(ligne1, sizeof(ligne1), \"Tr=%0.1f%% V:%s\", id(striac), id(validrouteur).state ? \"OK\" : \"NOK\");\n      snprintf(ligne2, sizeof(ligne2), \"Tp=%0.1fc E:%s\", id(temp_triac).state, id(temperatureok).state ? \"OK\" : \"NOK\");\n      snprintf(ligne3, sizeof(ligne3), \"Mode:%s\", id(_Mode_Fonctionnement_routeur).current_option());\n\n      it.print(0, 0, ligne0);\n      it.print(0, 1, ligne1);\n      it.print(0, 2, ligne2);\n      it.print(0, 3, ligne3);\n\ninterval:\n  - interval: 1s\n    then:\n      - if:\n          condition:\n            lambda: |-\n              \/\/ HP de 06:00 \u00e0 22:00, sinon HC\n              return id(is_hp_hours);   \/\/ true = HP, false = HC\n          then:\n            - script.execute: calcul_injection_hp\n          else:\n            - script.execute: calcul_injection_hc\n\n  - interval: 5s\n    then:      \n      - script.execute: etat_production\n      - script.execute: calcul_relais_surprod      \n  - interval: 60s\n    then:\n      - script.execute: maj_reserve_batt_auto\n ########################################################################\nscript:\n  ########################################################################\n  # \ud83d\udd39 Script : calcul_injection_hp\n  ########################################################################\n  - id: calcul_injection_hp\n    mode: single\n    then:\n      - lambda: |-\n          \/\/ ============================================================\n          \/\/ \ud83e\udded \u00c9VALUATION DES CONDITIONS DE D\u00c9MARRAGE\n          \/\/ ============================================================\n\n          \/\/ \u2705 V\u00e9rifie si la production solaire est suffisante\n          bool prod_ok = (id(pu_prod).state &gt; id(seuil_prod).state);\n          id(seuil_prod_ok).publish_state(prod_ok);\n\n          \/\/ \u2705 V\u00e9rifie si le SOC est sup\u00e9rieur au seuil (avec hysteresis de 2%)\n          bool soc_ok = false;\n          if (id(soc).state &gt;= id(seuil_soc).state) {\n            soc_ok = true;\n          } else if (id(soc).state &lt; (id(seuil_soc).state - 2)) {\n            soc_ok = false;\n          }\n          id(seuil_soc_ok).publish_state(soc_ok);\n\n          \/\/ \u2705 V\u00e9rifie la temp\u00e9rature du radiateur triac\n          bool temp_ok = (id(temp_triac).state &lt; (id(tmax).state - 2));\n          if (!temp_ok) {\n            id(temperatureok).publish_state(false);\n          } else {\n            id(temperatureok).publish_state(true);\n          }\n\n          ESP_LOGI(\"HP\", \n            \"Check HP \u2192 Prod=%.0fW (Seuil=%.0f) | SOC=%.1f%% (Seuil=%.1f) | TempTriac=%.1f\u00b0C (Tmax=%.1f\u00b0C)\",\n            id(pu_prod).state, id(seuil_prod).state,\n            id(soc).state, id(seuil_soc).state,\n            id(temp_triac).state, id(tmax).state\n          );\n\n      # 1\ufe0f\u20e3 Conditions NOK ou mode Arret \u2192 arr\u00eat complet\n      - if:\n          condition:\n            or:\n              # Cas 1 : Mode Arret\n              - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option() == \"Arret\";'\n              # Cas 2 : Mode Auto mais conditions NOK\n              - and:\n                  - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option() == \"Auto\";'\n                  - or:\n                      - switch.is_off: validrouteur\n                      - binary_sensor.is_off: temperatureok\n                      - binary_sensor.is_off: seuil_prod_ok\n                      - binary_sensor.is_off: seuil_soc_ok\n          then:\n            - lambda: |-\n                id(striac) = 0;\n                id(moderegul).publish_state(\"OFF\");\n                id(afpdispo).publish_state(0);\n                id(cons_batt_cours).publish_state(0);\n                ESP_LOGW(\"HP\", \"\u26a0\ufe0f R\u00e9gulation OFF : conditions NOK ou arr\u00eat manuel.\");\n            - light.turn_off: gradateur\n\n      # 2\ufe0f\u20e3 Mode Auto + toutes conditions OK \u2192 r\u00e9gulation active\n      - if:\n          condition:\n            and:\n              - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option() == \"Auto\";'\n              - switch.is_on: validrouteur\n              - binary_sensor.is_on: seuil_prod_ok\n              - binary_sensor.is_on: temperatureok\n              - binary_sensor.is_on: seuil_soc_ok\n          then:\n            - logger.log:\n                format: \"\u2705 Conditions OK (HP) \u2192 R\u00e9gulation interpolation active\"\n                level: INFO\n            - script.execute: regulation_interpolation\n\n            - light.turn_on:\n                id: gradateur\n                brightness: !lambda |-\n                  return id(striac) \/ 100.0f;\n\n      # 3\ufe0f\u20e3 Mode Manuel \u2192 application directe de la consigne\n      - if:\n          condition:\n            and:\n              - lambda: 'return id(_Mode_Fonctionnement_routeur).current_option() == \"Manu\";'\n              - switch.is_on: validrouteur\n          then:\n            - lambda: |-\n                \/\/ Application directe de la consigne manuelle\n                id(striac) = id(ctriac_manu).state;\n                id(afpdispo).publish_state(0);\n                id(moderegul).publish_state(\"Manu\");\n                id(cons_batt_cours).publish_state(0);\n                ESP_LOGI(\"HP\", \"\ud83e\udde9 Mode MANU \u2192 Triac forc\u00e9 \u00e0 %.1f%%\", id(striac));\n            - light.turn_on:\n                id: gradateur\n                brightness: !lambda |-\n                  return id(striac) \/ 100.0f;\n\n      # Publication de la valeur du triac (affichage + MQTT)\n      - lambda: |-\n          id(afstriac).publish_state(id(striac));\n\n  ########################################################################\n  # \ud83d\udd39 Script : calcul_injection_hc (avec hyst\u00e9r\u00e9sis ECS)\n  ########################################################################\n\n  - id: calcul_injection_hc\n    mode: single\n    then:\n      - script.execute: decision_chauffe_hc\n      - script.execute: apply_chauffe_hc\n\n  - id: decision_chauffe_hc\n    mode: single\n    then:\n      - lambda: |-\n          float vol = id(volume_ecs_ha).state;\n          float sb  = id(seuil_bas_ha).state;\n          float sh  = id(seuil_haut_ha).state;\n          float p_reseau = id(pureseau1).state;\n\n          bool temp_ok = (id(temp_triac).state &lt; (id(tmax).state - 2));\n          id(temperatureok).publish_state(temp_ok);\n\n          bool injection_ok = (p_reseau &gt;= -200);\n\n          ESP_LOGI(\"HC\",\n            \"HC Decision \u2192 Vol=%.0fL (SB=%.0f \/ SH=%.0f) | chauffe=%d | P_R\u00e9seau=%.0fW | TempOK=%d\",\n            vol, sb, sh, id(hc_chauffe), p_reseau, temp_ok\n          );\n\n          \/\/ S\u00e9curit\u00e9s globales\n          if (id(_Mode_Fonctionnement_routeur).current_option() != std::string(\"Auto\") ||\n              !id(validrouteur).state ||\n              !temp_ok ||\n              !injection_ok) {\n\n            id(hc_chauffe) = false;\n            id(moderegul).publish_state(\"HC OFF - S\u00e9curit\u00e9\");\n            ESP_LOGW(\"HC\", \"\u26d4 Chauffe OFF (s\u00e9curit\u00e9)\");\n            return;\n          }\n\n          \/\/ ----- HYST\u00c9R\u00c9SIS ECS -----\n          if (vol &lt; sb) {\n            \/\/ D\u00e9marrage forc\u00e9\n            id(hc_chauffe) = true;\n            id(moderegul).publish_state(\"HC ON - Volume &lt; SB\");\n            ESP_LOGI(\"HC\", \"\ud83d\udd25 Vol %.0f &lt; %.0f \u2192 ON\", vol, sb);\n          }\n          else if (vol &gt;= sh) {\n            \/\/ Arr\u00eat forc\u00e9\n            id(hc_chauffe) = false;\n            id(moderegul).publish_state(\"HC OFF - Volume \u2265 SH\");\n            ESP_LOGI(\"HC\", \"\u2705 Vol %.0f \u2265 %.0f \u2192 OFF\", vol, sh);\n          }\n          else {\n            \/\/ Zone SB\u2013SH \u2192 on conserve l'\u00e9tat pr\u00e9c\u00e9dent\n            if (id(hc_chauffe)) {\n              id(moderegul).publish_state(\"HC ON - Maintien\");\n              ESP_LOGI(\"HC\", \"\u2668\ufe0f Vol %.0f (SB\u2013SH) \u2192 Maintien ON\", vol);\n            } else {\n              id(moderegul).publish_state(\"HC OFF - Attente\");\n              ESP_LOGI(\"HC\", \"\ud83d\udd53 Vol %.0f (SB\u2013SH) \u2192 OFF\", vol);\n            }\n          }\n\n  - id: apply_chauffe_hc\n    mode: single\n    then:\n      - if:\n          condition:\n            lambda: 'return id(hc_chauffe);'\n          then:\n            - script.execute: regulation_interpolation\n            - light.turn_on:\n                id: gradateur\n                brightness: !lambda 'return id(striac) \/ 100.0f;'\n            - lambda: |-\n                id(afstriac).publish_state(id(striac));\n          else:\n            - lambda: |-\n                id(striac) = 0.0f;\n                id(afstriac).publish_state(0);\n                id(afpdispo).publish_state(0);\n            - light.turn_off: gradateur\n  ########################################################################\n  # \ud83d\udd39 Script : regulation_interpolation (commun HP + HC)\n  ########################################################################\n  - id: regulation_interpolation\n    mode: single\n    then:\n      - lambda: |-\n          \/\/ \ud83d\udd39 D\u00e9termination du mode HP \/ HC \u00e0 partir de l'heure\n          bool is_hp = id(is_hp_hours);  \/\/ true = HP (06\u201322h), false = HC (22\u201306h)\n          float pdispo = 0.0f;  \/\/ puissance disponible pour ECS\n\n          \/\/ \ud83d\udd38 Branche HP : r\u00e9gulation sur production PV \/ batteries\n          if (is_hp) {\n            if (id(etatbus_ve).state == \"Bulk\") {  \/\/ Bulk\n              pdispo = id(pu_prod).state - id(conso_maison).state - id(pu_clim).state - id(pu_pac).state - id(res_pubatt).state;\n              if (pdispo &lt; 0.0f) pdispo = 0.0f;\n              id(cons_batt_cours).publish_state(id(res_pubatt).state);\n              id(regul) = \"HP Reg. (Bulk)\";\n            } \n            else if (id(etatbus_ve).state == \"Absorption\" || id(etatbus_ve).state == \"Float\") { \/\/ Absorption \/ Float\n              pdispo = id(pu_prod).state - id(conso_maison).state - id(pu_clim).state - id(pu_pac).state + id(pu_batteries).state;\n              if (pdispo &lt; 0.0f) pdispo = 0.0f;\n              id(cons_batt_cours).publish_state(id(pu_batteries).state * -1);\n              id(regul) = \"HP Reg. (Abs\/Float)\";\n            } \n            else {\n              id(regul) = \"HP Reg. (Bus VE NOK)\";\n              id(cons_batt_cours).publish_state(0);\n              pdispo = 0.0f;\n            }\n\n            ESP_LOGI(\"regul\",\n                     \"HP \u2192 Prod=%.0fW | Conso=%.0fW | Clim=%.0fW | PAC=%.0fW | Batt=%.0fW | p_dispo=%.0fW | Mode=%s\",\n                     id(pu_prod).state,\n                     id(conso_maison).state,\n                     id(pu_clim).state,\n                     id(pu_pac).state,\n                     id(pu_batteries).state,\n                     pdispo,\n                     id(regul).c_str());\n          }\n          \/\/ \ud83d\udd38 Branche HC : calcul pr\u00e9dictif r\u00e9seau (chauffe ECS)\n          else {\n            const float RESEAU_CIBLE_HC = 6000.0f;  \/\/ puissance max vis\u00e9e sur r\u00e9seau\n\n            float conso = id(conso_maison).state;\n            float clim  = id(pu_clim).state;\n            float pac  = id(pu_pac).state;\n            float batt  = id(pu_batteries).state;   \/\/ &gt;0 d\u00e9charge, &lt;0 charge\n\n            if (isnan(conso)) conso = 0;\n            if (isnan(clim))  clim  = 0;\n            if (isnan(pac))  pac  = 0;\n            if (isnan(batt))  batt  = 0;\n\n            \/\/ p_dispo = cible - conso_maison - pu_clim - pu_batteries\n            pdispo = RESEAU_CIBLE_HC - conso - clim - pac - batt;\n            if (pdispo &lt; 0.0f) pdispo = 0.0f;\n\n            id(regul) = \"HC Chauffe ECS\";\n            id(cons_batt_cours).publish_state(batt);\n\n            ESP_LOGI(\"regul\",\n                     \"HC \u2192 Cible=%.0fW | Conso=%.0fW | Clim=%.0fW | PAC=%.0fW| Batt=%.0fW | p_dispo=%.0fW\",\n                     RESEAU_CIBLE_HC, conso, clim, pac, batt, pdispo);\n          }\n\n          \/\/ \ud83d\udd39 Limitation &amp; interpolation table puissance\n          pdispo = constrain(pdispo, 0.0f, id(pmax).state);\n          id(p_dispo) = pdispo;\n\n          float striac_f = 0.0f;\n          for (size_t i = 1; i &lt; id(table_puissance).size(); i++) {\n            float x0 = id(table_puissance)&#091;i-1].first;   \/\/ % Triac\n            float y0 = id(table_puissance)&#091;i-1].second;  \/\/ Puissance\n            float x1 = id(table_puissance)&#091;i].first;\n            float y1 = id(table_puissance)&#091;i].second;\n            if (pdispo &lt;= y1) {\n              \/\/ interpolation lin\u00e9aire\n              striac_f = x0 + (pdispo - y0) * (x1 - x0) \/ (y1 - y0);\n              break;\n            }\n            striac_f = x1;\n          }\n\n          if (isnan(striac_f)) striac_f = 0.0f;\n          id(striac) = constrain(striac_f, 0.0f, 100.0f);\n\n          id(afpdispo).publish_state(id(p_dispo));\n          id(moderegul).publish_state(id(regul));\n\n          ESP_LOGI(\"regul\",\n                   \"%s | p_dispo=%.0fW | STriac=%.1f%% | Mode=%s\",\n                   is_hp ? \"HP\" : \"HC\",\n                   pdispo, id(striac),\n                   id(regul).c_str());\n  ########################################################################\n  # Script : Calcul dynamique de la production PV attribu\u00e9e aux batteries\n  ########################################################################\n  - id: maj_reserve_batt_auto\n    mode: single\n    then:\n      - lambda: |-\n          if (!id(auto_reserve_batt).state) return;\n\n          float soc_val = id(soc).state;\n          float pv  = id(pu_prod).state;\n          std::string st = id(etatbus_ve).state;\n\n          if (isnan(soc_val)) soc_val = 0;\n          if (isnan(pv))  pv  = 0;\n\n          \/\/ Param\u00e8tres (\u00e0 ajuster)\n          const float SOC_LOW   = 70.0f;   \/\/ en dessous \u2192 on charge fort\n          const float SOC_HIGH  = 90.0f;   \/\/ au-dessus \u2192 on privil\u00e9gie ECS\n          const float PV_MIN    = id(seuil_prod).state;  \/\/ en dessous \u2192 pas la peine d'assouplir\n          const float R_MIN     = 50.0f;    \/\/ r\u00e9serve min pour garder une micro-charge\n          const float R_MAX     = 2500.0f; \/\/ r\u00e9serve max \"raisonnable\"\n\n          float r = id(res_pubatt).state; \/\/ valeur actuelle\n\n          \/\/ Cas 1 : PV faible \u2192 r\u00e9serve haute (on prot\u00e8ge la charge batterie)\n          if (pv &lt; PV_MIN) {\n            r = 1200.0f;\n          }\n          else {\n            \/\/ Cas 2 : SOC bas \u2192 r\u00e9serve haute\n            if (soc_val &lt;= SOC_LOW) {\n              r = R_MAX;\n            }\n            \/\/ Cas 3 : SOC haut \u2192 r\u00e9serve basse pour retarder la fin (\u00e9viter FLOAT)\n            else if (soc_val &gt;= SOC_HIGH) {\n              r = R_MIN; \n            }\n            \/\/ Cas 4 : interpolation entre les deux\n            else {\n              float t = (soc_val - SOC_LOW) \/ (SOC_HIGH - SOC_LOW); \/\/ 0..1\n              r = R_MAX + t * (R_MIN - R_MAX);\n            }\n          }\n\n          \/\/ Option : si d\u00e9j\u00e0 en Float, inutile de garder une r\u00e9serve haute\n          \/\/ (vous pouvez au contraire mettre R_MIN pour maximiser ECS)\n          if (st == \"Float\") {\n            r = R_MIN;\n          }\n\n          \/\/ S\u00e9curit\u00e9s\n          if (r &lt; R_MIN) r = R_MIN;\n          if (r &gt; R_MAX) r = R_MAX;\n\n          \/\/ \u00c9vite de spammer HA pour des petites variations\n          float cur = id(res_pubatt).state;\n          if (fabsf(cur - r) &gt;= 50.0f) {\n            id(res_pubatt).publish_state(r);\n            ESP_LOGI(\"RES\", \"Auto reserve: SOC=%.1f PV=%.0fW State=%s -&gt; res_pubatt=%.0fW\",\n                     soc_val, pv, st.c_str(), r);\n          }\n\n\n  ########################################################################\n  # Mode Etalonnage Increment S Triac\n  ########################################################################\n  - id: etalonnage_striac\n    mode: restart  \n    then:\n      - lambda: |-\n          id(striac) = 0.0;\n      - while:\n          condition:\n            lambda: 'return id(striac) &lt; 100.0;'  # S'arr\u00eate apr\u00e8s striac = 100\n          then:\n            - lambda: |-\n                id(striac) += 1.0; \/\/ Incr\u00e9mente striac\n                ESP_LOGI(\"striac\", \"Valeur striac: %.2f\", id(striac));\n            - light.turn_on:\n                id: gradateur\n                brightness: !lambda 'return id(striac) \/ 100.0;'  # Normalise entre 0.0 et 1.0\n\n            - delay: 20s  # Temporisation\n\n            - lambda: |-\n                ESP_LOGI(\"striac\", \"Valeur striac: %.2f Pu ECS %.0f\", id(striac), id(puecs).state);            \n\n            - lambda: |-\n                std::string mess = \"{\";\n                mess += std::to_string(id(striac)) + \",\";\n                mess += std::to_string(id(puecs).state)+\"}\";\n                ESP_LOGI(\"fichier\", \"Message: %s\", mess.c_str());\n                id(_log_etalonnage).execute(mess);\n\n      - lambda: |-\n          ESP_LOGI(\"striac\", \"Fin de l'\u00e9talonnage, striac = %.2f\", id(striac));\n\n  ########################################################################\n  # ------------  Pilotage led\n  ########################################################################\n  - id: etat_production\n    mode: single\n    then:\n      - if: \n          condition:\n            sensor.in_range:\n              id: pureseau1\n              below: -50\n          then:\n            - output.turn_on: led_rouge\n          else:\n            - output.turn_off: led_rouge\n            \n      - if: \n          condition:\n            switch.is_on: validrouteur\n          then:\n            - output.turn_on: led_jaune\n          else:\n            - output.turn_off: led_jaune\n \n  ########################################################################\n  - id: calcul_relais_surprod\n    mode: single\n    then:\n      - if: \n          condition:\n            - lambda: 'return (id(striac)&gt;=90 &amp;&amp; id(puecs).state&lt;10);'\n          then:\n            - delay: 300s\n            - switch.turn_on: relais\n            - logger.log: \"Relais Activ\u00e9\"\n\n      - if: \n          condition:\n            - lambda: 'return id(puecs).state &gt;= 10;'\n          then:\n            - switch.turn_off: relais\n            - logger.log: \"Relais D\u00e9sactiv\u00e9\"\n \n  ########################################################################\n  - id: _log_fichier\n    parameters:\n      mess1: std::string\n    then:\n      - lambda: |-\n          std::string mess = mess1;\n          id(telegram_msg_buffer) = mess;\n          ESP_LOGI(\"log_message\", \"Telegram buffer: %s\", id(telegram_msg_buffer).c_str());\n      - homeassistant.service:                \n          action: notify.send_message\n          data:\n            entity_id: notify.log_esp176\n            message: !lambda 'return id(telegram_msg_buffer).c_str();'\n\n  ########################################################################\n  - id: _log_etalonnage\n    parameters:\n      mess1: std::string\n    then:\n      - lambda: |-\n          std::string mess = mess1;\n          id(telegram_msg_buffer) = mess;\n          ESP_LOGI(\"log_message\", \"Telegram buffer: %s\", id(telegram_msg_buffer).c_str());\n      - homeassistant.service:                \n          action: notify.send_message\n          data:\n            entity_id: notify.etalonnage_routeur\n            message: !lambda 'return id(telegram_msg_buffer).c_str();'\n<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Intro Dans un article pr\u00e9c\u00e9dent https:\/\/domo.rem81.com\/index.php\/2025\/06\/02\/routeur-solaire-photovoltaique-avec-esphome-une-solution-diy-pour-optimiser-votre-autoconsommation\/ , je d\u00e9crivais une version V2 de mon routeur PV d\u00e9velopp\u00e9 sous ESPHome et totalement int\u00e9gr\u00e9 \u00e0 Home Assistant.Depuis, le routeur a continu\u00e9 d\u2019\u00e9voluer &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[15,17,83],"class_list":["post-4189","post","type-post","status-publish","format-standard","hentry","category-homeassistant","tag-esp32","tag-esphome","tag-routeur-solaire"],"_links":{"self":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4189","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/comments?post=4189"}],"version-history":[{"count":9,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4189\/revisions"}],"predecessor-version":[{"id":4204,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/posts\/4189\/revisions\/4204"}],"wp:attachment":[{"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/media?parent=4189"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/categories?post=4189"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/domo.rem81.com\/index.php\/wp-json\/wp\/v2\/tags?post=4189"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}