From 394c72d6d98fb98c3ab272b564e1ea0acacfafce Mon Sep 17 00:00:00 2001 From: Joachim Hummel Date: Mon, 1 Jun 2026 14:37:32 +0200 Subject: [PATCH] MQTT: Birth-Message + Last Will auf {MQTT_TOPIC}/status - "online" (retained) beim (Re-)Connect via on_connect-Callback - "offline" automatisch durch Broker bei Verbindungsabbruch (LWT) - Abnehmer (n8n / Home Assistant) sehen jederzeit den Live-Zustand - README um Status-/Availability-Topic ergaenzt Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 4 ++++ app.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 0f08b3f..3d300d4 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,10 @@ Datenbank wie **NocoDB** ablegen. - Die Events werden auf dem Topic `{MQTT_TOPIC}/crossing` mit **QoS 1** (nicht retained) publiziert. +- Zusätzlich gibt es ein **Status-/Availability-Topic** `{MQTT_TOPIC}/status` + (retained): Beim Verbinden sendet die App `online` (Birth-Message), bei einem + Verbindungsabbruch publiziert der Broker automatisch `offline` (Last Will / + LWT). So lässt sich jederzeit erkennen, ob der Counter läuft. - Das Payload ist JSON, z. B.: ```json { diff --git a/app.py b/app.py index eb4ae73..1df587b 100644 --- a/app.py +++ b/app.py @@ -73,9 +73,20 @@ MQTT_PASS = os.environ.get("MQTT_PASS") MQTT_TOPIC = os.environ.get("MQTT_TOPIC", "vehiclecounter/cam1") CAMERA_ID = os.environ.get("CAMERA_ID", "cam1") +# Availability-/Status-Topic: "online" beim Verbinden (Birth-Message), +# "offline" automatisch via Last Will (LWT), falls die Verbindung abreisst. +STATUS_TOPIC = f"{MQTT_TOPIC}/status" + # Zeitzone fuer den Zeitstempel (DST-aware). Standard Europe/Berlin. LOCAL_TZ = ZoneInfo(os.environ.get("TZ_NAME", "Europe/Berlin")) + +def _on_mqtt_connect(client, userdata, flags, *args): + """Birth-Message: nach jedem (Re-)Connect 'online' (retained) senden.""" + client.publish(STATUS_TOPIC, "online", qos=1, retain=True) + 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) @@ -83,6 +94,9 @@ 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)