odoo_mqtt/IMPLEMENTATION_PLAN.md

806 lines
32 KiB
Markdown

# Implementation Plan: IoT Bridge & Odoo Integration
**Ziel:** Sidecar-Container-Architektur mit periodischer Session-Aggregation
**Stand:** 12.02.2026 (aktualisiert nach Autonomie-Fix)
**Strategie:** Bridge zuerst (standalone testbar), dann Odoo API, dann Integration
---
## 📦 Bestandsaufnahme
### Vorhanden (wiederverwendbar)
-`python_prototype/` - Standalone MQTT Client & Session Detector (Basis für Bridge)
-`services/mqtt_client.py` - Odoo-integrierter MQTT Client (Referenz)
-`services/session_detector.py` - State Machine Logik (portierbar)
-`services/parsers/shelly_parser.py` - Shelly PM Parser (direkt übernehmen)
-`models/` - Odoo Models (anzupassen)
- ✅ Mosquitto MQTT Broker (läuft)
### Neu zu erstellen (Architektur-Änderung)
-`iot_bridge/` Container-Source (Skeleton existiert)
- ✅ Odoo REST API Controller für Event-Empfang (bereits vorhanden)
-**Bridge HTTP Server** für Config-Empfang (`POST /config`)
-**Odoo Config-Push-Logik** (Model-Hooks für device create/write)
-**Dynamic Subscription Management** in Bridge (Topics zur Laufzeit ändern)
- ✅ Session-Aggregation in Bridge
- ✅ Odoo Session-Model mit Billing-Logik
- ✅ Docker Compose Integration
---
## 🎯 Phase 1: Bridge Container (Standalone)
**Ziel:** Bridge läuft unabhängig, loggt auf Console, nutzt YAML-Config
### 1.1 Bridge Grundstruktur ✅
- [x] `iot_bridge/config.yaml` erstellen (Device-Registry, MQTT Settings)
- [x] `iot_bridge/config.py` - Config Loader (YAML → Dataclass)
- [x] Mock-OdooClient (gibt hardcoded Config zurück, kein HTTP)
- [x] Logger Setup (structlog mit JSON output)
**Test:** ✅ Bridge startet, lädt Config, loggt Status (JSON), Graceful Shutdown funktioniert
---
---
### 1.2 MQTT Client portieren ✅
- [x] `python_prototype/mqtt_client.py``iot_bridge/mqtt_client.py`
- [x] Shelly Parser integrieren (copy from `services/parsers/`)
- [x] Connection Handling (reconnect, error handling)
- [x] Message-Callback registrieren
- [x] TLS/SSL Support (Port 8883)
**Test:** ✅ Bridge empfängt Shelly-Messages (40-55W), parst apower, loggt auf Console (JSON), TLS funktioniert
**Reconnect Test:** ✅ Broker neugestartet → Bridge disconnected (rc=7) → Auto-Reconnect → Re-Subscribe → Messages wieder empfangen
---
### 1.3 Session Detector mit Aggregation ✅
- [x] `session_detector.py` portieren (5-State Machine)
- [x] Aggregation-Logik hinzufügen:
- Interner State-Tracker (1s Updates)
- Timer für `heartbeat_interval_s`
- Berechnung: `interval_working_s`, `interval_standby_s`
- [x] Session-IDs generieren (UUID)
- [x] Events sammeln (event_callback)
- [x] Unit Tests (9 Tests, alle PASSED)
- [x] Shelly Simulator für Testing
- [x] Integration Tests (7 Tests, alle PASSED)
- [x] Lokaler Mosquitto Container (docker-compose.dev.yaml)
**Test:** ✅ Session gestartet (34.5W > 20W threshold), State-Machine funktioniert, Heartbeat-Events nach 10s
**Unit Tests:** ✅ 9/9 passed (test_session_detector.py)
**Integration Tests:** ✅ 7/7 passed (test_bridge_integration.py) - Session Start, Heartbeat, Multi-Device, Timeout
---
### 1.4 Event Queue & Retry Logic ✅
- [x] `event_queue.py`: Event-Queue mit Retry-Logic
- Exponential Backoff (1s → 2s → 4s → ... → max 60s)
- Max 10 Retries
- Background-Thread für Queue-Processing
- Thread-safe mit `collections.deque` und `threading.Lock`
- [x] `event_uid` zu allen Events hinzufügen (UUID)
- [x] Queue-Integration in `main.py`
- `on_event_generated()` nutzt Queue statt direktem send
- Queue-Start/Stop im Lifecycle
- [x] Mock-Odoo Failure-Simulation
- `mock_failure_rate` in config.yaml (0.0-1.0)
- MockOdooClient wirft Exceptions bei failures
- [x] Unit Tests: `test_event_queue.py` (13 Tests, alle PASSED)
- Queue Operations (enqueue, statistics)
- Exponential Backoff Berechnung
- Retry Logic mit Mock-Callback
- Max Retries exceeded
- [x] Integration Tests: `test_retry_logic.py` (2 Tests, PASSED in 48.29s)
- test_retry_on_odoo_failure: Events werden enqueued
- test_eventual_success_after_retries: 50% failure rate → eventual success
**Test:** ✅ Mock-Odoo-Client gibt 500 → Events in Queue → Retry mit Backoff → Success
**Unit Tests:** ✅ 13/13 passed
**Integration Tests:** ✅ 2/2 passed in 48.29s
---
### 1.5 Docker Container Build
- [x] Multi-stage `Dockerfile` finalisiert
- Builder Stage: gcc + pip install dependencies
- Runtime Stage: minimal image, non-root user (bridge:1000)
- Python packages korrekt nach /usr/local/lib/python3.11/site-packages kopiert
- [x] `.dockerignore` erstellt (venv/, tests/, __pycache__, logs/, data/)
- [x] `requirements.txt` erweitert mit PyYAML>=6.0
- [x] `docker build -t iot_mqtt_bridge:latest`
- 3 Build-Iterationen (Package-Path-Fix erforderlich)
- Finale Image: 8b690d20b5f7
- [x] `docker run` erfolgreich getestet
- Volume mount: config.yaml (read-only)
- Network: host (für MQTT-Zugriff)
- Container verbindet sich mit mqtt.majufilo.eu:8883
- Sessions werden erkannt und Events verarbeitet
- [x] Development Setup
- `config.yaml.dev` für lokale Tests (Mosquitto + Odoo)
- `docker-compose.dev.yaml` erweitert mit iot-bridge service
**Test:** ✅ Container läuft produktiv, empfängt MQTT, erkennt Sessions
**Docker:** ✅ Multi-stage build, 71MB final image, non-root user
---
## 🎯 Phase 2: Odoo REST API
**Ziel:** Odoo REST API für Bridge-Config und Event-Empfang
### 2.1 Models anpassen
- [x] **ows.iot.event** Model erstellt (`models/iot_event.py`)
- `event_uid` (unique constraint)
- `event_type` (selection: session_started/updated/stopped/timeout/heartbeat/power_change)
- `payload_json` (JSON Field)
- `device_id`, `session_id` (Char fields)
- Auto-Linking zu `mqtt.device` und `mqtt.session`
- Payload-Extraktion: `power_w`, `state`
- `processed` flag, `processing_error` für Error-Tracking
- [x] **mqtt.device** erweitert:
- `device_id` (External ID für Bridge API)
- Bestehende Felder: `strategy_config` (JSON), session_strategy, parser_type, topic_pattern
- [x] **mqtt.session** - bereits vorhanden:
- `session_id` (external UUID)
- Duration fields: `total_duration_s`, `standby_duration_s`, `working_duration_s`
- State tracking: `status` (running/completed), `current_state`
- Power tracking: `start_power_w`, `end_power_w`, `current_power_w`
**Test:** ✅ Models erstellt, Security Rules aktualisiert
---
### 2.2 REST API Controller
- [x] `controllers/iot_api.py` erstellt
- [x] **GET /ows/iot/config**:
- Returns: Alle aktiven Devices mit `session_config` als JSON
- Auth: public (später API-Key möglich)
- Response Format:
```json
{
"status": "success",
"devices": [{
"device_id": "...",
"mqtt_topic": "...",
"parser_type": "...",
"machine_name": "...",
"session_config": { strategy, thresholds, ... }
}],
"timestamp": "ISO8601"
}
```
- [x] **POST /ows/iot/event**:
- Schema-Validation (event_type, device_id, event_uid, timestamp required)
- Event-UID Duplikat-Check → 409 Conflict (idempotent)
- Event speichern in `ows.iot.event`
- Auto-Processing: Session erstellen/updaten/beenden
- Response Codes: 201 Created, 409 Duplicate, 400 Bad Request, 500 Error
- JSON-RPC Format (Odoo type='json')
**Test:** ✅ Controller erstellt, bereit für manuellen Test
```bash
curl http://localhost:8069/ows/iot/config
curl -X POST http://localhost:8069/ows/iot/event -d '{...}'
```
---
### 2.3 Bridge Client - OdooClient
- [x] **OdooClient** implementiert (`iot_bridge/odoo_client.py`)
- ~~`get_config()`: GET /ows/iot/config~~ → **ENTFERNEN (nicht mehr verwendet)**
- `send_event()`: POST /ows/iot/event (JSON-RPC) → **BEHALTEN**
- Retry-Logic über EventQueue (bereits in Phase 1.4)
- Duplicate Handling: 409 wird als Success behandelt
- Error Handling: Exceptions für 4xx/5xx
- HTTP Session mit requests library
- [x] **config.py** erweitert:
- `OdooConfig`: `base_url`, `database`, `username`, `api_key`
- Entfernt: alte `url`, `token` Felder
- [x] **main.py** angepasst:
- OdooClient Initialisierung mit neuen Parametern
- Conditional: MockOdooClient (use_mock=true) oder OdooClient (use_mock=false)
**Test:** ✅ Code fertig, bereit für Integration Test
---
### 2.4 Integration Testing (Alt-Status, vor Architektur-Änderung)
- [x] Odoo Modul upgraden (Models + Controller laden)
- ✅ Module installed in odoo database
- ✅ Deprecation warnings (non-blocking, documented)
- [x] mqtt.device angelegt: "Shaper Origin" (device_id: shellypmminig3-48f6eeb73a1c)
- ✅ Widget fix: CodeEditor 'ace' → 'text' (Odoo 18 compatibility)
- ✅ connection_id made optional (deprecated legacy field)
- [x] API manuell getestet
- ✅ GET /ows/iot/config → HTTP 200, returns device list
- ✅ POST /ows/iot/event → HTTP 200, creates event records
- [x] `config.yaml.dev`: `use_mock=false` gesetzt
- [x] Docker Compose gestartet: Odoo + Mosquitto + Bridge
- [x] End-to-End Tests:
- ✅ MQTT → Bridge → Odoo event flow working
- ✅ Bridge fetches config from Odoo (1 device)
- ✅ Events stored in ows_iot_event table
- ✅ session_started creates mqtt.session automatically
- ✅ session_heartbeat updates session (controller fix applied)
- [x] Odoo 18 Compatibility Fixes:
- ✅ type='json' routes: Use function parameters instead of request.jsonrequest
- ✅ event_type Selection: Added 'session_heartbeat'
- ✅ Timestamp parsing: ISO format → Odoo datetime format
- ✅ Multiple databases issue: Deleted extra DBs (hh18, odoo18) for db_monodb()
- ✅ auth='none' working with single database
**Outstanding Issues:**
- [x] UI Cleanup - MQTT Connection (deprecated) view removal
- ✅ Menu items hidden (commented out in mqtt_menus.xml)
- ✅ mqtt.message menu hidden (replaced by ows.iot.event)
- ✅ New "IoT Events" menu added
- [x] UI Cleanup - mqtt.device form:
- ✅ connection_id field hidden (invisible="1")
- ✅ device_id field prominent ("External Device ID")
- ✅ Parser Type + Session Strategy fields clarified
- ✅ Help text: "Configuration is sent to IoT Bridge via REST API"
- ✅ Strategy config placeholder updated with all required fields
- [x] ows.iot.event Views created:
- ✅ List view with filters (event type, processed status)
- ✅ Form view with payload display
- ✅ Search/filters: event type, device, session, date grouping
- ✅ Security rules configured
- [x] Create Test Device in Odoo:
- ✅ "Test Device - Manual Simulation" (device_id: test-device-manual)
- ✅ Low thresholds for easy testing (10W standby, 50W working)
- ✅ Test script created: tests/send_test_event.sh
- [x] Verify session auto-create/update workflow:
- ✅ session_started creates mqtt.session
- ✅ session_heartbeat updates durations + power
- ✅ total_duration_s = total_working_s + total_standby_s (calculated in controller)
**Next Steps:**
- [ ] UI Testing: Login to Odoo and verify:
- [ ] MQTT -> Devices: See "Shaper Origin" + "Test Device"
- [ ] MQTT -> Sessions: Check duration calculations (Total = Working + Standby)
- [ ] MQTT -> IoT Events: Verify event list, filters work
- [ ] No "Connections" or "Messages" menus visible
- [ ] Duration Field Validation:
- [ ] Check if Total Hours displays correctly (computed from _s fields)
- [ ] Verify Standby Hours + Working Hours sum equals Total Hours
**Test:** ⏳ Core functionality working, UI cleanup needed
---
## 🎯 Phase 2.6: **KRITISCHE FIXES** - Autonomie & Event-Type-Kompatibilität ✅
**Ziel:** Bridge resilient machen (läuft ohne Odoo) und Session-Closing fixen
### 2.6.1 Bridge Autonomie-Fix (CRITICAL) ✅
**Problem:** Bridge crashed in Endlosschleife wenn Odoo nicht erreichbar war
**Ursache:**
```python
# iot_bridge/main.py:113
try:
device_config = odoo_client.get_config()
except Exception as e:
logger.error("config_load_failed", error=str(e))
sys.exit(1) # ← Container crash → Docker restart → Endlosschleife!
```
**Lösung:**
- [x] `sys.exit(1)` entfernt
- [x] Odoo-Config-Check wird zu optionalem Warning (nicht blocker)
- [x] Bridge läuft autark mit lokaler `config.yaml`
- [x] Loggt: `bridge_autonomous_mode` + `odoo_not_reachable` (warning)
**Test:**
- [x] Bridge startet ohne Odoo → läuft stabil (kein Crash)
- [x] Session-Detection funktioniert autark
- [x] Events werden in Queue gesammelt
- [x] Bei Odoo-Reconnect: Queue wird automatisch geleert
---
### 2.6.2 Event-Type Kompatibilität Fix ✅
**Problem:** Sessions blieben offen - `session_ended` Events wurden ignoriert
**Ursache:**
- Bridge sendet: `session_ended`
- Controller erwartet: `session_stopped`
- Mismatch → Event wird nicht verarbeitet → Session bleibt `status='running'`
**Lösung:**
- [x] `iot_event.py`: `session_ended` zu Selection hinzugefügt
- [x] `iot_api.py`: Controller akzeptiert beide Event-Types
```python
elif event.event_type in ['session_stopped', 'session_ended', 'session_timeout']:
```
**Test:**
- [x] 3 offene Sessions manuell geschlossen via Re-Send
- [x] Neue Sessions werden korrekt geschlossen
- [x] Keine 500 Errors mehr bei `session_ended` Events
---
### 2.6.3 Ergebnis: Resiliente Architektur ✅
**Vorher:**
- ❌ Bridge crashed ohne Odoo (Endlosschleife)
- ❌ Sessions blieben offen (Event-Type-Mismatch)
- ❌ Keine Autarkie
**Nachher:**
- ✅ Bridge läuft autark mit lokaler `config.yaml`
- ✅ Events werden in Queue gesammelt bei Odoo-Ausfall
- ✅ Automatic Retry mit Exponential Backoff (bis zu 10 Versuche)
- ✅ Bei Odoo-Wiederverbindung: Queue wird automatisch geleert
- ✅ Sessions werden korrekt geschlossen (`session_ended` wird verarbeitet)
**Commit:**`18cac26 - fix: Make IoT Bridge autonomous and fix session closing`
---
## 🎯 Phase 3: **ARCHITEKTUR-ÄNDERUNG** - Odoo konfiguriert Bridge (PUSH statt PULL)
**Ziel:** Umkehrung der Config-Flow-Richtung: Odoo pusht Config an Bridge (statt Bridge holt Config)
### 3.1 Bridge: HTTP Server für Config-Empfang ✅
**Status:** ✅ ABGESCHLOSSEN (12.02.2026)
**Implementiert:**
- [x] **HTTP Server implementieren** (FastAPI)
- Port 8080 (konfigurierbar via ENV `BRIDGE_PORT`)
- Endpoint: `POST /config` - Config von Odoo empfangen
- Endpoint: `GET /config` - Aktuelle Config abfragen
- Endpoint: `GET /health` - Health Check
- Authentifizierung: Bearer Token (optional, ENV `BRIDGE_API_TOKEN`)
- [x] **Config-Validation & Processing**:
- Pydantic Models für JSON Schema Validation (BridgeConfig, DeviceConfig, SessionConfig)
- Validierung: unique device_id, unique mqtt_topic, working_threshold_w > standby_threshold_w
- DeviceManager: Device diff (add/remove/update detection)
- MQTT Subscriptions dynamisch updaten (subscribe/unsubscribe)
- Session Detectors erstellen/updaten/entfernen
- [x] **Config-Persistence**:
- Schreibe empfangene Config nach `/data/config-active.yaml`
- Beim Bridge-Restart: Lade `/data/config-active.yaml` (Fallback vor config.yaml)
- Volume `iot-bridge-data:/data` in docker-compose.dev.yaml
- [x] **Health-Endpoint**: `GET /health``{ "status": "ok", "devices": 2, "subscriptions": 2, "last_config_update": "..." }`
**Neue Dateien:**
- `iot_bridge/config_server.py` - FastAPI Server mit Config API
- `iot_bridge/device_manager.py` - Dynamisches Device/MQTT Subscription Management
- `iot_bridge/tests/test-config-push.sh` - Test-Skript für Config Push
- `iot_bridge/tests/test-config-push.json` - Test-Config (2 Devices)
**Geänderte Dateien:**
- `iot_bridge/main.py` - Integration HTTP Server (Thread), DeviceManager, config-active.yaml Fallback
- `iot_bridge/mqtt_client.py` - subscribe()/unsubscribe() Methoden für dynamische Topics
- `iot_bridge/requirements.txt` - FastAPI, Uvicorn, Pydantic hinzugefügt
- `iot_bridge/Dockerfile` - EXPOSE 8080, HEALTHCHECK via HTTP
- `odoo/docker-compose.dev.yaml` - Bridge Port 8080, Volume /data, ENV BRIDGE_PORT
**Test-Durchführung:**
```bash
# 1. Bridge neu bauen und starten
cd iot_bridge && docker build -t iot_mqtt_bridge:latest .
cd ../odoo && docker compose -f docker-compose.dev.yaml restart iot-bridge
# 2. Health Check
curl http://localhost:8080/health
# → {"status": "ok", "devices": 2, "subscriptions": 2, "last_config_update": null}
# 3. Config Push
cd ../iot_bridge/tests
./test-config-push.sh
# Testet:
# - POST /config mit test-config-push.json (2 Devices)
# - Validierung: HTTP 200, Config applied
# - Health Check: last_config_update gesetzt
# - GET /config: Neue Config wird zurückgegeben
# 4. Persistence verifizieren
docker exec hobbyhimmel_odoo_18-dev_iot_bridge cat /data/config-active.yaml
# → YAML-File mit neuer Config
# 5. Bridge-Logs prüfen
docker logs hobbyhimmel_odoo_18-dev_iot_bridge | grep -E "(config_received|device_added|device_removed)"
# → Logs zeigen:
# - config_received (2 devices)
# - config_persisted (/data/config-active.yaml)
# - device_removed (alte Devices: testshelly, shaperorigin)
# - device_added (neue Devices: shaper-origin-pm, test-device-manual)
# - mqtt unsubscribe/subscribe für neue Topics
```
**Ergebnis:**
- ✅ Config Push funktioniert (HTTP 200, devices_configured: 2)
- ✅ Alte Devices werden entfernt (MQTT unsubscribe)
- ✅ Neue Devices werden hinzugefügt (MQTT subscribe, SessionDetector erstellt)
- ✅ Config wird persistiert nach `/data/config-active.yaml`
- ✅ Bridge läuft autark weiter (config-active.yaml wird beim Restart geladen)
- ✅ Health Endpoint zeigt last_config_update Timestamp
---
### 3.2 Odoo: Config-Push-System ✅
**Status:** ✅ ABGESCHLOSSEN (12.02.2026)
**Implementiert:**
- [x] **Model-Hooks in `mqtt.device`**:
- Override `create()`: Nach Device-Erstellung → `_push_bridge_config()`
- Override `write()`: Nach Device-Update → `_push_bridge_config()`
- Override `unlink()`: Nach Device-Löschung → `_push_bridge_config()`
- Smart detection: Nur bei relevanten Feldern (active, device_id, topic_pattern, etc.)
- [x] **Config-Builder**: `_build_bridge_config()`
- Sammelt alle `mqtt.device` mit `active=True`
- Konvertiert zu Bridge-JSON-Format (BridgeConfig Schema)
- Topic Pattern Transformation (/# → /status/pm1:0)
- Session Config Extraktion aus strategy_config JSON
- Returns: `{"devices": [...], "timestamp": "...", "version": "1.0"}`
- [x] **HTTP-Client**: `_push_bridge_config()`
- `requests.post(f"{bridge_url}/config", json=config, timeout=10)`
- Retry-Logic: 3 Versuche mit exponential backoff (1s, 2s, 4s)
- Error-Handling: Loggt Fehler, wirft KEINE Exception (non-blocking)
- Response: dict mit success/message für UI-Notifications
- Unterscheidung: 4xx = kein Retry, 5xx = Retry
- [x] **System Parameter**: `open_workshop_mqtt.bridge_url`
- Default: `http://iot-bridge:8080`
- Gespeichert in `data/system_parameters.xml`
- Abruf via `ir.config_parameter.get_param()`
- [x] **Manual Push**: Button in Device-List-View ✅
- "🔄 Push Config to Bridge" Button im List Header
- Ruft `action_push_config_to_bridge()` auf
- UI Success/Error Notifications (green/red toast)
- [x] **External Dependencies**
- `requests` library zu `__manifest__.py` hinzugefügt
**Neue/Geänderte Dateien:**
- `models/mqtt_device.py` - +250 Zeilen (Config Builder, HTTP Client, Hooks)
- `data/system_parameters.xml` - System Parameter für Bridge URL
- `views/mqtt_device_views.xml` - Manual Push Button
- `__manifest__.py` - requests dependency, data file
- `tests/test_config_push_integration.py` - Integration Test Script
**Test-Durchführung:**
```bash
# Manuelle Tests:
# 1. Odoo UI öffnen: http://localhost:9018
# 2. MQTT -> Devices -> "🔄 Push Config to Bridge" Button
# 3. Erfolgs-Notification erscheint
# 4. Bridge Logs prüfen:
docker logs hobbyhimmel_odoo_18-dev_iot_bridge | grep config_received
# 5. Device erstellen/ändern/löschen
# -> Config wird automatisch gepusht (model hooks)
# Bridge Config überprüfen:
curl http://localhost:8080/config | jq '.devices[].machine_name'
curl http://localhost:8080/health | jq '.'
```
**Ergebnis:**
- ✅ Model Hooks funktionieren (create/write/unlink)
- ✅ Config Builder erstellt valide Bridge Config
- ✅ HTTP Push mit Retry erfolgreich
- ✅ Manual Push Button funktioniert
- ✅ System Parameter wird geladen
- ✅ Non-blocking: Odoo-UI funktioniert auch wenn Bridge offline
---
### 3.3 Refactoring: Config-Removal in Bridge ✅
**Status:** ✅ ABGESCHLOSSEN (12.02.2026)
**Implementiert:**
- [x] **Entfernt**: `odoo_client.get_config()` Methode (nicht mehr verwendet)
- Entfernt aus `MockOdooClient` (LEGACY PULL-Logik)
- Entfernt aus `OdooClient` (LEGACY PULL-Logik)
- [x] **Verifiziert**: Keine periodische Config-Refresh-Logik in `main.py`
- Bridge lädt config-active.yaml beim Start
- Config wird nur via POST /config empfangen (von Odoo gepusht)
- Keine GET-Requests an Odoo mehr für Config
- [x] **Beibehalten**: `odoo_client.send_event()` für Event-Push ✅
- [x] **main.py Startup-Flow geprüft**:
1. Lade lokale `/data/config-active.yaml` (falls vorhanden) ✅
2. Starte HTTP Server (Port 8080) ✅
3. Warte auf MQTT + Config-Updates ✅
4. Laufe autark mit lokaler Config ✅
**Geänderte Dateien:**
- `iot_bridge/odoo_client.py` - ~40 Zeilen entfernt (2x get_config() Methoden)
**Ergebnis:**
- ✅ Bridge hat keine PULL-Logik mehr
- ✅ Nur PUSH-Architektur: Odoo → POST /config → Bridge
- ✅ Bridge läuft vollständig autark (config-active.yaml)
- ✅ Keine periodischen Config-Requests an Odoo
- ✅ Sauberer Code ohne Legacy-Logik
**Test:**
- [x] Bridge startet ohne Odoo → lädt config-active.yaml → subscribed Topics ✅
- [x] Bridge startet ohne config-active.yaml → wartet auf Push (loggt Warning) ✅
---
### 3.4 Docker Compose Integration
- [x] **Bridge Container**:
- Volume: `/data` für config-active.yaml Persistence
- ENV: `BRIDGE_PORT=8080`, `BRIDGE_API_TOKEN=...` (optional)
- Expose Port 8080 (nur zu Odoo, nicht nach außen)
- [x] **Odoo Container**:
- ENV: `IOT_BRIDGE_URL=http://iot-bridge:8080`
- [x] **Network**: Shared Network `odoo18-nw` (bereits vorhanden)
**Test:** `docker compose up -d` → alle Services starten → Config-Push funktioniert
---
### 3.5 End-to-End Tests (Neue Architektur) ✅
**Status:** ✅ ABGESCHLOSSEN (12.02.2026)
**Implementiert:**
- [x] **Automated E2E Test Suite** (`iot_bridge/tests/test_e2e_push_architecture.py`)
- Python-basiertes Test-Framework
- Vollautomatische Tests ohne manuelle Interaktion
- Farbiger Output mit detaillierter Fehlerdiagnose
- Docker-Integration für Bridge Restart Tests
**Test-Szenarien (alle ✅ PASSED):**
- [x] **Test 1: Device Create → Config Push**
- Device in Odoo erstellen via JSON-RPC API
- Model Hook triggert automatischen Config Push
- Bridge empfängt Config via POST /config
- Bridge Device Count erhöht sich (2 → 3)
- Device-ID im Bridge Config vorhanden
- **Ergebnis:** ✅ PASSED
- [x] **Test 2: Device Update → Config Push**
- Device working_threshold_w ändern (50W → 75W)
- Model Hook triggert automatischen Config Push
- Bridge empf ängt Update
- Bridge Session Detector Config aktualisiert
- Neue Schwellenwerte in Bridge Config
- **Ergebnis:** ✅ PASSED
- [x] **Test 3: Manual Push Button**
- Manual Push Button via Odoo API triggern
- Bridge empfängt Config
- Config Timestamp ändert sich
- **Ergebnis:** ✅ PASSED
- [x] **Test 4: Device Delete → Config Remove**
- Device in Odoo löschen
- Model Hook triggert automatischen Config Push
- Bridge entfernt Device
- Bridge Device Count verringert sich (3 → 2)
- Device-ID NICHT mehr in Bridge Config
- **Ergebnis:** ✅ PASSED
- [x] **Test 5: Bridge Restart → Config Persistence**
- Bridge Container neu starten (docker restart)
- Bridge lädt config-active.yaml automatisch
- Device Count bleibt erhalten (2 Devices)
- Device IDs bleiben erhalten
- Bridge läuft ohne Odoo-Zugriff weiter (autark)
- **Ergebnis:** ✅ PASSED
**Test-Ausführung:**
```bash
cd iot_bridge
python3 tests/test_e2e_push_architecture.py
# Output:
# ======================================================================
# End-to-End Tests: PUSH Architecture (Phase 3.5)
# ======================================================================
# Device Create..................................... ✓ PASSED
# Device Update..................................... ✓ PASSED
# Manual Push....................................... ✓ PASSED
# Device Delete..................................... ✓ PASSED
# Bridge Restart.................................... ✓ PASSED
#
# Result: 5/5 tests passed
# ======================================================================
# ALL TESTS PASSED ✓
# ======================================================================
```
**Neue Dateien:**
- `iot_bridge/tests/test_e2e_push_architecture.py` - Vollautomatisches E2E Test-Suite
**Ergebnis:**
- ✅ Alle 5 End-to-End Tests bestanden
- ✅ PUSH-Architektur vollständig funktionsfähig
- ✅ Model Hooks triggern automatisch Config Push
- ✅ Manual Push Button funktioniert
- ✅ Bridge Config Persistence funktioniert (config-active.yaml)
- ✅ Bridge läuft autark ohne Odoo
- ✅ No Volume Deletion required (Tests laufen mit running infrastructure)
---
## 🎯 Phase 4: Polishing & Dokumentation
### 4.1 Error Handling & Monitoring
- [x] Bridge: Structured Logging (JSON)
- [x] Odoo: Event-Processing Errors loggen
- [x] Metriken: Events sent/failed, Session count
- [x] Health-Checks für beide Services
---
### 4.2 Dokumentation
- [x] `iot_bridge/README.md` aktualisieren (ENV vars, Config)
- [x] `DEPLOYMENT.md` - Produktiv-Setup Guide
- [x] API Docs - REST Endpoints dokumentieren
- [x] Troubleshooting Guide
---
## 📊 Dependency Graph
```
Phase 1.1-1.4 (Bridge Core)
Phase 1.5 (Docker)
Phase 2.1-2.3 (Odoo API) ← kann parallel zu 1.1-1.4
Phase 2.4 (Integration Testing)
Phase 2.6 (Autonomie-Fix) ✅
Phase 3.1 (Bridge HTTP Server) ✅
Phase 3.2 (Odoo Config-Push) ✅
Phase 3.3 (Legacy Code Cleanup) ✅
Phase 3.5 (E2E Tests) ✅
Phase 4 (Polish & Dokumentation) ✅
```
---
## 🚀 Quick Start (nächster Schritt)
**Aktueller Stand:** ✅ Phase 1 & 2 abgeschlossen, Phase 2.6 (Autonomie-Fix) abgeschlossen, **Phase 3.1 & 3.2 abgeschlossen** (PUSH-Architektur vollständig)
**Nächste Schritte:**
1. [x] Phase 3.1: Bridge HTTP Server implementieren (`POST /config`) ✅
2. [x] Phase 3.2: Odoo Config-Push-System (Model-Hooks) ✅
3. [x] Phase 3.3: Refactoring (get_config() entfernen) ✅
4. [x] Phase 3.4: Docker Compose Integration (Volumes für config-active.yaml) ✅ (bereits in 3.1 erledigt)
5. [x] Phase 3.5: End-to-End Tests (neue Architektur) ✅
6. [x] Phase 4: Polishing & Dokumentation ✅
---
## ✅ Definition of Done
**Phase 1 Done:** ✅ ABGESCHLOSSEN
- [x] Bridge läuft als Docker Container ✅
- [x] Empfängt Shelly-Messages ✅
- [x] State-Detection + Aggregation funktioniert ✅
- [x] Loggt aggregierte Events auf Console ✅
**Phase 2 Done:** ✅ ABGESCHLOSSEN
- [x] Odoo REST API antwortet ✅
- [x] Events werden in DB gespeichert ✅
- [x] Sessions werden erstellt/aktualisiert ✅
- [x] Billing Units werden berechnet ✅
**Phase 2.6 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Bridge läuft autark ohne Odoo ✅
- [x] Event-Queue mit Retry-Mechanismus ✅
- [x] Session-Closing funktioniert (session_ended Support) ✅
- [x] Keine Endlosschleife bei Odoo-Ausfall ✅
**Phase 3.1 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Bridge HTTP Server (`POST /config`, `GET /config`, `GET /health`) ✅
- [x] FastAPI mit Pydantic Validation ✅
- [x] Dynamic Subscription Management (DeviceManager) ✅
- [x] Config-Persistence in Bridge (`/data/config-active.yaml`) ✅
- [x] Device Add/Update/Remove mit MQTT Subscribe/Unsubscribe ✅
**Phase 3.2 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Model Hooks (create/write/unlink) ✅
- [x] Config Builder (_build_bridge_config) ✅
- [x] HTTP Client mit Retry-Logic ✅
- [x] System Parameter (bridge_url) ✅
- [x] Manual Push Button in UI ✅
**Phase 3.3 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] get_config() aus MockOdooClient entfernt ✅
- [x] get_config() aus OdooClient entfernt ✅
- [x] Keine periodische Config-Refresh Logik ✅
- [x] Bridge läuft vollständig mit PUSH-only Architektur ✅
**Phase 3.5 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Automated E2E Test Suite (5 Tests) ✅
- [x] Test 1: Device Create → Config Push ✅
- [x] Test 2: Device Update → Config Push ✅
- [x] Test 3: Manual Push Button ✅
- [x] Test 4: Device Delete → Config Remove ✅
- [x] Test 5: Bridge Restart → Config Persistence ✅
- [x] All tests PASSED (5/5) ✅
**Phase 3 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Bridge HTTP Server (POST /config) ✅
- [x] Odoo Config-Push System ✅
- [x] Dynamic Subscription Management ✅
- [x] Config-Persistence in Bridge (`/data/config-active.yaml`) ✅
- [x] Legacy Code Cleanup (get_config() entfernt) ✅
- [x] End-to-End Tests (5/5 passed) ✅
**Phase 4 Done:** ✅ ABGESCHLOSSEN (12.02.2026)
- [x] Dokumentation vollständig
- [x] Error Handling robust
- [x] Produktiv-Ready
---
## 📊 Aktueller Gesamtstatus (12.02.2026)
### Funktioniert ✅
- ✅ Docker Compose Setup (Odoo, PostgreSQL, Mosquitto, Bridge, pgAdmin)
- ✅ Bridge läuft autark mit lokaler config.yaml oder config-active.yaml
-**HTTP Config API (Port 8080): POST /config, GET /config, GET /health**
-**Dynamic Device Management (DeviceManager mit Add/Update/Remove)**
-**Config Persistence nach /data/config-active.yaml**
-**Odoo Config-Push System (Model Hooks: create/write/unlink)**
-**Manual Push Button in Odoo UI ("🔄 Push Config to Bridge")**
-**Config Builder (sammelt alle active devices, konvertiert zu Bridge-JSON)**
-**HTTP Client mit Retry (3 Versuche, exponential backoff)**
-**System Parameter: open_workshop_mqtt.bridge_url**
-**Legacy Code Cleanup (get_config() vollständig entfernt)**
-**End-to-End Tests (5/5 passed):**
- ✅ Device Create → Config Push
- ✅ Device Update → Config Push
- ✅ Manual Push Button
- ✅ Device Delete → Config Remove
- ✅ Bridge Restart → Config Persistence
-**IoT Bridge Status Monitoring:**
-`ows.mqtt.bridge` Model mit Health-Status-Feldern
- ✅ Bridge List/Form Views mit Status-Ampel
- ✅ Scheduled Action für Health-Checks (alle 2 Min)
- ✅ Status: Online ✅ / Offline ❌ / Unknown ⚠️
- ✅ Health-Metriken: Devices Count, Subscriptions, Last Seen
- ✅ Manual Health-Check Button "Check Health Now"
- ✅ Smart Button: Broker → Bridge Relation
-**Dynamic MQTT Reconnection (ohne Bridge-Restart):**
- ✅ Automatische Reconnection bei Broker-Änderungen (host/port/auth)
- ✅ TLS-Änderungen erfordern manuellen Restart (paho-mqtt Limitation)
- ✅ Robuster Startup (Bridge startet auch bei MQTT-Fehler)
- ✅ MQTT Subscription (dynamisch, automatisches Subscribe/Unsubscribe)
- ✅ Session Detection Engine (5-State Machine)
- ✅ Event-Aggregation (30s Heartbeat-Intervall)
- ✅ Event-Queue mit Retry-Logic (Exponential Backoff, max 10 retries)
- ✅ Odoo REST API: `POST /ows/iot/event`
- ✅ Sessions werden korrekt erstellt/aktualisiert/geschlossen
- ✅ Shelly Simulator für Testing
- ✅ Test-Skript für Config Push (`iot_bridge/tests/test-config-push.sh`)
- ✅ Integration Test Script (`extra-addons/.../tests/test_config_push_integration.py`)
-**Automated End-to-End Test Suite (Phase 3.5 - 5/5 tests passed)**
### Nächste Aufgabe 🎯
- ✅ Phase 4 abgeschlossen
### Architektur-Status ✅
- ✅ Bridge ist autark-fähig (läuft ohne Odoo)
- ✅ Bridge kann Config via HTTP empfangen (POST /config)
- ✅ Odoo pusht Config automatisch (Model Hooks)
- ✅ Manual Push Button in UI verfügbar
- ✅ Legacy get_config() vollständig entfernt (nur PUSH-Architektur)
- ✅ End-to-End Tests validieren gesamte PUSH-Architektur (5/5 passed)