Elle reste : la retirer aurait laissé un trou dans une liste que le client a composée lui-même. Elle affiche maintenant ce que la box publie, et là où il n'y a rien à lire, elle le dit. Ce qui est lu, et sa source · branché / en charge ← états pluggedIn, charging du Thing · puissance ← état currentPower du Thing · mode ← GetChargingInfos + notification ChargingInfoChanged Absent = « non publié », jamais zéro. Un mode que l'app ne connaît pas se lit « mode non reconnu (X) » plutôt que d'être deviné, et aucun bouton n'est allumé tant que la box n'a pas dit ce qu'elle porte. L'écriture restitue le refus, sinon elle n'existe pas. Les pastilles de 7 à 10 px qui avalaient les codes d'erreur de setChargingInfo passent à 44 px, montrent l'attente, et affichent le refus tel que la box le nomme. Et l'app ne tient plus d'état de mode local : après une écriture acceptée, elle RELIT GetChargingInfos. Elle se souvenait de ce qu'elle avait demandé, ce qui n'est pas ce que la borne porte. La fiction perd sa porte d'entrée. chargingMode, chargingPower (3,6 kW) et solarSourcePercent (82 %) sont SUPPRIMÉS de EnergyData : trois valeurs par défaut que _updateEnergyData ne touchait jamais. Ce que la box publie sur une borne vit désormais dans EvChargerLive, champ par champ, nullable. ⚠️ Et le Sankey du tableau de bord en tirait un flux « Voiture » permanent à 3,6 kW, voiture débranchée. Il est branché sur la mesure de la borne — « non lu » quand il n'y en a pas. Mais la même vue invente encore PAC = 38 % de la maison, Eau chaude = 18 %, Autres = 22 %, dont un seul marqué « estimé » : entrée 🔴 au TODO, avec la source réelle (GetLoadTelemetry publie measuredW par charge : 1500 W et 800 W ce midi). Brancher le Sankey dessus est une refonte de l'écran d'accueil, pas un correctif — à décider. Deux attentes tombent, une reste (relevé Integrations.GetThings sur .75, cet après-midi) · currentL1/L2/L3 est DÉPLOYÉ (5,86 / 5,94 / 6,02 A) — powerL1/L2/L3 a disparu · phaseCount EXISTE sur la Trydan (3) — la note qui le disait absent était en retard · sessionEnergy existe (4,229 kWh) MAIS vaut exactement chargeEnergy : c'est ce qu'on attend d'une session sans interruption, donc ça ne prouve rien. LM-1009 §C1 reste entière tant qu'une remise à zéro de chargeEnergy n'a pas été vue avec un sessionEnergy qui continue. Le brief moteur dit désormais du masquage WRITE_FAILED que ce n'est pas « ne se produit pas » mais « ne s'est pas encore produit » : il attend une charge variable qui perd son Thing, et le banc n'a pas eu ce cas. 154 tests (8 nouveaux sur la tuile, dont les cibles tactiles et la restitution du refus), flutter analyze à 27 remarques, aucune nouvelle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQbZKrWqsMFP1Lh2jjjd9f
9.5 KiB
TODO — ETM PowerSync App
Ouvert au 2026-08-27
-
🔴 Bilan JOURNALIER— fait le 2026-08-28. Les quatre tuiles montrent la journée, sous un en-tête « Aujourd'hui » qui le dit une fois. Méthode : différence des compteurs entre minuit local et maintenant, jamais l'intégration des puissances — mesurée contre estimée, 0,8 % d'écart sur.75. Le contrôle qui la valide est l'identité production + soutiré == consommation + injecté, vérifiée exactement sur le banc.- Deux bugs d'unité trouvés en chemin : les compteurs
total*sont en kWh et le modèle les nommaitWh. L'écran Énergie les divisait donc encore par mille — une journée de 78 kWh s'y lisait « 78 Wh » — et la simulation accumulait des Wh. - Vérifié sur appareil contre
.75en lecture seule. Le passage a trouvé quatre libellés tronqués qu'aucun des 138 tests unitaires ne pouvait voir.
- Deux bugs d'unité trouvés en chemin : les compteurs
-
❓ « ECS d'abord, batterie ensuite » — à porter au moteur. Demandé le 2026-08-27, et pas exprimable aujourd'hui.
batteryLevelConsiderationfait l'inverse : il annule le budget sous le seuil, donc la batterie passe avant. La batterie n'est pas dans le waterfall — aucune entréeGetLoadConfig, etenableCharging/chargingRate/enableDischargingn'apparaissent nulle part dans le plugin ETM. Le levier existe pourtant : la classe SunSpec Storage de.75déclare ces cinq actions. Question : le stockage est-il une charge arbitrable, ou reste-t-il hors du budget ? Une règleRules.*serait un contournement — deux commandeurs sur la batterie, exactement le défaut que 3g-2 vient de supprimer sur les bornes. -
Zones de climatisation — cadrage écrit, banc à préparer. Voir
docs/CADRAGE_zones_clim.md. Le blocage n'est pas « aucune zone déclarée » : sur les 58 classes de.75, aucune ne portethermostat,closablesensor,temperaturesensorninotifications— aucun plugin installé ne sait en créer une. Deux paquets du dépôt apt du banc suffisent (nymea-plugin-generic-heatingcooling,nymea-plugin-generic-sensors). Six décisions à prendre avant de coder. -
EV_GRID_START/PHASE_LIMIT— éprouvés par test seulement. Les clés ARB sont prêtes, les motifs n'ont jamais été vus sur machine. Signaler la première occurrence réelle. Ne rien bâtir autour d'ici là. -
⏳
sessionEnergyexiste maintenant — mais rien ne prouve encore qu'il survive à une interruption. Relevé du 2026-08-28 après-midi sur.75(Integrations.GetThings) : la V2C publiesessionEnergyetchargeEnergy, tous deux à 4,229 kWh — égaux, ce qui est exactement ce qu'on attend d'une session sans interruption. Le test qui tranche est une remise à zéro observée dechargeEnergyavec unsessionEnergyqui continue. Tant qu'elle n'est pas vue, LM-1009 §C1 reste entière : pas d'avancement affiché. À vérifier sur la campagne de mesures en cours.- Deux attentes levées au même relevé : le renommage
currentL1/L2/L3(unité Ampère) est déployé — plus depowerL1/L2/L3— etphaseCountexiste sur la Trydan (valeur 3), contrairement à ce quedocs/PREPA_BANC_20260828.mdavait conclu.
- Deux attentes levées au même relevé : le renommage
-
— tranché le 2026-08-28 : elle part. La monter aurait publié de la fiction. Trois des grandeurs qu'elle affiche ne sont jamais lues sur la box :EVChargingCardn'est montée par AUCUN écranchargingMode,chargingPower(3,6 kW) etsolarSourcePercent(82 %) sont des valeurs par défaut deEnergyData, et le mode n'est écrit qu'en optimiste après une commande — jamais relu parGetChargingInfos. Sa feuille « Paramètres de charge » proposait en plus un courant minimum, qui n'existe dans aucun contrat, et son bouton « Enregistrer » ne faisait queNavigator.pop: rien n'était écrit. -
La tuile EV des favoris affiche des valeurs par défaut comme des mesures— fait le 2026-08-28 : rendue muette, pas retirée. La retirer aurait laissé un trou dans une liste que le client a composée lui-même.EvFavoriteTile(widgets/ev_favorite_tile.dart) lit la borne —pluggedIn,charging,currentPowerdu Thing, mode parGetChargingInfos+ChargingInfoChanged— et dit ce qui n'est pas publié plutôt que d'afficher zéro. L'écriture du mode restitue le refus et les cibles tactiles passent de 7–10 px à 44 px. Les trois champs fictifs deEnergyData(chargingMode,chargingPower,solarSourcePercent) sont supprimés du modèle : la fiction n'a plus de porte d'entrée. -
🔴 La vue « consommation » du Sankey est INVENTÉE — tableau de bord, écran d'accueil.
features/dashboard/widgets/energy_flow_card.dart: au drill-down consommation, PAC = 38 % de la maison, Eau chaude = 18 %, Autres = 22 % — trois ratios en dur, dont un seul (Autres) porte le marqueurestimated. La voiture, elle, a été branchée sur la mesure réelle de la borne le 2026-08-28 (currentPower, « non lu » sans borne). Le reste attend une décision : la source existe —GetLoadTelemetrypubliemeasuredWpar charge (relevé du jour : chauffe-eau 1500 W, PAC 800 W) — mais brancher le Sankey dessus est une refonte de l'écran d'accueil, pas un correctif. Ce qu'une carte de recharge dédiée pourrait dire, si elle revient un jour :- le mode courant, lu par
NymeaEnergy.GetChargingInfoset tenu à jour parChargingInfoChanged— l'écriture existe déjà et est vérifiée (nymea_service.setChargingInfo, lecture → patch → réécriture complète) ; - la puissance mesurée de la borne, prise sur le Thing (
currentPower) ; - l'échéance (
targetPercentage+endDateTime) — mais uniquement comme intention saisie, jamais comme un avancement : LM-1009 §C1. Pas de SOC véhicule (il n'existe pas), pas dechargeEnergy(remis à zéro à chaque interruption), pas de courant minimum (aucun champ), pas dephaseCount(la V2C ne le publie pas).
- le mode courant, lu par
-
Le passage sur appareil doit devenir une habitude, pas un rattrapage. Il a glissé deux fois, et au premier passage il a trouvé deux cibles tactiles hors norme (26 px, 18 px) qu'aucun des 129 tests unitaires ne pouvait voir.
integration_test/ui_3g2_on_device_test.dartest le harnais. Rappel MIUI : la confirmation d'installation est redemandée à chaque fois — faire unadb install -r -tpréalable, puis lancer le test.
Charges pilotables — ce qui attend la box (2026-08-26)
— fait le 2026-08-27. Le motif est publié :kCodesReserveBatterieBATTERY_RESERVE, paramssocPercent/reservePercent/withheldW. Les clés que le fichier cherchait (batteryLevel,threshold) n'avaient jamais existé côté plugin — le motif sortait donc en repli anglais sur le seul cas qu'aucune mesure ne révèle.minPowerW == nullsans véhicule branché — couvert en test unitaire (test/ev_runtime_test.dart), pas exercé sur appareil : la borne n'entre pas dansloads[]avant3g. À reprendre quand l'arbitrage prendra la recharge en charge.- Réserve batterie — portée élargie. Le jour où le seuil gouverne tout le budget,
reprendre le texte de
BatteryReserveCard: il annonce aujourd'hui un futur, il devra décrire un présent. Zones — à ne pas entreprendre— décision revue le 2026-08-27 : le lot est cadré (docs/CADRAGE_zones_clim.md), voir l'entrée en tête de ce fichier.ac_screen.dartreste de la donnée fictive — quatre pièces, huit températures crédibles affichées comme des mesures.
En cours / priorité haute
-
TariffProvider — connexion RPC réelle
load()appelle encore unFuture.delayedfictif- Brancher sur nymea JSON-RPC pour récupérer les prix live (ENTSO-E, Tibber, etc.)
-
EnergySetupProvider — persistance
- Les assignations de rôles (EV, PAC, batterie…) ne survivent pas au redémarrage
- Sérialiser en SharedPreferences ou JSON local
-
SchedulerProvider — exécution réelle
forceRecalc()ne fait rien pour l'instant- Brancher sur l'API nymea Energy Scheduler (si disponible)
Screens à implémenter
- DeveloperScreen — logs, introspection JSON-RPC, test ping nymea
- AboutScreen — version app, version nymea, licences
- SystemSettingsScreen — adresse serveur nymea, port, transport (TCP/WS)
Amélioration UX
- Timeline — remplacer la simulation bell-curve par les vraies créneaux du scheduler nymea
- RoleConfigFlow Step 3 — test de connexion sur RPC réel au lieu du délai simulé
- InstallerModeProvider — persister le hash PIN dans SharedPreferences (actuellement en mémoire)
- AppSettingsProvider — persister les préférences (thème, densité, écrans actifs) dans SharedPreferences
Intégration nymea live
- Mapping
EmsRole → nymea thingClasspour les installations réelles - Vérifier les interfaces nymea sur serveur réel (comparer avec
docs/nymea_integrations.md) Energy.GetSchedule/Energy.SetSchedulesi l'API le supporte
Technique / dette
- Ajouter des tests unitaires pour
TariffProvideretEnergySetupProvider - Gérer le cas où
NymeaService.thingClassesest vide en mode live (classes non chargées) - Revoir
_Step1ThingList: passer encontext.watchpour réactivité si les things changent