# # mqtt service # import json, time, logging, ssl import paho.mqtt.client as mqtt logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class MqttBase(object): def __init__(self, **p): super().__init__() self.host = p.get('host') self.port = p.get('port') self.username = p.get('username') self.password = p.get('password') self.keepalive = p.get('keepalive') self.use_ssl = p.get('use_ssl', False) self.ca_cert = p.get('ca_cert') self.tls_version = p.get('tls_version', ssl.PROTOCOL_TLSv1_2) self.subscriptions = [(topic, qos) for topic, qos in p.get('topic_subs', [])] self.unsubs = self.client_get_unsubs() self.topic_base = p.get('topic_base', '') self.on_message_callback = p.get('on_messages', self._on_message_callback) self.on_bytes_callback = p.get('on_bytes', self._on_bytes_callback) # mqtt Client self.client = mqtt.Client() if self.username: self.client.username_pw_set(username=self.username, password=self.password) if self.use_ssl and self.ca_cert: self.client.tls_set(ca_certs=self.ca_cert, tls_version=self.tls_version) self.client.on_connect = self._on_connect self.client.on_disconnect = self._on_disconnect self.client.on_message = self._on_message self.client.on_log = self._on_log def client_get_unsubs(self): return [topic for topic, _ in self.subscriptions ] def client_set_subscriptions(self): self.client.unsubscribe(self.client_get_unsubs()) self.client.subscribe(self.subscriptions) logger.info(f"\n client_set_subscription {self.subscriptions}") def client_add_subscriptions(self, subs=[]): self.subscriptions += subs self.subscriptions = list(set(self.subscriptions)) def _publish_message(self, topic, **payload): try: qos = payload.pop('qos', 0) retain = payload.pop('retain', False) message = json.dumps(payload) self.client.publish(topic, payload=message.encode('utf-8'), qos=qos, retain=retain) except Exception as e: logger.error(e) def _publish_bytes(self, topic, payload, **conf): try: qos = conf.pop('qos', 0) retain = conf.pop('retain', False) self.client.publish(topic, payload=payload, qos=qos, retain=retain) except Exception as e: logger.error(f"\n _publish_bytes error: {e}") def _on_log(self, mqttc, obj, level, string): # @UnusedVariable pass def _on_connect_info(self, info): logger.info(info) def _on_connect(self, client, userdata, flags, rc): msg = f"Client mqtt\n {client._client_id} connected\n status {rc}\n host={self.host}:{self.port}\n username={self.username}" try: if rc: raise self.client_set_subscriptions() self._on_connect_info(msg) except Exception as e: logger.error(f"\n _on_connect error {e}") def _on_disconnect(self, client, userdata, rc): logger.info(f"Client mqtt\n Disconnected: {client._client_id} with status {rc}") try: j = 3 for i in range(j): #client.unsubscribe(self.unsubs) try: client.reconnect() break except Exception as e: if i < j: logger.warn(e) time.sleep(1) continue else: raise except Exception as e: logger.error(f"Client mqtt\n _on_disconnect error {e}") def _on_stop_mqtt(self): pass def _on_message_callback(self, topic, payload): pass def _on_bytes_callback(self, topic, payload): pass def _on_message(self, client, userdata, message): try: try: payload = json.loads(message.payload.decode("utf-8")) self.on_message_callback(message.topic, payload) except: self.on_bytes_callback(message.topic, message.payload) except Exception as e: logger.error(f"\n==== _on_message error {e}\n {message.topic} {message.payload[:80]}") def connectMQTT(self): self.client.connect_async(self.host, self.port, self.keepalive) def loop_forever(self): self.connectMQTT() self.client.loop_forever() def startMQTT(self): self.connectMQTT() self.client.loop_start() def stopMQTT(self): self._on_stop_mqtt() self.client.loop_stop() self.client.disconnect()