From 9eee06782983188fdd50fa20bcb706b27c5fc2d2 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Tue, 2 Jun 2026 08:53:54 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20docs-as-code=20=E2=80=94=20g=C3=A9n?= =?UTF-8?q?=C3=A9rateur,=20literate-nav,=20badges,=20CI=20Gitea=20Actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PORTING_STATUS.yaml : source de vérité canal APT + placement nav - scripts/gen_device_reference.py : génération matrice + fiches + SUMMARY.md depuis integrationplugin*.json + meta.json ; nightly sans JSON = invisible - mkdocs.yml : plugin literate-nav, nav 6 sections, Appareils via SUMMARY.md - .gitea/workflows/docs.yml : CI complet — fetch JSON (branche auto-détectée), génération, build --strict, check idempotence, rsync deploy - Badges HTML (stable/testing/nightly + consumer/community + ok/part/road) - Fiches appareils : Eastron, ABB B2x, ABB Terra, Keba, Waveshare - requirements.txt : mkdocs-material, mkdocs-literate-nav, PyYAML Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/docs.yml | 94 +++++++ .gitignore | 3 + PORTING_STATUS.yaml | 63 +++++ docs/appareils/SUMMARY.md | 15 + docs/appareils/bornes.md | 3 - docs/appareils/bornes/abb-terra.md | 104 +++++++ docs/appareils/bornes/index.md | 8 + docs/appareils/bornes/keba.md | 61 +++++ docs/appareils/compatibilite.md | 66 +++-- docs/appareils/compteurs.md | 3 - docs/appareils/compteurs/abb-b2x.md | 71 +++++ docs/appareils/compteurs/eastron.md | 407 +++++++++++++++++++++++++++ docs/appareils/compteurs/index.md | 8 + docs/appareils/index.md | 13 +- docs/appareils/onduleurs.md | 3 - docs/appareils/smart/index.md | 7 + docs/appareils/smart/waveshare.md | 64 +++++ docs/fonctionnalites/api.md | 8 - docs/fonctionnalites/index.md | 16 +- docs/index.md | 6 +- docs/installation/application.md | 33 +++ docs/stylesheets/extra.css | 22 +- mkdocs.yml | 28 +- requirements.txt | 3 + scripts/gen_device_reference.py | 409 ++++++++++++++++++++++++++++ 25 files changed, 1434 insertions(+), 84 deletions(-) create mode 100644 .gitea/workflows/docs.yml create mode 100644 PORTING_STATUS.yaml create mode 100644 docs/appareils/SUMMARY.md delete mode 100644 docs/appareils/bornes.md create mode 100644 docs/appareils/bornes/abb-terra.md create mode 100644 docs/appareils/bornes/index.md create mode 100644 docs/appareils/bornes/keba.md delete mode 100644 docs/appareils/compteurs.md create mode 100644 docs/appareils/compteurs/abb-b2x.md create mode 100644 docs/appareils/compteurs/eastron.md create mode 100644 docs/appareils/compteurs/index.md delete mode 100644 docs/appareils/onduleurs.md create mode 100644 docs/appareils/smart/index.md create mode 100644 docs/appareils/smart/waveshare.md delete mode 100644 docs/fonctionnalites/api.md create mode 100644 docs/installation/application.md create mode 100644 requirements.txt create mode 100644 scripts/gen_device_reference.py diff --git a/.gitea/workflows/docs.yml b/.gitea/workflows/docs.yml new file mode 100644 index 0000000..cf7fdd1 --- /dev/null +++ b/.gitea/workflows/docs.yml @@ -0,0 +1,94 @@ +name: Build & Deploy docs + +on: + push: + branches: [main] + schedule: + - cron: '0 3 * * *' # mise à jour nocturne (meta.json des repos plugins) + workflow_dispatch: + +jobs: + build-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: pip install -r requirements.txt + + # ── Récupération des JSON depuis les 5 repos drivers ───────────────── + - name: Fetch plugin JSON files + env: + GITEA_TOKEN: ${{ secrets.MKDOCS_TOKEN }} + run: | + GITEA_BASE="https://git.etm-powersync.fr" + AUTH_BASE="https://pakutz79:${GITEA_TOKEN}@git.etm-powersync.fr" + mkdir -p .plugins-src + + for repo in etm-powersync-plugins etm-powersync-plugins-modbus \ + nymea-plugins nymea-plugins-modbus nymea-generic; do + + # Branche par défaut via API Gitea (pas de hardcoding main/master) + BRANCH=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${GITEA_BASE}/api/v1/repos/ETM-Schurig/${repo}" \ + | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('default_branch','main'))" \ + 2>/dev/null) || BRANCH="main" + + echo "→ ${repo} (branche: ${BRANCH})" + git clone --depth 1 --branch "${BRANCH}" \ + "${AUTH_BASE}/ETM-Schurig/${repo}.git" \ + ".plugins-src/${repo}" \ + || echo "WARNING: ${repo} introuvable ou inaccessible — ignoré" + done + + # ── Génération de la doc ────────────────────────────────────────────── + - name: Generate device reference + SUMMARY.md + run: | + python3 scripts/gen_device_reference.py \ + --src .plugins-src \ + --docs docs \ + --lang fr + + # ── Build MkDocs ────────────────────────────────────────────────────── + - name: MkDocs build --strict + run: mkdocs build --strict + + # ── Vérification idempotence ────────────────────────────────────────── + - name: Check generated content is up-to-date + run: | + python3 scripts/gen_device_reference.py \ + --src .plugins-src \ + --docs docs \ + --lang fr \ + --check + + # ── SSH ─────────────────────────────────────────────────────────────── + - name: Setup SSH deploy key + env: + SSH_KEY: ${{ secrets.DOCS_DEPLOY_SSH_KEY }} + DEPLOY_HOST: ${{ secrets.DOCS_DEPLOY_HOST }} + run: | + mkdir -p ~/.ssh + printf '%s\n' "${SSH_KEY}" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keyscan -H "${DEPLOY_HOST}" >> ~/.ssh/known_hosts + + # ── Déploiement ─────────────────────────────────────────────────────── + - name: Deploy via rsync + env: + DEPLOY_USER: ${{ secrets.DOCS_DEPLOY_USER }} + DEPLOY_HOST: ${{ secrets.DOCS_DEPLOY_HOST }} + DEPLOY_PATH: ${{ secrets.DOCS_DEPLOY_PATH }} + run: | + rsync -az --delete \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=yes" \ + site/ \ + "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}" diff --git a/.gitignore b/.gitignore index 21d0b89..9b3a049 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .venv/ +site/ +.plugins-src/ +.claude/ diff --git a/PORTING_STATUS.yaml b/PORTING_STATUS.yaml new file mode 100644 index 0000000..733a655 --- /dev/null +++ b/PORTING_STATUS.yaml @@ -0,0 +1,63 @@ +# Source de vérité : canal APT + placement nav. +# title/tagline/stability/icon → meta.json du repo plugin. +# slug : nom de fichier de la fiche (défaut = plugin). + +- repo: etm-powersync-plugins-modbus + plugin: eastron + name: Eastron SDM + channel: stable + category: compteur + +- repo: etm-powersync-plugins-modbus + plugin: abbb2x + slug: abb-b2x + name: Compteur ABB B2x + channel: testing + category: compteur + +- repo: etm-powersync-plugins-modbus + plugin: waveshare-relay-d8 + slug: waveshare + name: Waveshare relais + channel: testing + category: smartdevice + +- repo: etm-powersync-plugins-modbus + plugin: abbterra + slug: abb-terra + name: Borne ABB Terra AC + channel: testing + category: irve + +- repo: nymea-plugins + plugin: keba + name: Keba + channel: nightly + category: irve + +- repo: nymea-plugins + plugin: fronius + name: Fronius + channel: nightly + category: onduleur + +- repo: nymea-plugins + plugin: daikinairco + name: Daikin + channel: nightly + category: hvac + subcategory: climatisation + +- repo: nymea-plugins + plugin: sgready + name: SG-Ready + channel: nightly + category: hvac + subcategory: pac + +- repo: nymea-plugins + plugin: simpleheatpump + name: SimpleHeatpump + channel: nightly + category: hvac + subcategory: pac diff --git a/docs/appareils/SUMMARY.md b/docs/appareils/SUMMARY.md new file mode 100644 index 0000000..19310ee --- /dev/null +++ b/docs/appareils/SUMMARY.md @@ -0,0 +1,15 @@ +* [Compatibilité](compatibilite.md) +* [Compteurs](compteurs/index.md) + * [Eastron SDM](compteurs/eastron.md) + * [Compteur ABB B2x](compteurs/abb-b2x.md) +* [Bornes de recharge](bornes/index.md) + * [Borne ABB Terra AC](bornes/abb-terra.md) + * [Keba](bornes/keba.md) +* [SmartDevices](smart/index.md) + * [Waveshare relais](smart/waveshare.md) +* [HVAC](hvac/index.md) + * [Daikin](hvac/daikinairco.md) + * [SG-Ready](hvac/sgready.md) + * [SimpleHeatpump](hvac/simpleheatpump.md) +* [Onduleurs / PV](onduleurs/index.md) + * [Fronius](onduleurs/fronius.md) diff --git a/docs/appareils/bornes.md b/docs/appareils/bornes.md deleted file mode 100644 index 07e278d..0000000 --- a/docs/appareils/bornes.md +++ /dev/null @@ -1,3 +0,0 @@ -# Bornes de recharge - -> Stub — détail des bornes supportées, registres Modbus, particularités d'intégration. diff --git a/docs/appareils/bornes/abb-terra.md b/docs/appareils/bornes/abb-terra.md new file mode 100644 index 0000000..cfdb419 --- /dev/null +++ b/docs/appareils/bornes/abb-terra.md @@ -0,0 +1,104 @@ +# Borne ABB Terra AC + +TESTING CONSUMER + +La borne de recharge ABB Terra AC existe en deux variantes de communication : +**Modbus TCP** (réseau Ethernet/LAN) et **Modbus RTU** (bus RS485). + +## 1. Choix de la variante + +- **TCP** : la borne est sur le réseau local, repérée par son adresse IP / + nom d'hôte. Ajout par découverte réseau. +- **RTU** : la borne est sur un bus RS485, repérée par son adresse esclave. + +## 2. Raccordement + +- TCP : câble Ethernet vers le réseau du hub. +- RTU : A↔A, B↔B, masse, terminaison 120 Ω. + +## 3. Ajout dans PowerSync + +Variante TCP : **découverte automatique** sur le réseau, ou ajout **manuel** +(IP, port). Variante RTU : ajout via le maître RTU + adresse esclave. +Voir [Ajouter un appareil](../../installation/application.md). + +## 4. Vérification + +`connected`, `pluggedIn` puis `charging` reflètent l'état de la session ; +`maxChargingCurrent` reflète la consigne de courant. + +--- + +## Référence + + +**Fabricant :** ABB +**Plugin :** `AbbTerra` + +#### Modèles pris en charge +| Modèle | Rôle | Transport | Ajout | Grandeurs | +| --- | --- | --- | --- | --- | +| **Terra AC Charger (TCP)** | Borne de recharge | Modbus TCP | Découverte automatique / Ajout manuel | 17 | +| **Terra AC Charger (RTU)** | Borne de recharge | Modbus RTU | Découverte automatique / Ajout manuel | 17 | + +#### Détail par modèle +??? abstract "Terra AC Charger (TCP) — `terraAcTcp`" + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `macAddress` | MAC address | QString | — | — | oui | + | `address` | Host address | QString | — | — | non | + | `hostName` | Host name | QString | — | — | non | + | `port` | Port | uint | — | `502` | non | + | `slaveId` | Slave ID | uint | 1–255 | `1` | non | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `pluggedIn` | Plugged in | bool | — | + | `charging` | Charging | bool | — | + | `power` | Charging enabled | bool | — | + | `maxChargingCurrent` | Maximum charging current | double | Ampere | + | `phaseCount` | Phase count | uint | — | + | `currentPower` | Active power | double | Watt | + | `currentPhase1` | Current phase 1 | double | Ampere | + | `currentPhase2` | Current phase 2 | double | Ampere | + | `currentPhase3` | Current phase 3 | double | Ampere | + | `voltagePhase1` | Voltage phase 1 | double | Volt | + | `voltagePhase2` | Voltage phase 2 | double | Volt | + | `voltagePhase3` | Voltage phase 3 | double | Volt | + | `sessionEnergy` | Session energy | double | KiloWattHour | + | `firmwareVersion` | Firmware version | QString | — | + | `serialNumber` | Serial number | QString | — | + | `errorCode` | Error code | uint | — | + +??? abstract "Terra AC Charger (RTU) — `terraAcRtu`" + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `rtuMaster` | Modbus RTU master | QString | — | — | non | + | `slaveId` | Modbus slave ID | uint | 1–247 | `1` | non | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `pluggedIn` | Plugged in | bool | — | + | `charging` | Charging | bool | — | + | `power` | Charging enabled | bool | — | + | `maxChargingCurrent` | Maximum charging current | double | Ampere | + | `phaseCount` | Phase count | uint | — | + | `currentPower` | Active power | double | Watt | + | `currentPhase1` | Current phase 1 | double | Ampere | + | `currentPhase2` | Current phase 2 | double | Ampere | + | `currentPhase3` | Current phase 3 | double | Ampere | + | `voltagePhase1` | Voltage phase 1 | double | Volt | + | `voltagePhase2` | Voltage phase 2 | double | Volt | + | `voltagePhase3` | Voltage phase 3 | double | Volt | + | `sessionEnergy` | Session energy | double | KiloWattHour | + | `firmwareVersion` | Firmware version | QString | — | + | `serialNumber` | Serial number | QString | — | + | `errorCode` | Error code | uint | — | + + diff --git a/docs/appareils/bornes/index.md b/docs/appareils/bornes/index.md new file mode 100644 index 0000000..ffaebfd --- /dev/null +++ b/docs/appareils/bornes/index.md @@ -0,0 +1,8 @@ +# Bornes de recharge + +Bornes EVSE intégrées dans ETM PowerSync pour la recharge pilotée des véhicules électriques. + +| Appareil | Protocole | Canal | Stabilité | +|---|---|---|---| +| [Borne ABB Terra AC](abb-terra.md) | Modbus TCP / RTU | TESTING | CONSUMER | +| [Keba](keba.md) | Modbus TCP | NIGHTLY | COMMUNITY | diff --git a/docs/appareils/bornes/keba.md b/docs/appareils/bornes/keba.md new file mode 100644 index 0000000..e41666f --- /dev/null +++ b/docs/appareils/bornes/keba.md @@ -0,0 +1,61 @@ +# Keba + +NIGHTLY COMMUNITY + +Bornes de recharge Keba (séries P30, P31), communication **Modbus TCP** via réseau local. + +## 1. Matériel requis + +- Borne Keba P30 ou P31 +- Connexion Ethernet vers le réseau du hub + +## 2. Activation Modbus TCP + +Modbus TCP est **désactivé par défaut** sur les bornes Keba. +Activez-le dans l'interface web de la borne (port `502`). + +!!! warning "Activation Modbus" + Sans cette étape, la borne ne répondra pas à PowerSync. + +## 3. Ajout dans PowerSync + +Ajout par **découverte automatique** sur le réseau, ou **manuel** (IP, port 502). +Voir [Ajouter un appareil](../../installation/application.md). + +## 4. Vérification + +`connected`, `pluggedIn` puis `charging` reflètent l'état de la session. + +--- + +## Référence + + +**Fabricant :** Keba +**Plugin :** `keba` + +#### Modèles pris en charge +| Modèle | Rôle | Transport | Ajout | Grandeurs | +| --- | --- | --- | --- | --- | +| **Keba P30 / P31** | Borne de recharge | Modbus TCP | Découverte automatique / Ajout manuel | 6 | + +#### Détail par modèle +??? abstract "Keba P30 / P31 — `kebaEVCharger`" + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `macAddress` | MAC address | QString | — | — | oui | + | `address` | Host address | QString | — | — | non | + | `port` | Port | uint | — | `502` | non | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `pluggedIn` | Plugged in | bool | — | + | `charging` | Charging | bool | — | + | `maxChargingCurrent` | Maximum charging current | double | Ampere | + | `currentPower` | Active power | double | Watt | + | `sessionEnergy` | Session energy | double | KiloWattHour | + + diff --git a/docs/appareils/compatibilite.md b/docs/appareils/compatibilite.md index f06f5ac..75eed93 100644 --- a/docs/appareils/compatibilite.md +++ b/docs/appareils/compatibilite.md @@ -1,39 +1,47 @@ # Compatibilité -Liste de référence des équipements. **Source de vérité** : générée à partir du suivi de -portage (`PORTING_STATUS`). Statuts : [Supporté]{.badge .ok} · -[Partiel]{.badge .part} · [Roadmap]{.badge .road}. +Liste de référence des équipements supportés par ETM PowerSync. Générée depuis +`PORTING_STATUS.yaml` — ne pas modifier manuellement entre les marqueurs. -!!! warning "À automatiser" - Cette page sera générée depuis `PORTING_STATUS` pour éviter la double saisie. - Le tableau ci-dessous est un point de départ manuel. - -## Bornes de recharge (EVSE) - -| Marque / Modèle | Protocole | Statut | -|---|---|---| -| Keba | Modbus TCP | [Partiel]{.badge .part} | +| Badge | Signification | +|---|---| +| STABLE | Disponible sur le dépôt APT **stable** — prêt pour la production | +| TESTING | Disponible sur le dépôt APT **testing** — fonctionnel, en cours d'épreuve | +| NIGHTLY | Portage en cours — non disponible en production | + ## Compteurs -| Marque / Modèle | Protocole | Statut | -|---|---|---| -| Eastron SDM | Modbus RTU | [Supporté]{.badge .ok} | -| Waveshare (relais) | Modbus | [Supporté]{.badge .ok} | +| Marque / Modèle | Protocole | Canal | Stabilité | +|---|---|---|---| +| Eastron SDM | Modbus RTU | STABLE | CONSUMER | +| Compteur ABB B2x | Modbus RTU | TESTING | CONSUMER | + +## Bornes de recharge + +| Marque / Modèle | Protocole | Canal | Stabilité | +|---|---|---|---| +| Borne ABB Terra AC | Modbus TCP | TESTING | CONSUMER | +| Keba | Modbus TCP | NIGHTLY | CONSUMER | + +## SmartDevices + +| Marque / Modèle | Protocole | Canal | Stabilité | +|---|---|---|---| +| Waveshare relais | Modbus RTU | TESTING | CONSUMER | + +## HVAC + +| Marque / Modèle | Protocole | Canal | Stabilité | +|---|---|---|---| +| Daikin | — | NIGHTLY | CONSUMER | +| SG-Ready | — | NIGHTLY | CONSUMER | +| SimpleHeatpump | — | NIGHTLY | CONSUMER | ## Onduleurs / PV -| Marque / Modèle | Protocole | Statut | -|---|---|---| -| SMA | Modbus / SunSpec | [Partiel]{.badge .part} | -| Fronius | Modbus / SunSpec | [Roadmap]{.badge .road} | -| Huawei | Modbus | [Roadmap]{.badge .road} | -| SolarEdge | Modbus | [Roadmap]{.badge .road} | +| Marque / Modèle | Protocole | Canal | Stabilité | +|---|---|---|---| +| Fronius | Modbus TCP | NIGHTLY | CONSUMER | -## Batteries / ESS - -| Marque / Modèle | Protocole | Statut | -|---|---|---| -| Victron | Modbus / MQTT | [Roadmap]{.badge .road} | - -> Stub — compléter à partir du suivi de portage réel. + diff --git a/docs/appareils/compteurs.md b/docs/appareils/compteurs.md deleted file mode 100644 index 00d5f75..0000000 --- a/docs/appareils/compteurs.md +++ /dev/null @@ -1,3 +0,0 @@ -# Compteurs - -> Stub — compteurs d'énergie supportés (Eastron SDM, etc.), câblage, configuration Modbus. diff --git a/docs/appareils/compteurs/abb-b2x.md b/docs/appareils/compteurs/abb-b2x.md new file mode 100644 index 0000000..5382378 --- /dev/null +++ b/docs/appareils/compteurs/abb-b2x.md @@ -0,0 +1,71 @@ +# Compteur ABB B2x + +TESTING CONSUMER + +Le compteur d'énergie ABB B2x communique en **Modbus RTU** sur le bus RS485 du +hub. Mesure triphasée (tensions, courants et puissances par phase). + +## 1. Matériel requis + +- Compteur ABB B2x +- Adaptateur USB↔RS485 côté hub +- Câble bus 2 fils (A/B) + masse + +## 2. Raccordement RS485 + +A↔A, B↔B, masse commune, terminaison **120 Ω** en bout de bus. + +## 3. Adressage Modbus + +Adresse esclave unique sur le bus (`1`–`254`, défaut `1`). + +## 4. Ajout dans PowerSync + +Ajout par **découverte automatique** ou **manuel** (saisie de l'adresse esclave). +Voir [Ajouter un appareil](../../installation/application.md). + +--- + +## Référence + + +**Fabricant :** ABB +**Plugin :** `AbbB2x` + +#### Modèles pris en charge +| Modèle | Rôle | Transport | Ajout | Grandeurs | +| --- | --- | --- | --- | --- | +| **ABB B2x energy meter** | Compteur d'énergie | Modbus RTU | Découverte automatique / Ajout manuel | 14 | + +#### Détail par modèle +??? abstract "ABB B2x energy meter — `abbB2x`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | 1–254 | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | 1–254 | `1` | oui | + | `modbusMasterUuid` | Modbus RTU master | QString | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + | `voltagePhaseA` | Voltage phase A | double | Volt | + | `voltagePhaseB` | Voltage phase B | double | Volt | + | `voltagePhaseC` | Voltage phase C | double | Volt | + | `currentPhaseA` | Current phase A | double | Ampere | + | `currentPhaseB` | Current phase B | double | Ampere | + | `currentPhaseC` | Current phase C | double | Ampere | + | `currentPowerPhaseA` | Current power phase A | double | Watt | + | `currentPowerPhaseB` | Current power phase B | double | Watt | + | `currentPowerPhaseC` | Current power phase C | double | Watt | + + diff --git a/docs/appareils/compteurs/eastron.md b/docs/appareils/compteurs/eastron.md new file mode 100644 index 0000000..c74466e --- /dev/null +++ b/docs/appareils/compteurs/eastron.md @@ -0,0 +1,407 @@ +# Compteurs Eastron (SDM) + +STABLE CONSUMER + +Les compteurs Eastron de la série SDM (SDM72, SDM120, SDM220, SDM230, SDM630) +communiquent en **Modbus RTU** sur le bus RS485 du hub. Selon le modèle, ils +mesurent un raccordement monophasé ou triphasé et peuvent être affectés à trois +rôles : compteur général, compteur de consommation ou compteur de production. + +## 1. Matériel requis + +- Le compteur Eastron (modèle selon le besoin de mesure) +- Un adaptateur USB↔RS485 côté hub +- Câble bus 2 fils torsadés (A/B) + masse + +## 2. Raccordement RS485 + +Relier A↔A, B↔B entre l'adaptateur et le compteur, masse commune. Placer une +résistance de terminaison **120 Ω** à chaque extrémité du bus. + +!!! warning "À valider sur votre banc" + Vitesse (baudrate) et parité par défaut du compteur : à confirmer dans le + menu de l'appareil avant mise en service. + +## 3. Adressage Modbus + +Chaque appareil du bus doit avoir une **adresse esclave unique** (réglée via le +menu du compteur). L'adresse par défaut est `1`. Si plusieurs Eastron partagent +le bus, attribuez `1`, `2`, `3`, … + +## 4. Ajout et configuration dans l'application + +L'ajout se fait depuis l'application, en mode installateur — voir +[Ajouter un appareil](../../installation/application.md). Pour l'Eastron : +découverte sur le bus, sélection du modèle, puis choix du **rôle** (compteur +général / consommation / production). + +## 5. Vérification + +Une fois ajouté, l'état `connected` passe à vrai et les grandeurs (`currentPower`, +etc.) se mettent à jour. En cas d'absence de données : vérifier câblage A/B, +adresse esclave et terminaison. + +--- + +## Référence {#reference} + + +**Fabricant :** Eastron +**Plugin :** `eastron` + +#### Modèles pris en charge +| Modèle | Rôle | Transport | Ajout | Grandeurs | +| --- | --- | --- | --- | --- | +| **SDM630 — Energy Meter** | Compteur d'énergie | Modbus RTU | Découverte automatique | 20 | +| **SDM630 — Consumer Meter** | Compteur de consommation | Modbus RTU | Découverte automatique | 4 | +| **SDM630 — Producer Meter** | Compteur de production | Modbus RTU | Découverte automatique | 4 | +| **SDM72 — Energy Meter** | Compteur d'énergie | Modbus RTU | Découverte automatique | 14 | +| **SDM72 — Consumer Meter** | Compteur de consommation | Modbus RTU | Découverte automatique | 4 | +| **SDM72 — Producer Meter** | Compteur de production | Modbus RTU | Découverte automatique | 4 | +| **SDM120 — Energy Meter** | Compteur d'énergie | Modbus RTU | Découverte automatique | 7 | +| **SDM120 — Consumer Meter** | Compteur de consommation | Modbus RTU | Découverte automatique | 4 | +| **SDM120 — Producer Meter** | Compteur de production | Modbus RTU | Découverte automatique | 4 | +| **SDM220 — Energy Meter** | Compteur d'énergie | Modbus RTU | Découverte automatique | 7 | +| **SDM220 — Consumer Meter** | Compteur de consommation | Modbus RTU | Découverte automatique | 4 | +| **SDM220 — Producer Meter** | Compteur de production | Modbus RTU | Découverte automatique | 4 | +| **SDM230 — Energy Meter** | Compteur d'énergie | Modbus RTU | Découverte automatique | 7 | +| **SDM230 — Consumer Meter** | Compteur de consommation | Modbus RTU | Découverte automatique | 4 | +| **SDM230 — Producer Meter** | Compteur de production | Modbus RTU | Découverte automatique | 4 | + +#### Détail par modèle +??? abstract "SDM630 — Energy Meter — `sdm630`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `voltagePhaseA` | Voltage phase A | double | Volt | + | `voltagePhaseB` | Voltage phase B | double | Volt | + | `voltagePhaseC` | Voltage phase C | double | Volt | + | `currentPhaseA` | Current phase A | double | Ampere | + | `currentPhaseB` | Current phase B | double | Ampere | + | `currentPhaseC` | Current phase C | double | Ampere | + | `currentPower` | Current power | double | Watt | + | `currentPowerPhaseA` | Current power phase A | double | Watt | + | `currentPowerPhaseB` | Current power phase B | double | Watt | + | `currentPowerPhaseC` | Current power phase C | double | Watt | + | `frequency` | Frequency | double | Hertz | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `energyConsumedPhaseA` | Energy consumed phase A | double | KiloWattHour | + | `energyConsumedPhaseB` | Energy consumed phase B | double | KiloWattHour | + | `energyConsumedPhaseC` | Energy consumed phase C | double | KiloWattHour | + | `energyProducedPhaseA` | Energy produced phase A | double | KiloWattHour | + | `energyProducedPhaseB` | Energy produced phase B | double | KiloWattHour | + | `energyProducedPhaseC` | Energy produced phase C | double | KiloWattHour | + +??? abstract "SDM630 — Consumer Meter — `sdm630Consumer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM630 — Producer Meter — `sdm630Producer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM72 — Energy Meter — `sdm72`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `voltagePhaseA` | Voltage phase A | double | Volt | + | `voltagePhaseB` | Voltage phase B | double | Volt | + | `voltagePhaseC` | Voltage phase C | double | Volt | + | `currentPhaseA` | Current phase A | double | Ampere | + | `currentPhaseB` | Current phase B | double | Ampere | + | `currentPhaseC` | Current phase C | double | Ampere | + | `currentPower` | Current power | double | Watt | + | `currentPowerPhaseA` | Current power phase A | double | Watt | + | `currentPowerPhaseB` | Current power phase B | double | Watt | + | `currentPowerPhaseC` | Current power phase C | double | Watt | + | `frequency` | Frequency | double | Hertz | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + +??? abstract "SDM72 — Consumer Meter — `sdm72Consumer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM72 — Producer Meter — `sdm72Producer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM120 — Energy Meter — `sdm120`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `voltagePhaseA` | Voltage | double | Volt | + | `currentPhaseA` | Current | double | Ampere | + | `currentPower` | Current power | double | Watt | + | `frequency` | Frequency | double | Hertz | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + +??? abstract "SDM120 — Consumer Meter — `sdm120Consumer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM120 — Producer Meter — `sdm120Producer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM220 — Energy Meter — `sdm220`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `voltagePhaseA` | Voltage | double | Volt | + | `currentPhaseA` | Current | double | Ampere | + | `currentPower` | Current power | double | Watt | + | `frequency` | Frequency | double | Hertz | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + +??? abstract "SDM220 — Consumer Meter — `sdm220Consumer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM220 — Producer Meter — `sdm220Producer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM230 — Energy Meter — `sdm230`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `voltagePhaseA` | Voltage | double | Volt | + | `currentPhaseA` | Current | double | Ampere | + | `currentPower` | Current power | double | Watt | + | `frequency` | Frequency | double | Hertz | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + +??? abstract "SDM230 — Consumer Meter — `sdm230Consumer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyConsumed` | Total energy consumed | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + +??? abstract "SDM230 — Producer Meter — `sdm230Producer`" + _Paramètres de découverte :_ + | Clé | Libellé | Type | Plage | Défaut | + | --- | --- | --- | --- | --- | + | `slaveAddress` | Slave address | int | — | `1` | + + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `slaveAddress` | Modbus slave address | uint | — | `1` | non | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `currentPower` | Current power | double | Watt | + | `totalEnergyProduced` | Total energy produced | double | KiloWattHour | + | `frequency` | Frequency | double | Hertz | + + diff --git a/docs/appareils/compteurs/index.md b/docs/appareils/compteurs/index.md new file mode 100644 index 0000000..26b714a --- /dev/null +++ b/docs/appareils/compteurs/index.md @@ -0,0 +1,8 @@ +# Compteurs + +Compteurs d'énergie supportés par ETM PowerSync via Modbus RTU. + +| Appareil | Protocole | Canal | Stabilité | +|---|---|---|---| +| [Eastron SDM](eastron.md) | Modbus RTU | STABLE | CONSUMER | +| [Compteur ABB B2x](abb-b2x.md) | Modbus RTU | TESTING | CONSUMER | diff --git a/docs/appareils/index.md b/docs/appareils/index.md index 405bb99..4c7154f 100644 --- a/docs/appareils/index.md +++ b/docs/appareils/index.md @@ -1,10 +1,9 @@ # Appareils -ETM PowerSync communique avec les équipements via Modbus (TCP/RTU), MQTT et protocoles -propriétaires, grâce aux plugins du dépôt -[powersync-plugins](https://github.com/etmschurig/powersync-plugins). +ETM PowerSync communique avec les équipements via Modbus (TCP/RTU) et protocoles +propriétaires, grâce aux plugins embarqués. -- [Compatibilité](compatibilite.md) — liste de référence filtrable -- [Bornes de recharge](bornes.md) -- [Compteurs](compteurs.md) -- [Onduleurs / PV](onduleurs.md) +- [Compatibilité](compatibilite.md) — matrice complète avec canaux APT +- [Compteurs](compteurs/index.md) — Eastron SDM, ABB B2x +- [Bornes de recharge](bornes/index.md) — ABB Terra AC, Keba +- [SmartDevices](smart/index.md) — Waveshare diff --git a/docs/appareils/onduleurs.md b/docs/appareils/onduleurs.md deleted file mode 100644 index e8d053b..0000000 --- a/docs/appareils/onduleurs.md +++ /dev/null @@ -1,3 +0,0 @@ -# Onduleurs / PV - -> Stub — onduleurs et données PV (SunSpec/Modbus), lecture de production. diff --git a/docs/appareils/smart/index.md b/docs/appareils/smart/index.md new file mode 100644 index 0000000..f81cdee --- /dev/null +++ b/docs/appareils/smart/index.md @@ -0,0 +1,7 @@ +# SmartDevices + +Modules de pilotage de charges supportés par ETM PowerSync. + +| Appareil | Protocole | Canal | Stabilité | +|---|---|---|---| +| [Waveshare relais](waveshare.md) | Modbus RTU | TESTING | CONSUMER | diff --git a/docs/appareils/smart/waveshare.md b/docs/appareils/smart/waveshare.md new file mode 100644 index 0000000..38f8b83 --- /dev/null +++ b/docs/appareils/smart/waveshare.md @@ -0,0 +1,64 @@ +# Waveshare relais + +TESTING CONSUMER + +Module de relais Waveshare (modèle 8 canaux RS485), pilotage via **Modbus RTU**. + +## 1. Matériel requis + +- Module Waveshare 8-Channel Relay (RS485) +- Adaptateur USB↔RS485 côté hub +- Alimentation 12 V DC + câble bus (A/B) + +## 2. Raccordement RS485 + +A↔A, B↔B, masse commune, terminaison **120 Ω** en bout de bus. +Alimentation 12 V DC sur les bornes VCC/GND du module. + +## 3. Adressage Modbus + +Adresse par défaut : **1**. Configurable via l'utilitaire Waveshare ou par +commande Modbus de changement d'adresse. + +Paramètres : **9600 bps, 8N1**. + +## 4. Ajout dans PowerSync + +Ajout manuel (port série, adresse esclave). +Voir [Ajouter un appareil](../../installation/application.md). + +--- + +## Référence + + +**Fabricant :** Waveshare +**Plugin :** `waveshare-relay-d8` + +#### Modèles pris en charge +| Modèle | Rôle | Transport | Ajout | Grandeurs | +| --- | --- | --- | --- | --- | +| **Waveshare 8-Channel Relay (RS485)** | — | Modbus RTU | Ajout manuel | 9 | + +#### Détail par modèle +??? abstract "Waveshare 8-Channel Relay (RS485) — `waveshareRelayD8`" + _Réglages :_ + | Clé | Libellé | Type | Plage | Défaut | Lecture seule | + | --- | --- | --- | --- | --- | --- | + | `modbusMasterUuid` | Modbus RTU master | QUuid | — | — | oui | + | `slaveAddress` | Slave address | uint | 1–247 | `1` | non | + + _Grandeurs mesurées :_ + | Clé | Grandeur | Type | Unité | + | --- | --- | --- | --- | + | `connected` | Connected | bool | — | + | `relay1` | Relay 1 | bool | — | + | `relay2` | Relay 2 | bool | — | + | `relay3` | Relay 3 | bool | — | + | `relay4` | Relay 4 | bool | — | + | `relay5` | Relay 5 | bool | — | + | `relay6` | Relay 6 | bool | — | + | `relay7` | Relay 7 | bool | — | + | `relay8` | Relay 8 | bool | — | + + diff --git a/docs/fonctionnalites/api.md b/docs/fonctionnalites/api.md deleted file mode 100644 index 1c08c7f..0000000 --- a/docs/fonctionnalites/api.md +++ /dev/null @@ -1,8 +0,0 @@ -# API REST / MQTT [Supporté]{.badge .ok} - -ETM PowerSync expose l'API JSON-RPC de nymea, permettant l'intégration domotique -(Home Assistant, etc.). Vos données restent accessibles, en local. - -Voir [Intégrations](../integrations/rest-api.md) pour les détails. - -> Stub — documenter les endpoints principaux. diff --git a/docs/fonctionnalites/index.md b/docs/fonctionnalites/index.md index 04284c4..d0f515f 100644 --- a/docs/fonctionnalites/index.md +++ b/docs/fonctionnalites/index.md @@ -4,14 +4,14 @@ Vue d'ensemble des fonctions du HEMS et de leur état réel. | Fonction | État | |---|---| -| [Surplus solaire](surplus-solaire.md) | [Partiel]{.badge .part} | -| [Délestage / Load management](delestage.md) | [Partiel]{.badge .part} | -| [Gestion batterie](gestion-batterie.md) | [Partiel]{.badge .part} | -| [API REST / MQTT](api.md) | [Supporté]{.badge .ok} | -| Tarifs dynamiques (FR) | [Roadmap]{.badge .road} | -| Optimisation CO₂ (RTE) | [Roadmap]{.badge .road} | -| Planificateur de charge | [Roadmap]{.badge .road} | -| Pompe à chaleur | [Roadmap]{.badge .road} | +| [Surplus solaire](surplus-solaire.md) | EN COURS | +| [Délestage / Load management](delestage.md) | EN COURS | +| [Gestion batterie](gestion-batterie.md) | EN COURS | +| [API REST](../integrations/rest-api.md) / [MQTT](../integrations/mqtt.md) | DISPONIBLE | +| Tarifs dynamiques (FR) | ROADMAP | +| Optimisation CO₂ (RTE) | ROADMAP | +| Planificateur de charge | ROADMAP | +| Pompe à chaleur | ROADMAP | !!! note Les fonctions en *Roadmap* ne sont pas encore disponibles. Elles sont listées par diff --git a/docs/index.md b/docs/index.md index b3a7550..86061d9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,9 +8,9 @@ Bâti sur [nymea.io](https://nymea.io), selon une architecture **Open Core** : u libre (GPL-3.0) et une couche d'optimisation propriétaire. !!! info "Statuts honnêtes" - Chaque fonction affiche son état réel : [:material-circle:{.ok} **Supporté**]{.badge .ok} - en production · [:material-circle:{.part} **Partiel**]{.badge .part} en industrialisation · - [:material-circle:{.road} **Roadmap**]{.badge .road} planifié. On ne documente pas une promesse. + Chaque fonction affiche son état réel : STABLE + en production · EN COURS en industrialisation · + ROADMAP planifié. On ne documente pas une promesse. ## Par où commencer diff --git a/docs/installation/application.md b/docs/installation/application.md new file mode 100644 index 0000000..c8ed04c --- /dev/null +++ b/docs/installation/application.md @@ -0,0 +1,33 @@ +# L'application PowerSync + +Guide d'utilisation de l'application ETM PowerSync pour ajouter et configurer des appareils. + +## Accéder à l'interface + +L'interface est accessible depuis un navigateur sur le réseau local : + +``` +http:// +``` + +!!! note "Capture" + *Placeholder — capture du menu principal de l'application à ajouter.* + +## Ajouter un appareil + +1. Ouvrir l'application et naviguer vers **Appareils** → **Ajouter un appareil** +2. Sélectionner le type d'appareil dans la liste +3. Renseigner les paramètres de connexion (adresse IP ou port série, adresse Modbus) +4. Valider — l'appareil apparaît dans le tableau de bord si la connexion est établie + +!!! note "Capture" + *Placeholder — capture de l'écran de configuration d'un appareil + (`app-config-thing.png`) à remplacer quand l'app sera prête.* + +## Tableau de bord + +Le tableau de bord affiche en temps réel les puissances, l'état des bornes et le bilan +énergétique du site. + +!!! note "Capture" + *Placeholder — capture du tableau de bord à ajouter.* diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index 95b928c..a8e0c12 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -11,9 +11,21 @@ } [data-md-color-scheme="slate"] .md-typeset a { color: var(--md-primary-fg-color); } -/* badges de statut : {.badge .ok} / {.badge .part} / {.badge .road} */ +/* badges — 3 familles : canal APT · stabilité plugin · maturité fonctionnalité */ .badge{font-family:"IBM Plex Mono",monospace;font-size:.7rem;letter-spacing:.06em; - text-transform:uppercase;padding:2px 8px;border-radius:20px;font-weight:600;white-space:nowrap} -.badge.ok{color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)} -.badge.part{color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)} -.badge.road{color:#8fa9b5;background:rgba(143,169,181,.1);border:1px solid rgba(143,169,181,.3)} + text-transform:uppercase;padding:2px 8px;border-radius:20px;font-weight:600;white-space:nowrap; + display:inline-block;vertical-align:middle;line-height:1.4} + +/* canal APT */ +.badge.stable {color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)} +.badge.testing {color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)} +.badge.nightly {color:#8fa9b5;background:rgba(143,169,181,.1);border:1px solid rgba(143,169,181,.3)} + +/* stabilité plugin (meta.json) */ +.badge.consumer {color:#31a3dd;background:rgba(49,163,221,.1);border:1px solid rgba(49,163,221,.3)} +.badge.community {color:#a78bfa;background:rgba(167,139,250,.1);border:1px solid rgba(167,139,250,.3)} + +/* maturité fonctionnalités (à la main) */ +.badge.ok {color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)} +.badge.part {color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)} +.badge.road {color:#8fa9b5;background:rgba(143,169,181,.1);border:1px solid rgba(143,169,181,.3)} diff --git a/mkdocs.yml b/mkdocs.yml index edea4be..19efee3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,8 +22,7 @@ theme: primary: custom # couleurs réelles définies dans stylesheets/extra.css accent: custom features: - - navigation.sections - - navigation.tabs + - navigation.indexes - navigation.top - navigation.instant - navigation.footer @@ -47,6 +46,7 @@ markdown_extensions: - pymdownx.highlight: anchor_linenums: true - pymdownx.superfences + - pymdownx.details - pymdownx.inlinehilite - pymdownx.tabbed: alternate_style: true @@ -57,6 +57,8 @@ markdown_extensions: plugins: - search: lang: fr + - literate-nav: + nav_file: SUMMARY.md nav: - Accueil: index.md @@ -64,22 +66,18 @@ nav: - installation/index.md - Dépôt APT: installation/depot-apt.md - Configuration: installation/configuration.md + - L'application: installation/application.md + - Appareils: appareils/ - Fonctionnalités: - fonctionnalites/index.md - Surplus solaire: fonctionnalites/surplus-solaire.md - - Délestage / Load management: fonctionnalites/delestage.md + - Délestage: fonctionnalites/delestage.md - Gestion batterie: fonctionnalites/gestion-batterie.md - - API REST / MQTT: fonctionnalites/api.md - - Appareils: - - appareils/index.md - - Compatibilité: appareils/compatibilite.md - - Bornes de recharge: appareils/bornes.md - - Compteurs: appareils/compteurs.md - - Onduleurs / PV: appareils/onduleurs.md - - Intégrations: + - Tarifs dynamiques: tarifs.md + - Référence: + - reference.md - API REST: integrations/rest-api.md - MQTT: integrations/mqtt.md - - Tarifs: tarifs.md - - Référence: reference.md - - Dépannage: depannage.md - - FAQ: faq.md + - Aide: + - Dépannage: depannage.md + - FAQ: faq.md diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0716a54 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +mkdocs-material>=9.5 +mkdocs-literate-nav>=0.6 +PyYAML>=6.0 diff --git a/scripts/gen_device_reference.py b/scripts/gen_device_reference.py new file mode 100644 index 0000000..22c1e78 --- /dev/null +++ b/scripts/gen_device_reference.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +""" +gen_device_reference.py — génère la matrice de compatibilité, les sections de +référence des appareils et le fichier de nav literate-nav (SUMMARY.md) depuis +PORTING_STATUS.yaml + integrationplugin*.json + meta.json. + +Principe docs-as-code : ne remplace que le contenu entre marqueurs existants. + + + ... contenu régénéré ... + + +Marqueur spécial pour la matrice de compatibilité : + + +Usage : + python3 scripts/gen_device_reference.py --src .plugins-src --docs docs --lang fr + python3 scripts/gen_device_reference.py --src .plugins-src --docs docs --lang fr --check +""" +from __future__ import annotations +import argparse +import json +import re +import sys +from pathlib import Path + +try: + import yaml +except ImportError: + sys.exit("PyYAML est requis : pip install PyYAML") + +INTERFACE_ROLE = { + "energymeter": "Compteur d'énergie", + "smartmeterconsumer": "Compteur de consommation", + "smartmeterproducer": "Compteur de production", + "evcharger": "Borne de recharge", + "heatpump": "Pompe à chaleur", + "smartmeter": "Compteur intelligent", +} +TECH_INTERFACES = {"connectable", "networkdevice"} + +CREATE_METHOD = { + "discovery": "Découverte automatique", + "user": "Ajout manuel", + "auto": "Automatique", +} + +CATEGORY_LABELS = { + "compteur": "Compteurs", + "irve": "Bornes de recharge", + "smartdevice": "SmartDevices", + "hvac": "HVAC", + "onduleur": "Onduleurs / PV", + "batterie": "Batteries / ESS", + "tarif": "Tarifs & prévisions", +} + +CATEGORY_FOLDER = { + "compteur": "compteurs", + "irve": "bornes", + "smartdevice": "smart", + "hvac": "hvac", + "onduleur": "onduleurs", + "batterie": "batteries", + "tarif": "tarifs", +} + +CATEGORY_ORDER = ["compteur", "irve", "smartdevice", "hvac", "onduleur", "batterie", "tarif"] + +CHANNEL_BADGES = { + "stable": 'STABLE', + "testing": 'TESTING', + "nightly": 'NIGHTLY', +} + +STABILITY_BADGES = { + "consumer": 'CONSUMER', + "community": 'COMMUNITY', +} + +MARKER_RE = re.compile( + r"()(?P.*?)()", + re.DOTALL, +) + + +# ── Chargement ──────────────────────────────────────────────────────────────── + +def load_porting_status(root: Path) -> list: + path = root / "PORTING_STATUS.yaml" + if not path.exists(): + sys.exit(f"ERREUR : PORTING_STATUS.yaml introuvable à {path}") + with open(path) as f: + return yaml.safe_load(f) + + +def load_plugins(src: Path) -> dict: + """Charge tous les integrationplugin*.json trouvés (récursivement).""" + plugins: dict = {} + for f in sorted(src.rglob("integrationplugin*.json")): + if f.name not in plugins: + plugins[f.name] = json.loads(f.read_text(encoding="utf-8")) + return plugins + + +def load_meta(src: Path, plugin: str) -> dict: + """Charge meta.json depuis le même dossier que integrationplugin.json.""" + for f in sorted(src.rglob(f"integrationplugin{plugin}.json")): + meta_path = f.parent / "meta.json" + if meta_path.exists(): + return json.loads(meta_path.read_text(encoding="utf-8")) + return {} + return {} + + +# ── Libellé d'un appareil ──────────────────────────────────────────────────── + +def entry_name(e: dict, meta: dict) -> str: + """Titre : PORTING_STATUS.name > meta.json.title > plugin (fallback dernier recours).""" + return e.get("name") or meta.get("title") or e.get("plugin", "?") + + +# ── Helpers JSON nymea ──────────────────────────────────────────────────────── + +def transport_of(tc: dict) -> str: + params = {p["name"] for p in tc.get("paramTypes", [])} | \ + {p["name"] for p in tc.get("discoveryParamTypes", [])} + ifaces = set(tc.get("interfaces", [])) + if "networkdevice" in ifaces or {"hostName", "address", "port", "macAddress"} & params: + return "Modbus TCP" + if {"modbusMasterUuid", "rtuMaster"} & params: + return "Modbus RTU" + return "—" + + +def roles(tc: dict) -> str: + r = [INTERFACE_ROLE.get(i, i) for i in tc.get("interfaces", []) if i not in TECH_INTERFACES] + return ", ".join(r) if r else "—" + + +def add_method(tc: dict) -> str: + return " / ".join(CREATE_METHOD.get(m, m) for m in tc.get("createMethods", [])) or "—" + + +def resolve_protocol(plugin_data: dict | None) -> str: + if not plugin_data: + return "—" + for v in plugin_data.get("vendors", []): + for tc in v.get("thingClasses", []): + t = transport_of(tc) + if t != "—": + return t + return "—" + + +# ── Rendu Markdown ──────────────────────────────────────────────────────────── + +def fmt_range(p: dict) -> str: + lo, hi = p.get("minValue"), p.get("maxValue") + if lo is not None and hi is not None: + return f"{lo}–{hi}" + return "—" + + +def fmt_default(p: dict) -> str: + d = p.get("defaultValue", "") + if d == "" or d is None: + return "—" + return f"`{d}`" + + +def md_table(headers: list, rows: list) -> str: + out = ["| " + " | ".join(headers) + " |", + "| " + " | ".join("---" for _ in headers) + " |"] + for r in rows: + out.append("| " + " | ".join(str(c) for c in r) + " |") + return "\n".join(out) + + +def render_params(tc: dict) -> list: + lines = [] + disc = tc.get("discoveryParamTypes", []) + if disc: + rows = [[f"`{p['name']}`", p.get("displayName", ""), p.get("type", ""), + fmt_range(p), fmt_default(p)] for p in disc] + lines.append("_Paramètres de découverte :_") + lines.append(md_table(["Clé", "Libellé", "Type", "Plage", "Défaut"], rows)) + lines.append("") + settings = tc.get("paramTypes", []) + if settings: + rows = [[f"`{p['name']}`", p.get("displayName", ""), p.get("type", ""), + fmt_range(p), fmt_default(p), + "oui" if p.get("readOnly") else "non"] for p in settings] + lines.append("_Réglages :_") + lines.append(md_table(["Clé", "Libellé", "Type", "Plage", "Défaut", "Lecture seule"], rows)) + lines.append("") + return lines + + +def render_states(tc: dict) -> list: + states = tc.get("stateTypes", []) + if not states: + return [] + rows = [[f"`{s['name']}`", s.get("displayName", ""), s.get("type", ""), + s.get("unit") or "—"] for s in states] + return [md_table(["Clé", "Grandeur", "Type", "Unité"], rows), ""] + + +def render_plugin(plugin: dict) -> str: + out = [] + vendors = plugin.get("vendors", []) + vname = ", ".join(v.get("displayName", v.get("name", "")) for v in vendors) + out.append(f"**Fabricant :** {vname} ") + out.append(f"**Plugin :** `{plugin.get('name', '')}`") + out.append("") + + tcs = [(v, tc) for v in vendors for tc in v.get("thingClasses", [])] + rows = [[f"**{tc.get('displayName', tc.get('name'))}**", + roles(tc), transport_of(tc), add_method(tc), + str(len(tc.get("stateTypes", [])))] + for _, tc in tcs] + out.append("#### Modèles pris en charge") + out.append(md_table(["Modèle", "Rôle", "Transport", "Ajout", "Grandeurs"], rows)) + out.append("") + + out.append("#### Détail par modèle") + for _, tc in tcs: + title = tc.get("displayName", tc.get("name")) + out.append(f'??? abstract "{title} — `{tc.get("name")}`"') + body = [] + body += render_params(tc) + if tc.get("stateTypes"): + body.append("_Grandeurs mesurées :_") + body += render_states(tc) + for ln in ("\n".join(body)).splitlines(): + out.append(" " + ln if ln else "") + out.append("") + return "\n".join(out).rstrip() + "\n" + + +def render_matrix(entries: list, plugins: dict, src: Path) -> str: + by_cat: dict = {} + for e in entries: + by_cat.setdefault(e.get("category", "autre"), []).append(e) + + lines = [] + ordered_cats = [c for c in CATEGORY_ORDER if c in by_cat] + ordered_cats += [c for c in by_cat if c not in CATEGORY_ORDER] + + for cat in ordered_cats: + label = CATEGORY_LABELS.get(cat, cat.capitalize()) + cat_rows = [] + for e in by_cat[cat]: + fname = f"integrationplugin{e['plugin']}.json" if e.get("plugin") else None + plugin_data = plugins.get(fname) if fname else None + # nightly sans JSON → absent de la matrice et du nav + if e["channel"] == "nightly" and (not fname or fname not in plugins): + continue + meta = load_meta(src, e["plugin"]) if e.get("plugin") else {} + name = entry_name(e, meta) + protocol = resolve_protocol(plugin_data) + channel_badge = CHANNEL_BADGES.get(e["channel"], e["channel"]) + stability = meta.get("stability", "") + stability_badge = STABILITY_BADGES.get(stability, "—") if stability else "—" + cat_rows.append(f"| {name} | {protocol} | {channel_badge} | {stability_badge} |") + if not cat_rows: + continue + lines.append(f"## {label}\n") + lines.append("| Marque / Modèle | Protocole | Canal | Stabilité |") + lines.append("|---|---|---|---|") + lines.extend(cat_rows) + lines.append("") + + return "\n".join(lines) + + +# ── SUMMARY.md pour mkdocs-literate-nav ────────────────────────────────────── + +def generate_summary(entries: list, docs: Path, src: Path, plugins: dict) -> None: + """Écrit docs/appareils/SUMMARY.md (nav literate-nav).""" + by_cat: dict = {} + for e in entries: + by_cat.setdefault(e.get("category", "autre"), []).append(e) + + lines = ["* [Compatibilité](compatibilite.md)"] + + ordered_cats = [c for c in CATEGORY_ORDER if c in by_cat] + ordered_cats += [c for c in by_cat if c not in CATEGORY_ORDER] + + for cat in ordered_cats: + label = CATEGORY_LABELS.get(cat, cat.capitalize()) + folder = CATEGORY_FOLDER.get(cat, cat) + cat_entries = [] + for e in by_cat[cat]: + if not e.get("plugin"): + continue + fname = f"integrationplugin{e['plugin']}.json" + # nightly sans JSON → pas de fiche, pas d'entrée nav + if e["channel"] == "nightly" and fname not in plugins: + continue + meta = load_meta(src, e["plugin"]) + name = entry_name(e, meta) + slug = e.get("slug") or e["plugin"] + cat_entries.append(f" * [{name}]({folder}/{slug}.md)") + if cat_entries: + lines.append(f"* [{label}]({folder}/index.md)") + lines.extend(cat_entries) + + summary_path = docs / "appareils" / "SUMMARY.md" + summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"SUMMARY.md → {summary_path.relative_to(docs.parent)}") + + +# ── Validation ──────────────────────────────────────────────────────────────── + +def validate_entries(entries: list, plugins: dict, src: Path) -> None: + errors = [] + for e in entries: + if not e.get("plugin"): + continue + fname = f"integrationplugin{e['plugin']}.json" + + if e["channel"] != "nightly": + # JSON obligatoire pour stable / testing + if fname not in plugins: + errors.append( + f" BLOQUANT : {fname} introuvable dans --src " + f"(channel={e['channel']}, plugin={e['plugin']})" + ) + # Libellé obligatoire + meta = load_meta(src, e["plugin"]) + if not e.get("name") and not meta.get("title"): + errors.append( + f" BLOQUANT : libellé manquant pour plugin={e['plugin']} " + f"(ni PORTING_STATUS.name ni meta.json.title)" + ) + + if errors: + sys.exit("ERREURS BLOQUANTES :\n" + "\n".join(errors)) + + +# ── Traitement des fichiers MD ──────────────────────────────────────────────── + +def process(docs: Path, entries: list, plugins: dict, src: Path, check: bool) -> int: + entry_by_plugin = {e["plugin"]: e for e in entries if e.get("plugin")} + changed = [] + + for md in sorted(docs.rglob("*.md")): + text = md.read_text(encoding="utf-8") + if "