// EVCS Energy Balance — PREPARE_BALANCE // ===================================================================== // Colle de câblage n8n, et rien d'autre. Les nœuds de calcul (CENSORSHIP, // RESOURCE_ESTIMATE, BALANCE) sont volontairement écrits pour ne lire que // `$input`, afin de rester testables hors de n8n. Ce nœud rassemble donc ce // que BALANCE attend, en puisant dans les nœuds précédents. // // Il fait aussi la seule conversion nécessaire : les profils horaires vivent // dans des helpers `input_text` sous forme de CSV compact (24 valeurs, moins // de 255 caractères), il faut les redonner sous forme de tableaux. // ===================================================================== function parseCsv24(raw) { if (typeof raw !== 'string') return null; const parts = raw.split(',').map(v => Number.parseFloat(v.trim())); if (parts.length !== 24 || parts.some(v => !Number.isFinite(v))) return null; return parts; } // Le nœud HTTP de n8n renvoie le corps du template dans `data`, sous forme de // chaîne. Hors n8n (tests), l'objet arrive déjà décodé — on accepte les deux. function unwrapSnapshot(raw) { if (raw && typeof raw.data === 'string') { try { return JSON.parse(raw.data); } catch { return raw; } } return (raw && raw.data) || raw; } const snapshot = unwrapSnapshot($('HA Snapshot Balance').first().json); const resource = $input.first().json; return [{ json: { nowMs: Number.parseFloat(snapshot.nowMs), consumption_p50: parseCsv24(snapshot.consumption_p50_csv), consumption_p80: parseCsv24(snapshot.consumption_p80_csv), horizon: resource.horizon || [], soc_percent: snapshot.soc_percent, usable_capacity_kwh: snapshot.usable_capacity_kwh, modules_en_service: snapshot.modules_en_service, // Plancher dur : même valeur de repli que INPUT_PARSE.js // (evcs_night_battery_hard_floor_pct n'est aujourd'hui exposé par AUCUN // helper — la valeur « 22 % » documentée partout est en réalité un défaut // de code, cf. backlog du chantier). soc_floor_percent: 22, // Vigilance Météo-France, lue sans connaître la commune (l'entité // s'appelle sensor._weather_alert). Absente tant que // l'intégration n'est pas installée : le bilan publie alors // 'indisponible' et ne change rien. storm_alert_level: snapshot.storm_alert_level, storm_alert_detail: snapshot.storm_alert_detail, // Fenetres HC (28/09, « tenir jusqu'a la prochaine HC ») : horodatages // calcules par le gabarit depuis les helpers victron_evcs_hc_*. Sans cette // recopie, BALANCE.js ne les voit pas et la regle reste muette. // Plancher de charge HC manuelle (18/09) : oublie ici jusqu'au 28/09, le // bouton n'avait donc jamais atteint le bilan. manual_hc_floor_percent: snapshot.manual_hc_floor_percent, // Vehicule branche a midi (28/09, JEEP/VW/ZERO) : depart du jour et besoin saisi. vehicle_connected: snapshot.vehicle_connected, vehicle_departure_ms: snapshot.vehicle_departure_ms, vehicle_level_pct: snapshot.vehicle_level_pct, vehicle_level_valid: snapshot.vehicle_level_valid, vehicle_capacity_kwh: snapshot.vehicle_capacity_kwh, vehicle_charger_kw: snapshot.vehicle_charger_kw, vehicle_delivered_since_level_kwh: snapshot.vehicle_delivered_since_level_kwh, hc_window_end_ms: snapshot.hc_window_end_ms, next_hc_start_ms: snapshot.next_hc_start_ms, next_night_hc_start_ms: snapshot.next_night_hc_start_ms, // Repris pour l'écriture des helpers, sans influencer le calcul. resourceUsable: resource.usable, shapeConfidence: resource.source ? resource.source.shapeConfidence : null, }, }];