%global _empty_manifest_terminate_build 0 Name: python-asyncio-mqtt Version: 0.16.1 Release: 1 Summary: Idiomatic asyncio wrapper around paho-mqtt License: BSD 3-Clause License URL: https://pypi.org/project/asyncio-mqtt/ Source0: https://mirrors.nju.edu.cn/pypi/web/packages/b9/ae/bb75e07de548e883ef0b813f139613f13ac43e79874b1365af21572a7eaa/asyncio_mqtt-0.16.1.tar.gz BuildArch: noarch Requires: python3-paho-mqtt Requires: python3-typing-extensions Requires: python3-black Requires: python3-mypy Requires: python3-ruff Requires: python3-types-paho-mqtt Requires: python3-pytest Requires: python3-pytest-cov Requires: python3-anyio %description

Idiomatic asyncio MQTT Client 🙌

License: BSD-3-Clause PyPI version Supported Python versions PyPI downloads Coverage Coverage pre-commit.ci status Typing: strict Code Style: Black

Write code like this: **Subscriber** ```python async with Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) ``` **Publisher** ```python async with Client("test.mosquitto.org") as client: await client.publish("humidity/outside", payload=0.38) ``` _asyncio-mqtt_ combines the stability of the time-proven [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) library with a modern, asyncio-based interface. - No more callbacks! 👍 - No more return codes (welcome to the `MqttError`) - Graceful disconnection (forget about `on_unsubscribe`, `on_disconnect`, etc.) - Compatible with `async` code - Fully type-hinted - Did we mention no more callbacks? The whole thing is less than [700 lines of code](asyncio-mqtt/client.py). ## Contents 🔍 - [Installation](#installation-) - [Note for Windows users](#note-for-windows-users) - [Advanced usage](#advanced-usage-) - [Configuring the client](#configuring-the-client) - [Filtering messages](#filtering-messages) - [Sharing the connection](#sharing-the-connection) - [Side by side with web frameworks](#side-by-side-with-web-frameworks) - [Why can't I `connect`/`disconnect` manually?](#why-cant-i-connectdisconnect-manually) - [Listening without blocking](#listening-without-blocking) - [Reconnecting](#reconnecting) - [TLS](#tls) - [Proxying](#proxying) - [License](#license-) - [Versioning](#versioning-) - [Changelog](#changelog-) - [Related projects](#related-projects-) ## Installation 📚 _asyncio-mqtt_ can be installed via `pip install asyncio-mqtt`. It requires Python 3.7+ to run. The only dependency is [paho-mqtt](https://github.com/eclipse/paho.mqtt.python). If you can't wait for the latest version and want to install directly from GitHub, use: `pip install git+https://github.com/sbtinstruments/asyncio-mqtt` ### Note for Windows users Since Python 3.8, the default asyncio event loop is the `ProactorEventLoop`. Said loop [doesn't support the `add_reader` method](https://docs.python.org/3/library/asyncio-platforms.html#windows) that is required by _asyncio-mqtt_. Please switch to an event loop that supports the `add_reader` method such as the built-in `SelectorEventLoop`: ```python # Change to the "Selector" event loop asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # Run your async application as usual asyncio.run(main()) ``` ## Advanced usage ⚡ Let's make the example from before more interesting: ### Configuring the client You can configure quite a few things when initializing the client. These are all the possible parameters together with their default values. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio_mqtt as aiomqtt import paho.mqtt as mqtt aiomqtt.Client( hostname="test.mosquitto.org", # The only non-optional parameter port=1883, username=None, password=None, logger=None, client_id=None, tls_context=None, tls_params=None, proxy=None, protocol=None, will=None, clean_session=None, transport="tcp", keepalive=60, bind_address="", bind_port=0, clean_start=mqtt.client.MQTT_CLEAN_START_FIRST_ONLY, properties=None, message_retry_set=20, socket_options=(), max_concurrent_outgoing_calls=None, websocket_path=None, websocket_headers=None, ) ``` ### Filtering messages Imagine you're measuring temperature and humidity on the outside and inside, and our topics look like this: `temperature/outside`. You want to receive all types of measurements but handle them differently. _asyncio-mqtt_ provides `Topic.matches()` to make this easy: ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("#") async for message in messages: if message.topic.matches("humidity/outside"): print(f"[humidity/outside] {message.payload}") if message.topic.matches("+/inside"): print(f"[+/inside] {message.payload}") if message.topic.matches("temperature/#"): print(f"[temperature/#] {message.payload}") asyncio.run(main()) ``` Note that in our example, messages to `temperature/inside` are handled twice! ### Sharing the connection In many cases, you'll want to send and receive messages in different locations in your code. You could create a new client each time, but 1. this is not very performant, and 2. you'll use a lot more network bandwidth. You can share the connection by passing the `Client` instance to all functions that need it: ```python import asyncio import asyncio_mqtt as aiomqtt async def publish_humidity(client): await client.publish("humidity/outside", payload=0.38) async def publish_temperature(client): await client.publish("temperature/outside", payload=28.3) async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: await publish_humidity(client) await publish_temperature(client) asyncio.run(main()) ``` #### Side by side with web frameworks Most web frameworks take control over the "main" function, which makes it difficult to figure out where to create and connect the `Client` and how to share this connection. Some frameworks like [Starlette](https://github.com/encode/starlette) directly support lifespan context managers, with which you can safely set up a global client instance that you can then pass to functions that need it, just like before: ```python import asyncio import asyncio_mqtt as aiomqtt import starlette.applications import contextlib client = None @contextlib.asynccontextmanager async def lifespan(app): global client async with aiomqtt.Client("test.mosquitto.org") as c: client = c yield app = starlette.applications.Starlette( routes=[], lifespan=lifespan, ) ``` FastAPI (which is built upon Starlette) doesn't expose that API yet, but there are [multiple](https://github.com/tiangolo/fastapi/pull/5503) [open PRs](https://github.com/tiangolo/fastapi/pull/2944) to add it. In the meantime, you can work around it via FastAPI's dependency injection. #### Why can't I `connect`/`disconnect` manually? Managing a connection by calling `connect` and `disconnect` directly is a bit tricky. For example, when you're disconnecting the client, you'd have to make sure that there's no other task that still relies on the connection. There are many similar situations where something can easily go wrong. Context managers take care of all connection and disconnection logic for you, in a way that makes it very difficult to shoot yourself in the foot. They are a lot easier and less error-prone to use than `connect`/`disconnect`. Supporting both context managers and manual `connect`/`disconnect` would add a lot of complexity to _asyncio-mqtt_. To keep maintainer burden manageable, we focus only on the preferred option: context managers. ### Listening without blocking If you run the basic example for subscribing and listening for messages, you'll notice that the program doesn't finish until you stop it. Waiting for messages blocks the execution of everything that comes afterwards. If you want to run other code after starting your listener (e.g. handling HTTP requests in a web framework) you don't want this. To solve this, you can use asyncio's `create_task` without `await`ing the created task. The concept is similar to starting a new thread without `join`ing it in a multithreaded application. ```python import asyncio import asyncio_mqtt as aiomqtt async def listen(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) async def main(): # Wait for messages in (unawaited) asyncio task loop = asyncio.get_event_loop() task = loop.create_task(listen()) # This will still run! print("Magic!") # If you don't await the task here the program will simply finish. # However, if you're using an async web framework you usually don't have to await # the task, as the framework runs in an endless loop. await task asyncio.run(main()) ``` ### Reconnecting You can reconnect when the connection to the broker is lost by wrapping your code in a `try/except`-block and listening for `MqttError`s. ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): reconnect_interval = 5 # In seconds while True: try: async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload.decode()) except aiomqtt.MqttError as error: print(f'Error "{error}". Reconnecting in {reconnect_interval} seconds.') await asyncio.sleep(reconnect_interval) asyncio.run(main()) ``` ### TLS You can configure TLS via the `TLSParameters` class. The parameters are directly passed through to paho-mqtt's `tls_set` function. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import ssl tls_params = aiomqtt.TLSParameters( ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS, ciphers=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", tls_params=tls_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ### Proxying You can configure proxying via the `ProxySettings` class. The parameters are directly passed through to paho-mqtt's `proxy_set` functionality. Both SOCKS and HTTP proxies are supported. Note that proxying is an extra feature (even in paho-mqtt) that requires the `PySocks` dependency. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import socks proxy_params = aiomqtt.ProxySettings( proxy_type=socks.HTTP, proxy_addr="www.example.com", proxy_rdns=True, proxy_username=None, proxy_password=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", proxy=proxy_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ## License 📋 License: BSD-3-Clause Note that the underlying paho-mqtt library is dual-licensed. One of the licenses is the so-called [Eclipse Distribution License v1.0](https://www.eclipse.org/org/documents/edl-v10.php). It is almost word-for-word identical to the [BSD 3-clause License](https://opensource.org/licenses/BSD-3-Clause). The only differences are: - One use of "COPYRIGHT OWNER" (EDL) instead of "COPYRIGHT HOLDER" (BSD) - One use of "Eclipse Foundation, Inc." (EDL) instead of "copyright holder" (BSD) ## Versioning 🎯 PyPI This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Expect API changes until we reach version `1.0.0`. After `1.0.0`, breaking changes will only occur in major release (e.g., `2.0.0`, `3.0.0`, etc.). ## Changelog 🚧 Please refer to the [CHANGELOG](https://github.com/sbtinstruments/asyncio-mqtt/blob/master/CHANGELOG.md) document. It adheres to the principles of [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Related projects 🌟 Is _asyncio-mqtt_ not what you are looking for? Try another client: - [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) — Own protocol implementation. Synchronous.
![GitHub stars](https://img.shields.io/github/stars/eclipse/paho.mqtt.python) ![license](https://img.shields.io/github/license/eclipse/paho.mqtt.python) - [gmqtt](https://github.com/wialon/gmqtt) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/wialon/gmqtt) ![license](https://img.shields.io/github/license/wialon/gmqtt) - [fastapi-mqtt](https://github.com/sabuhish/fastapi-mqtt) — Asynchronous wrapper around gmqtt. Simplifies integration in your FastAPI application.
![GitHub stars](https://img.shields.io/github/stars/sabuhish/fastapi-mqtt) ![license](https://img.shields.io/github/license/sabuhish/fastapi-mqtt) - [amqtt](https://github.com/Yakifo/amqtt) — Own protocol implementation. Asynchronous. Includes a broker.
![GitHub stars](https://img.shields.io/github/stars/Yakifo/amqtt) ![license](https://img.shields.io/github/license/Yakifo/amqtt) - [mqttools](https://github.com/eerimoq/mqttools) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/eerimoq/mqttools) ![license](https://img.shields.io/github/license/eerimoq/mqttools) - [trio-paho-mqtt](https://github.com/bkanuka/trio-paho-mqtt) — Asynchronous wrapper around paho-mqtt (similar to _asyncio-mqtt_). Based on trio instead of asyncio.
![GitHub stars](https://img.shields.io/github/stars/bkanuka/trio-paho-mqtt) ![license](https://img.shields.io/github/license/bkanuka/trio-paho-mqtt) %package -n python3-asyncio-mqtt Summary: Idiomatic asyncio wrapper around paho-mqtt Provides: python-asyncio-mqtt BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-asyncio-mqtt

Idiomatic asyncio MQTT Client 🙌

License: BSD-3-Clause PyPI version Supported Python versions PyPI downloads Coverage Coverage pre-commit.ci status Typing: strict Code Style: Black

Write code like this: **Subscriber** ```python async with Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) ``` **Publisher** ```python async with Client("test.mosquitto.org") as client: await client.publish("humidity/outside", payload=0.38) ``` _asyncio-mqtt_ combines the stability of the time-proven [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) library with a modern, asyncio-based interface. - No more callbacks! 👍 - No more return codes (welcome to the `MqttError`) - Graceful disconnection (forget about `on_unsubscribe`, `on_disconnect`, etc.) - Compatible with `async` code - Fully type-hinted - Did we mention no more callbacks? The whole thing is less than [700 lines of code](asyncio-mqtt/client.py). ## Contents 🔍 - [Installation](#installation-) - [Note for Windows users](#note-for-windows-users) - [Advanced usage](#advanced-usage-) - [Configuring the client](#configuring-the-client) - [Filtering messages](#filtering-messages) - [Sharing the connection](#sharing-the-connection) - [Side by side with web frameworks](#side-by-side-with-web-frameworks) - [Why can't I `connect`/`disconnect` manually?](#why-cant-i-connectdisconnect-manually) - [Listening without blocking](#listening-without-blocking) - [Reconnecting](#reconnecting) - [TLS](#tls) - [Proxying](#proxying) - [License](#license-) - [Versioning](#versioning-) - [Changelog](#changelog-) - [Related projects](#related-projects-) ## Installation 📚 _asyncio-mqtt_ can be installed via `pip install asyncio-mqtt`. It requires Python 3.7+ to run. The only dependency is [paho-mqtt](https://github.com/eclipse/paho.mqtt.python). If you can't wait for the latest version and want to install directly from GitHub, use: `pip install git+https://github.com/sbtinstruments/asyncio-mqtt` ### Note for Windows users Since Python 3.8, the default asyncio event loop is the `ProactorEventLoop`. Said loop [doesn't support the `add_reader` method](https://docs.python.org/3/library/asyncio-platforms.html#windows) that is required by _asyncio-mqtt_. Please switch to an event loop that supports the `add_reader` method such as the built-in `SelectorEventLoop`: ```python # Change to the "Selector" event loop asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # Run your async application as usual asyncio.run(main()) ``` ## Advanced usage ⚡ Let's make the example from before more interesting: ### Configuring the client You can configure quite a few things when initializing the client. These are all the possible parameters together with their default values. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio_mqtt as aiomqtt import paho.mqtt as mqtt aiomqtt.Client( hostname="test.mosquitto.org", # The only non-optional parameter port=1883, username=None, password=None, logger=None, client_id=None, tls_context=None, tls_params=None, proxy=None, protocol=None, will=None, clean_session=None, transport="tcp", keepalive=60, bind_address="", bind_port=0, clean_start=mqtt.client.MQTT_CLEAN_START_FIRST_ONLY, properties=None, message_retry_set=20, socket_options=(), max_concurrent_outgoing_calls=None, websocket_path=None, websocket_headers=None, ) ``` ### Filtering messages Imagine you're measuring temperature and humidity on the outside and inside, and our topics look like this: `temperature/outside`. You want to receive all types of measurements but handle them differently. _asyncio-mqtt_ provides `Topic.matches()` to make this easy: ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("#") async for message in messages: if message.topic.matches("humidity/outside"): print(f"[humidity/outside] {message.payload}") if message.topic.matches("+/inside"): print(f"[+/inside] {message.payload}") if message.topic.matches("temperature/#"): print(f"[temperature/#] {message.payload}") asyncio.run(main()) ``` Note that in our example, messages to `temperature/inside` are handled twice! ### Sharing the connection In many cases, you'll want to send and receive messages in different locations in your code. You could create a new client each time, but 1. this is not very performant, and 2. you'll use a lot more network bandwidth. You can share the connection by passing the `Client` instance to all functions that need it: ```python import asyncio import asyncio_mqtt as aiomqtt async def publish_humidity(client): await client.publish("humidity/outside", payload=0.38) async def publish_temperature(client): await client.publish("temperature/outside", payload=28.3) async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: await publish_humidity(client) await publish_temperature(client) asyncio.run(main()) ``` #### Side by side with web frameworks Most web frameworks take control over the "main" function, which makes it difficult to figure out where to create and connect the `Client` and how to share this connection. Some frameworks like [Starlette](https://github.com/encode/starlette) directly support lifespan context managers, with which you can safely set up a global client instance that you can then pass to functions that need it, just like before: ```python import asyncio import asyncio_mqtt as aiomqtt import starlette.applications import contextlib client = None @contextlib.asynccontextmanager async def lifespan(app): global client async with aiomqtt.Client("test.mosquitto.org") as c: client = c yield app = starlette.applications.Starlette( routes=[], lifespan=lifespan, ) ``` FastAPI (which is built upon Starlette) doesn't expose that API yet, but there are [multiple](https://github.com/tiangolo/fastapi/pull/5503) [open PRs](https://github.com/tiangolo/fastapi/pull/2944) to add it. In the meantime, you can work around it via FastAPI's dependency injection. #### Why can't I `connect`/`disconnect` manually? Managing a connection by calling `connect` and `disconnect` directly is a bit tricky. For example, when you're disconnecting the client, you'd have to make sure that there's no other task that still relies on the connection. There are many similar situations where something can easily go wrong. Context managers take care of all connection and disconnection logic for you, in a way that makes it very difficult to shoot yourself in the foot. They are a lot easier and less error-prone to use than `connect`/`disconnect`. Supporting both context managers and manual `connect`/`disconnect` would add a lot of complexity to _asyncio-mqtt_. To keep maintainer burden manageable, we focus only on the preferred option: context managers. ### Listening without blocking If you run the basic example for subscribing and listening for messages, you'll notice that the program doesn't finish until you stop it. Waiting for messages blocks the execution of everything that comes afterwards. If you want to run other code after starting your listener (e.g. handling HTTP requests in a web framework) you don't want this. To solve this, you can use asyncio's `create_task` without `await`ing the created task. The concept is similar to starting a new thread without `join`ing it in a multithreaded application. ```python import asyncio import asyncio_mqtt as aiomqtt async def listen(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) async def main(): # Wait for messages in (unawaited) asyncio task loop = asyncio.get_event_loop() task = loop.create_task(listen()) # This will still run! print("Magic!") # If you don't await the task here the program will simply finish. # However, if you're using an async web framework you usually don't have to await # the task, as the framework runs in an endless loop. await task asyncio.run(main()) ``` ### Reconnecting You can reconnect when the connection to the broker is lost by wrapping your code in a `try/except`-block and listening for `MqttError`s. ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): reconnect_interval = 5 # In seconds while True: try: async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload.decode()) except aiomqtt.MqttError as error: print(f'Error "{error}". Reconnecting in {reconnect_interval} seconds.') await asyncio.sleep(reconnect_interval) asyncio.run(main()) ``` ### TLS You can configure TLS via the `TLSParameters` class. The parameters are directly passed through to paho-mqtt's `tls_set` function. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import ssl tls_params = aiomqtt.TLSParameters( ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS, ciphers=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", tls_params=tls_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ### Proxying You can configure proxying via the `ProxySettings` class. The parameters are directly passed through to paho-mqtt's `proxy_set` functionality. Both SOCKS and HTTP proxies are supported. Note that proxying is an extra feature (even in paho-mqtt) that requires the `PySocks` dependency. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import socks proxy_params = aiomqtt.ProxySettings( proxy_type=socks.HTTP, proxy_addr="www.example.com", proxy_rdns=True, proxy_username=None, proxy_password=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", proxy=proxy_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ## License 📋 License: BSD-3-Clause Note that the underlying paho-mqtt library is dual-licensed. One of the licenses is the so-called [Eclipse Distribution License v1.0](https://www.eclipse.org/org/documents/edl-v10.php). It is almost word-for-word identical to the [BSD 3-clause License](https://opensource.org/licenses/BSD-3-Clause). The only differences are: - One use of "COPYRIGHT OWNER" (EDL) instead of "COPYRIGHT HOLDER" (BSD) - One use of "Eclipse Foundation, Inc." (EDL) instead of "copyright holder" (BSD) ## Versioning 🎯 PyPI This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Expect API changes until we reach version `1.0.0`. After `1.0.0`, breaking changes will only occur in major release (e.g., `2.0.0`, `3.0.0`, etc.). ## Changelog 🚧 Please refer to the [CHANGELOG](https://github.com/sbtinstruments/asyncio-mqtt/blob/master/CHANGELOG.md) document. It adheres to the principles of [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Related projects 🌟 Is _asyncio-mqtt_ not what you are looking for? Try another client: - [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) — Own protocol implementation. Synchronous.
![GitHub stars](https://img.shields.io/github/stars/eclipse/paho.mqtt.python) ![license](https://img.shields.io/github/license/eclipse/paho.mqtt.python) - [gmqtt](https://github.com/wialon/gmqtt) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/wialon/gmqtt) ![license](https://img.shields.io/github/license/wialon/gmqtt) - [fastapi-mqtt](https://github.com/sabuhish/fastapi-mqtt) — Asynchronous wrapper around gmqtt. Simplifies integration in your FastAPI application.
![GitHub stars](https://img.shields.io/github/stars/sabuhish/fastapi-mqtt) ![license](https://img.shields.io/github/license/sabuhish/fastapi-mqtt) - [amqtt](https://github.com/Yakifo/amqtt) — Own protocol implementation. Asynchronous. Includes a broker.
![GitHub stars](https://img.shields.io/github/stars/Yakifo/amqtt) ![license](https://img.shields.io/github/license/Yakifo/amqtt) - [mqttools](https://github.com/eerimoq/mqttools) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/eerimoq/mqttools) ![license](https://img.shields.io/github/license/eerimoq/mqttools) - [trio-paho-mqtt](https://github.com/bkanuka/trio-paho-mqtt) — Asynchronous wrapper around paho-mqtt (similar to _asyncio-mqtt_). Based on trio instead of asyncio.
![GitHub stars](https://img.shields.io/github/stars/bkanuka/trio-paho-mqtt) ![license](https://img.shields.io/github/license/bkanuka/trio-paho-mqtt) %package help Summary: Development documents and examples for asyncio-mqtt Provides: python3-asyncio-mqtt-doc %description help

Idiomatic asyncio MQTT Client 🙌

License: BSD-3-Clause PyPI version Supported Python versions PyPI downloads Coverage Coverage pre-commit.ci status Typing: strict Code Style: Black

Write code like this: **Subscriber** ```python async with Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) ``` **Publisher** ```python async with Client("test.mosquitto.org") as client: await client.publish("humidity/outside", payload=0.38) ``` _asyncio-mqtt_ combines the stability of the time-proven [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) library with a modern, asyncio-based interface. - No more callbacks! 👍 - No more return codes (welcome to the `MqttError`) - Graceful disconnection (forget about `on_unsubscribe`, `on_disconnect`, etc.) - Compatible with `async` code - Fully type-hinted - Did we mention no more callbacks? The whole thing is less than [700 lines of code](asyncio-mqtt/client.py). ## Contents 🔍 - [Installation](#installation-) - [Note for Windows users](#note-for-windows-users) - [Advanced usage](#advanced-usage-) - [Configuring the client](#configuring-the-client) - [Filtering messages](#filtering-messages) - [Sharing the connection](#sharing-the-connection) - [Side by side with web frameworks](#side-by-side-with-web-frameworks) - [Why can't I `connect`/`disconnect` manually?](#why-cant-i-connectdisconnect-manually) - [Listening without blocking](#listening-without-blocking) - [Reconnecting](#reconnecting) - [TLS](#tls) - [Proxying](#proxying) - [License](#license-) - [Versioning](#versioning-) - [Changelog](#changelog-) - [Related projects](#related-projects-) ## Installation 📚 _asyncio-mqtt_ can be installed via `pip install asyncio-mqtt`. It requires Python 3.7+ to run. The only dependency is [paho-mqtt](https://github.com/eclipse/paho.mqtt.python). If you can't wait for the latest version and want to install directly from GitHub, use: `pip install git+https://github.com/sbtinstruments/asyncio-mqtt` ### Note for Windows users Since Python 3.8, the default asyncio event loop is the `ProactorEventLoop`. Said loop [doesn't support the `add_reader` method](https://docs.python.org/3/library/asyncio-platforms.html#windows) that is required by _asyncio-mqtt_. Please switch to an event loop that supports the `add_reader` method such as the built-in `SelectorEventLoop`: ```python # Change to the "Selector" event loop asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # Run your async application as usual asyncio.run(main()) ``` ## Advanced usage ⚡ Let's make the example from before more interesting: ### Configuring the client You can configure quite a few things when initializing the client. These are all the possible parameters together with their default values. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio_mqtt as aiomqtt import paho.mqtt as mqtt aiomqtt.Client( hostname="test.mosquitto.org", # The only non-optional parameter port=1883, username=None, password=None, logger=None, client_id=None, tls_context=None, tls_params=None, proxy=None, protocol=None, will=None, clean_session=None, transport="tcp", keepalive=60, bind_address="", bind_port=0, clean_start=mqtt.client.MQTT_CLEAN_START_FIRST_ONLY, properties=None, message_retry_set=20, socket_options=(), max_concurrent_outgoing_calls=None, websocket_path=None, websocket_headers=None, ) ``` ### Filtering messages Imagine you're measuring temperature and humidity on the outside and inside, and our topics look like this: `temperature/outside`. You want to receive all types of measurements but handle them differently. _asyncio-mqtt_ provides `Topic.matches()` to make this easy: ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("#") async for message in messages: if message.topic.matches("humidity/outside"): print(f"[humidity/outside] {message.payload}") if message.topic.matches("+/inside"): print(f"[+/inside] {message.payload}") if message.topic.matches("temperature/#"): print(f"[temperature/#] {message.payload}") asyncio.run(main()) ``` Note that in our example, messages to `temperature/inside` are handled twice! ### Sharing the connection In many cases, you'll want to send and receive messages in different locations in your code. You could create a new client each time, but 1. this is not very performant, and 2. you'll use a lot more network bandwidth. You can share the connection by passing the `Client` instance to all functions that need it: ```python import asyncio import asyncio_mqtt as aiomqtt async def publish_humidity(client): await client.publish("humidity/outside", payload=0.38) async def publish_temperature(client): await client.publish("temperature/outside", payload=28.3) async def main(): async with aiomqtt.Client("test.mosquitto.org") as client: await publish_humidity(client) await publish_temperature(client) asyncio.run(main()) ``` #### Side by side with web frameworks Most web frameworks take control over the "main" function, which makes it difficult to figure out where to create and connect the `Client` and how to share this connection. Some frameworks like [Starlette](https://github.com/encode/starlette) directly support lifespan context managers, with which you can safely set up a global client instance that you can then pass to functions that need it, just like before: ```python import asyncio import asyncio_mqtt as aiomqtt import starlette.applications import contextlib client = None @contextlib.asynccontextmanager async def lifespan(app): global client async with aiomqtt.Client("test.mosquitto.org") as c: client = c yield app = starlette.applications.Starlette( routes=[], lifespan=lifespan, ) ``` FastAPI (which is built upon Starlette) doesn't expose that API yet, but there are [multiple](https://github.com/tiangolo/fastapi/pull/5503) [open PRs](https://github.com/tiangolo/fastapi/pull/2944) to add it. In the meantime, you can work around it via FastAPI's dependency injection. #### Why can't I `connect`/`disconnect` manually? Managing a connection by calling `connect` and `disconnect` directly is a bit tricky. For example, when you're disconnecting the client, you'd have to make sure that there's no other task that still relies on the connection. There are many similar situations where something can easily go wrong. Context managers take care of all connection and disconnection logic for you, in a way that makes it very difficult to shoot yourself in the foot. They are a lot easier and less error-prone to use than `connect`/`disconnect`. Supporting both context managers and manual `connect`/`disconnect` would add a lot of complexity to _asyncio-mqtt_. To keep maintainer burden manageable, we focus only on the preferred option: context managers. ### Listening without blocking If you run the basic example for subscribing and listening for messages, you'll notice that the program doesn't finish until you stop it. Waiting for messages blocks the execution of everything that comes afterwards. If you want to run other code after starting your listener (e.g. handling HTTP requests in a web framework) you don't want this. To solve this, you can use asyncio's `create_task` without `await`ing the created task. The concept is similar to starting a new thread without `join`ing it in a multithreaded application. ```python import asyncio import asyncio_mqtt as aiomqtt async def listen(): async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload) async def main(): # Wait for messages in (unawaited) asyncio task loop = asyncio.get_event_loop() task = loop.create_task(listen()) # This will still run! print("Magic!") # If you don't await the task here the program will simply finish. # However, if you're using an async web framework you usually don't have to await # the task, as the framework runs in an endless loop. await task asyncio.run(main()) ``` ### Reconnecting You can reconnect when the connection to the broker is lost by wrapping your code in a `try/except`-block and listening for `MqttError`s. ```python import asyncio import asyncio_mqtt as aiomqtt async def main(): reconnect_interval = 5 # In seconds while True: try: async with aiomqtt.Client("test.mosquitto.org") as client: async with client.messages() as messages: await client.subscribe("humidity/#") async for message in messages: print(message.payload.decode()) except aiomqtt.MqttError as error: print(f'Error "{error}". Reconnecting in {reconnect_interval} seconds.') await asyncio.sleep(reconnect_interval) asyncio.run(main()) ``` ### TLS You can configure TLS via the `TLSParameters` class. The parameters are directly passed through to paho-mqtt's `tls_set` function. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import ssl tls_params = aiomqtt.TLSParameters( ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS, ciphers=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", tls_params=tls_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ### Proxying You can configure proxying via the `ProxySettings` class. The parameters are directly passed through to paho-mqtt's `proxy_set` functionality. Both SOCKS and HTTP proxies are supported. Note that proxying is an extra feature (even in paho-mqtt) that requires the `PySocks` dependency. See [paho-mqtt's documentation](https://github.com/eclipse/paho.mqtt.python) for more information about the individual parameters. ```python import asyncio import asyncio_mqtt as aiomqtt import socks proxy_params = aiomqtt.ProxySettings( proxy_type=socks.HTTP, proxy_addr="www.example.com", proxy_rdns=True, proxy_username=None, proxy_password=None, ) async def main(): async with aiomqtt.Client("test.mosquitto.org", proxy=proxy_params) as client: await client.publish("humidity/outside", payload=0.38) asyncio.run(main()) ``` ## License 📋 License: BSD-3-Clause Note that the underlying paho-mqtt library is dual-licensed. One of the licenses is the so-called [Eclipse Distribution License v1.0](https://www.eclipse.org/org/documents/edl-v10.php). It is almost word-for-word identical to the [BSD 3-clause License](https://opensource.org/licenses/BSD-3-Clause). The only differences are: - One use of "COPYRIGHT OWNER" (EDL) instead of "COPYRIGHT HOLDER" (BSD) - One use of "Eclipse Foundation, Inc." (EDL) instead of "copyright holder" (BSD) ## Versioning 🎯 PyPI This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Expect API changes until we reach version `1.0.0`. After `1.0.0`, breaking changes will only occur in major release (e.g., `2.0.0`, `3.0.0`, etc.). ## Changelog 🚧 Please refer to the [CHANGELOG](https://github.com/sbtinstruments/asyncio-mqtt/blob/master/CHANGELOG.md) document. It adheres to the principles of [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Related projects 🌟 Is _asyncio-mqtt_ not what you are looking for? Try another client: - [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) — Own protocol implementation. Synchronous.
![GitHub stars](https://img.shields.io/github/stars/eclipse/paho.mqtt.python) ![license](https://img.shields.io/github/license/eclipse/paho.mqtt.python) - [gmqtt](https://github.com/wialon/gmqtt) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/wialon/gmqtt) ![license](https://img.shields.io/github/license/wialon/gmqtt) - [fastapi-mqtt](https://github.com/sabuhish/fastapi-mqtt) — Asynchronous wrapper around gmqtt. Simplifies integration in your FastAPI application.
![GitHub stars](https://img.shields.io/github/stars/sabuhish/fastapi-mqtt) ![license](https://img.shields.io/github/license/sabuhish/fastapi-mqtt) - [amqtt](https://github.com/Yakifo/amqtt) — Own protocol implementation. Asynchronous. Includes a broker.
![GitHub stars](https://img.shields.io/github/stars/Yakifo/amqtt) ![license](https://img.shields.io/github/license/Yakifo/amqtt) - [mqttools](https://github.com/eerimoq/mqttools) — Own protocol implementation. Asynchronous.
![GitHub stars](https://img.shields.io/github/stars/eerimoq/mqttools) ![license](https://img.shields.io/github/license/eerimoq/mqttools) - [trio-paho-mqtt](https://github.com/bkanuka/trio-paho-mqtt) — Asynchronous wrapper around paho-mqtt (similar to _asyncio-mqtt_). Based on trio instead of asyncio.
![GitHub stars](https://img.shields.io/github/stars/bkanuka/trio-paho-mqtt) ![license](https://img.shields.io/github/license/bkanuka/trio-paho-mqtt) %prep %autosetup -n asyncio-mqtt-0.16.1 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-asyncio-mqtt -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Tue Apr 25 2023 Python_Bot - 0.16.1-1 - Package Spec generated