Workflow n8n "EVCS Energy Balance V1" (id QoSfPeyGq35orF1K), actif, cycle de 5 min : snapshot HA -> CENSORSHIP et RESOURCE_ESTIMATE en parallele -> PREPARE_BALANCE -> BALANCE -> BUILD_HA_CALLS -> appels de service HA. MODE OMBRE garanti structurellement : BUILD_HA_CALLS n'ecrit que vers des entites evcs_balance_* plus une trace logbook. La suite de tests echoue si une ecriture sort de ce perimetre — verifie par mutation, l'injection d'un turn_on sur switch.edf_general_ok_switch fait rougir la suite. Kill-switch input_boolean.evcs_balance_enabled : a OFF, aucune ecriture du tout. Package HA evcs_energy_balance.yaml : 18 helpers. Profils de consommation, enveloppe de production et taux de censure amorces avec les valeurs calculees sur 28 jours reels ; capacite utile posee a 15 kWh (7 modules actifs). Outillage : gen-energy-balance-workflow.mjs, enregistrement dans lib-node-map.mjs, suite cablee dans n8n-push.mjs (bloquante). Suite a 24 scenarios. Premiere execution reelle : le bilan s'est declare indisponible faute d'entrees, exactement comme prevu, au lieu d'inventer des valeurs. Le format du snapshot (corps JSON dans `data`) et la lecture des profils CSV ont ete corriges ensuite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
72 lines
3.0 KiB
JavaScript
72 lines
3.0 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' },
|
|
],
|
|
},
|
|
];
|
|
|
|
// 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));
|
|
}
|