Tutoriais

MQTT 5 em Python com gmqtt

Use o gmqtt para MQTT 5 em Python: session expiry, user properties, message expiry, request/response, opções de assinatura e reason codes, com código real.

Atualizado · 8 min de leitura

MQTT 5 em Python com gmqtt

gmqtt é um cliente MQTT asyncio para Python projetado em torno do MQTT 5 desde o início. Toda propriedade do MQTT 5 — session expiry, message expiry, user properties, response topic, correlation data, subscription identifiers — é um simples argumento nomeado, e as propriedades recebidas chegam como um dicionário. Se você quer experimentar recursos do MQTT 5 em Python sem montar objetos Properties na mão, o gmqtt é o caminho mais curto.

Este tutorial mostra a conexão com MQTT 5, publicação com propriedades, opções de assinatura, request/response, leitura de reason codes e como testar tudo contra um broker real.

Instalar e conectar

Instalação
pip install gmqtt

O gmqtt usa callbacks executados no event loop do asyncio. Conecte, assine em on_connect e mantenha o programa vivo com um asyncio.Event:

connect.py
import asyncio
import os
from gmqtt import Client as MQTTClient
from gmqtt.mqtt.constants import MQTTv50

STOP = asyncio.Event()

def on_connect(client, flags, rc, properties):
    print("conectado, rc =", rc, "propriedades do broker:", properties)
    client.subscribe("testmqtt/gmqtt/#", qos=1)

def on_message(client, topic, payload, qos, properties):
    print("mensagem", topic, payload.decode(), properties)
    return 0  # reason code para o PUBACK

def on_disconnect(client, packet, exc=None):
    print("desconectado", exc)

async def main():
    client = MQTTClient(
        "testmqtt-gmqtt-demo-01",
        session_expiry_interval=3600,   # mantém a sessão por 1 h após desconectar
        receive_maximum=100,            # no máximo 100 mensagens QoS 1/2 sem confirmação
    )
    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect
    client.set_auth_credentials(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])

    await client.connect("broker.connectmq.com", 1883, keepalive=30, version=MQTTv50)
    await STOP.wait()
    await client.disconnect()

asyncio.run(main())

Duas coisas são específicas do gmqtt. Argumentos nomeados passados ao construtor de MQTTClient viram propriedades do CONNECT — aqui session_expiry_interval e receive_maximum. E o argumento properties de on_connect traz o que o broker devolveu no CONNACK: o próprio receive maximum, topic alias maximum, maximum packet size ou um client identifier atribuído, dependendo do broker. Imprimir isso uma vez é o jeito mais rápido de ver o que seu broker realmente suporta.

Para TLS, passe ssl=True (ou um ssl.SSLContext) e a porta 8883: await client.connect("broker.connectmq.com", 8883, ssl=True).

Publicar com propriedades do MQTT 5

As propriedades de publicação são argumentos nomeados de client.publish():

publish.py (dentro de main, após conectar)
import json, time

client.publish(
    "testmqtt/gmqtt/sensor-1/temperature",
    json.dumps({"value": 21.7, "t": time.time()}),
    qos=1,
    content_type="application/json",
    message_expiry_interval=60,                     # descartar se não for entregue em 60 s
    user_property=("firmware", "2.4.1"),
)

No subscriber, os mesmos valores aparecem em properties. Repare no formato — os valores são listas, porque o MQTT 5 permite que algumas propriedades apareçam mais de uma vez:

Saída do subscriber
message testmqtt/gmqtt/sensor-1/temperature {"value": 21.7, "t": 1792223400.12}
  {'content_type': ['application/json'], 'message_expiry_interval': [60],
   'user_property': [('firmware', '2.4.1')], 'dup': 0, 'retain': 0}

Leia de forma defensiva, por exemplo ctype = properties.get("content_type", [None])[0]. Se a mensagem ficou numa fila antes da entrega, o broker envia o tempo de expiração restante, então o subscriber pode ver um valor menor que 60 — um jeito prático de verificar que o message expiry funciona. Para saber quando user properties são a ferramenta certa e quando não são, veja MQTT user properties.

Opções de assinatura

O MQTT 5 adiciona opções por assinatura. O gmqtt as expõe por meio de objetos Subscription:

Opções de assinatura
from gmqtt import Subscription

client.subscribe(
    [
        Subscription("testmqtt/gmqtt/+/temperature", qos=1),
        Subscription("testmqtt/gmqtt/chat/#", qos=1, no_local=True),
        Subscription("testmqtt/gmqtt/config/#", qos=1, retain_handling_options=2),
    ],
    subscription_identifier=7,
)
OpçãoEfeito
no_local=TrueO broker não devolve suas próprias publicações para você — ideal para topics estilo chat
retain_as_published=TrueMantém o flag retain original nas mensagens encaminhadas (útil para bridges)
retain_handling_options0 envia retained ao assinar, 1 só em assinaturas novas, 2 nunca
subscription_identifierUm número que o broker anexa a cada mensagem correspondente, para você saber de qual assinatura ela veio

Quando uma mensagem corresponde à assinatura acima, properties["subscription_identifier"] contém [7]. Isso torna o roteamento em on_message barato e explícito, sem comparar strings de topic de novo.

Request/response com response topic e correlation data

O MQTT 5 padroniza request/response: quem pede define um response topic e correlation data; quem responde publica a resposta ali e devolve a correlation data.

responder.py (callbacks)
def on_connect(client, flags, rc, properties):
    client.subscribe("testmqtt/gmqtt/rpc/time", qos=1)

def on_message(client, topic, payload, qos, properties):
    reply_to = properties.get("response_topic", [None])[0]
    corr = properties.get("correlation_data", [None])[0]
    if reply_to:
        client.publish(reply_to, str(time.time()), qos=1, correlation_data=corr)
    return 0
requester.py (dentro de main)
import uuid

corr = uuid.uuid4().bytes
client.subscribe("testmqtt/gmqtt/rpc/replies/client-01", qos=1)
client.publish(
    "testmqtt/gmqtt/rpc/time", "",
    qos=1,
    response_topic="testmqtt/gmqtt/rpc/replies/client-01",
    correlation_data=corr,
)

No on_message do requisitante, compare properties["correlation_data"][0] com corr para associar a resposta à requisição. O padrão completo, com timeouts, está em MQTT request/response.

Reason codes e desconexões limpas

O MQTT 5 substitui os antigos códigos de retorno de um byte por reason codes detalhados em CONNACK, SUBACK, PUBACK e DISCONNECT. No gmqtt você os vê em alguns lugares:

  • rc em on_connect — 0 é sucesso; valores como 134 (Bad User Name or Password) ou 135 (Not authorized) explicam uma conexão recusada.
  • A tupla qos em on_subscribe(client, mid, qos, properties) — um valor 128 ou maior para um topic significa que o broker recusou aquela assinatura, normalmente por causa de uma ACL.
  • O valor de retorno de on_message é usado como reason code no PUBACK/PUBREC que você envia de volta; 0 significa sucesso.

Consulte qualquer número na busca de reason codes do MQTT.

Para sair deixando o Last Will ser enviado mesmo assim (útil para testar o monitoramento), desconecte com reason code 4: await client.disconnect(reason_code=4). O Will em si é configurado com um objeto Message, opcionalmente com will delay interval:

Mensagem Will
from gmqtt import Client as MQTTClient, Message

will = Message("testmqtt/gmqtt/sensor-1/status", "offline", qos=1, retain=True, will_delay_interval=10)
client = MQTTClient("testmqtt-gmqtt-sensor-1", will_message=will)

Reconexões e session expiry

Diferente do aiomqtt, o gmqtt reconecta automaticamente. Você pode ajustar quantas vezes ele tenta e quanto espera:

Configuração de reconexão
client.set_config({"reconnect_retries": 10, "reconnect_delay": 5})

Combine reconexões com session_expiry_interval e um client ID fixo: o broker mantém suas assinaturas e enfileira mensagens QoS 1 enquanto você está fora, e os flags do CONNACK dizem se uma sessão foi retomada. Numa nova conexão, flags em on_connect indica session present; se for 0 depois de uma queda, a sessão expirou e você precisa assinar de novo (assinar em on_connect, como acima, resolve isso). Para verificar na prática, veja testar session expiry.

Testando seu cliente gmqtt

  1. Rode o subscriber do primeiro exemplo.
  2. Abra o cliente MQTT online, conecte em broker.connectmq.com com MQTT 5 e publique em testmqtt/gmqtt/sensor-1/temperature com uma user property e um message expiry.
  3. Confirme que o dicionário de propriedades no terminal contém exatamente o que você enviou.
  4. Pare o subscriber, publique uma mensagem QoS 1 e inicie-o de novo dentro da janela de session expiry — a mensagem deve chegar na reconexão.

Publique mensagens MQTT 5 para o seu cliente gmqtt

gmqtt vs outros clientes Python

BibliotecaEstiloErgonomia MQTT 5Reconexão automática
gmqttasyncio, callbacksPropriedades como argumentos nomeadosSim
aiomqttasyncio, async forObjetos Properties do pahoNão — loop em volta do async with (guia)
paho-mqtt 2.xthreads ou loop manualObjetos PropertiesSim, com loop_start() / loop_forever()

O gmqtt é uma boa escolha quando os metadados do MQTT 5 são centrais no seu design — request/response, content types, expiração — e seu código já é assíncrono. Como é mantido por uma equipe menor que a do paho-mqtt, confira os releases recentes e fixe a versão com a qual você testa. Para o panorama geral, veja MQTT 5 vs 3.1.1.

O gmqtt faz o MQTT 5 parecer Python normal: propriedades são argumentos, resultados são dicionários e reconexões acontecem sozinhas. Imprima uma vez as propriedades do CONNACK, leia os valores das propriedades como listas e verifique cada recurso com um segundo cliente antes de depender dele em produção.

Perguntas frequentes

O gmqtt suporta MQTT 3.1.1?

Sim. O gmqtt conecta com MQTT 5 por padrão e pode voltar para 3.1.1 se o broker rejeitar a versão 5. Você também pode forçar uma versão com version=MQTTv311 em connect().

Por que os valores das propriedades no gmqtt são listas?

Algumas propriedades do MQTT 5, como user properties e subscription identifiers, podem aparecer mais de uma vez em um pacote. Por isso o gmqtt retorna os valores como listas: leia properties['content_type'][0] em vez do valor direto.

Devo usar gmqtt ou aiomqtt?

Ambos são clientes asyncio. O gmqtt expõe as propriedades do MQTT 5 como argumentos nomeados e reconecta sozinho; o aiomqtt tem uma API mais pythônica com async with/async for sobre o paho-mqtt. Confira a atividade recente de releases de cada projeto antes de escolher.