ZERO ajoutee au selecteur avec son heure de depart (P9). Le bilan lit la capacite et la puissance de charge mesurees du vehicule selectionne ; 0 = inconnue (regle muette / pas de borne). Bilan 64/64, golden 123/123. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
80 lines
3.6 KiB
JavaScript
80 lines
3.6 KiB
JavaScript
// 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.<commune>_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,
|
|
},
|
|
}];
|