Deux constats du brief moteur, qui touchent le même fichier de lecture. 1. kCodesReserveBatterie était vide, et les clés cherchées n'ont JAMAIS existé côté plugin : batteryLevel/soc et threshold/batteryLevelConsideration contre socPercent, reservePercent, withheldW. Le motif sortait donc en repli — c'est-à-dire en anglais brut — sur le seul cas que le client ne peut déduire d'aucune mesure : « il y a du surplus, une règle l'interdit ». Vérifié contre decisionreason.h. Les trois paramètres sont des entiers DÉJÀ en pourcentage : reservePercent vaut 40, pas 0,4 — à ne pas confondre avec batteryLevelConsideration, qui est la fraction du réglage. Et withheldW mène la phrase, comme côté plugin : le moteur en fait la condition d'émission du motif, parce qu'un motif à 0 W dirait « une règle vous bloque » là où la vérité est « il n'y a pas de surplus ». Le motif n'a PAS pu être observé en vrai sur .75 : la bascule du seuil à 0,9 a produit surplusW à −3 551 W, donc rien à confisquer, donc garde du plugin active et motif tu — ce qui est le comportement juste. Il faut du surplus réel sous le seuil. Seuil remis à 0,4. 2. funding (« surplus » / « grid ») est lu, et avec lui l'identité que le plugin déclare : somme des allocatedW financés au surplus == budget.allocatedW. Sommer sans filtrer compte une borne servie au réseau et creuse un écart qu'on ne peut pas interpréter. Absent = surplus, pas zéro : c'est le seul financement qui existait avant ce champ. 3. Les bornes sont entrées dans loads[] (+etm22), et adjustEvChargers() re-décide après le waterfall : pour une borne, allocatedW et son motif sont la DÉCISION DE L'ARBITRE, pas l'état de la borne. L'écran affiche donc d'abord ce que le matériel fait (mechanism.chargingEnabled / currentA, qui viennent du Thing), ensuite ce que le moteur a voulu, étiqueté comme tel. L'écart mesuré/commandé se tait sur une borne — « sous la consigne » y supposerait une consigne. La réserve ne touche PAS l'ECS ni la PAC, où l'allocation publiée est bien la commande : elle est portée par allocationIsCommand, et tombe avec le lot plugin 3g-2. EV_SURPLUS, EV_ECO_MIN et EV_IDLE ne sont plus émis. Les branches restent : les retirer rendrait illisible un journal antérieur, pas l'app plus juste. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
373 lines
18 KiB
Plaintext
373 lines
18 KiB
Plaintext
{
|
|
"@@locale": "fr",
|
|
|
|
"installerLoadsTitle": "Rôles & appareils",
|
|
"@installerLoadsTitle": {
|
|
"description": "Titre de l'écran installateur de configuration des charges pilotables"
|
|
},
|
|
"installerLoadsSubtitle": "Ordre de service des charges pilotables",
|
|
"loadsEmpty": "Aucune charge pilotable déclarée sur cette installation.",
|
|
"loadsLoadError": "Lecture de la configuration impossible : {error}",
|
|
"@loadsLoadError": {
|
|
"placeholders": { "error": { "type": "String" } }
|
|
},
|
|
|
|
"sectionUnclassified": "Non classé",
|
|
"@sectionUnclassified": {
|
|
"description": "Bucket des charges sans o:domain — jamais rangées d'office dans un domaine"
|
|
},
|
|
|
|
"domainEv": "Véhicule électrique",
|
|
"domainEcs": "Eau chaude sanitaire",
|
|
"domainHvac": "Climatisation",
|
|
"domainHeating": "Chauffage",
|
|
"domainUnknown": "Domaine « {code} »",
|
|
"@domainUnknown": {
|
|
"description": "Repli quand la box renvoie un domaine que cette version d'app ne connaît pas",
|
|
"placeholders": { "code": { "type": "String" } }
|
|
},
|
|
|
|
"adapterRelayRouter": "Routeur de relais",
|
|
"adapterEtmVariableLoad": "Charge modulable",
|
|
"adapterSgReady": "SG-Ready",
|
|
"adapterEvCharger": "Borne de recharge",
|
|
"adapterUnknown": "Mécanisme « {code} »",
|
|
"@adapterUnknown": {
|
|
"placeholders": { "code": { "type": "String" } }
|
|
},
|
|
|
|
"loadEnabled": "Active",
|
|
"loadDisabled": "Exclue de l'arbitrage",
|
|
"loadPriority": "Rang {rank}",
|
|
"@loadPriority": {
|
|
"placeholders": { "rank": { "type": "int" } }
|
|
},
|
|
"loadMinOn": "Maintien ON minimal : {seconds} s",
|
|
"@loadMinOn": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"loadMinOff": "Maintien OFF minimal : {seconds} s",
|
|
"@loadMinOff": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"loadRelayCount": "{count, plural, =1{1 relais} other{{count} relais}}",
|
|
"@loadRelayCount": {
|
|
"placeholders": { "count": { "type": "int" } }
|
|
},
|
|
"loadMeterSlot": "Compteur dédié : non rattachable",
|
|
"@loadMeterSlot": {
|
|
"description": "Emplacement visuel réservé — LoadConfig n'a aucun champ pour rattacher un compteur à une charge"
|
|
},
|
|
|
|
"orderNotGroupedTitle": "Ordre non groupé",
|
|
"orderNotGroupedBody": "L'ordre en place sur la box n'est pas groupé par domaine : les charges d'un même domaine ne se suivent pas. L'ordre réel est affiché ci-dessous, tel que la box l'applique.",
|
|
"orderNotGroupedAction": "Normaliser l'ordre",
|
|
"orderNotGroupedWarning": "Normaliser modifiera l'ordre d'arbitrage réel de l'installation. Aucune écriture ne part tant que vous n'avez pas confirmé.",
|
|
"orderNormalizeConfirm": "Normaliser et enregistrer",
|
|
"cancel": "Annuler",
|
|
|
|
"saveOrder": "Enregistrer l'ordre",
|
|
"savePending": "Envoi en cours…",
|
|
"saveWaitingBox": "En attente de confirmation de la box…",
|
|
"saveConfirmed": "Configuration confirmée par la box.",
|
|
"saveRejected": "La box a refusé : {reason}",
|
|
"@saveRejected": {
|
|
"placeholders": { "reason": { "type": "String" } }
|
|
},
|
|
"saveDiverged": "La box a enregistré un état différent de celui demandé. L'affichage montre ce que la box applique réellement.",
|
|
|
|
"validationDuplicateId": "L'identifiant de charge « {id} » apparaît deux fois.",
|
|
"@validationDuplicateId": {
|
|
"placeholders": { "id": { "type": "String" } }
|
|
},
|
|
"validationThingClaimedTwice": "L'appareil {thingId} est commandé par deux charges actives, « {first} » et « {second} » : deux commandes contradictoires et une puissance comptée deux fois.",
|
|
"@validationThingClaimedTwice": {
|
|
"placeholders": {
|
|
"thingId": { "type": "String" },
|
|
"first": { "type": "String" },
|
|
"second": { "type": "String" }
|
|
}
|
|
},
|
|
|
|
"telemetryUnavailableTitle": "Télémétrie indisponible",
|
|
"telemetryUnavailableBody": "Cette box n'expose pas GetLoadTelemetry. Puissance allouée, verrous, disponibilité et motifs de décision ne sont pas lisibles depuis l'app.",
|
|
"telemetryStale": "Données anciennes — dernier point il y a {seconds} s",
|
|
"@telemetryStale": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"telemetryDegradedMode": "Mode dégradé L2 — compteur muet, planification suspendue",
|
|
|
|
"faultTitle": "En défaut",
|
|
"faultClearAction": "Lever le défaut",
|
|
"faultCleared": "Défaut levé — confirmé par la télémétrie relue.",
|
|
"faultClearNoop": "Aucun défaut à lever sur cette charge.",
|
|
"faultUnknown": "Défaut « {code} »",
|
|
"@faultUnknown": {
|
|
"placeholders": { "code": { "type": "String" } }
|
|
},
|
|
|
|
"decisionUnknown": "Décision « {code} »{params}",
|
|
"@decisionUnknown": {
|
|
"description": "Repli obligatoire quand la box renvoie un decision.code absent de cet ARB — box plus récente que l'app. Affiche le code brut et ses paramètres, jamais une phrase vide.",
|
|
"placeholders": {
|
|
"code": { "type": "String" },
|
|
"params": { "type": "String" }
|
|
}
|
|
},
|
|
|
|
"telemetryTitle": "Arbitrage en direct",
|
|
"telemetryNeverRan": "Aucun cycle d'arbitrage depuis le démarrage du service.",
|
|
"@telemetryNeverRan": {
|
|
"description": "timestamp absent — l'absence de cycle est une information, pas un zéro"
|
|
},
|
|
"telemetryLastCycle": "Dernier cycle il y a {seconds} s",
|
|
"@telemetryLastCycle": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"telemetryBudgetTitle": "Budget de surplus du cycle",
|
|
"telemetryBudgetAbsent": "Aucun budget publié pour ce cycle : la planification est suspendue.",
|
|
"budgetSurplus": "Surplus entrant",
|
|
"budgetEvReserved": "Réservé au véhicule",
|
|
"budgetRecredited": "Recrédité",
|
|
"budgetAllocated": "Alloué",
|
|
"budgetRemaining": "Reliquat",
|
|
"watts": "{watts} W",
|
|
"@watts": {
|
|
"placeholders": { "watts": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"telemetryAllocated": "Alloué {watts} W",
|
|
"@telemetryAllocated": {
|
|
"placeholders": { "watts": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"telemetryEvArbiterIntent": "L'arbitre a décidé {watts} W",
|
|
"@telemetryEvArbiterIntent": {
|
|
"description": "Remplace « Alloué X W » pour une BORNE. Tant que le lot plugin 3g-2 n'est pas livré, adjustEvChargers() re-décide après le waterfall : l'allocation publiée est une intention, pas un ordre. Le verbe doit donc nommer QUI a décidé, pas ce que la borne fait.",
|
|
"placeholders": { "watts": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"telemetryEvIntentDisclaimer": "Décision de l'arbitre — l'état réel de la borne est ci-dessus.",
|
|
"@telemetryEvIntentDisclaimer": {
|
|
"description": "Ligne qui suit le motif d'une borne. Sans elle, un écran qui affiche « budget sous le plancher » à côté d'une borne qui charge à 6 A a l'air faux — et c'est le moteur qui se contredit, pas l'écran. Tombe avec 3g-2."
|
|
},
|
|
"telemetryEvCharging": "Borne : en charge, {current} A sur {phases} phase(s)",
|
|
"@telemetryEvCharging": {
|
|
"description": "ÉTAT RÉEL, lu dans mechanism.chargingEnabled / currentA / phaseCount — qui viennent du Thing, pas de l'arbitre.",
|
|
"placeholders": {
|
|
"current": { "type": "double", "format": "decimalPattern" },
|
|
"phases": { "type": "int" }
|
|
}
|
|
},
|
|
"telemetryEvIdle": "Borne : ne charge pas",
|
|
"telemetryEvUnplugged": "Borne : aucun véhicule branché",
|
|
"telemetryEvStateUnknown": "Borne : état non publié ce cycle",
|
|
"@telemetryEvStateUnknown": {
|
|
"description": "mechanism.chargingEnabled absent. Ne rien dire vaut mieux que déduire l'état du motif d'arbitrage, qui peut le contredire."
|
|
},
|
|
"telemetryFundedByGrid": "Financée au réseau (échéance ou tarif dynamique) — hors budget de surplus",
|
|
"@telemetryFundedByGrid": {
|
|
"description": "funding == « grid » : l'allocation vit dans budget.evReservedW, pas dans budget.allocatedW. Sans cette mention, l'utilisateur voit une charge servie alors que le surplus est nul et croit à un défaut."
|
|
},
|
|
"telemetryNotArbitrated": "Non arbitrée — absente du dernier cycle publié",
|
|
"@telemetryNotArbitrated": {
|
|
"description": "Charge présente dans GetLoadConfig mais absente de loads[] : elle est désactivée, jamais perdue"
|
|
},
|
|
|
|
"lockMinOn": "Maintien ON — {seconds} s restantes",
|
|
"@lockMinOn": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"lockMinOff": "Maintien OFF — {seconds} s restantes",
|
|
"@lockMinOff": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"lockMinStateHold": "Maintien d'état — {seconds} s restantes",
|
|
"@lockMinStateHold": {
|
|
"placeholders": { "seconds": { "type": "int" } }
|
|
},
|
|
"lockUnknown": "Verrou « {kind} » — {seconds} s restantes",
|
|
"@lockUnknown": {
|
|
"placeholders": {
|
|
"kind": { "type": "String" },
|
|
"seconds": { "type": "int" }
|
|
}
|
|
},
|
|
"lockAsOfCycle": "au dernier cycle",
|
|
"@lockAsOfCycle": {
|
|
"description": "Les secondes restantes ne sont PAS décomptées côté app : elles datent du cycle publié"
|
|
},
|
|
|
|
"mechanismRelayStage": "Palier {stage} W sur {max} W",
|
|
"@mechanismRelayStage": {
|
|
"placeholders": {
|
|
"stage": { "type": "int", "format": "decimalPattern" },
|
|
"max": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"mechanismVariableSetpoint": "Consigne {setpoint} W ({percent} %)",
|
|
"@mechanismVariableSetpoint": {
|
|
"placeholders": {
|
|
"setpoint": { "type": "int", "format": "decimalPattern" },
|
|
"percent": { "type": "int" }
|
|
}
|
|
},
|
|
"mechanismSgReadyState": "État SG-Ready {state} — estimation {estimated} W",
|
|
"@mechanismSgReadyState": {
|
|
"placeholders": {
|
|
"state": { "type": "int" },
|
|
"estimated": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"mechanismEvCharger": "{current} A sur {phases} phase(s)",
|
|
"@mechanismEvCharger": {
|
|
"placeholders": {
|
|
"current": { "type": "double", "format": "decimalPattern" },
|
|
"phases": { "type": "int" }
|
|
}
|
|
},
|
|
"mechanismUnknown": "Mécanisme « {kind} »",
|
|
"@mechanismUnknown": {
|
|
"placeholders": { "kind": { "type": "String" } }
|
|
},
|
|
|
|
"decisionEvSurplus": "Véhicule rechargé sur le surplus photovoltaïque.",
|
|
"decisionEvSpotMarket": "Véhicule rechargé sur un créneau tarifaire favorable.",
|
|
"decisionEvDeadline": "Véhicule prioritaire : l'échéance approche.",
|
|
"decisionEvEcoMin": "Pas de surplus : le courant minimum est maintenu.",
|
|
"decisionEvIdle": "Pas de surplus : la recharge est suspendue.",
|
|
"decisionLoadUnavailable": "Charge en défaut : aucune commande n'est émise, et {frozen} W restent comptés au budget.",
|
|
"@decisionLoadUnavailable": {
|
|
"placeholders": { "frozen": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"decisionLockMinOn": "Le maintien ON a relevé la consigne à {applied} W ; le budget en payait {budget} W.",
|
|
"@decisionLockMinOn": {
|
|
"placeholders": {
|
|
"applied": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionLockMinOff": "Le maintien OFF interdit l'enclenchement ; le budget payait pourtant {requested} W sur {budget} W disponibles.",
|
|
"@decisionLockMinOff": {
|
|
"placeholders": {
|
|
"requested": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionLockMinOffCapped": "Le maintien OFF plafonne la consigne à {applied} W ; {requested} W étaient demandés sur {budget} W de budget.",
|
|
"@decisionLockMinOffCapped": {
|
|
"placeholders": {
|
|
"applied": { "type": "int", "format": "decimalPattern" },
|
|
"requested": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionSurplusSetpoint": "Consigne de {setpoint} W servie par le surplus ({budget} W de budget).",
|
|
"@decisionSurplusSetpoint": {
|
|
"placeholders": {
|
|
"setpoint": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionSurplusSetpointStepped": "Consigne de {setpoint} W servie par le surplus, arrondie sur un palier déclaré ({budget} W de budget).",
|
|
"@decisionSurplusSetpointStepped": {
|
|
"placeholders": {
|
|
"setpoint": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionSurplusInsufficient": "Aucun surplus à distribuer ({budget} W) — rien n'est commandé.",
|
|
"@decisionSurplusInsufficient": {
|
|
"placeholders": { "budget": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"decisionLockMinStateHold": "État {state} maintenu par protection anti court-cycle.",
|
|
"@decisionLockMinStateHold": {
|
|
"placeholders": { "state": { "type": "int" } }
|
|
},
|
|
"decisionSgForced": "Pompe à chaleur en état 4 (forcé) — estimation {estimated} W sur {budget} W de budget.",
|
|
"@decisionSgForced": {
|
|
"placeholders": {
|
|
"estimated": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionSgRecommended": "Pompe à chaleur en état 3 (recommandation) — estimation {estimated} W sur {budget} W de budget.",
|
|
"@decisionSgRecommended": {
|
|
"placeholders": {
|
|
"estimated": { "type": "int", "format": "decimalPattern" },
|
|
"budget": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionSgNormal": "Aucun surplus ({budget} W) — pompe à chaleur en état 2 (normal, sans consigne).",
|
|
"@decisionSgNormal": {
|
|
"placeholders": { "budget": { "type": "int", "format": "decimalPattern" } }
|
|
},
|
|
"decisionBelowMinPower": "Budget de {budget} W sous le premier palier ({min} W) — rien n'est commandé, le budget passe à la charge suivante.",
|
|
"@decisionBelowMinPower": {
|
|
"description": "Il Y A du surplus, mais pas au bon format : sous le plus petit palier commandable. À ne pas confondre avec SURPLUS_INSUFFICIENT, qui dit qu'il n'y a rien.",
|
|
"placeholders": {
|
|
"budget": { "type": "int", "format": "decimalPattern" },
|
|
"min": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionBelowMinPowerState": "Budget de {budget} W sous l'état {state} ({min} W estimés) — maintien en état 2, le budget passe à la charge suivante.",
|
|
"@decisionBelowMinPowerState": {
|
|
"description": "Même sens, mécanisme à états : le premier état utile coûterait plus que ce qui reste.",
|
|
"placeholders": {
|
|
"budget": { "type": "int", "format": "decimalPattern" },
|
|
"state": { "type": "int" },
|
|
"min": { "type": "int", "format": "decimalPattern" }
|
|
}
|
|
},
|
|
"decisionBatteryReserveWithheld": "{withheld} W de surplus retenus par la réserve batterie ({soc} % sous le seuil de {seuil} %) — il y a du surplus, mais une règle interdit de le distribuer.",
|
|
"@decisionBatteryReserveWithheld": {
|
|
"description": "Variante NOMINALE : le plugin publie toujours les trois paramètres ensemble, et sa garde interne exige withheldW > 0 — un motif à 0 W dirait « une règle vous bloque » là où il n'y a simplement pas de surplus. Le nombre de watts retenus vient en TÊTE parce que c'est lui qui rend la cause lisible : « 3 200 W retenus » se comprend, « batterie à 47 % » se subit.",
|
|
"placeholders": {
|
|
"withheld": { "type": "int", "format": "decimalPattern" },
|
|
"soc": { "type": "int" },
|
|
"seuil": { "type": "int" }
|
|
}
|
|
},
|
|
"decisionBatteryReserve": "Budget annulé par la réserve batterie ({soc} % sous le seuil de {seuil} %) — il y a du surplus, mais une règle interdit de le distribuer.",
|
|
"@decisionBatteryReserve": {
|
|
"description": "Repli si « withheldW » manque (box plus ancienne). À NE PAS confondre avec « pas de surplus » : le surplus existe, une règle l'annule. Le client voit une installation qui ne pilote rien par grand soleil, sans erreur ni panne — la phrase doit donc nommer la cause, pas décrire l'effet.",
|
|
"placeholders": {
|
|
"soc": { "type": "int" },
|
|
"seuil": { "type": "int" }
|
|
}
|
|
},
|
|
"decisionBatteryReserveShort": "Budget annulé par la réserve batterie — il y a du surplus, mais une règle interdit de le distribuer.",
|
|
"@decisionBatteryReserveShort": {
|
|
"description": "Variante sans paramètres, si le motif n'en porte pas."
|
|
},
|
|
"decisionDegradedL2": "Consigne de repli : le compteur est muet et le watchdog a suspendu la planification.",
|
|
"decisionSafeStateRelay": "État sûr avant retrait : tous les relais sont ouverts.",
|
|
"decisionSafeStateSetpoint": "État sûr avant retrait : consigne ramenée à 0 W.",
|
|
"decisionSafeStateSgReady": "État sûr avant retrait : état 2 (normal), jamais le blocage.",
|
|
|
|
"faultWriteFailed": "Échec d'écriture : l'échelle de commande est épuisée, plus aucune commande n'est émise tant que le défaut n'est pas levé.",
|
|
"faultThingMissing": "L'appareil commandé est absent de la configuration nymea.",
|
|
"faultUnusableEncoding": "Encodage SG-Ready inexploitable : l'état 2 ne peut pas être exprimé, cette pompe à chaleur n'est pas pilotable.",
|
|
"faultClearPending": "Levée demandée — en attente de la télémétrie…",
|
|
"faultClearStillFaulty": "La charge est toujours en défaut après la levée.",
|
|
"faultClearRejected": "La box a refusé la levée : {reason}",
|
|
"@faultClearRejected": {
|
|
"placeholders": { "reason": { "type": "String" } }
|
|
},
|
|
"faultClearHint": "La levée est une action délibérée : le défaut ne se lève jamais tout seul.",
|
|
|
|
"domainBattery": "Batterie",
|
|
"domainAssignTitle": "Classer « {label} »",
|
|
"@domainAssignTitle": {
|
|
"placeholders": { "label": { "type": "String" } }
|
|
},
|
|
"domainAssignAction": "Changer de domaine",
|
|
"domainAssignNone": "Retirer du classement",
|
|
"domainAssignHint": "Le domaine est une intention déclarée par l'installateur. L'arbitre ne la lit pas : elle ne sert qu'à grouper l'ordre de service.",
|
|
|
|
"localeRequested": "Locale demandée : {requested} · retenue par la box : {effective}",
|
|
"@localeRequested": {
|
|
"placeholders": {
|
|
"requested": { "type": "String" },
|
|
"effective": { "type": "String" }
|
|
}
|
|
}
|
|
}
|