mirror of
https://github.com/emqx/neuron.git
synced 2026-10-17 04:40:42 +08:00
1b4d373b9c
1. Start the mosquitto broker 2. Change the mqtt client version to 3.1.1
259 lines
7.7 KiB
Python
259 lines
7.7 KiB
Python
import asyncio
|
|
import time
|
|
from collections import defaultdict, deque
|
|
|
|
from robot.api import logger
|
|
from robot.api.deco import keyword, library
|
|
from gmqtt import Client as MQTTClient, Subscription
|
|
from gmqtt.mqtt.constants import MQTTv311
|
|
|
|
ROBOT_AUTO_KEYWORDS = False
|
|
|
|
|
|
@library
|
|
class Client:
|
|
"""A keyword library for Robot Framework. It provides keywords for
|
|
performing various operations on an MQTT broker. See http://mqtt.org/
|
|
for more details on MQTT specification.
|
|
|
|
This library uses gmqtt: Python async MQTT client. For more information
|
|
on underlying methods and documentation, see:
|
|
https://github.com/wialon/gmqtt
|
|
|
|
"""
|
|
|
|
ROBOT_LIBRARY_SCOPE = "GLOBAL"
|
|
ROBOT_LIBRARY_VERSION = "0.1.0"
|
|
|
|
def __init__(self, reconnect_retries=5, reconnect_delay=0):
|
|
"""
|
|
`reconnect_retries` number of reconnect attempt in case of lost
|
|
connections, default to 5
|
|
|
|
`reconnect_delay` delay between reconnect attemps in seconds,
|
|
default to 0
|
|
|
|
"""
|
|
self._client = None
|
|
self._reconnect_retries = reconnect_retries
|
|
self._reconnect_delay = reconnect_delay
|
|
self._messages = defaultdict(deque)
|
|
self._subcribed = asyncio.Event()
|
|
self._unsubcribed = asyncio.Event()
|
|
self._recved = asyncio.Event()
|
|
self._run = asyncio.get_event_loop().run_until_complete
|
|
|
|
@keyword
|
|
def connect(
|
|
self,
|
|
host,
|
|
port=1883,
|
|
client_id="",
|
|
username="",
|
|
password="",
|
|
keepalive=60,
|
|
clean_session=True,
|
|
):
|
|
"""Connect to an MQTT broker. This is a pre-requisite step for publish
|
|
and subscribe keywords.
|
|
|
|
`host` MQTT broker host
|
|
|
|
`port` broker port (default 1883)
|
|
|
|
`client_id` if not specified, a random id is generated
|
|
|
|
`username` user name, default to ""
|
|
|
|
`password` user password, default to ""
|
|
|
|
`keepalive` keepalive in seconds
|
|
|
|
`clean_session` specifies the clean session flag for the connection
|
|
|
|
Examples:
|
|
|
|
Connect to a broker with default port and client id
|
|
| Connect | 127.0.0.1 |
|
|
|
|
Connect to a broker by specifying the port and client id explicitly
|
|
| Connect | 127.0.0.1 | 1883 | test.client |
|
|
|
|
Connect to a broker with clean session flag set to false
|
|
| Connect | 127.0.0.1 | clean_session=${false} |
|
|
|
|
"""
|
|
client = MQTTClient(client_id, clean_session=clean_session)
|
|
client.on_connect = self._on_connect
|
|
client.on_message = self._on_message
|
|
client.on_disconnect = self._on_disconnect
|
|
client.on_subscribe = self._on_subscribe
|
|
client.on_unsubscribe = self._on_unsubscribe
|
|
client.set_config(
|
|
{
|
|
"reconnect_retries": self._reconnect_retries,
|
|
"reconnect_delay": self._reconnect_delay,
|
|
}
|
|
)
|
|
self._client = client
|
|
|
|
self._run(self._client.connect(host, port, version=MQTTv311))
|
|
|
|
@keyword
|
|
def disconnect(self):
|
|
"""Disconnect from MQTT Broker.
|
|
|
|
Example:
|
|
| Disconnect |
|
|
|
|
"""
|
|
self._run(self._client.disconnect())
|
|
self._messages.clear()
|
|
|
|
@keyword
|
|
def unsubscribe_all_and_clear_messages(self):
|
|
"""Unsubscribe all subscriptions and clear all messages in queue."""
|
|
for topic in self._messages:
|
|
self.unsubscribe(topic)
|
|
self._messages.clear()
|
|
|
|
@keyword
|
|
def subscribe(self, topic, qos=0, timeout=1):
|
|
"""Subscribe to a topic and return a message payload received
|
|
within the specified time.
|
|
|
|
`topic` topic to subscribe to
|
|
|
|
`qos` quality of service for the subscription, default to QoS 0
|
|
|
|
`timeout` duration of subscription, default to 1 second
|
|
|
|
Examples:
|
|
|
|
Subscribe and get a list of all messages received within 5 seconds
|
|
| ${messages}= | Subscribe | test/test | qos=1 | timeout=5 |
|
|
|
|
"""
|
|
timeout = 1 if timeout < 1 else timeout
|
|
self._client.subscribe(Subscription(topic, qos))
|
|
if self._wait(self._subcribed, timeout=timeout):
|
|
raise Exception(
|
|
"MQTT client subscribe timeout topic=%s qos=%s" % (topic, qos))
|
|
logger.info("MQTT client subscribed to topic=%s qos=%s" % (topic, qos))
|
|
|
|
@keyword
|
|
def unsubscribe(self, topic, timeout=1):
|
|
"""Unsubscribe the client from the specified topic.
|
|
|
|
`topic` topic to unsubscribe from
|
|
|
|
`timeout` duration of unsubscription, default to 1 second
|
|
|
|
Example:
|
|
| Unsubscribe | test/mqtt_test |
|
|
|
|
"""
|
|
timeout = 1 if timeout < 1 else timeout
|
|
self._client.unsubscribe(topic)
|
|
if self._wait(self._unsubcribed, timeout=timeout):
|
|
raise Exception("MQTT client unsubscribe timeout topic=%s" % topic)
|
|
logger.info("MQTT client unsubscribed topic=%s" % topic)
|
|
|
|
@keyword
|
|
def publish(self, topic, payload, qos=0, retain=False):
|
|
"""Publish a message to a topic with specified qos and retained flag.
|
|
It is required that a connection has been established using `Connect`
|
|
keyword before using this keyword.
|
|
|
|
`topic` topic to which the message will be published
|
|
|
|
`paylod` message payload to publish
|
|
|
|
`qos` qos of the message, default to QoS 0
|
|
|
|
`retain` retained flag
|
|
|
|
Examples:
|
|
|
|
| Publish | test/test | test message | 1 | ${false} |
|
|
|
|
"""
|
|
self._client.publish(topic, payload, qos=qos, retain=retain)
|
|
|
|
@keyword
|
|
def listen(self, topic, timeout=1):
|
|
"""Listen to a topic and return a message payload received within the
|
|
specified time. Requires an Subscribe to have been called previously.
|
|
|
|
`topic` topic to listen to
|
|
|
|
`timeout` duration to listen, default to 1 second
|
|
|
|
Examples:
|
|
|
|
Listen and get a message received within 5 seconds
|
|
| ${messages}= | Listen | test/test | timeout=5 |
|
|
|
|
"""
|
|
timeout = 1 if timeout < 1 else timeout
|
|
start = time.time()
|
|
while True:
|
|
if topic in self._messages and self._messages[topic]:
|
|
msg = self._messages[topic].popleft()
|
|
return msg
|
|
|
|
if (
|
|
self._wait(self._recved, timeout=timeout)
|
|
or (time.time() - start) >= timeout
|
|
):
|
|
break
|
|
|
|
def _wait(self, event, timeout):
|
|
"""Waits for the given event within timeout.
|
|
Returns true on timeout, false otherwise."""
|
|
try:
|
|
self._run(asyncio.wait_for(event.wait(), timeout))
|
|
event.clear()
|
|
except asyncio.TimeoutError:
|
|
return True
|
|
return False
|
|
|
|
def _on_connect(self, client, flags, rc, properties):
|
|
logger.info("MQTT client connected")
|
|
|
|
def _on_message(self, client, topic, payload, qos, properties):
|
|
logger.info("MQTT client received: topic=%s qos=%s payload=%s" %
|
|
(topic, qos, payload))
|
|
self._messages[topic].append(payload.decode())
|
|
self._recved.set()
|
|
return 0
|
|
|
|
def _on_disconnect(self, client, packet, exc=None):
|
|
logger.info("MQTT client disconnected")
|
|
|
|
def _on_subscribe(self, client, mid, qos, properties):
|
|
self._subcribed.set()
|
|
|
|
def _on_unsubscribe(self, client, mid, granted_qos):
|
|
self._unsubcribed.set()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# import logging
|
|
# logging.basicConfig(level=logging.DEBUG)
|
|
|
|
host = "127.0.0.1"
|
|
topic = "test/test_mqtt"
|
|
client = Client()
|
|
|
|
client.connect(host)
|
|
client.subscribe(topic)
|
|
|
|
for i in range(3):
|
|
client.publish(topic, "hello world %s" % i)
|
|
msg = client.listen(topic)
|
|
print("MQTT client receive: topic=%s msg=%s" % (topic, msg))
|
|
|
|
client.unsubscribe(topic)
|
|
client.disconnect()
|