R1: déduplication du moteur nuit — WINDOWS_SOURCEV2 unique calculateur
- Le moteur nuit (161 lignes) était calculé 2x (WINDOWS + BCL), chaque patch nuit devait être appliqué 2x (P2, 04/08, P7) -> risque de divergence. - WINDOWS calcule tout (+ startMs, needsCapture strict, recoveryAllowedNow, targetReached) et exporte nightEvcsPreview étendu. - BCL consomme prev.nightEvcsPreview avec fallbacks sûrs (preview absent -> 'blocked'). Export nightEvcs inchangé pour DECISION/HA_SERVICE. - A/B git HEAD vs patché : 7 scénarios + 2 cas limites, sorties strictement identiques. Déployé en production, exécutions vérifiées success.
This commit is contained in:
44
HANDOFF.md
44
HANDOFF.md
@@ -476,3 +476,47 @@ const nightGridHcPreferred =
|
|||||||
|
|
||||||
---
|
---
|
||||||
*Document mis à jour le : 07/08/2026 (~15:50) — P7 charge HC nuit auto + tools n8n-pull/n8n-push*
|
*Document mis à jour le : 07/08/2026 (~15:50) — P7 charge HC nuit auto + tools n8n-pull/n8n-push*
|
||||||
|
|
||||||
|
## 12. Session du 07/08/2026 (soir) — R1 : Déduplication du moteur nuit
|
||||||
|
|
||||||
|
### Contexte
|
||||||
|
Refactor n°1 du plan de maintenabilité (conseil validé avec l'utilisateur) :
|
||||||
|
le moteur nuit complet (~161 lignes : nightSessionActive, batteryAssistAllowed,
|
||||||
|
nightGridTakeoverNeeded, nightPreferredSourceMode, cibles/triggers, latch...)
|
||||||
|
était calculé DEUX FOIS, dans WINDOWS_SOURCEV2 ET BATTERY_CLOUD_LOCKS.
|
||||||
|
Chaque patch nuit devait être appliqué 2× (P2, fix 04/08, P7) → risque de
|
||||||
|
divergence silencieuse entre sourceMode (WINDOWS) et conditions (BCL).
|
||||||
|
|
||||||
|
### Fix R1 (déployé 07/08 ~19:03, A/B validé)
|
||||||
|
- **WINDOWS_SOURCEV2 = unique calculateur.** Reprend les 4 éléments que seul BCL
|
||||||
|
calculait : `nightSessionStartMs`, `nightSessionNeedsCapture` version stricte
|
||||||
|
(critère startMs inclus), `batteryRecoveryAllowedNow`, `batteryTargetReached`.
|
||||||
|
Export `nightEvcsPreview` étendu (+ `nightTakeoverLatched`, `nightGridHcPreferred`).
|
||||||
|
- **BATTERY_CLOUD_LOCKS consomme** `prev.nightEvcsPreview` (bloc de 161 lignes
|
||||||
|
remplacé par ~50 lignes de lecture avec fallbacks sûrs : preview absent
|
||||||
|
→ session inactive / 'blocked'). −1,7K chars.
|
||||||
|
- Le shape de l'export `nightEvcs` de BCL (consommé par DECISION_ENGINEV2 et
|
||||||
|
HA_SERVICE_CALLS_OUTPUT) est strictement inchangé.
|
||||||
|
- **RÈGLE DÉSORMAIS : toute évolution de la logique nuit se fait UNIQUEMENT
|
||||||
|
dans WINDOWS_SOURCEV2.** BCL ne contient plus aucune logique nuit.
|
||||||
|
|
||||||
|
### Validation A/B (tools/ab-20260807-r1.mjs)
|
||||||
|
- Pipeline complet WINDOWS→BCL exécuté avant (git HEAD) / après (patché) sur les
|
||||||
|
7 scénarios golden + 2 cas limites (capture session history vide, clear résiduel).
|
||||||
|
- Sorties BCL **strictement identiques** (nightEvcs, conditions, safeguards, context).
|
||||||
|
- Fallback preview absent : pas de crash, retombe en 'blocked'.
|
||||||
|
- Piège rencontré : Date.now() dans le harnais → faux diffs de quelques ms ;
|
||||||
|
corrigé en construisant le snapshot une fois puis clone JSON par exécution.
|
||||||
|
|
||||||
|
### Outillage ajouté
|
||||||
|
- `tools/patch-20260807-r1-dedup-night.mjs` : le refactor (rejouable).
|
||||||
|
- `tools/ab-20260807-r1.mjs` : harnais A/B git HEAD vs working tree — réutilisable
|
||||||
|
pour tout futur refactor (modèle pour R3, la suite de tests golden).
|
||||||
|
|
||||||
|
### Prochaines étapes du plan validé
|
||||||
|
- R3 : suite de tests golden (~15 scénarios, exécutée avant chaque push).
|
||||||
|
- R2 : git source de vérité (src/nodes/*.js + assemblage dans n8n-push.mjs).
|
||||||
|
- Puis agent IA planificateur en mode ombre (jamais dans la boucle 30 s).
|
||||||
|
|
||||||
|
---
|
||||||
|
*Document mis à jour le : 07/08/2026 (~19:10) — R1 déployé, comportement inchangé (A/B), production stable*
|
||||||
|
|||||||
76
tools/ab-20260807-r1.mjs
Normal file
76
tools/ab-20260807-r1.mjs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// A/B R1 : compare la sortie de BATTERY_CLOUD_LOCKS avant/après déduplication.
|
||||||
|
// AVANT = git HEAD ; APRÈS = working tree. Exigence : sorties identiques
|
||||||
|
// (hors nightEvcsPreview, volontairement étendu) sur tous les scénarios.
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import vm from 'vm';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
const OLD = JSON.parse(execSync('git show HEAD:workflows/evcs-surplus-controller-v2.6.json', { maxBuffer: 64 * 1024 * 1024 }).toString());
|
||||||
|
const NEW = JSON.parse(fs.readFileSync('workflows/evcs-surplus-controller-v2.6.json', 'utf8'));
|
||||||
|
|
||||||
|
const code = wf => name => wf.nodes.find(n => n.name === name).parameters.jsCode;
|
||||||
|
const oldW = code(OLD)('WINDOWS_SOURCEV2'), oldB = code(OLD)('BATTERY_CLOUD_LOCKS');
|
||||||
|
const newW = code(NEW)('WINDOWS_SOURCEV2'), newB = code(NEW)('BATTERY_CLOUD_LOCKS');
|
||||||
|
|
||||||
|
// Réutilise buildPrev + scénarios du harnais dry-run existant
|
||||||
|
const harness = fs.readFileSync('tools/dryrun-20260807-night-hc.mjs', 'utf8');
|
||||||
|
const buildPrev = eval('(' + harness.slice(harness.indexOf('function buildPrev'), harness.indexOf('function run')).replace('function buildPrev', 'function') + ')');
|
||||||
|
const scenarios = eval(harness.slice(harness.indexOf('const scenarios = ') + 'const scenarios = '.length, harness.indexOf('];') + 1));
|
||||||
|
|
||||||
|
function run(jsCode, prev) {
|
||||||
|
const ctx = { $input: { first: () => ({ json: prev }) }, console, Date, Intl, Math, Number, Object, String, JSON };
|
||||||
|
vm.createContext(ctx);
|
||||||
|
return vm.runInContext(`(function(){\n${jsCode}\n})()`, ctx)[0].json;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff récursif (chemins des différences)
|
||||||
|
function diff(a, b, path = '', out = []) {
|
||||||
|
if (a === b) return out;
|
||||||
|
if (typeof a !== typeof b || a === null || b === null || typeof a !== 'object') {
|
||||||
|
if (!(Number.isNaN(a) && Number.isNaN(b))) out.push(`${path}: ${JSON.stringify(a)} != ${JSON.stringify(b)}`);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
for (const k of new Set([...Object.keys(a), ...Object.keys(b)])) diff(a[k], b[k], path ? `${path}.${k}` : k, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
let failures = 0;
|
||||||
|
for (const sc of scenarios) {
|
||||||
|
// Snapshot construit UNE fois puis cloné : élimine la non-déterminisme Date.now()
|
||||||
|
const basePrev = buildPrev(sc);
|
||||||
|
const clone = () => JSON.parse(JSON.stringify(basePrev));
|
||||||
|
const bOld = run(oldB, run(oldW, clone()));
|
||||||
|
const bNew = run(newB, run(newW, clone()));
|
||||||
|
// nightEvcsPreview est volontairement étendu (champs additionnels) : exclu du diff strict
|
||||||
|
const strip = o => { const c = JSON.parse(JSON.stringify(o)); delete c.nightEvcsPreview; return c; };
|
||||||
|
const diffs = diff(strip(bOld), strip(bNew));
|
||||||
|
// nightEvcs (consommé par DECISION/HA_SERVICE) doit être STRICTEMENT identique
|
||||||
|
const nightDiffs = diff(bOld.nightEvcs, bNew.nightEvcs, 'nightEvcs');
|
||||||
|
const all = [...diffs, ...nightDiffs.filter(d => !diffs.includes(d))];
|
||||||
|
if (all.length) {
|
||||||
|
failures++;
|
||||||
|
console.log(`❌ ${sc.label}`);
|
||||||
|
for (const d of all.slice(0, 10)) console.log(` ${d}`);
|
||||||
|
} else {
|
||||||
|
console.log(`✅ identique | ${sc.label}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test fallback : preview absent => pas de crash, mode 'blocked'
|
||||||
|
const prevNoPreview = run(newW, buildPrev(scenarios[0]));
|
||||||
|
delete prevNoPreview.nightEvcsPreview;
|
||||||
|
try {
|
||||||
|
const out = run(newB, prevNoPreview);
|
||||||
|
const n = out.nightEvcs;
|
||||||
|
const ok = n.nightSessionActive === false && n.nightPreferredSourceMode === 'blocked';
|
||||||
|
console.log(`${ok ? '✅' : '❌'} fallback preview absent : sessionActive=${n.nightSessionActive}, preferred=${n.nightPreferredSourceMode}`);
|
||||||
|
if (!ok) failures++;
|
||||||
|
} catch (e) {
|
||||||
|
failures++;
|
||||||
|
console.log(`❌ fallback preview absent : CRASH ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(failures ? `\n${failures} ÉCHEC(S) — NE PAS POUSSER` : '\nA/B PARFAIT — comportement inchangé, prêt à pousser');
|
||||||
|
process.exit(failures ? 1 : 0);
|
||||||
253
tools/patch-20260807-r1-dedup-night.mjs
Normal file
253
tools/patch-20260807-r1-dedup-night.mjs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// R1 (07/08/2026) : déduplication du moteur nuit EVCS.
|
||||||
|
// - WINDOWS_SOURCEV2 devient l'UNIQUE calculateur (ajoute les 4 champs que seul
|
||||||
|
// BCL calculait : nightSessionStartMs, needsCapture strict, batteryRecoveryAllowedNow,
|
||||||
|
// batteryTargetReached) et exporte tout dans nightEvcsPreview.
|
||||||
|
// - BATTERY_CLOUD_LOCKS ne recalcule plus rien : il consomme prev.nightEvcsPreview.
|
||||||
|
// Le shape de l'export nightEvcs de BCL (consommé par DECISION/HA_SERVICE) est inchangé.
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const FILE = 'workflows/evcs-surplus-controller-v2.6.json';
|
||||||
|
const wf = JSON.parse(fs.readFileSync(FILE, 'utf8'));
|
||||||
|
|
||||||
|
function getNode(name) {
|
||||||
|
const node = wf.nodes.find(n => n.name === name);
|
||||||
|
if (!node) throw new Error(`Node introuvable: ${name}`);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceExact(code, from, to, label) {
|
||||||
|
if (!code.includes(from)) throw new Error(`Pattern introuvable (${label}):\n---\n${from}\n---`);
|
||||||
|
if (code.split(from).length !== 2) throw new Error(`Pattern non unique (${label})`);
|
||||||
|
return code.replace(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// 1. WINDOWS_SOURCEV2
|
||||||
|
// =====================================================
|
||||||
|
const wNode = getNode('WINDOWS_SOURCEV2');
|
||||||
|
let w = wNode.parameters.jsCode;
|
||||||
|
|
||||||
|
// 1a. nightSessionStartMs (repris de BCL — bookkeeping de la mémoire de session)
|
||||||
|
w = replaceExact(
|
||||||
|
w,
|
||||||
|
`const nightSessionStartAt =
|
||||||
|
nightSessionActive
|
||||||
|
? (history.nightSessionStartAt || '')
|
||||||
|
: '';`,
|
||||||
|
`const nightSessionStartAt =
|
||||||
|
nightSessionActive
|
||||||
|
? (history.nightSessionStartAt || '')
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const nightSessionStartMs =
|
||||||
|
nightSessionActive
|
||||||
|
? (history.nightSessionStartMs ?? null)
|
||||||
|
: null;`,
|
||||||
|
'W-1a startMs'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1b. needsCapture : version stricte (celle de BCL, qui faisait foi en prod
|
||||||
|
// via DECISION) — inclut le critère nightSessionStartMs.
|
||||||
|
w = replaceExact(
|
||||||
|
w,
|
||||||
|
`const nightSessionNeedsCapture =
|
||||||
|
nightSessionActive &&
|
||||||
|
(
|
||||||
|
!Number.isFinite(history.nightSessionStartSocPct) ||
|
||||||
|
!history.nightSessionStartAt
|
||||||
|
);`,
|
||||||
|
`const nightSessionNeedsCapture =
|
||||||
|
nightSessionActive &&
|
||||||
|
(
|
||||||
|
!Number.isFinite(history.nightSessionStartSocPct) ||
|
||||||
|
!nightSessionStartAt ||
|
||||||
|
nightSessionStartMs === null
|
||||||
|
);`,
|
||||||
|
'W-1b needsCapture strict'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1c. batteryRecoveryAllowedNow + batteryTargetReached (repris de BCL)
|
||||||
|
w = replaceExact(
|
||||||
|
w,
|
||||||
|
`const nightPreferredSourceMode =
|
||||||
|
!nightSessionActive
|
||||||
|
? 'blocked'
|
||||||
|
: batteryAssistAllowed
|
||||||
|
? 'battery_assist_night'
|
||||||
|
: (
|
||||||
|
batteryBelowHardFloor ||
|
||||||
|
batteryBelowMorningTarget ||
|
||||||
|
batteryRecoveryUrgent
|
||||||
|
)
|
||||||
|
? 'grid_forced'
|
||||||
|
: 'grid_hc_allowed';`,
|
||||||
|
`const nightPreferredSourceMode =
|
||||||
|
!nightSessionActive
|
||||||
|
? 'blocked'
|
||||||
|
: batteryAssistAllowed
|
||||||
|
? 'battery_assist_night'
|
||||||
|
: (
|
||||||
|
batteryBelowHardFloor ||
|
||||||
|
batteryBelowMorningTarget ||
|
||||||
|
batteryRecoveryUrgent
|
||||||
|
)
|
||||||
|
? 'grid_forced'
|
||||||
|
: 'grid_hc_allowed';
|
||||||
|
|
||||||
|
const batteryRecoveryAllowedNow =
|
||||||
|
nightSessionActive &&
|
||||||
|
nightGridTakeoverNeeded &&
|
||||||
|
(
|
||||||
|
batteryBelowMorningTarget ||
|
||||||
|
batteryRecoveryUrgent
|
||||||
|
);
|
||||||
|
|
||||||
|
const batteryTargetReached =
|
||||||
|
nightSessionActive &&
|
||||||
|
parsed.soc >= batteryMorningTargetPct;`,
|
||||||
|
'W-1c recoveryAllowedNow + targetReached'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1d. Export complet (nightEvcsPreview ⊇ nightEvcs de BCL + champs debug)
|
||||||
|
w = replaceExact(
|
||||||
|
w,
|
||||||
|
` nightEvcsPreview: {
|
||||||
|
nightSessionActive,
|
||||||
|
nightSessionNeedsCapture,
|
||||||
|
nightSessionNeedsClear,
|
||||||
|
nightSessionStartSocPct,
|
||||||
|
nightSessionStartAt,
|
||||||
|
|
||||||
|
batteryHardFloorPct,
|
||||||
|
batteryMorningTargetPct,
|
||||||
|
maxBatteryDischargePoints,
|
||||||
|
gridTakeoverTriggerPct,
|
||||||
|
|
||||||
|
batteryBelowHardFloor,
|
||||||
|
batteryBelowMorningTarget,
|
||||||
|
batteryAtOrBelowGridTakeoverTrigger,
|
||||||
|
|
||||||
|
batteryKwhMissingToTarget,
|
||||||
|
batteryRecoveryMinutesNeeded,
|
||||||
|
batteryRecoveryUrgent,
|
||||||
|
|
||||||
|
batteryAssistAllowed,
|
||||||
|
nightGridTakeoverNeeded,
|
||||||
|
nightPreferredSourceMode,
|
||||||
|
}`,
|
||||||
|
` // R1 : source de vérité UNIQUE du moteur nuit.
|
||||||
|
// BATTERY_CLOUD_LOCKS consomme cet objet, il ne recalcule plus rien.
|
||||||
|
nightEvcsPreview: {
|
||||||
|
nightSessionActive,
|
||||||
|
nightSessionNeedsCapture,
|
||||||
|
nightSessionNeedsClear,
|
||||||
|
nightSessionStartSocPct,
|
||||||
|
nightSessionStartAt,
|
||||||
|
nightSessionStartMs,
|
||||||
|
|
||||||
|
batteryHardFloorPct,
|
||||||
|
batteryMorningTargetPct,
|
||||||
|
maxBatteryDischargePoints,
|
||||||
|
gridTakeoverTriggerPct,
|
||||||
|
|
||||||
|
batteryBelowHardFloor,
|
||||||
|
batteryBelowMorningTarget,
|
||||||
|
batteryAtOrBelowGridTakeoverTrigger,
|
||||||
|
|
||||||
|
batteryKwhMissingToTarget,
|
||||||
|
batteryRecoveryMinutesNeeded,
|
||||||
|
batteryRecoveryUrgent,
|
||||||
|
batteryRecoveryAllowedNow,
|
||||||
|
batteryTargetReached,
|
||||||
|
|
||||||
|
batteryAssistAllowed,
|
||||||
|
nightGridTakeoverNeeded,
|
||||||
|
nightTakeoverLatched,
|
||||||
|
nightGridHcPreferred,
|
||||||
|
nightPreferredSourceMode,
|
||||||
|
}`,
|
||||||
|
'W-1d export étendu'
|
||||||
|
);
|
||||||
|
|
||||||
|
wNode.parameters.jsCode = w;
|
||||||
|
console.log('OK: WINDOWS_SOURCEV2 (4 modifications)');
|
||||||
|
|
||||||
|
// =====================================================
|
||||||
|
// 2. BATTERY_CLOUD_LOCKS : remplacement du bloc dupliqué
|
||||||
|
// =====================================================
|
||||||
|
const bNode = getNode('BATTERY_CLOUD_LOCKS');
|
||||||
|
let b = bNode.parameters.jsCode;
|
||||||
|
|
||||||
|
const START_MARKER = `// =====================================================
|
||||||
|
// EVCS NIGHT ENGINE
|
||||||
|
// =====================================================`;
|
||||||
|
const END_MARKER = `const batteryTargetReached =
|
||||||
|
nightSessionActive &&
|
||||||
|
parsed.soc >= batteryMorningTargetPct;`;
|
||||||
|
|
||||||
|
const startIdx = b.indexOf(START_MARKER);
|
||||||
|
const endIdx = b.indexOf(END_MARKER);
|
||||||
|
if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {
|
||||||
|
throw new Error('Bornes du bloc EVCS NIGHT ENGINE introuvables dans BCL');
|
||||||
|
}
|
||||||
|
const removed = b.slice(startIdx, endIdx + END_MARKER.length);
|
||||||
|
console.log(`BCL: bloc dupliqué localisé (${removed.split('\n').length} lignes, ${removed.length} chars)`);
|
||||||
|
|
||||||
|
const CONSUME_BLOCK = `// =====================================================
|
||||||
|
// EVCS NIGHT ENGINE — R1 (07/08/2026)
|
||||||
|
// Le moteur nuit est calculé UNE SEULE FOIS dans WINDOWS_SOURCEV2
|
||||||
|
// (payload nightEvcsPreview). Ce node CONSOMME, il ne recalcule plus.
|
||||||
|
// TOUTE évolution de la logique nuit se fait dans WINDOWS_SOURCEV2.
|
||||||
|
// Fallback sûr si preview absent : session inactive / 'blocked'.
|
||||||
|
// =====================================================
|
||||||
|
const nightPrev = prev.nightEvcsPreview || {};
|
||||||
|
|
||||||
|
const nightSessionActive = !!nightPrev.nightSessionActive;
|
||||||
|
const nightSessionNeedsCapture = !!nightPrev.nightSessionNeedsCapture;
|
||||||
|
const nightSessionNeedsClear = !!nightPrev.nightSessionNeedsClear;
|
||||||
|
const nightSessionStartSocPct =
|
||||||
|
Number.isFinite(nightPrev.nightSessionStartSocPct) ? nightPrev.nightSessionStartSocPct : null;
|
||||||
|
const nightSessionStartAt = nightPrev.nightSessionStartAt || '';
|
||||||
|
const nightSessionStartMs =
|
||||||
|
Number.isFinite(nightPrev.nightSessionStartMs) ? nightPrev.nightSessionStartMs : null;
|
||||||
|
|
||||||
|
const batteryHardFloorPct =
|
||||||
|
Number.isFinite(nightPrev.batteryHardFloorPct)
|
||||||
|
? nightPrev.batteryHardFloorPct
|
||||||
|
: params.evcsNightBatteryHardFloorPct;
|
||||||
|
const batteryMorningTargetPct =
|
||||||
|
Number.isFinite(nightPrev.batteryMorningTargetPct)
|
||||||
|
? nightPrev.batteryMorningTargetPct
|
||||||
|
: params.evcsNightTargetWeakForecastPct;
|
||||||
|
const maxBatteryDischargePoints =
|
||||||
|
Number.isFinite(nightPrev.maxBatteryDischargePoints) ? nightPrev.maxBatteryDischargePoints : 0;
|
||||||
|
const gridTakeoverTriggerPct =
|
||||||
|
Number.isFinite(nightPrev.gridTakeoverTriggerPct)
|
||||||
|
? nightPrev.gridTakeoverTriggerPct
|
||||||
|
: Math.max(batteryHardFloorPct, batteryMorningTargetPct);
|
||||||
|
|
||||||
|
const batteryBelowHardFloor = !!nightPrev.batteryBelowHardFloor;
|
||||||
|
const batteryBelowMorningTarget = !!nightPrev.batteryBelowMorningTarget;
|
||||||
|
const batteryAtOrBelowGridTakeoverTrigger = !!nightPrev.batteryAtOrBelowGridTakeoverTrigger;
|
||||||
|
|
||||||
|
const batteryKwhMissingToTarget =
|
||||||
|
Number.isFinite(nightPrev.batteryKwhMissingToTarget) ? nightPrev.batteryKwhMissingToTarget : 0;
|
||||||
|
const batteryRecoveryMinutesNeeded =
|
||||||
|
Number.isFinite(nightPrev.batteryRecoveryMinutesNeeded) ? nightPrev.batteryRecoveryMinutesNeeded : 0;
|
||||||
|
const batteryRecoveryUrgent = !!nightPrev.batteryRecoveryUrgent;
|
||||||
|
const batteryRecoveryAllowedNow = !!nightPrev.batteryRecoveryAllowedNow;
|
||||||
|
const batteryTargetReached = !!nightPrev.batteryTargetReached;
|
||||||
|
|
||||||
|
const batteryAssistAllowed = !!nightPrev.batteryAssistAllowed;
|
||||||
|
const nightGridTakeoverNeeded = !!nightPrev.nightGridTakeoverNeeded;
|
||||||
|
const nightTakeoverLatched = !!nightPrev.nightTakeoverLatched;
|
||||||
|
const nightPreferredSourceMode = nightPrev.nightPreferredSourceMode || 'blocked';`;
|
||||||
|
|
||||||
|
b = b.slice(0, startIdx) + CONSUME_BLOCK + b.slice(endIdx + END_MARKER.length);
|
||||||
|
bNode.parameters.jsCode = b;
|
||||||
|
console.log('OK: BATTERY_CLOUD_LOCKS (bloc dupliqué remplacé par la consommation du preview)');
|
||||||
|
|
||||||
|
fs.writeFileSync(FILE, JSON.stringify(wf, null, 2) + '\n');
|
||||||
|
console.log(`Écrit: ${FILE}`);
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user