MOTIF BELOW_MIN_POWER — premier ajout au catalogue depuis qu'il existe.
Le sens porté est précis et facile à perdre : il Y A du surplus, mais pas au bon format —
sous le plus petit cran commandable, la charge ne prend rien et le budget file à la
suivante. À distinguer de SURPLUS_INSUFFICIENT et SG_NORMAL, qui ne sortent plus que pour
un budget ≤ 0, « il n'y a rien ». Les confondre ferait chercher un défaut de production là
où il n'y a qu'une granularité. Deux variantes, départagées par la présence de `state`.
Les deux phrases voisines sont reprises en conséquence : SG_NORMAL peut enfin dire « aucun
surplus » sans risque, ce qu'elle ne pouvait pas avant, et SURPLUS_INSUFFICIENT ne parle
plus de « palier non payé » — elle dit qu'il n'y a rien à distribuer.
LE REPLI AURAIT TENU, et c'est vérifié plutôt qu'affirmé : sur BELOW_MIN_POWER privé de
`minPowerW`, l'app rend le code et ses paramètres triés au lieu de fabriquer une phrase
approximative. C'était exactement le cas prévu, et c'est le premier vrai à se présenter.
GLISSEMENT — le test POSE désormais sa condition au lieu de la subir. Il regroupe les deux
charges dans un même domaine, exerce, puis REND le classement d'origine. Deux fois de suite
il s'était contenté d'annoncer « non exercé » parce que le banc était revenu à une charge
par domaine : un test qui dépend de l'état du terrain déclare forfait au moment où on
aurait besoin de lui. Exercé sur appareil : [chauffe-eau, pac-terrain] →
[pac-terrain, chauffe-eau], classement rétabli {ecs, heating}.
COMPTEURS — rattachés pour de vrai depuis le chemin de l'app, et non par une sonde.
measuredW apparaît par charge, chacune portant SON compteur. L'affichage met la mesure à
côté de l'allocation, jamais à sa place : l'une est ce que le waterfall a décidé, l'autre
ce qui passe réellement.
Mon assertion initiale était fautive : j'exigeais des mesures DISTINCTES pour prouver le
rattachement par charge. Deux compteurs à l'arrêt lisent légitimement 0 W tous les deux —
le test échouait donc sur une installation saine. Ce qui prouve le rattachement, c'est que
chaque charge porte un compteur différent. Vérifié aussi, en détachant puis rattachant :
une charge sans compteur ne publie RIEN — null n'est pas 0, et afficher un zéro ferait
passer une absence de mesure pour une absence de consommation.
PLANCHER — préparé AVANT son arrivée au schéma, précisément à cause de ce qui vient de se
passer avec meterThingId et sensorThingId : le grisage dérivé les a activés tout seuls,
mais sur un placeholder mort. Le bloc est donc câblé maintenant, reste grisé tant que le
champ n'existe pas, et fonctionnera le jour où il apparaîtra. La box publie déjà
mechanism.minPowerW en télémétrie : on l'affiche en lecture, ce qui donne la valeur en
vigueur même sans réglage ouvert.
Tests 101/101, analyze 0 erreur.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff
325 lines
14 KiB
Plaintext
325 lines
14 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" } }
|
|
},
|
|
"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" }
|
|
}
|
|
},
|
|
"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" }
|
|
}
|
|
}
|
|
}
|