# Architecture — écosystème ETM-PowerSync > Document de référence transverse. Source de vérité ; les `README` de dépôt en sont des vues locales. > Statut : v0.1 (structuration initiale). Les contrats d'interface sont en `draft`. --- ## 1. Principe directeur : deux frontières alignées Tout l'écosystème repose sur une seule ligne, qui sépare **deux choses à la fois** : | | Côté plugin | Côté service | |---|---|---| | **Licence** | GPL-3.0-or-later | Propriétaire (ou ouvrable) | | **Exécution** | dans le processus `nymead` | processus séparé | | **Données** | scalaire, événementiel (valeur instantanée) | série temporelle (horizon) | | **Rôle** | capteurs, données, transport, règles simples | intelligence (prévision, MPC, arbitrage) | La frontière de licence et la frontière technique **coïncident**. Un plugin nymea est lié à `libnymea` (GPL) et chargé dans `nymead` : il doit être GPL et ne contient donc **aucune logique propriétaire**. L'intelligence vit dans des services séparés, joints uniquement par API HTTP (« arm's length »), ce qui préserve le propriétaire. Précédent de référence : [EOS](https://github.com/Akkudoktor-EOS/EOS), optimiseur autonome consommé par HTTP. --- ## 2. Vue d'ensemble par couches ``` ┌─ Couche plugins nymea (GPL-3, dans nymead) ───────────────────────────┐ │ etm-powersync-plugins (mono-repo) energy-plugin-etm │ │ ├─ openmeteo (Integration) (Energy) │ │ ├─ linky (Integration) rule-based + load-management│ │ └─ tarif-api (Integration) + OptimizerManager │ │ + plugins matériel existants : Keba, Eastron, Waveshare │ └───────────────────────────────────────────────────────────────────────┘ │ HTTP │ TIC local │ HTTP │ socket/REST ▼ ▼ ▼ ▼ ┌─ Couche services (hors nymea) ────────────────────────────────────────┐ │ Open-Meteo self-hosted tarif-provider optimizer │ │ (meteo-service) (propriétaire/ (PROPRIÉTAIRE) │ │ déploiement) ouvrable) MPC, Perez, │ │ météo + satellite grille TRV + RTE Tempo ombrage, arbitrage │ │ → rank / prix │ └───────────────────────────────────────────────────────────────────────┘ ┌─ Couche infrastructure (existante) ───────────────────────────────────┐ │ Proxmox (LXC/VM) · reverse proxy · dépôt APT (reprepro/GPG, 3 canaux)│ └───────────────────────────────────────────────────────────────────────┘ ``` > Dans les schémas, `energy-plugin-etm` désigne le dépôt `etm-powersync-energy-plugin-etm` (cf. §10). Symétrie à retenir : pour la **météo** comme pour le **tarif**, on a *un backend* (produit la donnée, maintenu une fois) + *un plugin GPL* (l'expose dans nymea). Le plugin ne contient jamais la logique du backend. --- ## 3. Composants ### Plugins nymea (GPL-3) Les plugins d'intégration sont des **modules d'un seul dépôt** `etm-powersync-plugins` (chacun dans son sous-dossier, build et packaging communs). L'energy manager est à part (fork avec amont distinct, type de plugin différent). | Module / dépôt | Type (`IID`) | Rôle | |---|---|---| | `etm-powersync-plugins/openmeteo` | `io.nymea.IntegrationPlugin` | Conditions météo/solaires actuelles (GHI/DNI/DHI, GTI par pan, satellite) | | `etm-powersync-plugins/linky` | `io.nymea.IntegrationPlugin` | Compteur principal (TIC) : option, période HC/HP, `ISOUSC`, couleur Tempo, puissances | | `etm-powersync-plugins/tarif-api` | `io.nymea.IntegrationPlugin` | Client GPL d'une API tarifaire ; expose `currentMarketPrice` + `rank` | | `etm-powersync-energy-plugin-etm` | `io.nymea.EnergyPlugin` | Energy manager : rule-based surplus, load-management, `OptimizerManager` | | *(existants)* Keba, Eastron, Waveshare | `io.nymea.IntegrationPlugin` | Charges et compteurs pilotés | ### Services et déploiements (hors nymea) | Dépôt | Licence | Nature | Rôle | |---|---|---|---| | `etm-powersync-optimizer` | **propriétaire** | code (FastAPI) | MPC, transposition Perez, dérating NOCT, masque d'ombrage, arbitrage | | `etm-powersync-tarif-provider` | propriétaire / **ouvrable** | code | Grille TRV (maj 2×/an) + poll RTE Tempo → prix/`rank` par client | | `etm-powersync-meteo-service` | — | **déploiement + how-to** | Instance Open-Meteo auto-hébergée (docker-compose, config `sync`) — pas du code applicatif | > L'`optimizer` n'est **pas** un plugin : c'est le cœur de l'écosystème, composant à part entière. > `meteo-service` documente/déploie un logiciel amont (Open-Meteo, AGPLv3) ; on ne le développe pas. ### `tarif-api` vs `tarif-provider` — à ne pas confondre - **`tarif-api`** : le **plugin GPL** (client), dans `etm-powersync-plugins`. Façade nymea, bête. - **`tarif-provider`** : le **backend** de référence (serveur), qui produit prix/`rank`. - **`interfaces/tariff.md`** : le contrat qui les relie. Comme `tarif-api` parle un protocole ouvert, un tiers peut substituer son propre provider sans toucher au plugin. --- ## 4. Frontière de licence **Ne doit JAMAIS entrer dans un plugin GPL :** - transposition Perez, dérating thermique (NOCT) ; - modèle de prévision, MPC, scheduling sur horizon ; - masque d'ombrage appris (grille azimut × élévation) ; - stratégie de pondération tarifaire ; - arbitrage économique (revente, coût de stockage). **Peut/doit être dans un plugin GPL :** - lecture des capteurs et de l'équilibre énergétique ; - rule-based surplus (hystérésis, priorités) ; - load-management (cf. §6) ; - `OptimizerManager` = **transport pur** : sérialise l'état → HTTP → applique les consignes. Aucune mathématique d'optimisation. **Communication arm's length** : l'optimizer et le `tarif-provider` sont des processus séparés joints par REST/socket. Ils ne lient pas `libnymea` ni aucun en-tête GPL. Échange de **données**, jamais de code. **Attributions à conserver :** - code amont : copyright nymea GmbH + chargebyte (fork `etm-powersync-energy-plugin-etm`), licence GPL-3 ; - données : Météo-France / EUMETSAT / Open-Meteo (CC-BY), RTE / CRE (Tempo, TRV). *(Ceci décrit la mécanique technique, pas un avis juridique formel.)* --- ## 5. Flux ``` nymea (energy experience) │ ┌──────────────┬─────────────────┼──────────────────┬──────────────┐ openmeteo linky tarif-api energy-plugin-etm (weather) (TIC: période, (rank, prix) ├─ rule-based (Keba, ISOUSC, Tempo) ├─ load-management Eastron, │ │ │ └─ OptimizerManager Waveshare) │ │ │ │ (HTTP, transport) ▼ ▼ ▼ ▼ Open-Meteo (local) tarif-provider ◄─── optimizer (meteo- (RTE Tempo, (récupère lui-même service) grille TRV) météo + tarif en série) ``` L'optimizer récupère **lui-même** ses séries (météo Open-Meteo, prix/`rank` du `tarif-provider`). Les plugins ne lui poussent que l'état temps réel et les contraintes. --- ## 6. Conventions partagées ### Scalaire vs série Plugin = valeur instantanée (état nymea). Service = série temporelle (horizon). Aucune série ne transite par un état nymea. ### Azimut Stockage en **convention géographique** (0=N, 90=E, **180=S**, 270=O), cohérent avec les diagrammes de course du soleil et le masque d'ombrage. Conversion vers Open-Meteo au moment de la requête : ``` azimuth_openmeteo = azimuth_geographique − 180 ``` ### Vocabulaire tarifaire (states nymea, adopté tel quel) nymea n'a **pas** d'interface `pricing` formelle ; le vocabulaire est conventionnel (cf. plugin Awattar) : | État | Unité | Rôle | |---|---|---| | `currentMarketPrice` | `EuroCentPerKiloWattHour` | prix courant | | `rank` | 0–100 (plus bas = meilleur) | **pondération** (classement de l'heure) | | `validUntil` | `UnixTime` | fraîcheur | | `averagePrice` / `lowestPrice` / `highestPrice` | `EuroCentPerKiloWattHour` | stats ±12 h | **Pondération = `rank`.** Le signal relatif de scheduling se mappe sur le `rank` natif de nymea ; rule-based et optimizer le consomment. Tout fournisseur (Awattar, Tibber, `tarif-api`) exposant ce vocabulaire est interchangeable du point de vue de l'energy manager. ### Load-management = contrainte dure, locale, prioritaire La surveillance de la puissance souscrite (`ISOUSC`, protection fusible/disjoncteur) est une fonction de **sécurité** : elle agit en temps réel, sans dépendre du réseau ni de l'optimizer, et a **priorité absolue** sur toute consigne — y compris celles de l'optimizer. C'est une contrainte appliquée *après coup* par le plugin, pas une suggestion envoyée à l'optimizer. ### Dégradation gracieuse Si l'optimizer est injoignable ou renvoie un planning périmé (`valid_until` dépassé), `OptimizerManager` retombe sur le rule-based local. Le plugin fonctionne **toujours** seul. Résilience et hygiène de licence dans le même mécanisme. --- ## 7. Contrats d'interface (draft v0.1, à versionner) Référence détaillée et versionnée dans [`interfaces/`](interfaces/README.md). - [`interfaces/optimize.md`](interfaces/optimize.md) — `POST {optimizerUrl}/optimize` : état temps réel + contraintes → planning. - [`interfaces/tariff.md`](interfaces/tariff.md) — `GET {tariffUrl}/tariff/{client}/now` (scalaire) et `/forecast` (série). - Données Open-Meteo : voir le module `openmeteo` (`etm-powersync-plugins`) et `etm-powersync-meteo-service`. --- ## 8. Configuration à l'installation Un **écran unique** pour l'installateur (paramètres généraux de l'installation), mais **deux destinations** de données selon la frontière : le tarif va au `tarif-provider`/config locale, le matériel d'arbitrage va à l'optimizer. ### Tarif consommation - **Manuel** : tarif unique *ou* HC/HP — saisie des prix et des plages horaires. - **Provider** : choix du fournisseur et de l'option (EDF + Base/HC-HP/Tempo, Awattar, Tibber…). - Sortie unifiée : `rank` + prix absolu (même en tarif fixe, via un `rank` trivial). ### Tarif revente - **Même mécanisme** (manuel ou provider). - Cas dominant : OA surplus → **constante par client**. Le mécanisme identique permet de basculer en série (revente spot) sans changer le modèle. ### Coût de stockage Saisie : prix d'achat batterie, garantie (années), cycles garantis fabricant, DOD. ``` capacité_utile = capacité_nominale × DOD cycles_effectifs = min(cycles_garantis, années × cycles_par_an_estimés) c_batt (€/kWh) = prix_achat / (cycles_effectifs × capacité_utile) ``` - **Attention** : les cycles fabricant sont donnés *à* un DOD ; le DOD qualifie la capacité utile, il ne se multiplie pas une seconde fois. - La garantie en années plafonne la durée de vie quand le cyclage annuel est faible (d'où le `min`). ### Rendement - `η` (aller-retour, défaut ~0,90, ajustable) — paramètre de l'optimizer, par site. | Saisie installateur | Stocké dans | Nature | |---|---|---| | Tarif conso (manuel/provider) | tarif-provider + config locale | tarif | | Tarif revente (manuel/provider) | tarif-provider + config locale | tarif | | Prix, garantie, cycles, DOD | config optimizer (par install) | matériel | | `η` | config optimizer | matériel | --- ## 9. Niveaux d'optimisation (tiers) | Tier | Optimisation | Données nécessaires | Où | |---|---|---|---| | **Community** | Surplus rule-based + load-management | `rank` (+ équilibre énergétique) | plugin GPL | | **Auto / Predict** | MPC, prévision, arbitrage | `rank` + `p_achat` + `p_revente` + `c_batt` + `η` | optimizer propriétaire | **Règle d'arbitrage** (optimizer) — stocker/revendre n'est rentable que si l'écart de prix dépasse le coût de cycle : ``` p_utilisation_évitée − p_achat(t_charge) > c_batt / η (stockage pour autoconso différée) p_revente(t_vente) − p_achat(t_charge) > c_batt / η (revente depuis batterie) ``` Sans le terme `c_batt`, l'optimizer sur-cycle la batterie pour des gains inexistants. La détermination du tier (clé de licence / config) est lue par `OptimizerManager`, qui active ou non la stratégie distante — avec repli rule-based dans tous les cas. --- ## 10. Dépôts | Dépôt | Contenu | Licence | |---|---|---| | `etm-powersync-docs` | ce document, source de vérité transverse | — | | `etm-powersync-plugins` | plugins GPL (modules : `openmeteo`, `linky`, `tarif-api`) | GPL-3 | | `etm-powersync-energy-plugin-etm` | energy manager (fork nymea-energy-plugin-nymea) | GPL-3 | | `etm-powersync-optimizer` | service d'optimisation | propriétaire | | `etm-powersync-tarif-provider` | backend tarif (implémentation de référence) | propriétaire / ouvrable | | `etm-powersync-meteo-service` | déploiement Open-Meteo + how-to | — | Chaque dépôt porte son `README` (vue locale + renvoi ici), un `CLAUDE.md` (workflow multi-agent), et ses fichiers de licence/attribution. --- ## Annexe — points encore ouverts - Schéma exact et versionnement formel des contrats `/optimize` et `/tariff` (passer de `draft` à `v1`). - Modèle fin de `c_batt` (dépendance DOD/vieillissement) — plus tard ; coût plat suffisant pour démarrer. - Cas revente en série (spot) — prévu par le mécanisme, non prioritaire. - Éventuelle lib commune `optimizer` ↔ `tarif-provider` (client Open-Meteo, modèles de séries) — seulement si le code partagé grossit. YAGNI pour l'instant.