From 77cd3f4595d7d9dd3a027735e7549c0458f45c62 Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Mon, 19 Jan 2026 20:27:17 +0100 Subject: [PATCH] fix: Deprecated force_sync Feature entfernt und wiki_doku_id bei Reset erhalten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Änderungen: - maintenance_equipment.py: wiki_doku_id wird bei action_reset_wiki_sync_status() nicht mehr zurückgesetzt (ist manuell editierbar) - equipment_wiki_sync_wizard.py: Deprecated force_sync Feld und zugehörige Logik entfernt - equipment_wiki_sync_wizard_views.xml: force_sync UI-Element entfernt - FEATURE_REQUEST/Odoo18_MQTT_IoT.md: Projektplan für MQTT-basierte IoT-Events hinzugefügt Grund: - force_sync Feature referenzierte nicht existierende Methoden (_generate_wiki_doku_page_content, _get_wiki_doku_page_id) - wiki_doku_id ist jetzt manuell editierbar und sollte nicht automatisch zurückgesetzt werden - Cleanup von veralteter/fehlerhafter Funktionalität --- FEATURE_REQUEST/Odoo18_MQTT_IoT.md | 324 ++++++++++++++++++ .../models/maintenance_equipment.py | 2 +- .../wizard/equipment_wiki_sync_wizard.py | 20 +- .../equipment_wiki_sync_wizard_views.xml | 3 - 4 files changed, 326 insertions(+), 23 deletions(-) create mode 100644 FEATURE_REQUEST/Odoo18_MQTT_IoT.md diff --git a/FEATURE_REQUEST/Odoo18_MQTT_IoT.md b/FEATURE_REQUEST/Odoo18_MQTT_IoT.md new file mode 100644 index 0000000..328e7c6 --- /dev/null +++ b/FEATURE_REQUEST/Odoo18_MQTT_IoT.md @@ -0,0 +1,324 @@ +# Projektplan: MQTT-basierte IoT-Events in Odoo 18 Community (Simulation-first) + +Stand: 2026-01-10 : https://chatgpt.com/share/696e559d-1640-800f-9d53-f7a9d1e784bd + +Ziel: **Odoo-18-Community (self-hosted)** soll **Geräte-Events** (Timer/Maschinenlaufzeit, Waage, Zustandswechsel) über **MQTT** aufnehmen und in Odoo als **saubere, nachvollziehbare Sessions/Events** verarbeiten. +Wichtig: **Hardware wird zunächst vollständig simuliert** (Software-Simulatoren), damit die Odoo-Schnittstelle stabil steht, bevor echte Geräte angebunden werden. + +--- + +## 1. Ziele und Nicht-Ziele + +### 1.1 Ziele (MVP) +- Einheitliche **Device-Event-Schnittstelle** in Odoo (REST/Webhook) inkl. Authentifizierung +- **Event-Log** in Odoo (persistente Rohereignisse + Normalisierung) +- **Session-Logik** für Timer/Maschinenlaufzeit (Start/Stop/Heartbeat) +- **Simulation** von: + - Maschinenzustand (running/idle/fault) + - Timer-Events (run_start/run_stop) + - Waage (stable_weight/tare) +- Reproduzierbare Tests (Unit/Integration) und eindeutige Fehlerdiagnostik (Logging) + +### 1.2 Nicht-Ziele (für Phase 1) +- Keine Enterprise-IoT-Box, keine Enterprise-Module +- Keine echte Hardware-Treiberentwicklung in Phase 1 +- Kein POS-Frontend-Live-Widget (optional erst in späterer Phase) +- Keine Abrechnungslogik/Preisregeln (kann vorbereitet, aber nicht umgesetzt werden) + +--- + +## 2. Zielarchitektur (Simulation-first) + +### 2.1 Komponenten +1. **MQTT Broker** : Mosquitto in Docker vorhanden +2. **Device Simulator(s)** (Python/Node) + - veröffentlicht MQTT Topics wie echte Geräte +3. **Gateway/Bridge (Software)** + - abonniert MQTT Topics + - validiert/normalisiert Payload + - sendet Events via HTTPS an Odoo (Webhook) +4. **Odoo Modul** `ows_iot_bridge` + - REST Controller `/ows/iot/event` + - Modelle für Devices, Events, Sessions + - Business-Regeln für Session-Start/Stop/Hysterese (softwareseitig) +5. Optional später: **Realtime-Feed** (WebSocket) für POS/Display + +### 2.2 Datenfluss +1. Simulator publiziert MQTT → `hobbyhimmel/machines//state` +2. Bridge konsumiert MQTT, mappt auf Event-Format +3. Bridge POSTet JSON an Odoo Endpoint +4. Odoo speichert Roh-Event + erzeugt ggf. Session-Updates + +--- + +## 3. Schnittstelle zu Odoo (Kern des Projekts) + +### 3.1 Authentifizierung +- Pro Device oder pro Bridge ein **API-Token** (Bearer) +- Header: `Authorization: Bearer ` +- Token in Odoo in `ows.iot.device` oder `ir.config_parameter` hinterlegt +- Optional: IP-Allowlist / Rate-Limit (in Reverse Proxy) + +### 3.2 Endpoint (REST/Webhook) +- `POST /ows/iot/event` +- Content-Type: `application/json` +- Antwort: + - `200 OK` mit `{ "status": "ok", "event_id": "...", "session_id": "..." }` + - Fehler: `400` (Schema), `401/403` (Auth), `409` (Duplikat), `500` (Server) + +### 3.3 Idempotenz / Duplikaterkennung +- Jedes Event enthält eine eindeutige `event_uid` +- Odoo legt Unique-Constraint auf `event_uid` (pro device) +- Wiederholte Events liefern `409 Conflict` oder `200 OK (duplicate=true)` (Designentscheidung) + +--- + +## 4. Ereignis- und Topic-Standard (Versioniert) + +### 4.1 MQTT Topics (v1) +- Maschinenzustand: + `hobbyhimmel/machines//state` +- Maschinenereignisse: + `hobbyhimmel/machines//event` +- Waage: + `hobbyhimmel/scales//event` +- Geräte-Status (optional): + `hobbyhimmel/devices//status` + +### 4.2 Gemeinsames JSON Event Schema (v1) +Pflichtfelder: +- `schema_version`: `"v1"` +- `event_uid`: UUID/string +- `ts`: ISO-8601 UTC (z. B. `"2026-01-10T12:34:56Z"`) +- `source`: `"simulator" | "device" | "gateway"` +- `device_id`: string +- `entity_type`: `"machine" | "scale" | "sensor"` +- `entity_id`: string (z. B. machine_id) +- `event_type`: string (siehe unten) +- `payload`: object +- `confidence`: `"high" | "medium" | "low"` (für Sensorfusion) + +### 4.3 Event-Typen (v1) +**Maschine/Timer** +- `run_start` (Start einer Laufphase) +- `run_stop` (Ende einer Laufphase) +- `heartbeat` (optional, laufend während running) +- `state_changed` (idle/running/fault) +- `fault` (Fehler mit Code/Severity) + +**Waage** +- `stable_weight` (stabiler Messwert) +- `weight` (laufend) +- `tare` +- `zero` +- `error` + +--- + +## 5. Odoo Datenmodell (Vorschlag) + +### 5.1 `ows.iot.device` +- `name` +- `device_id` (unique) +- `token_hash` (oder Token in separater Tabelle) +- `device_type` (machine/scale/...) +- `active` +- `last_seen` +- `notes` + +### 5.2 `ows.iot.event` +- `event_uid` (unique) +- `device_id` (m2o -> device) +- `entity_type`, `entity_id` +- `event_type` +- `timestamp` +- `payload_json` (Text/JSON) +- `confidence` +- `processing_state` (new/processed/error) +- `session_id` (m2o optional) + +### 5.3 `ows.machine.session` (Timer-Sessions) +- `machine_id` (Char oder m2o auf bestehendes Maschinenmodell) +- `start_ts`, `stop_ts` +- `duration_s` (computed) +- `state` (running/stopped/aborted) +- `origin` (sensor/manual/sim) +- `confidence_summary` +- `event_ids` (o2m) + +> Hinweis: Wenn du bereits `ows.machine` aus deinem open_workshop nutzt, referenziert `machine_id` direkt dieses Modell. + +--- + +## 6. Verarbeitungslogik (Phase 1: minimal, robust) + +### 6.1 Session-Automat (State Machine) +- Eingang: Events `run_start`, `run_stop`, optional `heartbeat` +- Regeln: + - `run_start` erstellt neue Session, wenn keine läuft + - `run_start` während laufender Session → nur Log, keine neue Session + - `run_stop` schließt laufende Session + - Timeout-Regel: wenn `heartbeat` ausbleibt (z. B. 10 min) → Session `aborted` + +### 6.2 Hysterese (Simulation als Stellvertreter für Sensorik) +In Simulation definierst du: +- Start, wenn „running“ mindestens **2s stabil** +- Stop, wenn „idle“ mindestens **15s stabil** +Diese Parameter als Odoo Systemparameter, z. B.: +- `ows_iot.start_debounce_s` +- `ows_iot.stop_debounce_s` + +--- + +## 7. Simulation (Software statt Hardware) + +### 7.1 Device Simulator: Maschine +- Konfigurierbar: + - Muster: random, fixed schedule, manuell per CLI + - Zustände: idle/running/fault + - Optional: „power_w“ und „vibration“ als Felder im Payload +- Publiziert MQTT in realistischen Intervallen + +### 7.2 Device Simulator: Waage +- Modi: + - stream weight (mehrfach pro Sekunde) + - stable_weight nur auf „stabil“ + - tare/zero Events per CLI + +### 7.3 Bridge Simulator (MQTT → Odoo) +- Abonniert alle relevanten Topics +- Validiert Schema v1 +- POSTet Events an Odoo +- Retry-Queue (lokal) bei Odoo-Ausfall +- Metriken/Logs: + - gesendete Events, Fehlerquoten, Latenz + +--- + +## 8. Milestones & Deliverables + +### M0 – Repo/Grundgerüst (0.5–1 Tag) +**Deliverables** +- Git-Repo Struktur +- Docker Compose: mosquitto + simulator + bridge +- Odoo Modul Skeleton `ows_iot_bridge` + +### M1 – Odoo Endpoint & Modelle (1–2 Tage) +**Deliverables** +- `/ows/iot/event` Controller inkl. Token-Auth +- Modelle `ows.iot.device`, `ows.iot.event` +- Admin UI: Geräte anlegen, Token setzen, letzte Events anzeigen + +### M2 – Session-Engine (1–2 Tage) +**Deliverables** +- Modell `ows.machine.session` +- Event → Session Zuordnung +- Timeout/Abbruch-Logik +- Parameter für Debounce/Timeout + +### M3 – Simulatoren & End-to-End Test (1–2 Tage) +**Deliverables** +- Machine Simulator + Scale Simulator +- Bridge mit Retry-Queue +- End-to-End: Simulator → MQTT → Bridge → Odoo → Session entsteht + +### M4 – Qualität & Betrieb (1 Tag) +**Deliverables** +- Logging-Konzept (Odoo + Bridge) +- Tests (Unit: Controller/Auth, Integration: Session Engine) +- Doku: Event-Schema v1, Topics, Beispielpayloads + +### Optional M5 – POS/Anzeige (später) +- Realtime Anzeige im POS oder auf Display +- Live-Weight in POS + +--- + +## 9. Testplan (Simulation-first) + +### 9.1 Unit Tests (Odoo) +- Auth: gültiger Token → 200 +- ungültig/fehlend → 401/403 +- Schema-Validation → 400 +- Idempotenz: duplicate `event_uid` → 409 oder duplicate-flag + +### 9.2 Integration Tests +- Sequenz: start → heartbeat → stop → Session duration plausibel +- stop ohne start → kein Crash, Event loggt Fehlerzustand +- Timeout: start → keine heartbeat → Session aborted + +### 9.3 Last-/Stabilitätstest (Simulator) +- 20 Maschinen, je 1 Event/s, 1h Lauf +- Ziel: Odoo bleibt stabil, Event-Insert performant, Queue läuft nicht über + +--- + +## 10. Betriebs- und Sicherheitskonzept + +- Token-Rotation möglich (neues Token, altes deaktivieren) +- Reverse Proxy: + - HTTPS + - Rate limiting auf `/ows/iot/event` + - optional Basic WAF Regeln +- Payload-Größenlimit (z. B. 16–64 KB) +- Bridge persistiert Retry-Queue auf Disk + +--- + +## 11. Offene Entscheidungen (später, nicht blocker für MVP) + +- Event-Consumer in Odoo vs. Bridge-only Webhook (empfohlen: Webhook) +- Genaues Mapping zu bestehenden open_workshop Modellen (`ows.machine`, Nutzer/RFID) +- Abrechnung: Preisregeln, Rundungen, POS-Integration +- Realtime: Odoo Bus vs. eigener WebSocket Service + +--- + +## 12. Nächste Schritte (konkret) + +1. `ows_iot_bridge` Modul: Endpoint + Device/Event Modelle implementieren +2. Docker Compose: mosquitto + bridge + simulator bereitstellen +3. Simulatoren produzieren v1-Events; Bridge sendet an Odoo +4. Session-Engine anschließen und End-to-End testen + +--- + +## Anhang A: Beispiel-Event (Maschine run_start) +```json +{ + "schema_version": "v1", + "event_uid": "c2a7d6f1-7d8d-4a63-9a7f-4e6d7b0d9e2a", + "ts": "2026-01-10T12:34:56Z", + "source": "simulator", + "device_id": "esp32-fraser-01", + "entity_type": "machine", + "entity_id": "formatkreissaege", + "event_type": "run_start", + "confidence": "high", + "payload": { + "power_w": 820, + "vibration": 0.73, + "reason": "power_threshold" + } +} +``` + +## Anhang B: Beispiel-Event (Waage stable_weight) +```json +{ + "schema_version": "v1", + "event_uid": "b6d0a2c5-9b1f-4a0b-8b19-7f2e1b8f3d11", + "ts": "2026-01-10T12:40:12Z", + "source": "simulator", + "device_id": "scale-sim-01", + "entity_type": "scale", + "entity_id": "waage-01", + "event_type": "stable_weight", + "confidence": "high", + "payload": { + "weight_g": 1532.4, + "unit": "g", + "stable_ms": 1200 + } +} +``` diff --git a/open_workshop_dokuwiki/models/maintenance_equipment.py b/open_workshop_dokuwiki/models/maintenance_equipment.py index b049455..32819cc 100644 --- a/open_workshop_dokuwiki/models/maintenance_equipment.py +++ b/open_workshop_dokuwiki/models/maintenance_equipment.py @@ -915,7 +915,7 @@ Diese Seite wird automatisch aktualisiert. 'wiki_synced': False, 'wiki_last_sync': False, 'wiki_status_id': False, # Status-ID neu generieren - 'wiki_doku_id': False, # Doku-ID neu generieren + # wiki_doku_id wird NICHT zurückgesetzt - ist manuell editierbar! }) _logger.info(f"Wiki-Sync-Status für {count} Equipment zurückgesetzt") diff --git a/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard.py b/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard.py index 47b664d..7e5217d 100644 --- a/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard.py +++ b/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard.py @@ -24,12 +24,6 @@ class EquipmentWikiSyncWizard(models.TransientModel): help='Nur Equipment aus diesem Bereich synchronisieren' ) - force_sync = fields.Boolean( - string='Zentrale Doku-Seiten überschreiben', - default=False, - help='Auch bereits existierende zentrale Dokumentationsseiten neu generieren (VORSICHT: Überschreibt manuell erstellten Inhalt!)' - ) - # Statistik-Felder (nach Sync) total_count = fields.Integer(string='Gefunden', readonly=True) success_count = fields.Integer(string='Erfolgreich', readonly=True) @@ -91,19 +85,7 @@ class EquipmentWikiSyncWizard(models.TransientModel): _logger.warning(error_msg) continue - # Force-Sync: Zentrale Doku-Seite überschreiben - if self.force_sync and equipment.wiki_doku_id: - doku_page_id = equipment._get_wiki_doku_page_id() - doku_content = equipment._generate_wiki_doku_page_content() - dokuwiki_client = self.env['dokuwiki.client'] - dokuwiki_client.create_page( - doku_page_id, - doku_content, - f"Überschrieben von Massen-Sync: {equipment.name}" - ) - _logger.info(f"Zentrale Doku-Seite überschrieben: {doku_page_id}") - - # Standard-Synchronisation + # Standard-Synchronisation (erstellt nur Odoo-Status-Seiten) equipment.sync_to_dokuwiki() success += 1 _logger.info(f"✓ {equipment.name} synchronisiert") diff --git a/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard_views.xml b/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard_views.xml index 43f3c48..4fe2aea 100644 --- a/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard_views.xml +++ b/open_workshop_dokuwiki/wizard/equipment_wiki_sync_wizard_views.xml @@ -11,9 +11,6 @@ - - -