Prevision de nuit : bilan degrade relaye sous reserve, plus muet

18:03 le 29/09 : « prevision indisponible » avec 16 points de marge, parce
que le bilan etait `degrade` (forme minoree sur une heure de deficit, chaque
soir ~1 h). Statut `sous_reserve` avec la reserve dans le texte ;
`indisponible` seulement sans bilan, perime ou statut inconnu, avec la raison.
Diagnostics 90/90.

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:
Tallagrand Thierry
2026-09-29 18:10:00 +02:00
parent c2f9e83a39
commit 0975e62bda
3 changed files with 52 additions and 4 deletions

View File

@@ -1168,6 +1168,22 @@ affiner ; demarrage solaire de l'apres-midi toujours non couvert par le besoin d
pour passer de 18 h a 09 h sans EDF (17,87 kWh), il faudrait la batterie a 82 % a pour passer de 18 h a 09 h sans EDF (17,87 kWh), il faudrait la batterie a 82 % a
18 h ; si EDF s'engage a 01:24, la maison y achetera environ 4,2 kWh ». Attributs 18 h ; si EDF s'engage a 01:24, la maison y achetera environ 4,2 kWh ». Attributs
`prochaine_traversee*`. Diagnostics 87/87, mutation qui mord. En reel a 10:53. `prochaine_traversee*`. Diagnostics 87/87, mutation qui mord. En reel a 10:53.
- **18:03 « prevision de la nuit indisponible » en vocal** alors que le bilan (5 min)
donnait SOC 83 pour 67,2 requis : statut `degrade`. Constat en base : le bilan passe
`degrade` **chaque soir pendant ~1 h** (19:00-19:55 du 23 au 28/09, 18:00 le 29/09) —
une heure de deficit encore ensoleillee dont l'historique est censure (batterie souvent
pleine a cette heure) est marquee « forme minoree » (`RESOURCE_ESTIMATE`, taux de
censure > 0,25). Avec la courbe horaire VRM/Forecast.Solar, cette forme n'entre plus
dans le calcul de production : l'alerte est tres probablement FAUSSE. Correctif capteur
: `degrade` -> prevision publiee, statut `sous_reserve`, phrase « Sous reserve :
l'historique de production de ces heures de fin de journee est incomplet … la decision
reelle est reprise a 01:24 » ; `indisponible` seulement sans bilan, bilan perime ou
statut inconnu, avec la raison dans `explication`. Diagnostics 90/90, mutation qui mord.
⚠️ **OUVERT, decision du proprietaire** : pendant cette heure, le controleur ignore
AUSSI le bilan (`balanceStatusOk`) -> plancher du soir retombe sur 85 % fixe, cible et
session sur les regles historiques. A 01:24 le statut est `ok` (pas d'effet sur la
session de nuit constate). Piste : ne pas degrader sur une forme minoree quand la
production horaire vient de la courbe de prevision (et non de l'enveloppe).
- SPARK : les transcripts SQLite (`openclaw-agent.sqlite`, droits 600) remontent au - SPARK : les transcripts SQLite (`openclaw-agent.sqlite`, droits 600) remontent au
18/07, AUCUNE rotation ; un evenement contient le jeton HA (l'agent a lu `.ha-token`). 18/07, AUCUNE rotation ; un evenement contient le jeton HA (l'agent a lu `.ha-token`).
Signale au proprietaire par SPARK, base non modifiee. Signale au proprietaire par SPARK, base non modifiee.

View File

@@ -422,6 +422,22 @@ for (const [nom, r, test] of imp) {
'input_boolean.victron_evcs_night_grid_session_committed': 'off', 'input_text.evcs_balance_next_deficit_window': 'déficit en cours', 'input_boolean.victron_evcs_night_grid_session_committed': 'off', 'input_text.evcs_balance_next_deficit_window': 'déficit en cours',
})) m.set(k, { state: v }); return previsionNuit(m, t); })(), })) m.set(k, { state: v }); return previsionNuit(m, t); })(),
(r) => r.etat === 'jour' && !/Repere provisoire/.test(r.attributs.texte_rapport)], (r) => r.etat === 'jour' && !/Repere provisoire/.test(r.attributs.texte_rapport)],
// Releve REEL du 29/09 18:05 : bilan `degrade` (forme minoree), SOC 83,
// requis 67,2, 13,56 kWh a couvrir. Le vocal a dit « indisponible ».
['29/09 18:05 : bilan degrade -> prevision publiee SOUS RESERVE, pas muette', (() => {
const m = base(); for (const [k, v] of Object.entries({
'input_text.evcs_balance_status': 'degrade', 'input_number.evcs_balance_energy_to_cover_kwh': '13.56',
'input_boolean.evcs_balance_grid_needed': 'off', 'input_number.evcs_balance_required_soc': '67.2',
'input_number.evcs_balance_required_soc_prudent': '76', 'sensor.victron_battery_soc': '83',
'input_boolean.victron_evcs_night_grid_session_committed': 'off', 'input_datetime.victron_evcs_hc_night_start': '01:24:00',
})) m.set(k, { state: v }); return previsionNuit(m, t); })(),
(r) => r.etat === 0 && r.attributs.statut === 'sous_reserve' && r.attributs.verdict === 'batterie_suffit'
&& /Sous reserve/.test(r.attributs.texte_rapport) && /reprise a 01:24/.test(r.attributs.texte_rapport)],
['statut inconnu -> indisponible, avec la raison', (() => {
const m = base(); m.set('input_text.evcs_balance_status', { state: 'erreur' }); return previsionNuit(m, t); })(),
(r) => r.etat === 'indisponible' && /etat « erreur »/.test(r.attributs.explication)],
['bilan perime -> indisponible, dit depuis combien de temps', previsionNuit(base(), new Date('2026-09-28T00:30:00Z')),
(r) => r.etat === 'indisponible' && /recalcule depuis 65 min/.test(r.attributs.explication)],
['plus jamais d attribut reseau_necessaire_kwh (lu comme un achat le 28/09)', previsionNuit(base(), t), ['plus jamais d attribut reseau_necessaire_kwh (lu comme un achat le 28/09)', previsionNuit(base(), t),
(r) => !('reseau_necessaire_kwh' in r.attributs) && 'import_prevu_kwh' in r.attributs], (r) => !('reseau_necessaire_kwh' in r.attributs) && 'import_prevu_kwh' in r.attributs],
['jamais d attribut qui ressemble a un SOC du matin (incident 21:16)', previsionNuit(base(), t), ['jamais d attribut qui ressemble a un SOC du matin (incident 21:16)', previsionNuit(base(), t),

View File

@@ -632,10 +632,26 @@ export function previsionNuit(st, maintenant = new Date()) {
? 'la production du matin vient de la prevision VRM, en essai depuis le 28/09 : l heure de retour du solaire reste une estimation' ? 'la production du matin vient de la prevision VRM, en essai depuis le 28/09 : l heure de retour du solaire reste une estimation'
: 'la production du matin vient de Forecast.Solar, qui sous-estime ces jours-ci : l heure de retour du solaire peut etre plus precoce', : 'la production du matin vient de Forecast.Solar, qui sous-estime ces jours-ci : l heure de retour du solaire peut etre plus precoce',
}; };
if (statut !== 'ok' || age === null || age > BILAN_FRAIS_MIN || aCouvrir === null || retour === null) { // `degrade` n'est PAS une panne (29/09, 18:03 : « prevision indisponible »
// en vocal avec 16 points de marge). Le bilan passe `degrade` chaque soir
// pendant une heure environ (19 h depuis le 23/09, 18 h le 29/09) : une
// heure de deficit dont l'historique de production est censure (batterie
// souvent pleine a cette heure) est marquee « forme minoree ». Le calcul est
// fait et publie ; on le relaie AVEC la reserve. Muet seulement sans bilan,
// bilan perime, ou statut inconnu.
const degrade = statut === 'degrade';
const pourquoi = age === null || age > BILAN_FRAIS_MIN
? `le bilan energetique n a pas ete recalcule depuis ${age === null ? 'un moment' : `${age} min`}`
: statut !== 'ok' && !degrade ? `le bilan energetique est en etat « ${statut ?? 'inconnu'} »`
: 'le bilan ne donne pas l energie a couvrir ou l heure de retour du solaire';
if ((statut !== 'ok' && !degrade) || age === null || age > BILAN_FRAIS_MIN || aCouvrir === null || retour === null) {
return { etat: 'indisponible', attributs: { ...attributs, statut: 'indisponible', return { etat: 'indisponible', attributs: { ...attributs, statut: 'indisponible',
explication: 'Le bilan energetique n est pas a jour : prevision de la nuit indisponible.' } }; explication: `Prevision de la nuit indisponible pour l instant : ${pourquoi}. Elle revient au prochain calcul du bilan (toutes les 5 min).` } };
} }
const reserveBilan = degrade
? ' Sous reserve : l historique de production de ces heures de fin de journee est incomplet (batterie souvent pleine a cette heure) ; la decision reelle est reprise ' + (hcDebut ? `a ${hcDebut}` : 'a l ouverture des heures creuses') + '.'
: '';
attributs.reserve_bilan = degrade ? 'forme de production minoree sur les heures de deficit' : null;
const voiture = attributs.voiture === 'aucune' const voiture = attributs.voiture === 'aucune'
? 'Aucun vehicule branche.' ? 'Aucun vehicule branche.'
: attributs.voiture === 'branchee' : attributs.voiture === 'branchee'
@@ -684,9 +700,9 @@ export function previsionNuit(st, maintenant = new Date()) {
explication: texteJour, texte_rapport: texteJour } }; explication: texteJour, texte_rapport: texteJour } };
} }
const texte = `La maison doit couvrir environ ${aCouvrir} kWh jusqu au retour du solaire vers ${retour}. ` + const texte = `La maison doit couvrir environ ${aCouvrir} kWh jusqu au retour du solaire vers ${retour}. ` +
`${edf} Batterie a ${soc !== null ? Math.round(soc) : '?'} %. ${voiture}`; `${edf} Batterie a ${soc !== null ? Math.round(soc) : '?'} %. ${voiture}${reserveBilan}`;
const etat = verdict === 'edf_engage' || verdict === 'reseau_necessaire' ? (importPrevu ?? manqueKwh) : 0; const etat = verdict === 'edf_engage' || verdict === 'reseau_necessaire' ? (importPrevu ?? manqueKwh) : 0;
return { etat, attributs: { ...attributs, statut: 'frais', explication: texte, texte_rapport: texte } }; return { etat, attributs: { ...attributs, statut: degrade ? 'sous_reserve' : 'frais', explication: texte, texte_rapport: texte } };
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------