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:
@@ -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
|
||||
@@ -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``)
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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:])
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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))
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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()
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
@@ -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 "CONTENT"). 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
|
||||
("EPL"). 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, "Program" 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 ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</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 ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
|
||||
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</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 "features". Within a Feature, files named "feature.xml" 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 ("Included Features"). Within a Feature, files named "feature.xml" 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 "about.html" ("Abouts"). The terms and conditions governing Features and
|
||||
Included Features should be contained in files named "license.html" ("Feature Licenses"). 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 "src" 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 ("Feature Update License") 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 "license" property of files named "feature.properties" 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 ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
|
||||
other materials (collectively "Installable Software"). 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>
|
||||
("Specification").</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 ("Provisioning Process") in which a user may execute the Provisioning Technology
|
||||
on a machine ("Target Machine") 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 ("Installable Software Agreement") 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>
|
||||
@@ -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"
|
||||
@@ -0,0 +1,5 @@
|
||||
import pathlib
|
||||
|
||||
tests_path = pathlib.Path(__file__).parent
|
||||
lib_path = tests_path.parent
|
||||
ssl_path = tests_path / "ssl"
|
||||
@@ -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}")
|
||||
@@ -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")
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
Reference in New Issue
Block a user