Exemple #1
0
 def _fire_connection_event(self, connection_status):
     evt = ClientConnectionEvent(current_status=connection_status)
     for c in self._connection_event_callbacks:
         try:
             c(evt)
         except:
             l.exception(
                 "Unhandled error occurred while executing event handler")
Exemple #2
0
    def notify_message_received(self, error=None, response=None):
        self._response = copy.deepcopy(response)
        self._error = error

        if self._event is not None:
            self._event.set()
        elif self._callback is not None:
            try:
                self._callback(self._error, self._response)
            except:
                l.exception("Unhandled error occurred while executing the callback")
Exemple #3
0
    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)
Exemple #4
0
    def _on_disconnect(self, client, userdata, rc):
        l.info("Disconnection detected. Reason: %s" % str(rc))

        # When the mqtt connection is dropped, we need to reset the subscription counter.
        self._subscription_count = AtomicCounter(0)
        self.connection_status.update_status(ClientStatus.CONNECTION_DROPPED)

        # TODO: should we handle disconnection in some way at this level?

        if rc == mqtt.MQTT_ERR_SUCCESS:
            pass
        else:
            client.loop_stop(True)
Exemple #5
0
    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.")
Exemple #6
0
    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.")
Exemple #7
0
    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']
Exemple #8
0
    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.")
Exemple #9
0
    def _on_disconnect(self, client, userdata, rc):
        l.info("Disconnection detected. Reason: %s" % str(rc))

        # When the mqtt connection is dropped, we need to reset the subscription counter.
        self._subscription_count = AtomicCounter(0)
        self.connection_status.update_status(ClientStatus.CONNECTION_DROPPED)

        # If the client disconnected explicitly, the mqtt library handles thred stop autonomously
        if rc == mqtt.MQTT_ERR_SUCCESS:
            pass
        else:
            # Otherwise, if the disconnection was not intentional, we probably had a connection drop.
            # In this case, we only stop the loop thread if auto_reconnect is not set. In fact, the loop will
            # handle reconnection autonomously on connection drops.
            if not self._auto_reconnect:
                l.info("Stopping mqtt loop on connection drop")
                client.loop_stop(True)
            else:
                l.warning(
                    "Client has been disconnected, however auto_reconnect flag is set. "
                    "Won't stop the looping thread, as it will retry to connect."
                )
Exemple #10
0
 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)
Exemple #11
0
 def _on_unsubscribe(self):
     l.debug("Unsubscribed from topic")
     self._subscription_count.dec()
Exemple #12
0
 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.")
Exemple #13
0
 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.")