odoo_mqtt/IMPLEMENTATION_PLAN.md

32 KiB

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

  • iot_bridge/config.yaml erstellen (Device-Registry, MQTT Settings)
  • iot_bridge/config.py - Config Loader (YAML → Dataclass)
  • Mock-OdooClient (gibt hardcoded Config zurück, kein HTTP)
  • Logger Setup (structlog mit JSON output)

Test: Bridge startet, lädt Config, loggt Status (JSON), Graceful Shutdown funktioniert



1.2 MQTT Client portieren

  • python_prototype/mqtt_client.pyiot_bridge/mqtt_client.py
  • Shelly Parser integrieren (copy from services/parsers/)
  • Connection Handling (reconnect, error handling)
  • Message-Callback registrieren
  • 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

  • session_detector.py portieren (5-State Machine)
  • Aggregation-Logik hinzufügen:
    • Interner State-Tracker (1s Updates)
    • Timer für heartbeat_interval_s
    • Berechnung: interval_working_s, interval_standby_s
  • Session-IDs generieren (UUID)
  • Events sammeln (event_callback)
  • Unit Tests (9 Tests, alle PASSED)
  • Shelly Simulator für Testing
  • Integration Tests (7 Tests, alle PASSED)
  • 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

  • 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
  • event_uid zu allen Events hinzufügen (UUID)
  • Queue-Integration in main.py
    • on_event_generated() nutzt Queue statt direktem send
    • Queue-Start/Stop im Lifecycle
  • Mock-Odoo Failure-Simulation
    • mock_failure_rate in config.yaml (0.0-1.0)
    • MockOdooClient wirft Exceptions bei failures
  • 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
  • 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

  • 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
  • .dockerignore erstellt (venv/, tests/, pycache, logs/, data/)
  • requirements.txt erweitert mit PyYAML>=6.0
  • docker build -t iot_mqtt_bridge:latest
    • 3 Build-Iterationen (Package-Path-Fix erforderlich)
    • Finale Image: 8b690d20b5f7
  • 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
  • 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

  • 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
  • mqtt.device erweitert:
    • device_id (External ID für Bridge API)
    • Bestehende Felder: strategy_config (JSON), session_strategy, parser_type, topic_pattern
  • 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

  • controllers/iot_api.py erstellt
  • GET /ows/iot/config:
    • Returns: Alle aktiven Devices mit session_config als JSON
    • Auth: public (später API-Key möglich)
    • Response Format:
      {
        "status": "success",
        "devices": [{
          "device_id": "...",
          "mqtt_topic": "...",
          "parser_type": "...",
          "machine_name": "...",
          "session_config": { strategy, thresholds, ... }
        }],
        "timestamp": "ISO8601"
      }
      
  • 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

curl http://localhost:8069/ows/iot/config
curl -X POST http://localhost:8069/ows/iot/event -d '{...}'

2.3 Bridge Client - OdooClient

  • OdooClient implementiert (iot_bridge/odoo_client.py)
    • get_config(): GET /ows/iot/configENTFERNEN (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
  • config.py erweitert:
    • OdooConfig: base_url, database, username, api_key
    • Entfernt: alte url, token Felder
  • 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)

  • Odoo Modul upgraden (Models + Controller laden)
    • Module installed in odoo database
    • Deprecation warnings (non-blocking, documented)
  • mqtt.device angelegt: "Shaper Origin" (device_id: shellypmminig3-48f6eeb73a1c)
    • Widget fix: CodeEditor 'ace' → 'text' (Odoo 18 compatibility)
    • connection_id made optional (deprecated legacy field)
  • API manuell getestet
    • GET /ows/iot/config → HTTP 200, returns device list
    • POST /ows/iot/event → HTTP 200, creates event records
  • config.yaml.dev: use_mock=false gesetzt
  • Docker Compose gestartet: Odoo + Mosquitto + Bridge
  • 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)
  • 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:

  • 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
  • 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
  • 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
  • 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
  • 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:

# 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:

  • sys.exit(1) entfernt
  • Odoo-Config-Check wird zu optionalem Warning (nicht blocker)
  • Bridge läuft autark mit lokaler config.yaml
  • Loggt: bridge_autonomous_mode + odoo_not_reachable (warning)

Test:

  • Bridge startet ohne Odoo → läuft stabil (kein Crash)
  • Session-Detection funktioniert autark
  • Events werden in Queue gesammelt
  • 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:

  • iot_event.py: session_ended zu Selection hinzugefügt
  • iot_api.py: Controller akzeptiert beide Event-Types
    elif event.event_type in ['session_stopped', 'session_ended', 'session_timeout']:
    

Test:

  • 3 offene Sessions manuell geschlossen via Re-Send
  • Neue Sessions werden korrekt geschlossen
  • 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:

  • 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)
  • 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
  • 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
  • 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:

# 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:

  • 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.)
  • 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"}
  • 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
  • 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()
  • 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)
  • 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:

# 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:

  • Entfernt: odoo_client.get_config() Methode (nicht mehr verwendet)
    • Entfernt aus MockOdooClient (LEGACY PULL-Logik)
    • Entfernt aus OdooClient (LEGACY PULL-Logik)
  • 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
  • Beibehalten: odoo_client.send_event() für Event-Push
  • 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:

  • Bridge startet ohne Odoo → lädt config-active.yaml → subscribed Topics
  • Bridge startet ohne config-active.yaml → wartet auf Push (loggt Warning)

3.4 Docker Compose Integration

  • 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)
  • Odoo Container:
    • ENV: IOT_BRIDGE_URL=http://iot-bridge:8080
  • 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:

  • 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):

  • 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
  • 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
  • Test 3: Manual Push Button

    • Manual Push Button via Odoo API triggern
    • Bridge empfängt Config
    • Config Timestamp ändert sich
    • Ergebnis: PASSED
  • 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
  • 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:

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

  • Bridge: Structured Logging (JSON)
  • Odoo: Event-Processing Errors loggen
  • Metriken: Events sent/failed, Session count
  • Health-Checks für beide Services

4.2 Dokumentation

  • iot_bridge/README.md aktualisieren (ENV vars, Config)
  • DEPLOYMENT.md - Produktiv-Setup Guide
  • API Docs - REST Endpoints dokumentieren
  • 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. Phase 3.1: Bridge HTTP Server implementieren (POST /config)
  2. Phase 3.2: Odoo Config-Push-System (Model-Hooks)
  3. Phase 3.3: Refactoring (get_config() entfernen)
  4. Phase 3.4: Docker Compose Integration (Volumes für config-active.yaml) (bereits in 3.1 erledigt)
  5. Phase 3.5: End-to-End Tests (neue Architektur)
  6. Phase 4: Polishing & Dokumentation

Definition of Done

Phase 1 Done: ABGESCHLOSSEN

  • Bridge läuft als Docker Container
  • Empfängt Shelly-Messages
  • State-Detection + Aggregation funktioniert
  • Loggt aggregierte Events auf Console

Phase 2 Done: ABGESCHLOSSEN

  • Odoo REST API antwortet
  • Events werden in DB gespeichert
  • Sessions werden erstellt/aktualisiert
  • Billing Units werden berechnet

Phase 2.6 Done: ABGESCHLOSSEN (12.02.2026)

  • Bridge läuft autark ohne Odoo
  • Event-Queue mit Retry-Mechanismus
  • Session-Closing funktioniert (session_ended Support)
  • Keine Endlosschleife bei Odoo-Ausfall

Phase 3.1 Done: ABGESCHLOSSEN (12.02.2026)

  • Bridge HTTP Server (POST /config, GET /config, GET /health)
  • FastAPI mit Pydantic Validation
  • Dynamic Subscription Management (DeviceManager)
  • Config-Persistence in Bridge (/data/config-active.yaml)
  • Device Add/Update/Remove mit MQTT Subscribe/Unsubscribe

Phase 3.2 Done: ABGESCHLOSSEN (12.02.2026)

  • Model Hooks (create/write/unlink)
  • Config Builder (_build_bridge_config)
  • HTTP Client mit Retry-Logic
  • System Parameter (bridge_url)
  • Manual Push Button in UI

Phase 3.3 Done: ABGESCHLOSSEN (12.02.2026)

  • get_config() aus MockOdooClient entfernt
  • get_config() aus OdooClient entfernt
  • Keine periodische Config-Refresh Logik
  • Bridge läuft vollständig mit PUSH-only Architektur

Phase 3.5 Done: ABGESCHLOSSEN (12.02.2026)

  • Automated E2E Test Suite (5 Tests)
  • Test 1: Device Create → Config Push
  • Test 2: Device Update → Config Push
  • Test 3: Manual Push Button
  • Test 4: Device Delete → Config Remove
  • Test 5: Bridge Restart → Config Persistence
  • All tests PASSED (5/5)

Phase 3 Done: ABGESCHLOSSEN (12.02.2026)

  • Bridge HTTP Server (POST /config)
  • Odoo Config-Push System
  • Dynamic Subscription Management
  • Config-Persistence in Bridge (/data/config-active.yaml)
  • Legacy Code Cleanup (get_config() entfernt)
  • End-to-End Tests (5/5 passed)

Phase 4 Done: ABGESCHLOSSEN (12.02.2026)

  • Dokumentation vollständig
  • Error Handling robust
  • 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)