7676}
7777
7878
79+ _the_interface = None # pylint: disable=invalid-name
80+ _the_sock = None # pylint: disable=invalid-name
81+
7982class MMQTTException (Exception ):
8083 """MiniMQTT Exception class."""
8184
8285 # pylint: disable=unnecessary-pass
8386 # pass
8487
8588
89+ def set_socket (sock , iface = None ):
90+ """Helper to set the global socket and optionally set the global network interface.
91+ :param sock: socket object.
92+ :param iface: internet interface object
93+
94+ """
95+ global _the_sock # pylint: disable=invalid-name, global-statement
96+ _the_sock = sock
97+ if iface :
98+ global _the_interface # pylint: disable=invalid-name, global-statement
99+ _the_interface = iface
100+ _the_sock .set_interface (iface )
101+
86102class MQTT :
87103 """MQTT Client for CircuitPython
88- :param socket: Socket object for provided network interface
89104 :param str broker: MQTT Broker URL or IP Address.
90105 :param int port: Optional port definition, defaults to 8883.
91106 :param str username: Username for broker authentication.
@@ -95,33 +110,18 @@ class MQTT:
95110 :param bool is_ssl: Sets a secure or insecure connection with the broker.
96111 :param bool log: Attaches a logger to the MQTT client, defaults to logging level INFO.
97112 :param int keep_alive: KeepAlive interval between the broker and the MiniMQTT client.
113+
98114 """
99115
100116 # pylint: disable=too-many-arguments,too-many-instance-attributes, not-callable, invalid-name, no-member
101- def __init__ (
102- self ,
103- socket ,
104- broker ,
105- port = None ,
106- username = None ,
107- password = None ,
108- network_manager = None ,
109- client_id = None ,
110- is_ssl = True ,
111- log = False ,
112- keep_alive = 60 ,
113- ):
114- # network management
115- self ._socket = socket
116- network_manager_type = str (type (network_manager ))
117- if "ESPSPI_WiFiManager" in network_manager_type :
118- self ._wifi = network_manager
119- else :
120- raise TypeError ("This library requires a NetworkManager object." )
117+ def __init__ (self , broker , port = None , username = None ,
118+ password = None , client_id = None ,
119+ is_ssl = True , log = False , keep_alive = 60 ):
120+ self ._sock = None
121121 # broker
122- try : # set broker IP
123- self .broker = self . _wifi . esp .unpretty_ip (broker )
124- except ValueError : # set broker URL
122+ try : # set broker IP
123+ self .broker = _the_interface .unpretty_ip (broker )
124+ except ValueError : # set broker URL
125125 self .broker = broker
126126 # port/ssl
127127 self .port = MQTT_TCP_PORT
@@ -181,6 +181,7 @@ def __exit__(self, exception_type, exception_value, traceback):
181181 def deinit (self ):
182182 """De-initializes the MQTT client and disconnects from
183183 the mqtt broker.
184+
184185 """
185186 self .disconnect ()
186187
@@ -190,6 +191,7 @@ def last_will(self, topic=None, message=None, qos=0, retain=False):
190191 :param str message: Last will disconnection message.
191192 :param int qos: Quality of Service level.
192193 :param bool retain: Specifies if the message is to be retained when it is published.
194+
193195 """
194196 if self ._is_connected :
195197 raise MMQTTException (
@@ -204,37 +206,45 @@ def last_will(self, topic=None, message=None, qos=0, retain=False):
204206 self ._lw_msg = message
205207 self ._lw_retain = retain
206208
207- # pylint: disable=too-many-branches, too-many-statements
209+ # pylint: disable=too-many-branches, too-many-statements, too-many-locals
208210 def connect (self , clean_session = True ):
209211 """Initiates connection with the MQTT Broker.
210212 :param bool clean_session: Establishes a persistent session.
213+
211214 """
212- self ._set_interface ()
213- if self .logger is not None :
214- self .logger .debug ("Creating new socket" )
215- self ._sock = self ._socket .socket ()
216- self ._sock .settimeout (10 )
215+ try :
216+ proto , dummy , self .broker , path = self .broker .split ("/" , 3 )
217+ # replace spaces in path
218+ path = path .replace (" " , "%20" )
219+ except ValueError :
220+ proto , dummy , self .broker = self .broker .split ("/" , 2 )
221+ path = ""
222+ if proto == "http:" :
223+ self .port = MQTT_TCP_PORT
224+ elif proto == "https:" :
225+ self .port = MQTT_TLS_PORT
226+ else :
227+ raise ValueError ("Unsupported protocol: " + proto )
228+
229+ if ":" in self .broker :
230+ self .broker , port = self .broker .split (":" , 1 )
231+ port = int (port )
232+
233+ addr = _the_sock .getaddrinfo (self .broker , self .port , 0 , _the_sock .SOCK_STREAM )[0 ]
234+ self ._sock = _the_sock .socket (addr [0 ], addr [1 ], addr [2 ])
235+ self ._sock .settimeout (15 )
217236 if self .port == 8883 :
218237 try :
219238 if self .logger is not None :
220- self .logger .debug (
221- "Attempting to establish secure MQTT connection..."
222- )
223- self ._sock .connect ((self .broker , self .port ), TLS_MODE )
224- except RuntimeError :
225- raise MMQTTException ("Invalid broker address defined." )
239+ self .logger .debug ('Attempting to establish secure MQTT connection...' )
240+ self ._sock .connect ((self .broker , self .port ), _the_interface .TLS_MODE )
241+ except RuntimeError as e :
242+ raise MMQTTException ("Invalid broker address defined." , e )
226243 else :
227- if isinstance (self .broker , str ):
228- addr = self ._socket .getaddrinfo (self .broker , self .port )[0 ][- 1 ]
229- else :
230- addr = (self .broker , self .port )
231244 try :
232245 if self .logger is not None :
233- self .logger .debug (
234- "Attempting to establish insecure MQTT connection..."
235- )
236- # self._sock.connect((self.broker, self.port), TCP_MODE)
237- self ._sock .connect (addr , TCP_MODE )
246+ self .logger .debug ('Attempting to establish insecure MQTT connection...' )
247+ self ._sock .connect (addr [- 1 ], TCP_MODE )
238248 except RuntimeError as e :
239249 raise MMQTTException ("Invalid broker address defined." , e )
240250
@@ -376,9 +386,9 @@ def publish(self, topic, msg, retain=False, qos=0):
376386 raise MMQTTException ("Publish topic can not contain wildcards." )
377387 # check msg/qos kwargs
378388 if msg is None :
379- raise MMQTTException (" Message can not be None." )
389+ raise MMQTTException (' Message can not be None.' )
380390 if isinstance (msg , (int , float )):
381- msg = str (msg ).encode (" ascii" )
391+ msg = str (msg ).encode (' ascii' )
382392 elif isinstance (msg , str ):
383393 msg = str (msg ).encode ("utf-8" )
384394 else :
@@ -574,55 +584,6 @@ def unsubscribe(self, topic):
574584 self ._subscribed_topics .remove (t )
575585 return
576586
577- @property
578- def is_wifi_connected (self ):
579- """Returns if the ESP module is connected to
580- an access point, resets module if False"""
581- if self ._wifi :
582- return self ._wifi .esp .is_connected
583- raise MMQTTException ("MiniMQTT Client does not use a WiFi NetworkManager." )
584-
585- # pylint: disable=line-too-long, protected-access
586- @property
587- def is_sock_connected (self ):
588- """Returns if the socket is connected."""
589- return (
590- self .is_wifi_connected
591- and self ._sock
592- and self ._wifi .esp .socket_connected (self ._sock ._socknum )
593- )
594-
595- def reconnect_socket (self ):
596- """Re-establishes the socket's connection with the MQTT broker.
597- """
598- try :
599- if self .logger is not None :
600- self .logger .debug ("Attempting to reconnect with MQTT Broker..." )
601- self .reconnect ()
602- except RuntimeError as err :
603- if self .logger is not None :
604- self .logger .debug (
605- "Failed to reconnect with MQTT Broker, retrying..." , err
606- )
607- time .sleep (1 )
608- self .reconnect_socket ()
609-
610- def reconnect_wifi (self ):
611- """Reconnects to WiFi Access Point and socket, if disconnected.
612- """
613- while not self .is_wifi_connected :
614- try :
615- if self .logger is not None :
616- self .logger .debug ("Connecting to WiFi AP..." )
617- self ._wifi .connect ()
618- except (RuntimeError , ValueError ):
619- if self .logger is not None :
620- self .logger .debug ("Failed to reset WiFi module, retrying..." )
621- time .sleep (1 )
622- # we just reconnected, is the socket still connected?
623- if not self .is_sock_connected :
624- self .reconnect_socket ()
625-
626587 def reconnect (self , resub_topics = True ):
627588 """Attempts to reconnect to the MQTT broker.
628589 :param bool resub_topics: Resubscribe to previously subscribed topics.
@@ -645,37 +606,30 @@ def loop_forever(self):
645606 """Starts a blocking message loop. Use this
646607 method if you want to run a program forever.
647608 Code below a call to this method will NOT execute.
648- Network reconnection is handled within this call.
609+
610+ NOTE: This method is depreciated and will be removed in the
611+ next major release. Please see examples/minimqtt_pub_sub_blocking.py
612+ for an example of creating a blocking loop which can handle wireless
613+ network events.
649614
650615 """
651616 while True :
652- # Check WiFi and socket status
653- if self .is_sock_connected :
654- try :
655- self .loop ()
656- except (RuntimeError , ValueError ):
657- if self ._wifi :
658- # Reconnect the WiFi module and the socket
659- self .reconnect_wifi ()
660- continue
617+ if self ._sock .connected :
618+ self .loop ()
661619
662620 def loop (self ):
663621 """Non-blocking message loop. Use this method to
664622 check incoming subscription messages.
665623
666- This method does NOT handle networking or
667- network hardware management, use loop_forever
668- or handle in code instead.
669624 """
670625 if self ._timestamp == 0 :
671626 self ._timestamp = time .monotonic ()
672627 current_time = time .monotonic ()
673628 if current_time - self ._timestamp >= self .keep_alive :
674629 # Handle KeepAlive by expecting a PINGREQ/PINGRESP from the server
675630 if self .logger is not None :
676- self .logger .debug (
677- "KeepAlive period elapsed - requesting a PINGRESP from the server..."
678- )
631+ self .logger .debug ('KeepAlive period elapsed - \
632+ requesting a PINGRESP from the server...' )
679633 self .ping ()
680634 self ._timestamp = 0
681635 self ._sock .settimeout (0.1 )
@@ -745,10 +699,10 @@ def _check_topic(topic):
745699 raise MMQTTException ("Topic may not be NoneType" )
746700 # [MQTT-4.7.3-1]
747701 if not topic :
748- raise MMQTTException (" Topic may not be empty." )
702+ raise MMQTTException (' Topic may not be empty.' )
749703 # [MQTT-4.7.3-3]
750- if len (topic .encode (" utf-8" )) > MQTT_TOPIC_LENGTH_LIMIT :
751- raise MMQTTException (" Topic length is too large." )
704+ if len (topic .encode (' utf-8' )) > MQTT_TOPIC_LENGTH_LIMIT :
705+ raise MMQTTException (' Topic length is too large.' )
752706
753707 @staticmethod
754708 def _check_qos (qos_level ):
0 commit comments