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
pip install gmqttO gmqtt usa callbacks executados no event loop do asyncio. Conecte, assine em on_connect e mantenha o programa vivo com um asyncio.Event:
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():
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:
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:
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ção | Efeito |
|---|---|
no_local=True | O broker não devolve suas próprias publicações para você — ideal para topics estilo chat |
retain_as_published=True | Mantém o flag retain original nas mensagens encaminhadas (útil para bridges) |
retain_handling_options | 0 envia retained ao assinar, 1 só em assinaturas novas, 2 nunca |
subscription_identifier | Um 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.
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 0import 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:
rcemon_connect—0é sucesso; valores como134(Bad User Name or Password) ou135(Not authorized) explicam uma conexão recusada.- A tupla
qosemon_subscribe(client, mid, qos, properties)— um valor128ou 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;0significa 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:
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:
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
- Rode o subscriber do primeiro exemplo.
- Abra o cliente MQTT online, conecte em
broker.connectmq.comcom MQTT 5 e publique emtestmqtt/gmqtt/sensor-1/temperaturecom uma user property e um message expiry. - Confirme que o dicionário de propriedades no terminal contém exatamente o que você enviou.
- 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
| Biblioteca | Estilo | Ergonomia MQTT 5 | Reconexão automática |
|---|---|---|---|
| gmqtt | asyncio, callbacks | Propriedades como argumentos nomeados | Sim |
| aiomqtt | asyncio, async for | Objetos Properties do paho | Não — loop em volta do async with (guia) |
| paho-mqtt 2.x | threads ou loop manual | Objetos Properties | Sim, 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.


