def _on_message(self, client, userdata, msg): """ This handler is called when a message is received from the MQTT broker, on the subscribed topics. The current implementation checks the validity of the message itself, by verifying its signature. :param client: is the MQTT client reference, useful to respond back :param userdata: metadata about the received data :param msg: message that was received :return: nothing, it simply handles the message accordingly. """ networkl.debug(msg.topic + " --> " + str(msg.payload)) try: message = json.loads(str(msg.payload, "utf8")) header = message['header'] message_hash = md5() strtohash = "%s%s%s" % (header['messageId'], self._cloud_creds.key, header['timestamp']) message_hash.update(strtohash.encode("utf8")) expected_signature = message_hash.hexdigest().lower() if header['sign'] != expected_signature: # TODO: custom exception for invalid signature raise Exception('The signature did not match!') # Check if there is any thread waiting for this message or if there is a callback that we need to invoke. # If so, do it here. handle = None with self._pending_responses_lock: msg_id = header['messageId'] handle = self._pending_response_messages.get(msg_id) from_myself = False if handle is not None: # There was a handle for this message-id. It means it is a response message to some # request performed by the library itself. from_myself = True try: l.debug("Calling handle event handler for message %s" % msg_id) # Call the handler handle.notify_message_received(error=None, response=message) l.debug("Done handler for message %s" % msg_id) # Remove the message from the pending queue with self._pending_responses_lock: del self._pending_response_messages[msg_id] except: l.exception( "Error occurred while invoking message handler") # Let's also catch all the "PUSH" notifications and dispatch them to the push_notification_callback. if self._push_message_callback is not None and header[ 'method'] == "PUSH" and 'namespace' in header: self._push_message_callback(message, from_myself=from_myself) except Exception: l.exception("Failed to process message.")
def close(self): l.info("Closing the MQTT connection...") self._mqtt_client.disconnect() l.debug("Waiting for the client to disconnect...") self.connection_status.wait_for_status(ClientStatus.CONNECTION_DROPPED) # Starts a new thread that handles mqtt protocol and calls us back via callbacks l.debug("Stopping the MQTT looper.") self._mqtt_client.loop_stop(True) l.info("Client has been fully disconnected.")
def _on_connect(self, client, userdata, rc, other): l.debug("Connected with result code %s" % str(rc)) self.connection_status.update_status(ClientStatus.CONNECTED) self._client_response_topic = "/app/%s-%s/subscribe" % (self._cloud_creds.user_id, self._app_id) self._user_topic = "/app/%s/subscribe" % self._cloud_creds.user_id # Subscribe to the relevant topics l.debug("Subscribing to topics...") client.subscribe(self._user_topic) client.subscribe(self._client_response_topic)
def connect(self): """ Starts the connection to the MQTT broker :return: """ l.info("Initializing the MQTT connection...") self._mqtt_client.connect(self._domain, self._port, keepalive=30) self.connection_status.update_status(ClientStatus.CONNECTING) # Starts a new thread that handles mqtt protocol and calls us back via callbacks l.debug("(Re)Starting the MQTT looper.") self._mqtt_client.loop_stop(True) self._mqtt_client.loop_start() l.debug("Waiting for the client to connect...") self.connection_status.wait_for_status(ClientStatus.SUBSCRIBED) l.info("Client connected to MQTT broker and subscribed to relevant topics.")
def execute_cmd(self, dst_dev_uuid, method, namespace, payload, callback=None, timeout=SHORT_TIMEOUT): start = time.time() # Build the mqtt message we will send to the broker message, message_id = self._build_mqtt_message(method, namespace, payload) # Register the waiting handler for that message handle = PendingMessageResponse(message_id=message_id, callback=callback) with self._pending_responses_lock: self._pending_response_messages[message_id] = handle # Send the message to the broker l.debug("Executing message-id %s, %s on %s command for device %s" % (message_id, method, namespace, dst_dev_uuid)) self._mqtt_client.publish(topic=build_client_request_topic(dst_dev_uuid), payload=message) # If the caller has specified a callback, we don't need to actively wait for the message ACK. So we can # immediately return. if callback is not None: return None # Otherwise, we need to wait until the message is received. l.debug("Waiting for response to message-id %s" % message_id) success, resp = handle.wait_for_response(timeout=timeout) if not success: raise CommandTimeoutException("A timeout occurred while waiting for the ACK: %d" % timeout) elapsed = time.time() - start l.debug("Message-id: %s, command %s-%s command for device %s took %s" % (message_id, method, namespace, dst_dev_uuid, str(elapsed))) return resp['payload']
def _on_subscribe(self, client, userdata, mid, granted_qos): l.debug("Succesfully subscribed to topic. Subscription count: %d" % self._subscription_count.get()) if self._subscription_count.inc() == 2: self.connection_status.update_status(ClientStatus.SUBSCRIBED)
def _on_unsubscribe(self): l.debug("Unsubscribed from topic") self._subscription_count.dec()
def unregister_connection_event_callback(self, callback): with self._connection_event_callbacks_lock: if callback in self._connection_event_callbacks: self._connection_event_callbacks.remove(callback) else: l.debug("Callback was present: nothing to unregister.")
def register_connection_event_callback(self, callback): with self._connection_event_callbacks_lock: if callback not in self._connection_event_callbacks: self._connection_event_callbacks.append(callback) else: l.debug("Callback was already registered.")