Patrick Schurig 577535d2b2 Add AGENTS.md
2026-06-13 14:10:51 +02:00

25 KiB
Raw Permalink Blame History

AGENTS.md — plugin Eastron (compteurs SDM)

Repo : etm-powersync-plugins-modbus · Dossier : eastron/ · Branche : main Compteurs d'énergie Eastron SDM (SDM72, SDM120, SDM220, SDM230, SDM630). Comm : Modbus RTU / RS485 uniquement (pas de variante TCP dans ce plugin). Statut cible : Supporté / prod — SDM72 (principal) + SDM120 (PV) en prod sur etm-nymea-dev. Paquet : eastron 1.15.0+etm3 (amd64 + cross arm64, canaux nightly/testing/stable). Plugin id : 2078c2fc-c4cd-46bb-95ab-e9a55ef0a281 · Vendor eastron 33529c0d-67e6-441e-b0bb-6e76e9d4bc1c.

Nature du plugin (à comprendre AVANT toute modif) : c'est un plugin généré par le modbus-tool de nymea, pas un plugin écrit à la main. La carte de registres et le décodage sont déclaratifs (*-registers.json) ; le .cpp/.h est en grande partie du code généré + un peu de glue (discovery, setup, câblage des states). On édite le JSON, pas les classes de connexion générées.

Sources de vérité (lues juin 2026, commit b1da668) :

  • sdm72-registers.json — carte SDM72 (triphasé). lue intégralement.
  • sdm120-registers.json — carte SDM120 (monophasé). lue intégralement.
  • integrationplugineastron.json — ThingClasses, interfaces, params, states. lue (tronquée en toute fin sur sdm230Producer, mais les 5 familles de modèles sont vues).
  • Protocoles constructeur (datasheets Eastron, lus juin 2026) : SDM120-Modbus V2.4, SDM230-Modbus V1.2, SDM72D-M-2 V1.1.1 (2023). croisés avec les *-registers.json — adresses, FC04, endianness confirmées (voir §4).
  • Non encore relus dans ce brief : integrationplugineastron.cpp/.h (discovery + glue), eastron.pro, meta.json, sdm630-registers.json, sdm220-registers.json, sdm230-registers.json (contenu JSON ; les adresses SDM230 sont confirmées par le datasheet). → voir « À vérifier dans le code » en fin de fichier.

1. RÔLE

Exposer des compteurs Eastron SDM lus en Modbus RTU comme Things nymea implémentant les interfaces énergie (energymeter / smartmeterconsumer / smartmeterproducer), consommées par le moteur energy-etm (EnergyArbitrator) pour le bilan de puissance/énergie (point de livraison, production PV).

Read-only. Un compteur ne se pilote pas : aucune action, aucun registre en écriture, aucun paramRegisters d'écriture. Tout est inputRegister access: RO. Toute tentative d'ajouter une écriture est hors périmètre (et faux pour ce matériel).

2. ARCHITECTURE — la séparation à ne jamais oublier

Le point central de ce plugin (équivalent de la « règle d'or transport » du brief V2C, mais inversé) : le plugin ne possède PAS le port série.

nymead
 └─ ModbusRtuMaster  (objet nymea, configuré HORS plugin)
      • /dev/ttyUSB0  ← le port physique appartient au master, pas au plugin
      • 9600 8N1, timeout, retries  ← configurés sur le MASTER
      • un seul master = un seul maître sur le bus RS485
        └─ Thing « SDM72 »   (slaveAddress 1)  ─┐  partagent le même
        └─ Thing « SDM120 »  (slaveAddress 2)  ─┘  master via modbusMasterUuid
  • Chaque ThingClass a deux paramTypes clés :
    • slaveAddress (uint, défaut 1) — l'unit ID Modbus du compteur sur le bus.
    • modbusMasterUuid (QUuid, readOnly) — pointe vers le ModbusRtuMaster nymea qui porte le port série. Renseigné par le mécanisme de setup/discovery, jamais saisi à la main.
  • 🔴 Conséquence directe : le baud (9600), la parité (N), le timeout (500 ms) et les retries (3) ne sont PAS dans ce plugin. Ils vivent dans la config du ModbusRtuMaster (côté nymea-app → Modbus RTU, ou config nymead). Chercher un bug de baud/timeout dans eastron/ est une perte de temps : c'est au niveau du master. (Localisation exacte du réglage à confirmer selon la version de nymea-app, mais ce n'est pas dans ce dossier.)
  • 🔴 Bus mono-maître : tous les SDM partagent le même master = le même /dev/ttyUSB0. nymead est le seul maître. Voir §7 (discipline de bus) — c'est la cause racine n°1 des timeouts intermittents.

3. THINGCLASSES, RÔLES ÉNERGIE & CONVENTION ETM

3.1 Matrice complète du plugin (ce qui existe)

5 modèles × 3 variantes de rôle = 15 ThingClasses (toutes en createMethods: ["discovery"], toutes avec connectable + une interface énergie selon la variante) :

Modèle « — Energy Meter » « — Producer Meter » « — Consumer Meter »
SDM72 (3φ) sdm72 (energymeter) sdm72Producer (smartmeterproducer) sdm72Consumer (smartmeterconsumer)
SDM120 (1φ) sdm120 (energymeter) sdm120Producer (smartmeterproducer) sdm120Consumer (smartmeterconsumer)
SDM220 (1φ) sdm220 (energymeter) sdm220Producer (smartmeterproducer) sdm220Consumer (smartmeterconsumer)
SDM230 (1φ) sdm230 (energymeter) sdm230Producer (smartmeterproducer) sdm230Consumer (smartmeterconsumer)
SDM630 (3φ) sdm630 (energymeter) sdm630Producer (smartmeterproducer) sdm630Consumer (smartmeterconsumer)

States par variante : Energy Meter = riche (tension/courant/puissance ± par phase + totalEnergyConsumed + totalEnergyProduced) ; Producer = currentPower, totalEnergyProduced, frequency ; Consumer = currentPower, totalEnergyConsumed, frequency. Toutes ont connected (bool, cached:false).

3.2 Comment nymea attribue le rôle (source energymanagerimpl.cpp)

🔴 Le rôle énergie d'un compteur = son INTERFACE, lue statiquement par l'EnergyManager. Il n'y a aucun paramètre runtime « rôle » côté plugin. La logique du manager :

  • Root meter (grid) : setRootMeter() refuse un thing qui n'a pas l'interface energymeter. Le compteur réseau doit être un energymeter, et il n'y en a qu'un. Son currentPower signé (+ = consommé / = produit) et ses deux totaux donnent l'échange réseau dans les deux sens → un seul thing couvre import ET export.
  • Producteurs : production = configuredThings().filterByInterface("smartmeterproducer"). Seuls les things smartmeterproducer comptent comme production (onduleur/PV).
  • Consommateurs : filterByInterface("smartmeterconsumer") (charge dédiée, PAC…).
  • ⚠️ Gotcha auto-root : si aucun root meter n'est défini, le manager promeut automatiquement le premier energymeter qui apparaît. Ne pas laisser traîner un energymeter parasite qui raflerait le rôle de root à la place du bon compteur.

🔴 Pourquoi on ne peut PAS fusionner en une seule ThingClass par modèle : un thing portant à la fois energymeter + smartmeterproducer + smartmeterconsumer serait ramassé par les deux boucles filterByInterfacedouble comptage du bilan (compté en prod ET en conso). Le modèle interface-driven impose donc une ThingClass par rôle. C'est une limite de l'energy experience, pas du plugin eastron.

3.3 Le rôle dépend de l'INSTALLATION, pas du modèle (convention ETM)

Le même modèle sert à des rôles différents selon le chantier (ex. onduleur 3 kW → SDM120, onduleur 10 kW → SDM230 ; en triphasé SDM72/SDM630 en compteur réseau, onduleur ou conso PAC). La matrice modèle×rôle est donc irréductible : aucune variante n'est « jamais utilisée », ne pas élaguer (on casserait des cas réels). Couverture ETM des (modèle × rôle) effectivement déployés :

Modèle Root meter (energymeter) Producteur (smartmeterproducer) Consommateur (smartmeterconsumer)
SDM72 (3φ)
SDM630 (3φ) ¹ ¹
SDM230 (1φ)
SDM120 (1φ) —²
SDM220 (1φ) —³ —³ —³

¹ SDM630 en producteur/consommateur = gros onduleur ou PAC triphasés (selon la remarque triphasé : SDM72/SDM630 couvrent les trois rôles en 3φ). ² SDM120 n'est pas déployé comme compteur réseau (trop petit / dédié branche). La ThingClass sdm120 (energymeter) existe et serait techniquement acceptée comme root par nymea, mais hors convention ETM — et attention au gotcha auto-root. ³ SDM220 : présent dans le plugin, hors convention de déploiement ETM listée (à traiter comme les autres 1φ si un cas se présente).

3.4 Convention displayName par fonction (action court terme, ZÉRO risque)

La liste « ajouter un thing » montre les ThingClasses par displayName. Renommer uniquement le displayName (jamais l'id ni le name) ne casse aucun thing appairé en prod — c'est l'action sûre et disponible tout de suite pour rendre la liste lisible (l'installateur choisit par fonction, pas par « Producer/Consumer/Energy Meter »). Convention ETM proposée :

ThingClass (name) displayName proposé
sdm72 / sdm230 / sdm630 Compteur réseau — Eastron SDMxx (Nφ)
sdm72Producer / sdm230Producer / sdm120Producer / sdm630Producer Onduleur / PV — Eastron SDMxx (Nφ)
sdm72Consumer / sdm230Consumer / sdm120Consumer / sdm630Consumer Consommateur (PAC/charge) — Eastron SDMxx (Nφ)

Édition à faire dans integrationplugineastron.json (champ displayName de chaque ThingClass). Regrouper les libellés par rôle aide à scanner les 15 entrées en 3 blocs. Ça ne réduit pas le nombre d'entrées (15 restent 15) — ça les rend intentionnelles.

3.5 Le vrai fix de fond (hors périmètre plugin — feature request nymea)

Réduire à « 1 entrée par modèle, rôle choisi après » suppose de modifier nymea-experience-plugin-energy, pas ce plugin. Demande à porter à l'équipe nymea :

Permettre d'assigner le rôle énergie (root/grid, producteur, consommateur) à un seul thing energymeter bidirectionnel depuis l'energy experience — sur le modèle de setRootMeter() déjà existant, étendu à producteur/consommateur — au lieu d'imposer une ThingClass par interface. Motivation : le rôle dépend de l'installation, pas du modèle ; la matrice modèle×rôle explose la liste d'ajout côté installateur. C'est additif → ne casse rien des setups existants.

Si nymea suit, la matrice eastron se simplifie d'elle-même (une ThingClass energymeter par modèle, rôle réglé en réglages énergie). Pas pour aujourd'hui ni le mois prochain.

3.6 Config prod ETM (etm-nymea-dev, 192.168.1.120)

  • SDM72 = compteur principal / réseau, unit ID 1 → ThingClass sdm72 (energymeter), désigné root meter dans les réglages énergie.
  • SDM120 = compteur PV, unit ID 2 → ThingClass sdm120Producer (smartmeterproducer). (Un sdm120 energymeter non-root ne serait PAS agrégé en production — il faut bien la variante Producer.)
  • ⚠️ Vérifier sur le Thing réel que ces variantes sont bien celles instanciées (convention ETM « pas de valeur inventée »), via nymea-app ou la conf nymead.

4. CARTES DE REGISTRES (confirmées depuis les *-registers.json)

Communes aux deux : protocol: "RTU", endianness: "BigEndian", type: "float" (32 bits, 2 registres), registerType: "inputRegister" (FC04), access: "RO", errorLimitUntilNotReachable: 15. Adresses en base 0 (registre Modbus, pas l'offset 3xxxx). Adresses, FC04 et word order Big-endian (MSR first) confirmés contre les protocoles constructeur (SDM72D-M-2 V1.1.1, SDM120 V2.4, SDM230 V1.2).

🔎 totalEnergyConsumed/totalEnergyProduced = Import/Export, PAS le « Total » du compteur. Le plugin lit Import active energy (reg 72) et Export active energy (reg 74), et non le registre « Total active energy » (reg 342). C'est volontaire et correct : sur SDM120/SDM230 le « total » dépend du réglage interne Measurement mode (mode 2 = import+export par défaut, mode 1 = import seul, mode 3 = importexport) ; sur SDM72 le « total » = import+export. Lire import et export séparément contourne cette ambiguïté de mode. Ne pas « simplifier » en lisant reg 342.

SDM72 — className: "Sdm72" · checkReachableRegister: "totalCurrentPower"

addr id unité groupé en block ?
52 totalCurrentPower (puissance système totale, 30053) W non (registre seul, sonde reachable)
0 / 2 / 4 voltagePhaseA/B/C (30001/03/05) V block phaseVoltageAndCurrent
6 / 8 / 10 currentPhaseA/B/C (30007/09/11) A block phaseVoltageAndCurrent
12 / 14 / 16 powerPhaseA/B/C (30013/15/17) W block phasePower
70 frequency (30071) Hz block frequencyAndTotalEnergy
72 totalEnergyConsumed = Import active energy (30073) kWh block frequencyAndTotalEnergy
74 totalEnergyProduced = Export active energy (30075) kWh block frequencyAndTotalEnergy

SDM120 — className: "Sdm120" · checkReachableRegister: "activePower"

addr id unité groupé en block ?
0 voltage (30001) V non
6 current (30007) A non
12 activePower (30013) W non (sonde reachable)
70 frequency (30071) Hz block frequencyAndTotalEnergy
72 totalEnergyConsumed = Import active energy (30073) kWh block frequencyAndTotalEnergy
74 totalEnergyProduced = Export active energy (30075) kWh block frequencyAndTotalEnergy

SDM230 — className: "Sdm230" (JSON non relu, adresses confirmées par datasheet V1.2)

Layout identique au SDM120 (monophasé) : voltage(0), current(6), activePower(12), frequency(70), import→totalEnergyConsumed(72), export→totalEnergyProduced(74). Le sdm230-registers.json doit donc être la copie du SDM120 avec className: "Sdm230" — à vérifier sur le fichier réel.

⚠️ Block reads = OK ici (contrairement au piège V2C). Les blocks regroupent des registres contigus et non chevauchants (le SDM expose des floats sur des adresses pairement espacées). Le modbus-tool lit chaque block en une transaction groupée — comportement voulu et sûr sur SDM. Ne PAS « casser » les blocks en lectures individuelles pour imiter V2C : c'est inutile et ça multiplie les transactions sur un bus déjà partagé.

📏 Limite de transaction par modèle (datasheets) : SDM72 = 30 paramètres / 60 registres max par requête ; SDM120 et SDM230 = 40 / 80. Les blocks actuels sont très en dessous, mais à respecter si on ajoute des registres à un block (un block trop large lit aussi des registres réservés inutiles).

4bis. PARAMÈTRES SÉRIE DU COMPTEUR (réglés sur le compteur, vus dans les datasheets)

Confirmé par les manuels (utile au commissioning ; rappel : ces valeurs sont côté compteur, le ModbusRtuMaster nymea doit matcher — cf. §2) :

  • Baud supportés : SDM72D-M-2 = 1200/2400/4800/9600/19200 (défaut 9600) ; SDM120 & SDM230 = 1200/2400/4800/9600 (défaut 2400, pas de 19200).
  • 🔴 Cap bus = 9600 : un SDM120/SDM230 ne peut pas dépasser 9600. Le bus partagé est donc plafonné à 9600 tant qu'un de ces modèles y est. Inutile d'espérer accélérer le bus au-delà.
  • 🔧 Commissioning : un SDM120/SDM230 sorti d'usine est à 2400 → le passer à 9600 (et régler son adresse Modbus unique) via le menu façade avant de le brancher au bus prod.
  • Parité/stop : défaut None / 1 stop. Le bus prod est en 9600 8N1 → cohérent.
  • Word order : MSR first (Big-endian) par défaut sur les trois ; SDM72 l'auto-détecte et ne l'expose pas au menu. Le plugin suppose BigEndian → ne pas changer ce réglage sur le compteur.

5. SÉMANTIQUE DE REACHABILITY (lien direct avec les timeouts)

errorLimitUntilNotReachable: 15 + checkReachableRegister définissent comment le plugin gère les erreurs Modbus :

  • Le générateur lit périodiquement le checkReachableRegister (totalCurrentPower pour SDM72, activePower pour SDM120) pour juger si le device répond.
  • Il faut 15 erreurs consécutives (timeouts/exceptions) avant que le Thing bascule connected = false. En dessous, les valeurs continuent d'être servies (d'où « parfois ça répond, parfois rafales » sans que le Thing tombe forcément).
  • 🔎 Implication debug : des rafales < 15 sont absorbées silencieusement côté state connected, mais polluent les logs (SdmXXModbusRtuConnection: ... TimeoutError) et trouent la mesure. Le seuil masque le symptôme, il ne le corrige pas. La cause se traite au niveau bus (§7), pas en montant ce seuil.

6. DISCOVERY

  • createMethods: ["discovery"], discoveryParamTypes: [ slaveAddress (int, défaut 1) ].
  • La discovery sonde le bus (via le ModbusRtuMaster courant) à l'adresse demandée et tente de lire le checkReachableRegister ; si ça répond, le device est proposé.
  • ⚠️ La discovery émet du trafic sur le bus partagé : lancer une discovery pendant que les SDM prod tournent ajoute des transactions. Pas bloquant, mais à savoir pour le diagnostic.
  • (Détail d'implémentation discovery — choix du master, gestion multi-master, dédoublonnage — à lire dans integrationplugineastron.cpp, non couvert ici.)

7. DISCIPLINE DE BUS RS485 — la cause racine n°1 des timeouts (OPÉRATIONNEL)

Cette section encode une leçon de prod (incident timeouts SDM apparu pendant une session de debug v2c/Trydan avec mbpoll sur /dev/ttyUSB0). À lire avant tout diagnostic de timeout.

  1. Un seul maître. Le RS485 est mono-maître ; nymead l'est. Ne jamais lancer mbpoll / modpoll sur /dev/ttyUSB0 pendant que nymead tourne → deux maîtres → collisions de trames → timeouts intermittents par rafales. Pour sonder au mbpoll, arrêter nymead d'abord (sudo systemctl stop nymead), puis le relancer.
  2. Pas de process série résiduel. Après une session de debug, vérifier qu'aucun mbpoll zombie ne tient le port : pgrep -a mbpoll ; sudo lsof /dev/ttyUSB0 (doit ne montrer que nymead).
  3. Épingler le port par identité, pas par ttyUSBn : le ModbusRtuMaster doit pointer un /dev/serial/by-id/usb-Silicon_Labs_CP2102...-if00-port0 (cp210x), sinon renommage possible au reboot (ttyUSB0 ↔ ttyUSB1) si un 2e adaptateur est branché.
  4. Stabilité cp210x : surveiller dmesg | grep -i cp210x (disconnect/reset) — un adaptateur qui décroche donne aussi des rafales, indépendamment du bus.
  5. Charge VM : sous forte charge CPU, nymead peut ne pas tenir la cadence du master → pseudo- timeouts. Contributeur, rarement cause unique. Mécanisme (confirmé par le protocole Eastron, §3.3) : une trame RTU doit être un flux continu ; un silence > 1,5 temps-caractère (≈ 1,6 ms à 9600 8N1) en milieu de trame fait jeter la trame par le SDM → pas de réponse → timeout côté master. Le latency timer du cp210x (défaut 16 ms) combiné au jitter d'ordonnancement de la VM peut fragmenter une trame au-delà de ce seuil. Pistes : baisser le latency timer du cp210x (/sys/bus/usb-serial/devices/ttyUSB0/latency_timer), réduire la charge, ou prioriser nymead. À traiter seulement si le bus est propre par ailleurs (14 ci-dessus).
  6. Réglages bus (au niveau master, pas plugin) : A/B non inversés, GND commun adaptateur↔SDM, borniers serrés, terminaison 120 Ω peu critique sur bus court 2 nœuds à 9600. Topologie en daisy-chain, pas d'étoile ni de stub (réflexions → corruption ; protocole §2.2).

8. CLASSES GÉNÉRÉES & LECTURE DES LOGS

Le modbus-tool génère, à partir du className, une classe de connexion <ClassName>ModbusRtuConnection :

  • Sdm72Sdm72ModbusRtuConnection
  • Sdm120Sdm120ModbusRtuConnection

D'où les lignes de log Sdm72ModbusRtuConnection: ModbusRtu reply error ... TimeoutError. Un log préfixé par une de ces classes = couche transport générée, pas la glue du plugin. Utile pour router un bug : préfixe SdmXXModbusRtuConnection → registre/transport/bus ; messages du plugin eastron lui-même → setup/discovery/câblage des states.

9. POUR ÉTENDRE (ajouter un modèle ou un registre)

Workflow déclaratif, dans l'ordre :

  1. Ajouter/éditer <modele>-registers.json (className unique, registres inputRegister float Big-endian, choisir un checkReachableRegister qui répond toujours, garder les blocks contigus). Réutiliser les conventions SDM72/SDM120 ci-dessus.
  2. Ajouter la/les ThingClass(es) dans integrationplugineastron.json (3 variantes de rôle si pertinent), avec les paramTypes slaveAddress + modbusMasterUuid (readOnly) et le state connected (cached: false).
  3. Générer de nouveaux UUID pour chaque nouvel id (plugin/thingclass/param/state). Ne jamais réutiliser un UUID existant.
  4. Régénérer via le modbus-tool nymea (cible du .pro), recompiler amd64 puis cross arm64.
  5. Tester en mono-maître (nymead arrêté, mbpoll -1 ...) que le device répond, puis via nymead.
  6. Mettre à jour PORTING_STATUS* / doc catalogue (badge canal/origine/stabilité) si applicable.

10. INTERDIT

  • Éditer les classes de connexion générées (SdmXXModbusRtuConnection) à la main — elles sont régénérées, toute modif sera écrasée. Corriger dans les *-registers.json.
  • Mettre la config série (baud/parité/timeout/retries) dans le plugin — elle appartient au ModbusRtuMaster nymea (§2).
  • Ajouter de l'écriture (action/registre access: WO/RW) — un SDM est read-only.
  • Changer/réutiliser les UUID de ThingClasses ou de plugin publiés (casse l'appairage des Things existants en prod).
  • Casser les blocks en lectures individuelles pour copier le pattern V2C — non pertinent ici (registres contigus, pas de chevauchement firmware).
  • Lancer mbpoll sur le bus pendant que nymead tourne (§7).
  • Monter errorLimitUntilNotReachable pour « régler » des timeouts — masque, ne corrige pas.
  • Toucher aux autres plugins du repo (abbterra, abbb2x, waveshare-relay-d8, …).

11. BUILD / DEPLOY

  • Paquet eastron versionné 1.15.0+etm3. Cross-compilation arm64 via le container build-cross-arm64 (Debian trixie amd64 + crossbuild-essential-arm64).
  • Publié sur les canaux APT powersync-nightly / powersync-testing / powersync-stable (reprepro, GPG 77033A6E…9D1B4986). Recette complète dans DEPLOY.md du projet.
  • Déployé sur le RPi hems (192.168.1.75, canal powersync-testing) et en prod sur etm-nymea-dev (192.168.1.120).

12. DEFINITION OF DONE (pour une modif de ce plugin)

  1. Le plugin compile (amd64 + cross arm64) et reste cohérent avec le .pro.
  2. Toute nouvelle valeur passe par un *-registers.json (déclaratif), pas par du code à la main.
  3. Reachability conservée : checkReachableRegister valide, errorLimitUntilNotReachable inchangé sans justification.
  4. UUID neufs pour tout nouvel élément ; UUID existants intacts.
  5. Testé d'abord en mono-maître (nymead arrêté) au mbpoll -1, puis intégré via nymead, sur le bus réel.
  6. États visibles et cohérents dans nymea-app (valeurs qui évoluent, connected=true stable).
  7. PORTING_STATUS* / doc catalogue à jour si le périmètre modèle change.

13. À VÉRIFIER DANS LE CODE (non lu dans ce brief)

Honnêteté sur le périmètre lu : ce brief s'appuie sur les 2 cartes de registres (SDM72/SDM120) + le JSON du plugin + les 3 protocoles constructeur (SDM72/SDM120/SDM230). Restent à confirmer en lisant le dépôt :

  • integrationplugineastron.cpp/.h : logique de discovery, sélection du ModbusRtuMaster, câblage des states, gestion de la reconnexion.
  • eastron.pro : invocation exacte du modbus-tool, liste des *-registers.json compilés.
  • meta.json : packaging (nom, deps, version).
  • sdm230-registers.json : adresses confirmées par le datasheet V1.2 (= SDM120) ; reste à vérifier que le fichier JSON existe bien et porte className: "Sdm230".
  • sdm220-registers.json, sdm630-registers.json : non vérifiés — ni le JSON ni le datasheet fournis (SDM630 = 3φ riche type SDM72, SDM220 = 1φ type SDM120/230). Vérifier adresses, checkReachableRegister et blocks avant de s'y fier.
  • Variante réellement instanciée pour le SDM72 (principal) et le SDM120 (PV) sur etm-nymea-dev (cf. §3) — à lire sur le Thing réel, pas à supposer.

14. RÉFÉRENCES

  • Source plugin : git.etm-powersync.fr/ETM-Schurig/etm-powersync-plugins-modbus, dossier eastron/ (commit b1da668).
  • Cartes registres : eastron/sdm72-registers.json, eastron/sdm120-registers.json (+ 630/220/230).
  • ThingClasses/interfaces/states : eastron/integrationplugineastron.json.
  • Modèle de structure dans le repo : abbterra (evcharger Modbus TCP), eastron lui-même (pattern registres RTU déclaratif) — cités comme références dans le brief V2C.
  • Datasheets/protocoles constructeur : Eastron SDM72D-M-2 User Manual V1.1.1 (2023, meter code 00 89), SDM120-Modbus Protocol V2.4, SDM230-Modbus User Manual + Protocol V1.2. Registres input float IEEE754 Big-endian (MSR first), FC04 ; limites de transaction 30 (SDM72) / 40 (SDM120/230) paramètres ; baud max 9600 (SDM120/230) vs 19200 (SDM72).