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
63 lines
2.8 KiB
JavaScript
63 lines
2.8 KiB
JavaScript
// EVCS Energy Balance — BUILD_HA_CALLS_PROFILES
|
|
// =====================================================================
|
|
// Publie les quatre profils recalculés dans leurs helpers input_text.
|
|
//
|
|
// MÊME GARANTIE DE PÉRIMÈTRE que le workflow de bilan : ce nœud n'écrit que
|
|
// vers des entités `evcs_balance_*`, plus une trace logbook. La suite de tests
|
|
// échoue si une écriture en sort.
|
|
//
|
|
// Refus explicite de publier un profil douteux : si l'historique est trop court
|
|
// pour couvrir les 24 heures, on n'écrase PAS les profils en place. Mieux vaut
|
|
// un profil un peu ancien qu'un profil creux.
|
|
// =====================================================================
|
|
|
|
const profiles = $input.first().json;
|
|
|
|
function stampParis() {
|
|
const p = new Intl.DateTimeFormat('fr-FR', {
|
|
timeZone: 'Europe/Paris', day: '2-digit', month: '2-digit',
|
|
hour: '2-digit', minute: '2-digit', hour12: false,
|
|
}).formatToParts(new Date());
|
|
const g = t => p.find(x => x.type === t)?.value || '';
|
|
return `${g('day')}/${g('month')} ${g('hour')}:${g('minute')}`;
|
|
}
|
|
|
|
const calls = [];
|
|
function setText(entity, value) {
|
|
calls.push({ json: {
|
|
domain: 'input_text', service: 'set_value',
|
|
data: { entity_id: entity, value: (value ?? '').toString().slice(0, 250) },
|
|
} });
|
|
}
|
|
|
|
if (!profiles.complete) {
|
|
setText('input_text.evcs_balance_status',
|
|
`profils NON mis à jour : ${profiles.quality.unreliableHours.length} h sans assez de jours observés`);
|
|
calls.push({ json: {
|
|
domain: 'logbook', service: 'log',
|
|
data: {
|
|
name: 'EVCS Bilan — profils quotidiens',
|
|
message: `Recalcul refusé : historique insuffisant (${profiles.days} jour(s), heures creuses : ${profiles.quality.unreliableHours.join(', ')}). Profils précédents conservés.`,
|
|
entity_id: 'input_text.evcs_balance_status',
|
|
},
|
|
} });
|
|
return calls;
|
|
}
|
|
|
|
setText('input_text.evcs_balance_consumption_p50', profiles.csv.p50);
|
|
setText('input_text.evcs_balance_consumption_p80', profiles.csv.p80);
|
|
setText('input_text.evcs_balance_envelope', profiles.csv.envelope);
|
|
setText('input_text.evcs_balance_censorship_rate', profiles.csv.censorshipRate);
|
|
setText('input_text.evcs_balance_last_run', stampParis());
|
|
|
|
calls.push({ json: {
|
|
domain: 'logbook', service: 'log',
|
|
data: {
|
|
name: 'EVCS Bilan — profils quotidiens',
|
|
message: `Profils recalculés sur ${profiles.days} jours (${profiles.quality.hoursUsed} h de consommation, ${profiles.quality.pvHoursUsed} h de production, ${profiles.quality.countersReset} remise(s) à zéro écartée(s)). Consommation typique ${profiles.totals.typicalDailyKwh} kWh/j, prudente ${profiles.totals.prudentDailyKwh} kWh/j. Production démontrée ${profiles.production.demonstratedDailyKwh} kWh/j, ${profiles.production.censoredDayHours} heure(s) de jour censurée(s).`,
|
|
entity_id: 'input_text.evcs_balance_consumption_p50',
|
|
},
|
|
} });
|
|
|
|
return calls;
|