2 Commits

Author SHA1 Message Date
Patrick Schurig
553a182e9b fix(config): l'ordre entrelacé se voit enfin, et un appareil disparu cesse d'être un défaut à lever
L'arrivée de la V2C sur .75 a révélé deux défauts que le banc ne pouvait pas montrer avant,
plus deux pièges de contrat à documenter.

1. ⚠️ isNotGrouped ÉTAIT CALCULÉ ET AFFICHÉ NULLE PART. GroupedOrder détecte depuis le
   premier jour qu'un ordre n'est pas groupé par domaine, le provider l'expose, et aucune
   vue ne le lisait — le cas ne s'était jamais produit, chaque domaine du banc ayant ses
   charges d'un seul tenant.

   Il se produit maintenant : la V2C, créée d'office au rang 1 que le chauffe-eau occupait
   déjà, donne l'ordre à plat « ecs · ev · heating · ev · ev ». Le domaine ev est coupé en
   deux par le chauffage.

   Ce n'était pas un détail d'affichage. L'écran groupe pour afficher, donc ce qu'on voit
   n'est plus l'ordre réel de la box — et le premier enregistrement réaplatit EN GROUPANT,
   ce qui change l'arbitrage sans que personne ne l'ait demandé. Chiffré par test sur la
   configuration réelle : la PAC passerait du rang 2 au rang 5. C'est exactement ce que le
   commentaire de GroupedOrder interdit depuis le début ; le garde-fou existait, il n'était
   pas branché.

2. « L'APPAREIL N'EXISTE PLUS » N'EST PAS UN DÉFAUT À LEVER. La Terra AC a été supprimée de
   nymea ; son entrée LoadConfig survit, occupe le rang 4, et s'affiche en THING_MISSING
   avec un bouton « Lever le défaut ». ClearLoadFault y répondrait EnergyErrorNoError sans
   rien changer, et le défaut reviendrait au cycle suivant — un bouton qui ne peut pas
   tenir sa promesse fait douter de la box au lieu de désigner la cause.

   Le bouton disparaît sur ce code, remplacé par ce qu'il faut faire : réinstaller
   l'appareil, ou retirer la charge. Et le libellé cesse de se lire « votre configuration
   est cassée » : le cas normal est un appareil remplacé, le plugin crée d'office une
   entrée par borne détectée mais ne la retire pas quand le Thing s'en va.

3. Deux pièges du Thing V2C, documentés là où quelqu'un serait tenté de les câbler :

   chargeEnergy est EXACT pendant la charge — sa croissance recoupe currentPower à mieux
   d'un pour cent — et REMIS À ZÉRO à l'arrêt de la charge, pas au débranchement. Mesuré :
   0,3456 kWh → 0 dès que charging passe à faux, 119 secondes avant que le câble ne bouge.
   Sur du pilotage par surplus il repartirait de zéro à chaque nuage, et après coup une
   nuit entière vaudrait 0 — soit « rien livré » affiché là où la vérité est « pas
   mesurable ». Le plugin publiera un sessionEnergy construit ; rien n'est bâti en
   attendant, et surtout aucune accumulation côté app.

   powerL1/L2/L3 sont des ampères mal nommés, renommage currentL* en cours côté plugin.
   Ni affichage, ni conversion : une conversion écrite ici survivrait au correctif et le
   contredirait.

Fixture ajoutée : la configuration réelle à cinq entrées, avec ses deux enseignements de
terrain — deux rangs à 1, et une entrée orpheline qui consomme un rang.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
2026-08-28 11:57:12 +02:00
Patrick Schurig
a44d64d6c2 feat(3g-2): les bornes sont des charges configurées — une seule liste, un seul rang
+etm23 rend à chaque borne détectée une entrée GetLoadConfig, et l'invariant
loads[] ⊆ GetLoadConfig est rétabli. Sur .75 : 2 entrées → 4. L'app s'appuyait déjà sur cet
invariant pour résoudre libellé, domaine et rang de toute charge publiée.

Vérifié sur machine avant d'écrire quoi que ce soit :
- l'aller-retour verbatim reste NEUTRE avec les quatre entrées (loadconfig_roundtrip.dart,
  verdict IDENTIQUE) ;
- une entrée evcharger porte id/label/domain/priority/enabled, mode dynamic, et AUCUNE
  charge utile de mécanisme.

Trois défauts que ce contrat révèle côté app :

1. isVariableLoad était défini par défaut (« ni relais ni sg-ready »), donc une borne y
   tombait. L'écran de mécanisme lui proposait une puissance nominale, des paliers et des
   temporisations — des réglages qui n'existent pas pour elle et dont l'écriture partirait
   quand même. Pire, le sélecteur offrait de la basculer en routeur de relais : une bascule
   que basculerMecanisme() sait construire et qu'aucun écran ne sait défaire. Une borne a
   maintenant sa section, en lecture seule, qui dit pourquoi il n'y a rien à régler.

2. BorneSection existait pour rattraper des bornes qui n'apparaissaient nulle part. Elles
   apparaissent maintenant dans leur domaine : la section les afficherait DEUX fois, en
   affirmant qu'elles n'ont « ni rang ni domaine » et qu'elles ne passent pas par
   SetLoadConfig. Trois affirmations devenues fausses. Retirée ; son contenu de runtime
   vit désormais dans l'écran de mécanisme, où il a un sens.

3. Le rang par défaut d'une borne est un ARTEFACT, et l'écran le disait comme un réglage.
   À la création le plugin donne « le plus petit rang existant moins un, borné à 1 » ;
   le chauffe-eau occupant déjà le rang 1, l'intention « en tête » dégénère en égalité —
   trois charges à 1 sur le banc. Le tri reste total (l'id départage), donc l'ordre est
   reproductible, et c'est précisément ce qui le rend crédible : l'installateur lit un
   classement plausible et n'y touche pas, validant un ordre que personne n'a posé. Une
   note le nomme, et disparaît dès le premier vrai réglage.

Le glissement, lui, n'a demandé aucun cas particulier — c'était tout l'intérêt d'avoir une
seule liste. Vérifié par test, y compris l'inversion borne / charge pilotée.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
2026-08-27 14:32:56 +02:00