La provocation solaire se demande aux chargeurs, pas au SOC
Le correctif precedent bloque l incident de 19:44, mais un excedent proche de zero est ambigu : c est la signature de la censure ET celle de l equilibre de fin de journee. Le releve du 03/09 contient les deux avec le meme excedent — 18:50 : +14 W, ressource refusee ; 19:41 : +1 W, plus rien a donner. Rejeu du 19:41 : batteryAssistSolarStartOk etait encore vrai. Trois minutes avant l incident, le systeme aurait commis la meme faute. Le plan annoncait de lire evcs_balance_pv_censored. Lecture de CENSORSHIP.js : ce drapeau est un OU de motifs dont l un est exactement le proxy qu on voulait remplacer (socPct >= 90 et batterie sans puits). Et il est reste ON jusqu au calcul de 19:50, soit cinq minutes APRES le demarrage de la charge — ce qui l a fait basculer n est pas la censure mais sun_elevation passant sous 10 degres. Il n aurait pas bloque l incident. Le signal juste, ce sont les chargeurs eux-memes : sept entites sensor.*_mppt_operation_mode, jamais lues par aucun workflow. mppt_active = je suis a mon point de puissance maximale, je donne tout ; voltage_current_limited ou off = je me retiens faute de preneur. Le 03/09 elles ont bascule a 19:20, soit 24 minutes avant pv_censored, sans aucun proxy et a la cadence de la boucle. Contre-verification le 01/09 a 14:22 : les trackers restaient limites PENDANT une charge de 3,4 kW sous 6,9 kW de PV, donc le signal ne s auto-annule pas a midi. Collecte sans aucun entity_id code en dur (selectattr sur mppt et operation_mode), expression testee sur /api/template puis gabarit complet re-rendu : 191 champs, JSON valide, aucun null. Lectures inexploitables ecartees, et repli sur le comportement anterieur quand il n en reste aucune. Garde de DEMARRAGE uniquement, structurellement : la lier au sourceMode ou au maintien ferait que la charge, en donnant un preneur au MPPT, detruirait sa propre condition d autorisation — le bistable de l abondance P6 corrige le 24/08. Golden 64 -> 66. Mutation dans trois directions : garde neutralisee -> 1 echec cible, provocation toujours refusee -> 3 echecs, lecture illisible traitee comme "a fond" -> 1 echec sur le scenario de repli. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
@@ -607,6 +607,43 @@ const commonSafetyHoldOk =
|
||||
// recreerait l'oscillateur qu'on vient d'eteindre.
|
||||
const solarSurplusW = parsed.pvPowerW - derived.houseNonEvcsW;
|
||||
|
||||
// LA PROVOCATION DOIT ETRE JUSTIFIEE PAR LES CHARGEURS EUX-MEMES (03/09/2026)
|
||||
// =====================================================================
|
||||
// Un excedent proche de zero est AMBIGU : c'est la signature de la censure
|
||||
// (production bridee, calee sur la maison) ET celle de l'equilibre de fin de
|
||||
// journee (production reelle qui rejoint la consommation). Le releve du 03/09
|
||||
// contient les deux a 51 minutes d'ecart, avec le meme excedent :
|
||||
//
|
||||
// 18:50 PV 769 W, maison 755 W, excedent +14 W -> 7 trackers LIMITES
|
||||
// 19:41 PV 817 W, maison 816 W, excedent +1 W -> 7 trackers MPPT_ACTIVE
|
||||
//
|
||||
// Le premier cache de la ressource refusee, le second n'a plus rien a donner.
|
||||
// Aucun seuil de puissance ne les separe. Les chargeurs, eux, le disent :
|
||||
// 'mppt_active' = je suis a mon point de puissance maximale, je donne tout ;
|
||||
// 'voltage_current_limited' / 'off' = je me retiens faute de preneur.
|
||||
//
|
||||
// Signal DIRECT (le materiel lui-meme, pas un proxy), INSTANTANE (30 s, la
|
||||
// cadence de la boucle) et, sur l'incident du 03/09, il a bascule a 19:20 —
|
||||
// 24 minutes avant le drapeau `pv_censored` du bilan energetique, qui tourne
|
||||
// toutes les 5 min et dont l'un des motifs est justement le proxy SOC >= 90
|
||||
// qu'on cherchait a remplacer.
|
||||
//
|
||||
// ⚠️ DEMARRAGE UNIQUEMENT, et c'est structurel, pas une precaution :
|
||||
// gager le sourceMode ou le MAINTIEN sur ce signal ferait que la charge, en
|
||||
// donnant un preneur au MPPT, detruirait sa propre condition d'autorisation —
|
||||
// exactement le bistable de l'abondance P6 corrige le 24/08. La garde n'est
|
||||
// lue que quand la voiture est a l'arret, instant ou l'etat des trackers
|
||||
// decrit la situation SANS elle. Verifie le 01/09 a 14:22-14:34 : les trackers
|
||||
// restaient limites PENDANT une charge de 3,4 kW sous 6,9 kW de PV — a midi la
|
||||
// ressource depasse ce que la voiture peut prendre, le signal ne s'auto-annule
|
||||
// donc pas non plus.
|
||||
//
|
||||
// Aucune lecture exploitable (integration muette) => on ne sait pas => on
|
||||
// retombe sur le comportement anterieur. On ne devine jamais un etat.
|
||||
const mpptReadable = parsed.mpptReadableCount > 0;
|
||||
const mpptAllAtMaxPower = mpptReadable && parsed.mpptHoldingBackCount === 0;
|
||||
const solarProvocationJustified = !mpptAllAtMaxPower;
|
||||
|
||||
// Chemin PROVOCATION (batterie haute, ou sourceMode solar_potential) : on
|
||||
// n'exige pas d'excedent mesurable — il est censure par construction — mais on
|
||||
// exige que la batterie ne porte pas deja la maison.
|
||||
@@ -615,7 +652,7 @@ const solarSurplusProvocationOk = solarSurplusW >= 0;
|
||||
// Chemin SOLEIL REEL : l'excedent doit atteindre le seuil de demarrage.
|
||||
const solarSurplusStartOk =
|
||||
context.sourceMode === 'solar_potential'
|
||||
? solarSurplusProvocationOk
|
||||
? (solarSurplusProvocationOk && solarProvocationJustified)
|
||||
: solarSurplusW >= context.effectivePvStartMinW;
|
||||
|
||||
// =====================================================
|
||||
@@ -650,6 +687,7 @@ const batteryAssistSolarStartOk =
|
||||
parsed.soc > batteryAssistSolarHoldSocMin &&
|
||||
parsed.batteryCurrentA >= profileBatteryDischargeFloorA &&
|
||||
solarSurplusProvocationOk &&
|
||||
solarProvocationJustified &&
|
||||
cloudDebtNextKwh <= batteryAssistSolarDebtStartMaxKwh &&
|
||||
!cloudDebtActive;
|
||||
|
||||
@@ -1023,6 +1061,12 @@ return [{
|
||||
solarSurplusW,
|
||||
solarSurplusProvocationOk,
|
||||
solarSurplusStartOk,
|
||||
|
||||
// Etat declare par les chargeurs solaires (03/09/2026).
|
||||
mpptReadableCount: parsed.mpptReadableCount,
|
||||
mpptHoldingBackCount: parsed.mpptHoldingBackCount,
|
||||
mpptAllAtMaxPower,
|
||||
solarProvocationJustified,
|
||||
batteryCurrentHardExceeded,
|
||||
mustStopHard,
|
||||
gridBatteryProtectionStop,
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
"pv_power_10m_avg_w": states("sensor.victron_pv_power_10m_avg"),
|
||||
"pv_trend_w": states("sensor.victron_pv_power_trend_w"),
|
||||
|
||||
"mppt_operation_modes": states.sensor
|
||||
| selectattr('entity_id','search','mppt')
|
||||
| selectattr('entity_id','search','operation_mode')
|
||||
| map(attribute='state') | join(','),
|
||||
|
||||
"house_ac_power_w": states("sensor.victron_consumption_power_l1"),
|
||||
"house_ac_power_prev_w": states("sensor.victron_consumption_power_l1"),
|
||||
|
||||
|
||||
@@ -236,6 +236,25 @@ const parsed = {
|
||||
batteryCurrentA: num(input.battery_current_a, 0),
|
||||
|
||||
pvPowerW: Math.max(0, num(input.pv_power_w, 0)),
|
||||
|
||||
// MODE DE FONCTIONNEMENT DES CHARGEURS SOLAIRES (03/09/2026)
|
||||
// Chaque tracker declare lui-meme s'il est a son point de puissance maximale
|
||||
// ('mppt_active' : il donne tout ce qu'il a) ou s'il se retient
|
||||
// ('voltage_current_limited', 'off' : il y a de la ressource REFUSEE).
|
||||
// Lectures inexploitables (integration muette) => listes vides => les gardes
|
||||
// aval retombent sur le comportement anterieur. On ne devine pas un etat.
|
||||
...(() => {
|
||||
const modes = String(input.mppt_operation_modes || '')
|
||||
.split(',')
|
||||
.map(s => s.trim().toLowerCase())
|
||||
.filter(s => s !== '' && s !== 'unavailable' && s !== 'unknown');
|
||||
const atMaxPower = modes.filter(s => s === 'mppt_active').length;
|
||||
return {
|
||||
mpptReadableCount: modes.length,
|
||||
mpptAtMaxPowerCount: atMaxPower,
|
||||
mpptHoldingBackCount: modes.length - atMaxPower,
|
||||
};
|
||||
})(),
|
||||
pvPowerAvg10mW: Math.max(0, num(input.pv_power_10m_avg_w, num(input.pv_power_w, 0))),
|
||||
pvTrendW: num(input.pv_trend_w, 0),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user