def handle_ping(self):
     """
     Request connection check
     """
     command = tr50.create_diag_ping()
     message_desc = "Connected"
     message = defs.OutMessage(command, message_desc)
     status = self.send(message)
     return constants.STATUS_SUCCESS
 def handle_time(self):
     """
     Request time from the cloud
     """
     command = tr50.create_diag_time()
     message_desc = "Retrieving Time.."
     message = defs.OutMessage(command, message_desc)
     status = self.send(message)
     return constants.STATUS_SUCCESS
    def action_progress_update(self, request_id, message):
        """
        Update message for action (method) request in Cloud
        """

        cmd = tr50.create_mailbox_update(request_id, message)
        message = defs.OutMessage(cmd, "Update Action Progress "
                                  "{} \"{}\"".format(request_id, message))
        return self.send(message)
    def action_acknowledge(self, request_id, error_code, error_message):
        """
        Send acknowledgement for action (method) request by the cloud
        """

        cmd = tr50.create_mailbox_ack(request_id, error_code, error_message)
        message = defs.OutMessage(
            cmd, "Action Acknowledge "
            "{} {}: \"{}\"".format(request_id, error_code, error_message))
        return self.send(message)
    def request_download(self,
                         file_name,
                         file_dest,
                         blocking=False,
                         callback=None,
                         timeout=0,
                         file_global=False):
        """
        Request a C2D file transfer
        """

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

        self.logger.info("Request download of %s", file_name)

        # is file_dest the full path or the parent directory?
        if os.path.isdir(file_dest):
            file_dest = os.path.join(file_dest, file_name)

        # File Transfer object for tracking progress
        transfer = defs.FileTransfer(file_name,
                                     file_dest,
                                     self.client,
                                     callback=callback)

        # Generate and send message to request file transfer
        command = tr50.create_file_get(self.config.key, file_name, file_global)
        message = defs.OutMessage(command,
                                  "Download {}".format(file_name),
                                  data=transfer)
        status = self.send(message)

        # If blocking is set, wait for result of file transfer
        if status == constants.STATUS_SUCCESS and blocking:
            while ((timeout == 0 or current_time < end_time)
                   and transfer.status is None):
                sleep(0.1)
                current_time = datetime.utcnow()

            if transfer.status is None:
                status = constants.STATUS_TIMED_OUT
            else:
                status = transfer.status

        return status
    def handle_publish(self):
        """
        Publish any pending publishes in the publish queue, or the cloud logger
        """

        status = constants.STATUS_SUCCESS

        # Collect all pending publishes in publish queue
        to_publish = []
        while not self.publish_queue.empty():
            try:
                to_publish.append(self.publish_queue.get())
            except queue.Empty:
                break

        if to_publish:
            # If pending publishes are found, parse into list for sending
            messages = []
            for pub in to_publish:

                # Create publish command for an alarm
                if pub.type == "PublishAlarm":
                    command = tr50.create_alarm_publish(self.config.key,
                                                        pub.name, pub.state,
                                                        message=pub.message,
                                                        timestamp=pub.timestamp)
                    message_desc = "Alarm Publish {}".format(pub.name)
                    message_desc += " : {}".format(pub.state)
                    message = defs.OutMessage(command, message_desc)

                # Create publish command for strings
                elif pub.type == "PublishAttribute":
                    command = tr50.create_attribute_publish(self.config.key,
                                                            pub.name, pub.value,
                                                            timestamp=pub.timestamp)
                    message_desc = "Attribute Publish {}".format(pub.name)
                    message_desc += " : \"{}\"".format(pub.value)
                    message = defs.OutMessage(command, message_desc)

                # Create publish command for numbers
                elif pub.type == "PublishTelemetry":
                    command = tr50.create_property_publish(self.config.key,
                                                           pub.name, pub.value,
                                                           timestamp=pub.timestamp)
                    message_desc = "Property Publish {}".format(pub.name)
                    message_desc += " : {}".format(pub.value)
                    message = defs.OutMessage(command, message_desc)

                # Create publish command for location
                elif pub.type == "PublishLocation":
                    command = tr50.create_location_publish(self.config.key,
                                                           pub.latitude,
                                                           pub.longitude,
                                                           heading=pub.heading,
                                                           altitude=pub.altitude,
                                                           speed=pub.speed,
                                                           fix_accuracy=pub.accuracy,
                                                           fix_type=pub.fix_type,
                                                           timestamp=pub.timestamp)
                    message_desc = "Location Publish {}".format(str(pub))
                    message = defs.OutMessage(command, message_desc)

                # Create publish command for a log
                elif pub.type == "PublishLog":
                    command = tr50.create_log_publish(self.config.key,
                                                      pub.message,
                                                      timestamp=pub.timestamp)
                    message_desc = "Log Publish {}".format(pub.message)
                    message = defs.OutMessage(command, message_desc)

                messages.append(message)

            # Send all publishes
            if messages:
                status = self.send(messages)

        return status
    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
    def handle_action(self, action_request):
        """
        Handle action execution requests from Cloud
        """

        result_code = -1
        result_args = {"mail_id":action_request.request_id}
        action_result = None
        action_failed = False

        try:
            # Execute callback
            action_result = self.callbacks.execute_action(action_request)

        except Exception as error:
            # Error with action execution. Might not have been registered.
            action_failed = True
            self.logger.error("Action %s execution failed", action_request.name)
            self.logger.error(".... %s", str(error))
            result_code = constants.STATUS_FAILURE
            result_args["error_message"] = "ERROR: {}".format(str(error))
            if action_request.name not in self.callbacks:
                result_code = constants.STATUS_NOT_FOUND
            else:
                self.logger.exception("Exception:")

        # Action execution did not raise an error
        if not action_failed:
            # Handle returning a tuple or just a status code
            if action_result.__class__.__name__ == "tuple":
                result_code = action_result[0]
                if len(action_result) >= 2:
                    result_args["error_message"] = str(action_result[1])
                if len(action_result) >= 3:
                    result_args["params"] = action_result[2]
            else:
                result_code = action_result

            if not is_valid_status(result_code):
                # Returned 'status' is not a valid status
                error_string = ("Invalid return status: " +
                                str(result_code))
                self.logger.error(error_string)
                result_code = constants.STATUS_BAD_PARAMETER
                result_args["error_message"] = "ERROR: " + error_string

        # Return status to Cloud
        # Check for invoked status.  If so, return mail box update not
        # ack.  Ack is the final notification.  This breaks triggers
        # etc because it doesn't update the status.
        result_args["error_code"] = tr50.translate_error_code(result_code)
        if result_code == constants.STATUS_INVOKED:
                update_args = {"mail_id":action_request.request_id}
                update_args["message"] = "Invoked"
                mailbox_ack = tr50.create_mailbox_update(**update_args)
        else:
                mailbox_ack = tr50.create_mailbox_ack(**result_args)

        message_desc = "Action Complete \"{}\"".format(action_request.name)
        message_desc += " result : {}({})".format(result_code,
                                                  status_string(result_code))
        if result_args.get("error_message"):
            message_desc += " \"{}\"".format(result_args["error_message"])
        if result_args.get("params"):
            message_desc += " \"{}\"".format(str(result_args["params"]))
        message = defs.OutMessage(mailbox_ack, message_desc)
        status = self.send(message)
        return status
    def request_upload(self, file_path, upload_name=None, blocking=False,
                       callback=None, timeout=0, file_global=False):
        """
        Request a D2C file transfer
        """

        status = constants.STATUS_SUCCESS
        current_time = datetime.utcnow()
        end_time = current_time + timedelta(seconds=timeout)
        transfer = None

        self.logger.info("Request upload of %s", file_path)

        # Path must be absolute
        if not os.path.isabs(file_path):
            self.logger.error("Path must be absolute \"%s\"", file_path)
            status = constants.STATUS_NOT_FOUND

        if status == constants.STATUS_SUCCESS:
            # Check if file exists
            if not os.path.isfile(file_path):
                # No file to upload
                self.logger.error("Cannot find file %s. "
                                  "Upload cancelled.", file_path)
                status = constants.STATUS_NOT_FOUND
            else:
                transfer = None
                file_name = os.path.basename(file_path)
                if not upload_name:
                    upload_name = file_name

                # Get file crc32 checksum
                checksum = 0
                with open(file_path, "rb") as up_file:
                    for chunk in up_file:
                        checksum = crc32(chunk, checksum)
                checksum = checksum & 0xffffffff

                # File Transfer object for tracking progress
                transfer = defs.FileTransfer(upload_name, file_path,
                                             self.client,
                                             callback=callback)

                # Generate and send message to request file transfer
                command = tr50.create_file_put(self.config.key, upload_name,
                                               crc32=checksum,
                                               file_global=file_global)
                message_desc = "Upload {} as {}".format(file_name,
                                                        upload_name)
                message = defs.OutMessage(command, message_desc,
                                          data=transfer)
                status = self.send(message)

                # If blocking is set, wait for result of file transfer
                if status == constants.STATUS_SUCCESS and blocking:
                    while ((timeout == 0 or current_time < end_time) and
                           not self.to_quit and transfer.status is None):
                        sleep(0.1)
                        current_time = datetime.utcnow()

                    if transfer.status is None:
                        status = constants.STATUS_TIMED_OUT
                    else:
                        status = transfer.status

        return status