├── README.md ├── messages.sql ├── mqtt-mysql └── mqtt-mysql-admin /README.md: -------------------------------------------------------------------------------- 1 | # mqtt-mysql 2 | MQTT mysql client and server 3 | 4 | Run ```mqtt-mysql-admin``` to clean up database. 5 | -------------------------------------------------------------------------------- /messages.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE mqtt; 2 | USE mqtt; 3 | 4 | DROP TABLE IF EXISTS messages; 5 | CREATE TABLE messages ( 6 | timestamp timestamp DEFAULT CURRENT_TIMESTAMP, 7 | topic text NOT NULL, 8 | qos tinyint(1) NOT NULL, 9 | message text NOT NULL 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 11 | 12 | DROP TABLE IF EXISTS settings; 13 | CREATE TABLE settings ( 14 | setting varchar(8) NOT NULL PRIMARY KEY, 15 | state tinyint(1) NOT NULL, 16 | timestamp timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 17 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 18 | -------------------------------------------------------------------------------- /mqtt-mysql: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # mqtt-mysql by Theo Arends 4 | # 5 | # Provides MQTT MySql service 6 | # 7 | # Execute: mqtt-mysql & 8 | # 9 | # Needs paho.mqtt.client 10 | # - git clone http://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.python.git 11 | # - python setup.py install 12 | # Needs python MySql support 13 | # - apt-get install python-mysqldb 14 | # 15 | # /select 16 | # return all logged topics as JSON data 17 | # /select/[%] [|latest] 18 | # return logged topic data as JSON 19 | # /setting/unique [0|1] 20 | # set or return a setting 21 | # 22 | # **** Start of user configuration values 23 | 24 | broker = "localhost" # MQTT broker ip address or name 25 | broker_port = 1883 # MQTT broker port 26 | 27 | sub_prefix = "mysql" # Own subscribe topic 28 | pub_prefix = "stat" # General publish topic 29 | 30 | db_hostname = "localhost" # MySQL host ip address or name 31 | db_database = "mqtt" # MySQL database name 32 | db_username = "mqttuser" # MySQL database user name 33 | db_password = "mqttpass" # MySQL database password 34 | 35 | # **** End of user configuration values 36 | 37 | M_VERSION = "1.1" 38 | 39 | M_INFO = 0 # Print messages: 0 - none, 1 - Startup, 2 - Serial, 3 - All 40 | F_UNIQUE = 0 # 1 = Output no topic name if only one topic type is found 41 | 42 | import paho.mqtt.client as mqtt 43 | import MySQLdb as mdb 44 | import time 45 | import datetime 46 | 47 | S_UNIQUE = F_UNIQUE 48 | 49 | def my_info(type, message): 50 | if M_INFO > type: 51 | print(message) 52 | 53 | def log_message(msg): 54 | with con: 55 | cur = con.cursor() 56 | cur.execute("INSERT INTO messages (topic , qos, message) VALUES (%s, %s, %s)", (msg.topic, msg.qos, msg.payload)) 57 | my_info(2, "MySQL INSERT INTO messages (topic_id , qos, message_id) VALUES ("+str(msg.topic)+", "+str(msg.qos)+", "+str(msg.payload)+")") 58 | 59 | def get_setting(setting, default): 60 | state = default 61 | with con: 62 | cur = con.cursor() 63 | cur.execute("SELECT state FROM settings WHERE setting = %s", (setting)) 64 | if cur.rowcount == 1: 65 | row = cur.fetchone() 66 | state = row[0] 67 | else: 68 | cur.execute("INSERT INTO settings (setting, state) VALUES (%s, %s)", (setting, default)) 69 | return int(state) 70 | 71 | def update_setting(setting, state): 72 | with con: 73 | cur = con.cursor() 74 | cur.execute("UPDATE settings SET state = %s WHERE setting = %s", (state, setting)) 75 | return int(state) 76 | 77 | # The callback for when the client receives a CONNACK response from the server. 78 | def on_connect(client, userdata, flags, rc): 79 | my_info(0, "\nMQTT-MySql service connected with result code "+str(rc)) 80 | 81 | # Subscribing in on_connect() means that if we lose the connection and 82 | # reconnect then subscriptions will be renewed. 83 | client.subscribe([("#",0),("/#",0),("$SYS/broker/log/#",0)]) 84 | 85 | # The callback for when a PUBLISH message is received from the server. 86 | def on_message(client, userdata, msg): 87 | global S_UNIQUE 88 | 89 | my_info(2, "MQTT subscribed |"+msg.topic+"|"+str(msg.qos)+"|"+str(msg.payload)+"|") 90 | part = msg.topic.split("/",2) # mysql/select/% 91 | if part[0] == sub_prefix: 92 | if len(part) > 1 and part[1] != "result": 93 | feedback = 1 94 | 95 | if part[1] == "version": 96 | mytopic = "version" 97 | payload = M_VERSION 98 | 99 | elif part[1] == "select": 100 | if len(part) > 2: 101 | topic = part[2] 102 | with con: 103 | cur = con.cursor() 104 | unique = 1 105 | try: 106 | history = int(msg.payload) * 60 107 | timestamp = datetime.datetime.fromtimestamp(time.time()-history).strftime('%Y-%m-%d %H:%M:%S') 108 | cur.execute("SELECT DISTINCT topic FROM messages \ 109 | WHERE topic LIKE %s AND timestamp > %s", (topic+"%", timestamp)) 110 | unique = cur.rowcount 111 | cur.execute("SELECT timestamp, topic, message FROM messages \ 112 | WHERE topic LIKE %s AND timestamp > %s ORDER BY timestamp ASC LIMIT 1000", (topic+"%", timestamp)) 113 | except: 114 | cur.execute("SELECT timestamp, topic, message FROM messages \ 115 | WHERE topic LIKE %s ORDER BY timestamp DESC LIMIT 1", (topic+"%")) 116 | pass 117 | if cur.rowcount > 0: 118 | rows = cur.fetchall() 119 | payload = "[" 120 | jsonnext = "" 121 | for row in rows: 122 | payload = payload + jsonnext + '{"time":"' + str(row[0]) +'"' 123 | if unique == 1 and S_UNIQUE != 0: 124 | topic = str(row[1]) 125 | else: 126 | payload = payload + ',"topic":"' + str(row[1]) + '"' 127 | payload = payload + ',"value":"' + str(row[2]) + '"}' 128 | jsonnext = "," 129 | payload = payload + "]" 130 | else: 131 | payload = "none" 132 | mytopic = topic 133 | else: 134 | with con: 135 | cur = con.cursor() 136 | cur.execute("SELECT DISTINCT topic FROM messages ORDER BY topic ASC LIMIT 1000") 137 | if cur.rowcount > 0: 138 | rows = cur.fetchall() 139 | payload = "[" 140 | jsonnext = "" 141 | for row in rows: 142 | payload = payload + jsonnext + '{"topic":"' + str(row[0]) + '"}' 143 | jsonnext = "," 144 | payload = payload + "]" 145 | else: 146 | payload = "none" 147 | mytopic = "topics" 148 | 149 | elif part[1] == "setting": 150 | feedback = 0 151 | if len(part) > 2: 152 | mytopic = part[2] 153 | myset = mytopic.split("/",2) 154 | 155 | if len(msg.payload) > 0: # Set 156 | state = "0" 157 | if str(msg.payload)[0] != "0": 158 | state = "1" 159 | if myset[0] == "unique": 160 | S_UNIQUE = update_setting(myset[0], state) 161 | 162 | else: # Get 163 | if myset[0] == "unique": 164 | payload = str(get_setting(myset[0], F_UNIQUE)) 165 | feedback = 1 166 | 167 | else: 168 | feedback = 0 169 | 170 | if feedback == 1: 171 | mytopic = sub_prefix + "/result/" + mytopic 172 | myquote = '"' 173 | if payload[0] == "[": 174 | myquote = "" 175 | payload = '{"result":' + myquote + payload + myquote +'}' 176 | my_info(2, "MQTT published |"+mytopic+"|"+payload+"|") 177 | client.publish(mytopic, payload) 178 | 179 | else: 180 | log_message(msg) 181 | 182 | my_info(0, "MQTT mysql service.") 183 | 184 | mainloop = 1 185 | while mainloop == 1: 186 | client = mqtt.Client() 187 | client.on_connect = on_connect 188 | client.on_message = on_message 189 | 190 | dc = 1 191 | while dc == 1: 192 | try: 193 | con = mdb.connect(db_hostname, db_username, db_password, db_database) 194 | dc = 0 195 | except: 196 | my_info(0, "Warning: No database (connection) found. Retry in one minute.") 197 | time.sleep(60) 198 | pass 199 | 200 | S_UNIQUE = get_setting("unique", F_UNIQUE) 201 | 202 | rc = 1 203 | while rc == 1: 204 | try: 205 | client.connect(broker, broker_port) 206 | rc = 0 207 | except: 208 | my_info(0, "Warning: No broker found. Retry in one minute.") 209 | time.sleep(60) 210 | pass 211 | 212 | client.publish(pub_prefix+"/MySql/MESSAGE", "Connection with broker established.") 213 | 214 | while rc == 0: 215 | try: 216 | rc = client.loop() 217 | except: 218 | rc = 1 219 | 220 | print("Warning: Connection error - Restarting.") 221 | 222 | if M_INFO > 0: 223 | mainloop = 0 224 | -------------------------------------------------------------------------------- /mqtt-mysql-admin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arendst/mqtt-mysql/36bd99fb0fe2de80620030430838d7a07dedc813/mqtt-mysql-admin --------------------------------------------------------------------------------