26
26
https://github.com/adafruit/Adafruit_CircuitPython_Logging
27
27
28
28
"""
29
+
29
30
import time
30
31
32
+ try :
33
+ from typing import Any , Callable , Dict , Optional , Type , Union
34
+ from types import TracebackType
35
+
36
+ from adafruit_esp32spi import adafruit_esp32spi as ESP32SPI
37
+ from adafruit_minimqtt import adafruit_minimqtt as MQTT
38
+ except ImportError :
39
+ pass
40
+
31
41
import adafruit_logging as logging
32
42
from adafruit_jwt import JWT
33
43
import rtc
@@ -46,11 +56,21 @@ class MQTT_API_ERROR(Exception):
46
56
class MQTT_API :
47
57
"""Client for interacting with Google's Cloud Core MQTT API.
48
58
49
- :param MiniMQTT mqtt_client: MiniMQTT Client object.
50
-
59
+ :param ~MQTT.MQTT mqtt_client: MiniMQTT Client object
51
60
"""
52
61
53
- def __init__ (self , mqtt_client ):
62
+ device_id : str
63
+ logger : bool
64
+ on_connect : Optional [Callable [["MQTT_API" , Optional [Any ], int , int ], None ]]
65
+ on_disconnect : Optional [Callable [["MQTT_API" ], None ]]
66
+ on_message : Optional [Callable [["MQTT_API" , str , str ], None ]]
67
+ on_subscribe : Optional [Callable [["MQTT_API" , Optional [Any ], str , int ], None ]]
68
+ on_unsubscribe : Optional [Callable [["MQTT_API" , Optional [Any ], str , int ], None ]]
69
+ user : str
70
+
71
+ _client : MQTT .MQTT
72
+
73
+ def __init__ (self , mqtt_client : MQTT .MQTT ) -> None :
54
74
# Check that provided object is a MiniMQTT client object
55
75
mqtt_client_type = str (type (mqtt_client ))
56
76
if "MQTT" in mqtt_client_type :
@@ -98,13 +118,18 @@ def __init__(self, mqtt_client):
98
118
# Set up a device identifier by splitting out the full CID
99
119
self .device_id = self ._client .client_id .split ("/" )[7 ]
100
120
101
- def __enter__ (self ):
121
+ def __enter__ (self ) -> "MQTT_API" :
102
122
return self
103
123
104
- def __exit__ (self , exception_type , exception_value , traceback ):
124
+ def __exit__ (
125
+ self ,
126
+ exception_type : Optional [Type [type ]],
127
+ exception_value : Optional [BaseException ],
128
+ traceback : Optional [TracebackType ],
129
+ ) -> None :
105
130
self .disconnect ()
106
131
107
- def disconnect (self ):
132
+ def disconnect (self ) -> None :
108
133
"""Disconnects from the Google MQTT Broker."""
109
134
try :
110
135
self ._client .disconnect ()
@@ -120,25 +145,31 @@ def disconnect(self):
120
145
# De-initialize MiniMQTT Client
121
146
self ._client .deinit ()
122
147
123
- def reconnect (self ):
148
+ def reconnect (self ) -> None :
124
149
"""Reconnects to the Google MQTT Broker."""
125
150
try :
126
151
self ._client .reconnect ()
127
152
except Exception as err :
128
153
raise MQTT_API_ERROR ("Error reconnecting to Google MQTT." ) from err
129
154
130
- def connect (self ):
155
+ def connect (self ) -> None :
131
156
"""Connects to the Google MQTT Broker."""
132
157
self ._client .connect ()
133
158
self ._connected = True
134
159
135
160
@property
136
- def is_connected (self ):
161
+ def is_connected (self ) -> bool :
137
162
"""Returns if client is connected to Google's MQTT broker."""
138
163
return self ._connected
139
164
140
165
# pylint: disable=not-callable, unused-argument
141
- def _on_connect_mqtt (self , client , userdata , flags , return_code ):
166
+ def _on_connect_mqtt (
167
+ self ,
168
+ client : MQTT .MQTT ,
169
+ user_data : Optional [Any ],
170
+ flags : int ,
171
+ return_code : int ,
172
+ ) -> None :
142
173
"""Runs when the mqtt client calls on_connect."""
143
174
if self .logger :
144
175
self ._client .logger .debug ("Client called on_connect." )
@@ -148,10 +179,15 @@ def _on_connect_mqtt(self, client, userdata, flags, return_code):
148
179
raise MQTT_API_ERROR (return_code )
149
180
# Call the user-defined on_connect callback if defined
150
181
if self .on_connect is not None :
151
- self .on_connect (self , userdata , flags , return_code )
182
+ self .on_connect (self , user_data , flags , return_code )
152
183
153
184
# pylint: disable=not-callable, unused-argument
154
- def _on_disconnect_mqtt (self , client , userdata , return_code ):
185
+ def _on_disconnect_mqtt (
186
+ self ,
187
+ client : MQTT .MQTT ,
188
+ user_data : Optional [Any ],
189
+ return_code : int ,
190
+ ) -> None :
155
191
"""Runs when the client calls on_disconnect."""
156
192
if self .logger :
157
193
self ._client .logger .debug ("Client called on_disconnect" )
@@ -161,30 +197,42 @@ def _on_disconnect_mqtt(self, client, userdata, return_code):
161
197
self .on_disconnect (self )
162
198
163
199
# pylint: disable=not-callable
164
- def _on_message_mqtt (self , client , topic , payload ) :
200
+ def _on_message_mqtt (self , client : MQTT . MQTT , topic : str , payload : str ) -> None :
165
201
"""Runs when the client calls on_message."""
166
202
if self .logger :
167
203
self ._client .logger .debug ("Client called on_message" )
168
204
if self .on_message is not None :
169
205
self .on_message (self , topic , payload )
170
206
171
207
# pylint: disable=not-callable
172
- def _on_subscribe_mqtt (self , client , user_data , topic , qos ):
208
+ def _on_subscribe_mqtt (
209
+ self ,
210
+ client : MQTT .MQTT ,
211
+ user_data : Optional [Any ],
212
+ topic : str ,
213
+ qos : int ,
214
+ ) -> None :
173
215
"""Runs when the client calls on_subscribe."""
174
216
if self .logger :
175
217
self ._client .logger .debug ("Client called on_subscribe" )
176
218
if self .on_subscribe is not None :
177
219
self .on_subscribe (self , user_data , topic , qos )
178
220
179
221
# pylint: disable=not-callable
180
- def _on_unsubscribe_mqtt (self , client , user_data , topic , pid ):
222
+ def _on_unsubscribe_mqtt (
223
+ self ,
224
+ client : MQTT .MQTT ,
225
+ user_data : Optional [Any ],
226
+ topic : str ,
227
+ pid : int ,
228
+ ) -> None :
181
229
"""Runs when the client calls on_unsubscribe."""
182
230
if self .logger :
183
231
self ._client .logger .debug ("Client called on_unsubscribe" )
184
232
if self .on_unsubscribe is not None :
185
233
self .on_unsubscribe (self , user_data , topic , pid )
186
234
187
- def loop (self ):
235
+ def loop (self ) -> None :
188
236
"""Maintains a connection with Google Cloud IoT Core's MQTT broker. You will
189
237
need to manually call this method within a loop to retain connection.
190
238
@@ -194,73 +242,83 @@ def loop(self):
194
242
195
243
while True:
196
244
google_iot.loop()
197
-
198
245
"""
199
246
if self ._connected :
200
247
self ._client .loop ()
201
248
202
- def unsubscribe (self , topic , subfolder = None ):
249
+ def unsubscribe (self , topic : str , subfolder : Optional [ str ] = None ) -> None :
203
250
"""Unsubscribes from a Google Cloud IoT device topic.
204
251
205
252
:param str topic: Required MQTT topic. Defaults to events.
206
253
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
207
-
208
254
"""
209
255
if subfolder is not None :
210
256
mqtt_topic = "/devices/{}/{}/{}" .format (self .device_id , topic , subfolder )
211
257
else :
212
258
mqtt_topic = "/devices/{}/{}" .format (self .device_id , topic )
213
259
self ._client .unsubscribe (mqtt_topic )
214
260
215
- def unsubscribe_from_all_commands (self ):
261
+ def unsubscribe_from_all_commands (self ) -> None :
216
262
"""Unsubscribes from a device's "commands/#" topic.
217
263
218
264
:param int qos: Quality of Service level for the message.
219
-
220
265
"""
221
266
self .unsubscribe ("commands/#" )
222
267
223
- def subscribe (self , topic , subfolder = None , qos = 1 ):
268
+ def subscribe (
269
+ self ,
270
+ topic : str ,
271
+ subfolder : Optional [str ] = None ,
272
+ qos : int = 1 ,
273
+ ) -> None :
224
274
"""Subscribes to a Google Cloud IoT device topic.
225
275
226
276
:param str topic: Required MQTT topic. Defaults to events.
227
277
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
228
278
:param int qos: Quality of Service level for the message.
229
-
230
279
"""
231
280
if subfolder is not None :
232
281
mqtt_topic = "/devices/{}/{}/{}" .format (self .device_id , topic , subfolder )
233
282
else :
234
283
mqtt_topic = "/devices/{}/{}" .format (self .device_id , topic )
235
284
self ._client .subscribe (mqtt_topic , qos )
236
285
237
- def subscribe_to_subfolder (self , topic , subfolder , qos = 1 ):
286
+ def subscribe_to_subfolder (
287
+ self ,
288
+ topic : str ,
289
+ subfolder : Optional [str ] = None ,
290
+ qos : int = 1 ,
291
+ ) -> None :
238
292
"""Subscribes to a Google Cloud IoT device's topic subfolder
239
293
240
294
:param str topic: Required MQTT topic.
241
295
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
242
296
:param int qos: Quality of Service level for the message.
243
-
244
297
"""
245
298
self .subscribe (topic , subfolder , qos )
246
299
247
- def subscribe_to_config (self , qos = 1 ) :
300
+ def subscribe_to_config (self , qos : int = 1 ) -> None :
248
301
"""Subscribes to a Google Cloud IoT device's configuration
249
302
topic.
250
303
251
304
:param int qos: Quality of Service level for the message.
252
-
253
305
"""
254
306
self .subscribe ("config" , qos = qos )
255
307
256
- def subscribe_to_all_commands (self , qos = 1 ) :
308
+ def subscribe_to_all_commands (self , qos : int = 1 ) -> None :
257
309
"""Subscribes to a device's "commands/#" topic.
258
- :param int qos: Quality of Service level for the message.
259
310
311
+ :param int qos: Quality of Service level for the message.
260
312
"""
261
313
self .subscribe ("commands/#" , qos = qos )
262
314
263
- def publish (self , payload , topic = "events" , subfolder = None , qos = 0 ):
315
+ def publish (
316
+ self ,
317
+ payload : Union [int , str ],
318
+ topic : str = "events" ,
319
+ subfolder : Optional [str ] = None ,
320
+ qos : int = 0 ,
321
+ ) -> None :
264
322
"""Publishes a payload from the device to its Google Cloud IoT
265
323
device topic, defaults to "events" topic. To send state, use the
266
324
publish_state method.
@@ -271,7 +329,6 @@ def publish(self, payload, topic="events", subfolder=None, qos=0):
271
329
:param str topic: Required MQTT topic. Defaults to events.
272
330
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
273
331
:param int qos: Quality of Service level for the message.
274
-
275
332
"""
276
333
if subfolder is not None :
277
334
mqtt_topic = "/devices/{}/{}/{}" .format (self .device_id , topic , subfolder )
@@ -283,12 +340,11 @@ def publish(self, payload, topic="events", subfolder=None, qos=0):
283
340
raise TypeError ("A topic string must be specified." )
284
341
self ._client .publish (mqtt_topic , payload , qos = qos )
285
342
286
- def publish_state (self , payload ) :
343
+ def publish_state (self , payload : Union [ int , str ]) -> None :
287
344
"""Publishes a device state message to the Cloud IoT MQTT API. Data
288
345
sent by this method should be information about the device itself (such as number of
289
346
crashes, battery level, or device health). This method is unidirectional,
290
347
it communicates Device-to-Cloud only.
291
-
292
348
"""
293
349
self ._client .publish (payload , "state" )
294
350
@@ -300,10 +356,27 @@ class Cloud_Core:
300
356
:param ESP_SPIcontrol esp: ESP32SPI object.
301
357
:param dict secrets: Secrets.py file.
302
358
:param bool log: Enable Cloud_Core logging, defaults to False.
303
-
304
359
"""
305
360
306
- def __init__ (self , esp = None , secrets = None , log = False ):
361
+ broker : str
362
+ username : str
363
+ cid : str
364
+ logger : Optional [logging .Logger ]
365
+
366
+ _esp : Optional [ESP32SPI .ESP_SPIcontrol ]
367
+ _secrets : Optional [Dict [str , Any ]]
368
+ _proj_id : str
369
+ _region : str
370
+ _reg_id : str
371
+ _device_id : str
372
+ _private_key : str
373
+
374
+ def __init__ (
375
+ self ,
376
+ esp : Optional [ESP32SPI .ESP_SPIcontrol ] = None ,
377
+ secrets : Optional [Dict [str , Any ]] = None ,
378
+ log : bool = False ,
379
+ ) -> None :
307
380
self ._esp = esp
308
381
# Validate Secrets
309
382
if secrets and hasattr (secrets , "keys" ):
@@ -328,7 +401,7 @@ def __init__(self, esp=None, secrets=None, log=False):
328
401
self .cid = self .client_id
329
402
330
403
@property
331
- def client_id (self ):
404
+ def client_id (self ) -> str :
332
405
"""Returns a Google Cloud IOT Core Client ID."""
333
406
client_id = "projects/{0}/locations/{1}/registries/{2}/devices/{3}" .format (
334
407
self ._proj_id , self ._region , self ._reg_id , self ._device_id
@@ -337,7 +410,7 @@ def client_id(self):
337
410
self .logger .debug ("Client ID: {}" .format (client_id ))
338
411
return client_id
339
412
340
- def generate_jwt (self , ttl = 43200 , algo = "RS256" ):
413
+ def generate_jwt (self , ttl : int = 43200 , algo : str = "RS256" ) -> str :
341
414
"""Generates a JSON Web Token (https://jwt.io/) using network time.
342
415
343
416
:param int jwt_ttl: When the JWT token expires, defaults to 43200 minutes (or 12 hours).
@@ -349,7 +422,6 @@ def generate_jwt(self, ttl=43200, algo="RS256"):
349
422
350
423
jwt = CloudCore.generate_jwt()
351
424
print("Generated JWT: ", jwt)
352
-
353
425
"""
354
426
if self .logger :
355
427
self .logger .debug ("Generating JWT..." )
0 commit comments