Deux gestes que le relevé du banc a tranchés, dans les deux sens.
1. THING_MISSING — MESURÉ, la réserve tombe
GetLoadTelemetry sur .75, 2026-08-28 10:29 UTC : la Terra AC supprimée est publiée
available=false · faultCode=THING_MISSING · decision=LOAD_UNAVAILABLE{frozenW:0} ·
mechanism.kind=evcharger. Le code SORT. Le test de rendu ne repose plus sur une trame
composée mais sur cette capture (test/fixtures/loadtelemetry_hems75_thingmissing.json).
Le masquage relevé mardi (etmvariableloadadapter.cpp:148-150, m_faulted testé avant que
THING_MISSING ne puisse s'exprimer) concerne l'adaptateur des charges VARIABLES — relais
et SG-Ready. L'adaptateur evcharger n'a pas ce chemin, d'où la différence. Il n'est donc
pas corrigé, seulement contourné par le hasard du chemin de code : remonté au brief
moteur avec la trame, parce que les deux codes envoient le client faire deux gestes
différents — « écriture échouée » fait démonter un coffret, « appareil absent » fait
rouvrir nymea.
2. EVChargingCard — elle part, et la monter aurait publié de la fiction
Trois des grandeurs qu'elle affiche ne sont JAMAIS lues sur la box : chargingMode,
chargingPower (3,6 kW) et solarSourcePercent (82 %) sont des valeurs par défaut de
EnergyData — _updateEnergyData ne les touche pas, et le mode n'est écrit qu'en optimiste
après une commande, jamais relu par GetChargingInfos. Sa feuille « Paramètres de charge »
proposait en plus un courant minimum, qui n'existe dans aucun contrat, et son bouton
« Enregistrer » ne faisait que Navigator.pop : rien n'était écrit. Ce n'était pas du code
mort à ranimer, c'était un écran à réécrire depuis le modèle.
⚠️ Et la surface vivante est pire que la morte : _EVWidget (favorites_screen.dart:419)
affiche les MÊMES valeurs par défaut comme si elles étaient mesurées — « 3600 W »,
« 82 % solaire », « Mode PV » — et écrit le mode sur des pastilles de 7 à 10 px sans
restituer un refus. Entrée 🔴 au TODO, avec ce qu'une carte de recharge peut dire
aujourd'hui : le mode lu par GetChargingInfos, la puissance mesurée du Thing, l'échéance
comme intention saisie. Ni SOC (n'existe pas), ni chargeEnergy (remis à zéro à chaque
interruption), ni courant minimum, ni phaseCount.
146 tests passent. flutter analyze : 27 remarques, deux de moins qu'avant — les deux
étaient dans le fichier supprimé.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQbZKrWqsMFP1Lh2jjjd9f
Le test d'appareil passe. Il a trouvé deux défauts réels que 129 tests unitaires ne
pouvaient pas voir, parce qu'aucun ne rend ces widgets à une taille d'écran réelle.
1. LA POIGNÉE DE GLISSEMENT MESURAIT 26 × 27 px, pour un minimum Material de 48. Le
Container transparent posé lors de l'enquête précédente avait supprimé la bande morte
entre l'icône et le reste, mais il n'avait jamais AGRANDI la cible : le geste accrochait
quand on visait juste et ratait sinon. C'est le seul geste de cet écran sans repli —
sans lui, l'ordre des domaines ne se change pas du tout.
2. LES FLÈCHES DE RANG MESURAIENT 18 × 18 px, la taille de l'icône, sans aucune marge de
touche. Elles n'ont pas de repli non plus : réordonner DANS un domaine ne se fait que
par elles.
Portées à 48 et 44. Et la capture d'appareil a montré le coût de la correction naïve :
empilées, deux cibles de 44 font 88 px de haut pour deux icônes de 18, et la colonne de
rang devient une colonne vide. Côte à côte, la hauteur retombe à 44 et la largeur ne coûte
rien au libellé. La cible tactile est la contrainte ; la disposition ne l'est pas.
Le harnais a lui-même demandé trois corrections avant de dire la vérité, et chacune est un
piège qui se reproduira :
- il regardait le HAUT de l'écran. Dans un ListView, ce qui n'est pas visible n'est pas
construit : les cibles n'étaient pas mesurables faute d'être montées.
- `find` trouve un widget POSÉ MAIS HORS VIEWPORT, et getCenter en rend une coordonnée qui
n'existe pas — mesuré y=1129 sur un écran de 825 px. Le geste partait dans le vide, avec
un symptôme identique à un glissement ignoré.
- la course était devinée. Une liste réordonnable ne permute qu'une fois passé le milieu du
suivant, et un bloc de domaine porte ses cartes : 384 px mesurés, contre les ~150 devinés.
Elle se calcule maintenant depuis l'écart réel entre deux poignées.
Le test mesure aussi le défilement de la page pendant le glissement : si le ListView
extérieur gagnait l'arène, le symptôme serait le même que celui d'une poignée qui
n'accroche pas, pour une cause opposée. Mesuré à 0 px — c'est bien la poignée qui prend.
Aucun débordement de mise en page sur cet écran. Deux captures d'appareil jointes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq
Le test d'appareil est ÉCRIT ET NON PASSÉ : l'installation est refusée par MIUI
(INSTALL_FAILED_USER_RESTRICTED — « Installer via USB » remis à zéro, troisième fois). Ni
adb install, ni pm install depuis /data/local/tmp ne passent : c'est une restriction
utilisateur, elle demande le toggle sur le téléphone. Rien de ce lot n'a donc été vu à
l'écran, et c'est dit plutôt que contourné.
Ce qu'il ira chercher, et qu'aucun test unitaire ne montrera :
- les débordements de mise en page, RÉCOLTÉS tous ensemble via FlutterError.onError plutôt
qu'échoués au premier — un « overflowed by » ne fait échouer aucun test de ce dépôt,
parce qu'aucun ne rend ces widgets à une taille d'écran réelle ;
- la taille des cibles tactiles, mesurée sur l'écran réel, avec un plancher dur sur la
poignée de glissement : c'est le seul geste de cet écran qui n'a pas de repli ;
- le glissement AU DOIGT, en appui maintenu puis mouvements successifs — un tester.drag
simple « passe » sans rien réordonner, ce qui avait déjà fait croire à un geste avalé.
Exercé puis ABANDONNÉ : le geste est ce qu'on veut voir, pas l'écriture.
Il n'exerce pas la carte véhicule, et pour une raison qu'il fallait constater : EVChargingCard
n'est montée par AUCUN écran. Le tableau de bord utilise features/dashboard/widgets/. Le SOC
inventé à 62 % que j'ai retiré hier n'était donc affiché nulle part — le défaut était réel,
sa portée non.
Cadrage des zones de clim, avant toute maquette. Le constat qui commande le lot : le banc ne
peut pas héberger une zone, et ce n'est pas « il manque un appareil ». Sur les 58 classes de
.75, AUCUNE ne porte thermostat, closablesensor, temperaturesensor ni notifications — aucun
plugin installé ne sait en créer une. Deux paquets du dépôt apt du banc suffisent, et le
document donne l'exigence d'interface par emplacement, lue dans verifyThingIds() plutôt que
devinée. Avec une réserve trouvée en chemin : valves est au schéma mais n'est PAS vérifié par
la box — un emplacement qui accepte n'importe quoi sans le contrôler.
L'écran ac_screen.dart n'appelle pas rien : il appelle GetZones, et seulement pour compter.
Ses quatre pièces restent de la maquette, avec huit températures crédibles affichées comme
des mesures — le même défaut que le SOC à 62 %, en plus grand.
Et le brief plugin, complété entre-temps, tranche la question de la cible aveugle : le
carBatteryLevel auquel targetPercentage se compare est une valeur que le MOTEUR écrit
lui-même, publiée sous un nom de mesure. L'avancement s'affichera en énergie livrée.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016vrTVifar2GN5rtUh89Wkq