9.9 KiB
9.9 KiB
IoT MQTT Bridge for Odoo ../odoo
Separater Docker Container für MQTT-IoT-Device-Integration
Architektur-Übersicht
┌─────────────┐ MQTT ┌──────────────┐ REST API ┌────────────┐
│ Shelly PM │ ────────────────► │ IoT Bridge │ ──────────────► │ Odoo 18 │
│ (Hardware) │ │ (THIS!) │ │ (Business) │
└─────────────┘ └──────────────┘ └────────────┘
│ │
▼ │
┌──────────────┐ │
│ Mosquitto │ ◄──────────────────────┘
│ MQTT Broker │ (Config via API)
└──────────────┘
Zweck
Die IoT Bridge ist ein eigenständiger Python-Service der:
-
MQTT-Verbindung verwaltet
- Subscribed auf Device-Topics (z.B. Shelly PM Mini G3)
- Auto-Reconnect bei Verbindungsabbruch
- Exponential Backoff
-
Event-Normalisierung durchführt
- Parser für verschiedene Device-Typen (Shelly, Tasmota, Generic)
- Konvertiert zu Unified Event Schema v1
- Generiert eindeutige Event-UIDs
-
Session Detection (State Machine)
- Dual-Threshold Detection (Standby/Working)
- Debounce Timer (Start/Stop)
- Timeout Detection
- 5-State Machine: IDLE → STARTING → STANDBY/WORKING → STOPPING
-
Odoo-Kommunikation (REST API)
- Holt Device-Config:
GET /ows/iot/config - Sendet Events:
POST /ows/iot/event - Bearer Token Authentication
- Retry-Queue bei Odoo-Ausfall
- Holt Device-Config:
Projekt-Struktur
iot_bridge/
├── main.py # Haupt-Entry-Point
├── mqtt_client.py # MQTT Client (paho-mqtt)
├── session_detector.py # State Machine für Session Detection
├── odoo_client.py # REST API Client für Odoo
├── config.py # Config Management (ENV + Odoo)
├── parsers/
│ ├── __init__.py
│ ├── base_parser.py # Abstract Parser Interface
│ ├── shelly_parser.py # Shelly PM Mini G3
│ ├── tasmota_parser.py # Tasmota (optional)
│ └── generic_parser.py # Generic JSON
├── requirements.txt # Python Dependencies
├── Dockerfile # Multi-stage Build
└── README.md # Dieses Dokument
Konfiguration
ENV-Variablen
Die Bridge wird ausschließlich über Umgebungsvariablen konfiguriert:
| Variable | Pflicht | Default | Beschreibung |
|---|---|---|---|
ODOO_URL |
✅ | - | Odoo Base-URL (z.B. http://odoo:8069) |
ODOO_TOKEN |
✅ | - | API Token für Authentifizierung |
MQTT_URL |
✅ | - | MQTT Broker URL (z.B. mqtt://mosquitto:1883) |
MQTT_USERNAME |
❌ | None |
MQTT Username (optional) |
MQTT_PASSWORD |
❌ | None |
MQTT Password (optional) |
LOG_LEVEL |
❌ | INFO |
Logging Level (DEBUG, INFO, WARNING, ERROR) |
CONFIG_REFRESH_INTERVAL |
❌ | 300 |
Config-Refresh in Sekunden (5 Min) |
Odoo-Konfiguration
Die Bridge holt Device-spezifische Konfiguration von Odoo via:
Request: GET /ows/iot/config
Response:
{
"devices": [
{
"device_id": "shellypmminig3-48f6eeb73a1c",
"mqtt_topic": "shaperorigin/status/pm1:0",
"parser_type": "shelly_pm_mini_g3",
"machine_name": "Shaper Origin",
"session_config": {
"strategy": "power_threshold",
"standby_threshold_w": 20,
"working_threshold_w": 100,
"start_debounce_s": 3,
"stop_debounce_s": 15,
"message_timeout_s": 20
}
}
]
}
Event-Flow
1. MQTT Message empfangen
# Topic: shaperorigin/status/pm1:0
# Payload (Shelly Format):
{
"id": 0,
"voltage": 234.7,
"current": 0.289,
"apower": 120.5, # ← Power-Wert!
"aenergy": { "total": 256.325 }
}
2. Parser normalisiert zu Event Schema v1
{
"schema_version": "v1",
"event_uid": "b6d0a2c5-9b1f-4a0b-8b19-7f2e1b8f3d11",
"ts": "2026-01-31T10:30:15.123Z",
"device_id": "shellypmminig3-48f6eeb73a1c",
"event_type": "power_measurement",
"payload": {
"apower": 120.5,
"voltage": 234.7,
"current": 0.289,
"total_energy_kwh": 0.256325
}
}
3. Session Detector analysiert
State Machine:
IDLE (Power < 20W)
↓ Power > 100W
STARTING (Debounce 3s)
↓ 3s vergangen, Power > 100W
WORKING (Session läuft)
↓ Power < 100W
STOPPING (Debounce 15s)
↓ 15s vergangen, Power < 20W
IDLE (Session beendet)
4. Events an Odoo senden
run_start Event:
POST /ows/iot/event
Authorization: Bearer <token>
{
"schema_version": "v1",
"event_uid": "...",
"event_type": "run_start",
"device_id": "shellypmminig3-48f6eeb73a1c",
"ts": "2026-01-31T10:30:18.500Z",
"payload": {
"power_w": 120.5,
"reason": "power_threshold"
}
}
run_stop Event:
POST /ows/iot/event
{
"event_type": "run_stop",
"payload": {
"power_w": 8.2,
"reason": "normal",
"duration_s": 187.3
}
}
Docker Integration
Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Run as non-root
RUN useradd -m -u 1000 bridge && chown -R bridge:bridge /app
USER bridge
CMD ["python", "-u", "main.py"]
docker-compose.yaml
services:
iot_bridge:
image: iot_mqtt_bridge_for_odoo
build:
context: ./extra-addons/open_workshop/open_workshop_mqtt/iot_bridge
environment:
ODOO_URL: http://odoo:8069
ODOO_TOKEN: ${IOT_BRIDGE_TOKEN}
MQTT_URL: mqtt://mosquitto:1883
LOG_LEVEL: INFO
depends_on:
- odoo
- mosquitto
networks:
- odoo_network
restart: unless-stopped
Development
Lokales Testen
# 1. Python Dependencies installieren
cd iot_bridge/
python -m venv venv
source venv/bin/activate # Linux/Mac
pip install -r requirements.txt
# 2. ENV-Variablen setzen
export ODOO_URL=http://localhost:8069
export ODOO_TOKEN=your-token-here
export MQTT_URL=mqtt://localhost:1883
export LOG_LEVEL=DEBUG
# 3. Bridge starten
python main.py
Docker Build & Run
# Build
docker build -t iot_mqtt_bridge_for_odoo .
# Run
docker run --rm \
-e ODOO_URL=http://odoo:8069 \
-e ODOO_TOKEN=your-token \
-e MQTT_URL=mqtt://mosquitto:1883 \
iot_mqtt_bridge_for_odoo
Monitoring
Logs
# Docker Container Logs
docker compose logs -f iot_bridge
# Wichtige Log-Events:
# - [INFO] Bridge started, connecting to MQTT...
# - [INFO] MQTT connected, subscribing to topics...
# - [INFO] Config loaded: 3 devices
# - [DEBUG] Message received: topic=..., power=120.5W
# - [INFO] Session started: device=..., session_id=...
# - [WARNING] Odoo API error, retrying in 5s...
Health Check
Die Bridge sollte einen Health-Check-Endpoint anbieten:
# GET http://bridge:8080/health
{
"status": "ok",
"mqtt_connected": true,
"odoo_reachable": true,
"devices_configured": 3,
"active_sessions": 1,
"uptime_seconds": 3600
}
Fehlerbehandlung
MQTT Disconnect
- Auto-Reconnect mit Exponential Backoff
- Topics werden nach Reconnect neu subscribed
- Laufende Sessions werden nicht beendet
Odoo Unreachable
- Events werden in lokaler Queue gespeichert (in-memory oder SQLite)
- Retry alle 5 Sekunden
- Max. 1000 Events in Queue (älteste werden verworfen)
Config-Reload
- Alle 5 Minuten:
GET /ows/iot/config - Neue Devices → subscribe Topics
- Gelöschte Devices → unsubscribe Topics
- Geänderte Schwellenwerte → SessionDetector aktualisieren
Testing
Manuelle Tests
# Shelly Simulator für Tests
python tests/tools/shelly_simulator.py --scenario session_end
python tests/tools/shelly_simulator.py --scenario full_session
python tests/tools/shelly_simulator.py --scenario timeout
python3 tests/tools/shelly_simulator.py --broker localhost --port 1883 --no-tls --username "" --password "" --scenario full_session
### Unit Tests
```bash
pytest tests/test_session_detector.py -v
pytest tests/test_parsers.py -v
pytest tests/test_odoo_client.py -v
Integration Tests
# Requires: Running MQTT Broker + Odoo instance
pytest tests/integration/ -v
Production Deployment
Best Practices
-
Token Security
- Token in
.env(nicht in Git) - Regelmäßige Token-Rotation
- Separate Tokens pro Umgebung (dev/staging/prod)
- Token in
-
Logging
LOG_LEVEL=INFOin ProductionLOG_LEVEL=DEBUGnur für Troubleshooting- Log-Aggregation (z.B. via Docker Logging Driver)
-
Monitoring
- Health-Check in Docker Compose
- Alerts bei Container-Restart
- Metrics: Events/s, Queue-Größe, Odoo-Latenz
-
Scaling
- Eine Bridge-Instanz pro MQTT-Broker
- Mehrere Broker → mehrere Bridge-Container
- Shared Subscriptions (MQTT 5.0) für Load-Balancing
Roadmap
Phase 1 (MVP)
- Architektur-Design
- MQTT Client Implementation
- Shelly Parser
- Session Detector (aus Odoo portiert)
- Odoo REST Client
- Dockerfile
Phase 2 (Features)
- Retry-Queue (SQLite)
- Health-Check-Endpoint
- Tasmota Parser
- Generic JSON Parser
- Config-Hot-Reload
Phase 3 (Production)
- Integration Tests
- Docker Compose Example
- Deployment Guide
- Monitoring Dashboard
- Performance Tuning
License
LGPL-3 (same as Odoo)