Patrick Schurig bb77039c82 docs: RECAP session 2026-08-25 + accès ssh banc .75
Point d'arrêt avant redémarrage machine. Consigne l'état du lot B app, le
bloqueur (lot plugin non déployé sur .75), le partage vérifié/non-vérifié,
et la question ouverte sur la fusion /energy/setup ↔ /energy/loads.

Note l'accès ssh au banc désormais documenté dans CLAUDE.md : il lève la
limite qui rendait le critère 7-1bis (non-reconstruction des adaptateurs)
invérifiable faute de journal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 14:31:58 +02:00

13 KiB

RECAP — etm-powersync-app · reprise de session

Session 2026-08-25. Branche feature/beta-add-config. Rien n'est poussé. La section « État au 2026-06-30 » plus bas reste valable pour tout ce qu'elle décrit.


Session 2026-08-25 — ce qui a été fait

Commits ajoutés

Commit Contenu
7ca4297 Lot B app — ordre de service des charges, locale, ossature i18n
40ac3b2 Fixtures : dump réel GetLoadConfig + schéma introspecté de .75

1. Menus (demande initiale)

  • « Rôles & appareils » et « Options développeur » → section MODE INSTALLATEUR uniquement.
  • Groupes / Scènes / Médias / Garages → masqués par défaut. Le drawer lit désormais AppSettingsProvider.visibleScreens (ces 4 écrans y étaient déjà à visible: false ; le drawer les affichait en dur et ignorait le réglage).
  • Garde de routeur _isInstallerRoute (main.dart:41) : /settings/system*, /settings/app/developer, /energy/setup, /energy/loads → redirection vers / si le mode installateur est verrouillé. Couvre l'URL directe, le deep-link et l'auto-lock 10 min.
  • Liens SUPPORT corrigés : doc → https://docs.etm-powersync.fr/, Telegram → https://t.me/etm_powersync_support, Discord retiré (commenté sur place).

2. Investigations rendues (constats factuels, pas de code)

  • Locale nymea : gérée PAR CONNEXION (o:locale dans Hello, m_clientLocales). Pas de forçage LANG/LC_ALL dans l'unité systemd. Mais sur 784 chaînes affichables de .75, l'allemand en change 1, le français 0 : aucun catalogue .qm fr n'est livré. Les displayName = identifiants anglais.
  • Frontière RPC / runtime de l'arbitre : quasi rien ne sort. Seuls degradedMode (notification ChargingSchedulesChanged uniquement — pas relisable par Get), les deux ratios d'énergie, chargingState de l'EV et le plan de recharge franchissent. Allocation, budget, verrous, paliers, available, motifs : journal seulement. SurplusContext / LoadContextTelemetry n'ont aucune sérialisation sortante.

3. Lot B app — livré

  • models/load_config_entry.dart — vue en lecture au-dessus de la map brute ; écriture par patched() depuis la map d'origine, jamais de re-sérialisation.
  • services/load_priority.dart — groupByDomain / flattenToPayload / applyDefaultDomainOrder / miroir client de validateSet().
  • providers/load_config_provider.dart — séquence pending → LoadConfigChanged → confirmed|diverged. Ne conclut jamais sur l'acquittement du RPC.
  • screens/energy/load_order_screen.dart — route /energy/loads, gatée installateur.
  • i18n : flutter_localizations + intl + lib/l10n/app_fr.arb + l10n.yaml. Extension context.l10n. Français seul.
  • NymeaEnergy.GetLoadConfig / SetLoadConfig deviennent réels (c'étaient des stubs).
  • Locale envoyée à chaque Hello, demandée + retenue journalisées.

4. ⚠️ Piège désamorcé — à ne pas réintroduire

EnergySetupProvider.persist() appelait setLoadConfig(buildLoadDescriptors()), une reconstruction depuis le modèle typé, et _commit() l'appelle à chaque changement d'UI. Inoffensif tant que le RPC était un stub ; destructeur une fois réel (SetLoadConfig remplace tout l'ensemble → la charge chauffe-eau de .75 aurait été écrasée). Émission débranchée, raison documentée sur place.


⛔ BLOQUEUR PRINCIPAL À LA REPRISE

Le lot plugin brief-plugin-lotB-telemetrie.md n'est PAS déployé sur .75. Introspect au 2026-08-25 : toujours 19 méthodes / 13 notifications.

GetLoadTelemetry      : ABSENT
LoadTelemetryChanged  : ABSENT
o:domain              : ABSENT du schéma SetLoadConfig

Conséquence : critères 4 à 8 du brief lot B hors d'atteinte (domaine, télémétrie vivante, fraîcheur, défaut/ClearLoadFault, code inconnu réel). Tant que ce lot n'est pas sur la box, il n'y a plus rien d'utile à faire côté app sur ce périmètre.

Vérifié / non vérifié (lot B app)

Critère Statut
§7-1 aller-retour neutre ✅ sur .75 en vrai — SetLoadConfig verbatim → relecture identique
§7-1bis non-rebuild ECS-412 ⚠️ lecture de code seulement (sameHardware()), journal non consulté
§7-2 aplatissement réversible ✅ test unitaire seul — .75 n'a qu'une charge
§7-3 config non contiguë ✅ test unitaire seul — fixture synthétique
§7-4 à §7-8 ❌ bloqués (lot plugin)
§7-9 locale ✅ protocole vérifié sur .75 ; ⚠️ chemin app jamais exécuté sur appareil (adb refusé)
§7-10 analyze / tests / gating ✅ 25 issues = ligne de base, 30/30 tests

Question ouverte à trancher

Le brief désignait « Rôles & appareils » (/energy/setup) comme écran cible, mais cet écran est bâti sur le modèle inverse (il construit des LoadDescriptor depuis des EmsRole). J'ai créé /energy/loads à côté et laissé /energy/setup intact. Fusionner les deux est une refonte, pas un câblage — décision Patrick attendue.

Nouveauté à exploiter à la reprise

CLAUDE.md documente désormais l'accès banc : ssh etm@192.168.1.75 (clé installée), journal via journalctl -u nymead. Cela lève la limite qui rendait §7-1bis invérifiable — au prochain aller-retour neutre, lire le journal pour confirmer qu'aucune reconstruction d'adaptateurs n'a lieu.


RECAP — etm-powersync-app · reprise de session

État au 2026-06-30. Branche feature/beta-add-config (9 commits au-dessus de master). Rien n'est poussé (push différé). Aucun merge vers master/silo.


1. Où on en est (commits, du plus récent au plus ancien)

Commit Contenu
b4a6c97 Lot C — squelette discovery de things (⚠️ pending SDM réel)
b7a7213 Catalogue de things (lecture seule) + filtres (lot A)
3a535e0 Écran Protocoles — CRUD masters Modbus RTU
0ebbde4 chore(docs) — specs/contrats/refs rangés dans docs/
be6694c chore — ignore /screenshots
f755c72 docs — contrat UI rev.5 (réserve décision-2 levée)
3821bf3 refactor(energy) — retrait calcul ratios app-side → seam EnergyRatiosInterim
b5abe42 docs — contrat UI rev.4
c638ec6 Connexions multi-HEMS + Rôles & appareils (gros commit fondateur)

Source de vérité contrats : docs/UI_data_contract.md (rev.5), docs/reference/jsonRPC.txt (introspection — fait foi pour les RPC).


2. Ce qui est LIVRÉ et fonctionnel

Connexions multi-HEMS (validé live)

  • models/installation.dart, services/installation_store.dart (token par UUID via flutter_secure_storage, métadonnées SharedPreferences).
  • services/connection_manager.dart : mono-connexion, switchTo, connectNew, enterDemo (démo = active factice), contrôle d'identité DHCP, invalidation des caches au switch.
  • nymea_service : connect() réveillé (ws://4444, token par UUID), capture Hello (uuid/name/initialSetupRequired), authenticate/createUser, resetState.
  • Écran Installations (3 états) + écran Connexion (auth 2 branches, détection auto).
  • Gate de routage (buildAppRouter, redirect + refreshListenable: merge([cm,svc])), hasActiveConnection = isSimulation || (connected && authenticated).
  • En-tête drawer cliquable → Installations.
  • Testé en vrai : login auth contre .120, .75 ouvert, switch, déverrouillage installateur.

Rôles & appareils

  • EnergySetupProvider tri-état (present/absent/non-configuré), écran roles_devices_screen, LoadDescriptor (rév.2 : PAC exclue → SG-Ready), Energy.SetRootMeter réel, Get/SetLoadConfig en stub loggé.

Energy — Phase 1 (interim)

  • services/energy_ratios.dart = SEAM UNIQUE EnergyRatiosInterim : ratios autoconso/autonomie par Δ-de-cumuls (reseed jour/non-monotone, gardes n/a, clamp). selfConsumptionPower net-signé sans .abs(). Test : test/energy_ratios_test.dart.
  • Calcul ratios app-side retiré de _parsePowerBalance + getter autoconsommationW supprimé.

Installateur — Modbus RTU (validé live)

  • models/modbus_models.dart, 5 RPC ModbusRtu.* dans nymea_service, écran protocols_screen (CRUD), route /settings/system/protocols.

Installateur — Catalogue things lot A (validé live)

  • getVendors() + getAllThingClasses(), écran thing_catalog_screen (liste groupée + 3 filtres : fabricant/type/recherche + détail read-only), route /settings/system/things.

Installateur — Discovery lot C (SQUELETTE, NON validé matériel)

  • ThingDescriptor, discoverThings(), addThingFromDescriptor(), thing_discovery_screen (form slaveAddress → scan → descriptors → AddThing).

3. ⚠️ BLOQUEURS / EN ATTENTE

  • Lot C — merge bloqué tant qu'on n'a pas un descriptor SDM réel sur le bus de hems .75 (besoin : adaptateur USB-RS485 + un vrai compteur SDM branché ; aujourd'hui le master /dev/ttyUSB0 est connected:false). 3 points marqués À REVALIDER SUR SDM RÉEL dans thing_discovery_screen.dart / nymea_service.dart :
    1. résolution master/slave par heuristique de nom de paramTypes ('master'/'modbus'/'slave') ;
    2. timeout scan (_sendRequest = 15 s, à allonger si bus réel plus lent) ;
    3. title/description du descriptor (peuvent être vides/génériques).
  • Push : jamais fait (volontaire).

4. Lots / phases NON FAITS (file d'attente)

  • Lot B — ajout direct CreateMethodUser + JustAdd (form paramTypes). Sous-ensemble = 22 classes (ABB B2x, ABB Terra AC RTU/TCP, génériques/simulés nymea). ⚠️ Les Eastron SDM N'EN SONT PAS (ils sont Discovery → lot C). Piège param : nom du master varie (modbusMasterUuid vs rtuMaster). Reporté après C.
  • Lot D — discovery → pairing (OAuth/PushButton/UserAndPassword).
  • Connexions Étape 3 — découverte mDNS native (multicast_dns, natif only ; web = ajout manuel). Différée.
  • Connexions — branche CreateUser (box neuve) : codée mais non testée live (pas de box vierge/factory-reset sous la main).
  • Energy Phase 2/3/4 (session dédiée, voir mémoire) : Phase 2 = states ratios côté energymanager (autre repo) ; Phase 3 = persistance Influx ; Phase 4 = refonte flow-card §1.1 + bandes + retrait définitif du calcul app-side. Contrat = docs/UI_data_contract.md.

Bugs latents notés (hors lot)

  • dashboard_screen.dart : onRefresh: () => service.startSimulation() → sur box réelle, pull-to-refresh bascule en démo. À corriger au câblage du refresh réel.
  • Affichage signe : sans .abs(), conso reste net-signée → Maison (flow-card) & ligne conso (graphe) montrent le signe brut jusqu'à la Phase 4 (voulu).

5. Banc de test

Cible Détail
hems .75 nymea 1.15.2, ouverte (authenticationRequired:false). Master RTU /dev/ttyUSB0 présent mais bus hors-ligne. Sert aux sondes read-only sans auth.
box 2 192.168.1.120 auth requise (initialSetupRequired:false → login). Vraie installation avec SDM120 PV.
InfluxDB local sur etm-powersync-dev, v1.6.7 (PAS Influx 3 Core), DB nymeatest, RP tiers déjà présentes.
Tél Android Redmi Note 9S, adb WiFi 192.168.1.107:5555.

PIN installateur par défaut = 1234.

Comment lancer / tester

  • Réel (réseau) : flutter run -d 192.168.1.107:5555 (Android WiFi). Pas Chrome pour le réseau (CORS LAN). Voir mémoire web-lan-cors.
  • Linux natif : ❌ bloqué (Flutter snap sans linker ld.lld). Web headless = page blanche (CanvasKit). Captures fiables = adb exec-out screencap -p.
  • Sonde RPC sans GUI : script dart:io WebSocket.connect('ws://<host>:4444') + JSON-RPC (ex. utilisé pour valider Modbus/discovery contre .75).

6. Faits techniques à ne pas reperdre

  • Signes nymea (power balance agrégé) : production positif, consumption NÉGATIF, acquisition net-signé (négatif = export). currentPower thing-level = négatif pour un producteur (convention conso). Ne jamais deviner/.abs() le signe.
  • Cumuls (totalAcquisition / totalReturn) séparés → autoconso/autonomie dérivables des logs. UUID Hello brace-wrapped {...} (normalisé dans _finishConnect).
  • Discovery SDM : discoveryParam = slaveAddress uniquement (Int, déf 1). Le master n'est PAS une entrée — il ressort dans descriptor.params. Ajout final = AddThing(thingDescriptorId).
  • CreateMethod = {User, Auto, Discovery} · SetupMethod = {JustAdd, DisplayPin, EnterPin, PushButton, UserAndPassword, OAuth}. Orthogonaux (une classe JustAdd peut exiger Discovery).

7. Reprise — prochaines actions probables

  1. Brancher un SDM sur .75 → sonde discovery réelle → lever les 3 points + débloquer le merge du lot C.
  2. Puis lot B (ABB + génériques) ou lot D (pairing).
  3. Côté energy : ouvrir la session dédiée Phase 4 (contrat docs/UI_data_contract.md).
  4. Décider du push de feature/beta-add-config.

Mémoires persistantes liées : web-lan-cors, energy-chart-redesign, deploy.