added ignored lib
This commit is contained in:
parent
1c9829a7aa
commit
ee0e6c8b98
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -15,7 +15,7 @@ dist/
|
|||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
#lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
|
|
|||
323
FEATURE_REQUEST/open_workshop_IoT.md
Normal file
323
FEATURE_REQUEST/open_workshop_IoT.md
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
# Projektplan: MQTT-basierte IoT-Events in Odoo 18 Community (Simulation-first)
|
||||
|
||||
Stand: 2026-01-10
|
||||
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** (z. B. Mosquitto in Docker)
|
||||
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/<machine_id>/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>`
|
||||
- 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/<machine_id>/state`
|
||||
- Maschinenereignisse:
|
||||
`hobbyhimmel/machines/<machine_id>/event`
|
||||
- Waage:
|
||||
`hobbyhimmel/scales/<scale_id>/event`
|
||||
- Geräte-Status (optional):
|
||||
`hobbyhimmel/devices/<device_id>/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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/*!
|
||||
* Cropper.js v1.6.1
|
||||
* https://fengyuanchen.github.io/cropperjs
|
||||
*
|
||||
* Copyright 2015-present Chen Fengyuan
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Date: 2023-09-17T03:44:17.565Z
|
||||
*/.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
|
||||
File diff suppressed because one or more lines are too long
20
open_workshop_employee_imagegenerator/static/lib/html2canvas.min.js
vendored
Normal file
20
open_workshop_employee_imagegenerator/static/lib/html2canvas.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user