def disconnect(self, wait_for_replies=False, timeout=0):
        """
        Stop threads and shut down MQTT client
        """

        current_time = datetime.utcnow()
        end_time = current_time + timedelta(seconds=timeout)

        # Publish any data that was queued before disconnecting
        if not self.publish_queue.empty():
            self.queue_work(defs.Work(constants.WORK_PUBLISH, None))

        # Wait for pending work that has not been dealt with
        self.logger.info("Disconnecting...")
        while ((timeout == 0 or current_time < end_time) and
               not self.work_queue.empty()):
            sleep(0.1)
            current_time = datetime.utcnow()

        # Optionally wait for any outstanding replies.
        if wait_for_replies and self.is_connected():
            self.logger.info("Waiting for replies...")
            while ((timeout == 0 or current_time < end_time) and
                   len(self.reply_tracker) != 0):
                sleep(0.1)
                current_time = datetime.utcnow()

        self.to_quit = True
        #TODO: Kill any hanging threads
        if threading.current_thread() not in self.worker_threads:
            if self.main_thread:
                self.main_thread.join()
                self.main_thread = None

        return constants.STATUS_SUCCESS
    def main_loop(self):
        """
        Main loop for MQTT to send and receive messages, as well as queue work
        for publishing and checking timeouts
        """

        # Continuously loop while connected or connecting
        while not self.to_quit:

            # If disconnected, attempt to reestablish connection
            if self.state == constants.STATE_DISCONNECTED:
                max_time = self.config.keep_alive
                elapsed_time = (datetime.utcnow() -
                                self.last_connected).total_seconds()
                if max_time == 0 or elapsed_time < max_time:
                    try:
                        result = self.mqtt.reconnect()
                        if result == 0:
                            self.logger.debug("Reconnecting...")
                            self.state = constants.STATE_CONNECTING
                    except Exception:
                        sleep(self.config.loop_time)
                else:
                    self.logger.error("No connection after %d seconds, "
                                      "exiting...",
                                      self.config.keep_alive)
                    self.to_quit = True
                    break

            self.mqtt.loop(timeout=self.config.loop_time)

            # Make a work item to publish anything that's pending
            if not self.publish_queue.empty():
                self.queue_work(defs.Work(constants.WORK_PUBLISH, None))

        # One last loop to send out any pending messages
        self.mqtt.loop(timeout=0.1)

        # Disconnect MQTT
        self.mqtt.disconnect()

        # Wait for worker threads to finish.
        for thread in self.worker_threads:
            thread.join()
        self.worker_threads = []

        # On disconnect, show all messages that never received replies
        if len(self.reply_tracker) > 0:
            self.logger.error("These messages never received a reply:")
            for mid, message in self.reply_tracker.items():
                self.logger.error(".... %s - %s", mid,
                                  message.description)

        return constants.STATUS_SUCCESS
    def on_message(self, mqtt, userdata, msg):
        """
        Callback when MQTT Client receives a message
        """

        message = defs.Message(msg.topic, json.loads(msg.payload.decode()))
        self.logger.debug("Received message on topic \"%s\"\n%s", msg.topic,
                          message)

        # Queue work to handle received message. Don't block main loop with this
        # task.
        work = defs.Work(constants.WORK_MESSAGE, message)
        self.queue_work(work)
Beispiel #4
0
    def alarm_publish(self, alarm_name, state, message=None):
        """
        Publish an alarm to the Cloud

        Parameters:
          alarm_name          (string) Name of alarm to publish
          state                  (int) State of publish
          message             (string) Optional message to accompany alarm

        Returns:
          STATUS_SUCCESS               Alarm has been queued for publishing
        """

        alarm = defs.PublishAlarm(alarm_name, state, message)
        self.handler.queue_publish(alarm)
        work = defs.Work(WORK_PUBLISH, None)
        return self.handler.queue_work(work)
    def handle_message(self, mqtt_message):
        """
        Handle messages received from Cloud
        """

        status = constants.STATUS_NOT_SUPPORTED

        msg_json = mqtt_message.json
        if "notify/" in mqtt_message.topic:
            # Received a notification
            if mqtt_message.topic[len("notify/"):] == "mailbox_activity":
                # Mailbox activity, send a request to check the mailbox
                self.logger.info("Recevied notification of mailbox activity")
                mailbox_check = tr50.create_mailbox_check(auto_complete=False)
                to_send = defs.OutMessage(mailbox_check, "Mailbox Check")
                self.send(to_send)
                status = constants.STATUS_SUCCESS

        elif "reply/" in mqtt_message.topic:
            # Received a reply to a previous message
            topic_num = mqtt_message.topic[len("reply/"):]
            for command_num in msg_json:
                reply = msg_json[command_num]

                # Retrieve the sent message that this is a reply for, removing
                # it from being tracked
                self.lock.acquire()
                try:
                    sent_message = self.reply_tracker.pop_message(topic_num,
                                                                  command_num)
                except KeyError as error:
                    self.logger.error(error.message)
                    continue
                finally:
                    self.lock.release()
                sent_command_type = sent_message.command.get("command")

                # Log success status of reply
                if reply.get("success"):
                    self.logger.info("Received success for %s-%s - %s",
                                     topic_num, command_num, sent_message)
                else:
                    self.logger.error("Received failure for %s-%s - %s",
                                      topic_num, command_num, sent_message)
                    self.logger.error(".... %s", str(reply))

                # Check what kind of message this is a reply to
                if sent_command_type == TR50Command.file_get:
                    # Recevied a reply for a file download request
                    if reply.get("success"):
                        file_id = reply["params"].get("fileId")
                        file_checksum = reply["params"].get("crc32")
                        file_transfer = sent_message.data
                        file_transfer.file_id = file_id
                        file_transfer.file_checksum = file_checksum
                        work = defs.Work(constants.WORK_DOWNLOAD, file_transfer)
                        self.queue_work(work)
                    else:
                        if -90008 in reply.get("errorCodes", []):
                            sent_message.data.status = constants.STATUS_NOT_FOUND
                        else:
                            sent_message.data.status = constants.STATUS_FAILURE

                elif sent_command_type == TR50Command.file_put:
                    # Received a reply for a file upload request
                    if reply.get("success"):
                        file_id = reply["params"].get("fileId")
                        file_transfer = sent_message.data
                        file_transfer.file_id = file_id
                        work = defs.Work(constants.WORK_UPLOAD, file_transfer)
                        self.queue_work(work)
                    else:
                        sent_message.data.status = constants.STATUS_FAILURE

                elif sent_command_type == TR50Command.mailbox_check:
                    # Received a reply for a mailbox check
                    if reply.get("success"):
                        try:
                            for mail in reply["params"]["messages"]:
                                mail_command = mail.get("command")
                                if mail_command == "method.exec":
                                    # Action execute request in mailbox
                                    mail_id = mail.get("id")
                                    action_name = mail["params"].get("method")
                                    action_params = mail["params"].get("params")
                                    action_request = defs.ActionRequest(mail_id,
                                                                        action_name,
                                                                        action_params)
                                    work = defs.Work(constants.WORK_ACTION,
                                                     action_request)
                                    self.queue_work(work)
                        except:
                            pass
                elif sent_command_type == TR50Command.diag_time:
                    # Recevied a reply for a ping request
                    if reply.get("success"):
                        mill = reply["params"].get("time")
                        print (datetime.fromtimestamp(mill/1000.0))

                    else:
                        if -90008 in reply.get("errorCodes", []):
                            sent_message.data.status = constants.STATUS_NOT_FOUND
                        else:
                            sent_message.data.status = constants.STATUS_FAILURE

                elif sent_command_type == TR50Command.diag_ping:
                    # Recevied a reply for a ping request
                    if reply.get("success"):
                       print ('*Connection Okay* \n')
                    else:
                        if -90008 in reply.get("errorCodes", []):
                            sent_message.data.status = constants.STATUS_NOT_FOUND
                        else:
                            sent_message.data.status = constants.STATUS_FAILURE

            status = constants.STATUS_SUCCESS

        return status