1# Copyright (c) 2014 Roger Light <roger@atchoo.org> 2# 3# All rights reserved. This program and the accompanying materials 4# are made available under the terms of the Eclipse Public License v1.0 5# and Eclipse Distribution License v1.0 which accompany this distribution. 6# 7# The Eclipse Public License is available at 8# http://www.eclipse.org/legal/epl-v10.html 9# and the Eclipse Distribution License is available at 10# http://www.eclipse.org/org/documents/edl-v10.php. 11# 12# Contributors: 13# Roger Light - initial API and implementation 14 15""" 16This module provides some helper functions to allow straightforward publishing 17of messages in a one-shot manner. In other words, they are useful for the 18situation where you have a single/multiple messages you want to publish to a 19broker, then disconnect and nothing else is required. 20""" 21from __future__ import absolute_import 22 23import collections 24 25from . import client as paho 26from .. import mqtt 27 28def _do_publish(client): 29 """Internal function""" 30 31 message = client._userdata.popleft() 32 33 if isinstance(message, dict): 34 client.publish(**message) 35 elif isinstance(message, (tuple, list)): 36 client.publish(*message) 37 else: 38 raise TypeError('message must be a dict, tuple, or list') 39 40 41def _on_connect(client, userdata, flags, rc): 42 """Internal callback""" 43 #pylint: disable=invalid-name, unused-argument 44 45 if rc == 0: 46 if len(userdata) > 0: 47 _do_publish(client) 48 else: 49 raise mqtt.MQTTException(paho.connack_string(rc)) 50 51 52def _on_publish(client, userdata, mid): 53 """Internal callback""" 54 #pylint: disable=unused-argument 55 56 if len(userdata) == 0: 57 client.disconnect() 58 else: 59 _do_publish(client) 60 61 62def multiple(msgs, hostname="localhost", port=1883, client_id="", keepalive=60, 63 will=None, auth=None, tls=None, protocol=paho.MQTTv311, 64 transport="tcp", proxy_args=None): 65 """Publish multiple messages to a broker, then disconnect cleanly. 66 67 This function creates an MQTT client, connects to a broker and publishes a 68 list of messages. Once the messages have been delivered, it disconnects 69 cleanly from the broker. 70 71 msgs : a list of messages to publish. Each message is either a dict or a 72 tuple. 73 74 If a dict, only the topic must be present. Default values will be 75 used for any missing arguments. The dict must be of the form: 76 77 msg = {'topic':"<topic>", 'payload':"<payload>", 'qos':<qos>, 78 'retain':<retain>} 79 topic must be present and may not be empty. 80 If payload is "", None or not present then a zero length payload 81 will be published. 82 If qos is not present, the default of 0 is used. 83 If retain is not present, the default of False is used. 84 85 If a tuple, then it must be of the form: 86 ("<topic>", "<payload>", qos, retain) 87 88 hostname : a string containing the address of the broker to connect to. 89 Defaults to localhost. 90 91 port : the port to connect to the broker on. Defaults to 1883. 92 93 client_id : the MQTT client id to use. If "" or None, the Paho library will 94 generate a client id automatically. 95 96 keepalive : the keepalive timeout value for the client. Defaults to 60 97 seconds. 98 99 will : a dict containing will parameters for the client: will = {'topic': 100 "<topic>", 'payload':"<payload">, 'qos':<qos>, 'retain':<retain>}. 101 Topic is required, all other parameters are optional and will 102 default to None, 0 and False respectively. 103 Defaults to None, which indicates no will should be used. 104 105 auth : a dict containing authentication parameters for the client: 106 auth = {'username':"<username>", 'password':"<password>"} 107 Username is required, password is optional and will default to None 108 if not provided. 109 Defaults to None, which indicates no authentication is to be used. 110 111 tls : a dict containing TLS configuration parameters for the client: 112 dict = {'ca_certs':"<ca_certs>", 'certfile':"<certfile>", 113 'keyfile':"<keyfile>", 'tls_version':"<tls_version>", 114 'ciphers':"<ciphers">, 'insecure':"<bool>"} 115 ca_certs is required, all other parameters are optional and will 116 default to None if not provided, which results in the client using 117 the default behaviour - see the paho.mqtt.client documentation. 118 Alternatively, tls input can be an SSLContext object, which will be 119 processed using the tls_set_context method. 120 Defaults to None, which indicates that TLS should not be used. 121 122 transport : set to "tcp" to use the default setting of transport which is 123 raw TCP. Set to "websockets" to use WebSockets as the transport. 124 proxy_args: a dictionary that will be given to the client. 125 """ 126 127 if not isinstance(msgs, collections.Iterable): 128 raise TypeError('msgs must be an iterable') 129 130 client = paho.Client(client_id=client_id, userdata=collections.deque(msgs), 131 protocol=protocol, transport=transport) 132 133 client.on_publish = _on_publish 134 client.on_connect = _on_connect 135 136 if proxy_args is not None: 137 client.proxy_set(**proxy_args) 138 139 if auth: 140 username = auth.get('username') 141 if username: 142 password = auth.get('password') 143 client.username_pw_set(username, password) 144 else: 145 raise KeyError("The 'username' key was not found, this is " 146 "required for auth") 147 148 if will is not None: 149 client.will_set(**will) 150 151 if tls is not None: 152 if isinstance(tls, dict): 153 insecure = tls.pop('insecure', False) 154 client.tls_set(**tls) 155 if insecure: 156 # Must be set *after* the `client.tls_set()` call since it sets 157 # up the SSL context that `client.tls_insecure_set` alters. 158 client.tls_insecure_set(insecure) 159 else: 160 # Assume input is SSLContext object 161 client.tls_set_context(tls) 162 163 client.connect(hostname, port, keepalive) 164 client.loop_forever() 165 166 167def single(topic, payload=None, qos=0, retain=False, hostname="localhost", 168 port=1883, client_id="", keepalive=60, will=None, auth=None, 169 tls=None, protocol=paho.MQTTv311, transport="tcp", proxy_args=None): 170 """Publish a single message to a broker, then disconnect cleanly. 171 172 This function creates an MQTT client, connects to a broker and publishes a 173 single message. Once the message has been delivered, it disconnects cleanly 174 from the broker. 175 176 topic : the only required argument must be the topic string to which the 177 payload will be published. 178 179 payload : the payload to be published. If "" or None, a zero length payload 180 will be published. 181 182 qos : the qos to use when publishing, default to 0. 183 184 retain : set the message to be retained (True) or not (False). 185 186 hostname : a string containing the address of the broker to connect to. 187 Defaults to localhost. 188 189 port : the port to connect to the broker on. Defaults to 1883. 190 191 client_id : the MQTT client id to use. If "" or None, the Paho library will 192 generate a client id automatically. 193 194 keepalive : the keepalive timeout value for the client. Defaults to 60 195 seconds. 196 197 will : a dict containing will parameters for the client: will = {'topic': 198 "<topic>", 'payload':"<payload">, 'qos':<qos>, 'retain':<retain>}. 199 Topic is required, all other parameters are optional and will 200 default to None, 0 and False respectively. 201 Defaults to None, which indicates no will should be used. 202 203 auth : a dict containing authentication parameters for the client: 204 auth = {'username':"<username>", 'password':"<password>"} 205 Username is required, password is optional and will default to None 206 if not provided. 207 Defaults to None, which indicates no authentication is to be used. 208 209 tls : a dict containing TLS configuration parameters for the client: 210 dict = {'ca_certs':"<ca_certs>", 'certfile':"<certfile>", 211 'keyfile':"<keyfile>", 'tls_version':"<tls_version>", 212 'ciphers':"<ciphers">, 'insecure':"<bool>"} 213 ca_certs is required, all other parameters are optional and will 214 default to None if not provided, which results in the client using 215 the default behaviour - see the paho.mqtt.client documentation. 216 Defaults to None, which indicates that TLS should not be used. 217 Alternatively, tls input can be an SSLContext object, which will be 218 processed using the tls_set_context method. 219 220 transport : set to "tcp" to use the default setting of transport which is 221 raw TCP. Set to "websockets" to use WebSockets as the transport. 222 proxy_args: a dictionary that will be given to the client. 223 """ 224 225 msg = {'topic':topic, 'payload':payload, 'qos':qos, 'retain':retain} 226 227 multiple([msg], hostname, port, client_id, keepalive, will, auth, tls, 228 protocol, transport, proxy_args) 229