SolarManager unter Versionsverwaltung

Erster Stand der Hintergrundprozesse, die auf der Synology unter
/volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler
je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der
AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins
Web-Verzeichnis.

Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die
nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie
von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot),
zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das
Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat.

Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py,
gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso
gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte.

Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein
pip, sie muessen neben den Skripten liegen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 20:46:59 +02:00
co-authored by Claude Opus 5
commit 79843aa2ae
968 changed files with 261182 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
/env/
/build/
/develop-eggs/
/dist/
/downloads/
/eggs/
/.eggs/
/lib/
/lib64/
/parts/
/sdist/
/var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
MANIFEST
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
tests/ssl/demoCA
tests/ssl/rootCA
tests/ssl/signingCA
*.csr
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
paho.mqtt.testing
+114
View File
@@ -0,0 +1,114 @@
# Contributing to Paho
Thanks for your interest in this project!
You can contribute bugfixes and new features by sending pull requests through GitHub.
## Legal
In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy.
Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git).
1. Sign the [Eclipse CLA](http://www.eclipse.org/legal/CLA.php)
1. Register for an Eclipse Foundation User ID. You can register [here](https://dev.eclipse.org/site_login/createaccount.php).
2. Log into the [Projects Portal](https://projects.eclipse.org/), and click on the '[Eclipse CLA](https://projects.eclipse.org/user/sign/cla)' link.
2. Go to your [account settings](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings) and add your GitHub username to your account.
3. Make sure that you _sign-off_ your Git commits in the following format:
``` Signed-off-by: John Smith <johnsmith@nowhere.com> ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g. ```git commit -s -m "Adding a cool feature"```
4. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with.
## Contributing a change
1. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.python/fork)
2. Clone the forked repository onto your computer: ``` git clone
https://github.com/<your username>/paho.mqtt.python.git ```
3. Most changes will go to branch ``master``. This include both bug fixes and
new features. Bug fixes are committed to ``master`` and if required,
cherry-picked to the release branch.
The only changes that goes directly to the release branch (``1.4``,
``1.5``, ...) are bug fixes that does not apply to ``master`` (e.g. because
there are fixed on master by a refactoring, or any other huge change we do
not want to cherry-pick to the release branch).
4. Create a new branch from the latest ```master``` branch
with ```git checkout -b YOUR_BRANCH_NAME origin/master```
5. Make your changes
6. Ensure that all new and existing tests pass by running ```tox```
7. Commit the changes into the branch: ``` git commit -s ``` Make sure that
your commit message is meaningful and describes your changes correctly.
8. If you have a lot of commits for the change, squash them into a single / few
commits.
9. Push the changes in your branch to your forked repository.
10. Finally, go to
[https://github.com/eclipse/paho.mqtt.python](https://github.com/eclipse/paho.mqtt.python)
and create a pull request from your "YOUR_BRANCH_NAME" branch to the
``master`` (or release branch if applicable) to request review and
merge of the commits in your pushed branch.
What happens next depends on the content of the patch. If it is 100% authored
by the contributor and is less than 1000 lines (and meets the needs of the
project), then it can be pulled into the main repository. If not, more steps
are required. These are detailed in the
[legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf).
## Developer resources:
Information regarding source code management, builds, coding standards, and
more.
- [https://projects.eclipse.org/projects/iot.paho/developer](https://projects.eclipse.org/projects/iot.paho/developer)
Contact:
--------
Contact the project developers via the project's development
[mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
Search for bugs:
----------------
This project uses [Github](https://github.com/eclipse/paho.mqtt.python/issues)
to track ongoing development and issues.
Create a new bug:
-----------------
Be sure to search for existing bugs before you create another one. Remember
that contributions are always welcome!
- [Create new Paho bug](https://github.com/eclipse/paho.mqtt.python/issues)
## Committer resources:
Making a release
----------------
The process to make a release is the following:
* Update the Changelog with the release version and date. Ensure it's up-to-date with latest fixes & PRs merged.
* Make sure test pass, check that Github actions are green.
* Check that documentation build (`cd docs; make html`)
* Bump the version number in ``paho/mqtt/__init__.py``, commit the change.
* Make a dry-run of build:
* Build using hatch: ``python -m hatch build``
* Check with twine for common errors: ``python -m twine check dist/*``
* Try uploading it to testpypi: ``python3 -m twine upload --repository testpypi dist/*``
* Do a GPG signed tag (assuming your GPG is correctly configured, it's ``git tag -s -m "Version 1.2.3" v1.2.3``)
* Push the commit and it's tag to Github
* Make sure your git is clean, especially the ``dist/`` folder.
* Build a release: ``python -m hatch build``
* You can also get the latest build from Github action. It should be identical to your local build:
https://github.com/eclipse/paho.mqtt.python/actions/workflows/build.yml?query=branch%3Amaster
* Then upload the dist file, you can follow instruction on https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-the-distribution-archives
It should mostly be ``python -m twine upload dist/*``
* Create a release on Github, copy-pasting the release note from Changelog.
* Build and publish the documentation
* To build the documentation, run `make clean html` in `docs` folder
* Copy `_build/html/` to https://github.com/eclipse/paho-website/tree/master/files/paho.mqtt.python/html
* Announce the release on the Mailing list.
* To allow installing from a git clone, update the version in ``paho/mqtt/__init__.py`` to next number WITH .dev0 (example ``1.2.3.dev0``)
+3
View File
@@ -0,0 +1,3 @@
This project is dual licensed under the Eclipse Public License 2.0 and the
Eclipse Distribution License 1.0 as described in the epl-v20 and edl-v10 files.
+635
View File
@@ -0,0 +1,635 @@
Metadata-Version: 2.3
Name: paho-mqtt
Version: 2.1.0
Summary: MQTT version 5.0/3.1.1 client class
Project-URL: Homepage, http://eclipse.org/paho
Author-email: Roger Light <roger@atchoo.org>
License: EPL-2.0 OR BSD-3-Clause
License-File: LICENSE.txt
Keywords: paho
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved
Classifier: Natural Language :: English
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Communications
Classifier: Topic :: Internet
Requires-Python: >=3.7
Provides-Extra: proxy
Requires-Dist: pysocks; extra == 'proxy'
Description-Content-Type: text/x-rst
Eclipse Paho™ MQTT Python Client
================================
The `full documentation is available here <documentation_>`_.
**Warning breaking change** - Release 2.0 contains a breaking change; see the `release notes <https://github.com/eclipse/paho.mqtt.python/releases/tag/v2.0.0>`_ and `migration details <https://eclipse.dev/paho/files/paho.mqtt.python/html/migrations.html>`_.
This document describes the source code for the `Eclipse Paho <http://eclipse.org/paho/>`_ MQTT Python client library, which implements versions 5.0, 3.1.1, and 3.1 of the MQTT protocol.
This code provides a client class which enables applications to connect to an `MQTT <http://mqtt.org/>`_ broker to publish messages, and to subscribe to topics and receive published messages. It also provides some helper functions to make publishing one off messages to an MQTT server very straightforward.
It supports Python 3.7+.
The MQTT protocol is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. Designed as an extremely lightweight publish/subscribe messaging transport, it is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium.
Paho is an `Eclipse Foundation <https://www.eclipse.org/org/foundation/>`_ project.
Contents
--------
* Installation_
* `Known limitations`_
* `Usage and API`_
* `Getting Started`_
* `Client`_
* `Network loop`_
* `Callbacks`_
* `Logger`_
* `External event loop support`_
* `Global helper functions`_
* `Publish`_
* `Single`_
* `Multiple`_
* `Subscribe`_
* `Simple`_
* `Using Callback`_
* `Reporting bugs`_
* `More information`_
Installation
------------
The latest stable version is available in the Python Package Index (PyPi) and can be installed using
::
pip install paho-mqtt
Or with ``virtualenv``:
::
virtualenv paho-mqtt
source paho-mqtt/bin/activate
pip install paho-mqtt
To obtain the full code, including examples and tests, you can clone the git repository:
::
git clone https://github.com/eclipse/paho.mqtt.python
Once you have the code, it can be installed from your repository as well:
::
cd paho.mqtt.python
pip install -e .
To perform all tests (including MQTT v5 tests), you also need to clone paho.mqtt.testing in paho.mqtt.python folder::
git clone https://github.com/eclipse/paho.mqtt.testing.git
cd paho.mqtt.testing
git checkout a4dc694010217b291ee78ee13a6d1db812f9babd
Known limitations
-----------------
The following are the known unimplemented MQTT features.
When ``clean_session`` is False, the session is only stored in memory and not persisted. This means that
when the client is restarted (not just reconnected, the object is recreated usually because the
program was restarted) the session is lost. This results in a possible message loss.
The following part of the client session is lost:
* QoS 2 messages which have been received from the server, but have not been completely acknowledged.
Since the client will blindly acknowledge any PUBCOMP (last message of a QoS 2 transaction), it
won't hang but will lose this QoS 2 message.
* QoS 1 and QoS 2 messages which have been sent to the server, but have not been completely acknowledged.
This means that messages passed to ``publish()`` may be lost. This could be mitigated by taking care
that all messages passed to ``publish()`` have a corresponding ``on_publish()`` call or use `wait_for_publish`.
It also means that the broker may have the QoS2 message in the session. Since the client starts
with an empty session it don't know it and will reuse the mid. This is not yet fixed.
Also, when ``clean_session`` is True, this library will republish QoS > 0 message across network
reconnection. This means that QoS > 0 message won't be lost. But the standard says that
we should discard any message for which the publish packet was sent. Our choice means that
we are not compliant with the standard and it's possible for QoS 2 to be received twice.
You should set ``clean_session = False`` if you need the QoS 2 guarantee of only one delivery.
Usage and API
-------------
Detailed API documentation `is available online <documentation_>`_ or could be built from ``docs/`` and samples are available in the `examples`_ directory.
The package provides two modules, a full `Client` and few `helpers` for simple publishing or subscribing.
Getting Started
***************
Here is a very simple example that subscribes to the broker $SYS topic tree and prints out the resulting messages:
.. code:: python
import paho.mqtt.client as mqtt
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected with result code {reason_code}")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("$SYS/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
mqttc.loop_forever()
Client
******
You can use the client class as an instance, within a class or by subclassing. The general usage flow is as follows:
* Create a client instance
* Connect to a broker using one of the ``connect*()`` functions
* Call one of the ``loop*()`` functions to maintain network traffic flow with the broker
* Use ``subscribe()`` to subscribe to a topic and receive messages
* Use ``publish()`` to publish messages to the broker
* Use ``disconnect()`` to disconnect from the broker
Callbacks will be called to allow the application to process events as necessary. These callbacks are described below.
Network loop
````````````
These functions are the driving force behind the client. If they are not
called, incoming network data will not be processed and outgoing network data
will not be sent. There are four options for managing the
network loop. Three are described here, the fourth in "External event loop
support" below. Do not mix the different loop functions.
loop_start() / loop_stop()
''''''''''''''''''''''''''
.. code:: python
mqttc.loop_start()
while True:
temperature = sensor.blocking_read()
mqttc.publish("paho/temperature", temperature)
mqttc.loop_stop()
These functions implement a threaded interface to the network loop. Calling
`loop_start()` once, before or after ``connect*()``, runs a thread in the
background to call `loop()` automatically. This frees up the main thread for
other work that may be blocking. This call also handles reconnecting to the
broker. Call `loop_stop()` to stop the background thread.
The loop is also stopped if you call `disconnect()`.
loop_forever()
''''''''''''''
.. code:: python
mqttc.loop_forever(retry_first_connection=False)
This is a blocking form of the network loop and will not return until the
client calls `disconnect()`. It automatically handles reconnecting.
Except for the first connection attempt when using `connect_async`, use
``retry_first_connection=True`` to make it retry the first connection.
*Warning*: This might lead to situations where the client keeps connecting to an
non existing host without failing.
loop()
''''''
.. code:: python
run = True
while run:
rc = mqttc.loop(timeout=1.0)
if rc != 0:
# need to handle error, possible reconnecting or stopping the application
Call regularly to process network events. This call waits in ``select()`` until
the network socket is available for reading or writing, if appropriate, then
handles the incoming/outgoing data. This function blocks for up to ``timeout``
seconds. ``timeout`` must not exceed the ``keepalive`` value for the client or
your client will be regularly disconnected by the broker.
Using this kind of loop, require you to handle reconnection strategie.
Callbacks
`````````
The interface to interact with paho-mqtt include various callback that are called by
the library when some events occur.
The callbacks are functions defined in your code, to implement the require action on those events. This could
be simply printing received message or much more complex behaviour.
Callbacks API is versioned, and the selected version is the `CallbackAPIVersion` you provided to `Client`
constructor. Currently two version are supported:
* ``CallbackAPIVersion.VERSION1``: it's the historical version used in paho-mqtt before version 2.0.
It's the API used before the introduction of `CallbackAPIVersion`.
This version is deprecated and will be removed in paho-mqtt version 3.0.
* ``CallbackAPIVersion.VERSION2``: This version is more consistent between protocol MQTT 3.x and MQTT 5.x. It's also
much more usable with MQTT 5.x since reason code and properties are always provided when available.
It's recommended for all user to upgrade to this version. It's highly recommended for MQTT 5.x user.
The following callbacks exists:
* `on_connect()`: called when the CONNACK from the broker is received. The call could be for a refused connection,
check the reason_code to see if the connection is successful or rejected.
* `on_connect_fail()`: called by `loop_forever()` and `loop_start()` when the TCP connection failed to establish.
This callback is not called when using `connect()` or `reconnect()` directly. It's only called following
an automatic (re)connection made by `loop_start()` and `loop_forever()`
* `on_disconnect()`: called when the connection is closed.
* `on_message()`: called when a MQTT message is received from the broker.
* `on_publish()`: called when an MQTT message was sent to the broker. Depending on QoS level the callback is called
at different moment:
* For QoS == 0, it's called as soon as the message is sent over the network. This could be before the corresponding ``publish()`` return.
* For QoS == 1, it's called when the corresponding PUBACK is received from the broker
* For QoS == 2, it's called when the corresponding PUBCOMP is received from the broker
* `on_subscribe()`: called when the SUBACK is received from the broker
* `on_unsubscribe()`: called when the UNSUBACK is received from the broker
* `on_log()`: called when the library log a message
* `on_socket_open`, `on_socket_close`, `on_socket_register_write`, `on_socket_unregister_write`: callbacks used for external loop support. See below for details.
For the signature of each callback, see the `online documentation <documentation_>`_.
Subscriber example
''''''''''''''''''
.. code:: python
import paho.mqtt.client as mqtt
def on_subscribe(client, userdata, mid, reason_code_list, properties):
# Since we subscribed only for a single channel, reason_code_list contains
# a single entry
if reason_code_list[0].is_failure:
print(f"Broker rejected you subscription: {reason_code_list[0]}")
else:
print(f"Broker granted the following QoS: {reason_code_list[0].value}")
def on_unsubscribe(client, userdata, mid, reason_code_list, properties):
# Be careful, the reason_code_list is only present in MQTTv5.
# In MQTTv3 it will always be empty
if len(reason_code_list) == 0 or not reason_code_list[0].is_failure:
print("unsubscribe succeeded (if SUBACK is received in MQTTv3 it success)")
else:
print(f"Broker replied with failure: {reason_code_list[0]}")
client.disconnect()
def on_message(client, userdata, message):
# userdata is the structure we choose to provide, here it's a list()
userdata.append(message.payload)
# We only want to process 10 messages
if len(userdata) >= 10:
client.unsubscribe("$SYS/#")
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Failed to connect: {reason_code}. loop_forever() will retry connection")
else:
# we should always subscribe from on_connect callback to be sure
# our subscribed is persisted across reconnections.
client.subscribe("$SYS/#")
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc.on_unsubscribe = on_unsubscribe
mqttc.user_data_set([])
mqttc.connect("mqtt.eclipseprojects.io")
mqttc.loop_forever()
print(f"Received the following message: {mqttc.user_data_get()}")
publisher example
'''''''''''''''''
.. code:: python
import time
import paho.mqtt.client as mqtt
def on_publish(client, userdata, mid, reason_code, properties):
# reason_code and properties will only be present in MQTTv5. It's always unset in MQTTv3
try:
userdata.remove(mid)
except KeyError:
print("on_publish() is called with a mid not present in unacked_publish")
print("This is due to an unavoidable race-condition:")
print("* publish() return the mid of the message sent.")
print("* mid from publish() is added to unacked_publish by the main thread")
print("* on_publish() is called by the loop_start thread")
print("While unlikely (because on_publish() will be called after a network round-trip),")
print(" this is a race-condition that COULD happen")
print("")
print("The best solution to avoid race-condition is using the msg_info from publish()")
print("We could also try using a list of acknowledged mid rather than removing from pending list,")
print("but remember that mid could be re-used !")
unacked_publish = set()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_publish = on_publish
mqttc.user_data_set(unacked_publish)
mqttc.connect("mqtt.eclipseprojects.io")
mqttc.loop_start()
# Our application produce some messages
msg_info = mqttc.publish("paho/test/topic", "my message", qos=1)
unacked_publish.add(msg_info.mid)
msg_info2 = mqttc.publish("paho/test/topic", "my message2", qos=1)
unacked_publish.add(msg_info2.mid)
# Wait for all message to be published
while len(unacked_publish):
time.sleep(0.1)
# Due to race-condition described above, the following way to wait for all publish is safer
msg_info.wait_for_publish()
msg_info2.wait_for_publish()
mqttc.disconnect()
mqttc.loop_stop()
Logger
``````
The Client emit some log message that could be useful during troubleshooting. The easiest way to
enable logs is the call `enable_logger()`. It's possible to provide a custom logger or let the
default logger being used.
Example:
.. code:: python
import logging
import paho.mqtt.client as mqtt
logging.basicConfig(level=logging.DEBUG)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.enable_logger()
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_start()
# Do additional action needed, publish, subscribe, ...
[...]
It's also possible to define a on_log callback that will receive a copy of all log messages. Example:
.. code:: python
import paho.mqtt.client as mqtt
def on_log(client, userdata, paho_log_level, messages):
if paho_log_level == mqtt.LogLevel.MQTT_LOG_ERR:
print(message)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_start()
# Do additional action needed, publish, subscribe, ...
[...]
The correspondence with Paho logging levels and standard ones is the following:
==================== ===============
Paho logging
==================== ===============
``MQTT_LOG_ERR`` ``logging.ERROR``
``MQTT_LOG_WARNING`` ``logging.WARNING``
``MQTT_LOG_NOTICE`` ``logging.INFO`` *(no direct equivalent)*
``MQTT_LOG_INFO`` ``logging.INFO``
``MQTT_LOG_DEBUG`` ``logging.DEBUG``
==================== ===============
External event loop support
```````````````````````````
To support other network loop like asyncio (see examples_), the library expose some
method and callback to support those use-case.
The following loop method exists:
* `loop_read`: should be called when the socket is ready for reading.
* `loop_write`: should be called when the socket is ready for writing AND the library want to write data.
* `loop_misc`: should be called every few seconds to handle message retrying and pings.
In pseudo code, it give the following:
.. code:: python
while run:
if need_read:
mqttc.loop_read()
if need_write:
mqttc.loop_write()
mqttc.loop_misc()
if not need_read and not need_write:
# But don't wait more than few seconds, loop_misc() need to be called regularly
wait_for_change_in_need_read_or_write()
updated_need_read_and_write()
The tricky part is implementing the update of need_read / need_write and wait for condition change. To support
this, the following method exists:
* `socket()`: which return the socket object when the TCP connection is open.
This call is particularly useful for select_ based loops. See ``examples/loop_select.py``.
* `want_write()`: return true if there is data waiting to be written. This is close to the
``need_writew`` of above pseudo-code, but you should also check whether the socket is ready for writing.
* callbacks ``on_socket_*``:
* `on_socket_open`: called when the socket is opened.
* `on_socket_close`: called when the socket is about to be closed.
* `on_socket_register_write`: called when there is data the client want to write on the socket
* `on_socket_unregister_write`: called when there is no more data to write on the socket.
Callbacks are particularly useful for event loops where you register or unregister a socket
for reading+writing. See ``examples/loop_asyncio.py`` for an example.
.. _select: https://docs.python.org/3/library/select.html#select.select
The callbacks are always called in this order:
- `on_socket_open`
- Zero or more times:
- `on_socket_register_write`
- `on_socket_unregister_write`
- `on_socket_close`
Global helper functions
```````````````````````
The client module also offers some global helper functions.
``topic_matches_sub(sub, topic)`` can be used to check whether a ``topic``
matches a ``subscription``.
For example:
the topic ``foo/bar`` would match the subscription ``foo/#`` or ``+/bar``
the topic ``non/matching`` would not match the subscription ``non/+/+``
Publish
*******
This module provides some helper functions to allow straightforward publishing
of messages in a one-shot manner. In other words, they are useful for the
situation where you have a single/multiple messages you want to publish to a
broker, then disconnect with nothing else required.
The two functions provided are `single()` and `multiple()`.
Both functions include support for MQTT v5.0, but do not currently let you
set any properties on connection or when sending messages.
Single
``````
Publish a single message to a broker, then disconnect cleanly.
Example:
.. code:: python
import paho.mqtt.publish as publish
publish.single("paho/test/topic", "payload", hostname="mqtt.eclipseprojects.io")
Multiple
````````
Publish multiple messages to a broker, then disconnect cleanly.
Example:
.. code:: python
from paho.mqtt.enums import MQTTProtocolVersion
import paho.mqtt.publish as publish
msgs = [{'topic':"paho/test/topic", 'payload':"multiple 1"},
("paho/test/topic", "multiple 2", 0, False)]
publish.multiple(msgs, hostname="mqtt.eclipseprojects.io", protocol=MQTTProtocolVersion.MQTTv5)
Subscribe
*********
This module provides some helper functions to allow straightforward subscribing
and processing of messages.
The two functions provided are `simple()` and `callback()`.
Both functions include support for MQTT v5.0, but do not currently let you
set any properties on connection or when subscribing.
Simple
``````
Subscribe to a set of topics and return the messages received. This is a
blocking function.
Example:
.. code:: python
import paho.mqtt.subscribe as subscribe
msg = subscribe.simple("paho/test/topic", hostname="mqtt.eclipseprojects.io")
print("%s %s" % (msg.topic, msg.payload))
Using Callback
``````````````
Subscribe to a set of topics and process the messages received using a user
provided callback.
Example:
.. code:: python
import paho.mqtt.subscribe as subscribe
def on_message_print(client, userdata, message):
print("%s %s" % (message.topic, message.payload))
userdata["message_count"] += 1
if userdata["message_count"] >= 5:
# it's possible to stop the program by disconnecting
client.disconnect()
subscribe.callback(on_message_print, "paho/test/topic", hostname="mqtt.eclipseprojects.io", userdata={"message_count": 0})
Reporting bugs
--------------
Please report bugs in the issues tracker at https://github.com/eclipse/paho.mqtt.python/issues.
More information
----------------
Discussion of the Paho clients takes place on the `Eclipse paho-dev mailing list <https://dev.eclipse.org/mailman/listinfo/paho-dev>`_.
General questions about the MQTT protocol itself (not this library) are discussed in the `MQTT Google Group <https://groups.google.com/forum/?fromgroups#!forum/mqtt>`_.
There is much more information available via the `MQTT community site <http://mqtt.org/>`_.
.. _examples: https://github.com/eclipse/paho.mqtt.python/tree/master/examples
.. _documentation: https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html
+606
View File
@@ -0,0 +1,606 @@
Eclipse Paho™ MQTT Python Client
================================
The `full documentation is available here <documentation_>`_.
**Warning breaking change** - Release 2.0 contains a breaking change; see the `release notes <https://github.com/eclipse/paho.mqtt.python/releases/tag/v2.0.0>`_ and `migration details <https://eclipse.dev/paho/files/paho.mqtt.python/html/migrations.html>`_.
This document describes the source code for the `Eclipse Paho <http://eclipse.org/paho/>`_ MQTT Python client library, which implements versions 5.0, 3.1.1, and 3.1 of the MQTT protocol.
This code provides a client class which enables applications to connect to an `MQTT <http://mqtt.org/>`_ broker to publish messages, and to subscribe to topics and receive published messages. It also provides some helper functions to make publishing one off messages to an MQTT server very straightforward.
It supports Python 3.7+.
The MQTT protocol is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. Designed as an extremely lightweight publish/subscribe messaging transport, it is useful for connections with remote locations where a small code footprint is required and/or network bandwidth is at a premium.
Paho is an `Eclipse Foundation <https://www.eclipse.org/org/foundation/>`_ project.
Contents
--------
* Installation_
* `Known limitations`_
* `Usage and API`_
* `Getting Started`_
* `Client`_
* `Network loop`_
* `Callbacks`_
* `Logger`_
* `External event loop support`_
* `Global helper functions`_
* `Publish`_
* `Single`_
* `Multiple`_
* `Subscribe`_
* `Simple`_
* `Using Callback`_
* `Reporting bugs`_
* `More information`_
Installation
------------
The latest stable version is available in the Python Package Index (PyPi) and can be installed using
::
pip install paho-mqtt
Or with ``virtualenv``:
::
virtualenv paho-mqtt
source paho-mqtt/bin/activate
pip install paho-mqtt
To obtain the full code, including examples and tests, you can clone the git repository:
::
git clone https://github.com/eclipse/paho.mqtt.python
Once you have the code, it can be installed from your repository as well:
::
cd paho.mqtt.python
pip install -e .
To perform all tests (including MQTT v5 tests), you also need to clone paho.mqtt.testing in paho.mqtt.python folder::
git clone https://github.com/eclipse/paho.mqtt.testing.git
cd paho.mqtt.testing
git checkout a4dc694010217b291ee78ee13a6d1db812f9babd
Known limitations
-----------------
The following are the known unimplemented MQTT features.
When ``clean_session`` is False, the session is only stored in memory and not persisted. This means that
when the client is restarted (not just reconnected, the object is recreated usually because the
program was restarted) the session is lost. This results in a possible message loss.
The following part of the client session is lost:
* QoS 2 messages which have been received from the server, but have not been completely acknowledged.
Since the client will blindly acknowledge any PUBCOMP (last message of a QoS 2 transaction), it
won't hang but will lose this QoS 2 message.
* QoS 1 and QoS 2 messages which have been sent to the server, but have not been completely acknowledged.
This means that messages passed to ``publish()`` may be lost. This could be mitigated by taking care
that all messages passed to ``publish()`` have a corresponding ``on_publish()`` call or use `wait_for_publish`.
It also means that the broker may have the QoS2 message in the session. Since the client starts
with an empty session it don't know it and will reuse the mid. This is not yet fixed.
Also, when ``clean_session`` is True, this library will republish QoS > 0 message across network
reconnection. This means that QoS > 0 message won't be lost. But the standard says that
we should discard any message for which the publish packet was sent. Our choice means that
we are not compliant with the standard and it's possible for QoS 2 to be received twice.
You should set ``clean_session = False`` if you need the QoS 2 guarantee of only one delivery.
Usage and API
-------------
Detailed API documentation `is available online <documentation_>`_ or could be built from ``docs/`` and samples are available in the `examples`_ directory.
The package provides two modules, a full `Client` and few `helpers` for simple publishing or subscribing.
Getting Started
***************
Here is a very simple example that subscribes to the broker $SYS topic tree and prints out the resulting messages:
.. code:: python
import paho.mqtt.client as mqtt
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected with result code {reason_code}")
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("$SYS/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
mqttc.loop_forever()
Client
******
You can use the client class as an instance, within a class or by subclassing. The general usage flow is as follows:
* Create a client instance
* Connect to a broker using one of the ``connect*()`` functions
* Call one of the ``loop*()`` functions to maintain network traffic flow with the broker
* Use ``subscribe()`` to subscribe to a topic and receive messages
* Use ``publish()`` to publish messages to the broker
* Use ``disconnect()`` to disconnect from the broker
Callbacks will be called to allow the application to process events as necessary. These callbacks are described below.
Network loop
````````````
These functions are the driving force behind the client. If they are not
called, incoming network data will not be processed and outgoing network data
will not be sent. There are four options for managing the
network loop. Three are described here, the fourth in "External event loop
support" below. Do not mix the different loop functions.
loop_start() / loop_stop()
''''''''''''''''''''''''''
.. code:: python
mqttc.loop_start()
while True:
temperature = sensor.blocking_read()
mqttc.publish("paho/temperature", temperature)
mqttc.loop_stop()
These functions implement a threaded interface to the network loop. Calling
`loop_start()` once, before or after ``connect*()``, runs a thread in the
background to call `loop()` automatically. This frees up the main thread for
other work that may be blocking. This call also handles reconnecting to the
broker. Call `loop_stop()` to stop the background thread.
The loop is also stopped if you call `disconnect()`.
loop_forever()
''''''''''''''
.. code:: python
mqttc.loop_forever(retry_first_connection=False)
This is a blocking form of the network loop and will not return until the
client calls `disconnect()`. It automatically handles reconnecting.
Except for the first connection attempt when using `connect_async`, use
``retry_first_connection=True`` to make it retry the first connection.
*Warning*: This might lead to situations where the client keeps connecting to an
non existing host without failing.
loop()
''''''
.. code:: python
run = True
while run:
rc = mqttc.loop(timeout=1.0)
if rc != 0:
# need to handle error, possible reconnecting or stopping the application
Call regularly to process network events. This call waits in ``select()`` until
the network socket is available for reading or writing, if appropriate, then
handles the incoming/outgoing data. This function blocks for up to ``timeout``
seconds. ``timeout`` must not exceed the ``keepalive`` value for the client or
your client will be regularly disconnected by the broker.
Using this kind of loop, require you to handle reconnection strategie.
Callbacks
`````````
The interface to interact with paho-mqtt include various callback that are called by
the library when some events occur.
The callbacks are functions defined in your code, to implement the require action on those events. This could
be simply printing received message or much more complex behaviour.
Callbacks API is versioned, and the selected version is the `CallbackAPIVersion` you provided to `Client`
constructor. Currently two version are supported:
* ``CallbackAPIVersion.VERSION1``: it's the historical version used in paho-mqtt before version 2.0.
It's the API used before the introduction of `CallbackAPIVersion`.
This version is deprecated and will be removed in paho-mqtt version 3.0.
* ``CallbackAPIVersion.VERSION2``: This version is more consistent between protocol MQTT 3.x and MQTT 5.x. It's also
much more usable with MQTT 5.x since reason code and properties are always provided when available.
It's recommended for all user to upgrade to this version. It's highly recommended for MQTT 5.x user.
The following callbacks exists:
* `on_connect()`: called when the CONNACK from the broker is received. The call could be for a refused connection,
check the reason_code to see if the connection is successful or rejected.
* `on_connect_fail()`: called by `loop_forever()` and `loop_start()` when the TCP connection failed to establish.
This callback is not called when using `connect()` or `reconnect()` directly. It's only called following
an automatic (re)connection made by `loop_start()` and `loop_forever()`
* `on_disconnect()`: called when the connection is closed.
* `on_message()`: called when a MQTT message is received from the broker.
* `on_publish()`: called when an MQTT message was sent to the broker. Depending on QoS level the callback is called
at different moment:
* For QoS == 0, it's called as soon as the message is sent over the network. This could be before the corresponding ``publish()`` return.
* For QoS == 1, it's called when the corresponding PUBACK is received from the broker
* For QoS == 2, it's called when the corresponding PUBCOMP is received from the broker
* `on_subscribe()`: called when the SUBACK is received from the broker
* `on_unsubscribe()`: called when the UNSUBACK is received from the broker
* `on_log()`: called when the library log a message
* `on_socket_open`, `on_socket_close`, `on_socket_register_write`, `on_socket_unregister_write`: callbacks used for external loop support. See below for details.
For the signature of each callback, see the `online documentation <documentation_>`_.
Subscriber example
''''''''''''''''''
.. code:: python
import paho.mqtt.client as mqtt
def on_subscribe(client, userdata, mid, reason_code_list, properties):
# Since we subscribed only for a single channel, reason_code_list contains
# a single entry
if reason_code_list[0].is_failure:
print(f"Broker rejected you subscription: {reason_code_list[0]}")
else:
print(f"Broker granted the following QoS: {reason_code_list[0].value}")
def on_unsubscribe(client, userdata, mid, reason_code_list, properties):
# Be careful, the reason_code_list is only present in MQTTv5.
# In MQTTv3 it will always be empty
if len(reason_code_list) == 0 or not reason_code_list[0].is_failure:
print("unsubscribe succeeded (if SUBACK is received in MQTTv3 it success)")
else:
print(f"Broker replied with failure: {reason_code_list[0]}")
client.disconnect()
def on_message(client, userdata, message):
# userdata is the structure we choose to provide, here it's a list()
userdata.append(message.payload)
# We only want to process 10 messages
if len(userdata) >= 10:
client.unsubscribe("$SYS/#")
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Failed to connect: {reason_code}. loop_forever() will retry connection")
else:
# we should always subscribe from on_connect callback to be sure
# our subscribed is persisted across reconnections.
client.subscribe("$SYS/#")
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.on_subscribe = on_subscribe
mqttc.on_unsubscribe = on_unsubscribe
mqttc.user_data_set([])
mqttc.connect("mqtt.eclipseprojects.io")
mqttc.loop_forever()
print(f"Received the following message: {mqttc.user_data_get()}")
publisher example
'''''''''''''''''
.. code:: python
import time
import paho.mqtt.client as mqtt
def on_publish(client, userdata, mid, reason_code, properties):
# reason_code and properties will only be present in MQTTv5. It's always unset in MQTTv3
try:
userdata.remove(mid)
except KeyError:
print("on_publish() is called with a mid not present in unacked_publish")
print("This is due to an unavoidable race-condition:")
print("* publish() return the mid of the message sent.")
print("* mid from publish() is added to unacked_publish by the main thread")
print("* on_publish() is called by the loop_start thread")
print("While unlikely (because on_publish() will be called after a network round-trip),")
print(" this is a race-condition that COULD happen")
print("")
print("The best solution to avoid race-condition is using the msg_info from publish()")
print("We could also try using a list of acknowledged mid rather than removing from pending list,")
print("but remember that mid could be re-used !")
unacked_publish = set()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_publish = on_publish
mqttc.user_data_set(unacked_publish)
mqttc.connect("mqtt.eclipseprojects.io")
mqttc.loop_start()
# Our application produce some messages
msg_info = mqttc.publish("paho/test/topic", "my message", qos=1)
unacked_publish.add(msg_info.mid)
msg_info2 = mqttc.publish("paho/test/topic", "my message2", qos=1)
unacked_publish.add(msg_info2.mid)
# Wait for all message to be published
while len(unacked_publish):
time.sleep(0.1)
# Due to race-condition described above, the following way to wait for all publish is safer
msg_info.wait_for_publish()
msg_info2.wait_for_publish()
mqttc.disconnect()
mqttc.loop_stop()
Logger
``````
The Client emit some log message that could be useful during troubleshooting. The easiest way to
enable logs is the call `enable_logger()`. It's possible to provide a custom logger or let the
default logger being used.
Example:
.. code:: python
import logging
import paho.mqtt.client as mqtt
logging.basicConfig(level=logging.DEBUG)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.enable_logger()
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_start()
# Do additional action needed, publish, subscribe, ...
[...]
It's also possible to define a on_log callback that will receive a copy of all log messages. Example:
.. code:: python
import paho.mqtt.client as mqtt
def on_log(client, userdata, paho_log_level, messages):
if paho_log_level == mqtt.LogLevel.MQTT_LOG_ERR:
print(message)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_start()
# Do additional action needed, publish, subscribe, ...
[...]
The correspondence with Paho logging levels and standard ones is the following:
==================== ===============
Paho logging
==================== ===============
``MQTT_LOG_ERR`` ``logging.ERROR``
``MQTT_LOG_WARNING`` ``logging.WARNING``
``MQTT_LOG_NOTICE`` ``logging.INFO`` *(no direct equivalent)*
``MQTT_LOG_INFO`` ``logging.INFO``
``MQTT_LOG_DEBUG`` ``logging.DEBUG``
==================== ===============
External event loop support
```````````````````````````
To support other network loop like asyncio (see examples_), the library expose some
method and callback to support those use-case.
The following loop method exists:
* `loop_read`: should be called when the socket is ready for reading.
* `loop_write`: should be called when the socket is ready for writing AND the library want to write data.
* `loop_misc`: should be called every few seconds to handle message retrying and pings.
In pseudo code, it give the following:
.. code:: python
while run:
if need_read:
mqttc.loop_read()
if need_write:
mqttc.loop_write()
mqttc.loop_misc()
if not need_read and not need_write:
# But don't wait more than few seconds, loop_misc() need to be called regularly
wait_for_change_in_need_read_or_write()
updated_need_read_and_write()
The tricky part is implementing the update of need_read / need_write and wait for condition change. To support
this, the following method exists:
* `socket()`: which return the socket object when the TCP connection is open.
This call is particularly useful for select_ based loops. See ``examples/loop_select.py``.
* `want_write()`: return true if there is data waiting to be written. This is close to the
``need_writew`` of above pseudo-code, but you should also check whether the socket is ready for writing.
* callbacks ``on_socket_*``:
* `on_socket_open`: called when the socket is opened.
* `on_socket_close`: called when the socket is about to be closed.
* `on_socket_register_write`: called when there is data the client want to write on the socket
* `on_socket_unregister_write`: called when there is no more data to write on the socket.
Callbacks are particularly useful for event loops where you register or unregister a socket
for reading+writing. See ``examples/loop_asyncio.py`` for an example.
.. _select: https://docs.python.org/3/library/select.html#select.select
The callbacks are always called in this order:
- `on_socket_open`
- Zero or more times:
- `on_socket_register_write`
- `on_socket_unregister_write`
- `on_socket_close`
Global helper functions
```````````````````````
The client module also offers some global helper functions.
``topic_matches_sub(sub, topic)`` can be used to check whether a ``topic``
matches a ``subscription``.
For example:
the topic ``foo/bar`` would match the subscription ``foo/#`` or ``+/bar``
the topic ``non/matching`` would not match the subscription ``non/+/+``
Publish
*******
This module provides some helper functions to allow straightforward publishing
of messages in a one-shot manner. In other words, they are useful for the
situation where you have a single/multiple messages you want to publish to a
broker, then disconnect with nothing else required.
The two functions provided are `single()` and `multiple()`.
Both functions include support for MQTT v5.0, but do not currently let you
set any properties on connection or when sending messages.
Single
``````
Publish a single message to a broker, then disconnect cleanly.
Example:
.. code:: python
import paho.mqtt.publish as publish
publish.single("paho/test/topic", "payload", hostname="mqtt.eclipseprojects.io")
Multiple
````````
Publish multiple messages to a broker, then disconnect cleanly.
Example:
.. code:: python
from paho.mqtt.enums import MQTTProtocolVersion
import paho.mqtt.publish as publish
msgs = [{'topic':"paho/test/topic", 'payload':"multiple 1"},
("paho/test/topic", "multiple 2", 0, False)]
publish.multiple(msgs, hostname="mqtt.eclipseprojects.io", protocol=MQTTProtocolVersion.MQTTv5)
Subscribe
*********
This module provides some helper functions to allow straightforward subscribing
and processing of messages.
The two functions provided are `simple()` and `callback()`.
Both functions include support for MQTT v5.0, but do not currently let you
set any properties on connection or when subscribing.
Simple
``````
Subscribe to a set of topics and return the messages received. This is a
blocking function.
Example:
.. code:: python
import paho.mqtt.subscribe as subscribe
msg = subscribe.simple("paho/test/topic", hostname="mqtt.eclipseprojects.io")
print("%s %s" % (msg.topic, msg.payload))
Using Callback
``````````````
Subscribe to a set of topics and process the messages received using a user
provided callback.
Example:
.. code:: python
import paho.mqtt.subscribe as subscribe
def on_message_print(client, userdata, message):
print("%s %s" % (message.topic, message.payload))
userdata["message_count"] += 1
if userdata["message_count"] >= 5:
# it's possible to stop the program by disconnecting
client.disconnect()
subscribe.callback(on_message_print, "paho/test/topic", hostname="mqtt.eclipseprojects.io", userdata={"message_count": 0})
Reporting bugs
--------------
Please report bugs in the issues tracker at https://github.com/eclipse/paho.mqtt.python/issues.
More information
----------------
Discussion of the Paho clients takes place on the `Eclipse paho-dev mailing list <https://dev.eclipse.org/mailman/listinfo/paho-dev>`_.
General questions about the MQTT protocol itself (not this library) are discussed in the `MQTT Google Group <https://groups.google.com/forum/?fromgroups#!forum/mqtt>`_.
There is much more information available via the `MQTT community site <http://mqtt.org/>`_.
.. _examples: https://github.com/eclipse/paho.mqtt.python/tree/master/examples
.. _documentation: https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>About</title>
</head>
<body lang="EN-US">
<h2>About This Content</h2>
<p><em>December 9, 2013</em></p>
<h3>License</h3>
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
indicated below, the Content is provided to you under the terms and conditions of the
Eclipse Public License Version 2.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
A copy of the EPL is available at
<a href="http://www.eclipse.org/legal/epl-v20.html">http://www.eclipse.org/legal/epl-v20.html</a>
and a copy of the EDL is available at
<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
For purposes of the EPL, "Program" will mean the Content.</p>
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
being redistributed by another party ("Redistributor") and different terms and conditions may
apply to your use of any object code in the Content. Check the Redistributor's license that was
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
<h3>Third Party Content</h3>
<p>The Content includes items that have been sourced from third parties as set out below. If you
did not receive this Content directly from the Eclipse Foundation, the following is provided
for informational purposes only, and you should look to the Redistributor's license for
terms and conditions of use.</p>
<p><em>
<strong>None</strong> <br><br>
<br><br>
</em></p>
</body></html>
+31
View File
@@ -0,0 +1,31 @@
Eclipse Distribution License - v 1.0
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the Eclipse Foundation, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+277
View File
@@ -0,0 +1,277 @@
Eclipse Public License - v 2.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial content
Distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from
and are Distributed by that particular Contributor. A Contribution
"originates" from a Contributor if it was added to the Program by
such Contributor itself or anyone acting on such Contributor's behalf.
Contributions do not include changes or additions to the Program that
are not Modified Works.
"Contributor" means any person or entity that Distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.
"Program" means the Contributions Distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement
or any Secondary License (as applicable), including Contributors.
"Derivative Works" shall mean any work, whether in Source Code or other
form, that is based on (or derived from) the Program and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship.
"Modified Works" shall mean any work in Source Code or other form that
results from an addition to, deletion from, or modification of the
contents of the Program, including, for purposes of clarity any new file
in Source Code form that contains any contents of the Program. Modified
Works shall not include works that contain only declarations,
interfaces, types, classes, structures, or files of the Program solely
in each case in order to link to, bind by name, or subclass the Program
or Modified Works thereof.
"Distribute" means the acts of a) distributing or b) making available
in any manner that enables the transfer of a copy.
"Source Code" means the form of a Program preferred for making
modifications, including but not limited to software source code,
documentation source, and configuration files.
"Secondary License" means either the GNU General Public License,
Version 2.0, or any later versions of that license, including any
exceptions or additional permissions as identified by the initial
Contributor.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare Derivative Works of, publicly display,
publicly perform, Distribute and sublicense the Contribution of such
Contributor, if any, and such Derivative Works.
b) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent
license under Licensed Patents to make, use, sell, offer to sell,
import and otherwise transfer the Contribution of such Contributor,
if any, in Source Code or other form. This patent license shall
apply to the combination of the Contribution and the Program if, at
the time the Contribution is added by the Contributor, such addition
of the Contribution causes such combination to be covered by the
Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity.
Each Contributor disclaims any liability to Recipient for claims
brought by any other entity based on infringement of intellectual
property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby
assumes sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party
patent license is required to allow Recipient to Distribute the
Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has
sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
e) Notwithstanding the terms of any Secondary License, no
Contributor makes additional grants to any Recipient (other than
those set forth in this Agreement) as a result of such Recipient's
receipt of the Program under the terms of a Secondary License
(if permitted under the terms of Section 3).
3. REQUIREMENTS
3.1 If a Contributor Distributes the Program in any form, then:
a) the Program must also be made available as Source Code, in
accordance with section 3.2, and the Contributor must accompany
the Program with a statement that the Source Code for the Program
is available under this Agreement, and informs Recipients how to
obtain it in a reasonable manner on or through a medium customarily
used for software exchange; and
b) the Contributor may Distribute the Program under a license
different than this Agreement, provided that such license:
i) effectively disclaims on behalf of all other Contributors all
warranties and conditions, express and implied, including
warranties or conditions of title and non-infringement, and
implied warranties or conditions of merchantability and fitness
for a particular purpose;
ii) effectively excludes on behalf of all other Contributors all
liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) does not attempt to limit or alter the recipients' rights
in the Source Code under section 3.2; and
iv) requires any subsequent distribution of the Program by any
party to be under a license that satisfies the requirements
of this section 3.
3.2 When the Program is Distributed as Source Code:
a) it must be made available under this Agreement, or if the
Program (i) is combined with other material in a separate file or
files made available under a Secondary License, and (ii) the initial
Contributor attached to the Source Code the notice described in
Exhibit A of this Agreement, then the Program may be made available
under the terms of such Secondary Licenses, and
b) a copy of this Agreement must be included with each copy of
the Program.
3.3 Contributors may not remove or alter any copyright, patent,
trademark, attribution notices, disclaimers of warranty, or limitations
of liability ("notices") contained within the Program from any copy of
the Program which they Distribute, provided that Contributors may add
their own appropriate notices.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the Program
in a commercial product offering. The obligations in this section do not
apply to any claims or Losses relating to any actual or alleged
intellectual property infringement. In order to qualify, an Indemnified
Contributor must: a) promptly notify the Commercial Contributor in
writing of such claim, and b) allow the Commercial Contributor to control,
and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may
participate in any such claim at its own expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those performance
claims and warranties, and if a court requires any other Contributor to
pay any damages as a result, the Commercial Contributor must pay
those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Each Recipient is solely responsible for determining the
appropriateness of using and distributing the Program and assumes all
risks associated with its exercise of rights under this Agreement,
including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs
or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other software
or hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
Distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is published,
Contributor may elect to Distribute the Program (including its
Contributions) under the new version.
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
receives no rights or licenses to the intellectual property of any
Contributor under this Agreement, whether expressly, by implication,
estoppel or otherwise. All rights in the Program not expressly granted
under this Agreement are reserved. Nothing in this Agreement is intended
to be enforceable by any entity that is not a Contributor or Recipient.
No third-party beneficiary rights are created under this Agreement.
Exhibit A - Form of Secondary Licenses Notice
"This Source Code may also be made available under the following
Secondary Licenses when the conditions for such availability set forth
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
version(s), and exceptions or additional permissions here}."
Simply including a copy of this Agreement, including this Exhibit A
is not sufficient to license the Source Code under Secondary Licenses.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to
look for such a notice.
You may add additional accurate notices of copyright ownership.
+129
View File
@@ -0,0 +1,129 @@
import base64
import datetime
import functools
import hashlib
import hmac
import os
import uuid
from paho.mqtt.client import Client, CallbackAPIVersion
def get_amazon_auth_headers(access_key, secret_key, region, host, port, headers=None):
""" Get the amazon auth headers for working with the amazon websockets
protocol
Requires a lot of extra stuff:
http://docs.aws.amazon.com/general/latest/gr//sigv4-create-canonical-request.html
http://docs.aws.amazon.com/general/latest/gr//signature-v4-examples.html#signature-v4-examples-pythonw
http://docs.aws.amazon.com/general/latest/gr//sigv4-signed-request-examples.html#sig-v4-examples-get-auth-header
Args:
access_key (str): Amazon access key (AWS_ACCESS_KEY_ID)
secret_key (str): Amazon secret access key (AWS_SECRET_ACCESS_KEY)
region (str): aws region
host (str): iot endpoint (xxxxxxxxxxxxxx.iot.<region>.amazonaws.com)
headers (dict): a dictionary of the original headers- normally websocket headers
Returns:
dict: A string containing the headers that amazon expects in the auth
request for the iot websocket service
"""
# pylint: disable=unused-variable,unused-argument
def sign(key, msg):
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
def getSignatureKey(key, dateStamp, regionName, serviceName):
kDate = sign(("AWS4" + key).encode("utf-8"), dateStamp)
kRegion = sign(kDate, regionName)
kService = sign(kRegion, serviceName)
kSigning = sign(kService, "aws4_request")
return kSigning
service = "iotdevicegateway"
algorithm = "AWS4-HMAC-SHA256"
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime("%Y%m%d") # Date w/o time, used in credential scope
if headers is None:
headers = {
"Host": "{0:s}:443".format(host),
"Upgrade": "websocket",
"Connection": "Upgrade",
"Origin": "https://{0:s}:443".format(host),
"Sec-WebSocket-Key": base64.b64encode(uuid.uuid4().bytes),
"Sec-Websocket-Version": "13",
"Sec-Websocket-Protocol": "mqtt",
}
headers.update({
"X-Amz-Date": amzdate,
})
# get into 'canonical' form - lowercase, sorted alphabetically
canonical_headers = "\n".join(sorted("{}:{}".format(i.lower(), j).strip() for i, j in headers.items()))
# Headers to sign - alphabetical order
signed_headers = ";".join(sorted(i.lower().strip() for i in headers.keys()))
# No payload
payload_hash = hashlib.sha256("").hexdigest().lower()
request_parts = [
"GET",
"/mqtt",
# no query parameters
"",
canonical_headers + "\n",
signed_headers,
payload_hash,
]
canonical_request = "\n".join(request_parts)
# now actually hash request and sign
hashed_request = hashlib.sha256(canonical_request).hexdigest()
credential_scope = "{datestamp:s}/{region:s}/{service:s}/aws4_request".format(**locals())
string_to_sign = "{algorithm:s}\n{amzdate:s}\n{credential_scope:s}\n{hashed_request:s}".format(**locals())
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
# create auth header
authorization_header = "{algorithm:s} Credential={access_key:s}/{credential_scope:s}, SignedHeaders={signed_headers:s}, Signature={signature:s}".format(**locals())
# get final header string
headers["Authorization"] = authorization_header
return headers
def example_use():
access_key = os.environ["AWS_ACCESS_KEY_ID"]
secret_key = os.environ["AWS_SECRET_ACCESS_KEY"]
port = 8883
region = "eu-west-1"
# This is specific to your AWS account
host = "abc123def456.iot.{0:s}.amazonaws.com".format(region)
extra_headers = functools.partial(
get_amazon_auth_headers,
access_key,
secret_key,
region,
host,
port,
)
client = Client(CallbackAPIVersion.VERSION2, transport="websockets")
client.ws_set_options(headers=extra_headers)
# Use client as normal from here
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2016 James Myatt <james@jamesmyatt.co.uk>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# James Myatt - initial implementation
# This shows a simple example of standard logging with an MQTT subscriber client.
import logging
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
logging.basicConfig(level=logging.DEBUG)
# If you want to use a specific client id, use
# mqttc = mqtt.Client("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
logger = logging.getLogger(__name__)
mqttc.enable_logger(logger)
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.subscribe("$SYS/#", 0)
mqttc.loop_forever()
@@ -0,0 +1,120 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
# All rights reserved.
# This shows an example of an MQTT client that clears all of the retained messages it receives.
import getopt
import sys
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
final_mid = 0
def on_connect(mqttc, userdata, flags, reason_code, properties):
if userdata:
print(f"reason_code: {reason_code}")
def on_message(mqttc, userdata, msg):
global final_mid
if msg.retain == 0:
pass
# sys.exit()
else:
if userdata:
print("Clearing topic " + msg.topic)
(rc, final_mid) = mqttc.publish(msg.topic, None, 1, True)
def on_publish(mqttc, userdata, mid, reason_code, properties):
global final_mid
if mid == final_mid:
sys.exit()
def on_log(mqttc, userdata, level, string):
print(string)
def print_usage():
print(
"mqtt_clear_retain.py [-d] [-h hostname] [-i clientid] [-k keepalive] [-p port] [-u username [-P password]] [-v] -t topic")
def main(argv):
debug = False
host = "mqtt.eclipseprojects.io"
client_id = None
keepalive = 60
port = 1883
password = None
topic = None
username = None
verbose = False
try:
opts, args = getopt.getopt(argv, "dh:i:k:p:P:t:u:v",
["debug", "id", "keepalive", "port", "password", "topic", "username", "verbose"])
except getopt.GetoptError:
print_usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-d", "--debug"):
debug = True
elif opt in ("-h", "--host"):
host = arg
elif opt in ("-i", "--id"):
client_id = arg
elif opt in ("-k", "--keepalive"):
keepalive = int(arg)
elif opt in ("-p", "--port"):
port = int(arg)
elif opt in ("-P", "--password"):
password = arg
elif opt in ("-t", "--topic"):
topic = arg
print(topic)
elif opt in ("-u", "--username"):
username = arg
elif opt in ("-v", "--verbose"):
verbose = True
if not topic:
print("You must provide a topic to clear.\n")
print_usage()
sys.exit(2)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id)
mqttc._userdata = verbose
mqttc.on_message = on_message
mqttc.on_publish = on_publish
mqttc.on_connect = on_connect
if debug:
mqttc.on_log = on_log
if username:
mqttc.username_pw_set(username, password)
mqttc.connect(host, port, keepalive)
mqttc.subscribe(topic)
mqttc.loop_forever()
if __name__ == "__main__":
main(sys.argv[1:])
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2010-2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
# All rights reserved.
# This shows a simple example of waiting for a message to be published.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_connect(mqttc, obj, flags, reason_code, properties):
print("reason_code: " + str(reason_code))
def on_message(mqttc, obj, msg):
print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_publish(mqttc, obj, mid, reason_code, properties):
print("mid: " + str(mid))
def on_log(mqttc, obj, level, string):
print(string)
# If you want to use a specific client id, use
# mqttc = mqtt.Client("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
# Uncomment to enable debug messages
# mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_start()
print("tuple")
(rc, mid) = mqttc.publish("tuple", "bar", qos=2)
print("class")
infot = mqttc.publish("class", "bar", qos=2)
infot.wait_for_publish()
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Jon Levell <levell@uk.ibm.com>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# All rights reserved.
# This shows a example of an MQTT publisher with the ability to use
# user name, password CA certificates based on command line arguments
import argparse
import os
import ssl
import time
import paho.mqtt.client as mqtt
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', required=False, default="mqtt.eclipseprojects.io")
parser.add_argument('-t', '--topic', required=False, default="paho/test/opts")
parser.add_argument('-q', '--qos', required=False, type=int,default=0)
parser.add_argument('-c', '--clientid', required=False, default=None)
parser.add_argument('-u', '--username', required=False, default=None)
parser.add_argument('-d', '--disable-clean-session', action='store_true', help="disable 'clean session' (sub + msgs not cleared when client disconnects)")
parser.add_argument('-p', '--password', required=False, default=None)
parser.add_argument('-P', '--port', required=False, type=int, default=None, help='Defaults to 8883 for TLS or 1883 for non-TLS')
parser.add_argument('-N', '--nummsgs', required=False, type=int, default=1, help='send this many messages before disconnecting')
parser.add_argument('-S', '--delay', required=False, type=float, default=1, help='number of seconds to sleep between msgs')
parser.add_argument('-k', '--keepalive', required=False, type=int, default=60)
parser.add_argument('-s', '--use-tls', action='store_true')
parser.add_argument('--insecure', action='store_true')
parser.add_argument('-F', '--cacerts', required=False, default=None)
parser.add_argument('--tls-version', required=False, default=None, help='TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1\n')
parser.add_argument('-D', '--debug', action='store_true')
args, unknown = parser.parse_known_args()
def on_connect(mqttc, obj, flags, reason_code, properties):
print("connect reason_code: " + str(reason_code))
def on_message(mqttc, obj, msg):
print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_publish(mqttc, obj, mid, reason_code, properties):
print("mid: " + str(mid))
def on_log(mqttc, obj, level, string):
print(string)
usetls = args.use_tls
if args.cacerts:
usetls = True
port = args.port
if port is None:
if usetls:
port = 8883
else:
port = 1883
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, args.clientid, clean_session = not args.disable_clean_session)
if usetls:
if args.tls_version == "tlsv1.2":
tlsVersion = ssl.PROTOCOL_TLSv1_2
elif args.tls_version == "tlsv1.1":
tlsVersion = ssl.PROTOCOL_TLSv1_1
elif args.tls_version == "tlsv1":
tlsVersion = ssl.PROTOCOL_TLSv1
elif args.tls_version is None:
tlsVersion = None
else:
print ("Unknown TLS version - ignoring")
tlsVersion = None
if not args.insecure:
cert_required = ssl.CERT_REQUIRED
else:
cert_required = ssl.CERT_NONE
mqttc.tls_set(ca_certs=args.cacerts, certfile=None, keyfile=None, cert_reqs=cert_required, tls_version=tlsVersion)
if args.insecure:
mqttc.tls_insecure_set(True)
if args.username or args.password:
mqttc.username_pw_set(args.username, args.password)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
if args.debug:
mqttc.on_log = on_log
print("Connecting to "+args.host+" port: "+str(port))
mqttc.connect(args.host, port, args.keepalive)
mqttc.loop_start()
for x in range (0, args.nummsgs):
msg_txt = '{"msgnum": "'+str(x)+'"}'
print("Publishing: "+msg_txt)
infot = mqttc.publish(args.topic, msg_txt, qos=args.qos)
infot.wait_for_publish()
time.sleep(args.delay)
mqttc.disconnect()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2020 Frank Pagliughi <fpagliughi@mindspring.com>
# All rights reserved.
#
# This program and the accompanying materials are made available
# under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Frank Pagliughi - initial implementation
#
# This shows an example of an MQTTv5 Remote Procedure Call (RPC) client.
# You should run the server_rpc_math.py before.
import json
import sys
import time
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
# These will be updated with the server-assigned Client ID
client_id = "mathcli"
reply_to = ""
# This correlates the outbound request with the returned reply
corr_id = b"1"
# This is sent in the message callback when we get the response
reply = None
# The MQTTv5 callback takes the additional 'props' parameter.
def on_connect(mqttc, userdata, flags, reason_code, props):
global client_id, reply_to
print(f"Connected: '{flags}', '{reason_code}', '{props}'")
if hasattr(props, 'AssignedClientIdentifier'):
client_id = props.AssignedClientIdentifier
reply_to = "replies/math/" + client_id
mqttc.subscribe(reply_to)
# An incoming message should be the reply to our request
def on_message(mqttc, userdata, msg):
global reply
print(msg.topic+" "+str(msg.payload)+" "+str(msg.properties))
props = msg.properties
if not hasattr(props, 'CorrelationData'):
print("No correlation ID")
# Match the response to the request correlation ID.
if props.CorrelationData == corr_id:
reply = msg.payload
if len(sys.argv) < 3:
print("USAGE: client_rpc_math.py [add|mult] n1 n2 ...")
sys.exit(1)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="", protocol=mqtt.MQTTv5)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.connect(host="mqtt.eclipseprojects.io", clean_start=True)
mqttc.loop_start()
# Wait for connection to set `client_id`, etc.
while not mqttc.is_connected():
time.sleep(0.1)
# Properties for the request specify the ResponseTopic and CorrelationData
props = mqtt.Properties(PacketTypes.PUBLISH)
props.CorrelationData = corr_id
props.ResponseTopic = reply_to
# Uncomment to see what got set
#print("Client ID: "+client_id)
#print("Reply To: "+reply_to)
#print(props)
# The requested operation, 'add' or 'mult'
func = sys.argv[1]
# Gather the numeric parameters as an array of numbers
# These can be int's or float's
args = []
for s in sys.argv[2:]:
args.append(float(s))
# Send the request
topic = "requests/math/" + func
payload = json.dumps(args)
mqttc.publish(topic, payload, qos=1, properties=props)
# Wait for the reply
while reply is None:
time.sleep(0.1)
# Extract the response and print it.
rsp = json.loads(reply)
print("Response: "+str(rsp))
mqttc.loop_stop()
@@ -0,0 +1,62 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
# All rights reserved.
# This demonstrates the session present flag when connecting.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_connect(mqttc, obj, flags, reason_code, properties):
if obj == 0:
print("First connection:")
elif obj == 1:
print("Second connection:")
elif obj == 2:
print("Third connection (with clean session=True):")
print(" Session present: " + str(flags.session_present))
print(" Connection result: " + str(reason_code))
mqttc.disconnect()
def on_disconnect(mqttc, obj, flags, reason_code, properties):
mqttc.user_data_set(obj + 1)
if obj == 0:
mqttc.reconnect()
def on_log(mqttc, obj, level, string):
print(string)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="asdfj", clean_session=False)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
# Uncomment to enable debug messages
# mqttc.on_log = on_log
mqttc.user_data_set(0)
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_forever()
# Clear session
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="asdfj", clean_session=True)
mqttc.on_connect = on_connect
mqttc.user_data_set(2)
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.loop_forever()
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This example shows how you can use the MQTT client in a class.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
class MyMQTTClass(mqtt.Client):
def on_connect(self, mqttc, obj, flags, reason_code, properties):
print("rc: "+str(reason_code))
def on_connect_fail(self, mqttc, obj):
print("Connect failed")
def on_message(self, mqttc, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def on_publish(self, mqttc, obj, mid, reason_codes, properties):
print("mid: "+str(mid))
def on_subscribe(self, mqttc, obj, mid, reason_code_list, properties):
print("Subscribed: "+str(mid)+" "+str(reason_code_list))
def on_log(self, mqttc, obj, level, string):
print(string)
def run(self):
self.connect("mqtt.eclipseprojects.io", 1883, 60)
self.subscribe("$SYS/#", 0)
rc = 0
while rc == 0:
rc = self.loop()
return rc
# If you want to use a specific client id, use
# mqttc = MyMQTTClass("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = MyMQTTClass(mqtt.CallbackAPIVersion.VERSION2)
rc = mqttc.run()
print("rc: "+str(rc))
@@ -0,0 +1,53 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# All rights reserved.
# This shows a simple example of an MQTT subscriber using a per-subscription message handler.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_message_msgs(mosq, obj, msg):
# This callback will only be called for messages with topics that match
# $SYS/broker/messages/#
print("MESSAGES: " + msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_message_bytes(mosq, obj, msg):
# This callback will only be called for messages with topics that match
# $SYS/broker/bytes/#
print("BYTES: " + msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_message(mosq, obj, msg):
# This callback will be called for messages that we receive that do not
# match any patterns defined in topic specific callbacks, i.e. in this case
# those messages that do not have topics $SYS/broker/messages/# nor
# $SYS/broker/bytes/#
print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
# Add message callbacks that will only trigger on a specific subscription match.
mqttc.message_callback_add("$SYS/broker/messages/#", on_message_msgs)
mqttc.message_callback_add("$SYS/broker/bytes/#", on_message_bytes)
mqttc.on_message = on_message
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.subscribe("$SYS/#", 0)
mqttc.loop_forever()
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2010-2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
# All rights reserved.
# This shows a simple example of an MQTT subscriber using connect_srv method.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_connect(mqttc, obj, flags, reason_code, properties):
print("Connected to %s:%s" % (mqttc.host, mqttc.port))
def on_message(mqttc, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def on_subscribe(mqttc, obj, mid, reason_code_list, properties):
print("Subscribed: "+str(mid)+" "+str(reason_code_list))
def on_log(mqttc, obj, level, string):
print(string)
# If you want to use a specific client id, use
# mqttc = mqtt.Client("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
#mqttc.on_log = on_log
mqttc.connect_srv("eclipseprojects.io", 60)
mqttc.subscribe("$SYS/broker/version", 0)
rc = 0
while rc == 0:
rc = mqttc.loop()
print("rc: "+str(rc))
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2010-2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
# All rights reserved.
# This shows a simple example of an MQTT subscriber.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_connect(mqttc, obj, flags, reason_code, properties):
print("reason_code: "+str(reason_code))
def on_message(mqttc, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def on_subscribe(mqttc, obj, mid, reason_code_list, properties):
print("Subscribed: "+str(mid)+" "+str(reason_code_list))
def on_log(mqttc, obj, level, string):
print(string)
# If you want to use a specific client id, use
# mqttc = mqtt.Client("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, transport="websockets")
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 80, 60)
mqttc.subscribe("$SYS/broker/version", 0)
mqttc.loop_forever()
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2010-2013 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# Copyright (c) 2010,2011 Roger Light <roger@atchoo.org>
# All rights reserved.
# This shows a simple example of an MQTT subscriber.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
def on_connect(mqttc, obj, flags, reason_code, properties):
print("reason_code: " + str(reason_code))
def on_message(mqttc, obj, msg):
print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_subscribe(mqttc, obj, mid, reason_code_list, properties):
print("Subscribed: " + str(mid) + " " + str(reason_code_list))
def on_log(mqttc, obj, level, string):
print(string)
# If you want to use a specific client id, use
# mqttc = mqtt.Client("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
# mqttc.on_log = on_log
mqttc.connect("mqtt.eclipseprojects.io", 1883, 60)
mqttc.subscribe("$SYS/#")
mqttc.loop_forever()
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Jon Levell <levell@uk.ibm.com>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# All rights reserved.
# This shows a example of an MQTT subscriber with the ability to use
# user name, password CA certificates based on command line arguments
import argparse
import os
import ssl
import paho.mqtt.client as mqtt
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', required=False, default="mqtt.eclipseprojects.io")
parser.add_argument('-t', '--topic', required=False, default="$SYS/#")
parser.add_argument('-q', '--qos', required=False, type=int, default=0)
parser.add_argument('-c', '--clientid', required=False, default=None)
parser.add_argument('-u', '--username', required=False, default=None)
parser.add_argument('-d', '--disable-clean-session', action='store_true', help="disable 'clean session' (sub + msgs not cleared when client disconnects)")
parser.add_argument('-p', '--password', required=False, default=None)
parser.add_argument('-P', '--port', required=False, type=int, default=None, help='Defaults to 8883 for TLS or 1883 for non-TLS')
parser.add_argument('-k', '--keepalive', required=False, type=int, default=60)
parser.add_argument('-s', '--use-tls', action='store_true')
parser.add_argument('--insecure', action='store_true')
parser.add_argument('-F', '--cacerts', required=False, default=None)
parser.add_argument('--tls-version', required=False, default=None, help='TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1\n')
parser.add_argument('-D', '--debug', action='store_true')
args, unknown = parser.parse_known_args()
def on_connect(mqttc, obj, flags, reason_code, properties):
print("reason_code: " + str(reason_code))
def on_message(mqttc, obj, msg):
print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))
def on_publish(mqttc, obj, mid):
print("mid: " + str(mid))
def on_subscribe(mqttc, obj, mid, reason_code_list, properties):
print("Subscribed: " + str(mid) + " " + str(reason_code_list))
def on_log(mqttc, obj, level, string):
print(string)
usetls = args.use_tls
if args.cacerts:
usetls = True
port = args.port
if port is None:
if usetls:
port = 8883
else:
port = 1883
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, args.clientid,clean_session = not args.disable_clean_session)
if usetls:
if args.tls_version == "tlsv1.2":
tlsVersion = ssl.PROTOCOL_TLSv1_2
elif args.tls_version == "tlsv1.1":
tlsVersion = ssl.PROTOCOL_TLSv1_1
elif args.tls_version == "tlsv1":
tlsVersion = ssl.PROTOCOL_TLSv1
elif args.tls_version is None:
tlsVersion = None
else:
print ("Unknown TLS version - ignoring")
tlsVersion = None
if not args.insecure:
cert_required = ssl.CERT_REQUIRED
else:
cert_required = ssl.CERT_NONE
mqttc.tls_set(ca_certs=args.cacerts, certfile=None, keyfile=None, cert_reqs=cert_required, tls_version=tlsVersion)
if args.insecure:
mqttc.tls_insecure_set(True)
if args.username or args.password:
mqttc.username_pw_set(args.username, args.password)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_subscribe = on_subscribe
if args.debug:
mqttc.on_log = on_log
print("Connecting to "+args.host+" port: "+str(port))
mqttc.connect(args.host, port, args.keepalive)
mqttc.subscribe(args.topic, args.qos)
mqttc.loop_forever()
+28
View File
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Ensure can import paho package
try:
import paho
except ImportError:
# This part is only required to run the examples from within the examples
# directory when the module itself is not installed.
import inspect
import os
import sys
cmd_subfolder = os.path.realpath(
os.path.abspath(
os.path.join(
os.path.split(
inspect.getfile(inspect.currentframe())
)[0],
"..",
"src"
)
)
)
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import paho
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
import asyncio
import socket
import uuid
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
client_id = 'paho-mqtt-python/issue72/' + str(uuid.uuid4())
topic = client_id
print("Using client_id / topic: " + client_id)
class AsyncioHelper:
def __init__(self, loop, client):
self.loop = loop
self.client = client
self.client.on_socket_open = self.on_socket_open
self.client.on_socket_close = self.on_socket_close
self.client.on_socket_register_write = self.on_socket_register_write
self.client.on_socket_unregister_write = self.on_socket_unregister_write
def on_socket_open(self, client, userdata, sock):
print("Socket opened")
def cb():
print("Socket is readable, calling loop_read")
client.loop_read()
self.loop.add_reader(sock, cb)
self.misc = self.loop.create_task(self.misc_loop())
def on_socket_close(self, client, userdata, sock):
print("Socket closed")
self.loop.remove_reader(sock)
self.misc.cancel()
def on_socket_register_write(self, client, userdata, sock):
print("Watching socket for writability.")
def cb():
print("Socket is writable, calling loop_write")
client.loop_write()
self.loop.add_writer(sock, cb)
def on_socket_unregister_write(self, client, userdata, sock):
print("Stop watching socket for writability.")
self.loop.remove_writer(sock)
async def misc_loop(self):
print("misc_loop started")
while self.client.loop_misc() == mqtt.MQTT_ERR_SUCCESS:
try:
await asyncio.sleep(1)
except asyncio.CancelledError:
break
print("misc_loop finished")
class AsyncMqttExample:
def __init__(self, loop):
self.loop = loop
def on_connect(self, client, userdata, flags, reason_code, properties):
print("Subscribing")
client.subscribe(topic)
def on_message(self, client, userdata, msg):
if not self.got_message:
print("Got unexpected message: {}".format(msg.decode()))
else:
self.got_message.set_result(msg.payload)
def on_disconnect(self, client, userdata, flags, reason_code, properties):
self.disconnected.set_result(reason_code)
async def main(self):
self.disconnected = self.loop.create_future()
self.got_message = None
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id)
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_disconnect = self.on_disconnect
aioh = AsyncioHelper(self.loop, self.client)
self.client.connect('mqtt.eclipseprojects.io', 1883, 60)
self.client.socket().setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2048)
for c in range(3):
await asyncio.sleep(5)
print("Publishing")
self.got_message = self.loop.create_future()
self.client.publish(topic, b'Hello' * 40000, qos=1)
msg = await self.got_message
print("Got response with {} bytes".format(len(msg)))
self.got_message = None
self.client.disconnect()
print("Disconnected: {}".format(await self.disconnected))
print("Starting")
loop = asyncio.get_event_loop()
loop.run_until_complete(AsyncMqttExample(loop).main())
loop.close()
print("Finished")
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
import socket
import uuid
from select import select
from time import time
import paho.mqtt.client as mqtt
client_id = 'paho-mqtt-python/issue72/' + str(uuid.uuid4())
topic = client_id
print("Using client_id / topic: " + client_id)
class SelectMqttExample:
def __init__(self):
pass
def on_connect(self, client, userdata, flags, reason_code, properties):
print("Subscribing")
client.subscribe(topic)
def on_message(self, client, userdata, msg):
if self.state not in {1, 3, 5}:
print("Got unexpected message: {}".format(msg.decode()))
return
print("Got message with len {}".format(len(msg.payload)))
self.state += 1
self.t = time()
def on_disconnect(self, client, userdata, flags, reason_code, properties):
self.disconnected = True, reason_code
def do_select(self):
sock = self.client.socket()
if not sock:
raise Exception("Socket is gone")
print("Selecting for reading" + (" and writing" if self.client.want_write() else ""))
r, w, e = select(
[sock],
[sock] if self.client.want_write() else [],
[],
1
)
if sock in r:
print("Socket is readable, calling loop_read")
self.client.loop_read()
if sock in w:
print("Socket is writable, calling loop_write")
self.client.loop_write()
self.client.loop_misc()
def main(self):
self.disconnected = (False, None)
self.t = time()
self.state = 0
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id)
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_disconnect = self.on_disconnect
self.client.connect('mqtt.eclipseprojects.io', 1883, 60)
print("Socket opened")
self.client.socket().setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2048)
while not self.disconnected[0]:
self.do_select()
if self.state in {0, 2, 4}:
if time() - self.t >= 5:
print("Publishing")
self.client.publish(topic, b'Hello' * 40000)
self.state += 1
if self.state == 6:
self.state += 1
self.client.disconnect()
print("Disconnected: {}".format(self.disconnected[1]))
print("Starting")
SelectMqttExample().main()
print("Finished")
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
import socket
import uuid
import trio
import paho.mqtt.client as mqtt
client_id = 'paho-mqtt-python/issue72/' + str(uuid.uuid4())
topic = client_id
print("Using client_id / topic: " + client_id)
class TrioAsyncHelper:
def __init__(self, client):
self.client = client
self.sock = None
self._event_large_write = trio.Event()
self.client.on_socket_open = self.on_socket_open
self.client.on_socket_register_write = self.on_socket_register_write
self.client.on_socket_unregister_write = self.on_socket_unregister_write
async def read_loop(self):
while True:
await trio.lowlevel.wait_readable(self.sock)
self.client.loop_read()
async def write_loop(self):
while True:
await self._event_large_write.wait()
await trio.lowlevel.wait_writable(self.sock)
self.client.loop_write()
async def misc_loop(self):
print("misc_loop started")
while self.client.loop_misc() == mqtt.MQTT_ERR_SUCCESS:
await trio.sleep(1)
print("misc_loop finished")
def on_socket_open(self, client, userdata, sock):
print("Socket opened")
self.sock = sock
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2048)
def on_socket_register_write(self, client, userdata, sock):
print('large write request')
self._event_large_write.set()
def on_socket_unregister_write(self, client, userdata, sock):
print("finished large write")
self._event_large_write = trio.Event()
class TrioAsyncMqttExample:
def on_connect(self, client, userdata, flags, reason_code, properties):
print("Subscribing")
client.subscribe(topic)
def on_message(self, client, userdata, msg):
print("Got response with {} bytes".format(len(msg.payload)))
def on_disconnect(self, client, userdata, flags, reason_code, properties):
print('Disconnect result {}'.format(reason_code))
async def test_write(self, cancel_scope: trio.CancelScope):
for c in range(3):
await trio.sleep(5)
print("Publishing")
self.client.publish(topic, b'Hello' * 40000, qos=1)
cancel_scope.cancel()
async def main(self):
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id)
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_disconnect = self.on_disconnect
trio_helper = TrioAsyncHelper(self.client)
self.client.connect('mqtt.eclipseprojects.io', 1883, 60)
async with trio.open_nursery() as nursery:
nursery.start_soon(trio_helper.read_loop)
nursery.start_soon(trio_helper.write_loop)
nursery.start_soon(trio_helper.misc_loop)
nursery.start_soon(self.test_write, nursery.cancel_scope)
self.client.disconnect()
print("Disconnected")
print("Starting")
trio.run(TrioAsyncMqttExample().main)
print("Finished")
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the publish.multiple helper function.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.publish as publish
msgs = [{'topic': "paho/test/multiple", 'payload': "multiple 1"}, ("paho/test/multiple", "multiple 2", 0, False)]
publish.multiple(msgs, hostname="mqtt.eclipseprojects.io")
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the publish.single helper function.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.publish as publish
publish.single("paho/test/single", "boo", hostname="mqtt.eclipseprojects.io")
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the publish.single helper function with unicode topic and payload.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.publish as publish
topic = u"paho/test/single/ô"
payload = u"bôô"
publish.single(topic, payload, hostname="mqtt.eclipseprojects.io")
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the publish.single helper function with unicode topic and payload.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.publish as publish
topic = u"paho/test/single/ô"
payload = u'German umlauts like "ä" ü"ö" are not supported'
publish.single(topic, payload, hostname="mqtt.eclipseprojects.io")
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2020 Frank Pagliughi <fpagliughi@mindspring.com>
# All rights reserved.
#
# This program and the accompanying materials are made available
# under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Frank Pagliughi - initial implementation
#
# This shows an example of an MQTTv5 Remote Procedure Call (RPC) server.
import json
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
# The math functions exported
def add(nums):
sum = 0
for x in nums:
sum += x
return sum
def mult(nums):
prod = 1
for x in nums:
prod *= x
return prod
# Remember that the MQTTv5 callback takes the additional 'props' parameter.
def on_connect(mqttc, userdata, flags, reason_code, props):
print(f"Connected: '{flags}', '{reason_code}', '{props}'")
if not flags.session_present:
print("Subscribing to math requests")
mqttc.subscribe("requests/math/#")
# Each incoming message should be an RPC request on the
# 'requests/math/#' topic.
def on_message(mqttc, userdata, msg):
print(msg.topic + " " + str(msg.payload))
# Get the response properties, abort if they're not given
props = msg.properties
if not hasattr(props, 'ResponseTopic') or not hasattr(props, 'CorrelationData'):
print("No reply requested")
return
corr_id = props.CorrelationData
reply_to = props.ResponseTopic
# The command parameters are in the payload
nums = json.loads(msg.payload)
# The requested command is at the end of the topic
res = 0
if msg.topic.endswith("add"):
res = add(nums)
elif msg.topic.endswith("mult"):
res = mult(nums)
# Now we have the result, res, so send it back on the 'reply_to'
# topic using the same correlation ID as the request.
print("Sending response "+str(res)+" on '"+reply_to+"': "+str(corr_id))
props = mqtt.Properties(PacketTypes.PUBLISH)
props.CorrelationData = corr_id
payload = json.dumps(res)
mqttc.publish(reply_to, payload, qos=1, properties=props)
def on_log(mqttc, obj, level, string):
print(string)
# Typically with an RPC service, you want to make sure that you're the only
# client answering requests for specific topics. Using a known client ID
# might help.
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="paho_rpc_math_srvr", protocol=mqtt.MQTTv5)
mqttc.on_message = on_message
mqttc.on_connect = on_connect
# Uncomment to enable debug messages
#mqttc.on_log = on_log
mqttc.connect(host="mqtt.eclipseprojects.io", clean_start=False)
mqttc.loop_forever()
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2016 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the subscribe.callback helper function.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.subscribe as subscribe
def print_msg(client, userdata, message):
print("%s : %s" % (message.topic, message.payload))
subscribe.callback(print_msg, "#", hostname="mqtt.eclipseprojects.io")
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2016 Roger Light <roger@atchoo.org>
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Distribution License v1.0
# which accompanies this distribution.
#
# The Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Roger Light - initial implementation
# This shows an example of using the subscribe.simple helper function.
import context # Ensures paho is in PYTHONPATH
import paho.mqtt.subscribe as subscribe
topics = ['#']
m = subscribe.simple(topics, hostname="mqtt.eclipseprojects.io", retained=False, msg_count=2)
for a in m:
print(a.topic)
print(a.payload)
+108
View File
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Eclipse Foundation Software User Agreement</title>
</head>
<body lang="EN-US">
<h2>Eclipse Foundation Software User Agreement</h2>
<p>February 1, 2011</p>
<h3>Usage Of Content</h3>
<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
(COLLECTIVELY &quot;CONTENT&quot;). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
<h3>Applicable Licenses</h3>
<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 2.0
(&quot;EPL&quot;). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v20.html">http://www.eclipse.org/legal/epl-v20.html</a>.
For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
repository (&quot;Repository&quot;) in software modules (&quot;Modules&quot;) and made available as downloadable archives (&quot;Downloads&quot;).</p>
<ul>
<li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins (&quot;Plug-ins&quot;), plug-in fragments (&quot;Fragments&quot;), and features (&quot;Features&quot;).</li>
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java&trade; ARchive) in a directory named &quot;plugins&quot;.</li>
<li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named &quot;features&quot;. Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of the Plug-ins
and/or Fragments associated with that Feature.</li>
<li>Features may also include other Features (&quot;Included Features&quot;). Within a Feature, files named &quot;feature.xml&quot; may contain a list of the names and version numbers of Included Features.</li>
</ul>
<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named &quot;about.html&quot; (&quot;Abouts&quot;). The terms and conditions governing Features and
Included Features should be contained in files named &quot;license.html&quot; (&quot;Feature Licenses&quot;). Abouts and Feature Licenses may be located in any directory of a Download or Module
including, but not limited to the following locations:</p>
<ul>
<li>The top-level (root) directory</li>
<li>Plug-in and Fragment directories</li>
<li>Inside Plug-ins and Fragments packaged as JARs</li>
<li>Sub-directories of the directory named &quot;src&quot; of certain Plug-ins</li>
<li>Feature directories</li>
</ul>
<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license (&quot;Feature Update License&quot;) during the
installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
inform you where you can locate them. Feature Update Licenses may be found in the &quot;license&quot; property of files named &quot;feature.properties&quot; found within a Feature.
Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
that directory.</p>
<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
<ul>
<li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
<li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
<li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
<li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
<li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
<li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
</ul>
<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
<h3>Use of Provisioning Technology</h3>
<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
Update Manager (&quot;Provisioning Technology&quot;) for the purpose of allowing users to install software, documentation, information and/or
other materials (collectively &quot;Installable Software&quot;). This capability is provided with the intent of allowing such users to
install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a
href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
(&quot;Specification&quot;).</p>
<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
<ol>
<li>A series of actions may occur (&quot;Provisioning Process&quot;) in which a user may execute the Provisioning Technology
on a machine (&quot;Target Machine&quot;) with the intent of installing, extending or updating the functionality of an Eclipse-based
product.</li>
<li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
accessed and copied to the Target Machine.</li>
<li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
Software (&quot;Installable Software Agreement&quot;) and such Installable Software Agreement shall be accessed from the Target
Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
</ol>
<h3>Cryptography</h3>
<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
possession, or use, and re-export of encryption software, to see if this is permitted.</p>
<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
</body>
</html>
+146
View File
@@ -0,0 +1,146 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "paho-mqtt"
dynamic = ["version"]
description = "MQTT version 5.0/3.1.1 client class"
readme = "README.rst"
# see https://lists.spdx.org/g/Spdx-legal/topic/request_for_adding_eclipse/67981884
# for why Eclipse Distribution License v1.0 is listed as BSD-3-Clause
license = { text = "EPL-2.0 OR BSD-3-Clause" }
requires-python = ">=3.7"
authors = [
{ name = "Roger Light", email = "roger@atchoo.org" },
]
keywords = [
"paho",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved",
"Natural Language :: English",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Communications",
"Topic :: Internet",
]
dependencies = []
[project.optional-dependencies]
proxy = [
"PySocks",
]
[project.urls]
Homepage = "http://eclipse.org/paho"
[tool.coverage.report]
exclude_also = [
"@(abc\\.)?abstractmethod",
"def __repr__",
"except ImportError:",
"if TYPE_CHECKING:",
"raise AssertionError",
"raise NotImplementedError",
]
[tool.hatch.build.targets.sdist]
include = [
"src/paho",
"/examples",
"/tests",
"about.html",
"CONTRIBUTING.md",
"edl-v10",
"epl-v20",
"LICENSE.txt",
"notice.html",
"README.rst",
]
[tool.hatch.build.targets.wheel]
sources = ["src"]
include = [
"src/paho",
]
[tool.hatch.version]
path = "src/paho/mqtt/__init__.py"
[tool.mypy]
[[tool.mypy.overrides]]
module = "paho.mqtt.client"
# check_untyped_defs = true
# disallow_untyped_calls = true
# disallow_incomplete_defs = true
disallow_untyped_defs = true
[tool.pytest.ini_options]
addopts = ["-r", "xs"]
testpaths = "tests src"
filterwarnings = [
"ignore:Callback API version 1 is deprecated, update to latest version"
]
[tool.ruff]
exclude = ["test/lib/python/*"]
extend-select = [
"B",
"C4",
"E",
"E9",
"F63",
"F7",
"F82",
"FLY", # flynt
"I",
"ISC",
"PERF",
"S", # Bandit
"UP",
"RUF",
"W",
]
ignore = []
line-length = 167
[tool.ruff.per-file-ignores]
"examples/**/*.py" = [
"B",
"E402",
"E711",
"E741",
"F401",
"F811",
"F841",
"I",
"PERF",
"S",
"UP",
]
"tests/**/*.py" = [
"F811",
"PERF203",
"S101",
"S105",
"S106",
]
[tool.typos.default.extend-words]
Mosquitto = "Mosquitto"
[tool.typos.type.sh.extend-words]
# gen.sh use the openssl option pass(word) in
passin = "passin"
View File
+5
View File
@@ -0,0 +1,5 @@
import pathlib
tests_path = pathlib.Path(__file__).parent
lib_path = tests_path.parent
ssl_path = tests_path / "ssl"
+223
View File
@@ -0,0 +1,223 @@
import binascii
import struct
from typing import Tuple
def dump_packet(prefix: str, data: bytes) -> None:
try:
data = to_string(data)
print(prefix, ": ", data, sep="")
except struct.error:
data = binascii.b2a_hex(data).decode('utf8')
print(prefix, " (not decoded): 0x", data, sep="")
def remaining_length(packet: bytes) -> Tuple[bytes, int]:
l = min(5, len(packet)) # noqa: E741
all_bytes = struct.unpack("!" + "B" * l, packet[:l])
mult = 1
rl = 0
for i in range(1, l - 1):
byte = all_bytes[i]
rl += (byte & 127) * mult
mult *= 128
if byte & 128 == 0:
packet = packet[i + 1:]
break
return (packet, rl)
def to_hex_string(packet: bytes) -> str:
if not packet:
return ""
s = ""
while len(packet) > 0:
packet0 = struct.unpack("!B", packet[0])
s = s+hex(packet0[0]) + " "
packet = packet[1:]
return s
def to_string(packet: bytes) -> str:
if not packet:
return ""
packet0 = struct.unpack("!B%ds" % (len(packet)-1), bytes(packet))
packet0 = packet0[0]
cmd = packet0 & 0xF0
if cmd == 0x00:
# Reserved
return "0x00"
elif cmd == 0x10:
# CONNECT
(packet, rl) = remaining_length(packet)
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 'sBBH' + str(len(packet) - slen - 4) + 's'
(protocol, proto_ver, flags, keepalive, packet) = struct.unpack(pack_format, packet)
kind = ("clean-session" if flags & 2 else "durable")
s = f"CONNECT, proto={protocol}{proto_ver}, keepalive={keepalive}, {kind}"
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 's' + str(len(packet) - slen) + 's'
(client_id, packet) = struct.unpack(pack_format, packet)
s = s + ", id=" + str(client_id)
if flags & 4:
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 's' + str(len(packet) - slen) + 's'
(will_topic, packet) = struct.unpack(pack_format, packet)
s = s + ", will-topic=" + str(will_topic)
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 's' + str(len(packet) - slen) + 's'
(will_message, packet) = struct.unpack(pack_format, packet)
s = s + ", will-message=" + will_message
s = s + ", will-qos=" + str((flags & 24) >> 3)
s = s + ", will-retain=" + str((flags & 32) >> 5)
if flags & 128:
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 's' + str(len(packet) - slen) + 's'
(username, packet) = struct.unpack(pack_format, packet)
s = s + ", username=" + str(username)
if flags & 64:
pack_format = "!H" + str(len(packet) - 2) + 's'
(slen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(slen) + 's' + str(len(packet) - slen) + 's'
(password, packet) = struct.unpack(pack_format, packet)
s = s + ", password=" + str(password)
if flags & 1:
s = s + ", reserved=1"
return s
elif cmd == 0x20:
# CONNACK
if len(packet) == 4:
(cmd, rl, resv, rc) = struct.unpack('!BBBB', packet)
return "CONNACK, rl="+str(rl)+", res="+str(resv)+", rc="+str(rc)
elif len(packet) == 5:
(cmd, rl, flags, reason_code, proplen) = struct.unpack('!BBBBB', packet)
return "CONNACK, rl="+str(rl)+", flags="+str(flags)+", rc="+str(reason_code)+", proplen="+str(proplen)
else:
return "CONNACK, (not decoded)"
elif cmd == 0x30:
# PUBLISH
dup = (packet0 & 0x08) >> 3
qos = (packet0 & 0x06) >> 1
retain = (packet0 & 0x01)
(packet, rl) = remaining_length(packet)
pack_format = "!H" + str(len(packet) - 2) + 's'
(tlen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(tlen) + 's' + str(len(packet) - tlen) + 's'
(topic, packet) = struct.unpack(pack_format, packet)
s = "PUBLISH, rl=" + str(rl) + ", topic=" + str(topic) + ", qos=" + str(qos) + ", retain=" + str(retain) + ", dup=" + str(dup)
if qos > 0:
pack_format = "!H" + str(len(packet) - 2) + 's'
(mid, packet) = struct.unpack(pack_format, packet)
s = s + ", mid=" + str(mid)
s = s + ", payload=" + str(packet)
return s
elif cmd == 0x40:
# PUBACK
if len(packet) == 5:
(cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet)
return "PUBACK, rl="+str(rl)+", mid="+str(mid)+", reason_code="+str(reason_code)
else:
(cmd, rl, mid) = struct.unpack('!BBH', packet)
return "PUBACK, rl="+str(rl)+", mid="+str(mid)
elif cmd == 0x50:
# PUBREC
if len(packet) == 5:
(cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet)
return "PUBREC, rl="+str(rl)+", mid="+str(mid)+", reason_code="+str(reason_code)
else:
(cmd, rl, mid) = struct.unpack('!BBH', packet)
return "PUBREC, rl="+str(rl)+", mid="+str(mid)
elif cmd == 0x60:
# PUBREL
dup = (packet0 & 0x08) >> 3
(cmd, rl, mid) = struct.unpack('!BBH', packet)
return "PUBREL, rl=" + str(rl) + ", mid=" + str(mid) + ", dup=" + str(dup)
elif cmd == 0x70:
# PUBCOMP
(cmd, rl, mid) = struct.unpack('!BBH', packet)
return "PUBCOMP, rl=" + str(rl) + ", mid=" + str(mid)
elif cmd == 0x80:
# SUBSCRIBE
(packet, rl) = remaining_length(packet)
pack_format = "!H" + str(len(packet) - 2) + 's'
(mid, packet) = struct.unpack(pack_format, packet)
s = "SUBSCRIBE, rl=" + str(rl) + ", mid=" + str(mid)
topic_index = 0
while len(packet) > 0:
pack_format = "!H" + str(len(packet) - 2) + 's'
(tlen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(tlen) + 'sB' + str(len(packet) - tlen - 1) + 's'
(topic, qos, packet) = struct.unpack(pack_format, packet)
s = s + ", topic" + str(topic_index) + "=" + str(topic) + "," + str(qos)
return s
elif cmd == 0x90:
# SUBACK
(packet, rl) = remaining_length(packet)
pack_format = "!H" + str(len(packet) - 2) + 's'
(mid, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + "B" * len(packet)
granted_qos = struct.unpack(pack_format, packet)
s = "SUBACK, rl=" + str(rl) + ", mid=" + str(mid) + ", granted_qos=" + str(granted_qos[0])
for i in range(1, len(granted_qos) - 1):
s = s + ", " + str(granted_qos[i])
return s
elif cmd == 0xA0:
# UNSUBSCRIBE
(packet, rl) = remaining_length(packet)
pack_format = "!H" + str(len(packet) - 2) + 's'
(mid, packet) = struct.unpack(pack_format, packet)
s = "UNSUBSCRIBE, rl=" + str(rl) + ", mid=" + str(mid)
topic_index = 0
while len(packet) > 0:
pack_format = "!H" + str(len(packet) - 2) + 's'
(tlen, packet) = struct.unpack(pack_format, packet)
pack_format = "!" + str(tlen) + 's' + str(len(packet) - tlen) + 's'
(topic, packet) = struct.unpack(pack_format, packet)
s = s + ", topic" + str(topic_index) + "=" + str(topic)
return s
elif cmd == 0xB0:
# UNSUBACK
(cmd, rl, mid) = struct.unpack('!BBH', packet)
return "UNSUBACK, rl=" + str(rl) + ", mid=" + str(mid)
elif cmd == 0xC0:
# PINGREQ
(cmd, rl) = struct.unpack('!BB', packet)
return "PINGREQ, rl=" + str(rl)
elif cmd == 0xD0:
# PINGRESP
(cmd, rl) = struct.unpack('!BB', packet)
return "PINGRESP, rl=" + str(rl)
elif cmd == 0xE0:
# DISCONNECT
if len(packet) == 3:
(cmd, rl, reason_code) = struct.unpack('!BBB', packet)
return "DISCONNECT, rl="+str(rl)+", reason_code="+str(reason_code)
else:
(cmd, rl) = struct.unpack('!BB', packet)
return "DISCONNECT, rl="+str(rl)
elif cmd == 0xF0:
# AUTH
(cmd, rl) = struct.unpack('!BB', packet)
return "AUTH, rl="+str(rl)
raise ValueError(f"Unknown packet type {cmd}")
View File
+89
View File
@@ -0,0 +1,89 @@
import asyncio
import socket
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port
client_id = 'asyncio-test'
class AsyncioHelper:
def __init__(self, loop, client):
self.loop = loop
self.client = client
self.client.on_socket_open = self.on_socket_open
self.client.on_socket_close = self.on_socket_close
self.client.on_socket_register_write = self.on_socket_register_write
self.client.on_socket_unregister_write = self.on_socket_unregister_write
def on_socket_open(self, client, userdata, sock):
def cb():
client.loop_read()
self.loop.add_reader(sock, cb)
self.misc = self.loop.create_task(self.misc_loop())
def on_socket_close(self, client, userdata, sock):
self.loop.remove_reader(sock)
self.misc.cancel()
def on_socket_register_write(self, client, userdata, sock):
def cb():
client.loop_write()
self.loop.add_writer(sock, cb)
def on_socket_unregister_write(self, client, userdata, sock):
self.loop.remove_writer(sock)
async def misc_loop(self):
while self.client.loop_misc() == mqtt.MQTT_ERR_SUCCESS:
try:
await asyncio.sleep(1)
except asyncio.CancelledError:
break
async def main():
loop = asyncio.get_event_loop()
payload = ""
def on_connect(client, obj, flags, rc):
client.subscribe("sub-test", 1)
def on_subscribe(client, obj, mid, granted_qos):
client.unsubscribe("unsub-test")
def on_unsubscribe(client, obj, mid):
nonlocal payload
payload = "message"
def on_message(client, obj, msg):
client.publish("asyncio", qos=1, payload=payload)
def on_publish(client, obj, mid):
client.disconnect()
def on_disconnect(client, userdata, rc):
disconnected.set_result(rc)
disconnected = loop.create_future()
client = mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION1, client_id=client_id)
client.on_connect = on_connect
client.on_message = on_message
client.on_publish = on_publish
client.on_subscribe = on_subscribe
client.on_unsubscribe = on_unsubscribe
client.on_disconnect = on_disconnect
_aioh = AsyncioHelper(loop, client)
client.connect('localhost', get_test_server_port(), 60)
client.socket().setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2048)
await disconnected
if __name__ == '__main__':
asyncio.run(main())
@@ -0,0 +1,42 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "decorators-test", clean_session=True)
payload = b""
@mqttc.connect_callback()
def on_connect(mqttc, obj, flags, rc):
mqttc.subscribe("sub-test", 1)
@mqttc.subscribe_callback()
def on_subscribe(mqttc, obj, mid, granted_qos):
mqttc.unsubscribe("unsub-test")
@mqttc.unsubscribe_callback()
def on_unsubscribe(mqttc, obj, mid):
global payload
payload = "message"
@mqttc.message_callback()
def on_message(mqttc, obj, msg):
global payload
mqttc.publish("decorators", qos=1, payload=payload)
@mqttc.publish_callback()
def on_publish(mqttc, obj, mid):
mqttc.disconnect()
@mqttc.disconnect_callback()
def on_disconnect(mqttc, obj, rc):
pass # TODO: should probably test that this gets called
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,14 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-keepalive-pingreq")
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port(), keepalive=4)
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,8 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-no-clean-session", clean_session=False)
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,16 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, wait_for_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
mqttc.publish("reconnect/test", "message")
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-reconnect-on-failure", reconnect_on_failure=False)
mqttc.on_connect = on_connect
with wait_for_keyboard_interrupt():
mqttc.connect("localhost", get_test_server_port())
mqttc.loop_forever()
exit(42) # this is expected by the test case
@@ -0,0 +1,9 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-unpwd-set")
mqttc.username_pw_set("uname", "")
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,9 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-unpwd-set")
mqttc.username_pw_set("", "")
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,9 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-unpwd-set")
mqttc.username_pw_set("uname", ";'[08gn=#")
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,12 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-unpwd-unicode-set")
username = "\u00fas\u00e9rn\u00e1m\u00e9-h\u00e9ll\u00f3"
password = "h\u00e9ll\u00f3"
mqttc.username_pw_set(username, password)
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,9 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-will-set")
mqttc.will_set("topic/on/unexpected/disconnect", "will message", 1, True)
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,10 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "01-will-unpwd-set")
mqttc.username_pw_set("oibvvwqw", "#'^2hg9a&nm38*us")
mqttc.will_set("will-topic", "will message", 2, False)
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,20 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.disconnect()
def on_disconnect(mqttc, obj, rc):
mqttc.loop()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "", clean_session=True, protocol=mqtt.MQTTv311)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,20 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.subscribe("qos0/test", 0)
def on_subscribe(mqttc, obj, mid, granted_qos):
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "subscribe-qos0-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,20 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.subscribe("qos1/test", 1)
def on_subscribe(mqttc, obj, mid, granted_qos):
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "subscribe-qos1-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,20 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.subscribe("qos2/test", 2)
def on_subscribe(mqttc, obj, mid, granted_qos):
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "subscribe-qos2-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,20 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.unsubscribe("unsubscribe/test")
def on_unsubscribe(mqttc, obj, mid):
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "unsubscribe-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_unsubscribe = on_unsubscribe
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,25 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
expected_payload = b"message"
def on_message(mqttc, obj, msg):
assert msg.mid == 123, f"Invalid mid: ({msg.mid})"
assert msg.topic == "pub/qos1/receive", f"Invalid topic: ({msg.topic})"
assert msg.payload == expected_payload, f"Invalid payload: ({msg.payload})"
assert msg.qos == 1, f"Invalid qos: ({msg.qos})"
assert not msg.retain, f"Invalid retain: ({msg.retain})"
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos1-test")
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,28 @@
import logging
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
expected_payload = b"message"
def on_message(mqttc, obj, msg):
assert msg.mid == 13423, f"Invalid mid: ({msg.mid})"
assert msg.topic == "pub/qos2/receive", f"Invalid topic: ({msg.topic})"
assert msg.payload == expected_payload, f"Invalid payload: ({msg.payload})"
assert msg.qos == 2, f"Invalid qos: ({msg.qos})"
assert not msg.retain, f"Invalid retain: ({msg.retain})"
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
logging.basicConfig(level=logging.DEBUG)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos2-test", clean_session=True)
mqttc.enable_logger()
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,33 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
sent_mid = -1
def on_connect(mqttc, obj, flags, rc):
global sent_mid
assert rc == 0, f"Connect failed ({rc})"
if sent_mid == -1:
res = mqttc.publish("pub/qos1/test", "message", 1)
sent_mid = res[1]
def on_disconnect(mqttc, obj, rc):
if rc != mqtt.MQTT_ERR_SUCCESS:
mqttc.reconnect()
def on_publish(mqttc, obj, mid):
global sent_mid
assert mid == sent_mid, f"Invalid mid: ({mid})"
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos1-test", clean_session=False)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.on_publish = on_publish
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,31 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
first_connection = 1
def on_connect(mqttc, obj, flags, rc):
global first_connection
assert rc == 0, f"Connect failed ({rc})"
if first_connection == 1:
mqttc.publish("pub/qos2/test", "message", 2)
first_connection = 0
def on_disconnect(mqttc, obj, rc):
if rc != 0:
mqttc.reconnect()
def on_publish(mqttc, obj, mid):
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos2-test", clean_session=False)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.on_publish = on_publish
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,39 @@
import logging
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def expected_payload(i: int) -> bytes:
return f"message{i}".encode()
def on_message(mqttc, obj, msg):
assert msg.mid == 123, f"Invalid mid: ({msg.mid})"
assert msg.topic == "pub/qos1/receive", f"Invalid topic: ({msg.topic})"
assert msg.payload == expected_payload, f"Invalid payload: ({msg.payload})"
assert msg.qos == 1, f"Invalid qos: ({msg.qos})"
assert msg.retain is not False, f"Invalid retain: ({msg.retain})"
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
for i in range(12):
mqttc.publish("topic", expected_payload(i), qos=1)
def on_disconnect(mqttc, rc, properties):
logging.info("disconnected")
mqttc.reconnect()
logging.basicConfig(level=logging.DEBUG)
logging.info(str(mqtt))
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos1-test")
mqttc.max_inflight_messages_set(10)
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect
mqttc.on_message = on_message
mqttc.enable_logger()
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,15 @@
import paho.mqtt.client
import paho.mqtt.publish
from tests.paho_test import get_test_server_port, wait_for_keyboard_interrupt
with wait_for_keyboard_interrupt():
paho.mqtt.publish.single(
"pub/qos0/test",
"message",
qos=0,
hostname="localhost",
port=get_test_server_port(),
client_id="publish-helper-qos0-test",
protocol=paho.mqtt.client.MQTTv5,
)
@@ -0,0 +1,13 @@
import paho.mqtt.publish
from tests.paho_test import get_test_server_port, wait_for_keyboard_interrupt
with wait_for_keyboard_interrupt():
paho.mqtt.publish.single(
"pub/qos0/test",
"message",
qos=0,
hostname="localhost",
port=get_test_server_port(),
client_id="publish-helper-qos0-test",
)
@@ -0,0 +1,13 @@
import paho.mqtt.publish
from tests.paho_test import get_test_server_port, wait_for_keyboard_interrupt
with wait_for_keyboard_interrupt():
paho.mqtt.publish.single(
"pub/qos1/test",
"message",
qos=1,
hostname="localhost",
port=get_test_server_port(),
client_id="publish-helper-qos1-disconnect-test",
)
@@ -0,0 +1,24 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
sent_mid = -1
def on_connect(mqttc, obj, flags, rc):
global sent_mid
assert rc == 0, f"Connect failed ({rc})"
(res, sent_mid) = mqttc.publish("pub/qos0/no-payload/test")
def on_publish(mqttc, obj, mid):
if sent_mid == mid:
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos0-test-np", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,26 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
sent_mid = -1
def on_connect(mqttc, obj, flags, rc):
global sent_mid
assert rc == 0, f"Connect failed ({rc})"
res = mqttc.publish("pub/qos0/test", "message")
sent_mid = res[1]
def on_publish(mqttc, obj, mid):
global sent_mid, run
if sent_mid == mid:
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "publish-qos0-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,15 @@
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.publish("retain/qos0/test", "retained message", 0, True)
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "retain-qos0-test", clean_session=True)
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,23 @@
import os
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-connect-alpn", clean_session=True)
mqttc.tls_set(
os.path.join(os.environ["PAHO_SSL_PATH"], "all-ca.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.key"),
alpn_protocols=["paho-test-protocol"],
)
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,23 @@
import os
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-connect-crt-auth-pw")
mqttc.tls_set(
os.path.join(os.environ["PAHO_SSL_PATH"], "all-ca.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client-pw.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client-pw.key"),
keyfile_password="password",
)
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,22 @@
import os
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-connect-crt-auth")
mqttc.tls_set(
os.path.join(os.environ["PAHO_SSL_PATH"], "all-ca.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.key"),
)
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,18 @@
import os
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, loop_until_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
assert rc == 0, f"Connect failed ({rc})"
mqttc.disconnect()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-connect-no-auth")
mqttc.tls_set(os.path.join(os.environ["PAHO_SSL_PATH"], "all-ca.crt"))
mqttc.on_connect = on_connect
mqttc.connect("localhost", get_test_server_port())
loop_until_keyboard_interrupt(mqttc)
@@ -0,0 +1,27 @@
import os
import ssl
import paho.mqtt.client as mqtt
from tests.paho_test import get_test_server_port, wait_for_keyboard_interrupt
def on_connect(mqttc, obj, flags, rc):
raise RuntimeError("Connection should have failed!")
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-fake-cacert")
mqttc.tls_set(
os.path.join(os.environ["PAHO_SSL_PATH"], "test-fake-root-ca.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.crt"),
os.path.join(os.environ["PAHO_SSL_PATH"], "client.key"),
)
mqttc.on_connect = on_connect
with wait_for_keyboard_interrupt():
try:
mqttc.connect("localhost", get_test_server_port())
except ssl.SSLError as msg:
assert msg.errno == 1 and "certificate verify failed" in msg.strerror
else:
raise Exception("Expected SSLError")
+80
View File
@@ -0,0 +1,80 @@
import os
import signal
import subprocess
import sys
import pytest
from tests.consts import ssl_path, tests_path
from tests.paho_test import create_server_socket, create_server_socket_ssl, ssl
clients_path = tests_path / "lib" / "clients"
def _yield_server(monkeypatch, sockport):
sock, port = sockport
monkeypatch.setenv("PAHO_SERVER_PORT", str(port))
try:
yield sock
finally:
sock.close()
@pytest.fixture()
def server_socket(monkeypatch):
yield from _yield_server(monkeypatch, create_server_socket())
@pytest.fixture()
def ssl_server_socket(monkeypatch):
if ssl is None:
pytest.skip("no ssl module")
yield from _yield_server(monkeypatch, create_server_socket_ssl())
@pytest.fixture()
def alpn_ssl_server_socket(monkeypatch):
if ssl is None:
pytest.skip("no ssl module")
if not getattr(ssl, "HAS_ALPN", False):
pytest.skip("ALPN not supported in this version of Python")
yield from _yield_server(monkeypatch, create_server_socket_ssl(alpn_protocols=["paho-test-protocol"]))
def stop_process(proc: subprocess.Popen) -> None:
if sys.platform == "win32":
proc.send_signal(signal.CTRL_C_EVENT)
else:
proc.send_signal(signal.SIGINT)
try:
proc.wait(5)
except subprocess.TimeoutExpired:
proc.terminate()
@pytest.fixture()
def start_client(request: pytest.FixtureRequest):
def starter(name: str, expected_returncode: int = 0) -> None:
client_path = clients_path / name
if not client_path.exists():
raise FileNotFoundError(client_path)
env = dict(
os.environ,
PAHO_SSL_PATH=str(ssl_path),
PYTHONPATH=f"{tests_path}{os.pathsep}{os.environ.get('PYTHONPATH', '')}",
)
assert 'PAHO_SERVER_PORT' in env, "PAHO_SERVER_PORT must be set in the environment when starting a client"
proc = subprocess.Popen([ # noqa: S603
sys.executable,
str(client_path),
], env=env)
def fin():
stop_process(proc)
if proc.returncode != expected_returncode:
raise RuntimeError(f"Client {name} exited with code {proc.returncode}, expected {expected_returncode}")
request.addfinalizer(fin)
return proc
return starter
+45
View File
@@ -0,0 +1,45 @@
# Test whether asyncio works
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("asyncio-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
subscribe_packet = paho_test.gen_subscribe(mid=1, topic="sub-test", qos=1)
suback_packet = paho_test.gen_suback(mid=1, qos=1)
unsubscribe_packet = paho_test.gen_unsubscribe(mid=2, topic="unsub-test")
unsuback_packet = paho_test.gen_unsuback(mid=2)
publish_packet = paho_test.gen_publish("b2c", qos=0, payload="msg")
publish_packet_in = paho_test.gen_publish("asyncio", qos=1, mid=3, payload="message")
puback_packet_in = paho_test.gen_puback(mid=3)
disconnect_packet = paho_test.gen_disconnect()
def test_01_asyncio(server_socket, start_client):
proc = start_client("01-asyncio.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "subscribe", subscribe_packet)
conn.send(suback_packet)
paho_test.expect_packet(conn, "unsubscribe", unsubscribe_packet)
conn.send(unsuback_packet)
conn.send(publish_packet)
paho_test.expect_packet(conn, "publish", publish_packet_in)
conn.send(puback_packet_in)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
assert proc.wait() == 0
+44
View File
@@ -0,0 +1,44 @@
# Test whether callback decorators work
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("decorators-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
subscribe_packet = paho_test.gen_subscribe(mid=1, topic="sub-test", qos=1)
suback_packet = paho_test.gen_suback(mid=1, qos=1)
unsubscribe_packet = paho_test.gen_unsubscribe(mid=2, topic="unsub-test")
unsuback_packet = paho_test.gen_unsuback(mid=2)
publish_packet = paho_test.gen_publish("b2c", qos=0, payload="msg")
publish_packet_in = paho_test.gen_publish("decorators", qos=1, mid=3, payload="message")
puback_packet_in = paho_test.gen_puback(mid=3)
disconnect_packet = paho_test.gen_disconnect()
def test_01_decorators(server_socket, start_client):
start_client("01-decorators.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "subscribe", subscribe_packet)
conn.send(suback_packet)
paho_test.expect_packet(conn, "unsubscribe", unsubscribe_packet)
conn.send(unsuback_packet)
conn.send(publish_packet)
paho_test.expect_packet(conn, "publish", publish_packet_in)
conn.send(puback_packet_in)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,32 @@
# Test whether a client sends a pingreq after the keepalive time
# The client should connect with keepalive=4, clean session set,
# and client id 01-keepalive-pingreq
# The client should send a PINGREQ message after the appropriate amount of time
# (4 seconds after no traffic).
import time
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("01-keepalive-pingreq", keepalive=4)
connack_packet = paho_test.gen_connack(rc=0)
pingreq_packet = paho_test.gen_pingreq()
pingresp_packet = paho_test.gen_pingresp()
def test_01_keepalive_pingreq(server_socket, start_client):
start_client("01-keepalive-pingreq.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "pingreq", pingreq_packet)
time.sleep(1.0)
conn.send(pingresp_packet)
paho_test.expect_packet(conn, "pingreq", pingreq_packet)
@@ -0,0 +1,20 @@
# Test whether a client produces a correct connect with clean session not set.
# The client should connect with keepalive=60, clean session not
# set, and client id 01-no-clean-session.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("01-no-clean-session", clean_session=False, keepalive=60)
def test_01_no_clean_session(server_socket, start_client):
start_client("01-no-clean-session.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
@@ -0,0 +1,31 @@
# Test the reconnect_on_failure = False mode
import pytest
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("01-reconnect-on-failure", keepalive=60)
connack_packet_ok = paho_test.gen_connack(rc=0)
connack_packet_failure = paho_test.gen_connack(rc=1) # CONNACK_REFUSED_PROTOCOL_VERSION
publish_packet = paho_test.gen_publish(
"reconnect/test", qos=0, payload="message")
@pytest.mark.parametrize("ok_code", [False, True])
def test_01_reconnect_on_failure(server_socket, start_client, ok_code):
client = start_client("01-reconnect-on-failure.py", expected_returncode=42)
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
if ok_code:
conn.send(connack_packet_ok)
# Connection is a success, so we expect a publish
paho_test.expect_packet(conn, "publish", publish_packet)
else:
conn.send(connack_packet_failure)
conn.close()
# Expect the client to quit here due to socket being closed
client.wait(1)
assert client.returncode == 42
@@ -0,0 +1,21 @@
# Test whether a client produces a correct connect with a username and password.
# The client should connect with keepalive=60, clean session set,
# client id 01-unpwd-set, username set to uname and password set to empty string
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-unpwd-set", keepalive=60, username="uname", password="")
def test_01_unpwd_empty_password_set(server_socket, start_client):
start_client("01-unpwd-empty-password-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
@@ -0,0 +1,21 @@
# Test whether a client produces a correct connect with a username and password.
# The client should connect with keepalive=60, clean session set,
# client id 01-unpwd-set, username and password set to empty string.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-unpwd-set", keepalive=60, username="", password='')
def test_01_unpwd_empty_set(server_socket, start_client):
start_client("01-unpwd-empty-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
+21
View File
@@ -0,0 +1,21 @@
# Test whether a client produces a correct connect with a username and password.
# The client should connect with keepalive=60, clean session set,
# client id 01-unpwd-set, username set to uname and password set to ;'[08gn=#
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-unpwd-set", keepalive=60, username="uname", password=";'[08gn=#")
def test_01_unpwd_set(server_socket, start_client):
start_client("01-unpwd-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
@@ -0,0 +1,25 @@
# Test whether a client produces a correct connect with a unicode username and password.
# The client should connect with keepalive=60, clean session set,
# client id 01-unpwd-unicode-set, username and password from corresponding variables
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-unpwd-unicode-set",
keepalive=60,
username="\u00fas\u00e9rn\u00e1m\u00e9-h\u00e9ll\u00f3",
password="h\u00e9ll\u00f3",
)
def test_01_unpwd_unicode_set(server_socket, start_client):
start_client("01-unpwd-unicode-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
+21
View File
@@ -0,0 +1,21 @@
# Test whether a client produces a correct connect with a will.
# Will QoS=1, will retain=1.
# The client should connect with keepalive=60, clean session set,
# client id 01-will-set will topic set to topic/on/unexpected/disconnect , will
# payload set to "will message", will qos set to 1 and will retain set.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-will-set", keepalive=60, will_topic="topic/on/unexpected/disconnect",
will_qos=1, will_retain=True, will_payload="will message")
def test_01_will_set(server_socket, start_client):
start_client("01-will-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
@@ -0,0 +1,26 @@
# Test whether a client produces a correct connect with a will, username and password.
# The client should connect with keepalive=60, clean session set,
# client id 01-will-unpwd-set , will topic set to "will-topic", will payload
# set to "will message", will qos=2, will retain not set, username set to
# "oibvvwqw" and password set to "#'^2hg9a&nm38*us".
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"01-will-unpwd-set",
keepalive=60, username="oibvvwqw", password="#'^2hg9a&nm38*us",
will_topic="will-topic", will_qos=2, will_payload="will message",
)
def test_01_will_unpwd_set(server_socket, start_client):
start_client("01-will-unpwd-set.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.close()
@@ -0,0 +1,23 @@
# Test whether a client connects correctly with a zero length clientid.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("", keepalive=60, proto_ver=4)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
def test_01_zero_length_clientid(server_socket, start_client):
start_client("01-zero-length-clientid.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,40 @@
# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 0.
# The client should connect with keepalive=60, clean session set,
# and client id subscribe-qos0-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
# message to subscribe to topic "qos0/test" with QoS=0. If rc!=0, the client
# should exit with an error.
# Upon receiving the correct SUBSCRIBE message, the test will reply with a
# SUBACK message with the accepted QoS set to 0. On receiving the SUBACK
# message, the client should send a DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("subscribe-qos0-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
subscribe_packet = paho_test.gen_subscribe(mid, "qos0/test", 0)
suback_packet = paho_test.gen_suback(mid, 0)
def test_02_subscribe_qos0(server_socket, start_client):
start_client("02-subscribe-qos0.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "subscribe", subscribe_packet)
conn.send(suback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,40 @@
# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 1.
# The client should connect with keepalive=60, clean session set,
# and client id subscribe-qos1-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
# message to subscribe to topic "qos1/test" with QoS=1. If rc!=0, the client
# should exit with an error.
# Upon receiving the correct SUBSCRIBE message, the test will reply with a
# SUBACK message with the accepted QoS set to 1. On receiving the SUBACK
# message, the client should send a DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("subscribe-qos1-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
subscribe_packet = paho_test.gen_subscribe(mid, "qos1/test", 1)
suback_packet = paho_test.gen_suback(mid, 1)
def test_02_subscribe_qos1(server_socket, start_client):
start_client("02-subscribe-qos1.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "subscribe", subscribe_packet)
conn.send(suback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,40 @@
# Test whether a client sends a correct SUBSCRIBE to a topic with QoS 2.
# The client should connect with keepalive=60, clean session set,
# and client id subscribe-qos2-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE
# message to subscribe to topic "qos2/test" with QoS=2. If rc!=0, the client
# should exit with an error.
# Upon receiving the correct SUBSCRIBE message, the test will reply with a
# SUBACK message with the accepted QoS set to 2. On receiving the SUBACK
# message, the client should send a DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("subscribe-qos2-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
subscribe_packet = paho_test.gen_subscribe(mid, "qos2/test", 2)
suback_packet = paho_test.gen_suback(mid, 2)
def test_02_subscribe_qos2(server_socket, start_client):
start_client("02-subscribe-qos2.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "subscribe", subscribe_packet)
conn.send(suback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,30 @@
# Test whether a client sends a correct UNSUBSCRIBE packet.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("unsubscribe-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
unsubscribe_packet = paho_test.gen_unsubscribe(mid, "unsubscribe/test")
unsuback_packet = paho_test.gen_unsuback(mid)
def test_02_unsubscribe(server_socket, start_client):
start_client("02-unsubscribe.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "unsubscribe", unsubscribe_packet)
conn.send(unsuback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,36 @@
# Test whether a client responds correctly to a PUBLISH with QoS 1.
# The client should connect with keepalive=60, clean session set,
# and client id publish-qos1-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK the client should verify that rc==0.
# The test will send the client a PUBLISH message with topic
# "pub/qos1/receive", payload of "message", QoS=1 and mid=123. The client
# should handle this as per the spec by sending a PUBACK message.
# The client should then exit with return code==0.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("publish-qos1-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 123
publish_packet = paho_test.gen_publish(
"pub/qos1/receive", qos=1, mid=mid, payload="message")
puback_packet = paho_test.gen_puback(mid)
def test_03_publish_b2c_qos1(server_socket, start_client):
start_client("03-publish-b2c-qos1.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
conn.send(publish_packet)
paho_test.expect_packet(conn, "puback", puback_packet)
conn.close()
@@ -0,0 +1,41 @@
# Test whether a client responds correctly to a PUBLISH with QoS 1.
# The client should connect with keepalive=60, clean session set,
# and client id publish-qos1-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK the client should verify that rc==0.
# The test will send the client a PUBLISH message with topic
# "pub/qos1/receive", payload of "message", QoS=1 and mid=123. The client
# should handle this as per the spec by sending a PUBACK message.
# The client should then exit with return code==0.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("publish-qos2-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 13423
publish_packet = paho_test.gen_publish(
"pub/qos2/receive", qos=2, mid=mid, payload="message")
pubrec_packet = paho_test.gen_pubrec(mid=mid)
pubrel_packet = paho_test.gen_pubrel(mid=mid)
pubcomp_packet = paho_test.gen_pubcomp(mid)
def test_03_publish_b2c_qos2(server_socket, start_client):
start_client("03-publish-b2c-qos2.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
conn.send(publish_packet)
paho_test.expect_packet(conn, "pubrec", pubrec_packet)
conn.send(pubrel_packet)
paho_test.expect_packet(conn, "pubcomp", pubcomp_packet)
conn.close()
@@ -0,0 +1,45 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 1, then responds correctly to a disconnect.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"publish-qos1-test", keepalive=60, clean_session=False,
)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
publish_packet = paho_test.gen_publish(
"pub/qos1/test", qos=1, mid=mid, payload="message")
publish_packet_dup = paho_test.gen_publish(
"pub/qos1/test", qos=1, mid=mid, payload="message", dup=True)
puback_packet = paho_test.gen_puback(mid)
def test_03_publish_c2b_qos1_disconnect(server_socket, start_client):
start_client("03-publish-c2b-qos1-disconnect.py")
(conn, address) = server_socket.accept()
conn.settimeout(15)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
# Disconnect client. It should reconnect.
conn.close()
(conn, address) = server_socket.accept()
conn.settimeout(15)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "retried publish", publish_packet_dup)
conn.send(puback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,61 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 2 and responds to a disconnect.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"publish-qos2-test", keepalive=60, clean_session=False,
)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
mid = 1
publish_packet = paho_test.gen_publish(
"pub/qos2/test", qos=2, mid=mid, payload="message")
publish_dup_packet = paho_test.gen_publish(
"pub/qos2/test", qos=2, mid=mid, payload="message", dup=True)
pubrec_packet = paho_test.gen_pubrec(mid)
pubrel_packet = paho_test.gen_pubrel(mid)
pubcomp_packet = paho_test.gen_pubcomp(mid)
def test_03_publish_c2b_qos2_disconnect(server_socket, start_client):
start_client("03-publish-c2b-qos2-disconnect.py")
(conn, address) = server_socket.accept()
conn.settimeout(5)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
# Disconnect client. It should reconnect.
conn.close()
(conn, address) = server_socket.accept()
conn.settimeout(15)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "retried publish", publish_dup_packet)
conn.send(pubrec_packet)
paho_test.expect_packet(conn, "pubrel", pubrel_packet)
# Disconnect client. It should reconnect.
conn.close()
(conn, address) = server_socket.accept()
conn.settimeout(15)
# Complete connection and message flow.
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "retried pubrel", pubrel_packet)
conn.send(pubcomp_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,90 @@
# Test whether a client responds to max-inflight and reconnect when max-inflight is reached
# The client should connect with keepalive=60, clean session set,
# and client id publish-fill-inflight
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK the client should verify that rc==0.
# Then client should send 10 PUBLISH with QoS == 1. On client side 12 message will be
# submitted, so 2 will be queued.
# The test will wait 0.5 seconds after received the 10 PUBLISH. After this wait, it will
# disconnect the client.
# The client should re-connect and re-sent the first 10 messages.
# The test will PUBACK one message, it should receive another PUBLISH.
# The test will wait 0.5 seconds and expect no PUBLISH.
# The test will then PUBACK all message.
# The client should disconnect once everything is acked.
import pytest
import tests.paho_test as paho_test
def expected_payload(i: int) -> bytes:
return f"message{i}"
connect_packet = paho_test.gen_connect("publish-qos1-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
first_connection_publishs = [
paho_test.gen_publish(
"topic", qos=1, mid=i+1, payload=expected_payload(i),
)
for i in range(10)
]
second_connection_publishs = [
paho_test.gen_publish(
# I'm not sure we should have the mid+13.
# Currently on reconnection client will do two wrong thing:
# * it sent more than max_inflight packet
# * it re-send message both with mid = old_mid + 12 AND with mid = old_mid & dup=1
"topic", qos=1, mid=i+13, payload=expected_payload(i),
)
for i in range(12)
]
second_connection_pubacks = [
paho_test.gen_puback(i+13)
for i in range(12)
]
@pytest.mark.xfail
def test_03_publish_fill_inflight(server_socket, start_client):
start_client("03-publish-fill-inflight.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
for packet in first_connection_publishs:
paho_test.expect_packet(conn, "publish", packet)
paho_test.expect_no_packet(conn, 0.5)
conn.close()
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
for packet in second_connection_publishs[:10]:
paho_test.expect_packet(conn, "publish", packet)
paho_test.expect_no_packet(conn, 0.2)
conn.send(second_connection_pubacks[0])
paho_test.expect_packet(conn, "publish", second_connection_publishs[10])
paho_test.expect_no_packet(conn, 0.5)
for packet in second_connection_pubacks[1:11]:
conn.send(packet)
paho_test.expect_packet(conn, "publish", second_connection_publishs[11])
paho_test.expect_no_packet(conn, 0.5)
@@ -0,0 +1,40 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 0.
# Use paho.mqtt.publish helper for that.
# The client should connect with keepalive=60, clean session set,
# and client id publish-helper-qos0-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
# to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the
# client should exit with an error.
# After sending the PUBLISH message, the client should send a
# DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"publish-helper-qos0-test", keepalive=60,
)
connack_packet = paho_test.gen_connack(rc=0)
publish_packet = paho_test.gen_publish(
"pub/qos0/test", qos=0, payload="message"
)
disconnect_packet = paho_test.gen_disconnect()
def test_03_publish_helper_qos0(server_socket, start_client):
start_client("03-publish-helper-qos0.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,40 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 0.
# Use paho.mqtt.publish helper for that.
# The client should connect with keepalive=60, clean session set,
# and client id publish-helper-qos0-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
# to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the
# client should exit with an error.
# After sending the PUBLISH message, the client should send a
# DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"publish-helper-qos0-test", keepalive=60, proto_ver=5, properties=None
)
connack_packet = paho_test.gen_connack(rc=0, proto_ver=5)
publish_packet = paho_test.gen_publish(
"pub/qos0/test", qos=0, payload="message", proto_ver=5
)
disconnect_packet = paho_test.gen_disconnect()
def test_03_publish_helper_qos0_v5(server_socket, start_client):
start_client("03-publish-helper-qos0-v5.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,50 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 1,
# then responds correctly to a disconnect.
# Use paho.mqtt.publish helper for that.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect(
"publish-helper-qos1-disconnect-test", keepalive=60,
)
connack_packet = paho_test.gen_connack(rc=0)
mid = 1
publish_packet = paho_test.gen_publish(
"pub/qos1/test", qos=1, mid=mid, payload="message"
)
publish_packet_dup = paho_test.gen_publish(
"pub/qos1/test", qos=1, mid=mid, payload="message",
dup=True,
)
puback_packet = paho_test.gen_puback(mid)
disconnect_packet = paho_test.gen_disconnect()
def test_03_publish_helper_qos1_disconnect(server_socket, start_client):
start_client("03-publish-helper-qos1-disconnect.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
# Disconnect client. It should reconnect.
conn.close()
(conn, address) = server_socket.accept()
conn.settimeout(15)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "retried publish", publish_packet_dup)
conn.send(puback_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,33 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 0.
# The client should connect with keepalive=60, clean session set,
# and client id publish-qos0-test
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
# to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the
# client should exit with an error.
# After sending the PUBLISH message, the client should send a DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("publish-qos0-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
publish_packet = paho_test.gen_publish("pub/qos0/test", qos=0, payload="message")
disconnect_packet = paho_test.gen_disconnect()
def test_03_publish_qos0(server_socket, start_client):
start_client("03-publish-qos0.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,34 @@
# Test whether a client sends a correct PUBLISH to a topic with QoS 0 and no payload.
# The client should connect with keepalive=60, clean session set,
# and client id publish-qos0-test-np
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a PUBLISH message
# to topic "pub/qos0/no-payload/test" with zero length payload and QoS=0. If
# rc!=0, the client should exit with an error.
# After sending the PUBLISH message, the client should send a DISCONNECT message.
import tests.paho_test as paho_test
connect_packet = paho_test.gen_connect("publish-qos0-test-np", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
publish_packet = paho_test.gen_publish("pub/qos0/no-payload/test", qos=0)
disconnect_packet = paho_test.gen_disconnect()
def test_03_publish_qos0_no_payload(server_socket, start_client):
start_client("03-publish-qos0-no-payload.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
conn.close()
@@ -0,0 +1,25 @@
# Test whether a client sends a correct retained PUBLISH to a topic with QoS 0.
import tests.paho_test as paho_test
mid = 16
connect_packet = paho_test.gen_connect("retain-qos0-test", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
publish_packet = paho_test.gen_publish(
"retain/qos0/test", qos=0, payload="retained message", retain=True)
def test_04_retain_qos0(server_socket, start_client):
start_client("04-retain-qos0.py")
(conn, address) = server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "publish", publish_packet)
conn.close()
@@ -0,0 +1,8 @@
import paho.mqtt.client as mqtt
import pytest
def test_08_ssl_bad_cacert():
with pytest.raises(IOError):
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "08-ssl-bad-cacert")
mqttc.tls_set("this/file/doesnt/exist")
@@ -0,0 +1,38 @@
# Test whether a client produces a correct connect and subsequent disconnect when using SSL.
# Client must provide a certificate.
#
# The client should connect with keepalive=60, clean session set,
# and client id 08-ssl-connect-alpn
# It should use the CA certificate ssl/all-ca.crt for verifying the server.
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a DISCONNECT
# message. If rc!=0, the client should exit with an error.
#
# Additionally, the secure socket must have been negotiated with the "paho-test-protocol"
from tests import paho_test
from tests.paho_test import ssl
def test_08_ssl_connect_alpn(alpn_ssl_server_socket, start_client):
connect_packet = paho_test.gen_connect("08-ssl-connect-alpn", keepalive=60)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()
start_client("08-ssl-connect-alpn.py")
(conn, address) = alpn_ssl_server_socket.accept()
conn.settimeout(10)
paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)
paho_test.expect_packet(conn, "disconnect", disconnect_packet)
if ssl.HAS_ALPN:
negotiated_protocol = conn.selected_alpn_protocol()
if negotiated_protocol != "paho-test-protocol":
raise Exception(f"Unexpected protocol '{negotiated_protocol}'")
conn.close()

Some files were not shown because too many files have changed in this diff Show More