Détail cellule exposé en 3 helpers HA pour l'agent vocal OpenClaw

Les trames BMS cellule de HA sont mortes depuis le 15/09 ; le relevé série est la
source vivante. Le service publie evcs_cell_spread_max_mv, evcs_cell_weakest_module
et evcs_cell_releve_age_min à chaque cycle (publierCellulesHelpers). L'âge donne à
l'agent la discipline anti-valeur-figée (comme le gel GX). Vérifié en réel :
21 mV, âge 1 min, K220803C30160323. Package evcs_cellules.yaml (hors dépôt, HA).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-25 00:07:28 +02:00
parent d68d8ee34c
commit d64b634a66
2 changed files with 65 additions and 0 deletions

View File

@@ -501,3 +501,25 @@ Regle-cle : bridage NORMAL ⇒ import nul ; substitution ANORMALE ⇒ import ~ c
--- ---
*Document mis a jour le : 24/09/2026 — banc PV 9/9 pour OpenClaw (source de verite + discrimination bridage/vraie baisse/substitution), la source vit dans MANUEL §0* *Document mis a jour le : 24/09/2026 — banc PV 9/9 pour OpenClaw (source de verite + discrimination bridage/vraie baisse/substitution), la source vit dans MANUEL §0*
## 78. Session du 24-25/09/2026 — Détail cellule exposé en helpers HA pour l'agent vocal
Les trames BMS cellule de HA sont mortes depuis le 15/09 (§69) ; le relevé série
(table `cellule`, sur le NAS depuis §75) est la seule source vivante. Pour que
l'agent vocal OpenClaw lise le détail cellule en direct sans taper PostgreSQL, le
service d'historisation publie désormais trois helpers HA à chaque cycle, depuis
le dernier relevé série :
- `input_number.evcs_cell_spread_max_mv` — écart max du pack (mV)
- `input_text.evcs_cell_weakest_module` — code-barres du module le plus sensible
- `input_number.evcs_cell_releve_age_min` — âge du dernier relevé (min)
L'âge permet à l'agent la même discipline que pour le gel GX : frais → il donne
la valeur, vieux → il dit « muet ». `publierCellulesHelpers()` dans
`tools/historise-evcs.mjs` (SQL sur les modules du dernier lot, à ±5 min du
max(ts)), appelée après l'ingestion. Package `evcs_cellules.yaml` (sans
`initial:` — réécrits chaque cycle), rechargé sans redémarrage. Vérifié en réel
le 25/09 : écart 21 mV, âge 1 min, module K220803C30160323 (le plus ancien du
pack 2). SPARK bascule l'agent dessus (fiche + gamme G13 live).
---
*Document mis a jour le : 25/09/2026 — détail cellule publié en helpers HA (source série vivante), agent vocal câblé*

View File

@@ -1792,6 +1792,43 @@ async function ecrireHelperTexte(entite, valeur) {
} catch { /* le helper reste a sa valeur, on reessaiera au prochain cycle */ } } catch { /* le helper reste a sa valeur, on reessaiera au prochain cycle */ }
} }
async function ecrireHelperNombre(entite, valeur) {
if (DRY || valeur === null || valeur === undefined || !Number.isFinite(valeur)) return;
try {
await fetch(`${HA_URL}/api/services/input_number/set_value`, {
method: 'POST',
headers: { Authorization: `Bearer ${HA_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ entity_id: entite, value: valeur }),
signal: AbortSignal.timeout(8000),
});
} catch { /* on reessaiera au prochain cycle */ }
}
// Publie le detail cellule (relevé serie, table `cellule`) dans des helpers HA,
// pour que l'agent vocal OpenClaw le lise sans taper PostgreSQL. Les trames BMS
// cellule de HA sont mortes depuis le 15/09 (§69) : ce relevé serie est la seule
// source vivante. On expose l'ECART max du pack, le module extreme, et l'AGE du
// dernier relevé — c'est l'age qui permet a l'agent d'appliquer la meme
// discipline que pour le gel GX (frais -> il le donne, vieux -> il dit muet).
async function publierCellulesHelpers(client) {
if (DRY || !client) return 0;
const { rows: [r] } = await client.query(
`WITH mx AS (SELECT max(ts) AS t FROM cellule),
recent AS (SELECT DISTINCT ON (module) module, ts FROM cellule
WHERE ts > (SELECT t FROM mx) - interval '5 minutes' ORDER BY module, ts DESC),
spreads AS (SELECT c.module, max(c.mv) - min(c.mv) AS ecart
FROM cellule c JOIN recent rc ON rc.module = c.module AND rc.ts = c.ts
GROUP BY c.module)
SELECT (SELECT max(ecart) FROM spreads) AS spread_max,
(SELECT module FROM spreads ORDER BY ecart DESC NULLS LAST LIMIT 1) AS weakest,
(SELECT round(EXTRACT(EPOCH FROM (now() - t)) / 60)::int FROM mx) AS age_min`);
if (!r || r.age_min === null) return 0; // table cellule vide : rien a publier
await ecrireHelperNombre('input_number.evcs_cell_spread_max_mv', r.spread_max === null ? null : Number(r.spread_max));
await ecrireHelperNombre('input_number.evcs_cell_releve_age_min', Number(r.age_min));
await ecrireHelperTexte('input_text.evcs_cell_weakest_module', r.weakest || 'indisponible');
return 1;
}
async function cycle(client) { async function cycle(client) {
// En blanc, on compte ce qu'on ECRIRAIT. Un essai a blanc qui renvoie // En blanc, on compte ce qu'on ECRIRAIT. Un essai a blanc qui renvoie
// toujours zero ne verifie rien — c'est le piege classique du test qui ne // toujours zero ne verifie rien — c'est le piege classique du test qui ne
@@ -1840,6 +1877,12 @@ async function cycle(client) {
console.error(`ingestion cellules en echec : ${e.message}`); console.error(`ingestion cellules en echec : ${e.message}`);
return 0; return 0;
}), }),
// Publie le detail cellule dans des helpers HA (pour l'agent vocal), APRES
// l'ingestion pour utiliser le relevé qui vient d'arriver.
cellulesHelpers: DRY ? 0 : await publierCellulesHelpers(client).catch((e) => {
console.error(`publication helpers cellule en echec : ${e.message}`);
return 0;
}),
}; };
const total = r.mesures + r.bilan + r.decision + r.prevision + r.evenement + r.session + r.cellules + r.observation + r.etiquette + (r.rapport || 0); const total = r.mesures + r.bilan + r.decision + r.prevision + r.evenement + r.session + r.cellules + r.observation + r.etiquette + (r.rapport || 0);
if (total > 0 || ONCE) { if (total > 0 || ONCE) {