Trois maquettes, neuf écrans, après le passage sur appareil qui a montré que /energy/setup décrit autre chose que l'installation. - charges_pilotables_mockup.html : la liste à deux niveaux, avec le bandeau d'arbitrage hérité de /energy/loads (qui disparaît) et la télémétrie sur la carte de chaque charge. - configurer_mecanismes_mockup.html : les six « Configurer » — routeur de relais, modulable, SG-Ready, borne, zones HVAC, sélecteur d'appareils. - raccordement_reseau_mockup.html : compteur réseau et protection de surcharge. Décisions de conception que les images ne portent pas seules : les paliers n'existent que chez le routeur de relais et y sont DÉDUITS des combinaisons ; le mode fixed d'etmvariableload (legacy) n'est plus proposé mais reste lisible, parce qu'ouvrir un écran ne doit pas convertir une config déployée ; SG1/SG2 sont une convention d'app retrouvée par l'encodage standard, la box ne stocke qu'une liste de relais par état ; le compteur par charge est dessiné inactif avec sa raison ; la borne règle un mode par DÉFAUT, pas une commande. CLAUDE.md corrigé contre JSONRPC.Introspect (plugin 1.15.2+etm15) : - Energy.SetChargingMode n'existe pas — le « bug critique » décrivait un appel impossible. Energy.* n'a que cinq méthodes. - Le namespace EnergyPlugin.* n'existe pas : c'est NymeaEnergy.*, 20 méthodes et 14 notifications, toutes listées. - ChargingInfo : chargingMode / targetPercentage / endDateTime (Uint), et non mode / targetSoc / endTime. minCurrent n'existe nulle part. - phasePowerLimit est en AMPÈRES par phase, et 0 coupe toute la recharge intelligente. - AirConditioning expose 8 méthodes, pas 3. Toutes les méthodes citées ont été revérifiées une par une : aucune introuvable. Règle ajoutée : contrôler ce fichier contre Introspect avant chaque lot — trois fois aujourd'hui il a envoyé un agent dans le vide. Et docs/BRIEF_plugin_depuis_maquettes.md : les six points que les maquettes remontent vers le moteur, dont un seul blocage dur (pas de champ de courant minimum pour le mode Min+PV). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
322 lines
18 KiB
HTML
322 lines
18 KiB
HTML
<!doctype html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>ETM PowerSync — Raccordement réseau et protection de surcharge</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#081f2b; --panel:#0d2b3b; --card:#12384a; --card2:#0f3242;
|
||
--line:rgba(255,255,255,.08); --line2:rgba(255,255,255,.14);
|
||
--blue:#31a3dd; --yellow:#fec113; --green:#35c98a; --red:#e07a7a; --violet:#9d8ce0;
|
||
--txt:#e8f1f6; --muted:#90a8b6; --muted2:#6f8b9a;
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:
|
||
radial-gradient(900px 500px at 15% -10%, #0e3145 0%, transparent 60%),
|
||
radial-gradient(900px 600px at 110% 10%, #0b2535 0%, transparent 55%),
|
||
var(--bg);
|
||
font-family:'Inter',system-ui,sans-serif;color:var(--txt);min-height:100vh;padding:34px 20px 60px}
|
||
.head{max-width:1000px;margin:0 auto 26px}
|
||
.head h1{font-family:'Poppins';font-weight:700;font-size:20px;margin:0 0 6px}
|
||
.head p{margin:0 0 8px;color:var(--muted);font-size:13.5px;line-height:1.55;max-width:880px}
|
||
.head code{color:#9fd4ee} .head b{color:var(--yellow)}
|
||
.stage{display:flex;gap:34px;justify-content:center;flex-wrap:wrap;align-items:flex-start}
|
||
.col{display:flex;flex-direction:column;align-items:center;gap:12px;max-width:392px}
|
||
.cap{color:var(--muted2);font-size:12px;font-family:'Poppins';font-weight:500;letter-spacing:.4px;text-transform:uppercase}
|
||
.cap b{color:var(--blue)}
|
||
|
||
.phone{width:392px;background:var(--panel);border:1px solid var(--line2);border-radius:30px;
|
||
overflow:hidden;box-shadow:0 30px 80px -30px rgba(0,0,0,.7);display:flex;flex-direction:column;height:850px}
|
||
.appbar{display:flex;align-items:center;gap:12px;padding:18px 16px 14px;border-bottom:1px solid var(--line);
|
||
background:linear-gradient(#102f40,#0d2b3b)}
|
||
.appbar .ic{width:26px;height:26px;display:grid;place-items:center;color:var(--muted);font-size:18px}
|
||
.appbar h2{font-family:'Poppins';font-weight:600;font-size:15px;margin:0;flex:1;text-align:center}
|
||
.scroll{overflow:auto;padding:16px 15px 26px}
|
||
.scroll::-webkit-scrollbar{width:0}
|
||
|
||
.sect{font-family:'Poppins';font-weight:600;font-size:12px;color:var(--muted);letter-spacing:.3px;
|
||
margin:20px 0 9px}
|
||
.sect:first-child{margin-top:2px}
|
||
|
||
.field{margin-top:18px} .field:first-child{margin-top:4px}
|
||
.field>label{display:block;font-family:'Poppins';font-weight:600;font-size:12px;color:var(--muted);
|
||
letter-spacing:.3px;margin-bottom:9px}
|
||
.field>label small{color:var(--muted2);font-weight:500;letter-spacing:0}
|
||
.saved-flag{font-size:10.5px;color:var(--green);background:rgba(53,201,138,.12);border:1px solid rgba(53,201,138,.3);
|
||
border-radius:20px;padding:2px 8px;font-weight:600;float:right;margin-top:-2px}
|
||
.derived-flag{font-size:10.5px;color:var(--muted2);background:rgba(255,255,255,.05);border:1px solid var(--line2);
|
||
border-radius:20px;padding:2px 8px;font-weight:600;float:right;margin-top:-2px}
|
||
.todo-flag{font-size:10.5px;color:var(--yellow);background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.32);
|
||
border-radius:20px;padding:2px 8px;font-weight:600;float:right;margin-top:-2px}
|
||
|
||
.sel{width:100%;background:var(--card2);border:1px solid var(--line2);border-radius:10px;color:var(--txt);
|
||
font-family:'Inter';font-size:12.5px;padding:11px 12px;display:flex;align-items:center;gap:8px}
|
||
.sel i{margin-left:auto;font-style:normal;color:var(--muted2);font-size:11px}
|
||
.mode-hint{margin-top:9px;font-size:11.5px;color:var(--muted2);line-height:1.5}
|
||
.mode-hint b{color:var(--muted)}
|
||
|
||
.meter{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:13px}
|
||
.meter .m1{display:flex;align-items:center;gap:11px}
|
||
.meter .ico{width:38px;height:38px;border-radius:11px;background:rgba(254,193,19,.13);display:grid;
|
||
place-items:center;font-size:19px;flex:none}
|
||
.meter .m1 .nm{flex:1;min-width:0}
|
||
.meter .m1 .nm b{display:block;font-family:'Poppins';font-weight:600;font-size:13.5px}
|
||
.meter .m1 .nm span{font-size:11px;color:var(--muted2)}
|
||
.tag{font-size:10.5px;color:var(--blue);background:rgba(49,163,221,.12);border:1px solid rgba(49,163,221,.3);
|
||
border-radius:20px;padding:3px 9px;font-family:'Poppins';font-weight:600}
|
||
.phases{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-top:11px}
|
||
.ph{background:var(--card2);border:1px solid var(--line);border-radius:10px;padding:8px 5px;text-align:center}
|
||
.ph u{display:block;text-decoration:none;font-size:9.5px;color:var(--muted2);margin-bottom:2px}
|
||
.ph b{font-family:'Poppins';font-weight:600;font-size:12.5px}
|
||
.ph.hot b{color:var(--red)}
|
||
.gorow{margin-top:11px;display:flex;align-items:center;gap:9px;background:var(--card2);border:1px solid var(--line2);
|
||
border-radius:11px;padding:11px 12px;cursor:pointer}
|
||
.gorow .lab{flex:1;font-size:12.5px}
|
||
.gorow .val{font-family:'Poppins';font-weight:600;font-size:12.5px;color:var(--yellow)}
|
||
.gorow i{font-style:normal;color:var(--muted2)}
|
||
|
||
.amp{display:flex;align-items:center;justify-content:space-between;padding:14px;border:1px solid var(--line2);
|
||
border-radius:13px;background:var(--card2)}
|
||
.amp .v{font-family:'Poppins';font-weight:700;font-size:26px}
|
||
.amp .v small{font-size:14px;font-weight:600;color:var(--muted);margin-left:3px}
|
||
.pm{width:34px;height:34px;border-radius:10px;border:1px solid var(--line2);color:var(--blue);background:transparent;
|
||
font-size:19px;display:grid;place-items:center;cursor:pointer}
|
||
.presets{display:flex;gap:6px;margin-top:9px;flex-wrap:wrap}
|
||
.preset{font-size:11px;border-radius:20px;padding:6px 12px;border:1px solid var(--line2);
|
||
background:var(--card2);color:var(--muted);font-family:'Poppins';font-weight:600;cursor:pointer}
|
||
.preset.on{border-color:var(--blue);color:var(--blue);background:rgba(49,163,221,.1)}
|
||
|
||
.stepper{display:flex;align-items:center;justify-content:space-between;padding:11px 12px;border:1px solid var(--line);
|
||
border-radius:11px;background:var(--card2);margin-bottom:8px}
|
||
.stepper .lab{font-size:12.5px}.stepper .lab small{display:block;color:var(--muted2);font-size:10.5px;margin-top:1px}
|
||
.stepper .ctl{display:flex;align-items:center;gap:13px}
|
||
.stepper .ctl b{font-family:'Poppins';font-weight:600;font-size:13.5px;min-width:64px;text-align:right}
|
||
.stepper .pm{width:26px;height:26px;border-radius:8px;font-size:16px}
|
||
.sw{width:38px;height:22px;border-radius:20px;background:rgba(53,201,138,.85);position:relative;cursor:pointer;flex:none}
|
||
.sw::after{content:"";position:absolute;top:2px;left:18px;width:18px;height:18px;border-radius:50%;background:#fff}
|
||
.sw.off{background:rgba(255,255,255,.16)} .sw.off::after{left:2px}
|
||
|
||
.warn{margin-top:10px;border:1px solid rgba(224,122,122,.32);background:rgba(224,122,122,.07);
|
||
border-radius:11px;padding:10px 11px;font-size:11px;color:var(--muted);line-height:1.5}
|
||
.warn b{color:var(--red);display:block;font-family:'Poppins';font-size:11.5px;margin-bottom:3px}
|
||
.info{margin-top:10px;border:1px solid rgba(49,163,221,.26);background:rgba(49,163,221,.06);
|
||
border-radius:11px;padding:10px 11px;font-size:11px;color:var(--muted);line-height:1.5}
|
||
.info b{color:var(--blue);display:block;font-family:'Poppins';font-size:11.5px;margin-bottom:3px}
|
||
.blocked{border:1px dashed rgba(254,193,19,.35);background:rgba(254,193,19,.05);border-radius:12px;padding:12px}
|
||
.blocked p{margin:9px 0 0;font-size:10.8px;color:var(--muted2);line-height:1.5} .blocked p b{color:var(--yellow)}
|
||
.blocked .row{display:flex;align-items:center;gap:10px;opacity:.55}
|
||
.blocked .row .lab{font-size:12.5px;flex:1}
|
||
|
||
.save{display:flex;gap:10px;margin-top:24px}
|
||
.btn{text-align:center;font-family:'Poppins';font-weight:600;font-size:13px;padding:12px;border-radius:11px;cursor:pointer}
|
||
.btn.go{background:var(--blue);color:#04222f;flex:2;border:0}
|
||
.btn.alt{flex:1.3;background:transparent;border:1px solid var(--line2);color:var(--txt)}
|
||
|
||
.note{width:392px;font-size:11.5px;color:var(--muted2);line-height:1.55;text-align:left}
|
||
.note b{color:var(--yellow)} .note code{color:#9fd4ee}
|
||
.note ul{margin:7px 0 0;padding-left:16px} .note li{margin-bottom:5px}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="head">
|
||
<h1>Raccordement réseau — la limite du disjoncteur, et ce qu'elle protège vraiment</h1>
|
||
<p>Le réglage existe déjà côté box : <code>NymeaEnergy.SetPhasePowerLimit</code>. Sa place naturelle est
|
||
bien le <b>compteur réseau</b>, puisqu'il n'a de sens qu'avec lui — la protection est inopérante sans
|
||
compteur racine.</p>
|
||
<p><b>Trois pièges relevés en lisant la box et le plugin :</b> ① le champ s'appelle « power » mais son unité
|
||
est l'<b>ampère par phase</b> ; ② <b>0 = désactivé</b>, et cela coupe aussi toute la recharge intelligente ;
|
||
③ la protection ne déleste <b>que le véhicule</b> — aucune charge du waterfall n'est réduite par elle.</p>
|
||
</div>
|
||
|
||
<div class="stage">
|
||
|
||
<!-- ══════════ ENTRÉE : CARTE COMPTEUR RÉSEAU ══════════ -->
|
||
<div class="col">
|
||
<div class="phone">
|
||
<div class="appbar"><div class="ic">‹</div><h2>Rôles & appareils</h2><div class="ic">⟳</div></div>
|
||
<div class="scroll">
|
||
|
||
<div class="sect">Compteurs · mesure — hors priorité</div>
|
||
|
||
<div class="meter">
|
||
<div class="m1">
|
||
<div class="ico">⚡</div>
|
||
<div class="nm"><b>Compteur réseau</b><span>Fronius Three Phase Meter · SunSpec</span></div>
|
||
<span class="tag">rootMeter</span>
|
||
</div>
|
||
|
||
<div class="phases">
|
||
<div class="ph"><u>Phase A</u><b>9,2 A</b></div>
|
||
<div class="ph"><u>Phase B</u><b>4,1 A</b></div>
|
||
<div class="ph hot"><u>Phase C</u><b>28,7 A</b></div>
|
||
</div>
|
||
|
||
<div class="gorow">
|
||
<span class="lab">Raccordement & protection</span>
|
||
<span class="val">32 A / phase</span>
|
||
<i>›</i>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info">
|
||
<b>Pourquoi ici</b>
|
||
La limite n'a de sens qu'attachée au compteur racine : la protection lit
|
||
<code>currentPowerPhaseA/B/C</code> sur ce Thing. Sans compteur réseau, la box journalise
|
||
« <i>overload protection is not available</i> » et ne fait rien.
|
||
</div>
|
||
|
||
<div class="sect">Compteur solaire</div>
|
||
<div class="meter" style="opacity:.75">
|
||
<div class="m1">
|
||
<div class="ico" style="background:rgba(53,201,138,.13)">☀</div>
|
||
<div class="nm"><b>Compteur solaire</b><span>Fronius Three Phase Inverter</span></div>
|
||
<span class="tag" style="color:var(--green);background:rgba(53,201,138,.12);
|
||
border-color:rgba(53,201,138,.3)">auto</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="cap">L'entrée — sur la carte du compteur réseau</div>
|
||
<div class="note">
|
||
Les trois ampérages par phase sont <b>déjà lisibles</b> : ce sont les états
|
||
<code>currentPowerPhaseA/B/C</code> du Thing compteur, divisés par la tension. Les afficher ici rend la
|
||
limite concrète — l'installateur voit tout de suite laquelle de ses phases approche.
|
||
<ul>
|
||
<li><b>Rien de tout ça n'est encore branché dans l'app</b> : l'écran actuel n'affiche que le nom du
|
||
Thing et le badge <code>rootMeter</code>.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════ ÉCRAN RACCORDEMENT ══════════ -->
|
||
<div class="col">
|
||
<div class="phone">
|
||
<div class="appbar"><div class="ic">‹</div><h2>Raccordement & protection</h2><div class="ic"></div></div>
|
||
<div class="scroll">
|
||
|
||
<div class="field">
|
||
<label>Courant maximal par phase <span class="saved-flag">enregistré · phasePowerLimit</span><br>
|
||
<small>le calibre du disjoncteur de branchement</small></label>
|
||
<div class="amp">
|
||
<button class="pm">−</button>
|
||
<div class="v">32<small>A</small></div>
|
||
<button class="pm">+</button>
|
||
</div>
|
||
<div class="presets">
|
||
<span class="preset">15 A</span><span class="preset">30 A</span>
|
||
<span class="preset on">32 A</span><span class="preset">45 A</span>
|
||
<span class="preset">60 A</span><span class="preset">63 A</span>
|
||
</div>
|
||
<div class="mode-hint"><b>Des ampères, pas des watts.</b> Le champ s'appelle
|
||
<code>phasePowerLimit</code> mais la box attend un courant par phase — sa propre description le
|
||
dit : « <i>maximum allowed Ampere per phase</i> ». Un formulaire en kVA se tromperait de grandeur.</div>
|
||
|
||
<div class="info">
|
||
<b>Équivalence indicative</b>
|
||
32 A × 230 V × 3 phases ≈ <b>22 kVA</b>
|
||
<span style="display:block;margin-top:5px;color:var(--muted2)">Déduit, jamais enregistré. La box
|
||
convertit avec <b>230 V codés en dur</b> quand la tension mesurée n'est pas disponible ; le nombre
|
||
de phases vient des états du compteur, pas d'un réglage.</span>
|
||
</div>
|
||
|
||
<div class="warn">
|
||
<b>⚠ 0 A ne veut pas dire « pas de limite »</b>
|
||
0 <b>désactive la protection</b> — et avec elle <b>toute la recharge intelligente</b>
|
||
(« <i>All smart charging will be disabled</i> »). C'est la valeur actuelle du banc
|
||
<code>.75</code>, et c'est pour ça que son journal répète
|
||
« <i>overload protection is disabled</i> ». Un champ vide ne doit jamais être enregistré comme 0
|
||
par inadvertance.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Ce que la protection déleste <span class="derived-flag">comportement observé</span></label>
|
||
<div class="stepper">
|
||
<div class="lab">Borne de recharge<small>bridée, puis coupée si nécessaire</small></div>
|
||
<div class="ctl"><span class="sw"></span></div>
|
||
</div>
|
||
<div class="stepper" style="opacity:.55">
|
||
<div class="lab">Chauffe-eau, PAC, charges modulables<small>non concernées aujourd'hui</small></div>
|
||
<div class="ctl"><span class="sw off"></span></div>
|
||
</div>
|
||
<div class="warn">
|
||
<b>⚠ Ce n'est pas un délestage général</b>
|
||
Quand une phase dépasse, la box parcourt <b>uniquement ses bornes de recharge</b>
|
||
(<code>m_evChargers</code>) et réduit leur courant, jusqu'à couper si le minimum n'est plus
|
||
tenable. Les charges du waterfall — routeur de relais, modulable, SG-Ready — ne sont
|
||
<b>pas</b> réduites par cette protection.
|
||
<span style="display:block;margin-top:6px">Étendre le délestage à ces charges est une question
|
||
<b>côté plugin</b>, pas côté app. Tant qu'elle n'est pas tranchée, l'écran ne doit pas laisser
|
||
croire que le raccordement est protégé dans son ensemble.</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Seuil de démarrage sur surplus <span class="saved-flag">enregistré · acquisitionTolerance</span></label>
|
||
<div class="stepper">
|
||
<div class="lab">Surplus requis pour lancer<small>valeur banc : 0,5</small></div>
|
||
<div class="ctl"><button class="pm">−</button><b>0,5</b><button class="pm">+</button></div>
|
||
</div>
|
||
<div class="mode-hint">La box décrit « <i>how much solar surplus is required to start the
|
||
charging</i> » sans donner d'unité. <b>À faire préciser avant d'écrire un suffixe</b> : afficher
|
||
« 0,5 kW » ou « 0,5 A » sur une supposition serait inventer la grandeur.</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Prise en compte de la batterie <span class="saved-flag">enregistré · batteryLevelConsideration</span></label>
|
||
<div class="stepper">
|
||
<div class="lab">Part du stockage considérée<small>valeur banc : 0,9</small></div>
|
||
<div class="ctl"><button class="pm">−</button><b>0,9</b><button class="pm">+</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Verrouillage au débranchement <span class="saved-flag">enregistré · lockOnUnplug</span></label>
|
||
<div class="stepper">
|
||
<div class="lab">Verrouiller la borne au retrait du câble<small>lockOnUnplug</small></div>
|
||
<div class="ctl"><span class="sw off"></span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Délestage par charge <span class="todo-flag">n'existe pas</span></label>
|
||
<div class="blocked">
|
||
<div class="row"><div class="lab">Ordre de délestage en surcharge</div><span class="sw off"></span></div>
|
||
<p><b>À ne pas confondre avec l'ordre de service.</b> L'ordre de « Charges pilotables » répartit
|
||
un <b>surplus</b> ; un ordre de délestage retirerait de la puissance en <b>surcharge</b>. Ce
|
||
sont deux listes qui peuvent différer — et la seconde n'a aujourd'hui aucun support côté box.
|
||
À spécifier avec l'agent plugin si le délestage doit dépasser le véhicule.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="save"><div class="btn alt">Annuler</div><div class="btn go">Enregistrer</div></div>
|
||
</div>
|
||
</div>
|
||
<div class="cap"><b>Raccordement</b> — la limite et ses voisins</div>
|
||
<div class="note">
|
||
Quatre réglages globaux vivent au même endroit parce qu'ils partagent le même public — l'installateur —
|
||
et le même namespace : <code>SetPhasePowerLimit</code>, <code>SetAcquisitionTolerance</code>,
|
||
<code>SetBatteryLevelConsideration</code>, <code>SetLockOnUnplug</code>.
|
||
<ul>
|
||
<li><b>Valeurs lues sur <code>.75</code> le 2026-08-25 :</b> <code>phasePowerLimit: 0</code> (non
|
||
réglé), <code>acquisitionTolerance: 0.5</code>, <code>batteryLevelConsideration: 0.9</code>.</li>
|
||
<li><b>Deux unités restent à faire confirmer</b> par l'agent plugin avant d'écrire un suffixe à
|
||
l'écran : celle de la tolérance d'acquisition, et celle de la prise en compte batterie (un ratio
|
||
0–1, ou un pourcentage ?).</li>
|
||
<li>La protection exige un compteur racine <b>et</b> des états par phase. Un compteur monophasé ou
|
||
sans détail de phase la rend inopérante — l'écran doit le dire au lieu d'afficher un réglage sans
|
||
effet.</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|