diff --git a/README.md b/README.md index 3d300d4..eda8185 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Eine webbasierte Anwendung zur Echtzeit-Objekterkennung und -Verfolgung mittels - Python 3.12 oder höher - **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)) -- **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) > ⚠️ **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 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_PORT=1883 export MQTT_TOPIC="counter/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 > Arbeitsverzeichnis auf das Projekt zeigen (`directory=/pfad/zum/projekt`), > 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: - | Variable | Standard | Beschreibung | - | ------------ | ---------------------- | ------------------------------------ | + | Variable | Standard | Beschreibung | + | -------------- | ---------------------- | ------------------------------------ | + | `MQTT_ENABLED` | `true` | MQTT komplett ein-/ausschalten | | `MQTT_HOST` | `127.0.0.1` | Adresse des MQTT-Brokers | | `MQTT_PORT` | `1883` | Broker-Port | | `MQTT_USER` | – | Benutzername (optional) | diff --git a/app.py b/app.py index 0e38522..5c65157 100644 --- a/app.py +++ b/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] # --- 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_PORT = int(os.environ.get("MQTT_PORT", "1883")) 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) -# paho-mqtt 2.x verlangt die CallbackAPIVersion, 1.x kennt sie nicht. -try: - _mqtt = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) -except AttributeError: - _mqtt = mqtt.Client() -if MQTT_USER: - _mqtt.username_pw_set(MQTT_USER, MQTT_PASS) -_mqtt.on_connect = _on_mqtt_connect -# Last Will: Broker publiziert das, sobald die Verbindung unsauber abbricht. -_mqtt.will_set(STATUS_TOPIC, "offline", qos=1, retain=True) -try: - # 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) +_mqtt = None +if not MQTT_ENABLED: + print("[mqtt] deaktiviert (MQTT_ENABLED=false) - keine Events", flush=True) +else: + # paho-mqtt 2.x verlangt die CallbackAPIVersion, 1.x kennt sie nicht. + try: + _mqtt = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) + except AttributeError: + _mqtt = mqtt.Client() + if MQTT_USER: + _mqtt.username_pw_set(MQTT_USER, MQTT_PASS) + _mqtt.on_connect = _on_mqtt_connect + # Last Will: Broker publiziert das, sobald die Verbindung unsauber abbricht. + _mqtt.will_set(STATUS_TOPIC, "offline", qos=1, retain=True) + try: + # 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): """Crossing-Event auf {MQTT_TOPIC}/crossing (QoS 1, nicht retained).""" + if _mqtt is None: + return payload = { "event": "crossing", "camera": CAMERA_ID,