|
| 1 | +# Analyse: Enpal WebSocket API für Version 3.0 |
| 2 | + |
| 3 | +## 🔍 Was @arigon entwickelt hat |
| 4 | + |
| 5 | +Der User @arigon hat das **Blazor SignalR WebSocket-Protokoll** der Enpal Box reverse-engineered und eine Go-Library entwickelt. Statt HTML-Scraping nutzt diese Library die **native API** der Enpal Box. |
| 6 | + |
| 7 | +## 📊 Technischer Vergleich |
| 8 | + |
| 9 | +### Aktueller Ansatz (v2.x - HTML Parsing) |
| 10 | +``` |
| 11 | +Home Assistant → HTTP GET /deviceMessages → HTML parsen → BeautifulSoup → Sensoren |
| 12 | +``` |
| 13 | + |
| 14 | +**Vorteile**: |
| 15 | +- ✅ Einfach zu verstehen |
| 16 | +- ✅ Funktioniert mit allen Enpal Boxen der 1. Generation |
| 17 | +- ✅ Keine komplexe Protokoll-Implementierung |
| 18 | + |
| 19 | +**Nachteile**: |
| 20 | +- ❌ HTML-Struktur kann sich ändern |
| 21 | +- ❌ Kein Echtzeit-Streaming |
| 22 | +- ❌ Polling notwendig (Last auf Enpal Box) |
| 23 | +- ❌ Begrenzt auf das, was im HTML steht |
| 24 | + |
| 25 | +### Neuer Ansatz (v3.0 - WebSocket API) |
| 26 | +``` |
| 27 | +Home Assistant → WebSocket /\_blazor → Blazor SignalR → MessagePack → JSON → Sensoren |
| 28 | +``` |
| 29 | + |
| 30 | +**Vorteile**: |
| 31 | +- ✅ **Native API** - stabiler als HTML-Scraping |
| 32 | +- ✅ **Echtzeit-Updates** via WebSocket Push |
| 33 | +- ✅ **Strukturierte Daten** (JSON statt HTML) |
| 34 | +- ✅ **Geringere Last** - persistente Verbindung statt Polling |
| 35 | +- ✅ **Mehr Daten** - API gibt mehr Details als HTML |
| 36 | +- ✅ **Device Classes** direkt im JSON (Inverter, Battery, Wallbox, etc.) |
| 37 | + |
| 38 | +**Nachteile**: |
| 39 | +- ⚠️ Komplexere Implementierung |
| 40 | +- ⚠️ Abhängig von Blazor-Protokoll |
| 41 | +- ⚠️ Nicht dokumentierte API (kann sich ändern) |
| 42 | + |
| 43 | +## 🔧 Technische Details des WebSocket-Protokolls |
| 44 | + |
| 45 | +### Ablauf der WebSocket-Verbindung |
| 46 | + |
| 47 | +1. **HTTP GET** `/collector` → HTML mit Blazor-Komponenten |
| 48 | +2. **HTTP POST** `/_blazor/negotiate` → Connection Token |
| 49 | +3. **WebSocket** `ws://.../_blazor?id=<token>` → Persistente Verbindung |
| 50 | +4. **Handshake** → `{"protocol":"blazorpack","version":1}` |
| 51 | +5. **StartCircuit** → Blazor-Circuit initialisieren |
| 52 | +6. **UpdateRootComponents** → UI-Komponenten registrieren |
| 53 | +7. **Button Click** → Event senden (EventHandler ID 4) |
| 54 | +8. **Daten empfangen** → JSON via MessagePack |
| 55 | + |
| 56 | +### Message Format |
| 57 | + |
| 58 | +**MessagePack-kodierte Arrays**: |
| 59 | +```go |
| 60 | +[msgType, headers, invocationId, target, args] |
| 61 | +``` |
| 62 | + |
| 63 | +Beispiel Button-Click: |
| 64 | +```go |
| 65 | +[1, {}, nil, "BeginInvokeDotNetFromJS", [ |
| 66 | + "1", nil, "DispatchEventAsync", 1, |
| 67 | + {"eventHandlerId": 4, "eventName": "click", ...} |
| 68 | +]] |
| 69 | +``` |
| 70 | + |
| 71 | +### Datenstruktur (JSON Response) |
| 72 | + |
| 73 | +```json |
| 74 | +{ |
| 75 | + "collectionId": "abc123...", |
| 76 | + "ioTDeviceId": "ENPAL-...", |
| 77 | + "timeStampUtc": "2025-12-26T10:30:00Z", |
| 78 | + "deviceCollections": [ |
| 79 | + { |
| 80 | + "deviceId": "INV-001", |
| 81 | + "deviceClass": "Inverter", |
| 82 | + "numberDataPoints": { |
| 83 | + "Power.DC.Total": { |
| 84 | + "value": 4500.0, |
| 85 | + "unit": "W", |
| 86 | + "timeStampUtcOfMeasurement": "..." |
| 87 | + } |
| 88 | + }, |
| 89 | + "textDataPoints": {...} |
| 90 | + }, |
| 91 | + { |
| 92 | + "deviceClass": "Battery", |
| 93 | + ... |
| 94 | + } |
| 95 | + ], |
| 96 | + "energyManagement": [...], |
| 97 | + "errorCodes": [...] |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +## 🐍 Python-Portierung |
| 102 | + |
| 103 | +### Benötigte Dependencies |
| 104 | + |
| 105 | +```python |
| 106 | +# Bestehend: |
| 107 | +- aiohttp (WebSocket Support) |
| 108 | +- BeautifulSoup4 (für initiales HTML-Parsing der Blazor-Komponenten) |
| 109 | + |
| 110 | +# Neu: |
| 111 | +- msgpack (MessagePack encoding/decoding) |
| 112 | +- re (Regex für Blazor-Komponenten-Extraktion) |
| 113 | +``` |
| 114 | + |
| 115 | +### Architektur-Vorschlag für v3.0 |
| 116 | + |
| 117 | +``` |
| 118 | +custom_components/enpal_webparser/ |
| 119 | +├── __init__.py |
| 120 | +├── config_flow.py |
| 121 | +├── const.py |
| 122 | +├── manifest.json |
| 123 | +├── sensor.py |
| 124 | +├── api/ |
| 125 | +│ ├── __init__.py |
| 126 | +│ ├── base.py # Abstrakte Basis-Klasse |
| 127 | +│ ├── html_parser.py # v2.x HTML-Parsing (Legacy) |
| 128 | +│ ├── websocket_client.py # v3.0 WebSocket-Client (NEU) |
| 129 | +│ └── data_models.py # Gemeinsame Datenmodelle |
| 130 | +├── utils.py |
| 131 | +└── tests/ |
| 132 | +``` |
| 133 | + |
| 134 | +## 🚀 Implementierungsplan für v3.0 |
| 135 | + |
| 136 | +### Phase 1: Dual-Mode Support (Rückwärtskompatibel) |
| 137 | + |
| 138 | +1. **Neue Architektur**: |
| 139 | + - Abstrakte `EnpalApiClient` Basis-Klasse |
| 140 | + - `HtmlParserClient` (bestehende Implementierung) |
| 141 | + - `WebSocketClient` (neue Implementierung) |
| 142 | + |
| 143 | +2. **Config Flow Erweiterung**: |
| 144 | + - Option zur Auswahl: "HTML Parsing" oder "WebSocket API" |
| 145 | + - Auto-Detection: Wenn `/collector` erreichbar → WebSocket verfügbar |
| 146 | + |
| 147 | +3. **Sensor Platform**: |
| 148 | + - Bleibt weitgehend unverändert |
| 149 | + - DataUpdateCoordinator nutzt ausgewählten Client |
| 150 | + |
| 151 | +### Phase 2: WebSocket-Client Implementierung |
| 152 | + |
| 153 | +**Kernfunktionen** (Python-Port von Go-Code): |
| 154 | + |
| 155 | +```python |
| 156 | +class EnpalWebSocketClient: |
| 157 | + async def connect(self) -> bool |
| 158 | + async def fetch_data(self) -> CollectorData |
| 159 | + async def close(self) -> None |
| 160 | + def is_connected(self) -> bool |
| 161 | +``` |
| 162 | + |
| 163 | +**Interne Methoden**: |
| 164 | +- `_extract_blazor_components()` - Regex-basiert aus HTML |
| 165 | +- `_negotiate_connection()` - HTTP POST für Token |
| 166 | +- `_websocket_handshake()` - WebSocket + Blazor Protocol |
| 167 | +- `_send_start_circuit()` - Circuit initialisieren |
| 168 | +- `_send_update_root_components()` - UI registrieren |
| 169 | +- `_click_button()` - Event triggern |
| 170 | +- `_handle_message()` - MessagePack decoding |
| 171 | +- `_parse_collector_data()` - JSON → Python Objects |
| 172 | + |
| 173 | +### Phase 3: Migration & Testing |
| 174 | + |
| 175 | +1. **Unit Tests** mit echten WebSocket-Responses |
| 176 | +2. **Integration Tests** gegen echte Enpal Box |
| 177 | +3. **Migration Guide** für bestehende Nutzer |
| 178 | +4. **Performance-Vergleich** HTML vs. WebSocket |
| 179 | + |
| 180 | +## 💡 Empfehlung |
| 181 | + |
| 182 | +### Kurzfristig (v2.3.x - v2.4.x) |
| 183 | +- ✅ HTML-Parsing weiter verwenden |
| 184 | +- ✅ Heatpump-Support fertigstellen |
| 185 | +- ✅ Stabilität verbessern |
| 186 | + |
| 187 | +### Mittelfristig (v3.0 - Q1/Q2 2026) |
| 188 | +- 🎯 **WebSocket-Client implementieren** |
| 189 | +- 🎯 **Dual-Mode Support** (HTML + WebSocket) |
| 190 | +- 🎯 Migration-Path für User |
| 191 | +- 🎯 Performance-Optimierung |
| 192 | + |
| 193 | +### Vorteile einer v3.0 mit WebSocket |
| 194 | + |
| 195 | +1. **Zukunftssicherheit**: Native API ist stabiler als HTML-Scraping |
| 196 | +2. **Echtzeit-Daten**: Push statt Polling |
| 197 | +3. **Mehr Datenpunkte**: API gibt mehr Informationen |
| 198 | +4. **Bessere Performance**: Weniger Last auf Enpal Box |
| 199 | +5. **Community-Beitrag**: Nutzt @arigons Research |
| 200 | + |
| 201 | +## 🤝 Zusammenarbeit mit @arigon |
| 202 | + |
| 203 | +### Möglichkeiten: |
| 204 | +1. **Code-Sharing**: Go-Code als Referenz für Python-Port |
| 205 | +2. **Protokoll-Dokumentation**: Gemeinsame Dokumentation der API |
| 206 | +3. **Testing**: Austausch über verschiedene Enpal-Hardware |
| 207 | +4. **Issues**: Gemeinsame Bug-Reports bei Protokoll-Änderungen |
| 208 | + |
| 209 | +### Lizenz-Kompatibilität: |
| 210 | +- ✅ @arigons Library: **MIT License** |
| 211 | +- ✅ Unsere Integration: **MIT License** |
| 212 | +- ✅ Portierung nach Python ist erlaubt |
| 213 | + |
| 214 | +## 📝 Nächste Schritte |
| 215 | + |
| 216 | +### Sofort (Diskussion): |
| 217 | +1. Mit @arigon Kontakt aufnehmen (GitHub Issue) |
| 218 | +2. Interesse an Zusammenarbeit mitteilen |
| 219 | +3. Erfahrungen austauschen |
| 220 | + |
| 221 | +### Vorbereitung (v3.0 Planning): |
| 222 | +1. Proof-of-Concept: Python WebSocket-Client |
| 223 | +2. Performance-Benchmarks |
| 224 | +3. Architektur-Design für Dual-Mode |
| 225 | +4. Community-Feedback einholen |
| 226 | + |
| 227 | +### Implementierung (v3.0 Development): |
| 228 | +1. `websocket_client.py` entwickeln |
| 229 | +2. Tests gegen echte Enpal Box |
| 230 | +3. Config Flow erweitern |
| 231 | +4. Documentation updaten |
| 232 | +5. Beta-Testing mit Community |
| 233 | + |
| 234 | +## ⚠️ Risiken & Mitigation |
| 235 | + |
| 236 | +### Risiko 1: Protokoll-Änderungen |
| 237 | +**Mitigation**: Dual-Mode behält HTML als Fallback |
| 238 | + |
| 239 | +### Risiko 2: Komplexität |
| 240 | +**Mitigation**: Schrittweise Implementierung, gute Tests |
| 241 | + |
| 242 | +### Risiko 3: Hardware-Varianten |
| 243 | +**Mitigation**: Community-Testing mit verschiedenen Boxen |
| 244 | + |
| 245 | +### Risiko 4: Enpal-Updates |
| 246 | +**Mitigation**: Monitoring + schnelle Fixes, Fallback auf HTML |
| 247 | + |
| 248 | +## 📊 Erwartete Ergebnisse |
| 249 | + |
| 250 | +### Technisch: |
| 251 | +- 🎯 50% weniger Netzwerk-Traffic |
| 252 | +- 🎯 90% schnellere Daten-Aktualisierung |
| 253 | +- 🎯 Mehr verfügbare Sensoren |
| 254 | + |
| 255 | +### User Experience: |
| 256 | +- 🎯 Echtzeit-Updates statt Polling-Delay |
| 257 | +- 🎯 Stabilere Integration |
| 258 | +- 🎯 Zukunftssichere Lösung |
| 259 | + |
| 260 | +--- |
| 261 | + |
| 262 | +## 🎯 Fazit |
| 263 | + |
| 264 | +Die WebSocket-API ist der **richtige Weg für v3.0**. Sie bietet: |
| 265 | +- ✅ Bessere Performance |
| 266 | +- ✅ Mehr Daten |
| 267 | +- ✅ Stabilere Basis |
| 268 | +- ✅ Echtzeit-Updates |
| 269 | + |
| 270 | +**Empfehlung**: |
| 271 | +1. v2.3.0 mit Heatpump-Support releasen |
| 272 | +2. v3.0 mit WebSocket in Q1/Q2 2026 planen |
| 273 | +3. Dual-Mode für sanfte Migration |
| 274 | +4. Mit @arigon zusammenarbeiten |
| 275 | + |
| 276 | +Die Go-Library von @arigon ist ein **großartiger Beitrag** zur Community und perfekte Basis für unsere v3.0! 🚀 |
0 commit comments