7 Commits

Author SHA1 Message Date
Patrick Schurig
95769938fc fix(ev): la tuile des favoris devient muette — elle lit la borne, ou le dit
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
2026-08-28 12:49:28 +02:00
Patrick Schurig
aada8e2ba5 fix(config): glissement, sélecteur de mécanisme, chip de domaine, thème — les 4 points ouverts
1) GLISSEMENT — diagnostic d'abord, et il a donné DEUX réponses.

   Le geste n'est PAS avalé par le ListView parent : les deux montages du test
   (section seule / section imbriquée) glissent identiquement. La cause du symptôme était
   bien que .75 n'a qu'une charge par domaine — rien à réordonner.

   Mais l'enquête a trouvé un vrai défaut au passage : le centre de la poignée tombait dans
   le vide de 3 px séparant l'icône de la pastille de rang, et ReorderableDragStartListener
   défère le test de collision à ses enfants. Une bande morte invisible au milieu d'une
   poignée qui paraît d'un seul tenant. Corrigé par un Container transparent.

   Tentative faite et ANNULÉE : rendre les domaines glissables aussi. Deux
   ReorderableListView imbriqués se disputent le geste et l'extérieur gagne — le
   glissement des cartes cassait. Les domaines gardent leurs flèches, et une charge seule
   dans son domaine n'offre plus de prise qui mènerait nulle part (avec l'infobulle qui
   renvoie au bon geste).

   Vérifié SUR L'APPAREIL, contre .75, avec les deux charges mises dans le même domaine :
   [chauffe-eau, pac-terrain] → [pac-terrain, chauffe-eau], brouillon abandonné sans rien
   écrire. Classement ecs/hvac rétabli ensuite.

2) SÉLECTEUR DE MÉCANISME — actif, et c'était le morceau délicat.

   LoadConfig est une union discriminée : écrire `adapter` sans nettoyer le reste produit
   une configuration que la box refuse EN BLOC, et le refus emporte toutes les charges de
   l'appel. D'où basculerMecanisme(), qui retire les clés du mécanisme quitté et pose
   celles du nouveau, en récupérant ce qui peut l'être — les deux premiers contacts
   deviennent SG1/SG2 dans l'encodage standard, les relais gardent leur puissance.

   Ce qui n'est PAS transposé : l'estimation d'un état SG-Ready n'est pas la puissance
   d'une résistance. La charge produite est alors incomplète, et c'est voulu — le
   formulaire réclame plutôt que d'inventer une valeur de plaque.

   La bascule franchit sameHardware() : confirmation préalable qui annonce la
   reconstruction et la durée de réarmement (900 s par défaut vers SG-Ready — la valeur
   d'une PAC réelle, pas celle du banc).

   Le mode fixed d'etmvariableload reste LISIBLE sans être proposé : bloc « Configuration
   héritée » en lecture seule, et conversion seulement sur geste explicite, avec le
   plafond que la déclaration en paliers ne porte pas. Ouvrir l'écran ne convertit personne.

3) CHIP DE DOMAINE — il ne rendait qu'une icône muette : le domaine d'une charge était
   illisible sur sa carte, alors qu'il décide de son groupe et de son rang. Il porte
   maintenant son libellé, et sa couleur suit le thème.

4) THÈME — les 11 derniers sites (pas 9) basculés. Trois cas : contexte disponible
   (main, ac_screen) ; peintre sans contexte, à qui la couleur est passée résolue
   (energy_flow_widget) ; tables statiques, où la teinte devient nullable et se résout au
   rendu (categoryInfoMap, favoris). Plus AUCUN jeton hors thème dans lib/.

5) ZONES — non entreprises, comme convenu.

Tests 93/93, analyze 0 erreur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff
2026-08-26 08:49:20 +02:00
Patrick Schurig
38475e0007 fix(dashboard+thème): compteurs réseau honnêtes, et le sélecteur clair/sombre s'applique partout
1) « Vers réseau » — TROIS défauts, dont un seul était visible.

   La valeur aberrante (9,10184409403335e+33 kWh) ne vient PAS de l'app : Energy.GetPowerBalance
   la publie telle quelle sur .75. Trois des quatre compteurs cumulés sont corrompus
   (totalReturn 9,1e+33, totalAcquisition 7,5e+33, totalConsumption -1,6e+33) alors que
   totalProduction reste sain, et que les états du Thing compteur sont tous plausibles
   (1429 kWh importés, 88 kWh exportés). C'est donc l'accumulation de l'experience-plugin
   énergie qui déraille, côté box — signalé, pas corrigé ici.

   Les deux autres défauts étaient bien dans l'app :
   - « Depuis réseau » lisait l'OPPOSÉ du compteur d'export au lieu de totalAcquisition,
     qui a son propre compteur. Dès que l'export était positif, la tuile affichait
     0,0 kWh en permanence, quelle que soit l'électricité réellement achetée ;
   - les deux tuiles étaient libellées « aujourd'hui » alors que ces compteurs sont
     CUMULÉS depuis l'origine, jamais remis à zéro.

   Corrigé : EnergyData porte gridExportKwh / gridImportKwh, transportés tels quels — l'app
   ne maquille pas une donnée que la box publie fausse. Un plafond de plausibilité
   (10 GWh, ~3000 ans de consommation) distingue « mesure » de « compteur cassé » : au-delà
   la tuile affiche « — · compteur box invalide ». Imprimer le nombre le présenterait comme
   une mesure ; l'effacer laisserait croire à zéro.

2) Thème : 126 sites basculés des constantes claires vers les variantes contextuelles
   (EtmTokens.bg → bgOf(context), card → surfaceOf, ink → inkOf, muted → mutedOf,
   faint → faintOf, line → lineOf) dans ac_screen, things_screen, energy_screen,
   favorites_screen, main_drawer, ev_charging_card, energy_flow_widget. Ces écrans
   restaient en palette claire quel que soit le sélecteur.

   9 sites RESTENT en constante, et c'est délibéré : ce sont des tables statiques et des
   helpers de haut niveau (categoryInfoMap, _options des favoris, _modeColor, peintres du
   flux) où la couleur est une identité d'icône et où aucun BuildContext n'est en portée.
   Les faire suivre le thème demande de threader le contexte jusqu'à ces tables — un
   refactor à part entière, pas un correctif de fin de soirée.

Tests 80/80, analyze 0 erreur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HajXLUczEyZd22JeewRfff
2026-08-25 23:13:20 +02:00
Patrick Schurig ETM-Schurig
80500e21e6 feat: favoris — persistance SharedPreferences + restyle EtmTokens
Persistance (FavoriteWidget)
- FavoriteWidget.fromJson / toJson ajoutés dans nymea_models.dart
- NymeaService : constructeur appelle _loadFavorites() au démarrage
- _saveFavorites() appelé après addFavorite / removeFavorite / reorderFavorites
- Clé SharedPreferences : 'etm_favorites_v1'
- Résistance aux données corrompues (try/catch sur fromJson)

Restyle (favorites_screen.dart)
- AppTheme → EtmTokens sur toutes les couleurs et typographies
- Valeurs en IBM Plex Mono (size 36 pour les grandes métriques)
- Cartes avec EtmTokens.cardShadow + border radius 22
- _EmptyState : bouton vert + étoile + message
- _AddSheet : DraggableScrollableSheet restyled, sans emoji, items visuellement
  distingués (ajouté vs disponible)
- ReorderableDelayedDragStartListener pour le drag discret
- Simulation auto-démarrée si non connecté (comme Dashboard/Things)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 22:07:14 +02:00
e42412fef8 feat: EV charging UI — 3 modes PV/Min+PV/Boost + deadline option
- setChargingInfo complet avec EcoWithMinCurrent + EcoMinWithTargetTime
- UI 3 boutons + toggle Cible + slider SOC 0-100% + time picker
2026-04-05 07:55:13 +02:00
c19c9d1a98 feat: navigation drawer, EMS setup, scheduler, tarifs, paramètres app
- Drawer custom (overlay Stack) avec mode installateur PIN SHA-256
- GoRouter + ShellRoute : navigation préservée entre onglets
- 6 providers : NavigationProvider, InstallerModeProvider, AppSettingsProvider,
  EnergySetupProvider, SchedulerProvider, TariffProvider
- Écrans Energy Manager : RoleConfigFlow (3 étapes), Scheduler, Tarifs, Timeline
- Écrans Paramètres : Apparence, Écrans actifs, AppSettingsScreen
- DrawerMenuButton présent dans les 5 AppBars principaux
- Simulation : _thingClasses générées avec interfaces EMS pour filtrage des rôles
- Compteur solaire : ajout smartmeter aux interfaces compatibles
- Thème ETM (etm_theme.dart), ProLockBadge, widgets PowerBar/RoleCard/TimelineSlotCard
- Dépendances : go_router, shared_preferences, crypto, url_launcher

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:52:32 +01:00
etm
d5dc0c7ca5 Initial commit : Flutter app nymea energy monitor
- NymeaService : auth complète (Hello → Authenticate → SetNotificationStatus)
- Token top-level dans chaque requête JSON-RPC (fix critique GetThings)
- Persistance token via shared_preferences par hôte
- Dashboard : champs utilisateur/mot de passe dans le dialog de connexion
- ThingDetailScreen : renommer, réglages (settingsTypes) et supprimer
- NymeaThingClass : champ settingsTypes parsé depuis l'API
- NymeaThing : copyWith(name) + settingValue()
- Fix overflow _StateChip dans ThingsScreen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:57:46 +01:00