Le conseiller ne fixe plus la cible du matin

Le premier cablage reel (§20, 09/08) faisait suivre a la cible SOC du matin
`input_number.evcs_advisor_night_target_soc`, derriere quatre gardes :
toggle, statut ok, fraicheur de 120 minutes, bornage [22, 90].

Depuis que le conseiller est devenu observateur le 12/09, il n emet plus de
cible et ce helper est fige a 55 %. Les quatre gardes continuaient pourtant
de passer toutes les quatre, parce que la garde de fraicheur date le RUN et
non la VALEUR : le conseiller tourne toujours, donc son horodatage est
frais. Activer le toggle aurait applique un chiffre perime sans qu aucune
garde ne s y oppose.

Retire : les quatre parametres d INPUT_PARSE, le bloc de WINDOWS_SOURCEV2,
le passe-plat de BATTERY_CLOUD_LOCKS, la trace (ADVISOR) de DECISION et les
quatre champs du gabarit de snapshot. Le cablage du BILAN (§39) est intact.
`parseAdvisorRunToMs` devient `parseRunStampToMs` : le bilan publie le meme
format et reste son seul appelant.

Golden 98 → 96. Les trois scenarios du cablage sont remplaces par un seul,
`advisor-cible-ignoree`, qui pinne l ABSENCE de cablage : toutes les
conditions de l ancienne regle reunies, et la cible doit rester celle des
regles de prevision. Mutation : cablage restaure → il tombe, avec
« target=22% (ADVISOR) » dans la raison ; retire → 96/96.

Gabarit re-rendu contre Home Assistant : 196 champs, JSON valide, aucun
null. Pousse en production, cycle suivant en succes.

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-13 12:14:38 +02:00
parent 4a9ad0bc65
commit 4fb401be96
8 changed files with 55 additions and 103 deletions

View File

@@ -5964,9 +5964,30 @@ traverse. Si le toggle `evcs_advisor_apply_night_target` est active, la cible de
la nuit devient un 55 fige d hier matin.
La garde de fraicheur ne protege donc pas ce qu elle croit proteger : elle date
le RUN, pas la VALEUR. Le toggle est OFF aujourd hui. Le cablage est devenu du
code mort qui ne peut que se tromper — a retirer (trois scenarios golden), mais
c est une decision du proprietaire, §20 etant un jalon qu il avait valide.
le RUN, pas la VALEUR.
**RETIRE le 13/09/2026 sur decision du proprietaire.** Les quatre parametres
d INPUT_PARSE, le bloc de WINDOWS_SOURCEV2, le passe-plat de BATTERY_CLOUD_LOCKS,
la trace `(ADVISOR)` de DECISION_ENGINEV2 et les quatre champs du gabarit de
snapshot. `batteryMorningTargetBasePct` vaut desormais simplement la regle de
prevision ; le cablage du BILAN (§39), lui, est intact et continue de ne pouvoir
que RELEVER cette cible.
`parseAdvisorRunToMs` est renommee `parseRunStampToMs` : elle sert encore au
bilan, qui publie le meme format, mais son nom designait un appelant disparu.
Golden 98 → 96 : les trois scenarios du cablage (applique / perime / toggle OFF)
sont remplaces par UN seul, `advisor-cible-ignoree`, qui pinne l ABSENCE de
cablage — toutes les conditions de l ancienne regle reunies, et la cible doit
rester celle des regles. Preuve par mutation : cablage restaure → ce scenario
tombe avec `target=22% (ADVISOR)` dans la raison ; retire → 96/96.
Gabarit re-rendu contre Home Assistant : 196 champs, JSON valide, aucun null,
plus aucun champ advisor.
⚠️ RESTE : les cinq helpers eux-memes existent toujours cote Home Assistant et
la carte « Conseiller IA » affiche encore « Cible SOC matin (agent) : 55 % »,
figee depuis le 12/09. Plus rien ne la lit, mais un chiffre fige affiche comme
une recommandation courante est exactement le fantome qui a cree le danger
ci-dessus. A nettoyer (paquet + carte).
---
*Document mis a jour le : 13/09/2026 — campagne conseiller : quatre defauts du prompt corriges, incidents 39/40, table `observation`*

View File

@@ -360,7 +360,6 @@ const batteryMorningTargetPct =
Number.isFinite(nightPrev.batteryMorningTargetPct)
? nightPrev.batteryMorningTargetPct
: params.evcsNightTargetWeakForecastPct;
const advisorNightTargetApplied = !!nightPrev.advisorNightTargetApplied;
const balanceMorningTargetApplied = !!nightPrev.balanceMorningTargetApplied;
const nightGridSessionCommitted = !!nightPrev.nightGridSessionCommitted;
const maxBatteryDischargePoints =
@@ -1081,7 +1080,6 @@ return [{
batteryHardFloorPct,
batteryMorningTargetPct,
advisorNightTargetApplied,
balanceMorningTargetApplied,
nightGridSessionCommitted,
maxBatteryDischargePoints,

View File

@@ -1319,7 +1319,7 @@ else {
// =====================================================
if (nightEvcs?.nightSessionActive) {
reason.push(
`Nuit EVCS: target=${round1(nightEvcs.batteryMorningTargetPct)}%${nightEvcs.advisorNightTargetApplied ? ' (ADVISOR)' : ''}${nightEvcs.balanceMorningTargetApplied ? ' (BILAN)' : ''} | trigger réseau=${round1(nightEvcs.gridTakeoverTriggerPct)}%`
`Nuit EVCS: target=${round1(nightEvcs.batteryMorningTargetPct)}%${nightEvcs.balanceMorningTargetApplied ? ' (BILAN)' : ''} | trigger réseau=${round1(nightEvcs.gridTakeoverTriggerPct)}%`
);
if (Number.isFinite(nightEvcs.nightSessionStartSocPct)) {

View File

@@ -141,10 +141,6 @@
"prev_surplus_state": states("input_text.victron_evcs_surplus_state"),
"evcs_night_session_start_soc": states("input_text.victron_evcs_night_session_start_soc"),
"evcs_night_session_start_at": states("input_text.victron_evcs_night_session_start_at"),
"advisor_apply_night_target": states("input_boolean.evcs_advisor_apply_night_target"),
"advisor_night_target_soc": states("input_number.evcs_advisor_night_target_soc"),
"advisor_status": states("input_text.evcs_advisor_status"),
"advisor_last_run": states("input_text.evcs_advisor_last_run"),
"balance_required_soc": states("input_number.evcs_balance_required_soc"),
"balance_energy_to_cover_kwh": states("input_number.evcs_balance_energy_to_cover_kwh"),

View File

@@ -172,10 +172,12 @@ function parseChargingTimeToSec(s) {
return matched ? sec : null;
}
// Advisor IA : input_text.evcs_advisor_last_run au format "DD/MM HH:mm"
// Horodatage "DD/MM HH:mm" publie par le bilan energetique
// (input_text.evcs_balance_last_run). Le conseiller employait le meme format ;
// son cablage a ete retire le 13/09/2026, la fonction sert desormais au bilan.
// (pas d'année). On suppose l'année courante ; si le résultat est dans le
// futur (> 1 jour), c'est un passage d'année → on retire un an.
function parseAdvisorRunToMs(s) {
function parseRunStampToMs(s) {
const m = (s ?? '').toString().trim().match(/^(\d{2})\/(\d{2}) (\d{2}):(\d{2})$/);
if (!m) return null;
@@ -464,17 +466,13 @@ const params = {
// Advisor IA — premier câblage réel (09/08/2026, HANDOFF §20) :
// cible SOC matin recommandée, appliquée UNIQUEMENT si le toggle est ON.
// Garde-fous consommés dans WINDOWS_SOURCEV2 (statut ok + fraîcheur + bornes).
advisorApplyNightTarget: low(input.advisor_apply_night_target || 'off') === 'on',
advisorNightTargetSocPct: num(input.advisor_night_target_soc, NaN),
advisorStatusOk: cleanText(input.advisor_status).toLowerCase() === 'ok',
advisorLastRunMs: parseAdvisorRunToMs(cleanText(input.advisor_last_run)),
// BILAN ENERGETIQUE (03/09/2026). Meme format d'horodatage que l'advisor
// ("DD/MM HH:mm"), d'ou la reutilisation du parseur.
balanceRequiredSocPct: num(input.balance_required_soc, NaN),
balanceEnergyToCoverKwh: num(input.balance_energy_to_cover_kwh, NaN),
balanceStatusOk: cleanText(input.balance_status).toLowerCase() === 'ok',
balanceLastRunMs: parseAdvisorRunToMs(cleanText(input.balance_last_run)),
balanceLastRunMs: parseRunStampToMs(cleanText(input.balance_last_run)),
// Duree de la fenetre d'etablissement. Valeur mesuree le 31/08/2026 : le
// transitoire de fermeture dure ~150 s (import 1985 -> 25 W et 2600 -> 52 W,

View File

@@ -268,31 +268,18 @@ const batteryMorningTargetRulePct =
: params.evcsNightTargetWeakForecastPct;
// =====================================================
// ADVISOR IA — PREMIER CÂBLAGE RÉEL (09/08/2026, HANDOFF §20)
// La cible SOC matin suit la recommandation de l'advisor UNIQUEMENT si :
// - toggle input_boolean.evcs_advisor_apply_night_target ON (rollback 1 clic)
// - dernier run advisor en statut 'ok'
// - recommandation fraîche (≤ 120 min, l'advisor tourne toutes les 15 min)
// - valeur bornée [max(22, hard floor), 90]
// Sinon : règles forecast historiques (comportement inchangé).
// LE CONSEILLER NE FIXE PLUS LA CIBLE (retire le 13/09/2026)
// =====================================================
const ADVISOR_TARGET_MAX_AGE_MIN = 120;
const advisorTargetAgeMin = minutesSince(params.advisorLastRunMs, nowMs);
const advisorNightTargetApplied =
params.advisorApplyNightTarget &&
params.advisorStatusOk &&
Number.isFinite(params.advisorNightTargetSocPct) &&
advisorTargetAgeMin !== null &&
advisorTargetAgeMin <= ADVISOR_TARGET_MAX_AGE_MIN;
const batteryMorningTargetBasePct =
advisorNightTargetApplied
? clamp(
params.advisorNightTargetSocPct,
Math.max(22, batteryHardFloorPct),
90
)
: batteryMorningTargetRulePct;
// Le premier cablage reel (09/08/2026, HANDOFF §20) faisait suivre a la cible
// du matin `input_number.evcs_advisor_night_target_soc`, derriere un toggle,
// un statut `ok`, une fraicheur de 120 min et un bornage.
// Le 12/09 le conseiller est devenu OBSERVATEUR : il n'emet plus de cible, et
// ce helper est fige depuis. Les quatre gardes continuaient pourtant de passer
// toutes les quatre — la garde de fraicheur date le RUN, pas la VALEUR, et le
// conseiller tourne toujours. Activer le toggle aurait donc applique un chiffre
// perime sans qu'aucune garde ne s'y oppose. Un cablage qui ne peut plus que se
// tromper se retire ; le scenario `advisor-cible-ignoree` le verifie.
const batteryMorningTargetBasePct = batteryMorningTargetRulePct;
// =====================================================
// CIBLE DU MATIN : UNE ENERGIE A COUVRIR, PAS UN PALIER (03/09/2026)
@@ -1059,7 +1046,6 @@ return [{
balanceTargetAgeMin:
balanceTargetAgeMin === null ? null : Math.round(balanceTargetAgeMin * 10) / 10,
batteryMorningTargetRulePct,
advisorNightTargetApplied,
maxBatteryDischargePoints,
gridTakeoverTriggerPct,

View File

@@ -140,12 +140,16 @@ export const scenarios = [
},
},
// ===== ADVISOR IA — premier câblage réel (09/08/2026, HANDOFF §20) =====
// Référence : night-soc-sous-cible (SOC 30 < cible règle 35 → grid_forced).
// Avec la cible advisor 22 APPLIQUÉE : SOC 30 ≥ 22 → grid_hc_allowed.
// ===== LE CONSEILLER NE FIXE PLUS LA CIBLE (13/09/2026) =====
// Les trois scenarios du premier cablage (applique / perime / toggle OFF)
// sont remplaces par celui-ci, qui pinne l'ABSENCE de cablage : toutes les
// conditions de l'ancienne regle sont reunies — toggle ON, statut ok,
// horodatage frais, valeur dans les bornes — et la cible doit rester celle
// des regles de prevision. Si quelqu'un recable un jour, ce test tombe.
// Reference : night-soc-sous-cible (SOC 30 < cible regle 35 → grid_forced).
{
id: 'night-advisor-target-applique',
label: 'Nuit HC, SOC 30, advisor cible 22 appliquée (toggle ON, ok, frais) → hc_allowed au lieu de grid_forced',
id: 'advisor-cible-ignoree',
label: 'Nuit HC, SOC 30, helpers advisor au complet et frais → IGNORES, cible des regles (grid_forced)',
time: NIGHT,
overrides: {
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
@@ -154,52 +158,6 @@ export const scenarios = [
advisor_status: 'ok',
advisor_last_run: '08/08 01:20',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_hc_allowed',
state: 'night_wait_grid_takeover',
decision: 'hold',
desiredCharging: false,
currentA: 6,
takeoverNeeded: true,
edfOnAction: true,
takeoverHelperService: 'turn_on',
},
},
{
id: 'night-advisor-target-stale',
label: 'Nuit HC, SOC 30, advisor cible 22 mais recommandation > 120 min → fallback règles (grid_forced)',
time: NIGHT,
overrides: {
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
advisor_apply_night_target: 'on',
advisor_night_target_soc: '22',
advisor_status: 'ok',
advisor_last_run: '07/08 20:00',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_forced',
state: 'night_wait_grid_takeover',
decision: 'hold',
desiredCharging: false,
currentA: 6,
takeoverNeeded: true,
edfOnAction: true,
takeoverHelperService: 'turn_on',
},
},
{
id: 'night-advisor-toggle-off',
label: 'Nuit HC, SOC 30, advisor cible 22 mais toggle APPLIQUER OFF → règles inchangées (grid_forced)',
time: NIGHT,
overrides: {
...VEHICLE_NIGHT, soc_percent: '30', allow_grid_hc_night: 'on',
advisor_apply_night_target: 'off',
advisor_night_target_soc: '22',
advisor_status: 'ok',
advisor_last_run: '08/08 01:20',
},
expect: {
profile: 'night_hc',
sourceMode: 'grid_forced',
@@ -212,11 +170,6 @@ export const scenarios = [
takeoverHelperService: 'turn_on',
},
},
// ===== P9 09/08/2026 : charge réseau avant départ (hors HC) + sélecteur véhicule =====
// VW départ 08:50 (helper existant), JEEP départ 06:30 (nouveau helper).
// À 08:00 (morning_solar, PV nul) : VW → fenêtre départ active (50 min) ;
// JEEP → départ passé depuis 90 min → rien.
{
id: 'p9-depart-vw-grid',
label: 'Matin 08:00, VW sélectionnée, départ 08:50, toggle P9 ON, SOC 45, PV nul → réseau autorisé + EDF demandé',

File diff suppressed because one or more lines are too long