5.5 KiB
invraw — Growatt SPF Inverter Raw Decoder
Proxy TCP transparent entre un onduleur Growatt SPF et le cloud Growatt, qui :
- Capture chaque paquet en temps réel
- Déchiffre le protocole V2 (XOR "Growatt")
- Décode les 40+ champs du layout
T05NNNNSPF/T06NNNNSPF - Affiche tout dans une web UI live (hex dump + valeurs lisibles)
- Publie vers Home Assistant via MQTT auto-discovery
Stack : Python 3.12 · asyncio · aiohttp · paho-mqtt · Docker
Architecture
Onduleur Growatt SPF
│ TCP :5279
▼
[ invraw ] ──── décode + UI live ────▶ https://invraw.nas.local
│ (hex dump + champs décodés)
│ TCP :5279
▼
server.growatt.com
│
▼ (optionnel)
[ Mosquitto ]
│ MQTT
▼
Home Assistant
L'onduleur se reconnecte automatiquement — aucune modification de sa config n'est nécessaire si l'IP du NAS est déjà configurée.
Déploiement
Prérequis : Docker, Docker Compose, Traefik sur le réseau proxy.
git clone http://<gitea>/perco/invraw.git && cd invraw
docker compose up -d --build
L'onduleur doit pointer vers l'IP du NAS sur le port 5279 (même port que le cloud Growatt).
Configuration
Toute la configuration se fait via variables d'environnement dans docker-compose.yml :
| Variable | Défaut | Description |
|---|---|---|
GROWATT_HOST |
server.growatt.com |
Adresse du cloud Growatt |
GROWATT_PORT |
5279 |
Port cloud Growatt |
LISTEN_PORT |
5279 |
Port d'écoute du proxy |
WEB_PORT |
8080 |
Port de la web UI (interne) |
MAX_PACKETS |
500 |
Nombre de paquets gardés en mémoire |
MQTT_ENABLED |
true |
Activer la publication MQTT |
MQTT_HOST |
192.168.1.29 |
Broker MQTT (container name ou IP) |
MQTT_PORT |
1883 |
Port MQTT |
HA_DISCOVERY |
true |
Activer le MQTT auto-discovery HA |
DEVICE_NAMES |
(vide) | Mapping serial → nom HA (voir ci-dessous) |
Nommer les appareils dans Home Assistant
Par défaut, l'appareil est créé avec le numéro de série de l'onduleur. Pour lui donner un nom personnalisé :
environment:
- DEVICE_NAMES=JNK1CM70FU:Onduleur_Est,JNK1CM70H5:Onduleur_Sud
Format : SERIAL1:NOM1,SERIAL2:NOM2 — plusieurs onduleurs séparés par des virgules.
Web UI
Accessible via https://invraw.nas.percolouco.com (ou le domaine Traefik configuré).
Fonctionnalités :
- Live feed des paquets (Server-Sent Events)
- Résumé inline : statut onduleur, SOC batterie %, puissance PV totale
- Expand → données décodées par catégorie (Panneaux solaires, Batterie, Réseau, Sortie, Température, Énergie)
- Hex dump complet + hex brut copiable
- Filtre par direction (onduleur→cloud / cloud→onduleur)
- Filtre "données seulement" pour masquer les pings
- Bouton Pause / Vider
Protocole Growatt SPF décodé
Structure d'un paquet
Offset Taille Description
0-3 4 B ID datalogger
4-5 2 B Longueur payload (big-endian)
6 1 B Version protocole (0x05 / 0x06 = chiffré V2)
7 1 B Type d'enregistrement (0x04 = live, 0x50 = tampon)
8+ N B Payload (XOR "Growatt" si protocole 05/06)
Déchiffrement V2
mask = [ord(c) for c in "Growatt"] # [71, 114, 111, 119, 97, 116, 116]
# Les 8 premiers octets (header) ne sont pas chiffrés
for i, j in zip(range(len(data) - 8), cycle(range(7))):
decrypted[i + 8] = data[i + 8] ^ mask[j]
Champs décodés (layout T06NNNNSPF)
| Champ | Offset hex | Longueur | Diviseur | Unité |
|---|---|---|---|---|
| datalogserial | 16 | 10 | — | texte |
| pvserial | 76 | 10 | — | texte |
| pvstatus | 158 | 2 | 1 | — |
| vpv1 / vpv2 | 162 / 166 | 2 | 10 | V |
| ppv1 / ppv2 | 170 / 178 | 4 | 10 | W |
| buck1curr / buck2curr | 186 / 190 | 2 | 10 | A |
| op_watt | 194 | 4 | 10 | W |
| bat_Volt | 226 | 2 | 100 | V |
| batterySoc | 230 | 2 | 1 | % |
| grid_volt | 238 | 2 | 10 | V |
| line_freq | 242 | 2 | 100 | Hz |
| outputvolt | 246 | 2 | 10 | V |
| invtemp | 258 | 2 | 10 | °C |
| loadpercent | 266 | 2 | 10 | % |
| buck1_ntc | 286 | 2 | 10 | °C |
| AC_InWatt | 302 | 4 | 10 | W |
| pvenergytoday | 358 | 4 | 10 | kWh |
| pvenergytotal | 366 | 4 | 10 | kWh |
| ebatDischarToday/Total | 406 / 414 | 4 | 10 | kWh |
| BatWatt | 474 | 4 signé | 10 | W |
| … | … | … | … | … |
Le layout T05NNNNSPF (protocole 0x05) utilise les mêmes champs avec des offsets décalés — voir proxy.py.
Home Assistant
Quand MQTT_ENABLED=true, invraw publie automatiquement :
- MQTT Discovery (premier paquet reçu) — crée les sensors dans HA
- Topic config :
homeassistant/sensor/invraw/{device}_{key}/config
- Topic config :
- State (chaque paquet data) — met à jour les valeurs
- Topic state :
homeassistant/invraw/{device}/state
- Topic state :
Les sensors expirent après 10 minutes sans données (expire_after: 600).
Structure du projet
invraw/
├── proxy.py # Proxy TCP + décodeur + serveur web (asyncio)
├── ha_mqtt.py # Publication MQTT / HA auto-discovery
├── templates/
│ └── index.html # Web UI (SSE live, hex dump, decoded view)
├── Dockerfile
└── docker-compose.yml
Versioning
| Version | Changements |
|---|---|
| v1.1.0 | MQTT HA auto-discovery, mapping serial → nom custom (DEVICE_NAMES) |
| v1.0.0 | Proxy TCP + décodeur SPF complet (43 champs) + web UI live |