Workflow "EVCS Solar Forecast V1" (07P8I349ugna9FH4), actif, cadence 15 min — la frequence de rafraichissement de la prevision elle-meme, l'API precisant qu'interroger plus souvent n'a aucun interet. 96 appels par jour pour un quota de 300 par heure. Rien n'est code en dur : la cle d'API, les plans et les coordonnees viennent tous de Home Assistant. La cle vit dans un input_text en mode password, exclue du recorder, et n'apparait ni dans le depot ni dans le JSON du workflow — l'URL est assemblee a l'execution par BUILD_FORECAST_URL, qui refuse explicitement de construire une URL incomplete plutot que de gaspiller le quota sur un appel malforme. BUILD_HA_CALLS_FORECAST ne publie rien en cas d'echec plutot que d'ecraser une prevision valide par du vide. Suite portee a 33 scenarios, dont le refus sur cle absente, sur plans mal formes, sur kill-switch, et la preservation de la prevision precedente en cas d'echec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
135 lines
4.6 KiB
JavaScript
135 lines
4.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// Génère le squelette du workflow "EVCS Solar Forecast V1" : récupération de la
|
|
// courbe horaire Forecast.Solar (abonnement Professional) et publication dans
|
|
// les helpers du bilan.
|
|
//
|
|
// Cadence : 15 min. C'est la fréquence de rafraîchissement de la prévision
|
|
// elle-même — l'API précise qu'interroger plus souvent n'a aucun intérêt.
|
|
// 96 appels par jour pour un quota de 300 par heure.
|
|
//
|
|
// La clé d'API n'apparaît NULLE PART ici : elle est lue dans un helper Home
|
|
// Assistant par le snapshot, et l'URL est assemblée par BUILD_FORECAST_URL.
|
|
|
|
import fs from 'node:fs';
|
|
|
|
const HA_CRED = { id: 'uv3sknYYigxBkaVN', name: 'Bearer Auth account 2' };
|
|
const HA_URL = 'http://10.10.10.17:8123';
|
|
|
|
const nodes = [
|
|
{
|
|
name: 'Schedule Forecast',
|
|
type: 'n8n-nodes-base.scheduleTrigger',
|
|
typeVersion: 1.2,
|
|
position: [0, 0],
|
|
parameters: { rule: { interval: [{ field: 'minutes', minutesInterval: 15 }] } },
|
|
},
|
|
{
|
|
name: 'HA Snapshot Forecast',
|
|
type: 'n8n-nodes-base.httpRequest',
|
|
typeVersion: 4.2,
|
|
position: [220, 0],
|
|
parameters: {
|
|
method: 'POST',
|
|
url: `${HA_URL}/api/template`,
|
|
authentication: 'genericCredentialType',
|
|
genericAuthType: 'httpBearerAuth',
|
|
sendHeaders: true,
|
|
headerParameters: {
|
|
parameters: [
|
|
{ name: 'Content-Type', value: 'application/json' },
|
|
{ name: 'Accept', value: 'text/plain' },
|
|
],
|
|
},
|
|
sendBody: true,
|
|
bodyParameters: { parameters: [{ name: 'template', value: '(injecté depuis src/energy-balance)' }] },
|
|
options: { timeout: 15000 },
|
|
},
|
|
credentials: { httpBearerAuth: HA_CRED },
|
|
},
|
|
{
|
|
name: 'BUILD_FORECAST_URL',
|
|
type: 'n8n-nodes-base.code',
|
|
typeVersion: 2,
|
|
position: [440, 0],
|
|
parameters: { jsCode: '// injecté depuis src/energy-balance/BUILD_FORECAST_URL.js' },
|
|
},
|
|
{
|
|
name: 'Forecast.Solar',
|
|
type: 'n8n-nodes-base.httpRequest',
|
|
typeVersion: 4.2,
|
|
position: [660, 0],
|
|
parameters: {
|
|
method: 'GET',
|
|
// URL assemblée par le nœud précédent : la clé ne transite pas par ce fichier.
|
|
url: '={{ $json.url }}',
|
|
options: {
|
|
timeout: 30000,
|
|
response: { response: { responseFormat: 'text', neverError: true } },
|
|
},
|
|
},
|
|
// Si l'URL est nulle (clé absente, kill-switch), l'appel échoue sans casser
|
|
// la chaîne : PARSE_FORECAST le détectera et rien ne sera publié.
|
|
onError: 'continueRegularOutput',
|
|
},
|
|
{
|
|
name: 'PARSE_FORECAST',
|
|
type: 'n8n-nodes-base.code',
|
|
typeVersion: 2,
|
|
position: [880, 0],
|
|
parameters: { jsCode: '// injecté depuis src/energy-balance/PARSE_FORECAST.js' },
|
|
},
|
|
{
|
|
name: 'BUILD_HA_CALLS_FORECAST',
|
|
type: 'n8n-nodes-base.code',
|
|
typeVersion: 2,
|
|
position: [1100, 0],
|
|
parameters: { jsCode: '// injecté depuis src/energy-balance/BUILD_HA_CALLS_FORECAST.js' },
|
|
},
|
|
{
|
|
name: 'Execute HA Service Forecast',
|
|
type: 'n8n-nodes-base.httpRequest',
|
|
typeVersion: 4.2,
|
|
position: [1320, 0],
|
|
parameters: {
|
|
method: 'POST',
|
|
url: `={{ '${HA_URL}/api/services/' + $json.domain + '/' + $json.service }}`,
|
|
authentication: 'genericCredentialType',
|
|
genericAuthType: 'httpBearerAuth',
|
|
sendHeaders: true,
|
|
headerParameters: {
|
|
parameters: [
|
|
{ name: 'Content-Type', value: 'application/json' },
|
|
{ name: 'accept', value: 'application/json' },
|
|
],
|
|
},
|
|
sendBody: true,
|
|
specifyBody: 'json',
|
|
jsonBody: '={{ $json.data }}',
|
|
options: { timeout: 15000 },
|
|
},
|
|
credentials: { httpBearerAuth: HA_CRED },
|
|
onError: 'continueRegularOutput',
|
|
},
|
|
];
|
|
|
|
const connections = {
|
|
'Schedule Forecast': { main: [[{ node: 'HA Snapshot Forecast', type: 'main', index: 0 }]] },
|
|
'HA Snapshot Forecast': { main: [[{ node: 'BUILD_FORECAST_URL', type: 'main', index: 0 }]] },
|
|
BUILD_FORECAST_URL: { main: [[{ node: 'Forecast.Solar', type: 'main', index: 0 }]] },
|
|
'Forecast.Solar': { main: [[{ node: 'PARSE_FORECAST', type: 'main', index: 0 }]] },
|
|
PARSE_FORECAST: { main: [[{ node: 'BUILD_HA_CALLS_FORECAST', type: 'main', index: 0 }]] },
|
|
BUILD_HA_CALLS_FORECAST: { main: [[{ node: 'Execute HA Service Forecast', type: 'main', index: 0 }]] },
|
|
};
|
|
|
|
const workflow = {
|
|
id: '07P8I349ugna9FH4',
|
|
name: 'EVCS Solar Forecast V1',
|
|
nodes,
|
|
connections,
|
|
settings: { executionOrder: 'v1', callerPolicy: 'workflowsFromSameOwner' },
|
|
staticData: null,
|
|
};
|
|
|
|
fs.writeFileSync('workflows/evcs-solar-forecast-v1.json', JSON.stringify(workflow, null, 2) + '\n');
|
|
console.log(`Squelette écrit : workflows/evcs-solar-forecast-v1.json (${nodes.length} nodes)`);
|