From dfd99ca957e20a5a4316504adc5f6ce72aadc71c Mon Sep 17 00:00:00 2001 From: Tallagrand Thierry Date: Sun, 27 Sep 2026 18:12:32 +0200 Subject: [PATCH] =?UTF-8?q?sensor.evcs=5Fbatterie=5Fprojection=5Fsoir=20:?= =?UTF-8?q?=20volontairement=20=C2=AB=20indetermine=20=C2=BB=20tant=20que?= =?UTF-8?q?=20la=20prevision=20est=20fausse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incident 27/09 : l'agent annoncait « vers 90 % » de sa tete. La prevision Forecast.Solar sous-estime d'environ 50-60 % : on publie des faits (SOC, ecart de prevision), jamais un SOC projete. Tests 61/61. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z --- tests/diagnostics-agent/run.mjs | 20 +++++++++++++++++++- tools/diagnostics-agent.mjs | 30 ++++++++++++++++++++++++++++++ tools/historise-evcs.mjs | 9 ++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/tests/diagnostics-agent/run.mjs b/tests/diagnostics-agent/run.mjs index 8ae3919..e151109 100644 --- a/tests/diagnostics-agent/run.mjs +++ b/tests/diagnostics-agent/run.mjs @@ -2,7 +2,7 @@ // Les cas PV reprennent les gammes du banc OpenClaw (HANDOFF §77) : G3, G4, G5, G7. // node tests/diagnostics-agent/run.mjs import fs from 'node:fs'; -import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, COMPTEURS_IMPORT, COMPTEUR_EDF, MPPT_MODES, ORIGINE_PAR_SOURCE, consoJour, COMPTEURS_CONSO } from '../../tools/diagnostics-agent.mjs'; +import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, COMPTEURS_IMPORT, COMPTEUR_EDF, MPPT_MODES, ORIGINE_PAR_SOURCE, consoJour, COMPTEURS_CONSO, projectionSoir } from '../../tools/diagnostics-agent.mjs'; const LIM = 'voltage_current_limited', ACT = 'mppt_active'; function st({ pv, conso, reseau, soc = 80, courant = 0, edf = 'off', modes }) { @@ -269,6 +269,24 @@ for (const [nom, r, test] of imp) { globalThis.__nConso = cj.length; } +// --- Projection du soir : indetermine tant que la prevision est fausse ----- +{ + const m = new Map([['sensor.victron_battery_soc', { state: '42' }], + ['sensor.energy_production_today_remaining', { state: '6.8' }], ['sensor.evcs_prevision_contre_reel', { state: '63' }]]); + const r = projectionSoir(m); + const pj = [ + ['projection : etat indetermine, JAMAIS un SOC chiffre (incident 27/09 « vers 90 % »)', r, + (x) => x.etat === 'indetermine' && !/\b(9\d|[1-8]\d) ?% au coucher/.test(x.attributs.explication)], + ['projection : l explication donne le SOC actuel et l ecart de prevision', r, + (x) => /42 %/.test(x.attributs.explication) && /63 %/.test(x.attributs.explication)], + ]; + for (const [nom, x, test] of pj) { + const ok = test(x); if (!ok) { ko++; console.log(JSON.stringify(x)); } + console.log(`${ok ? 'OK ' : 'KO '} ${nom}`); + } + globalThis.__nConso = (globalThis.__nConso || 0) + pj.length; +} + const n = (globalThis.__nConso || 0) + cas.length + cel.length + prod.length + charge.length + 1 + bn.length + res.length + imp.length; console.log(`\n${n - ko}/${n}`); process.exit(ko ? 1 : 0); diff --git a/tools/diagnostics-agent.mjs b/tools/diagnostics-agent.mjs index 51d51e5..bfc4926 100644 --- a/tools/diagnostics-agent.mjs +++ b/tools/diagnostics-agent.mjs @@ -519,3 +519,33 @@ export function consoJour(courant, minuit) { explication: `${total} kWh consommes depuis minuit (maison et borne), dont ` + `${detail.depuis_reseau} depuis le reseau.` } }; } + +// --------------------------------------------------------------------- +// Projection du SOC au coucher du soleil — VOLONTAIREMENT « indetermine » +// --------------------------------------------------------------------- +// 27/09/2026 15:56 : a « a combien sera la batterie quand le solaire declinera ? », +// l'agent a lu le reste a produire (6,8 kWh) et le SOC (42 %) et a annonce « vers +// 90 % » de sa tete — sans consommation, sans capacite, sans vehicule. +// Un calcul honnete existe (profil de conso mesure + courbe energie/SOC du +// bilan), mais sa donnee d'entree, la prevision Forecast.Solar, sous-estime de +// ~63 % sur 7 jours (sensor.evcs_prevision_contre_reel). Decision du +// proprietaire : publier « indetermine » tant que la prevision n'est pas +// corrigee, plutot qu'un chiffre faux. Seuls des FAITS sont donnes en attributs. +export function projectionSoir(st) { + const soc = nombre(st, 'sensor.victron_battery_soc'); + const reste = nombre(st, 'sensor.energy_production_today_remaining'); + const ecart = nombre(st, 'sensor.evcs_prevision_contre_reel'); + return { + etat: 'indetermine', + attributs: { + statut: 'indetermine', + soc_actuel_pct: soc, + reste_solaire_prevu_kwh: reste, + reste_solaire_source: 'Forecast.Solar, non corrige', + ecart_prevision_7j_pct: ecart, + explication: 'Je ne peux pas estimer la batterie de ce soir : la prevision solaire ' + + `sous-estime la production${ecart !== null ? ` d'environ ${Math.round(ecart)} % ces sept derniers jours` : ''}, ` + + `et tout chiffre serait faux.${soc !== null ? ` La batterie est a ${Math.round(soc)} % en ce moment.` : ''}`, + }, + }; +} diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index 56798b0..33bf0cb 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -30,7 +30,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import pg from 'pg'; import { texteRapport, envoyer, donneesNuit, texteDepuis } from './rapport-nuit.mjs'; -import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, minuitLocal, COMPTEURS_IMPORT, COMPTEUR_EDF, consoJour, COMPTEURS_CONSO } from './diagnostics-agent.mjs'; +import { diagnosticPv, etatCellules, productionJour, chargeSolaire, bilanNuit, etatReseau, importJour, minuitLocal, COMPTEURS_IMPORT, COMPTEUR_EDF, consoJour, COMPTEURS_CONSO, projectionSoir } from './diagnostics-agent.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const ONCE = process.argv.includes('--once'); @@ -1862,6 +1862,13 @@ async function publierDiagnosticPv(st) { await publierImportJour(st).catch((e) => { console.error(`import du jour : ${e.message}`); return 0; }); + // Projection du SOC au coucher du soleil : « indetermine » tant que la + // prevision solaire n'est pas corrigee (decision du proprietaire, 27/09). + const pj = projectionSoir(st); + await publierCapteur('sensor.evcs_batterie_projection_soir', pj.etat, { + friendly_name: 'EVCS — batterie attendue au coucher du soleil', icon: 'mdi:battery-clock', + ...pj.attributs, calcule_a: new Date().toISOString(), + }); // Raccordement reseau : contacteur ouvert = import nul, pas une panne (26/09). const rr = etatReseau(st); await publierCapteur('sensor.evcs_reseau', rr.etat, {