sensor.evcs_batterie_projection_soir : volontairement « indetermine » tant que la prevision est fausse
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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
// Les cas PV reprennent les gammes du banc OpenClaw (HANDOFF §77) : G3, G4, G5, G7.
|
// Les cas PV reprennent les gammes du banc OpenClaw (HANDOFF §77) : G3, G4, G5, G7.
|
||||||
// node tests/diagnostics-agent/run.mjs
|
// node tests/diagnostics-agent/run.mjs
|
||||||
import fs from 'node:fs';
|
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';
|
const LIM = 'voltage_current_limited', ACT = 'mppt_active';
|
||||||
function st({ pv, conso, reseau, soc = 80, courant = 0, edf = 'off', modes }) {
|
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;
|
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;
|
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}`);
|
console.log(`\n${n - ko}/${n}`);
|
||||||
process.exit(ko ? 1 : 0);
|
process.exit(ko ? 1 : 0);
|
||||||
|
|||||||
@@ -519,3 +519,33 @@ export function consoJour(courant, minuit) {
|
|||||||
explication: `${total} kWh consommes depuis minuit (maison et borne), dont ` +
|
explication: `${total} kWh consommes depuis minuit (maison et borne), dont ` +
|
||||||
`${detail.depuis_reseau} depuis le reseau.` } };
|
`${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.` : ''}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import pg from 'pg';
|
import pg from 'pg';
|
||||||
import { texteRapport, envoyer, donneesNuit, texteDepuis } from './rapport-nuit.mjs';
|
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 ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const ONCE = process.argv.includes('--once');
|
const ONCE = process.argv.includes('--once');
|
||||||
@@ -1862,6 +1862,13 @@ async function publierDiagnosticPv(st) {
|
|||||||
await publierImportJour(st).catch((e) => {
|
await publierImportJour(st).catch((e) => {
|
||||||
console.error(`import du jour : ${e.message}`); return 0;
|
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).
|
// Raccordement reseau : contacteur ouvert = import nul, pas une panne (26/09).
|
||||||
const rr = etatReseau(st);
|
const rr = etatReseau(st);
|
||||||
await publierCapteur('sensor.evcs_reseau', rr.etat, {
|
await publierCapteur('sensor.evcs_reseau', rr.etat, {
|
||||||
|
|||||||
Reference in New Issue
Block a user