Savoir quels appareils sont connectés à l'instant présent est la première chose que demande tout opérateur, et le statut en ligne des appareils MQTT est étonnamment facile à mal implémenter. Le modèle fiable combine trois briques : un birth message retenu, un Last Will retenu et un message hors ligne explicite à l'arrêt. Dans ce guide, vous allez construire ce modèle pas à pas, ajouter un moniteur de flotte en Python et apprendre à éviter les cas limites qui font mentir les tableaux de bord.
Les briques de base
Chaque élément résout une partie du problème :
| Brique | Qui publie | Quand | Couvre |
|---|---|---|---|
Birth message (online, retenu) |
L'appareil | Juste après chaque connexion réussie | L'appareil est actif, y compris après une reconnexion |
Last Will (offline, retenu) |
Le broker, au nom de l'appareil | Connexion perdue sans DISCONNECT | Plantages, coupures de courant, pannes réseau |
Message d'arrêt (offline, retenu) |
L'appareil | Avant une déconnexion propre | Redémarrages et arrêts planifiés |
| Flag retain sur les trois | Tous | Toujours | Les tableaux de bord ouverts plus tard voient quand même l'état actuel |
Omettez l'un d'eux et vous créez un angle mort. Sans birth message, un appareil qui se rétablit reste « hors ligne ». Sans will, les plantages passent inaperçus. Sans message d'arrêt, un arrêt planifié laisse l'appareil « en ligne » pour toujours, car un DISCONNECT propre indique au broker de supprimer le will. Si le mécanisme du will est nouveau pour vous, lisez d'abord MQTT Last Will and Testament.
Concevoir le topic de statut
Donnez à chaque appareil exactement un topic de statut, à un emplacement prévisible :
connectmq/demo/<prefix>/<site>/<device-id>/status
Un wildcard comme connectmq/demo/<prefix>/+/+/status renvoie alors le statut de toute la flotte en un seul abonnement. Comme tous les messages de statut sont retenus, un moniteur qui démarre reçoit immédiatement le dernier état connu de chaque appareil. Le fonctionnement de la rétention est expliqué dans les messages retenus MQTT.
Pour le payload, une simple chaîne online ou offline suffit pour de nombreux outils. Le JSON ajoute un contexte utile à l'exploitation :
{"state":"online","ts":1760600000,"fw":"2.4.1","ip":"10.0.4.17","reason":"boot"}
Pour le will, l'appareil ne peut pas connaître l'horodatage d'une panne future, alors restez simple : {"state":"offline","reason":"lwt"}. Le moniteur enregistre le moment où il le reçoit.
Étape 1 : l'appareil
Ce script paho-mqtt 2.x se comporte comme un appareil bien conçu. Il définit un will retenu avant de se connecter, publie un birth message retenu dans on_connect pour qu'il soit répété après chaque reconnexion, et publie un message hors ligne retenu lors d'un arrêt propre. Il se connecte au serveur MQTT public gratuit broker.connectmq.com, réservé aux tests et aux prototypes :
import json
import signal
import sys
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
DEVICE_ID = sys.argv[1] if len(sys.argv) > 1 else "pump-07"
STATUS = f"connectmq/demo/z8b2/plant1/{DEVICE_ID}/status"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"z8b2-{DEVICE_ID}")
client.will_set(STATUS, json.dumps({"state": "offline", "reason": "lwt"}),
qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=30)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("connect failed:", reason_code)
return
birth = {"state": "online", "ts": int(time.time()), "reason": "connect"}
client.publish(STATUS, json.dumps(birth), qos=1, retain=True)
print("online")
def shutdown(signum, frame):
bye = {"state": "offline", "ts": int(time.time()), "reason": "shutdown"}
client.publish(STATUS, json.dumps(bye), qos=1, retain=True).wait_for_publish(timeout=5)
client.disconnect()
client.loop_stop()
sys.exit(0)
client.on_connect = on_connect
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
client.connect(BROKER, 1883, keepalive=20)
client.loop_start()
while True:
time.sleep(1) # la vraie télémétrie serait publiée ici
Lancez-le avec python device.py pump-07, puis dans un autre terminal avec python device.py fan-02.
Étape 2 : le moniteur de flotte
Le moniteur s'abonne à tous les topics de statut et tient un tableau des appareils. Comme les statuts sont retenus, il connaît l'état complet de la flotte quelques secondes après son démarrage :
import json
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
FILTER = "connectmq/demo/z8b2/+/+/status"
fleet = {}
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(FILTER, qos=1)
def on_message(client, userdata, msg):
device = msg.topic.split("/")[-2]
if not msg.payload: # payload retenu vide = appareil retiré
fleet.pop(device, None)
return
try:
state = json.loads(msg.payload).get("state", "unknown")
except ValueError:
state = msg.payload.decode(errors="replace")
previous = fleet.get(device, {}).get("state")
fleet[device] = {"state": state, "since": time.strftime("%H:%M:%S")}
tag = "retained" if msg.retain else "live"
if previous != state:
print(f"[{tag}] {device}: {previous} -> {state}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"monitor-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_forever()
Étape 3 : tester chaque mode de défaillance
Lancez le moniteur et les deux appareils, puis parcourez cette check-list en vérifiant que le moniteur réagit correctement à chaque fois :
- Redémarrage du moniteur. Arrêtez puis relancez le moniteur. Les deux appareils apparaissent immédiatement
online, marquésretained. - Arrêt propre. Appuyez sur Ctrl+C dans
fan-02. Le moniteur affiche aussitôtofflineavec la raisonshutdown. - Plantage. Tuez
pump-07aveckill -9. Le système ferme le socket, le broker publie le will et le moniteur afficheoffline. - Perte réseau silencieuse. Coupez le réseau de l'appareil sans tuer le processus. Après environ 1,5 fois le keepalive de 20 secondes, le will se déclenche.
- Rétablissement. Rétablissez le réseau. paho se reconnecte,
on_connects'exécute et le birth message repasse l'appareilonline. - Mise hors service. Publiez un message retenu vide sur le topic de statut. Le moniteur retire l'appareil.
Vous pouvez aussi suivre les mêmes topics en direct dans un navigateur : le client intégré au navigateur et l'explorateur de topics du portail ConnectMQ affichent les changements de statut en temps réel lorsque vous dirigez vos appareils vers votre propre broker.
Les cas limites qui font mentir les tableaux de bord
Client ID en double. Deux appareils avec le même ID s'éjectent mutuellement. Chaque prise de contrôle peut déclencher un will et un birth, si bien que le statut oscille sans arrêt. Des ID uniques sont indispensables ; consultez le guide du client ID MQTT.
Birth publié une seule fois au démarrage. Si vous publiez online en dehors du callback de connexion, un appareil qui se reconnecte après une microcoupure réseau reste offline pour toujours. Publiez-le à chaque connexion.
Will non retenu. Un tableau de bord ouvert après la panne n'en apprend jamais l'existence.
Reconnexions rapides qui font clignoter le statut. Un appareil qui se reconnecte en quelques secondes génère quand même une paire hors ligne/en ligne. En MQTT 5, un will delay interval supprime le will si la session reprend à temps.
Keepalive trop long. Avec un keepalive de 300 secondes, un appareil mort paraît en ligne pendant plus de sept minutes. Choisissez un keepalive adapté à la rapidité avec laquelle vous devez être informé.
« Connecté » ne veut pas dire « en bonne santé ». Un appareil peut conserver sa connexion MQTT alors que son capteur est en panne. Publiez un champ de santé ou de heartbeat séparé si vous avez besoin d'une vivacité au niveau applicatif.
Présence ou heartbeat : de quoi avez-vous besoin ?
La présence basée sur la connexion et les heartbeats applicatifs répondent à des questions différentes, et beaucoup de flottes ont besoin des deux.
| Signal | Répond à | Délai de détection | Coût |
|---|---|---|---|
| Birth + LWT sur le topic de statut | L'appareil est-il connecté au broker ? | Immédiat à 1,5 x keepalive | Quasi nul ; uniquement les pings keepalive |
| Message heartbeat périodique | La boucle applicative tourne-t-elle encore ? | Un ou deux intervalles de heartbeat | Une petite publication par intervalle |
| Contrôle de fraîcheur de la télémétrie | Le capteur produit-il encore des données ? | Dépend de la fréquence de publication attendue | Aucun sur l'appareil ; logique côté consommateur |
Un appareil dont la boucle principale est bloquée peut continuer à répondre aux pings keepalive si le client MQTT tourne dans son propre thread : le statut de connexion reste alors online. Un heartbeat publié depuis la boucle principale, ou un consommateur qui signale les appareils dont la télémétrie n'est plus fraîche, permet de détecter ce cas. Gardez les heartbeats non retenus et d'une fréquence modérée ; une fois par minute suffit largement pour la plupart des supervisions.
Utiliser les topics de statut dans d'autres outils
Comme le modèle repose simplement sur des messages MQTT retenus, tout outil compatible MQTT peut l'exploiter sans intégration particulière. Dans Node-RED, un nœud mqtt in abonné au wildcard de statut alimente un tableau de bord ou un flux de notification. L'intégration MQTT de Home Assistant peut associer un topic de statut à un paramètre de disponibilité avec les payloads online et offline. L'entrée MQTT consumer de Telegraf peut écrire les changements d'état dans une base de séries temporelles pour le reporting de disponibilité. Chacun de ces outils se connecte au broker comme un client MQTT ordinaire : la seule exigence est de s'accorder sur les noms de topics et les valeurs de payload. Les chaînes simples online et offline sont le choix le plus portable si vous comptez utiliser des outils du marché ; le JSON apporte un contexte plus riche à votre propre code.
Statut côté broker sur ConnectMQ
Le modèle ci-dessus est portable vers n'importe quel broker. En complément, ConnectMQ publie l'état de connexion sur $SYS/broker/connection/{clientId}/state, auquel un moniteur peut s'abonner comme source indépendante qui ne dépend pas d'une configuration correcte du will dans le firmware de l'appareil. La vue des appareils du portail organise vos appareils selon les niveaux ISA-95, et des règles d'alerte à seuil peuvent vous avertir lorsque des valeurs dépassent des limites. En production, utilisez TLS sur le port 8883 avec YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID issus du portail. Un broker MQTT managé fait tourner l'infrastructure de présence à votre place ; pour les fondamentaux, consultez le tutoriel MQTT.
Démarrer avec ConnectMQ
Créez un broker gratuit sur app.connectmq.com avec l'offre Serverless, sans carte bancaire. Déployez les scripts de l'appareil et du moniteur avec vos identifiants, et vous disposez en quelques minutes d'une présence de flotte fiable sur un broker MQTT cloud.
Questions fréquentes
Pourquoi mon appareil apparaît-il en ligne après avoir été arrêté ?
Un DISCONNECT propre supprime le Last Will. Publiez vous-même un message hors ligne retenu avant de vous déconnecter.
Pourquoi mon appareil reste-t-il hors ligne après sa reconnexion ?
Le birth message n'est probablement publié qu'une seule fois au démarrage. Publiez-le dans le callback de connexion pour que chaque reconnexion mette à jour le statut.
En combien de temps un appareil hors ligne est-il détecté ?
Immédiatement si la connexion TCP se ferme. Si l'appareil disparaît silencieusement, après environ 1,5 fois l'intervalle de keepalive.
Les messages de statut doivent-ils être retenus ?
Oui. Retenir les messages de birth, de will et d'arrêt garantit que tout client qui s'abonne plus tard reçoit immédiatement l'état actuel.



