Chaque transition dit desormais QUI l a provoquee

Le 12/09, le contacteur reseau a bascule six fois en dix minutes. Trouver
le coupable a demande sept requetes enchainees — executions n8n des deux
workflows, liste des ecrivains, puis automations.yaml — parce que la
table des evenements disait QUE ca avait bascule, jamais PAR QUI.

Home Assistant le savait pourtant. Son logbook resout le contexte d un
changement d etat et nomme l auteur. Sur la coupure de 06:29:44 :
  auteur      : automation.victron_grid_off_si_soc_35
  declencheur : triggered by numeric state of sensor.victron_battery_soc

Un appel par transition REELLE — quelques-uns par jour, pas par cycle.
Echec de lecture => pas d auteur, la ligne est ecrite quand meme : on ne
devine jamais.

⚠️ L auteur est colle au RESUME, pas seulement au detail jsonb. C est le
resume que Grafana affiche en annotation sur TOUS les tableaux et dans
les panneaux « journal des evenements », et que le rapport de nuit
reprend. Le mettre la le rend visible partout sans toucher a un seul
tableau.

 remonte l auteur et son declencheur : l enquete de ce
matin tient desormais en UNE commande, et l observateur pourra nommer le
coupable au lieu de dire « un des trois ».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-12 09:12:35 +02:00
parent e675ec434c
commit 9448676cbd
2 changed files with 54 additions and 3 deletions

View File

@@ -155,6 +155,47 @@ const num = (v) => {
};
const txt = (s) => (s ?? '').toString().trim();
// ---------------------------------------------------------------------
// QUI A PROVOQUE CETTE TRANSITION (12/09/2026)
// ---------------------------------------------------------------------
// Le 12/09, le contacteur reseau a bascule six fois en dix minutes. Trouver le
// coupable a demande sept requetes enchainees — executions n8n des deux
// workflows, liste des ecrivains, puis automations.yaml — parce que la table
// des evenements disait QUE ca avait bascule, jamais PAR QUI.
//
// Home Assistant le sait pourtant : son logbook resout le contexte d un
// changement d etat et nomme l automatisation, le script ou l utilisateur qui
// l a declenche. Pour la coupure de 06:29:44 il rend :
// context_entity_id : automation.victron_grid_off_si_soc_35
// context_message : triggered by numeric state of sensor.victron_battery_soc
//
// Un appel par transition reelle — quelques-uns par jour, pas par cycle. En cas
// d echec on n invente rien : pas d auteur, et la ligne est ecrite quand meme.
async function auteurDeLaTransition(entite, horodatageHa) {
try {
const t = Date.parse(horodatageHa);
if (!Number.isFinite(t)) return null;
const debut = new Date(t - 5000).toISOString();
const fin = new Date(t + 5000).toISOString();
const r = await fetch(
`${HA_URL}/api/logbook/${debut}?end_time=${encodeURIComponent(fin)}&entity_id=${encodeURIComponent(entite)}`,
{ headers: { Authorization: `Bearer ${HA_TOKEN}` }, signal: AbortSignal.timeout(8000) },
);
if (!r.ok) return null;
const lignes = await r.json();
const l = (Array.isArray(lignes) ? lignes : []).find((x) => x.entity_id === entite && x.context_name);
if (!l) return null;
return {
auteur: l.context_entity_id || l.context_name,
nom: l.context_name,
type: l.context_domain || l.context_event_type || null,
declencheur: l.context_message || null,
};
} catch {
return null;
}
}
async function etats() {
const r = await fetch(`${HA_URL}/api/states`, {
headers: { Authorization: `Bearer ${HA_TOKEN}` },
@@ -332,9 +373,17 @@ async function detecterEvenements(sql, st) {
const prec = memoire.valeurs.get(id);
if (prec === undefined) { memoire.valeurs.set(id, cur); continue; }
if (prec === cur) continue;
await journaliser(sql, genre, `${libelle} : ${prec} -> ${cur}`, {
const qui = await auteurDeLaTransition(id, e.last_changed);
// L auteur est colle au RESUME, pas seulement au detail : c est le resume
// que Grafana affiche en annotation sur tous les tableaux et dans les
// panneaux « journal des evenements », et que le rapport de nuit reprend.
// Le mettre la le rend visible partout sans toucher a un seul tableau.
const resume = `${libelle} : ${prec} -> ${cur}`
+ (qui ? ` — par ${qui.nom}` : '');
await journaliser(sql, genre, resume, {
entite: id, avant: prec, apres: cur,
horodatage_ha: e.last_changed,
...(qui ? { auteur: qui.auteur, auteur_nom: qui.nom, auteur_type: qui.type, declencheur: qui.declencheur } : {}),
});
memoire.valeurs.set(id, cur);
n++;