# Plan de développement — Configuration EPEVER

Basé sur l'analyse de `consigne_amelioration_epever_config_md.md` et du code existant.

---

## État actuel du projet (base de travail)

| Module | Fichier | État |
|--------|---------|------|
| Modbus RTU (lecture brute) | `modbus_epever.cpp` | ✅ Fonctionnel à 115 200 bauds |
| FC03 holding read | `lireHoldingBruts()` | ✅ Utilisé pour RTC |
| FC16 holding write | `ecrireHoldingMultiplesBrut()` | ✅ Utilisé pour RTC |
| Webserver REST | `webserver.cpp` | ✅ Pattern AsyncCallbackJsonWebHandler établi |
| Persistance LittleFS | `rules.cpp` → `/rules.json` | ✅ Patron JSON réutilisable |
| Persistance NVS | `relais`, `noms`, `modbus`, `sleep` | ✅ Patron Preferences établi |
| UI web (onglets) | `data/index.html` + `app.js` | ✅ Architecture multi-onglets existante |

**Infrastructure à réutiliser sans modification** : `lireHoldingBruts()`, `ecrireHoldingMultiplesBrut()`, `lectureEnCours`, `AsyncCallbackJsonWebHandler`, pattern LittleFS JSON.

---

## Registres Epever cibles (Holding FC03 / FC16)

Deux blocs consécutifs lisibles en une seule trame chacun :

### Bloc 1 — 0x9000 à 0x900D (14 registres, facteur ÷100 sauf 0x9000/9001)

| Registre | Clé JSON | Unité | Facteur | Min | Max | Écriture |
|----------|----------|-------|---------|-----|-----|---------|
| 0x9000 | `battery_type` | — | ×1 | 0 | 4 | ✅ (0=User,1=Sealed,2=GEL,3=Flooded,4=Li) |
| 0x9001 | `battery_capacity` | Ah | ×1 | 1 | 9999 | ✅ |
| 0x9002 | `temp_compensation` | mV/°C/2V | ×1 | -9 | 0 | ✅ |
| 0x9003 | `overvoltage_disconnect` | V | ÷100 | 13.0 | 16.0 | ✅ |
| 0x9004 | `charging_limit` | V | ÷100 | 13.0 | 15.5 | ✅ |
| 0x9005 | `overvoltage_reconnect` | V | ÷100 | 13.0 | 16.0 | ✅ |
| 0x9006 | `equalize_voltage` | V | ÷100 | 13.0 | 15.5 | ✅ |
| 0x9007 | `boost_voltage` | V | ÷100 | 13.5 | 15.0 | ✅ |
| 0x9008 | `float_voltage` | V | ÷100 | 13.0 | 14.5 | ✅ |
| 0x9009 | `boost_reconnect` | V | ÷100 | 11.0 | 13.5 | ✅ |
| 0x900A | `low_reconnect` | V | ÷100 | 10.0 | 13.5 | ✅ |
| 0x900B | `undervolt_warning` | V | ÷100 | 10.0 | 13.0 | ✅ |
| 0x900C | `low_disconnect` | V | ÷100 | 9.0 | 13.0 | ✅ |
| 0x900D | `discharge_limit` | V | ÷100 | 9.0 | 13.0 | ✅ |

### Bloc 2 — 0x906B à 0x906E (4 registres)

| Registre | Clé JSON | Unité | Facteur | Min | Max | Écriture |
|----------|----------|-------|---------|-----|-----|---------|
| 0x906B | `rated_voltage` | — | ×1 | 0 | 2 | ✅ (0=auto,1=12V,2=24V) |
| 0x906C | `equalize_duration` | min | ×1 | 0 | 180 | ✅ |
| 0x906D | `boost_duration` | min | ×1 | 10 | 180 | ✅ |
| 0x906E | `bat_discharge_soc` | % | ×1 | 20 | 100 | ✅ |

---

## Architecture cible — nouveaux fichiers uniquement

```
src/
  epever_config.cpp     ← NOUVEAU : logique lecture/écriture/persistance config
include/
  epever_config.h       ← NOUVEAU : struct EpeverRegDef, API publique
data/
  epever_config.json    ← NOUVEAU : sauvegarde locale LittleFS (généré à l'usage)
```

**Fichiers modifiés** (ajouts uniquement, pas de refactoring) :

| Fichier | Modification |
|---------|-------------|
| `src/webserver.cpp` | +6 routes API `/api/epever/config*` |
| `src/main.cpp` | +1 appel `initConfigEpever()` dans `setup()` |
| `src/modbus_epever.cpp` | +1 fonction `isModbusBusy()` pour exposer `lectureEnCours` |
| `include/modbus_epever.h` | +1 déclaration `isModbusBusy()` |
| `data/index.html` | +1 onglet "Config EPEVER" |
| `data/app.js` | +section JS onglet config |
| `data/style.css` | +styles mineurs onglet config |

**Fichiers non touchés** : `state.h`, `rules.cpp`, `historique.cpp`, `sleep.cpp`, `wifi_ap.cpp`, `buttons.cpp`, `ota.cpp`.

---

## Étapes de développement

### Étape 1 — Abstraction registres (backend)

Créer `include/epever_config.h` et `src/epever_config.cpp` :

```cpp
struct EpeverRegDef {
    uint16_t    reg;
    const char *key;
    float       scale;
    float       valMin;
    float       valMax;
    bool        writable;
    const char *unit;
    const char *label;
};
```

Table statique des ~18 registres des deux blocs.  
Pas d'effet sur le comportement du système — compilation et link uniquement.

**Test** : `pio run` — vérifier que la compilation passe.

---

### Étape 2 — Lecture config Epever (backend)

Dans `epever_config.cpp` :

```cpp
bool lireConfigEpever();          // lit les deux blocs, populate configCache
void getConfigJson(String &out);  // sérialise + métadonnées (lastRead, rs485_ok, erreur)
```

Utilise `lireHoldingBruts()` existant. Même garde que `reglerHorlogeEpever()` :
```cpp
if (isModbusBusy()) return false;
```

Cache en RAM (`float configCache[18]` + `bool configValide`).

Dans `webserver.cpp` :
```
GET /api/epever/config  →  lireConfigEpever() + getConfigJson()
```

**Test** : appel depuis navigateur, vérifier JSON avec valeurs cohérentes de l'Epever.

---

### Étape 3 — Validation + écriture (backend)

Dans `epever_config.cpp` :

```cpp
bool validerConfig(JsonObject obj, String &erreur);
bool ecrireConfigEpever(JsonObject obj);  // valide puis FC16 par bloc
```

Règles de validation :
- plage min/max par registre (table)
- cohérence inter-registres : `float_voltage < boost_voltage < equalize_voltage < overvoltage_disconnect`
- `low_disconnect < low_reconnect < undervolt_warning`

Écriture en deux passes (bloc 0x9000, puis bloc 0x906B) avec vérification lecture-retour.

Dans `webserver.cpp` :
```
POST /api/epever/config  →  body JSON → validerConfig() → ecrireConfigEpever()
```
Réponse : `{"ok": true}` ou `{"ok": false, "erreur": "Float > Boost"}`.

**Test** : modifier Float Voltage de 0.1V depuis Postman/curl, relire pour confirmer.

---

### Étape 4 — Sauvegarde LittleFS (backend)

Dans `epever_config.cpp` :

```cpp
bool sauvegarderConfigJson();    // écrit /epever_config.json depuis configCache
bool restaurerConfigJson();      // lit /epever_config.json → écriture Epever
bool exporterConfigJson(String &out);
bool importerConfigJson(const String &json);
```

Format fichier identique à l'API (même structure JSON).

Nouvelles routes :
```
GET  /api/epever/config/saved    →  lire LittleFS
POST /api/epever/config/save     →  sauvegarderConfigJson()
POST /api/epever/config/restore  →  restaurerConfigJson() → ecrireConfigEpever()
```

**Test** : sauvegarder, modifier une valeur sur l'Epever, restaurer, relire.

---

### Étape 5 — Interface web (frontend)

Nouvel onglet "Config EPEVER" dans `index.html` + section dans `app.js`.

**Structure UI** :

```
┌─────────────────────────────────────────────────┐
│ [Lire depuis Epever]  [Sauvegarder]  [Restaurer]│
│ Statut : ✅ Synchronisé — 09:14:22              │
├───────────────────────┬─────────────────────────┤
│ 🔋 Batterie           │ ⚡ Tensions de charge    │
│ Type       [Sealed ▼] │ Boost       [14.4 V]    │
│ Capacité   [100 Ah]   │ Float       [13.6 V]    │
│ Tension    [auto ▼]   │ Equalize    [14.6 V]    │
├───────────────────────┼─────────────────────────┤
│ ⏱ Timing             │ 🌡 Température           │
│ Durée boost [120 min] │ Compensation [-3 mV/°C] │
│ Durée égal. [60 min]  │                         │
└───────────────────────┴─────────────────────────┘
[✏️ Écrire vers Epever]  [⬇ Exporter JSON]  [⬆ Importer]
```

Comportements :
- **Lire** : GET `/api/epever/config`, remplit les champs
- **Écrire** : confirmation modale → POST `/api/epever/config`
- **Sauvegarder** : POST `/api/epever/config/save`
- **Restaurer** : confirmation modale → POST `/api/epever/config/restore`
- **Exporter** : GET `/api/epever/config/saved`, `Blob` + `<a download>`
- **Importer** : `<input type="file">` → POST `/api/epever/config`

Aide intégrée (novice) : chaque paramètre a un `title` HTML + icône `ℹ` avec explication en français courte.

Exemples :
> **Tension Float** : tension de maintien après charge complète. Une valeur trop haute fatigue la batterie ; trop basse ne la charge pas suffisamment. Typique : 13.5–13.8V pour plomb.

**Test** : vérifier sur mobile (WiFi AP), formulaire responsive, confirmation avant écriture.

---

### Étape 6 — Gestion des erreurs UI

- Champ en rouge si valeur hors plage (validation JS temps réel)
- Alerte si cohérence inter-registres violée avant même d'écrire
- Toast de succès/erreur après écriture (3s)
- Indicateur `rs485_ok` dans le status bar de l'onglet
- Affichage de la dernière erreur Modbus si `rs485_ok = false`

---

## Contraintes de non-régression

| Risque | Mitigation |
|--------|-----------|
| Conflit accès Serial2 | `isModbusBusy()` — même garde que `reglerHorlogeEpever()` |
| Saturation LittleFS | `/epever_config.json` ≈ 500 octets — négligeable |
| Timeout HTTP long | `lireHoldingBruts()` sur 2 blocs ≈ 200ms — acceptable |
| Écriture valeur dangereuse | Double validation : JS (UX) + C++ (sécurité) |
| Régression onglets existants | Ajouts dans app.js isolés dans une section dédiée |

**Dead code connu à ne pas toucher** : les callbacks `cbPV`, `cbLoad`, `cbSOC`, `cbStatus`, `cbEnergie`, `cbJourNuit` dans `modbus_epever.cpp` sont inactifs mais laissés en place pour éviter le risque de régression.

---

## Ordre d'implémentation recommandé

```
Étape 1 : EpeverRegDef + table registres          (~1h)  → pio run
Étape 2 : lireConfigEpever + GET /api/epever/config (~2h)  → test JSON navigateur
Étape 3 : validation + POST /api/epever/config      (~2h)  → test curl
Étape 4 : LittleFS save/restore                     (~1h)  → test navigateur
Étape 5 : UI onglet complet                         (~3h)  → test mobile
Étape 6 : gestion erreurs UI + aide novice          (~1h)  → test cas d'erreur
```

Total estimé : **~10h** de développement incrémental, chaque étape testable indépendamment.

---

## Fonctionnalités futures (hors scope immédiat)

- Profils batterie prédéfinis : GEL, AGM, Sealed, Lithium (presets JSON)
- Rollback automatique si la tension batterie tombe anormalement après écriture
- Historique des modifications de config (date + delta)
- Synchronisation MQTT / Home Assistant
- Mode expert : affichage de tous les registres bruts
