├── README.md ├── mqtt_client ├── __init__.py ├── __main__.py ├── mqtt_client.py └── subscribe_callbacks.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # MQTT Client 2 | 3 | ver: 1.6.1 4 | 5 | [Documentation](https://mqtt.clubpulp.com/) 6 | 7 | [![Downloads](https://pepy.tech/badge/mqtt-client)](https://pepy.tech/project/mqtt-client) [![Downloads](https://pepy.tech/badge/mqtt-client/month)](https://pepy.tech/project/mqtt-client) [![Downloads](https://pepy.tech/badge/mqtt-client/week)](https://pepy.tech/project/mqtt-client) 8 | 9 | ## Install 10 | 11 | > pip install --upgrade mqtt-client 12 | 13 | ## Examples 14 | 15 | ```shell 16 | mqtt-client publish --config=my_config_file.json 17 | mqtt-client subscribe --config=my_config_file.json 18 | ``` 19 | 20 | ```shell 21 | mqtt-client publish --host=mqttbroker.testing:1883 --topic=home/room/1/up --payload=ok 22 | mqtt-client publish --host=mqttbroker.testing:1883 --topic=home/room/1/up --interactive 23 | mqtt-client subscribe --host=mqttbroker.testing:1883 --topic=home/room/1/up 24 | ``` 25 | 26 | ```shell 27 | mqtt-client subscribe --host=mqttbroker.testing:1883 --topic=home/room/1/up --callback=command --command=my_command 28 | ``` 29 | 30 | ## Usage 31 | 32 | Simple MQTT Client. 33 | 34 | ```shell 35 | Usage: 36 | mqtt-client (publish | subscribe) --config= 37 | mqtt-client publish --host= --topic= (--payload= | --interactive) [--client_id=] [--username=] [--password=] [--transport=] [--cert_path=] [--qos=] [--retain=] 38 | mqtt-client subscribe --host= --topic= [--client_id=] [--username=] [--password=] [--transport=] [--cert_path=] [--callback=] [--command=] 39 | mqtt-client (-h|--help) 40 | mqtt-client (-v|--version) 41 | 42 | Commands: 43 | publish Publish to topic from MQTT Broker. 44 | subscribe Subscribe to topic from MQTT Broker. 45 | 46 | Options: 47 | -h --help Show this screen. 48 | -v --version Show version. 49 | --config= Config file. 50 | --host= Broker Host. (Example: example.your_broker.com:1883) 51 | --topic= Topic. 52 | --payload= Payload to send. 53 | -i --interactive Interactive mode. 54 | --client_id= Client ID. 55 | --username= Username. 56 | --password= Password. 57 | --transport= TCP, TCP-TLS, WS, WS-TLS (Default: TCP) 58 | --cert_path= Path cert (Default: ./mqtt_broker_cert.pem) 59 | --qos= Qos (Default: 0) 60 | --retain= Retain (Default: false) 61 | --callback= Use a custom callback for subscriber. (default, raw, command) 62 | --command= Command for callback type command. 63 | 64 | ``` 65 | 66 | ## Example file config 67 | 68 | > mqtt-client publish --config=example_config.json 69 | 70 | ```json 71 | { 72 | "host": "mqttbroker:1883", 73 | "topic": "my_topic", 74 | "payload": "Testing Simple MQTT Client 1.5.0", 75 | "interactive": false, 76 | "client_id": "awesome-mqtt-client", 77 | "username": "user", 78 | "password": "pass", 79 | "transport": "TCP", 80 | "cert_path": "", 81 | "qos": 0, 82 | "retain": false, 83 | "callback": "", 84 | "command": "" 85 | } 86 | ``` 87 | -------------------------------------------------------------------------------- /mqtt_client/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdeancos/mqtt-client/abc7e655884b883840c79d0e71f26cf503d2da76/mqtt_client/__init__.py -------------------------------------------------------------------------------- /mqtt_client/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Usage: 5 | mqtt-client (publish | subscribe) --config= 6 | mqtt-client publish --host= --topic= (--payload= | --interactive) [--client_id=] [--username=] [--password=] [--transport=] [--cert_path=] [--qos=] [--retain=] 7 | mqtt-client subscribe --host= --topic= [--client_id=] [--username=] [--password=] [--transport=] [--cert_path=] [--callback=] [--command=] 8 | mqtt-client (-h|--help) 9 | mqtt-client (-v|--version) 10 | 11 | Commands: 12 | publish Publish to topic from MQTT Broker. 13 | subscribe Subscribe to topic from MQTT Broker. 14 | 15 | Options: 16 | -h --help Show this screen. 17 | -v --version Show version. 18 | --config= Config file. 19 | --host= Broker Host. (Example: example.your_broker.com:1883) 20 | --topic= Topic. 21 | --payload= Payload to send. 22 | -i --interactive Interactive mode. 23 | --client_id= Client ID. 24 | --username= Username. 25 | --password= Password. 26 | --transport= TCP, TCP-TLS, WS, WS-TLS (Default: TCP) 27 | --cert_path= Path cert (Default: ./mqtt_broker_cert.pem) 28 | --qos= Qos (Default: 0) 29 | --retain= Retain (Default: false) 30 | --callback= Use a custom callback for subscriber. (default, raw, command) 31 | --command= Command for callback type command. 32 | 33 | """ 34 | from docopt import docopt 35 | import readline 36 | import json 37 | 38 | from terminaltables import SingleTable 39 | 40 | from mqtt_client import mqtt_client 41 | 42 | NAME, VERSION = 'MQTT Client', '1.6.1' 43 | AUTHOR = 'Samuel de Ancos (2018-2023) ' 44 | 45 | 46 | def main(): 47 | arguments = docopt(f'{NAME} {VERSION}\n{AUTHOR}\n\n{__doc__}', version=f'{NAME} {VERSION}') 48 | 49 | config = None 50 | if arguments['--config']: 51 | with open(arguments['--config']) as f: 52 | _config_content = f.read() 53 | config = json.loads(_config_content) 54 | host, port = config.get('host', 'localhost:1883').split(':') 55 | port = int(port) 56 | topic = config.get('topic') 57 | client_id = config.get('client_id', False) 58 | transport = config.get('transport', 'TCP') 59 | path = config.get('cert_path') 60 | username, password = config.get('username'), config.get('password') 61 | callback, command = config.get('callback'), config.get('command') 62 | else: 63 | host, port = 'localhost', 1883 64 | topic = None 65 | client_id = False 66 | transport = 'TCP' 67 | path = None 68 | username, password = None, None 69 | callback, command = None, None 70 | 71 | if arguments['--host']: 72 | try: 73 | host, port = arguments['--host'].split(':') 74 | except Exception: 75 | exit('│ERROR│ broker host failed. Example: example.your_broker.com:1883') 76 | if port: 77 | port = int(port) 78 | 79 | if '--client_id' in arguments and arguments['--client_id']: 80 | client_id = arguments['--client_id'] 81 | 82 | if '--transport' in arguments and arguments['--transport']: 83 | transport = arguments['--transport'] 84 | 85 | if '--cert_path' in arguments and arguments['--cert_path']: 86 | path = arguments['--cert_path'] 87 | 88 | if '--username' in arguments and arguments['--username']: 89 | username = arguments['--username'] 90 | 91 | if '--password' in arguments and arguments['--password']: 92 | password = arguments['--password'] 93 | 94 | if '--callback' in arguments and arguments['--callback']: 95 | callback = arguments['--callback'] 96 | 97 | if '--command' in arguments and arguments['--command']: 98 | command = arguments['--command'] 99 | 100 | print(SingleTable([[NAME, VERSION]]).table) 101 | 102 | if not topic: 103 | topic = arguments['--topic'] 104 | 105 | try: 106 | mqtt_handler = mqtt_client.connect_to_broker( 107 | host=host, 108 | port=port, 109 | topic=topic, 110 | client_id=client_id, 111 | username=username, 112 | password=password, 113 | transport=transport, 114 | cert_path=path 115 | ) 116 | mqtt_handler.connect() 117 | except Exception as ex: 118 | exit(f'│ERROR│ {ex}') 119 | 120 | if arguments['publish']: 121 | if config and 'qos' in config: 122 | qos = config.get('qos', 0) 123 | else: 124 | qos = 0 125 | if '--qos' in arguments and arguments['--qos']: 126 | qos = int(arguments['--qos']) 127 | 128 | if config and 'retain' in config: 129 | retain = bool(config.get('retain', False)) 130 | else: 131 | retain = 0 132 | if '--retain' in arguments and arguments['--retain']: 133 | retain = bool(arguments['--retain']) 134 | 135 | if (config and not 'interactive' in config) or not arguments['--interactive']: 136 | if config and 'payload' in config: 137 | payload = config['payload'] 138 | elif '--payload' in arguments: 139 | payload = arguments['--payload'] 140 | else: 141 | exit(f'│ERROR│ Not payload defined') 142 | 143 | is_published = mqtt_client.publish(mqtt_handler=mqtt_handler, payload=payload, qos=qos, retain=retain) 144 | else: 145 | mqtt_handler.loop_start() 146 | exit_by = '' 147 | while True: 148 | try: 149 | payload = input('[insert payload] ? ') 150 | is_published = mqtt_client.publish(mqtt_handler=mqtt_handler, payload=payload, 151 | qos=qos, retain=retain) 152 | except KeyboardInterrupt: 153 | exit_by = '[CTRL+C] Exit' 154 | break 155 | except EOFError: 156 | exit_by = '[CTRL+D] Exit' 157 | break 158 | 159 | mqtt_handler.loop_stop() 160 | 161 | if exit_by is not '': 162 | exit(exit_by) 163 | 164 | if arguments['subscribe']: 165 | mqtt_client.subscribe(mqtt_handler=mqtt_handler, callback=callback, command=command) 166 | 167 | if __name__ == '__main__': 168 | main() 169 | -------------------------------------------------------------------------------- /mqtt_client/mqtt_client.py: -------------------------------------------------------------------------------- 1 | import ssl, time 2 | from pathlib import Path 3 | 4 | import random 5 | import string 6 | 7 | import paho.mqtt.client as mqtt 8 | from terminaltables import SingleTable 9 | 10 | from mqtt_client.subscribe_callbacks import ( 11 | default_subscribe_callback, 12 | subscribe_callback_raw, 13 | subscribe_callback_command, 14 | ) 15 | 16 | CERT_DEFAULT_PATH = 'mqtt_broker_cert.pem' 17 | CONNECT_MQTT_BROKER = False 18 | TIMEOUT_DEFAULT = 5 19 | 20 | 21 | class MqttWrapper: 22 | def __init__(self, host, port, topic, auth, client_id=False, transport='tcp'): 23 | self.host = host 24 | self.port = port 25 | self.auth = auth 26 | self.timeout = TIMEOUT_DEFAULT 27 | self.topic = topic 28 | self._set_transport(transport=transport) 29 | self.cert_path = CERT_DEFAULT_PATH 30 | 31 | clean_session = False 32 | if client_id is False: 33 | client_id = 'mqtt-client-' + ''.join(random.choice(string.ascii_lowercase) for i in range(6)) 34 | clean_session = True 35 | 36 | self.client_id = client_id 37 | self.client = mqtt.Client(self.client_id, clean_session, transport=self.transport) 38 | self.client.on_connect = self.on_connect 39 | 40 | def _set_transport(self, transport): 41 | if 'TCP' == transport or 'tcp' == transport: 42 | self.transport, self.tls = 'tcp', False 43 | elif 'TCP-TLS' == transport or 'tcp-tls' == transport: 44 | self.transport, self.tls = 'tcp', True 45 | elif 'WS' == transport or 'ws' == transport: 46 | self.transport, self.tls = 'websocket', False 47 | elif 'WS-TLS' == transport or 'ws-tls' == transport: 48 | self.transport, self.tls = 'websocket', True 49 | 50 | def set_tls(self, cert_path=None): 51 | if cert_path: 52 | self.cert_path = cert_path 53 | my_file = Path(self.cert_path) 54 | if not my_file.is_file(): 55 | self.cert_path = None 56 | 57 | self.client.tls_set(self.cert_path, certfile=None, keyfile=None, 58 | cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS, ciphers=None) 59 | 60 | # print(f'- SET TLS: {self.cert_path}') 61 | 62 | def connect(self): 63 | if 'username' in self.auth and 'password' in self.auth: 64 | if self.auth['username'] and self.auth['password']: 65 | self.client.username_pw_set(username=self.auth['username'], password=self.auth['password']) 66 | self.client.connect(self.host, self.port, self.timeout) 67 | 68 | def on_message(self, func): 69 | self.client.on_message = func 70 | 71 | def on_connect(self, mqttc, obj, flags, rc): 72 | if rc != 0: 73 | print('│ERROR│ from connect - rc: {}'.format(rc)) 74 | 75 | def loop_start(self): 76 | self.client.loop_start() 77 | 78 | def loop_stop(self): 79 | self.client.loop_stop(force=False) 80 | 81 | def loop_forever(self): 82 | try: 83 | self.client.loop_forever() 84 | except KeyboardInterrupt: 85 | exit('│CTRL+C│ Exit by KeyboardInterrupt') 86 | 87 | def publish(self, payload, qos=0, retain=False): 88 | try: 89 | message_info = self.client.publish(self.topic, payload=payload, qos=qos, retain=retain) 90 | message_info.wait_for_publish() 91 | return message_info.is_published() 92 | except Exception as ex: 93 | exit(ex) 94 | 95 | 96 | def connect_to_broker(host, port, topic, username, password, client_id=False, transport='tcp', cert_path=None): 97 | mqtt_handler = MqttWrapper( 98 | host=host, 99 | port=port, 100 | topic=topic, 101 | auth={'username': username, 'password': password}, 102 | client_id=client_id, 103 | transport=transport 104 | ) 105 | 106 | table_data = [ 107 | ['KEY', 'VALUE'], 108 | ['BROKER SETTINGS', f'{transport}://{host}:{port}'], 109 | ['CREDENTIALS USER/PASSWORD', f'{username if username else "-"} {password if password else "-"}'], 110 | ['CLIENT-ID', f'{mqtt_handler.client_id}'], 111 | ['TOPIC', f'{topic}'] 112 | ] 113 | 114 | print(SingleTable(table_data).table) 115 | 116 | if mqtt_handler.tls: 117 | if cert_path: 118 | mqtt_handler.set_tls(cert_path=cert_path) 119 | else: 120 | mqtt_handler.set_tls() 121 | 122 | return mqtt_handler 123 | 124 | 125 | def publish(mqtt_handler, payload, qos=0, retain=False): 126 | is_published = mqtt_handler.publish(payload=str(payload), qos=qos, retain=retain) 127 | table_data = [[f'publish to {mqtt_handler.topic}', payload, 'Published: OK' if is_published else False]] 128 | print(SingleTable(table_data).table) 129 | 130 | return is_published 131 | 132 | 133 | def subscribe(mqtt_handler, callback, command): 134 | mqtt_handler.client.subscribe(mqtt_handler.topic) 135 | 136 | if not callback or callback == 'default': 137 | callback = default_subscribe_callback 138 | 139 | if callback == 'raw': 140 | callback = subscribe_callback_raw 141 | 142 | if callback == 'command' and command: 143 | callback = subscribe_callback_command(command=command) 144 | 145 | mqtt_handler.on_message(func=callback) 146 | 147 | table_data = [[f'waiting from {callback}', '...']] 148 | print(SingleTable(table_data).table) 149 | mqtt_handler.loop_forever() 150 | -------------------------------------------------------------------------------- /mqtt_client/subscribe_callbacks.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def default_subscribe_callback(mqttc, obj, msg): 5 | try: 6 | print(f'│{msg.topic}│ payload: {msg.payload}') 7 | except Exception as ex: 8 | print(f'Error: {ex}') 9 | 10 | 11 | def subscribe_callback_raw(mqttc, obj, msg): 12 | try: 13 | print(msg.payload.decode('utf8')) 14 | except Exception as ex: 15 | print(f'Error: {ex}') 16 | 17 | 18 | def subscribe_callback_command(command): 19 | command = [command] 20 | 21 | def _(mqttc, obj, msg): 22 | command.append(msg.topic) 23 | command.append(msg.payload.decode('utf8')) 24 | #print(f'[COMMAND] {command}') 25 | 26 | response = subprocess.run(command, stdout=subprocess.PIPE) 27 | if response.stdout: 28 | print(response.stdout) 29 | if response.stderr: 30 | print(response.stderr) 31 | command.pop(1) 32 | command.pop(1) 33 | 34 | return _ 35 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import setup, find_packages 4 | 5 | with open("README.md", "r") as fh: 6 | long_description = fh.read() 7 | 8 | setup( 9 | name='mqtt-client', 10 | version='1.6.1', 11 | description='Simple MQTT Client.', 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | keywords='mqtt', 15 | author='Samuel de Ancos', 16 | author_email='sdeancos@gmail.com', 17 | url='https://mqtt.clubpulp.com/', 18 | packages=find_packages(), 19 | include_package_data=False, 20 | python_requires='>=3.6', 21 | install_requires=[ 22 | 'docopt', 23 | 'paho-mqtt', 24 | 'terminaltables' 25 | ], 26 | entry_points={ 27 | "console_scripts": [ 28 | "mqtt-client = mqtt_client.__main__:main", 29 | ] 30 | }, 31 | ) 32 | --------------------------------------------------------------------------------