Files
adminandClaude Opus 5 79843aa2ae 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>
2026-09-02 20:46:59 +02:00

1411 lines
56 KiB
Python

"""
*******************************************************************
Copyright (c) 2013, 2019 IBM Corp.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v20.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Ian Craggs - initial implementation and/or documentation
*******************************************************************
"""
import logging
import queue
import sys
import threading
import time
import unittest
import unittest.mock
import paho.mqtt
import paho.mqtt.client
from paho.mqtt.enums import CallbackAPIVersion
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.properties import Properties
from paho.mqtt.subscribeoptions import SubscribeOptions
DEFAULT_TIMEOUT = 5
# timeout for something that should not happen but we wait to
# give it time to happen if it does due to a bug.
WAIT_NON_EVENT_TIMEOUT = 1
class Callbacks:
def __init__(self):
self.messages = queue.Queue()
self.publisheds = queue.Queue()
self.subscribeds = queue.Queue()
self.unsubscribeds = queue.Queue()
self.disconnecteds = queue.Queue()
self.connecteds = queue.Queue()
self.conn_failures = queue.Queue()
def __str__(self):
return str(self.messages.queue) + str(self.messagedicts.queue) + str(self.publisheds.queue) + \
str(self.subscribeds.queue) + \
str(self.unsubscribeds.queue) + str(self.disconnects.queue)
def clear(self):
self.__init__()
def on_connect(self, client, userdata, flags, reasonCode, properties):
self.connecteds.put({"userdata": userdata, "flags": flags,
"reasonCode": reasonCode, "properties": properties})
def on_connect_fail(self, client, userdata):
self.conn_failures.put({"userdata": userdata})
def wait_connect_fail(self):
return self.conn_failures.get(timeout=10)
def wait_connected(self):
return self.connecteds.get(timeout=2)
def on_disconnect(self, client, userdata, reasonCode, properties=None):
self.disconnecteds.put(
{"reasonCode": reasonCode, "properties": properties})
def wait_disconnected(self):
return self.disconnecteds.get(timeout=2)
def on_message(self, client, userdata, message):
self.messages.put({"userdata": userdata, "message": message})
def published(self, client, userdata, msgid):
self.publisheds.put(msgid)
def wait_published(self):
return self.publisheds.get(timeout=2)
def on_subscribe(self, client, userdata, mid, reasonCodes, properties):
self.subscribeds.put({"mid": mid, "userdata": userdata,
"properties": properties, "reasonCodes": reasonCodes})
def wait_subscribed(self):
return self.subscribeds.get(timeout=2)
def unsubscribed(self, client, userdata, mid, properties, reasonCodes):
self.unsubscribeds.put({"mid": mid, "userdata": userdata,
"properties": properties, "reasonCodes": reasonCodes})
def wait_unsubscribed(self):
return self.unsubscribeds.get(timeout=2)
def on_log(self, client, userdata, level, buf):
print(buf)
def register(self, client):
client.on_connect = self.on_connect
client.on_subscribe = self.on_subscribe
client.on_publish = self.published
client.on_unsubscribe = self.unsubscribed
client.on_message = self.on_message
client.on_disconnect = self.on_disconnect
client.on_connect_fail = self.on_connect_fail
client.on_log = self.on_log
def get_messages(self, count: int, timeout: float = DEFAULT_TIMEOUT):
result = []
deadline = time.time() + timeout
while len(result) < count:
get_timeout = deadline - time.time()
if get_timeout <= 0:
result.append(self.messages.get_nowait())
else:
result.append(self.messages.get(timeout=get_timeout))
return result
def get_at_most_messages(self, count: int, timeout: float = DEFAULT_TIMEOUT):
result = []
deadline = time.time() + timeout
try:
while len(result) < count:
get_timeout = deadline - time.time()
if get_timeout <= 0:
result.append(self.messages.get_nowait())
else:
result.append(self.messages.get(timeout=get_timeout))
except queue.Empty:
pass
return result
def cleanRetained(port):
callback = Callbacks()
curclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1,
b"clean retained",
protocol=paho.mqtt.client.MQTTv5,
)
callback.register(curclient)
curclient.connect(host="localhost", port=port)
curclient.loop_start()
callback.wait_connected()
curclient.subscribe("#", options=SubscribeOptions(qos=0))
callback.wait_subscribed() # wait for retained messages to arrive
try:
while True:
message = callback.messages.get(timeout=WAIT_NON_EVENT_TIMEOUT)
if message["message"].payload != b"":
logging.info("deleting retained message for topic", message["message"])
curclient.publish(message["message"].topic, b"", 0, retain=True)
except queue.Empty:
pass
curclient.disconnect()
curclient.loop_stop()
def cleanup(port):
# clean all client state
print("clean up starting")
clientids = ("aclient", "bclient")
def _on_connect(client, *args):
client.disconnect()
for clientid in clientids:
curclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1,
clientid.encode("utf-8"),
protocol=paho.mqtt.client.MQTTv5,
)
curclient.on_connect = _on_connect
curclient.connect(host="localhost", port=port, clean_start=True)
curclient.loop_forever()
# clean retained messages
cleanRetained(port)
print("clean up finished")
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
global callback, callback2, aclient, bclient
sys.path.append("paho.mqtt.testing/interoperability/")
try:
import mqtt.brokers
except ImportError as ie:
raise unittest.SkipTest("paho.mqtt.testing not present.") from ie
# Hack: we need to patch `signal.signal()` because `mqtt.brokers.run()`
# calls it to set up a signal handler; however, that won't work
# from a thread...
with unittest.mock.patch("signal.signal", unittest.mock.MagicMock()):
cls._test_broker = threading.Thread(
target=mqtt.brokers.run,
kwargs={
"config": ["listener 0"],
},
)
cls._test_broker.daemon = True
cls._test_broker.start()
# Wait a bit for TCP server to bind to an address
for _ in range(20):
time.sleep(0.1)
if mqtt.brokers.listeners.TCPListeners.server is not None:
port = mqtt.brokers.listeners.TCPListeners.server.socket.getsockname()[1]
if port != 0:
cls._test_broker_port = port
break
else:
raise ValueError("can't find the test broker port")
setData()
cleanup(cls._test_broker_port)
callback = Callbacks()
callback2 = Callbacks()
#aclient = mqtt_client.Client(b"\xEF\xBB\xBF" + "myclientid".encode("utf-8"))
#aclient = mqtt_client.Client("myclientid".encode("utf-8"))
aclient = paho.mqtt.client.Client(CallbackAPIVersion.VERSION1, b"aclient", protocol=paho.mqtt.client.MQTTv5)
callback.register(aclient)
bclient = paho.mqtt.client.Client(CallbackAPIVersion.VERSION1, b"bclient", protocol=paho.mqtt.client.MQTTv5)
callback2.register(bclient)
@classmethod
def tearDownClass(cls):
# Another hack to stop the test broker... we rely on fact that it use a sockserver.TCPServer
import mqtt.brokers
mqtt.brokers.listeners.TCPListeners.server.shutdown()
cls._test_broker.join(5)
def test_basic(self):
import datetime
print(datetime.datetime.now(), "start")
aclient.connect(host="localhost", port=self._test_broker_port)
aclient.loop_start()
print(datetime.datetime.now(), "loop_start")
response = callback.wait_connected()
print(datetime.datetime.now(), "connected")
self.assertEqual(response["reasonCode"].getName(), "Success")
aclient.subscribe(topics[0], options=SubscribeOptions(qos=2))
response = callback.wait_subscribed()
print(datetime.datetime.now(), "wait_subscribed")
self.assertEqual(response["reasonCodes"][0].getName(), "Granted QoS 2")
aclient.publish(topics[0], b"qos 0")
aclient.publish(topics[0], b"qos 1", 1)
aclient.publish(topics[0], b"qos 2", 2)
msgs = callback.get_messages(3)
print(datetime.datetime.now(), "publish get")
got_payload = {
x["message"].payload
for x in msgs
}
self.assertEqual(got_payload, {b"qos 0", b"qos 1", b"qos 2"})
aclient.disconnect()
callback.clear()
aclient.loop_stop()
def test_connect_fail(self):
clientid = "connection failure"
fclient, fcallback = self.new_client(clientid)
fclient.user_data_set(1)
fclient.connect_async("localhost", 1)
response = fcallback.wait_connect_fail()
self.assertEqual(response["userdata"], 1)
fclient.loop_stop()
def test_retained_message(self):
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.UserProperty = ("a", "2")
publish_properties.UserProperty = ("c", "3")
# retained messages
callback.clear()
aclient.connect(host="localhost", port=self._test_broker_port)
aclient.loop_start()
response = callback.wait_connected()
aclient.publish(topics[1], b"qos 0", 0,
retain=True, properties=publish_properties)
aclient.publish(topics[2], b"qos 1", 1,
retain=True, properties=publish_properties)
aclient.publish(topics[3], b"qos 2", 2,
retain=True, properties=publish_properties)
# wait until those messages are published
time.sleep(WAIT_NON_EVENT_TIMEOUT)
aclient.subscribe(wildtopics[5], options=SubscribeOptions(qos=2))
response = callback.wait_subscribed()
self.assertEqual(response["reasonCodes"][0].getName(), "Granted QoS 2")
msgs = callback.get_messages(3)
aclient.disconnect()
aclient.loop_stop()
self.assertTrue(callback.messages.empty())
userprops = msgs[0]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
userprops = msgs[1]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
userprops = msgs[2]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
cleanRetained(self._test_broker_port)
def test_will_message(self):
# will messages and keep alive
callback.clear()
callback2.clear()
self.assertTrue(callback2.messages.empty(), callback2.messages.queue)
will_properties = Properties(PacketTypes.WILLMESSAGE)
will_properties.WillDelayInterval = 0 # this is the default anyway
will_properties.UserProperty = ("a", "2")
will_properties.UserProperty = ("c", "3")
aclient.will_set(topics[2], payload=b"will message",
properties=will_properties)
aclient.connect(host="localhost", port=self._test_broker_port, keepalive=2)
aclient.loop_start()
response = callback.wait_connected()
bclient.connect(host="localhost", port=self._test_broker_port)
bclient.loop_start()
response = callback2.wait_connected()
bclient.subscribe(topics[2], qos=2)
response = callback2.wait_subscribed()
self.assertEqual(response["reasonCodes"][0].getName(), "Granted QoS 2")
# keep alive timeout ought to be triggered so the will message is received
aclient.loop_stop() # so that pings aren't sent
msg = callback2.messages.get(timeout=10)
bclient.disconnect()
bclient.loop_stop()
props = msg["message"].properties
self.assertEqual(props.UserProperty, [("a", "2"), ("c", "3")])
def test_zero_length_clientid(self):
logging.info("Zero length clientid test starting")
callback0 = Callbacks()
client0 = paho.mqtt.client.Client(CallbackAPIVersion.VERSION1, protocol=paho.mqtt.client.MQTTv5)
callback0.register(client0)
client0.loop_start()
# should not be rejected
client0.connect(host="localhost", port=self._test_broker_port, clean_start=False)
response = callback0.wait_connected()
self.assertEqual(response["reasonCode"].getName(), "Success")
self.assertTrue(
len(response["properties"].AssignedClientIdentifier) > 0)
client0.disconnect()
client0.loop_stop()
client0 = paho.mqtt.client.Client(CallbackAPIVersion.VERSION1, protocol=paho.mqtt.client.MQTTv5)
callback0.register(client0)
client0.loop_start()
client0.connect(host="localhost", port=self._test_broker_port) # should work
response = callback0.wait_connected()
self.assertEqual(response["reasonCode"].getName(), "Success")
self.assertTrue(
len(response["properties"].AssignedClientIdentifier) > 0)
client0.disconnect()
client0.loop_stop()
# when we supply a client id, we should not get one assigned
client0 = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1, "client0", protocol=paho.mqtt.client.MQTTv5,
)
callback0.register(client0)
client0.loop_start()
client0.connect(host="localhost", port=self._test_broker_port) # should work
response = callback0.wait_connected()
self.assertEqual(response["reasonCode"].getName(), "Success")
self.assertFalse(
hasattr(response["properties"], "AssignedClientIdentifier"))
client0.disconnect()
client0.loop_stop()
def test_offline_message_queueing(self):
# message queueing for offline clients
cleanRetained(self._test_broker_port)
ocallback = Callbacks()
clientid = b"offline message queueing"
oclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1, clientid, protocol=paho.mqtt.client.MQTTv5,
)
ocallback.register(oclient)
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.SessionExpiryInterval = 99999
oclient.loop_start()
oclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
ocallback.wait_connected()
oclient.subscribe(wildtopics[5], qos=2)
ocallback.wait_subscribed()
oclient.disconnect()
oclient.loop_stop()
bclient.loop_start()
bclient.connect(host="localhost", port=self._test_broker_port)
callback2.wait_connected()
msg1 = bclient.publish(topics[1], b"qos 0", 0)
msg2 = bclient.publish(topics[2], b"qos 1", 1)
msg3 = bclient.publish(topics[3], b"qos 2", 2)
msg1.wait_for_publish()
msg2.wait_for_publish()
msg3.wait_for_publish()
bclient.disconnect()
bclient.loop_stop()
oclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1, clientid, protocol=paho.mqtt.client.MQTTv5,
)
ocallback.register(oclient)
oclient.loop_start()
oclient.connect(host="localhost", port=self._test_broker_port, clean_start=False)
ocallback.wait_connected()
msgs = ocallback.get_at_most_messages(3)
oclient.disconnect()
oclient.loop_stop()
self.assertTrue(len(msgs) in [
2, 3], ocallback.messages.qsize())
logging.info("This server %s queueing QoS 0 messages for offline clients" %
("is" if len(msgs) == 3 else "is not"))
def test_overlapping_subscriptions(self):
# overlapping subscriptions. When there is more than one matching subscription for the same client for a topic,
# the server may send back one message with the highest QoS of any matching subscription, or one message for
# each subscription with a matching QoS.
ocallback = Callbacks()
clientid = b"overlapping subscriptions"
oclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1, clientid, protocol=paho.mqtt.client.MQTTv5,
)
ocallback.register(oclient)
oclient.loop_start()
oclient.connect(host="localhost", port=self._test_broker_port)
ocallback.wait_connected()
oclient.subscribe([(wildtopics[6], SubscribeOptions(qos=2)),
(wildtopics[0], SubscribeOptions(qos=1))])
ocallback.wait_subscribed()
oclient.publish(topics[3], b"overlapping topic filters", 2)
ocallback.wait_published()
msgs = ocallback.get_at_most_messages(2)
if len(msgs) == 1:
logging.info(
"This server is publishing one message for all matching overlapping subscriptions, not one for each.")
self.assertEqual(
msgs[0]["message"].qos, 2, msgs[0]["message"].qos)
else:
logging.info(
"This server is publishing one message per each matching overlapping subscription.")
self.assertTrue((msgs[0]["message"].qos == 2 and msgs[1]["message"].qos == 1) or
(msgs[0]["message"].qos == 1 and msgs[1]["message"].qos == 2), msgs)
oclient.disconnect()
oclient.loop_stop()
ocallback.clear()
def test_subscribe_failure(self):
# Subscribe failure. A new feature of MQTT 3.1.1 is the ability to send back negative responses to subscribe
# requests. One way of doing this is to subscribe to a topic which is not allowed to be subscribed to.
logging.info("Subscribe failure test starting")
ocallback = Callbacks()
clientid = b"subscribe failure"
oclient = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1, clientid, protocol=paho.mqtt.client.MQTTv5,
)
ocallback.register(oclient)
oclient.loop_start()
oclient.connect(host="localhost", port=self._test_broker_port)
ocallback.wait_connected()
oclient.subscribe(nosubscribe_topics[0], qos=2)
response = ocallback.wait_subscribed()
self.assertEqual(response["reasonCodes"][0].getName(), "Unspecified error",
f"return code should be 0x80 {response['reasonCodes'][0].getName()}")
oclient.disconnect()
oclient.loop_stop()
def test_unsubscribe(self):
callback2.clear()
bclient.connect(host="localhost", port=self._test_broker_port)
bclient.loop_start()
callback2.wait_connected()
bclient.subscribe(topics[0], qos=2)
callback2.wait_subscribed()
bclient.subscribe(topics[1], qos=2)
callback2.wait_subscribed()
bclient.subscribe(topics[2], qos=2)
callback2.wait_subscribed()
time.sleep(1) # wait for any retained messages, hopefully
# Unsubscribe from one topic
bclient.unsubscribe(topics[0])
callback2.wait_unsubscribed()
callback2.clear() # if there were any retained messages
aclient.connect(host="localhost", port=self._test_broker_port)
aclient.loop_start()
callback.wait_connected()
aclient.publish(topics[0], b"topic 0 - unsubscribed", 1, retain=False)
aclient.publish(topics[1], b"topic 1", 1, retain=False)
aclient.publish(topics[2], b"topic 2", 1, retain=False)
msgs = callback2.get_messages(2)
bclient.disconnect()
bclient.loop_stop()
aclient.disconnect()
aclient.loop_stop()
self.assertEqual(len(msgs), 2)
def new_client(self, clientid):
callback = Callbacks()
client = paho.mqtt.client.Client(
CallbackAPIVersion.VERSION1,
clientid.encode("utf-8"),
protocol=paho.mqtt.client.MQTTv5,
)
callback.register(client)
client.loop_start()
return client, callback
def test_session_expiry(self):
# no session expiry property == never expire
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.SessionExpiryInterval = 0 # expire immediately
clientid = "session expiry"
eclient, ecallback = self.new_client(clientid)
eclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = ecallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
eclient.subscribe(topics[0], qos=2)
ecallback.wait_subscribed()
eclient.disconnect()
ecallback.wait_disconnected()
eclient.loop_stop()
fclient, fcallback = self.new_client(clientid)
# session should immediately expire
fclient.connect_async(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
connack = fcallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
fclient.disconnect()
fcallback.wait_disconnected()
connect_properties.SessionExpiryInterval = 5
eclient, ecallback = self.new_client(clientid)
eclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = ecallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
eclient.subscribe(topics[0], qos=2)
ecallback.wait_subscribed()
eclient.disconnect()
ecallback.wait_disconnected()
eclient.loop_stop()
time.sleep(2)
# session should still exist
fclient, fcallback = self.new_client(clientid)
fclient.connect(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
connack = fcallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], True)
fclient.disconnect()
fcallback.wait_disconnected()
fclient.loop_stop()
time.sleep(6)
# session should not exist
fclient, fcallback = self.new_client(clientid)
fclient.connect(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
connack = fcallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
fclient.disconnect()
fcallback.wait_disconnected()
fclient.loop_stop()
eclient, ecallback = self.new_client(clientid)
connect_properties.SessionExpiryInterval = 1
connack = eclient.connect(
host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = ecallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
eclient.subscribe(topics[0], qos=2)
ecallback.wait_subscribed()
disconnect_properties = Properties(PacketTypes.DISCONNECT)
disconnect_properties.SessionExpiryInterval = 5
eclient.disconnect(properties=disconnect_properties)
ecallback.wait_disconnected()
eclient.loop_stop()
time.sleep(3)
# session should still exist as we changed the expiry interval on disconnect
fclient, fcallback = self.new_client(clientid)
fclient.connect(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
connack = fcallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], True)
disconnect_properties.SessionExpiryInterval = 0
fclient.disconnect(properties=disconnect_properties)
fcallback.wait_disconnected()
fclient.loop_stop()
# session should immediately expire
fclient, fcallback = self.new_client(clientid)
fclient.connect(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
connack = fcallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
fclient.disconnect()
fcallback.wait_disconnected()
fclient.loop_stop()
fclient.loop_stop()
eclient.loop_stop()
def test_user_properties(self):
clientid = "user properties"
uclient, ucallback = self.new_client(clientid)
uclient.loop_start()
uclient.connect(host="localhost", port=self._test_broker_port)
ucallback.wait_connected()
uclient.subscribe(topics[0], qos=2)
ucallback.wait_subscribed()
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.UserProperty = ("a", "2")
publish_properties.UserProperty = ("c", "3")
uclient.publish(topics[0], b"", 0, retain=False,
properties=publish_properties)
uclient.publish(topics[0], b"", 1, retain=False,
properties=publish_properties)
uclient.publish(topics[0], b"", 2, retain=False,
properties=publish_properties)
msgs = ucallback.get_messages(3)
uclient.disconnect()
ucallback.wait_disconnected()
uclient.loop_stop()
self.assertTrue(ucallback.messages.empty(), ucallback.messages.queue)
userprops = msgs[0]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
userprops = msgs[1]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
userprops = msgs[2]["message"].properties.UserProperty
self.assertTrue(userprops in [[("a", "2"), ("c", "3")], [
("c", "3"), ("a", "2")]], userprops)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
def test_payload_format(self):
clientid = "payload format"
pclient, pcallback = self.new_client(clientid)
pclient.loop_start()
pclient.connect_async(host="localhost", port=self._test_broker_port)
pcallback.wait_connected()
pclient.subscribe(topics[0], qos=2)
pcallback.wait_subscribed()
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.PayloadFormatIndicator = 1
publish_properties.ContentType = "My name"
info = pclient.publish(
topics[0], b"qos 0", 0, retain=False, properties=publish_properties)
info.wait_for_publish()
info = pclient.publish(
topics[0], b"qos 1", 1, retain=False, properties=publish_properties)
info.wait_for_publish()
info = pclient.publish(
topics[0], b"qos 2", 2, retain=False, properties=publish_properties)
info.wait_for_publish()
msgs = pcallback.get_messages(3)
pclient.disconnect()
pcallback.wait_disconnected()
pclient.loop_stop()
self.assertTrue(pcallback.messages.empty(), pcallback.messages.queue)
props = msgs[0]["message"].properties
self.assertEqual(props.ContentType, "My name", props.ContentType)
self.assertEqual(props.PayloadFormatIndicator,
1, props.PayloadFormatIndicator)
props = msgs[1]["message"].properties
self.assertEqual(props.ContentType, "My name", props.ContentType)
self.assertEqual(props.PayloadFormatIndicator,
1, props.PayloadFormatIndicator)
props = msgs[2]["message"].properties
self.assertEqual(props.ContentType, "My name", props.ContentType)
self.assertEqual(props.PayloadFormatIndicator,
1, props.PayloadFormatIndicator)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
def test_message_expiry(self):
clientid = "message expiry"
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.SessionExpiryInterval = 99999
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.loop_start()
lbclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
lbcallback.wait_connected()
lbclient.subscribe(topics[0], qos=2)
lbcallback.wait_subscribed()
disconnect_properties = Properties(PacketTypes.DISCONNECT)
disconnect_properties.SessionExpiryInterval = 999999999
lbclient.disconnect(properties=disconnect_properties)
lbcallback.wait_disconnected()
lbclient.loop_stop()
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.loop_start()
laclient.connect(host="localhost", port=self._test_broker_port)
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.MessageExpiryInterval = 1
laclient.publish(topics[0], b"qos 1 - expire", 1,
retain=False, properties=publish_properties)
laclient.publish(topics[0], b"qos 2 - expire", 2,
retain=False, properties=publish_properties)
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.MessageExpiryInterval = 6
laclient.publish(topics[0], b"qos 1 - don't expire",
1, retain=False, properties=publish_properties)
laclient.publish(topics[0], b"qos 2 - don't expire",
2, retain=False, properties=publish_properties)
time.sleep(3)
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.loop_start()
lbclient.connect(host="localhost", port=self._test_broker_port, clean_start=False)
lbcallback.wait_connected()
msgs = lbcallback.get_messages(2)
self.assertTrue(lbcallback.messages.empty(), lbcallback.messages.queue)
self.assertTrue(msgs[0]["message"].properties.MessageExpiryInterval < 6,
msgs[0]["message"].properties.MessageExpiryInterval)
self.assertTrue(msgs[1]["message"].properties.MessageExpiryInterval < 6,
msgs[1]["message"].properties.MessageExpiryInterval)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
lbclient.disconnect()
lbcallback.wait_disconnected()
lbclient.loop_stop()
def test_subscribe_options(self):
# noLocal
clientid = 'subscribe options - noLocal'
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
lacallback.wait_connected()
laclient.loop_start()
laclient.subscribe(
topics[0], options=SubscribeOptions(qos=2, noLocal=True))
lacallback.wait_subscribed()
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.connect(host="localhost", port=self._test_broker_port)
lbcallback.wait_connected()
lbclient.loop_start()
lbclient.subscribe(
topics[0], options=SubscribeOptions(qos=2, noLocal=True))
lbcallback.wait_subscribed()
laclient.publish(topics[0], b"noLocal test", 1, retain=False)
lbcallback.messages.get(timeout=DEFAULT_TIMEOUT)
try:
lacallback.messages.get(timeout=WAIT_NON_EVENT_TIMEOUT)
raise ValueError("unexpected message received")
except queue.Empty:
pass
self.assertTrue(lacallback.messages.empty(), lacallback.messages.queue)
self.assertTrue(lbcallback.messages.empty(), lbcallback.messages.queue)
laclient.disconnect()
lacallback.wait_disconnected()
lbclient.disconnect()
lbcallback.wait_disconnected()
laclient.loop_stop()
lbclient.loop_stop()
# retainAsPublished
clientid = 'subscribe options - retain as published'
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
lacallback.wait_connected()
laclient.subscribe(topics[0], options=SubscribeOptions(
qos=2, retainAsPublished=True))
lacallback.wait_subscribed()
laclient.publish(
topics[0], b"retain as published false", 1, retain=False)
laclient.publish(
topics[0], b"retain as published true", 1, retain=True)
msgs = lacallback.get_messages(2)
self.assertTrue(lacallback.messages.empty(), lacallback.messages.queue)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
self.assertEqual(msgs[0]["message"].retain, False)
self.assertEqual(msgs[1]["message"].retain, True)
# retainHandling
clientid = 'subscribe options - retain handling'
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
lacallback.wait_connected()
laclient.publish(topics[1], b"qos 0", 0, retain=True)
laclient.publish(topics[2], b"qos 1", 1, retain=True)
laclient.publish(topics[3], b"qos 2", 2, retain=True)
time.sleep(1)
# retain handling 1 only gives us retained messages on a new subscription
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=1))
lacallback.wait_subscribed()
msgs = lacallback.get_messages(3)
self.assertTrue(lacallback.messages.empty())
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
lacallback.clear()
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=1))
lacallback.wait_subscribed()
time.sleep(1)
self.assertTrue(lacallback.messages.empty())
# remove that subscription
properties = Properties(PacketTypes.UNSUBSCRIBE)
properties.UserProperty = ("a", "2")
properties.UserProperty = ("c", "3")
laclient.unsubscribe(wildtopics[5], properties)
lacallback.wait_unsubscribed()
# check that we really did remove that subscription
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=1))
lacallback.wait_subscribed()
msgs = lacallback.get_messages(3)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
lacallback.clear()
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=1))
lacallback.wait_subscribed()
time.sleep(WAIT_NON_EVENT_TIMEOUT)
self.assertTrue(lacallback.messages.empty())
# remove that subscription
properties = Properties(PacketTypes.UNSUBSCRIBE)
properties.UserProperty = ("a", "2")
properties.UserProperty = ("c", "3")
laclient.unsubscribe(wildtopics[5], properties)
lacallback.wait_unsubscribed()
lacallback.clear()
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=2))
lacallback.wait_subscribed()
self.assertTrue(lacallback.messages.empty())
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=2))
lacallback.wait_subscribed()
self.assertTrue(lacallback.messages.empty())
# remove that subscription
laclient.unsubscribe(wildtopics[5])
lacallback.wait_unsubscribed()
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=0))
lacallback.wait_subscribed()
msgs = lacallback.get_messages(3)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
lacallback.clear()
laclient.subscribe(
wildtopics[5], options=SubscribeOptions(2, retainHandling=0))
msgs = lacallback.get_messages(3)
qoss = [x["message"].qos for x in msgs]
self.assertTrue(1 in qoss and 2 in qoss and 0 in qoss, qoss)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
cleanRetained(self._test_broker_port)
def test_subscription_identifiers(self):
clientid = 'subscription identifiers'
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
lacallback.wait_connected()
laclient.loop_start()
sub_properties = Properties(PacketTypes.SUBSCRIBE)
sub_properties.SubscriptionIdentifier = 456789
laclient.subscribe(topics[0], qos=2, properties=sub_properties)
lacallback.wait_subscribed()
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.connect(host="localhost", port=self._test_broker_port)
lbcallback.wait_connected()
lbclient.loop_start()
sub_properties = Properties(PacketTypes.SUBSCRIBE)
sub_properties.SubscriptionIdentifier = 2
lbclient.subscribe(topics[0], qos=2, properties=sub_properties)
lbcallback.wait_subscribed()
sub_properties.clear()
sub_properties.SubscriptionIdentifier = 3
lbclient.subscribe(f"{topics[0]}/#", qos=2, properties=sub_properties)
lbclient.publish(topics[0], b"sub identifier test", 1, retain=False)
msg = lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
self.assertTrue(lacallback.messages.empty(), lacallback.messages.queue)
self.assertEqual(msg["message"].properties.SubscriptionIdentifier[0],
456789, msg["message"].properties.SubscriptionIdentifier)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
msg = lbcallback.messages.get(timeout=DEFAULT_TIMEOUT)
self.assertTrue(lbcallback.messages.empty(), lbcallback.messages.queue)
expected_subsids = {2, 3}
received_subsids = set(
msg["message"].properties.SubscriptionIdentifier)
self.assertEqual(received_subsids, expected_subsids, received_subsids)
lbclient.disconnect()
lbcallback.wait_disconnected()
lbclient.loop_stop()
def test_request_response(self):
clientid = 'request response'
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
lacallback.wait_connected()
laclient.loop_start()
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.connect(host="localhost", port=self._test_broker_port)
lbcallback.wait_connected()
lbclient.loop_start()
laclient.subscribe(
topics[0], options=SubscribeOptions(2, noLocal=True))
lacallback.wait_subscribed()
lbclient.subscribe(
topics[0], options=SubscribeOptions(2, noLocal=True))
lbcallback.wait_subscribed()
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.ResponseTopic = topics[0]
publish_properties.CorrelationData = b"334"
# client a is the requester
laclient.publish(topics[0], b"request", 1,
properties=publish_properties)
# client b is the responder
msg = lbcallback.messages.get(timeout=DEFAULT_TIMEOUT)
self.assertEqual(msg["message"].properties.ResponseTopic, topics[0],
msg["message"].properties)
self.assertEqual(msg["message"].properties.CorrelationData, b"334",
msg["message"].properties)
lbclient.publish(msg["message"].properties.ResponseTopic, b"response", 1,
properties=msg["message"].properties)
# client a gets the response
lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
lbclient.disconnect()
lbcallback.wait_disconnected()
lbclient.loop_stop()
def test_client_topic_alias(self):
clientid = 'client topic alias'
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.TopicAliasMaximum = 0 # server topic aliases not allowed
connect_properties.SessionExpiryInterval = 99999
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = lacallback.wait_connected()
clientTopicAliasMaximum = 0
if hasattr(connack["properties"], "TopicAliasMaximum"):
clientTopicAliasMaximum = connack["properties"].TopicAliasMaximum
if clientTopicAliasMaximum == 0:
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
return
laclient.subscribe(topics[0], qos=2)
lacallback.wait_subscribed()
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.TopicAlias = 1
laclient.publish(topics[0], b"topic alias 1",
1, properties=publish_properties)
lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
laclient.publish("", b"topic alias 2", 1,
properties=publish_properties)
lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
laclient.disconnect() # should get rid of the topic aliases but not subscriptions
lacallback.wait_disconnected()
laclient.loop_stop()
# check aliases have been deleted
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, clean_start=False,
properties=connect_properties)
laclient.publish(topics[0], b"topic alias 3", 1)
lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
publish_properties = Properties(PacketTypes.PUBLISH)
publish_properties.TopicAlias = 1
laclient.publish("", b"topic alias 4", 1,
properties=publish_properties)
# should get back a disconnect with Topic alias invalid
lacallback.wait_disconnected()
laclient.loop_stop()
def test_server_topic_alias(self):
clientid = 'server topic alias'
serverTopicAliasMaximum = 1 # server topic alias allowed
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.TopicAliasMaximum = serverTopicAliasMaximum
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
lacallback.wait_connected()
laclient.loop_start()
laclient.subscribe(topics[0], qos=2)
lacallback.wait_subscribed()
for qos in range(3):
laclient.publish(topics[0], b"topic alias 1", qos)
msgs = lacallback.get_messages(3)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
# first message should set the topic alias
self.assertTrue(hasattr(
msgs[0]["message"].properties, "TopicAlias"), msgs[0]["message"].properties)
topicalias = msgs[0]["message"].properties.TopicAlias
self.assertTrue(topicalias > 0)
self.assertEqual(msgs[0]["message"].topic, topics[0])
self.assertEqual(
msgs[1]["message"].properties.TopicAlias, topicalias)
self.assertEqual(msgs[1]["message"].topic, "")
self.assertEqual(
msgs[2]["message"].properties.TopicAlias, topicalias)
self.assertEqual(msgs[2]["message"].topic, "")
serverTopicAliasMaximum = 0 # no server topic alias allowed
connect_properties = Properties(PacketTypes.CONNECT)
# connect_properties.TopicAliasMaximum = serverTopicAliasMaximum # default is 0
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
lacallback.wait_connected()
laclient.loop_start()
laclient.subscribe(topics[0], qos=2)
lacallback.wait_subscribed()
for qos in range(3):
laclient.publish(topics[0], b"topic alias 2", qos)
msgs = lacallback.get_messages(3)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
# No topic aliases
self.assertFalse(hasattr(
msgs[0]["message"].properties, "TopicAlias"), msgs[0]["message"].properties)
self.assertFalse(hasattr(
msgs[1]["message"].properties, "TopicAlias"), msgs[1]["message"].properties)
self.assertFalse(hasattr(
msgs[2]["message"].properties, "TopicAlias"), msgs[2]["message"].properties)
serverTopicAliasMaximum = 0 # no server topic alias allowed
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.TopicAliasMaximum = serverTopicAliasMaximum # default is 0
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
lacallback.wait_connected()
laclient.loop_start()
laclient.subscribe(topics[0], qos=2)
lacallback.wait_subscribed()
for qos in range(3):
laclient.publish(topics[0], b"topic alias 3", qos)
msgs = lacallback.get_messages(3)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
# No topic aliases
self.assertFalse(hasattr(
msgs[0]["message"].properties, "TopicAlias"), msgs[0]["message"].properties)
self.assertFalse(hasattr(
msgs[1]["message"].properties, "TopicAlias"), msgs[1]["message"].properties)
self.assertFalse(hasattr(
msgs[2]["message"].properties, "TopicAlias"), msgs[2]["message"].properties)
def test_maximum_packet_size(self):
clientid = 'maximum packet size'
# 1. server max packet size
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
connack = lacallback.wait_connected()
laclient.loop_start()
serverMaximumPacketSize = 2**28-1
if hasattr(connack["properties"], "MaximumPacketSize"):
serverMaximumPacketSize = connack["properties"].MaximumPacketSize
if serverMaximumPacketSize < 65535:
# publish bigger packet than server can accept
payload = b"."*serverMaximumPacketSize
laclient.publish(topics[0], payload, 0)
# should get back a disconnect with packet size too big
response = lacallback.wait_disconnected()
self.assertEqual(response["reasonCode"].getName(),
"Packet too large", response["reasonCode"].getName())
else:
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
# 1. client max packet size
maximumPacketSize = 64 # max packet size we want to receive
connect_properties = Properties(PacketTypes.CONNECT)
connect_properties.MaximumPacketSize = maximumPacketSize
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = lacallback.wait_connected()
laclient.loop_start()
serverMaximumPacketSize = 2**28-1
if hasattr(connack["properties"], "MaximumPacketSize"):
serverMaximumPacketSize = connack["properties"].MaximumPacketSize
laclient.subscribe(topics[0], qos=2)
response = lacallback.wait_subscribed()
# send a small enough packet, should get this one back
payload = b"."*(int(maximumPacketSize/2))
laclient.publish(topics[0], payload, 0)
lacallback.messages.get(timeout=DEFAULT_TIMEOUT)
# send a packet too big to receive
payload = b"."*maximumPacketSize
laclient.publish(topics[0], payload, 1)
try:
lacallback.messages.get(timeout=WAIT_NON_EVENT_TIMEOUT)
raise ValueError("unexpected message received")
except queue.Empty:
pass
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
"""
def test_server_keep_alive(self):
clientid = 'server keep alive'
laclient, lacallback = self.new_client(clientid+" a")
laclient.connect(host="localhost", port=self._test_broker_port)
connack = lacallback.wait_connected()
laclient.loop_start()
self.assertTrue(hasattr(connack["properties"], "ServerKeepAlive"))
self.assertEqual(connack["properties"].ServerKeepAlive, 60)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
"""
def test_will_delay(self):
# the will message should be received earlier than the session expiry
clientid = 'will delay'
will_properties = Properties(PacketTypes.WILLMESSAGE)
connect_properties = Properties(PacketTypes.CONNECT)
# set the will delay and session expiry to the same value -
# then both should occur at the same time
will_properties.WillDelayInterval = 3 # in seconds
connect_properties.SessionExpiryInterval = 5
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.will_set(
topics[0], payload=b"test_will_delay will message", properties=will_properties)
laclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = lacallback.wait_connected()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
laclient.loop_start()
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.connect(host="localhost", port=self._test_broker_port, properties=connect_properties)
connack = lbcallback.wait_connected()
lbclient.loop_start()
# subscribe to will message topic
lbclient.subscribe(topics[0], qos=2)
lbcallback.wait_subscribed()
# abort client a and wait for the will message
laclient.loop_stop()
laclient.socket().close()
start = time.time()
msg = lbcallback.messages.get(DEFAULT_TIMEOUT)
duration = time.time() - start
self.assertAlmostEqual(duration, 4, delta=1)
self.assertEqual(msg["message"].topic, topics[0])
self.assertEqual(
msg["message"].payload, b"test_will_delay will message")
lbclient.disconnect()
lbcallback.wait_disconnected()
lbclient.loop_stop()
def test_shared_subscriptions(self):
clientid = 'shared subscriptions'
shared_sub_topic = f"$share/sharename/{topic_prefix}x"
shared_pub_topic = f"{topic_prefix}x"
laclient, lacallback = self.new_client(f"{clientid} a")
laclient.connect(host="localhost", port=self._test_broker_port)
connack = lacallback.wait_connected()
laclient.loop_start()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
laclient.subscribe(
[(shared_sub_topic, SubscribeOptions(2)), (topics[0], SubscribeOptions(2))])
lacallback.wait_subscribed()
lbclient, lbcallback = self.new_client(f"{clientid} b")
lbclient.connect(host="localhost", port=self._test_broker_port)
connack = lbcallback.wait_connected()
lbclient.loop_start()
self.assertEqual(connack["reasonCode"].getName(), "Success")
self.assertEqual(connack["flags"]["session present"], False)
lbclient.subscribe(
[(shared_sub_topic, SubscribeOptions(2)), (topics[0], 2)])
lbcallback.wait_subscribed()
lacallback.clear()
lbcallback.clear()
count = 1
for i in range(count):
lbclient.publish(topics[0], f"message {i}", 0)
lacallback.get_messages(count)
lbcallback.get_messages(count)
self.assertTrue(lacallback.messages.empty())
self.assertTrue(lbcallback.messages.empty())
lacallback.clear()
lbcallback.clear()
for i in range(count):
lbclient.publish(shared_pub_topic, f"message {i}", 0)
# Each message should only be received once
result = []
deadline = time.time() + DEFAULT_TIMEOUT
while len(result) < count and time.time() < deadline:
get_timeout = deadline - time.time()
try:
if get_timeout <= 0:
result.append(lacallback.messages.get_nowait())
else:
result.append(lacallback.messages.get(timeout=get_timeout))
except queue.Empty:
# The message could be sent to other client, so empty queue
# could be normal
pass
try:
get_timeout = deadline - time.time()
if get_timeout <= 0:
result.append(lbcallback.messages.get_nowait())
else:
result.append(lbcallback.messages.get(timeout=get_timeout))
except queue.Empty:
# The message could be sent to other client, so empty queue
# could be normal
pass
self.assertEqual(
{x["message"].payload for x in result},
{f"message {i}".encode() for i in range(count)}
)
laclient.disconnect()
lacallback.wait_disconnected()
laclient.loop_stop()
lbclient.disconnect()
lbcallback.wait_disconnected()
lbclient.loop_stop()
def setData():
global topics, wildtopics, nosubscribe_topics, topic_prefix
topics = ("TopicA", "TopicA/B", "Topic/C", "TopicA/C", "/TopicA")
wildtopics = ("TopicA/+", "+/C", "#", "/#", "/+", "+/+", "TopicA/#")
nosubscribe_topics = ("test/nosubscribe",)
topic_prefix = "paho.mqtt.client.mqttv5/"