#!/usr/bin/env node // Patch 24/08/2026 — REVERT de FIX G (22/08/2026). // // Pourquoi : Fix G coupait EDF dès que l'override EVCS disparaissait ET que // SOC > gridSocOn. Le garde `soc > gridSocOn` ne protégeait que l'INSTANT de // l'engagement sur SOC bas (soc <= 20), pas la PHASE DE REMONTÉE 20 -> 30 qui // est 100 % de la durée de vie de ce cas. Résultat en production la nuit du // 23-24/08, véhicule débranché : // SOC 20 -> branche low_soc : EDF ON (bypass cooldown) // SOC 21 -> Fix G : EDF OFF (bypass min_on) // ... cycle de 2 à 4 minutes, MPPT qui flappe à chaque bascule. // Fix G avait de fait remplacé l'hystérésis 20/30 par une hystérésis 20/20,1. // // De plus, sa prémisse était fausse : l'incident du 21-22/08 qui l'avait // motivé n'est pas un problème d'override EVCS mais un problème de bilan // énergétique (couper EDF à 21 % à 06:43 alors que la PV ne couvre la conso // maison qu'à partir de ~09:00 rend le yoyo mécaniquement inévitable, quels // que soient les seuils). Ce sujet est traité séparément. // // Cible : workflows/victron-grid-hysteresis-v1.json (node "Compute Grid Hysteresis"). // Rejouable : détecte si déjà retiré. import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const FILE = path.join(ROOT, 'workflows/victron-grid-hysteresis-v1.json'); const wf = JSON.parse(fs.readFileSync(FILE, 'utf8')); const node = wf.nodes.find(n => n.name === 'Compute Grid Hysteresis'); if (!node) throw new Error('Node "Compute Grid Hysteresis" introuvable'); let code = node.parameters.jsCode; if (!code.includes('turn_off_evcs_override_released')) { console.log('Fix G déjà retiré — rien à faire.'); process.exit(0); } // --- 1. Bloc de décision ------------------------------------------------- const decisionStart = code.indexOf('\n// ======================================================\n// FIX G (22/08/2026)'); if (decisionStart === -1) throw new Error('En-tête du bloc de décision Fix G introuvable.'); const decisionEndMarker = " forcedCut = true;\n}\n"; const decisionEnd = code.indexOf(decisionEndMarker, decisionStart); if (decisionEnd === -1) throw new Error('Fin du bloc de décision Fix G introuvable.'); code = code.slice(0, decisionStart) + code.slice(decisionEnd + decisionEndMarker.length); // --- 2. Branche notification -------------------------------------------- const notifStart = code.indexOf("} else if (state === 'turn_off_evcs_override_released') {"); if (notifStart === -1) throw new Error('Branche notification Fix G introuvable.'); const notifEndMarker = " notifyWanted = true;\n\n"; const notifEnd = code.indexOf(notifEndMarker, notifStart); if (notifEnd === -1) throw new Error('Fin de la branche notification Fix G introuvable.'); code = code.slice(0, notifStart) + code.slice(notifEnd + notifEndMarker.length); if (code.includes('turn_off_evcs_override_released')) { throw new Error('Résidus de Fix G après retrait — vérifier manuellement.'); } node.parameters.jsCode = code; fs.writeFileSync(FILE, JSON.stringify(wf, null, 2) + '\n', 'utf8'); console.log('Fix G retiré de', FILE, `(jsCode: ${code.length} caractères)`);