def __init__(self, client_id, clean_session, protocol, use_wss): self._username = "" self._password = None self._enable_metrics_collection = True self._event_queue = Queue() self._event_cv = Condition() self._event_producer = EventProducer(self._event_cv, self._event_queue) self._client_status = ClientStatusContainer() self._internal_async_client = InternalAsyncMqttClient( client_id, clean_session, protocol, use_wss) self._subscription_manager = SubscriptionManager() self._offline_requests_manager = OfflineRequestsManager( -1, DropBehaviorTypes.DROP_NEWEST) # Infinite queue self._event_consumer = EventConsumer(self._event_cv, self._event_queue, self._internal_async_client, self._subscription_manager, self._offline_requests_manager, self._client_status) self._connect_disconnect_timeout_sec = DEFAULT_CONNECT_DISCONNECT_TIMEOUT_SEC self._operation_timeout_sec = DEFAULT_OPERATION_TIMEOUT_SEC self._init_offline_request_exceptions() self._init_workers() self._logger.info("MqttCore initialized") self._logger.info("Client id: %s" % client_id) self._logger.info( "Protocol version: %s" % ("MQTTv3.1" if protocol == MQTTv31 else "MQTTv3.1.1")) self._logger.info("Authentication type: %s" % ("SigV4 WebSocket" if use_wss else "TLSv1.2 certificate based Mutual Auth."))
def __init__(self, client_id, clean_session, protocol, use_wss): self._use_wss = use_wss self._username = "" self._password = None self._enable_metrics_collection = True self._event_queue = Queue() self._event_cv = Condition() self._event_producer = EventProducer(self._event_cv, self._event_queue) self._client_status = ClientStatusContainer() self._internal_async_client = InternalAsyncMqttClient(client_id, clean_session, protocol, use_wss) self._subscription_manager = SubscriptionManager() self._offline_requests_manager = OfflineRequestsManager(-1, DropBehaviorTypes.DROP_NEWEST) # Infinite queue self._event_consumer = EventConsumer(self._event_cv, self._event_queue, self._internal_async_client, self._subscription_manager, self._offline_requests_manager, self._client_status) self._connect_disconnect_timeout_sec = DEFAULT_CONNECT_DISCONNECT_TIMEOUT_SEC self._operation_timeout_sec = DEFAULT_OPERATION_TIMEOUT_SEC self._init_offline_request_exceptions() self._init_workers() self._logger.info("MqttCore initialized") self._logger.info("Client id: %s" % client_id) self._logger.info("Protocol version: %s" % ("MQTTv3.1" if protocol == MQTTv31 else "MQTTv3.1.1")) self._logger.info("Authentication type: %s" % ("SigV4 WebSocket" if use_wss else "TLSv1.2 certificate based Mutual Auth."))
class MqttCore(object): _logger = logging.getLogger(__name__) def __init__(self, client_id, clean_session, protocol, use_wss): self._username = "" self._password = None self._enable_metrics_collection = True self._event_queue = Queue() self._event_cv = Condition() self._event_producer = EventProducer(self._event_cv, self._event_queue) self._client_status = ClientStatusContainer() self._internal_async_client = InternalAsyncMqttClient( client_id, clean_session, protocol, use_wss) self._subscription_manager = SubscriptionManager() self._offline_requests_manager = OfflineRequestsManager( -1, DropBehaviorTypes.DROP_NEWEST) # Infinite queue self._event_consumer = EventConsumer(self._event_cv, self._event_queue, self._internal_async_client, self._subscription_manager, self._offline_requests_manager, self._client_status) self._connect_disconnect_timeout_sec = DEFAULT_CONNECT_DISCONNECT_TIMEOUT_SEC self._operation_timeout_sec = DEFAULT_OPERATION_TIMEOUT_SEC self._init_offline_request_exceptions() self._init_workers() self._logger.info("MqttCore initialized") self._logger.info("Client id: %s" % client_id) self._logger.info( "Protocol version: %s" % ("MQTTv3.1" if protocol == MQTTv31 else "MQTTv3.1.1")) self._logger.info("Authentication type: %s" % ("SigV4 WebSocket" if use_wss else "TLSv1.2 certificate based Mutual Auth.")) def _init_offline_request_exceptions(self): self._offline_request_queue_disabled_exceptions = { RequestTypes.PUBLISH: publishQueueDisabledException(), RequestTypes.SUBSCRIBE: subscribeQueueDisabledException(), RequestTypes.UNSUBSCRIBE: unsubscribeQueueDisabledException() } self._offline_request_queue_full_exceptions = { RequestTypes.PUBLISH: publishQueueFullException(), RequestTypes.SUBSCRIBE: subscribeQueueFullException(), RequestTypes.UNSUBSCRIBE: unsubscribeQueueFullException() } def _init_workers(self): self._internal_async_client.register_internal_event_callbacks( self._event_producer.on_connect, self._event_producer.on_disconnect, self._event_producer.on_publish, self._event_producer.on_subscribe, self._event_producer.on_unsubscribe, self._event_producer.on_message) def _start_workers(self): self._event_consumer.start() # Used for general message event reception def on_message(self, message): pass # Used for general online event notification def on_online(self): pass # Used for general offline event notification def on_offline(self): pass def configure_cert_credentials(self, cert_credentials_provider): self._logger.info("Configuring certificates...") self._internal_async_client.set_cert_credentials_provider( cert_credentials_provider) def configure_iam_credentials(self, iam_credentials_provider): self._logger.info("Configuring custom IAM credentials...") self._internal_async_client.set_iam_credentials_provider( iam_credentials_provider) def configure_endpoint(self, endpoint_provider): self._logger.info("Configuring endpoint...") self._internal_async_client.set_endpoint_provider(endpoint_provider) def configure_connect_disconnect_timeout_sec( self, connect_disconnect_timeout_sec): self._logger.info("Configuring connect/disconnect time out: %f sec" % connect_disconnect_timeout_sec) self._connect_disconnect_timeout_sec = connect_disconnect_timeout_sec def configure_operation_timeout_sec(self, operation_timeout_sec): self._logger.info("Configuring MQTT operation time out: %f sec" % operation_timeout_sec) self._operation_timeout_sec = operation_timeout_sec def configure_reconnect_back_off(self, base_reconnect_quiet_sec, max_reconnect_quiet_sec, stable_connection_sec): self._logger.info("Configuring reconnect back off timing...") self._logger.info("Base quiet time: %f sec" % base_reconnect_quiet_sec) self._logger.info("Max quiet time: %f sec" % max_reconnect_quiet_sec) self._logger.info("Stable connection time: %f sec" % stable_connection_sec) self._internal_async_client.configure_reconnect_back_off( base_reconnect_quiet_sec, max_reconnect_quiet_sec, stable_connection_sec) def configure_last_will(self, topic, payload, qos, retain=False): self._logger.info("Configuring last will...") self._internal_async_client.configure_last_will( topic, payload, qos, retain) def clear_last_will(self): self._logger.info("Clearing last will...") self._internal_async_client.clear_last_will() def configure_username_password(self, username, password=None): self._logger.info("Configuring username and password...") self._username = username self._password = password def enable_metrics_collection(self): self._enable_metrics_collection = True def disable_metrics_collection(self): self._enable_metrics_collection = False def configure_offline_requests_queue(self, max_size, drop_behavior): self._logger.info( "Configuring offline requests queueing: max queue size: %d", max_size) self._offline_requests_manager = OfflineRequestsManager( max_size, drop_behavior) self._event_consumer.update_offline_requests_manager( self._offline_requests_manager) def configure_draining_interval_sec(self, draining_interval_sec): self._logger.info( "Configuring offline requests queue draining interval: %f sec", draining_interval_sec) self._event_consumer.update_draining_interval_sec( draining_interval_sec) def connect(self, keep_alive_sec): self._logger.info("Performing sync connect...") event = Event() self.connect_async(keep_alive_sec, self._create_blocking_ack_callback(event)) if not event.wait(self._connect_disconnect_timeout_sec): self._logger.error("Connect timed out") raise connectTimeoutException() return True def connect_async(self, keep_alive_sec, ack_callback=None): self._logger.info("Performing async connect...") self._logger.info("Keep-alive: %f sec" % keep_alive_sec) self._start_workers() self._load_callbacks() self._load_username_password() self._client_status.set_status(ClientStatus.CONNECT) rc = self._internal_async_client.connect(keep_alive_sec, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Connect error: %d", rc) raise connectError(rc) return FixedEventMids.CONNACK_MID def _load_callbacks(self): self._logger.debug( "Passing in general notification callbacks to internal client...") self._internal_async_client.on_online = self.on_online self._internal_async_client.on_offline = self.on_offline self._internal_async_client.on_message = self.on_message def _load_username_password(self): username_candidate = self._username if self._enable_metrics_collection: username_candidate += METRICS_PREFIX username_candidate += AWSIoTPythonSDK.__version__ self._internal_async_client.set_username_password( username_candidate, self._password) def disconnect(self): self._logger.info("Performing sync disconnect...") event = Event() self.disconnect_async(self._create_blocking_ack_callback(event)) if not event.wait(self._connect_disconnect_timeout_sec): self._logger.error("Disconnect timed out") raise disconnectTimeoutException() if not self._event_consumer.wait_until_it_stops( self._connect_disconnect_timeout_sec): self._logger.error( "Disconnect timed out in waiting for event consumer") raise disconnectTimeoutException() return True def disconnect_async(self, ack_callback=None): self._logger.info("Performing async disconnect...") self._client_status.set_status(ClientStatus.USER_DISCONNECT) rc = self._internal_async_client.disconnect(ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Disconnect error: %d", rc) raise disconnectError(rc) return FixedEventMids.DISCONNECT_MID def publish(self, topic, payload, qos, retain=False): self._logger.info("Performing sync publish...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.PUBLISH, (topic, payload, qos, retain)) else: if qos > 0: event = Event() rc, mid = self._publish_async( topic, payload, qos, retain, self._create_blocking_ack_callback(event)) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Publish timed out") raise publishTimeoutException() else: self._publish_async(topic, payload, qos, retain) ret = True return ret def publish_async(self, topic, payload, qos, retain=False, ack_callback=None): self._logger.info("Performing async publish...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.PUBLISH, (topic, payload, qos, retain)) return FixedEventMids.QUEUED_MID else: rc, mid = self._publish_async(topic, payload, qos, retain, ack_callback) return mid def _publish_async(self, topic, payload, qos, retain=False, ack_callback=None): rc, mid = self._internal_async_client.publish(topic, payload, qos, retain, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Publish error: %d", rc) raise publishError(rc) return rc, mid def subscribe(self, topic, qos, message_callback=None): self._logger.info("Performing sync subscribe...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.SUBSCRIBE, (topic, qos, message_callback)) else: event = Event() rc, mid = self._subscribe_async( topic, qos, self._create_blocking_ack_callback(event), message_callback) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Subscribe timed out") raise subscribeTimeoutException() ret = True return ret def subscribe_async(self, topic, qos, ack_callback=None, message_callback=None): self._logger.info("Performing async subscribe...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.SUBSCRIBE, (topic, qos, message_callback)) return FixedEventMids.QUEUED_MID else: rc, mid = self._subscribe_async(topic, qos, ack_callback, message_callback) return mid def _subscribe_async(self, topic, qos, ack_callback=None, message_callback=None): self._subscription_manager.add_record(topic, qos, message_callback) rc, mid = self._internal_async_client.subscribe( topic, qos, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Subscribe error: %d", rc) raise subscribeError(rc) return rc, mid def unsubscribe(self, topic): self._logger.info("Performing sync unsubscribe...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.UNSUBSCRIBE, topic) else: event = Event() rc, mid = self._unsubscribe_async( topic, self._create_blocking_ack_callback(event)) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Unsubscribe timed out") raise unsubscribeTimeoutException() ret = True return ret def unsubscribe_async(self, topic, ack_callback=None): self._logger.info("Performing async unsubscribe...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.UNSUBSCRIBE, topic) return FixedEventMids.QUEUED_MID else: rc, mid = self._unsubscribe_async(topic, ack_callback) return mid def _unsubscribe_async(self, topic, ack_callback=None): self._subscription_manager.remove_record(topic) rc, mid = self._internal_async_client.unsubscribe(topic, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Unsubscribe error: %d", rc) raise unsubscribeError(rc) return rc, mid def _create_blocking_ack_callback(self, event): def ack_callback(mid, data=None): event.set() return ack_callback def _handle_offline_request(self, type, data): self._logger.info("Offline request detected!") offline_request = QueueableRequest(type, data) append_result = self._offline_requests_manager.add_one(offline_request) if AppendResults.APPEND_FAILURE_QUEUE_DISABLED == append_result: self._logger.error("Offline request queue has been disabled") raise self._offline_request_queue_disabled_exceptions[type] if AppendResults.APPEND_FAILURE_QUEUE_FULL == append_result: self._logger.error("Offline request queue is full") raise self._offline_request_queue_full_exceptions[type]
class MqttCore(object): _logger = logging.getLogger(__name__) def __init__(self, client_id, clean_session, protocol, use_wss): self._use_wss = use_wss self._username = "" self._password = None self._enable_metrics_collection = True self._event_queue = Queue() self._event_cv = Condition() self._event_producer = EventProducer(self._event_cv, self._event_queue) self._client_status = ClientStatusContainer() self._internal_async_client = InternalAsyncMqttClient(client_id, clean_session, protocol, use_wss) self._subscription_manager = SubscriptionManager() self._offline_requests_manager = OfflineRequestsManager(-1, DropBehaviorTypes.DROP_NEWEST) # Infinite queue self._event_consumer = EventConsumer(self._event_cv, self._event_queue, self._internal_async_client, self._subscription_manager, self._offline_requests_manager, self._client_status) self._connect_disconnect_timeout_sec = DEFAULT_CONNECT_DISCONNECT_TIMEOUT_SEC self._operation_timeout_sec = DEFAULT_OPERATION_TIMEOUT_SEC self._init_offline_request_exceptions() self._init_workers() self._logger.info("MqttCore initialized") self._logger.info("Client id: %s" % client_id) self._logger.info("Protocol version: %s" % ("MQTTv3.1" if protocol == MQTTv31 else "MQTTv3.1.1")) self._logger.info("Authentication type: %s" % ("SigV4 WebSocket" if use_wss else "TLSv1.2 certificate based Mutual Auth.")) def _init_offline_request_exceptions(self): self._offline_request_queue_disabled_exceptions = { RequestTypes.PUBLISH : publishQueueDisabledException(), RequestTypes.SUBSCRIBE : subscribeQueueDisabledException(), RequestTypes.UNSUBSCRIBE : unsubscribeQueueDisabledException() } self._offline_request_queue_full_exceptions = { RequestTypes.PUBLISH : publishQueueFullException(), RequestTypes.SUBSCRIBE : subscribeQueueFullException(), RequestTypes.UNSUBSCRIBE : unsubscribeQueueFullException() } def _init_workers(self): self._internal_async_client.register_internal_event_callbacks(self._event_producer.on_connect, self._event_producer.on_disconnect, self._event_producer.on_publish, self._event_producer.on_subscribe, self._event_producer.on_unsubscribe, self._event_producer.on_message) def _start_workers(self): self._event_consumer.start() def use_wss(self): return self._use_wss # Used for general message event reception def on_message(self, message): pass # Used for general online event notification def on_online(self): pass # Used for general offline event notification def on_offline(self): pass def configure_cert_credentials(self, cert_credentials_provider): self._logger.info("Configuring certificates...") self._internal_async_client.set_cert_credentials_provider(cert_credentials_provider) def configure_iam_credentials(self, iam_credentials_provider): self._logger.info("Configuring custom IAM credentials...") self._internal_async_client.set_iam_credentials_provider(iam_credentials_provider) def configure_endpoint(self, endpoint_provider): self._logger.info("Configuring endpoint...") self._internal_async_client.set_endpoint_provider(endpoint_provider) def configure_connect_disconnect_timeout_sec(self, connect_disconnect_timeout_sec): self._logger.info("Configuring connect/disconnect time out: %f sec" % connect_disconnect_timeout_sec) self._connect_disconnect_timeout_sec = connect_disconnect_timeout_sec def configure_operation_timeout_sec(self, operation_timeout_sec): self._logger.info("Configuring MQTT operation time out: %f sec" % operation_timeout_sec) self._operation_timeout_sec = operation_timeout_sec def configure_reconnect_back_off(self, base_reconnect_quiet_sec, max_reconnect_quiet_sec, stable_connection_sec): self._logger.info("Configuring reconnect back off timing...") self._logger.info("Base quiet time: %f sec" % base_reconnect_quiet_sec) self._logger.info("Max quiet time: %f sec" % max_reconnect_quiet_sec) self._logger.info("Stable connection time: %f sec" % stable_connection_sec) self._internal_async_client.configure_reconnect_back_off(base_reconnect_quiet_sec, max_reconnect_quiet_sec, stable_connection_sec) def configure_alpn_protocols(self): self._logger.info("Configuring alpn protocols...") self._internal_async_client.configure_alpn_protocols([ALPN_PROTCOLS]) def configure_last_will(self, topic, payload, qos, retain=False): self._logger.info("Configuring last will...") self._internal_async_client.configure_last_will(topic, payload, qos, retain) def clear_last_will(self): self._logger.info("Clearing last will...") self._internal_async_client.clear_last_will() def configure_username_password(self, username, password=None): self._logger.info("Configuring username and password...") self._username = username self._password = password def enable_metrics_collection(self): self._enable_metrics_collection = True def disable_metrics_collection(self): self._enable_metrics_collection = False def configure_offline_requests_queue(self, max_size, drop_behavior): self._logger.info("Configuring offline requests queueing: max queue size: %d", max_size) self._offline_requests_manager = OfflineRequestsManager(max_size, drop_behavior) self._event_consumer.update_offline_requests_manager(self._offline_requests_manager) def configure_draining_interval_sec(self, draining_interval_sec): self._logger.info("Configuring offline requests queue draining interval: %f sec", draining_interval_sec) self._event_consumer.update_draining_interval_sec(draining_interval_sec) def connect(self, keep_alive_sec): self._logger.info("Performing sync connect...") event = Event() self.connect_async(keep_alive_sec, self._create_blocking_ack_callback(event)) if not event.wait(self._connect_disconnect_timeout_sec): self._logger.error("Connect timed out") raise connectTimeoutException() return True def connect_async(self, keep_alive_sec, ack_callback=None): self._logger.info("Performing async connect...") self._logger.info("Keep-alive: %f sec" % keep_alive_sec) self._start_workers() self._load_callbacks() self._load_username_password() try: self._client_status.set_status(ClientStatus.CONNECT) rc = self._internal_async_client.connect(keep_alive_sec, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Connect error: %d", rc) raise connectError(rc) except Exception as e: # Provided any error in connect, we should clean up the threads that have been created self._event_consumer.stop() if not self._event_consumer.wait_until_it_stops(self._connect_disconnect_timeout_sec): self._logger.error("Time out in waiting for event consumer to stop") else: self._logger.debug("Event consumer stopped") self._client_status.set_status(ClientStatus.IDLE) raise e return FixedEventMids.CONNACK_MID def _load_callbacks(self): self._logger.debug("Passing in general notification callbacks to internal client...") self._internal_async_client.on_online = self.on_online self._internal_async_client.on_offline = self.on_offline self._internal_async_client.on_message = self.on_message def _load_username_password(self): username_candidate = self._username if self._enable_metrics_collection: username_candidate += METRICS_PREFIX username_candidate += AWSIoTPythonSDK.__version__ self._internal_async_client.set_username_password(username_candidate, self._password) def disconnect(self): self._logger.info("Performing sync disconnect...") event = Event() self.disconnect_async(self._create_blocking_ack_callback(event)) if not event.wait(self._connect_disconnect_timeout_sec): self._logger.error("Disconnect timed out") raise disconnectTimeoutException() if not self._event_consumer.wait_until_it_stops(self._connect_disconnect_timeout_sec): self._logger.error("Disconnect timed out in waiting for event consumer") raise disconnectTimeoutException() return True def disconnect_async(self, ack_callback=None): self._logger.info("Performing async disconnect...") self._client_status.set_status(ClientStatus.USER_DISCONNECT) rc = self._internal_async_client.disconnect(ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Disconnect error: %d", rc) raise disconnectError(rc) return FixedEventMids.DISCONNECT_MID def publish(self, topic, payload, qos, retain=False): self._logger.info("Performing sync publish...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.PUBLISH, (topic, payload, qos, retain)) else: if qos > 0: event = Event() rc, mid = self._publish_async(topic, payload, qos, retain, self._create_blocking_ack_callback(event)) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Publish timed out") raise publishTimeoutException() else: self._publish_async(topic, payload, qos, retain) ret = True return ret def publish_async(self, topic, payload, qos, retain=False, ack_callback=None): self._logger.info("Performing async publish...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.PUBLISH, (topic, payload, qos, retain)) return FixedEventMids.QUEUED_MID else: rc, mid = self._publish_async(topic, payload, qos, retain, ack_callback) return mid def _publish_async(self, topic, payload, qos, retain=False, ack_callback=None): rc, mid = self._internal_async_client.publish(topic, payload, qos, retain, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Publish error: %d", rc) raise publishError(rc) return rc, mid def subscribe(self, topic, qos, message_callback=None): self._logger.info("Performing sync subscribe...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.SUBSCRIBE, (topic, qos, message_callback)) else: event = Event() rc, mid = self._subscribe_async(topic, qos, self._create_blocking_ack_callback(event), message_callback) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Subscribe timed out") raise subscribeTimeoutException() ret = True return ret def subscribe_async(self, topic, qos, ack_callback=None, message_callback=None): self._logger.info("Performing async subscribe...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.SUBSCRIBE, (topic, qos, message_callback)) return FixedEventMids.QUEUED_MID else: rc, mid = self._subscribe_async(topic, qos, ack_callback, message_callback) return mid def _subscribe_async(self, topic, qos, ack_callback=None, message_callback=None): self._subscription_manager.add_record(topic, qos, message_callback) rc, mid = self._internal_async_client.subscribe(topic, qos, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Subscribe error: %d", rc) raise subscribeError(rc) return rc, mid def unsubscribe(self, topic): self._logger.info("Performing sync unsubscribe...") ret = False if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.UNSUBSCRIBE, topic) else: event = Event() rc, mid = self._unsubscribe_async(topic, self._create_blocking_ack_callback(event)) if not event.wait(self._operation_timeout_sec): self._internal_async_client.remove_event_callback(mid) self._logger.error("Unsubscribe timed out") raise unsubscribeTimeoutException() ret = True return ret def unsubscribe_async(self, topic, ack_callback=None): self._logger.info("Performing async unsubscribe...") if ClientStatus.STABLE != self._client_status.get_status(): self._handle_offline_request(RequestTypes.UNSUBSCRIBE, topic) return FixedEventMids.QUEUED_MID else: rc, mid = self._unsubscribe_async(topic, ack_callback) return mid def _unsubscribe_async(self, topic, ack_callback=None): self._subscription_manager.remove_record(topic) rc, mid = self._internal_async_client.unsubscribe(topic, ack_callback) if MQTT_ERR_SUCCESS != rc: self._logger.error("Unsubscribe error: %d", rc) raise unsubscribeError(rc) return rc, mid def _create_blocking_ack_callback(self, event): def ack_callback(mid, data=None): event.set() return ack_callback def _handle_offline_request(self, type, data): self._logger.info("Offline request detected!") offline_request = QueueableRequest(type, data) append_result = self._offline_requests_manager.add_one(offline_request) if AppendResults.APPEND_FAILURE_QUEUE_DISABLED == append_result: self._logger.error("Offline request queue has been disabled") raise self._offline_request_queue_disabled_exceptions[type] if AppendResults.APPEND_FAILURE_QUEUE_FULL == append_result: self._logger.error("Offline request queue is full") raise self._offline_request_queue_full_exceptions[type]