Files
EVCS/tools/lib-node-map.mjs
Tallagrand Thierry 6dc5ea25cb Chantier etapes 5b et 5c : recalcul quotidien des profils et fenetre de deficit suivante
5c — BALANCE ne repondait qu'a "combien jusqu'a ce que le solaire prenne le
relais". A 15 h la reponse est zero : exacte mais inutile, le deficit qui
approche etant celui de la nuit. Ajout de la prochaine traversee deficitaire
(heure de debut, energie, SOC a atteindre avant). Ce SOC n'est PAS ecrete a
100 % : au-dela, meme une batterie pleine ne passe pas, et le manque est
publie comme storage_shortfall_kwh — c'est le chiffre du dimensionnement.

5b — Workflow "EVCS Energy Profiles Daily V1" (N6UaB4ZozsbRPrFb), actif,
03:30 : lit /api/history/period sur 30 jours glissants et republie les quatre
CSV (conso p50 et p80, enveloppe de production, taux de censure).
CONSUMPTION_PROFILE devient DAILY_PROFILES et sait desormais decoder la
reponse brute de l'API en plus des series deja pretes.

Piege trouve : sans end_time, /api/history/period ne renvoie qu'UNE journee,
silencieusement. Mes premieres mesures "sur 3 jours" n'en couvraient qu'une.

Validation croisee : profils calcules depuis l'API REST (etats bruts, 11 j)
contre profils calcules depuis les statistiques long terme (28 j) — 0,20 kWh/h
d'ecart absolu moyen, par deux chemins de donnees independants.

BUILD_HA_CALLS_PROFILES refuse d'ecraser les profils en place si l'historique
ne couvre pas les 24 heures. Meme garantie de perimetre que le bilan.
Suite a 27 scenarios.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-08-24 15:58:10 +02:00

81 lines
3.3 KiB
JavaScript

// R2 — Cartographie src/ ↔ workflows JSON (source de vérité : src/).
// Multi-workflows depuis l'Advisor (08/2026).
// Utilisé par tools/n8n-extract.mjs (JSON → src) et tools/n8n-build.mjs (src → JSON).
export const WORKFLOWS = [
{
id: 'evcs',
workflowFile: 'workflows/evcs-surplus-controller-v2.6.json',
srcDir: 'src/nodes',
nodes: [
{ file: 'INPUT_PARSE.js', node: 'INPUT_PARSE', kind: 'jsCode' },
{ file: 'WINDOWS_SOURCEV2.js', node: 'WINDOWS_SOURCEV2', kind: 'jsCode' },
{ file: 'BATTERY_CLOUD_LOCKS.js', node: 'BATTERY_CLOUD_LOCKS', kind: 'jsCode' },
{ file: 'DECISION_ENGINEV2.js', node: 'DECISION_ENGINEV2', kind: 'jsCode' },
{ file: 'HA_SERVICE_CALLS_OUTPUT.js', node: 'HA_SERVICE_CALLS_OUTPUT', kind: 'jsCode' },
{ file: 'BUILD_ACTIONS.js', node: 'Build EVCS Surplus Controller V2.0 Actions', kind: 'jsCode' },
{ file: 'HA_SNAPSHOT_TEMPLATE.json.tpl', node: 'HA Snapshot EVCS Surplus Controller V2.2', kind: 'template' },
],
},
{
id: 'advisor',
workflowFile: 'workflows/evcs-advisor-shadow-v1.json',
srcDir: 'src/advisor',
nodes: [
{ file: 'BUILD_PROMPT.js', node: 'BUILD_PROMPT', kind: 'jsCode' },
{ file: 'VALIDATE_PARSE.js', node: 'VALIDATE_PARSE', kind: 'jsCode' },
{ file: 'BUILD_HA_CALLS.js', node: 'BUILD_HA_CALLS', kind: 'jsCode' },
{ file: 'HA_SNAPSHOT_TEMPLATE.json.tpl', node: 'HA Snapshot Advisor', kind: 'template' },
],
},
{
id: 'energy-balance',
workflowFile: 'workflows/evcs-energy-balance-v1.json',
srcDir: 'src/energy-balance',
nodes: [
{ file: 'CENSORSHIP.js', node: 'CENSORSHIP', kind: 'jsCode' },
{ file: 'RESOURCE_ESTIMATE.js', node: 'RESOURCE_ESTIMATE', kind: 'jsCode' },
{ file: 'PREPARE_BALANCE.js', node: 'PREPARE_BALANCE', kind: 'jsCode' },
{ file: 'BALANCE.js', node: 'BALANCE', kind: 'jsCode' },
{ file: 'BUILD_HA_CALLS.js', node: 'BUILD_HA_CALLS', kind: 'jsCode' },
{ file: 'HA_SNAPSHOT_TEMPLATE.json.tpl', node: 'HA Snapshot Balance', kind: 'template' },
],
},
{
id: 'energy-profiles',
workflowFile: 'workflows/evcs-energy-profiles-daily-v1.json',
srcDir: 'src/energy-balance',
nodes: [
{ file: 'DAILY_PROFILES.js', node: 'DAILY_PROFILES', kind: 'jsCode' },
{ file: 'BUILD_HA_CALLS_PROFILES.js', node: 'BUILD_HA_CALLS_PROFILES', kind: 'jsCode' },
],
},
];
// Rétro-compatibilité (outils historiques mono-workflow)
export const WORKFLOW_FILE = WORKFLOWS[0].workflowFile;
export const SRC_DIR = WORKFLOWS[0].srcDir;
export const NODE_MAP = WORKFLOWS[0].nodes;
export function getCode(workflowNode, kind) {
if (kind === 'jsCode') return workflowNode.parameters.jsCode;
if (kind === 'template') {
return workflowNode.parameters.bodyParameters.parameters.find(p => p.name === 'template').value;
}
throw new Error(`kind inconnu: ${kind}`);
}
export function setCode(workflowNode, kind, code) {
if (kind === 'jsCode') { workflowNode.parameters.jsCode = code; return; }
if (kind === 'template') {
workflowNode.parameters.bodyParameters.parameters.find(p => p.name === 'template').value = code;
return;
}
throw new Error(`kind inconnu: ${kind}`);
}
export function findWorkflowByFile(inputPath) {
const base = inputPath.replace(/\\/g, '/').split('/').pop();
return WORKFLOWS.find(w => w.workflowFile.endsWith(base));
}