P7: charge HC nuit automatique si véhicule branché + toggle 'Réseau HC nuit' ON
- Root cause 1: battery_assist_night ne démarrait jamais (floor décharge 0A impossible la nuit) -> idle_wait_conditions jusqu'à dérive du SOC au trigger. - Root cause 2: allow_grid_hc_night parsé mais jamais utilisé. - Fix: nightGridHcPreferred dans WINDOWS_SOURCEV2 + BATTERY_CLOUD_LOCKS -> grid_hc_allowed dès l'ouverture de la fenêtre HC (EDF ON auto, ACM hold 2A). - Toggle OFF = comportement historique. Dry-run 7 scénarios validés. - Outils: n8n-pull/n8n-push (push REST direct, backup auto, node --check). - Déployé en production le 07/08/2026 15:45, exécutions vérifiées success.
This commit is contained in:
178
tools/dryrun-20260807-night-hc.mjs
Normal file
178
tools/dryrun-20260807-night-hc.mjs
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env node
|
||||
// Dry-run du patch nightGridHcPreferred : exécute le jsCode réel de WINDOWS_SOURCEV2
|
||||
// avec un snapshot simulé (nuit HC, véhicule branché) et affiche la décision nuit.
|
||||
|
||||
import fs from 'fs';
|
||||
import vm from 'vm';
|
||||
|
||||
const wf = JSON.parse(fs.readFileSync('workflows/evcs-surplus-controller-v2.6.json', 'utf8'));
|
||||
const windowsCode = wf.nodes.find(n => n.name === 'WINDOWS_SOURCEV2').parameters.jsCode;
|
||||
|
||||
function buildPrev({ soc, allowGridHcNight, vehicleConnected, minNow, hour, batteryCurrentA, takeoverPrev, gridAvailable }) {
|
||||
return {
|
||||
input: { night_takeover_prev: takeoverPrev ? 'on' : 'off' },
|
||||
nowMs: Date.now(),
|
||||
now: { minNow, hour, minute: minNow % 60, stamp: '2026-08-07 01:30:00', date: '2026-08-07' },
|
||||
cfg: {
|
||||
batteryUsableCapacityKwh: 17.5,
|
||||
nominalAcVoltageV: 230,
|
||||
systemReserveW: 300,
|
||||
evcsDedicatedMaxW: 4900,
|
||||
vebusDcVoltageHardFloorV: 47,
|
||||
vebusDcVoltageSoftFloorV: 48,
|
||||
},
|
||||
parsed: {
|
||||
soc,
|
||||
batteryCurrentA,
|
||||
pvPowerW: 0,
|
||||
pvPowerAvg10mW: 0,
|
||||
pvTrendW: 0,
|
||||
houseLoadW: 700,
|
||||
houseAcPowerW: 700,
|
||||
houseAcPowerPrevW: 700,
|
||||
evcsAcPowerW: 0,
|
||||
vebusAcOutPowerW: 700,
|
||||
vebusAcOutVoltageV: 230,
|
||||
acInAvailable: false,
|
||||
evcsPowerW: 0,
|
||||
evcsVehicleConnected: vehicleConnected,
|
||||
evcsDeviceConnected: true,
|
||||
evcsChargingSwitchOn: false,
|
||||
evcsModeIsAuto: false,
|
||||
controlLatch: false,
|
||||
gridAvailable: gridAvailable ?? false,
|
||||
acVoltageV: 230,
|
||||
batteryVoltageV: 51,
|
||||
vebusAcOutW: 700,
|
||||
vebusDcVoltageV: 51,
|
||||
todayRemainingKwh: 0,
|
||||
tomorrowKwh: 25,
|
||||
lastStartAt: '',
|
||||
lastStopAt: '',
|
||||
lastCurrentChangeAt: '',
|
||||
probeStartedAt: '',
|
||||
departureTime: '08:50:00',
|
||||
departurePriority: true,
|
||||
requestedEnergyKwh: 10.5,
|
||||
currentSetA: 6,
|
||||
},
|
||||
params: {
|
||||
userStrategyRaw: 'auto_solar_plus',
|
||||
allowGridHcNight,
|
||||
allowGridHcMidday: true,
|
||||
hcNightStartMin: 1 * 60 + 24,
|
||||
hcNightStopMin: 7 * 60 + 24,
|
||||
morningStartMin: 7 * 60 + 30,
|
||||
morningStopMin: 11 * 60,
|
||||
hcMiddayStartMin: 12 * 60 + 24,
|
||||
hcMiddayStopMin: 14 * 60 + 24,
|
||||
eveningStartMin: 17 * 60,
|
||||
eveningStopMin: 20 * 60 + 30,
|
||||
currentMinA: 6,
|
||||
currentMaxA: 22,
|
||||
currentMaxAutoA: 22,
|
||||
gridCurrentMaxA: 20,
|
||||
acLoadsMaxSafeW: 8500,
|
||||
houseAcHeadroomW: 3500,
|
||||
endOfDaySocGuard: 30,
|
||||
endOfDaySocTarget: 45,
|
||||
pvStartMinW: 1000,
|
||||
pvHoldMinW: 700,
|
||||
probeEnabled: true,
|
||||
socStartMin: 80,
|
||||
socStopMin: 25,
|
||||
morningSocMin: 35,
|
||||
middaySocMin: 75,
|
||||
eveningSocStartMin: 85,
|
||||
eveningSocStopMin: 60,
|
||||
batteryDischargeSoftA: 0,
|
||||
batteryDischargeHardA: -15,
|
||||
morningBatteryMaxDischargeA: -5,
|
||||
middayBatteryMaxDischargeA: -8,
|
||||
eveningBatteryMaxDischargeA: -10,
|
||||
gridBatterySoftA: -3,
|
||||
gridBatteryHardA: -6,
|
||||
cloudDebtMaxKwh: 1.2,
|
||||
evcsNightBatteryHardFloorPct: 22,
|
||||
evcsNightStrongForecastKwh: 18,
|
||||
evcsNightMediumForecastKwh: 10,
|
||||
evcsNightTargetStrongForecastPct: 35,
|
||||
evcsNightTargetMediumForecastPct: 55,
|
||||
evcsNightTargetWeakForecastPct: 85,
|
||||
evcsNightBatteryRecoveryPowerKw: 1.5,
|
||||
evcsNightDepartureSafetyMin: 45,
|
||||
morningProbePvAvgMinW: 200,
|
||||
morningProbePvTrendMinW: 20,
|
||||
middayProbePvAvgMinW: 300,
|
||||
afternoonProbePvAvgMinW: 300,
|
||||
probeCurrentA: 10,
|
||||
},
|
||||
history: {
|
||||
nightSessionStartSocPct: soc,
|
||||
nightSessionStartAt: '2026-08-07 01:24:00',
|
||||
nightSessionStartMs: Date.now() - 6 * 60000,
|
||||
lastBlockReason: 'none',
|
||||
blockedUntilAt: '',
|
||||
},
|
||||
batteryTune: {
|
||||
global: { stepUpCooldownSeconds: 90, stepDownCooldownSeconds: 45 },
|
||||
profile: {},
|
||||
bands: {
|
||||
gt90: { dischargeSoftA: -50, dischargeHardA: -100, chargeReserveA: 5, softToleranceMin: 12, hardToleranceSec: 20, recoveryMin: 6 },
|
||||
'80_90': { dischargeSoftA: -40, dischargeHardA: -80, chargeReserveA: 10, softToleranceMin: 10, hardToleranceSec: 20, recoveryMin: 6 },
|
||||
'60_80': { dischargeSoftA: -20, dischargeHardA: -50, chargeReserveA: 40, softToleranceMin: 6, hardToleranceSec: 15, recoveryMin: 8 },
|
||||
'40_60': { dischargeSoftA: -12, dischargeHardA: -30, chargeReserveA: 70, softToleranceMin: 4, hardToleranceSec: 10, recoveryMin: 10 },
|
||||
'25_40': { dischargeSoftA: -5, dischargeHardA: -15, chargeReserveA: 110, softToleranceMin: 2, hardToleranceSec: 5, recoveryMin: 12 },
|
||||
lt25: { dischargeSoftA: -3, dischargeHardA: -8, chargeReserveA: 150, softToleranceMin: 1, hardToleranceSec: 2, recoveryMin: 15 },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function run(code, prev) {
|
||||
const ctx = {
|
||||
$input: { first: () => ({ json: prev }) },
|
||||
console,
|
||||
Date,
|
||||
Intl,
|
||||
Math,
|
||||
Number,
|
||||
Object,
|
||||
String,
|
||||
JSON,
|
||||
};
|
||||
vm.createContext(ctx);
|
||||
const result = vm.runInContext(`(function(){\n${code}\n})()`, ctx);
|
||||
return result[0].json;
|
||||
}
|
||||
|
||||
const scenarios = [
|
||||
{ label: 'Nuit HC 01:30, SOC 53, VW branchée, toggle HC nuit ON (nuit dernière)', soc: 53, allowGridHcNight: true, vehicleConnected: true, minNow: 90, hour: 1, batteryCurrentA: -14, takeoverPrev: false },
|
||||
{ label: 'Nuit HC 01:30, SOC 53, VW branchée, toggle HC nuit OFF (comportement historique)', soc: 53, allowGridHcNight: false, vehicleConnected: true, minNow: 90, hour: 1, batteryCurrentA: -14, takeoverPrev: false },
|
||||
{ label: 'Nuit HC 01:30, SOC 30 (< cible 35), branchée, toggle ON', soc: 30, allowGridHcNight: true, vehicleConnected: true, minNow: 90, hour: 1, batteryCurrentA: -14, takeoverPrev: false },
|
||||
{ label: 'Nuit HC 01:30, pas de véhicule, toggle ON', soc: 53, allowGridHcNight: true, vehicleConnected: false, minNow: 90, hour: 1, batteryCurrentA: -14, takeoverPrev: false },
|
||||
{ label: 'Jour 15:00, SOC 53, branchée, toggle ON (hors fenêtre nuit)', soc: 53, allowGridHcNight: true, vehicleConnected: true, minNow: 900, hour: 15, batteryCurrentA: 10, takeoverPrev: false },
|
||||
{ label: 'Nuit HC 03:00, takeover déjà engagé (latch), SOC 40, toggle ON', soc: 40, allowGridHcNight: true, vehicleConnected: true, minNow: 180, hour: 3, batteryCurrentA: 2, takeoverPrev: true },
|
||||
{ label: 'Nuit HC 02:00, EDF ON (cycle suivant), SOC 53, batterie tenue +2A, toggle ON', soc: 53, allowGridHcNight: true, vehicleConnected: true, minNow: 120, hour: 2, batteryCurrentA: 2, takeoverPrev: true, gridAvailable: true },
|
||||
];
|
||||
|
||||
for (const sc of scenarios) {
|
||||
const out = run(windowsCode, buildPrev(sc));
|
||||
const n = out.nightEvcsPreview;
|
||||
console.log(`\n=== ${sc.label}`);
|
||||
console.log(` profile=${out.context.profile} | sourceMode=${out.context.sourceMode}`);
|
||||
console.log(` nightSessionActive=${n.nightSessionActive} | batteryAssistAllowed=${n.batteryAssistAllowed}`);
|
||||
console.log(` nightGridTakeoverNeeded=${n.nightGridTakeoverNeeded} | nightPreferredSourceMode=${n.nightPreferredSourceMode}`);
|
||||
console.log(` target=${n.batteryMorningTargetPct}% trigger=${n.gridTakeoverTriggerPct}%`);
|
||||
|
||||
// Chaînage BATTERY_CLOUD_LOCKS sur la sortie de WINDOWS_SOURCEV2
|
||||
const bCode = wf.nodes.find(x => x.name === 'BATTERY_CLOUD_LOCKS').parameters.jsCode;
|
||||
try {
|
||||
const bOut = run(bCode, out);
|
||||
const c = bOut.conditions || {};
|
||||
const ne = bOut.nightEvcs || {};
|
||||
console.log(` [BCL] nightPreferred=${ne.nightPreferredSourceMode} takeoverNeeded=${ne.nightGridTakeoverNeeded} | gridStartOk=${c.gridStartOk} (gridAvailable=${sc.gridAvailable ?? false}) | assistNightStartOk=${c.batteryAssistNightStartOk}`);
|
||||
} catch (e) {
|
||||
console.log(` [BCL] échec exécution: ${e.message}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user