# Detection du silence du service d'historisation (10.10.10.22) # --------------------------------------------------------------------------- # Pose le 16/09/2026, sur validation du proprietaire. # # POURQUOI CE PAQUET EXISTE. Jusqu'ici, un cycle du service en echec n'ecrivait # que sur console.error — le journal du conteneur, que personne ne lit — et # l'incident n'etait journalise qu'au RETOUR a la normale, dans PostgreSQL, # c'est-a-dire jamais quand c'est PostgreSQL qui est tombe. Base injoignable : # plus de mesures, plus d'etiquetage, et le rapport de nuit de 06:47 ne partait # pas, sans qu'un mot ne le dise. # # ⚠️ POURQUOI UN CAPTEUR DEDIE ET PAS LA FRAICHEUR D'UNE ENTITE METIER. # Mesure du 16/09 : Home Assistant ne materialise PAS une ecriture identique, et # `last_reported` — le champ introduit en 2024 pour marquer chaque remontee # d'etat — est rigoureusement EGAL a `last_changed`. Aucun champ de l'API ne # distingue donc un ecrivain mort d'un ecrivain qui republie la meme valeur. # Releve le meme jour sur une installation parfaitement saine : # vehicle_connected inchange depuis 752 min, night_target_soc depuis 194, # source_mode depuis 139 — trois faux positifs sur cinq entites. # La vivacite se lit donc sur le battement SEUL, dont l'etat est un horodatage : # il change forcement a chaque ecriture. Les entites metier se lisent pour leur # VALEUR, jamais pour leur fraicheur. # # La source `sensor.evcs_historisation_heartbeat` est creee par l'API a chaque # cycle : elle disparait au redemarrage de Home Assistant et se recree dans la # minute. D'ou le `for:` obligatoire sur toute automatisation (voir plus bas). template: - sensor: - name: "Historisation EVCS — âge du battement" unique_id: evcs_historisation_age_min unit_of_measurement: "min" state: > {% set t = states('sensor.evcs_historisation_heartbeat') %} {% if t not in ['unknown','unavailable',''] %} {{ ((as_timestamp(now()) - as_timestamp(t, 0)) / 60) | round(0) }} {% else %}unknown{% endif %} - binary_sensor: # Le service tourne toutes les 60 s. Cinq minutes, c'est cinq cycles # manques : assez long pour ignorer un hoquet, assez court pour qu'une # panne de nuit ne coute pas une journee de mesures. # # Deux pannes distinctes, un seul drapeau : # - MUET : le service ne publie plus du tout (processus mort, # conteneur arrete, NAS eteint, reseau coupe) ; # - DEGRADE : le service vit mais ses cycles echouent — typiquement la # base injoignable. Le battement porte alors la cause. - name: "Historisation EVCS — muette" unique_id: evcs_historisation_muette device_class: problem state: > {% set t = states('sensor.evcs_historisation_heartbeat') %} {% if t in ['unknown','unavailable',''] %}on {% elif state_attr('sensor.evcs_historisation_heartbeat','statut') == 'degrade' %}on {% else %} {{ (as_timestamp(now()) - as_timestamp(t, 0)) > 300 }} {% endif %} attributes: statut: > {% set t = states('sensor.evcs_historisation_heartbeat') %} {% if t in ['unknown','unavailable',''] %}muet {% else %}{{ state_attr('sensor.evcs_historisation_heartbeat','statut') or 'inconnu' }}{% endif %} cause: > {{ state_attr('sensor.evcs_historisation_heartbeat','dernier_echec') or '' }} cycles_en_echec: > {{ state_attr('sensor.evcs_historisation_heartbeat','cycles_en_echec') or 0 }} # ⚠️ TOUTE AUTOMATISATION QUI ALERTE SUR CE DRAPEAU DOIT PORTER # for: "00:05:00" # Le capteur source est cree par l'API : au redemarrage de Home Assistant il # n'existe pas pendant une minute au plus, et le drapeau passe donc a `on` # brievement a chaque redemarrage. Sans le `for:`, chaque redemarrage produirait # une fausse alerte. # # ⚠️ CE QUE CE DETECTEUR NE COUVRE PAS : si 10.10.10.17 tombe, Home Assistant # meurt avec, et ce paquet avec lui. Cette direction-la est couverte dans # l'autre sens par le chien de garde qui tourne sur 10.10.10.22 et surveille # n8n. Chaque machine surveille l'autre, aucune ne se surveille elle-meme ; # aucune des deux briques n'est complete seule.