#!/usr/bin/env node // Patch 08/08/2026 — FIX C : coupure EDF immédiate au débranchement du véhicule. // Incident nuit 07-08/08 : après débranchement, EDF restait ON 25 min // (hold_on_min_runtime) → l'utilisateur a dû couper à la main. // Cible : workflows/victron-grid-hysteresis-v1.json (node "Compute Grid Hysteresis"). // Rejouable : détecte si déjà appliqué. 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_vehicle_unplugged')) { console.log('Déjà appliqué — rien à faire.'); process.exit(0); } const anchor = ` reason.push('Force grid + voiture pleine : coupure immediate du reseau'); forcedCut = true; } `; const insertion = ` reason.push('Force grid + voiture pleine : coupure immediate du reseau'); forcedCut = true; } // ====================================================== // FIX C (08/08/2026): VEHICULE DEBRANCHE -> COUPURE IMMEDIATE // Incident nuit 07-08/08 : apres debranchement du vehicule, EDF restait // maintenu ON 25 min (hold_on_min_runtime) alors que plus rien ne le // justifiait (SOC >= seuil OFF). Ici : coupure immediate, bypass min_on. // ====================================================== if ( !forcedCut && latchIsOn && gridIsOn && !evcsGridOverride && evcsSurplusState === 'vehicle_not_connected' && soc >= gridSocOff ) { desiredGrid = 'off'; desiredLatch = 'off'; state = 'turn_off_vehicle_unplugged'; reason.push('Vehicule debranche + SOC >= seuil OFF : coupure immediate du reseau (bypass min_on)'); forcedCut = true; } `; if (!code.includes(anchor)) throw new Error('Ancre force_grid/forcedCut introuvable — code inattendu'); code = code.replace(anchor, insertion); // Notification dédiée (facultative mais utile) : réutilise le bloc turn_off_recovered. const notifAnchor = `} else if (state === 'turn_off_recovered') {`; const notifInsertion = `} else if (state === 'turn_off_vehicle_unplugged') { notifyTitle = '✅ EDF coupé (véhicule débranché)'; notifyMessage = \`Véhicule débranché et SOC \${round1(soc)}% >= \${gridSocOff}%. Coupure immédiate du réseau.\`; notifyWanted = true; } else if (state === 'turn_off_recovered') {`; if (!code.includes(notifAnchor)) throw new Error('Ancre notification turn_off_recovered introuvable'); code = code.replace(notifAnchor, notifInsertion); node.parameters.jsCode = code; fs.writeFileSync(FILE, JSON.stringify(wf, null, 2) + '\n'); console.log('Patch appliqué →', FILE, `(jsCode ${code.length} chars)`);