Skip to content

Commit 02e6b8b

Browse files
authored
Merge pull request #26 from JulianOrteil/type-annotations
Fix #18
2 parents d0ada1e + d70040a commit 02e6b8b

File tree

4 files changed

+119
-39
lines changed

4 files changed

+119
-39
lines changed

adafruit_gc_iot_core.py

Lines changed: 110 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,18 @@
2626
https://github.com/adafruit/Adafruit_CircuitPython_Logging
2727
2828
"""
29+
2930
import time
3031

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+
3141
import adafruit_logging as logging
3242
from adafruit_jwt import JWT
3343
import rtc
@@ -46,11 +56,21 @@ class MQTT_API_ERROR(Exception):
4656
class MQTT_API:
4757
"""Client for interacting with Google's Cloud Core MQTT API.
4858
49-
:param MiniMQTT mqtt_client: MiniMQTT Client object.
50-
59+
:param ~MQTT.MQTT mqtt_client: MiniMQTT Client object
5160
"""
5261

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:
5474
# Check that provided object is a MiniMQTT client object
5575
mqtt_client_type = str(type(mqtt_client))
5676
if "MQTT" in mqtt_client_type:
@@ -98,13 +118,18 @@ def __init__(self, mqtt_client):
98118
# Set up a device identifier by splitting out the full CID
99119
self.device_id = self._client.client_id.split("/")[7]
100120

101-
def __enter__(self):
121+
def __enter__(self) -> "MQTT_API":
102122
return self
103123

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:
105130
self.disconnect()
106131

107-
def disconnect(self):
132+
def disconnect(self) -> None:
108133
"""Disconnects from the Google MQTT Broker."""
109134
try:
110135
self._client.disconnect()
@@ -120,25 +145,31 @@ def disconnect(self):
120145
# De-initialize MiniMQTT Client
121146
self._client.deinit()
122147

123-
def reconnect(self):
148+
def reconnect(self) -> None:
124149
"""Reconnects to the Google MQTT Broker."""
125150
try:
126151
self._client.reconnect()
127152
except Exception as err:
128153
raise MQTT_API_ERROR("Error reconnecting to Google MQTT.") from err
129154

130-
def connect(self):
155+
def connect(self) -> None:
131156
"""Connects to the Google MQTT Broker."""
132157
self._client.connect()
133158
self._connected = True
134159

135160
@property
136-
def is_connected(self):
161+
def is_connected(self) -> bool:
137162
"""Returns if client is connected to Google's MQTT broker."""
138163
return self._connected
139164

140165
# 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:
142173
"""Runs when the mqtt client calls on_connect."""
143174
if self.logger:
144175
self._client.logger.debug("Client called on_connect.")
@@ -148,10 +179,15 @@ def _on_connect_mqtt(self, client, userdata, flags, return_code):
148179
raise MQTT_API_ERROR(return_code)
149180
# Call the user-defined on_connect callback if defined
150181
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)
152183

153184
# 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:
155191
"""Runs when the client calls on_disconnect."""
156192
if self.logger:
157193
self._client.logger.debug("Client called on_disconnect")
@@ -161,30 +197,42 @@ def _on_disconnect_mqtt(self, client, userdata, return_code):
161197
self.on_disconnect(self)
162198

163199
# 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:
165201
"""Runs when the client calls on_message."""
166202
if self.logger:
167203
self._client.logger.debug("Client called on_message")
168204
if self.on_message is not None:
169205
self.on_message(self, topic, payload)
170206

171207
# 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:
173215
"""Runs when the client calls on_subscribe."""
174216
if self.logger:
175217
self._client.logger.debug("Client called on_subscribe")
176218
if self.on_subscribe is not None:
177219
self.on_subscribe(self, user_data, topic, qos)
178220

179221
# 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:
181229
"""Runs when the client calls on_unsubscribe."""
182230
if self.logger:
183231
self._client.logger.debug("Client called on_unsubscribe")
184232
if self.on_unsubscribe is not None:
185233
self.on_unsubscribe(self, user_data, topic, pid)
186234

187-
def loop(self):
235+
def loop(self) -> None:
188236
"""Maintains a connection with Google Cloud IoT Core's MQTT broker. You will
189237
need to manually call this method within a loop to retain connection.
190238
@@ -194,73 +242,83 @@ def loop(self):
194242
195243
while True:
196244
google_iot.loop()
197-
198245
"""
199246
if self._connected:
200247
self._client.loop()
201248

202-
def unsubscribe(self, topic, subfolder=None):
249+
def unsubscribe(self, topic: str, subfolder: Optional[str] = None) -> None:
203250
"""Unsubscribes from a Google Cloud IoT device topic.
204251
205252
:param str topic: Required MQTT topic. Defaults to events.
206253
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
207-
208254
"""
209255
if subfolder is not None:
210256
mqtt_topic = "/devices/{}/{}/{}".format(self.device_id, topic, subfolder)
211257
else:
212258
mqtt_topic = "/devices/{}/{}".format(self.device_id, topic)
213259
self._client.unsubscribe(mqtt_topic)
214260

215-
def unsubscribe_from_all_commands(self):
261+
def unsubscribe_from_all_commands(self) -> None:
216262
"""Unsubscribes from a device's "commands/#" topic.
217263
218264
:param int qos: Quality of Service level for the message.
219-
220265
"""
221266
self.unsubscribe("commands/#")
222267

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:
224274
"""Subscribes to a Google Cloud IoT device topic.
225275
226276
:param str topic: Required MQTT topic. Defaults to events.
227277
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
228278
:param int qos: Quality of Service level for the message.
229-
230279
"""
231280
if subfolder is not None:
232281
mqtt_topic = "/devices/{}/{}/{}".format(self.device_id, topic, subfolder)
233282
else:
234283
mqtt_topic = "/devices/{}/{}".format(self.device_id, topic)
235284
self._client.subscribe(mqtt_topic, qos)
236285

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:
238292
"""Subscribes to a Google Cloud IoT device's topic subfolder
239293
240294
:param str topic: Required MQTT topic.
241295
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
242296
:param int qos: Quality of Service level for the message.
243-
244297
"""
245298
self.subscribe(topic, subfolder, qos)
246299

247-
def subscribe_to_config(self, qos=1):
300+
def subscribe_to_config(self, qos: int = 1) -> None:
248301
"""Subscribes to a Google Cloud IoT device's configuration
249302
topic.
250303
251304
:param int qos: Quality of Service level for the message.
252-
253305
"""
254306
self.subscribe("config", qos=qos)
255307

256-
def subscribe_to_all_commands(self, qos=1):
308+
def subscribe_to_all_commands(self, qos: int = 1) -> None:
257309
"""Subscribes to a device's "commands/#" topic.
258-
:param int qos: Quality of Service level for the message.
259310
311+
:param int qos: Quality of Service level for the message.
260312
"""
261313
self.subscribe("commands/#", qos=qos)
262314

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:
264322
"""Publishes a payload from the device to its Google Cloud IoT
265323
device topic, defaults to "events" topic. To send state, use the
266324
publish_state method.
@@ -271,7 +329,6 @@ def publish(self, payload, topic="events", subfolder=None, qos=0):
271329
:param str topic: Required MQTT topic. Defaults to events.
272330
:param str subfolder: Optional MQTT topic subfolder. Defaults to None.
273331
:param int qos: Quality of Service level for the message.
274-
275332
"""
276333
if subfolder is not None:
277334
mqtt_topic = "/devices/{}/{}/{}".format(self.device_id, topic, subfolder)
@@ -283,12 +340,11 @@ def publish(self, payload, topic="events", subfolder=None, qos=0):
283340
raise TypeError("A topic string must be specified.")
284341
self._client.publish(mqtt_topic, payload, qos=qos)
285342

286-
def publish_state(self, payload):
343+
def publish_state(self, payload: Union[int, str]) -> None:
287344
"""Publishes a device state message to the Cloud IoT MQTT API. Data
288345
sent by this method should be information about the device itself (such as number of
289346
crashes, battery level, or device health). This method is unidirectional,
290347
it communicates Device-to-Cloud only.
291-
292348
"""
293349
self._client.publish(payload, "state")
294350

@@ -300,10 +356,27 @@ class Cloud_Core:
300356
:param ESP_SPIcontrol esp: ESP32SPI object.
301357
:param dict secrets: Secrets.py file.
302358
:param bool log: Enable Cloud_Core logging, defaults to False.
303-
304359
"""
305360

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:
307380
self._esp = esp
308381
# Validate Secrets
309382
if secrets and hasattr(secrets, "keys"):
@@ -328,7 +401,7 @@ def __init__(self, esp=None, secrets=None, log=False):
328401
self.cid = self.client_id
329402

330403
@property
331-
def client_id(self):
404+
def client_id(self) -> str:
332405
"""Returns a Google Cloud IOT Core Client ID."""
333406
client_id = "projects/{0}/locations/{1}/registries/{2}/devices/{3}".format(
334407
self._proj_id, self._region, self._reg_id, self._device_id
@@ -337,7 +410,7 @@ def client_id(self):
337410
self.logger.debug("Client ID: {}".format(client_id))
338411
return client_id
339412

340-
def generate_jwt(self, ttl=43200, algo="RS256"):
413+
def generate_jwt(self, ttl: int = 43200, algo: str = "RS256") -> str:
341414
"""Generates a JSON Web Token (https://jwt.io/) using network time.
342415
343416
: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"):
349422
350423
jwt = CloudCore.generate_jwt()
351424
print("Generated JWT: ", jwt)
352-
353425
"""
354426
if self.logger:
355427
self.logger.debug("Generating JWT...")

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
# Uncomment the below if you use native CircuitPython modules such as
2525
# digitalio, micropython and busio. List the modules you use. Without it, the
2626
# autodoc module docs will fail to generate with a warning.
27-
autodoc_mock_imports = ["adafruit_logging", "adafruit_jwt", "rtc"]
27+
autodoc_mock_imports = ["rtc"]
2828

2929
intersphinx_mapping = {
3030
"python": ("https://docs.python.org/3", None),

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
Adafruit-Blinka
66
adafruit-circuitpython-jwt
77
adafruit-circuitpython-logging>=4.0.1
8+
adafruit-circuitpython-esp32spi>=5.0.0
9+
adafruit-circuitpython-minimqtt>=5.3.2
10+
adafruit-circuitpython-rsa>=1.2.12
11+
adafruit-circuitpython-hashlib>=1.4.5

setup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
"Adafruit-Blinka",
3838
"adafruit-circuitpython-jwt",
3939
"adafruit-circuitpython-logging>=4.0.1",
40+
"adafruit-circuitpython-esp32spi>=5.0.0",
41+
"adafruit-circuitpython-minimqtt>=5.3.2",
42+
"adafruit-circuitpython-rsa>=1.2.12",
43+
"adafruit-circuitpython-hashlib>=1.4.5",
4044
],
4145
# Choose your license
4246
license="MIT",

0 commit comments

Comments
 (0)