 
# Brief agent — plugin V2C Trydan

> Repo : `etm-powersync-plugins-modbus` · Dossier : `v2c/` · Branche : `feature/v2c-trydan`
> Borne : V2C Trydan / Trydan Pro (monophasée·triphasée, ES). Comm locale : **Modbus TCP (port 502)** + **HTTP REST**.
> Statut cible : **Partiel** (comme Keba) — PAS dans la matrice beta validée. Ne pas promettre tant que non testé sur borne réelle.

---

## RÔLE

Créer le plugin nymea `v2c` dans `etm-powersync-plugins-modbus`, sur le modèle des plugins
existants du repo (abbterra pour la structure evcharger Modbus TCP, eastron pour le pattern
registres). ThingClass `trydan` implémentant l'interface **`evcharger`** — c'est elle que le
moteur energy-etm consomme (states `chargingEnabled`, `maxChargingCurrent`, `pluggedIn`,
`charging`, `currentPower` ; actions `setChargingEnabled`, `setMaxChargingCurrent`).

## TRANSPORT — décision d'architecture

**Modbus TCP = transport principal** (port 502, unit id 1). C'est le bus du repo, l'outillage
(`nymea-modbus-cli`, libnymea-modbus) existe, et le polling périodique colle au cycle du moteur.

**HTTP = complément, deux usages** :
1. **Découverte** : la Trydan est en WiFi/LAN, pas de discovery Modbus. `GET /RealTimeData`
   (JSON) sert de probe d'identification pendant le setup (NetworkDeviceDiscovery + test HTTP).
2. **Fallback diagnostic** : si un champ manque en Modbus, le compléter par HTTP est permis,
   mais le chemin de contrôle (pause, intensité) reste Modbus.

Ne PAS implémenter deux ThingClasses (une Modbus, une HTTP) — une seule, transport hybride interne.

## CARTE REGISTRES (source : lib officielle V2Charge/Trydan_Modbus_TCP)

Lecture : holding registers, **2 registres par valeur, décodage float 32 bits big-endian
(byte ET word order Big)**. Écriture : write_register simple (uint16).

### Lecture (0x0BC2…)
| Reg | Nom | Note |
|---|---|---|
| 0x0BC2 | ChargeState | 0=déconnecté, 1=connecté sans charge, 2=en charge (à confirmer sur borne) |
| 0x0BC3 | ChargePower | W |
| 0x0BC4 | ChargeEnergy | kWh session |
| 0x0BC5 | SlaveError | code erreur |
| 0x0BC6 | ChargeTime | s |
| 0x0BC7 | ValuePWM | duty CP |
| 0x0BC8 | HousePower | W — pince CT maison (si installée) |
| 0x0BC9 | PowerFV | W — production PV vue par la borne (si configurée) |
| 0x0BCA | PauseState | |
| 0x0BCB | Lock | |
| 0x0BCC | Program(Promgram) | |
| 0x0BCD | Intensity | A courante |
| 0x0BCE | Dynamic | mode dynamique V2C on/off |
| 0x0BCF | Payment | |
| 0x0BD0 | OCPP | |
| 0x0BD1 | MinIntensity | A |
| 0x0BD2 | MaxIntensity | A |
| 0x0BD3 | PauseDynamic | |
| 0x0BD4 | DynamicPowerMode | |
| 0x0BD5 | ContractedPower | W |

### Écriture (0x177A…)
| Reg | Nom | Usage HEMS |
|---|---|---|
| 0x177A | PauseState | **setChargingEnabled** (pause=1 → désactivé) |
| 0x177B | Lock | verrouillage |
| 0x177C | Program | timer interne — ne pas utiliser (conflit moteur) |
| 0x177D | Intensity | **setMaxChargingCurrent** (A) |
| 0x177E | Dynamic | voir CONFLIT ci-dessous |
| 0x177F | Payment | hors scope |
| 0x1780 | OCPP | hors scope |
| 0x1781 | MinIntensity | borne basse (6 A) |
| 0x1782 | MaxIntensity | borne haute (bug historique V2C corrigé upstream — vérifier firmware) |
| 0x1783 | PauseDynamic | voir CONFLIT |
| 0x1784 | DynamicPowerMode | voir CONFLIT |
| 0x1785 | ContractedPower | protection abonnement interne borne |

Vérifier la carte contre la feuille officielle V2C (lien dans le README du repo
V2Charge/Trydan_Modbus_TCP, gid=0) avant d'écrire les paramRegisters JSON.

## ⚠️ CONFLIT D'OPTIMISEURS — le piège central de cette borne

La Trydan embarque SON PROPRE pilotage solaire (« Dynamic », pince CT + lecture onduleur,
PID interne). Deux cerveaux ne doivent pas piloter la même borne :

- **Dynamic V2C ACTIF (0x0BCE=1)** → la borne s'autopilote au surplus ; toute écriture
  Intensity du moteur sera combattue par le PID interne. Oscillations garanties.
- **Mode HEMS (le nôtre)** → exiger Dynamic=0 ; le moteur energy-etm décide, la borne obéit.

Comportement du plugin :
1. À l'init, LIRE 0x0BCE. Si Dynamic=1, exposer un state `conflictDetected` (ou équivalent)
   et logger un avertissement explicite — ne PAS désactiver silencieusement le mode du client.
2. Setting de Thing `disableInternalOptimizer` (défaut: demander) : si l'utilisateur accepte,
   écrire Dynamic=0 + PauseDynamic/DynamicPowerMode cohérents.
3. `decisionReason` côté moteur doit pouvoir refléter « borne en autopilotage V2C — pilotage
   HEMS suspendu » si Dynamic revient à 1 (l'app V2C du client peut le réactiver à tout moment
   → re-lire à chaque poll, pas seulement à l'init).

C'est le même problème que la PV-Edition Keba : une borne « intelligente » à neutraliser
proprement pour que l'arbitrage central reste l'unique décideur.

## SÉCURITÉ / ROBUSTESSE

- Anti-flapping : respecter les verrous du moteur (chargingEnabledLockDuration etc.) — le
  plugin n'introduit PAS sa propre cadence d'écriture ; il applique ce que le moteur envoie.
- Connexion Modbus perdue → states `connected=false`, pas de retry agressif (backoff).
- Écritures idempotentes : ne réécrire Intensity que si la valeur cible change réellement
  (la borne journalise chaque write ; éviter l'usure flash et le spam).
- Min 6 A (IEC 61851). Clamper toute consigne < 6 A à pause plutôt qu'à une valeur illégale.
- Float decode : 2 registres, Big/Big. NE PAS supposer du uint16 sur les lectures.

## INTERDIT

- Toucher aux plugins publiés du repo (eastron, abbb2x, abbterra, waveshare-relay-d8).
- Promettre la borne dans la doc/matrice beta (statut Partiel, non testé matériel).
- Dépendance à un service privé ETM — plugin 100% GPL, parle à la borne en direct.
- Implémenter OCPP/Payment/RFID — hors scope HEMS.

## DEFINITION OF DONE

1. Plugin compile (amd64 + cross arm64) et s'intègre au .pro du repo.
2. ThingClass `trydan` expose l'interface `evcharger` complète + states diag
   (HousePower, PowerFV, SlaveError, Dynamic/conflit).
3. Discovery réseau + setup par IP manuelle fonctionnels.
4. Gestion du conflit Dynamic implémentée (lecture à chaque poll, state, setting).
5. Testé contre simulateur Modbus (registres mockés) — le test sur borne réelle est
   un jalon séparé, AVANT tout passage en « Supporté ».
6. Entrée PORTING_STATUS_modbus.md : statut Partiel, source de la carte registres notée.

## RÉFÉRENCES

- Lib officielle : github.com/V2Charge/Trydan_Modbus_TCP (carte registres + décodage float)
- API HTTP : feuille V2C (gid=1147522182) + github.com/V2Charge/API-doc-v2c — à vérifier,
  endpoints probables /RealTimeData (lecture JSON) et /write/<param>=<val>
- Dans le repo : abbterra (structure evcharger Modbus TCP), PORTING_STATUS_modbus.md
- Firmware : les updates V2C mentionnent des fixes Modbus (Max/MinIntensity) — documenter
  la version firmware minimale constatée lors du test réel.
