Skip to content

Commit 6c24d00

Browse files
committed
Add type hints
1 parent 413dd16 commit 6c24d00

File tree

3 files changed

+79
-55
lines changed

3 files changed

+79
-55
lines changed

adafruit_espatcontrol/adafruit_espatcontrol.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
import time
3737
from digitalio import Direction
3838

39+
try:
40+
import busio
41+
from typing import Optional, Dict, Union, List
42+
from digitalio import DigitalInOut
43+
except ImportError:
44+
pass
45+
3946
__version__ = "0.0.0-auto.0"
4047
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_espATcontrol.git"
4148

@@ -67,13 +74,13 @@ class ESP_ATcontrol:
6774

6875
def __init__(
6976
self,
70-
uart,
71-
default_baudrate,
77+
uart: busio.UART,
78+
default_baudrate: int,
7279
*,
73-
run_baudrate=None,
74-
rts_pin=None,
75-
reset_pin=None,
76-
debug=False
80+
run_baudrate: Optional[int] = None,
81+
rts_pin: Optional[DigitalInOut] = None,
82+
reset_pin: Optional[DigitalInOut] = None,
83+
debug: bool = False
7784
):
7885
"""This function doesn't try to do any sync'ing, just sets up
7986
# the hardware, that way nothing can unexpectedly fail!"""
@@ -100,7 +107,7 @@ def __init__(
100107
self._ifconfig = []
101108
self._initialized = False
102109

103-
def begin(self):
110+
def begin(self) -> None:
104111
"""Initialize the module by syncing, resetting if necessary, setting up
105112
the desired baudrate, turning on single-socket mode, and configuring
106113
SSL support. Required before using the module but we dont do in __init__
@@ -128,7 +135,7 @@ def begin(self):
128135
except OKError:
129136
pass # retry
130137

131-
def connect(self, secrets):
138+
def connect(self, secrets: Dict[str, Union[str, int]]) -> None:
132139
"""Repeatedly try to connect to an access point with the details in
133140
the passed in 'secrets' dictionary. Be sure 'ssid' and 'password' are
134141
defined in the secrets dict! If 'timezone' is set, we'll also configure
@@ -160,15 +167,15 @@ def connect(self, secrets):
160167
# *************************** SOCKET SETUP ****************************
161168

162169
@property
163-
def cipmux(self):
170+
def cipmux(self) -> int:
164171
"""The IP socket multiplexing setting. 0 for one socket, 1 for multi-socket"""
165172
replies = self.at_response("AT+CIPMUX?", timeout=3).split(b"\r\n")
166173
for reply in replies:
167174
if reply.startswith(b"+CIPMUX:"):
168175
return int(reply[8:])
169176
raise RuntimeError("Bad response to CIPMUX?")
170177

171-
def socket_connect(self, conntype, remote, remote_port, *, keepalive=10, retries=1):
178+
def socket_connect(self, conntype: str, remote: str, remote_port: int, *, keepalive: int = 10, retries: int = 1) -> bool:
172179
"""Open a socket. conntype can be TYPE_TCP, TYPE_UDP, or TYPE_SSL. Remote
173180
can be an IP address or DNS (we'll do the lookup for you. Remote port
174181
is integer port on other side. We can't set the local port"""
@@ -199,7 +206,7 @@ def socket_connect(self, conntype, remote, remote_port, *, keepalive=10, retries
199206
return True
200207
return False
201208

202-
def socket_send(self, buffer, timeout=1):
209+
def socket_send(self, buffer: bytes, timeout: int = 1) -> bool:
203210
"""Send data over the already-opened socket, buffer must be bytes"""
204211
cmd = "AT+CIPSEND=%d" % len(buffer)
205212
self.at_response(cmd, timeout=5, retries=1)
@@ -232,7 +239,7 @@ def socket_send(self, buffer, timeout=1):
232239
# Get newlines off front and back, then split into lines
233240
return True
234241

235-
def socket_receive(self, timeout=5):
242+
def socket_receive(self, timeout: int = 5) -> bytearray:
236243
# pylint: disable=too-many-nested-blocks, too-many-branches
237244
"""Check for incoming data over the open socket, returns bytes"""
238245
incoming_bytes = None
@@ -298,7 +305,7 @@ def socket_receive(self, timeout=5):
298305
gc.collect()
299306
return ret
300307

301-
def socket_disconnect(self):
308+
def socket_disconnect(self) -> None:
302309
"""Close any open socket, if there is one"""
303310
try:
304311
self.at_response("AT+CIPCLOSE", retries=1)
@@ -307,7 +314,7 @@ def socket_disconnect(self):
307314

308315
# *************************** SNTP SETUP ****************************
309316

310-
def sntp_config(self, enable, timezone=None, server=None):
317+
def sntp_config(self, enable: bool, timezone: Optional[int] = None, server: Optional[str] = None) -> None:
311318
"""Configure the built in ESP SNTP client with a UTC-offset number (timezone)
312319
and server as IP or hostname."""
313320
cmd = "AT+CIPSNTPCFG="
@@ -322,7 +329,7 @@ def sntp_config(self, enable, timezone=None, server=None):
322329
self.at_response(cmd, timeout=3)
323330

324331
@property
325-
def sntp_time(self):
332+
def sntp_time(self) -> Union[bytes, None]:
326333
"""Return a string with time/date information using SNTP, may return
327334
1970 'bad data' on the first few minutes, without warning!"""
328335
replies = self.at_response("AT+CIPSNTPTIME?", timeout=5).split(b"\r\n")
@@ -334,7 +341,7 @@ def sntp_time(self):
334341
# *************************** WIFI SETUP ****************************
335342

336343
@property
337-
def is_connected(self):
344+
def is_connected(self) -> bool:
338345
"""Initialize module if not done yet, and check if we're connected to
339346
an access point, returns True or False"""
340347
if not self._initialized:
@@ -354,7 +361,7 @@ def is_connected(self):
354361
return False
355362

356363
@property
357-
def status(self):
364+
def status(self) -> Union[int, None]:
358365
"""The IP connection status number (see AT+CIPSTATUS datasheet for meaning)"""
359366
replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n")
360367
for reply in replies:
@@ -363,7 +370,7 @@ def status(self):
363370
return None
364371

365372
@property
366-
def mode(self):
373+
def mode(self) -> Union[int, None]:
367374
"""What mode we're in, can be MODE_STATION, MODE_SOFTAP or MODE_SOFTAPSTATION"""
368375
if not self._initialized:
369376
self.begin()
@@ -374,7 +381,7 @@ def mode(self):
374381
raise RuntimeError("Bad response to CWMODE?")
375382

376383
@mode.setter
377-
def mode(self, mode):
384+
def mode(self, mode: int) -> None:
378385
"""Station or AP mode selection, can be MODE_STATION, MODE_SOFTAP or MODE_SOFTAPSTATION"""
379386
if not self._initialized:
380387
self.begin()
@@ -383,15 +390,15 @@ def mode(self, mode):
383390
self.at_response("AT+CWMODE=%d" % mode, timeout=3)
384391

385392
@property
386-
def local_ip(self):
393+
def local_ip(self) -> Union[str, None]:
387394
"""Our local IP address as a dotted-quad string"""
388395
reply = self.at_response("AT+CIFSR").strip(b"\r\n")
389396
for line in reply.split(b"\r\n"):
390397
if line and line.startswith(b'+CIFSR:STAIP,"'):
391398
return str(line[14:-1], "utf-8")
392399
raise RuntimeError("Couldn't find IP address")
393400

394-
def ping(self, host):
401+
def ping(self, host: str) -> Union[int, None]:
395402
"""Ping the IP or hostname given, returns ms time or None on failure"""
396403
reply = self.at_response('AT+PING="%s"' % host.strip('"'), timeout=5)
397404
for line in reply.split(b"\r\n"):
@@ -404,7 +411,7 @@ def ping(self, host):
404411
return None
405412
raise RuntimeError("Couldn't ping")
406413

407-
def nslookup(self, host):
414+
def nslookup(self, host: str) -> Union[str, None]:
408415
"""Return a dotted-quad IP address strings that matches the hostname"""
409416
reply = self.at_response('AT+CIPDOMAIN="%s"' % host.strip('"'), timeout=3)
410417
for line in reply.split(b"\r\n"):
@@ -415,7 +422,7 @@ def nslookup(self, host):
415422
# *************************** AP SETUP ****************************
416423

417424
@property
418-
def remote_AP(self): # pylint: disable=invalid-name
425+
def remote_AP(self) -> List[Union[int, str, None]]: # pylint: disable=invalid-name
419426
"""The name of the access point we're connected to, as a string"""
420427
stat = self.status
421428
if stat != self.STATUS_APCONNECTED:
@@ -434,7 +441,7 @@ def remote_AP(self): # pylint: disable=invalid-name
434441
return reply
435442
return [None] * 4
436443

437-
def join_AP(self, ssid, password): # pylint: disable=invalid-name
444+
def join_AP(self, ssid: str, password: str) -> None: # pylint: disable=invalid-name
438445
"""Try to join an access point by name and password, will return
439446
immediately if we're already connected and won't try to reconnect"""
440447
# First make sure we're in 'station' mode so we can connect to AP's
@@ -456,7 +463,7 @@ def join_AP(self, ssid, password): # pylint: disable=invalid-name
456463
raise RuntimeError("Didn't get IP address")
457464
return
458465

459-
def scan_APs(self, retries=3): # pylint: disable=invalid-name
466+
def scan_APs(self, retries: int = 3) -> Union[List[List[bytes]], None]: # pylint: disable=invalid-name
460467
"""Ask the module to scan for access points and return a list of lists
461468
with name, RSSI, MAC addresses, etc"""
462469
for _ in range(retries):
@@ -482,11 +489,11 @@ def scan_APs(self, retries=3): # pylint: disable=invalid-name
482489
# ************************** AT LOW LEVEL ****************************
483490

484491
@property
485-
def version(self):
492+
def version(self) -> Union[str, None]:
486493
"""The cached version string retrieved via the AT+GMR command"""
487494
return self._version
488495

489-
def get_version(self):
496+
def get_version(self) -> Union[str, None]:
490497
"""Request the AT firmware version string and parse out the
491498
version number"""
492499
reply = self.at_response("AT+GMR", timeout=3).strip(b"\r\n")
@@ -499,12 +506,12 @@ def get_version(self):
499506
self._version = str(line, "utf-8")
500507
return self._version
501508

502-
def hw_flow(self, flag):
509+
def hw_flow(self, flag: bool) -> None:
503510
"""Turn on HW flow control (if available) on to allow data, or off to stop"""
504511
if self._rts_pin:
505512
self._rts_pin.value = not flag
506513

507-
def at_response(self, at_cmd, timeout=5, retries=3):
514+
def at_response(self, at_cmd: str, timeout: int = 5, retries: int = 3) -> bytes:
508515
"""Send an AT command, check that we got an OK response,
509516
and then cut out the reply lines to return. We can set
510517
a variable timeout (how long we'll wait for response) and
@@ -554,7 +561,7 @@ def at_response(self, at_cmd, timeout=5, retries=3):
554561
return response[:-4]
555562
raise OKError("No OK response to " + at_cmd)
556563

557-
def sync(self):
564+
def sync(self) -> bool:
558565
"""Check if we have AT commmand sync by sending plain ATs"""
559566
try:
560567
self.at_response("AT", timeout=1)
@@ -563,12 +570,12 @@ def sync(self):
563570
return False
564571

565572
@property
566-
def baudrate(self):
573+
def baudrate(self) -> int:
567574
"""The baudrate of our UART connection"""
568575
return self._uart.baudrate
569576

570577
@baudrate.setter
571-
def baudrate(self, baudrate):
578+
def baudrate(self, baudrate: int) -> None:
572579
"""Change the modules baudrate via AT commands and then check
573580
that we're still sync'd."""
574581
at_cmd = "AT+UART_CUR=" + str(baudrate) + ",8,1,0,"
@@ -588,14 +595,14 @@ def baudrate(self, baudrate):
588595
if not self.sync():
589596
raise RuntimeError("Failed to resync after Baudrate change")
590597

591-
def echo(self, echo):
598+
def echo(self, echo: bool) -> None:
592599
"""Set AT command echo on or off"""
593600
if echo:
594601
self.at_response("ATE1", timeout=1)
595602
else:
596603
self.at_response("ATE0", timeout=1)
597604

598-
def soft_reset(self):
605+
def soft_reset(self) -> bool:
599606
"""Perform a software reset by AT command. Returns True
600607
if we successfully performed, false if failed to reset"""
601608
try:
@@ -609,13 +616,13 @@ def soft_reset(self):
609616
pass # fail, see below
610617
return False
611618

612-
def factory_reset(self):
619+
def factory_reset(self) -> None:
613620
"""Perform a hard reset, then send factory restore settings request"""
614621
self.hard_reset()
615622
self.at_response("AT+RESTORE", timeout=1)
616623
self._initialized = False
617624

618-
def hard_reset(self):
625+
def hard_reset(self) -> None:
619626
"""Perform a hardware reset by toggling the reset pin, if it was
620627
defined in the initialization of this object"""
621628
if self._reset_pin:

adafruit_espatcontrol/adafruit_espatcontrol_socket.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
"""A 'socket' compatible interface thru the ESP AT command set"""
66
from micropython import const
77

8+
try:
9+
from .adafruit_espatcontrol import ESP_ATcontrol
10+
from typing import Optional, Tuple, List
11+
except ImportError:
12+
pass
13+
814
_the_interface = None # pylint: disable=invalid-name
915

1016

11-
def set_interface(iface):
17+
def set_interface(iface: ESP_ATcontrol) -> None:
1218
"""Helper to set the global internet interface"""
1319
global _the_interface # pylint: disable=global-statement, invalid-name
1420
_the_interface = iface
@@ -18,7 +24,7 @@ def set_interface(iface):
1824
AF_INET = const(2)
1925

2026
# pylint: disable=too-many-arguments, unused-argument
21-
def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
27+
def getaddrinfo(host: str, port: int, family: int = 0, socktype: int = 0, proto:int = 0, flags:int = 0) -> List[Tuple[int, int, int, str, Tuple[str, int]]]:
2228
"""Given a hostname and a port name, return a 'socket.getaddrinfo'
2329
compatible list of tuples. Honestly, we ignore anything but host & port"""
2430
if not isinstance(port, int):
@@ -35,15 +41,15 @@ class socket:
3541
"""A simplified implementation of the Python 'socket' class, for connecting
3642
through an interface to a remote device"""
3743

38-
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
44+
def __init__(self, family: int = AF_INET, type: int = SOCK_STREAM, proto: int = 0, fileno: Optional[int] = None) -> None:
3945
if family != AF_INET:
4046
raise RuntimeError("Only AF_INET family supported")
4147
if type != SOCK_STREAM:
4248
raise RuntimeError("Only SOCK_STREAM type supported")
4349
self._buffer = b""
4450
self.settimeout(0)
4551

46-
def connect(self, address, conntype=None):
52+
def connect(self, address: Tuple[str, int], conntype: Optional[str] = None) -> None:
4753
"""Connect the socket to the 'address' (which should be dotted quad IP). 'conntype'
4854
is an extra that may indicate SSL or not, depending on the underlying interface"""
4955
host, port = address
@@ -61,11 +67,11 @@ def connect(self, address, conntype=None):
6167
raise RuntimeError("Failed to connect to host", host)
6268
self._buffer = b""
6369

64-
def send(self, data): # pylint: disable=no-self-use
70+
def send(self, data: bytes) -> None: # pylint: disable=no-self-use
6571
"""Send some data to the socket"""
6672
_the_interface.socket_send(data)
6773

68-
def readline(self):
74+
def readline(self) -> bytes:
6975
"""Attempt to return as many bytes as we can up to but not including '\r\n'"""
7076
if b"\r\n" not in self._buffer:
7177
# there's no line already in there, read some more
@@ -74,7 +80,7 @@ def readline(self):
7480
firstline, self._buffer = self._buffer.split(b"\r\n", 1)
7581
return firstline
7682

77-
def recv(self, num=0):
83+
def recv(self, num: int = 0) -> bytes:
7884
"""Read up to 'num' bytes from the socket, this may be buffered internally!
7985
If 'num' isnt specified, return everything in the buffer."""
8086
if num == 0:
@@ -90,15 +96,15 @@ def recv(self, num=0):
9096
self._buffer = self._buffer[num:]
9197
return ret
9298

93-
def close(self):
99+
def close(self) -> None:
94100
"""Close the socket, after reading whatever remains"""
95101
# read whatever's left
96102
self._buffer = self._buffer + _the_interface.socket_receive(
97103
timeout=self._timeout
98104
)
99105
_the_interface.socket_disconnect()
100106

101-
def settimeout(self, value):
107+
def settimeout(self, value: int) -> None:
102108
"""Set the read timeout for sockets, if value is 0 it will block"""
103109
self._timeout = value
104110

0 commit comments

Comments
 (0)