Lot B côté app, partie NON bloquée par le lot plugin (voir §Limites).
§1 Locale (indépendant) — JSONRPC.Hello portait {} : l'app était en en_US de
fait alors que nymea gère la locale PAR CONNEXION (o:locale). Envoi de la
locale à chaque Hello, donc aussi après reconnexion subie (m_clientLocales
repart au défaut de la box à chaque socket). Locale demandée ET retenue
journalisées : un .qm introuvable rend exactement comme une absence de
traduction, sans cette trace un catalogue mal packagé passe inaperçu.
§2 i18n — flutter_localizations + intl + ARB (français seul). Aucune chaîne
en dur dans le nouvel écran. Les codes reçus de la box (domaine, mécanisme)
sont des clés ARB avec repli explicite sur code brut si l'app ne les connaît
pas : une box en avance sur l'app est un cas normal en parc déployé.
§3 Modèle — LoadConfigEntry est une VUE en lecture au-dessus de la map brute ;
l'écriture repart de la map d'origine (patched()). Pas de couche de filtrage
préventive : l'aller-retour neutre passe contre .75, donc l'écho verbatim est
sûr par construction (§7-1).
§4 Priorité à deux niveaux — groupByDomain / flattenToPayload. Le rang de
domaine se déduit de l'ordre d'apparition à plat ; la box ne sait rien des
groupes. Config non contiguë : signalée, ordre réel affiché, normalisation
seulement sur confirmation explicite. Miroir client de validateSet().
§5 Écritures asynchrones — LoadConfigProvider ne conclut jamais sur
l'acquittement du RPC : pending → LoadConfigChanged → confirmed|diverged.
NymeaEnergy.GetLoadConfig / SetLoadConfig deviennent RÉELS (ils étaient des
stubs journalisés). Conséquence traitée : EnergySetupProvider.persist()
n'émet plus setLoadConfig(buildLoadDescriptors()) — SetLoadConfig remplace
TOUT l'ensemble, et _commit() l'appelait à chaque changement d'UI : une
reconstruction depuis le modèle typé aurait écrasé la config réelle de la
box sans geste explicite.
Écran /energy/loads, gaté installateur (drawer + garde de routeur).
Limites — le lot plugin n'est PAS déployé sur .75 : GetLoadTelemetry,
LoadTelemetryChanged et o:domain sont absents (19 méthodes / 13 notifications
inchangées). Toute la moitié télémétrie du brief est donc hors d'atteinte, et
o:domain n'étant pas au schéma, la box rejetterait la clé (jsonvalidator.cpp:143).
Fixtures multi-domaines synthétiques : .75 n'a qu'une charge.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
126 lines
5.0 KiB
Plaintext
126 lines
5.0 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" }
|
|
}
|
|
},
|
|
|
|
"localeRequested": "Locale demandée : {requested} · retenue par la box : {effective}",
|
|
"@localeRequested": {
|
|
"placeholders": {
|
|
"requested": { "type": "String" },
|
|
"effective": { "type": "String" }
|
|
}
|
|
}
|
|
}
|