MQTT per MQTT_ENABLED ein-/ausschaltbar (einsteigerfreundlich)
- MQTT_ENABLED=false -> App startet ohne Broker, keine Events/LWT - publish_crossing wird zum No-Op, kein Reconnect-Rauschen - README: optional gekennzeichnet, Env-Tabelle + .env-Beispiel ergaenzt Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
README.md
13
README.md
@@ -17,7 +17,7 @@ Eine webbasierte Anwendung zur Echtzeit-Objekterkennung und -Verfolgung mittels
|
|||||||
- Python 3.12 oder höher
|
- Python 3.12 oder höher
|
||||||
- **NVIDIA-Grafikkarte mit CUDA-Unterstützung** – für die Echtzeit-Erkennung erforderlich (siehe [Hardware & CUDA](#hardware--cuda))
|
- **NVIDIA-Grafikkarte mit CUDA-Unterstützung** – für die Echtzeit-Erkennung erforderlich (siehe [Hardware & CUDA](#hardware--cuda))
|
||||||
- **ESP32-CAM** als Live-Quelle, die einen MJPEG-Stream im Netzwerk bereitstellt (siehe [Kamera / Livestream (ESP32-CAM)](#kamera--livestream-esp32-cam))
|
- **ESP32-CAM** als Live-Quelle, die einen MJPEG-Stream im Netzwerk bereitstellt (siehe [Kamera / Livestream (ESP32-CAM)](#kamera--livestream-esp32-cam))
|
||||||
- **MQTT-Broker** zum Empfang der Zähl-Events (z. B. [Mosquitto](https://mosquitto.org/); siehe [MQTT / Zähl-Events](#mqtt--zähl-events))
|
- **MQTT-Broker** (*optional*) zum Empfang der Zähl-Events (z. B. [Mosquitto](https://mosquitto.org/)); per `MQTT_ENABLED=false` abschaltbar (siehe [MQTT / Zähl-Events](#mqtt--zähl-events))
|
||||||
- Die YOLO-Modelle werden beim ersten Start automatisch von Ultralytics geladen (Internetverbindung erforderlich)
|
- Die YOLO-Modelle werden beim ersten Start automatisch von Ultralytics geladen (Internetverbindung erforderlich)
|
||||||
|
|
||||||
> ⚠️ **Wichtig:** Für die flüssige Live-Erkennung wird eine **NVIDIA-GPU mit CUDA** benötigt. Ohne CUDA läuft die Inferenz auf der CPU und ist für Echtzeit-Streams zu langsam.
|
> ⚠️ **Wichtig:** Für die flüssige Live-Erkennung wird eine **NVIDIA-GPU mit CUDA** benötigt. Ohne CUDA läuft die Inferenz auf der CPU und ist für Echtzeit-Streams zu langsam.
|
||||||
@@ -66,12 +66,18 @@ beim Start **automatisch geladen** (`python-dotenv`):
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
export CAMERA_URL="http://CAMERA-IP:81/stream"
|
export CAMERA_URL="http://CAMERA-IP:81/stream"
|
||||||
|
export MQTT_ENABLED="true" # auf "false" setzen, wenn kein Broker vorhanden
|
||||||
export MQTT_HOST="MQTT-HOST"
|
export MQTT_HOST="MQTT-HOST"
|
||||||
export MQTT_PORT=1883
|
export MQTT_PORT=1883
|
||||||
export MQTT_TOPIC="counter/cam1"
|
export MQTT_TOPIC="counter/cam1"
|
||||||
export CAMERA_ID="cam1"
|
export CAMERA_ID="cam1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> 💡 **Ohne MQTT-Broker?** Setze `MQTT_ENABLED="false"` – dann startet die App
|
||||||
|
> ganz normal, zählt und streamt, sendet aber keine MQTT-Events. Ideal zum
|
||||||
|
> Ausprobieren ohne Broker-Setup. (Auch mit `true` startet die App übrigens,
|
||||||
|
> wenn der Broker gerade nicht erreichbar ist – sie verbindet sich dann später.)
|
||||||
|
|
||||||
> ⚠️ Beim Betrieb über einen Prozess-Manager (z. B. **supervisord**) muss das
|
> ⚠️ Beim Betrieb über einen Prozess-Manager (z. B. **supervisord**) muss das
|
||||||
> Arbeitsverzeichnis auf das Projekt zeigen (`directory=/pfad/zum/projekt`),
|
> Arbeitsverzeichnis auf das Projekt zeigen (`directory=/pfad/zum/projekt`),
|
||||||
> sonst wird die `.env` nicht gefunden und es greifen die Defaults.
|
> sonst wird die `.env` nicht gefunden und es greifen die Defaults.
|
||||||
@@ -155,8 +161,9 @@ Datenbank wie **NocoDB** ablegen.
|
|||||||
```
|
```
|
||||||
- Konfiguriert wird der Broker über Umgebungsvariablen:
|
- Konfiguriert wird der Broker über Umgebungsvariablen:
|
||||||
|
|
||||||
| Variable | Standard | Beschreibung |
|
| Variable | Standard | Beschreibung |
|
||||||
| ------------ | ---------------------- | ------------------------------------ |
|
| -------------- | ---------------------- | ------------------------------------ |
|
||||||
|
| `MQTT_ENABLED` | `true` | MQTT komplett ein-/ausschalten |
|
||||||
| `MQTT_HOST` | `127.0.0.1` | Adresse des MQTT-Brokers |
|
| `MQTT_HOST` | `127.0.0.1` | Adresse des MQTT-Brokers |
|
||||||
| `MQTT_PORT` | `1883` | Broker-Port |
|
| `MQTT_PORT` | `1883` | Broker-Port |
|
||||||
| `MQTT_USER` | – | Benutzername (optional) |
|
| `MQTT_USER` | – | Benutzername (optional) |
|
||||||
|
|||||||
42
app.py
42
app.py
@@ -66,6 +66,10 @@ YOLO_HALF = os.environ.get("YOLO_HALF", "1" if _CUDA else "0") == "1"
|
|||||||
VEHICLE_CLASS_IDS = [2, 3, 5, 7]
|
VEHICLE_CLASS_IDS = [2, 3, 5, 7]
|
||||||
|
|
||||||
# --- MQTT: ein Event pro Linienueberquerung (fuer n8n -> NocoDB) -----------
|
# --- MQTT: ein Event pro Linienueberquerung (fuer n8n -> NocoDB) -----------
|
||||||
|
# Komplett abschaltbar: MQTT_ENABLED=false -> App laeuft ohne Broker/Events.
|
||||||
|
MQTT_ENABLED = os.environ.get("MQTT_ENABLED", "true").strip().lower() in (
|
||||||
|
"1", "true", "yes", "on",
|
||||||
|
)
|
||||||
MQTT_HOST = os.environ.get("MQTT_HOST", "127.0.0.1")
|
MQTT_HOST = os.environ.get("MQTT_HOST", "127.0.0.1")
|
||||||
MQTT_PORT = int(os.environ.get("MQTT_PORT", "1883"))
|
MQTT_PORT = int(os.environ.get("MQTT_PORT", "1883"))
|
||||||
MQTT_USER = os.environ.get("MQTT_USER")
|
MQTT_USER = os.environ.get("MQTT_USER")
|
||||||
@@ -87,26 +91,32 @@ def _on_mqtt_connect(client, userdata, flags, *args):
|
|||||||
print(f"[mqtt] connected -> {STATUS_TOPIC} online", flush=True)
|
print(f"[mqtt] connected -> {STATUS_TOPIC} online", flush=True)
|
||||||
|
|
||||||
|
|
||||||
# paho-mqtt 2.x verlangt die CallbackAPIVersion, 1.x kennt sie nicht.
|
_mqtt = None
|
||||||
try:
|
if not MQTT_ENABLED:
|
||||||
_mqtt = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
print("[mqtt] deaktiviert (MQTT_ENABLED=false) - keine Events", flush=True)
|
||||||
except AttributeError:
|
else:
|
||||||
_mqtt = mqtt.Client()
|
# paho-mqtt 2.x verlangt die CallbackAPIVersion, 1.x kennt sie nicht.
|
||||||
if MQTT_USER:
|
try:
|
||||||
_mqtt.username_pw_set(MQTT_USER, MQTT_PASS)
|
_mqtt = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
||||||
_mqtt.on_connect = _on_mqtt_connect
|
except AttributeError:
|
||||||
# Last Will: Broker publiziert das, sobald die Verbindung unsauber abbricht.
|
_mqtt = mqtt.Client()
|
||||||
_mqtt.will_set(STATUS_TOPIC, "offline", qos=1, retain=True)
|
if MQTT_USER:
|
||||||
try:
|
_mqtt.username_pw_set(MQTT_USER, MQTT_PASS)
|
||||||
# async + loop_start -> blockiert den App-Start nicht, wenn der Broker weg ist
|
_mqtt.on_connect = _on_mqtt_connect
|
||||||
_mqtt.connect_async(MQTT_HOST, MQTT_PORT, keepalive=60)
|
# Last Will: Broker publiziert das, sobald die Verbindung unsauber abbricht.
|
||||||
_mqtt.loop_start()
|
_mqtt.will_set(STATUS_TOPIC, "offline", qos=1, retain=True)
|
||||||
except Exception as exc:
|
try:
|
||||||
print(f"[mqtt] init failed: {exc}", flush=True)
|
# async + loop_start -> blockiert den App-Start nicht, wenn der Broker weg ist
|
||||||
|
_mqtt.connect_async(MQTT_HOST, MQTT_PORT, keepalive=60)
|
||||||
|
_mqtt.loop_start()
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"[mqtt] init failed: {exc}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
def publish_crossing(vehicle_type, track_id, source):
|
def publish_crossing(vehicle_type, track_id, source):
|
||||||
"""Crossing-Event auf {MQTT_TOPIC}/crossing (QoS 1, nicht retained)."""
|
"""Crossing-Event auf {MQTT_TOPIC}/crossing (QoS 1, nicht retained)."""
|
||||||
|
if _mqtt is None:
|
||||||
|
return
|
||||||
payload = {
|
payload = {
|
||||||
"event": "crossing",
|
"event": "crossing",
|
||||||
"camera": CAMERA_ID,
|
"camera": CAMERA_ID,
|
||||||
|
|||||||
Reference in New Issue
Block a user