From 2a9a447b2404369636e84a28e4065ba214a8fd2b Mon Sep 17 00:00:00 2001 From: Steven Cogswell Date: Wed, 19 Oct 2022 15:31:31 -0300 Subject: [PATCH 01/33] WPA Enterprise support and status command changes --- .../adafruit_espatcontrol.py | 258 ++++++++++++++++-- .../adafruit_espatcontrol_wifimanager.py | 36 ++- 2 files changed, 265 insertions(+), 29 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index f159141..2895f48 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -65,10 +65,19 @@ class ESP_ATcontrol: TYPE_UDP = "UDP" TYPE_SSL = "SSL" TLS_MODE = "SSL" - STATUS_APCONNECTED = 2 - STATUS_SOCKETOPEN = 3 - STATUS_SOCKETCLOSED = 4 - STATUS_NOTCONNECTED = 5 + #STATUS_APCONNECTED = 2 # CIPSTATUS method + STATUS_WIFI_APCONNECTED = 2 # CWSTATE method + + #STATUS_SOCKETOPEN = 3 # CIPSTATUS method + STATUS_SOCKET_OPEN = 3 # CIPSTATE method + + #STATUS_SOCKETCLOSED = 4 # CIPSTATUS method + STATUS_SOCKET_CLOSED = 4 # CIPSTATE method + + #STATUS_NOTCONNECTED = 5 # CIPSTATUS method + STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method + STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method + USER_AGENT = "esp-idf/1.0 esp32" def __init__( @@ -169,6 +178,53 @@ def connect( print("Failed to connect\n", exp) raise + def connect_enterprise( + self, secrets: Dict[str, Union[str, int]], timeout: int = 15, retries: int = 3 + ) -> None: + """Repeatedly try to connect to an enterprise access point with the details in + the passed in 'secrets' dictionary. Be sure 'ssid','password','username','identity' + and 'method' are defined in the secrets dict! If 'timezone' is set, we'll also + configure SNTP""" + # Connect to WiFi if not already + retries = 3 + if self._debug: + print("In connect_enterprise()") + while True: + try: + if not self._initialized or retries == 0: + self.begin() + retries = 3 + AP = self.remote_AP # pylint: disable=invalid-name + if AP[0] is not None: + print("Connected to", AP[0]) + if AP[0] != secrets["ssid"]: + if self._debug: + print("Doing Enterprise connection sequence") + self.join_AP_Enterprise(secrets["ssid"], secrets["username"],secrets["identity"],secrets["password"],secrets["method"]) + if "timezone" in secrets: + tzone = secrets["timezone"] + ntp = None + if "ntp_server" in secrets: + ntp = secrets["ntp_server"] + self.sntp_config(True, tzone, ntp) + print("Connected to",self.remote_AP[0]) + print("My IP Address:", self.local_ip) + return # yay! + except (RuntimeError, OKError) as exp: + print("Failed to connect, retrying\n", exp) + retries -= 1 + continue + + + def set_autoconnect(self, autoconnect: bool) -> None: + """Set the auto connection status if the wifi connects automatically on powerup""" + if autoconnect == True: + auto_flag = "1" + else: + auto_flag = "0" + self.at_response("AT+CWAUTOCONN="+auto_flag) + + # *************************** SOCKET SETUP **************************** @property @@ -197,10 +253,11 @@ def socket_connect( # always disconnect for TYPE_UDP self.socket_disconnect() while True: - stat = self.status - if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): - break - if stat == self.STATUS_SOCKETOPEN: + stat_wifi = self.status_wifi + stat_socket = self.status_socket + if stat_wifi == self.STATUS_WIFI_APCONNECTED or stat_socket == self.STATUS_SOCKET_CLOSED: + break + if stat_socket == self.STATUS_SOCKET_OPEN: self.socket_disconnect() else: time.sleep(1) @@ -216,15 +273,18 @@ def socket_connect( + "," + str(keepalive) ) + if self._debug == True: + print("socket_connect(): Going to send command") replies = self.at_response(cmd, timeout=10, retries=retries).split(b"\r\n") for reply in replies: if reply == b"CONNECT" and ( - conntype == self.TYPE_TCP - and self.status == self.STATUS_SOCKETOPEN + (conntype == self.TYPE_TCP or conntype == self.TYPE_SSL) + and self.status_socket == self.STATUS_SOCKET_OPEN or conntype == self.TYPE_UDP ): self._conntype = conntype - return True + return True + return False def socket_send(self, buffer: bytes, timeout: int = 1) -> bool: @@ -375,26 +435,79 @@ def is_connected(self) -> bool: try: self.echo(False) self.baudrate = self.baudrate - stat = self.status - if stat in ( - self.STATUS_APCONNECTED, - self.STATUS_SOCKETOPEN, - self.STATUS_SOCKETCLOSED, - ): + if self.status_wifi == self.STATUS_WIFI_APCONNECTED or self.status_socket == self.STATUS_SOCKET_OPEN: + if self._debug: + print("is_connected(): status says connected") return True except (OKError, RuntimeError): pass + if self._debug: + print("is_connected(): status says not connected") return False @property def status(self) -> Union[int, None]: """The IP connection status number (see AT+CIPSTATUS datasheet for meaning)""" + # Note that CIPSTATUS, at least in the esp32-c3 version of espressif AT firmware + # is considered deprecated and you should use AT+CWSTATE for wifi state + # and AT+CIPSTATE for socket connection statuses. + # This muddies things with regards to how the original version of this routine + # ran the status responses since wifi + socket were mixed, so this has been broken + # out in to status_wifi() and status_socket() with their own constants for status + # This is here for historial reasons but isn't (shouldn't) be in use in the code now. replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"STATUS:"): + if self._debug: + print(f"CIPSTATUS state is {int(reply[7:8])}") return int(reply[7:8]) return None + @property + def status_wifi(self) -> Union[int, None]: + """The WIFI connection status number (see AT+CWSTATE datasheet for meaning)""" + # Note that as of 2022-Nov CIPSTATUS is deprecated and replaced with CWSTATE and CIPSTATE + # and the CWSTATE codes are different than the old CIPSTATUS codes. + # CWSTATE: + # : current Wi-Fi state. + # 0: ESP32-C3 station has not started any Wi-Fi connection. + # 1: ESP32-C3 station has connected to an AP, but does not get an IPv4 address yet. + # 2: ESP32-C3 station has connected to an AP, and got an IPv4 address. + # 3: ESP32-C3 station is in Wi-Fi connecting or reconnecting state. + # 4: ESP32-C3 station is in Wi-Fi disconnected state. + # <”ssid”>: the SSID of the target AP. + replies = self.at_response("AT+CWSTATE?", timeout=5).split(b"\r\n") + for reply in replies: + if reply.startswith(b"+CWSTATE:"): + state_info = reply.split(b",") + if self._debug: + print(f"State reply is {reply}, state_info[1] is {int(state_info[0][9:10])}") + return int(state_info[0][9:10]) + return None + + @property + def status_socket(self) -> Union[int, None]: + """The Socket connection status number (see AT+CIPSTATE for meaning)""" + # +CIPSTATE:,<"type">,<"remote IP">,,, + # OK + # When there is no connection, AT returns: + # OK + # Parameters + # : ID of the connection (0~4), used for multiple connections. + # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, “UDP”, “UDPv6”, “SSL”, or “SSLv6”. + # <”remote IP”>: string parameter showing the remote IPv4 address or IPv6 address. + # : the remote port number. + # : the local port number. + # : + # 0: ESP32-C3 runs as a client. + # 1: ESP32-C3 runs as a server. + replies = self.at_response("AT+CIPSTATE?", timeout=5).split(b"\r\n") + for reply in replies: + # If there are any +CIPSTATE lines that means it's an open socket + if reply.startswith(b"+CIPSTATE:"): + return self.STATUS_SOCKET_OPEN + return self.STATUS_SOCKET_CLOSED + @property def mode(self) -> Union[int, None]: """What mode we're in, can be MODE_STATION, MODE_SOFTAP or MODE_SOFTAPSTATION""" @@ -450,8 +563,8 @@ def nslookup(self, host: str) -> Union[str, None]: @property def remote_AP(self) -> List[Union[int, str, None]]: # pylint: disable=invalid-name """The name of the access point we're connected to, as a string""" - stat = self.status - if stat != self.STATUS_APCONNECTED: + stat = self.status_wifi + if stat != self.STATUS_WIFI_APCONNECTED: return [None] * 4 replies = self.at_response("AT+CWJAP?", timeout=10).split(b"\r\n") for reply in replies: @@ -473,6 +586,8 @@ def join_AP( # pylint: disable=invalid-name """Try to join an access point by name and password, will return immediately if we're already connected and won't try to reconnect""" # First make sure we're in 'station' mode so we can connect to AP's + if self._debug: + print("In join_AP()") if self.mode != self.MODE_STATION: self.mode = self.MODE_STATION @@ -492,6 +607,78 @@ def join_AP( # pylint: disable=invalid-name raise RuntimeError("Didn't get IP address") return + def join_AP_Enterprise( + self, ssid: str, username: str, identity: str, password: str, method: int, + timeout: int = 30, retries: int = 3 + ) -> None: # pylint: disable=invalid-name + """Try to join an Enterprise access point by name and password, will return + immediately if we're already connected and won't try to reconnect""" + # Not sure how to verify certificates so we set that to not verify. + certificate_security = 0 # Bit0: Client certificate.Bit1: Server certificate. + + # First make sure we're in 'station' mode so we can connect to AP's + if self._debug: + print("In join_AP_Enterprise()") + if self.mode != self.MODE_STATION: + self.mode = self.MODE_STATION + + router = self.remote_AP + if router and router[0] == ssid: + return # we're already connected! + reply = self.at_response( + # from https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-jeap + # AT+CWJEAP=,,,,,[,] + 'AT+CWJEAP="' + + ssid + '",' + str(method) + ',"' + identity + '","' + + username + '","' + password + '",' + str(certificate_security), + timeout=timeout, retries=retries + ) + if b"WIFI CONNECTED" not in reply: + print("no CONNECTED") + raise RuntimeError("Couldn't connect to Enterprise WiFi") + if b"WIFI GOT IP" not in reply: + print("no IP") + raise RuntimeError("Didn't get IP address") + return + + def disconnect(self, timeout: int = 5, retries: int = 3 ): + """Disconnect from the AP. Tries whether connected or not.""" + # If we're not connected we likely don't get a "WIFI DISCONNECT" and just get the OK + # Note it still tries to disconnect even if it says we're not connected. + if self.status_wifi == self.STATUS_WIFI_APCONNECTED: + wait_for_disconnect = True + else: + wait_for_disconnect = False + if self._debug == True: + print("disconnect(): Not connected, not waiting for disconnect message") + reply = self.at_response( + 'AT+CWQAP', timeout=timeout, retries=retries + ) + # Don't bother waiting for disconnect message if we weren't connected already + # sometimes the "WIFI DISCONNECT" shows up in the reply and sometimes it doesn't. + if wait_for_disconnect == True: + if b'WIFI DISCONNECT' in reply: + if self._debug == True: + print(f"disconnect(): Got WIFI DISCONNECT: {reply}") + else: + stamp = time.monotonic() + response = b"" + while (time.monotonic() - stamp) < timeout: + if self._uart.in_waiting: + response += self._uart.read(1) + self.hw_flow(False) + if response[-15:] == b"WIFI DISCONNECT": + break + else: + self.hw_flow(True) + if self._debug: + if response[-15:] == b"WIFI DISCONNECT": + print(f"disconnect(): Got WIFI DISCONNECT: {response}") + else: + print(f"disconnect(): Timed out wating for WIFI DISCONNECT: {response}") + return + + def scan_APs( # pylint: disable=invalid-name self, retries: int = 3 ) -> Union[List[List[bytes]], None]: @@ -567,7 +754,7 @@ def at_response(self, at_cmd: str, timeout: int = 5, retries: int = 3) -> bytes: break if response[-7:] == b"ERROR\r\n": break - if "AT+CWJAP=" in at_cmd: + if "AT+CWJAP=" in at_cmd or "AT+CWJEAP=" in at_cmd: if b"WIFI GOT IP\r\n" in response: break else: @@ -583,6 +770,11 @@ def at_response(self, at_cmd: str, timeout: int = 5, retries: int = 3) -> bytes: # special case, AT+CWJAP= does not return an ok :P if "AT+CWJAP=" in at_cmd and b"WIFI GOT IP\r\n" in response: return response + # special case, AT+CWJEAP= does not return an ok :P + if "AT+CWJEAP=" in at_cmd and b"WIFI GOT IP\r\n" in response: + return response + if "AT+CWQAP=" in at_cmd and b"WIFI DISCONNECT" in response: + return response # special case, ping also does not return an OK if "AT+PING" in at_cmd and b"ERROR\r\n" in response: return response @@ -638,16 +830,32 @@ def echo(self, echo: bool) -> None: else: self.at_response("ATE0", timeout=1) - def soft_reset(self) -> bool: + def soft_reset(self, timeout: int = 5) -> bool: """Perform a software reset by AT command. Returns True if we successfully performed, false if failed to reset""" try: self._uart.reset_input_buffer() reply = self.at_response("AT+RST", timeout=1) - if reply.strip(b"\r\n") == b"AT+RST": - time.sleep(2) - self._uart.reset_input_buffer() - return True + if self._debug: + print(f"Resetting with AT+RST, reply was {reply}") + stamp = time.monotonic() + response = b"" + while (time.monotonic() - stamp) < timeout: + if self._uart.in_waiting: + response += self._uart.read(1) + self.hw_flow(False) + if response[-5:] == b"ready": + break + else: + self.hw_flow(True) + if self._debug: + if response[-5:] == b"ready": + print(f"soft_reset(): Got ready: {response}") + else: + print(f"Tsoft_reset(): imed out waiting for ready: {response}") + self._uart.reset_input_buffer() + self.sync() + return True except OKError: pass # fail, see below return False diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index 9b25bf1..56f0d18 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -35,6 +35,8 @@ def __init__( secrets: Dict[str, Union[str, int]], status_pixel: Optional[FillBasedLED] = None, attempts: int = 2, + enterprise: bool=False, + debug: bool=False, ): """ :param ESP_SPIcontrol esp: The ESP object we are using @@ -45,20 +47,26 @@ def __init__( """ # Read the settings self._esp = esp - self.debug = False + self.debug = debug self.secrets = secrets self.attempts = attempts requests.set_socket(socket, esp) self.statuspix = status_pixel self.pixel_status(0) + self.enterprise = enterprise - def reset(self) -> None: + def reset(self, hard_reset: bool = True, soft_reset: bool = False) -> None: """ Perform a hard reset on the ESP """ + self.pixel_status((100,100,100)) if self.debug: print("Resetting ESP") - self._esp.hard_reset() + if hard_reset == True: + self._esp.hard_reset() + if soft_reset == True: + self._esp.soft_reset() + self.pixel_status(0) def connect(self, timeout: int = 15, retries: int = 3) -> None: """ @@ -68,12 +76,21 @@ def connect(self, timeout: int = 15, retries: int = 3) -> None: if self.debug: print("Connecting to AP...") self.pixel_status((100, 0, 0)) - self._esp.connect(self.secrets, timeout=timeout, retries=retries) + if self.enterprise == False: + self._esp.connect(self.secrets, timeout=timeout, retries=retries) + else: + self._esp.connect_enterprise(self.secrets, timeout=timeout, retries=retries) self.pixel_status((0, 100, 0)) except (ValueError, RuntimeError) as error: print("Failed to connect\n", error) raise + def disconnect(self) -> None: + """ + Disconnect the Wifi from the AP if any + """ + self._esp.disconnect() + def get(self, url: str, **kw: Any) -> requests.Response: """ Pass the Get request to requests and update Status NeoPixel @@ -89,6 +106,7 @@ def get(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) + requests.set_socket(socket, self._esp) return_val = requests.get(url, **kw) self.pixel_status(0) return return_val @@ -105,10 +123,17 @@ def post(self, url: str, **kw: Any) -> requests.Response: :return: The response from the request :rtype: Response """ + if self.debug: + print("in post()") if not self._esp.is_connected: + if self.debug: + print("post(): not connected, trying to connect") self.connect() self.pixel_status((0, 0, 100)) + requests.set_socket(socket, self._esp) return_val = requests.post(url, **kw) + self.pixel_status(0) + return return_val def put(self, url: str, **kw: Any) -> requests.Response: @@ -126,6 +151,7 @@ def put(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) + requests.set_socket(socket, self._esp) return_val = requests.put(url, **kw) self.pixel_status(0) return return_val @@ -145,6 +171,7 @@ def patch(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) + requests.set_socket(socket, self._esp) return_val = requests.patch(url, **kw) self.pixel_status(0) return return_val @@ -164,6 +191,7 @@ def delete(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) + requests.set_socket(socket, self._esp) return_val = requests.delete(url, **kw) self.pixel_status(0) return return_val From fb83f259f5aaf96b36cd6bec36e7a2a771f92964 Mon Sep 17 00:00:00 2001 From: scogswell Date: Wed, 19 Oct 2022 16:25:22 -0300 Subject: [PATCH 02/33] Add some documentation and spelling cleanup --- README.rst | 19 +++++++++++++++++++ .../adafruit_espatcontrol.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 489a996..72ec261 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,22 @@ +This is a fork of the Adafruit_CircuitPython_ESP_ATcontrol library with changes: + +* WPA Enterprise WPA-PEAP support in espatcontrol and wifimanager (must be compiled in your esp AT firmware) +* Moving from deprecated AT+CIPSTATUS to AT+CWSTATE for wifi status and AT+CIPSTATE for socket status. + See https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/TCP-IP_AT_Commands.html#cmd-status +* Fixes for reconnection issues with adafruit_requests and sockets in the wifimanager +* Changed handling for soft_reset(). Added soft_reset() + as an option in lots of places hard_reset() is being used. + For boards that don't have a hardware reset line + (e.g. - a repurposed Adafruit QT Py ESP32-C3 I tested this with https://www.adafruit.com/product/5405) +* Added function to disconnect wifi +* Added function to change autoreconnect status +* Bug fixes for SSL connection replies not being accounted for in socket_connect() + +My test module is an Adafruit Qt Py ESP32-C3 connected via UART to an Adafruit Feather M4 Express +Using circuitpython 7.3.3 and compiled espressif AT command firmware for the ESP32-C3. I haven't made any +code changes to the espressif AT firmware other than to enable WPA enterprise support in menuconfig +and recompile/flash the firmware onto the ESP32-C3. "Your mileage may vary" + Introduction ============ diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 2895f48..b397ebb 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -852,7 +852,7 @@ def soft_reset(self, timeout: int = 5) -> bool: if response[-5:] == b"ready": print(f"soft_reset(): Got ready: {response}") else: - print(f"Tsoft_reset(): imed out waiting for ready: {response}") + print(f"soft_reset(): imed out waiting for ready: {response}") self._uart.reset_input_buffer() self.sync() return True From a116f8b2a256c2f1884fa4315e166369af32171b Mon Sep 17 00:00:00 2001 From: scogswell Date: Wed, 19 Oct 2022 16:46:37 -0300 Subject: [PATCH 03/33] Provide examples --- README.rst | 4 + .../code-AIO-no-wifimanager-enterprise.py | 89 ++++++++ .../code-AIO-wifimanager-enterprise.py | 92 ++++++++ fork-examples/code-countviewer-enterprise.py | 200 ++++++++++++++++++ fork-examples/code-simple-enterprise.py | 88 ++++++++ fork-examples/secrets.py | 17 ++ 6 files changed, 490 insertions(+) create mode 100644 fork-examples/code-AIO-no-wifimanager-enterprise.py create mode 100644 fork-examples/code-AIO-wifimanager-enterprise.py create mode 100644 fork-examples/code-countviewer-enterprise.py create mode 100644 fork-examples/code-simple-enterprise.py create mode 100644 fork-examples/secrets.py diff --git a/README.rst b/README.rst index 72ec261..e657dcd 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,10 @@ Using circuitpython 7.3.3 and compiled espressif AT command firmware for the ESP code changes to the espressif AT firmware other than to enable WPA enterprise support in menuconfig and recompile/flash the firmware onto the ESP32-C3. "Your mileage may vary" +I've provided some updated examples in ``fork-examples`` + +Original README follows: + Introduction ============ diff --git a/fork-examples/code-AIO-no-wifimanager-enterprise.py b/fork-examples/code-AIO-no-wifimanager-enterprise.py new file mode 100644 index 0000000..1be981f --- /dev/null +++ b/fork-examples/code-AIO-no-wifimanager-enterprise.py @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Note, you must create a feed called "test" in your AdafruitIO account. +# Your secrets file must contain your aio_username and aio_key + +import time +import board +import busio +import adafruit_requests as requests +from digitalio import DigitalInOut +from digitalio import Direction +import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket + + +# ESP32 AT +from adafruit_espatcontrol import ( + adafruit_espatcontrol) + + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +# Debug Level +# Change the Debug Flag if you have issues with AT commands +debugflag = False + +if board.board_id == "challenger_rp2040_wifi": + RX = board.ESP_RX + TX = board.ESP_TX + resetpin = DigitalInOut(board.WIFI_RESET) + rtspin = False + uart = busio.UART(TX, RX, baudrate=11520, receiver_buffer_size=2048) + esp_boot = DigitalInOut(board.WIFI_MODE) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None +else: + RX = board.TX + TX = board.RX + resetpin = DigitalInOut(board.D4) + rtspin = DigitalInOut(board.D5) + uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + esp_boot = DigitalInOut(board.D9) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None + +print("ESP AT commands") +esp = adafruit_espatcontrol.ESP_ATcontrol( + uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag +) + +requests.set_socket(socket, esp) + +counter = 0 + +while True: + try: + while not esp.is_connected: + print("Connecting...") + esp.connect_enterprise(secrets) + print("Posting data...", end="") + data = counter + feed = "example" + payload = {"value": data} + response = requests.post( + "https://io.adafruit.com/api/v2/" + + secrets["aio_username"] + + "/feeds/" + + feed + + "/data", + json=payload, + headers={"X-AIO-KEY": secrets["aio_key"]}, + ) + print(response.json()) + response.close() + counter = counter + 1 + print("OK") + except (ValueError, RuntimeError, adafruit_espatcontrol.OKError) as e: + print("Failed to get data, retrying\n", e) + esp.soft_reset() + continue + response = None + time.sleep(15) diff --git a/fork-examples/code-AIO-wifimanager-enterprise.py b/fork-examples/code-AIO-wifimanager-enterprise.py new file mode 100644 index 0000000..4e9a84f --- /dev/null +++ b/fork-examples/code-AIO-wifimanager-enterprise.py @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Note, you must create a feed called "test" in your AdafruitIO account. +# Your secrets file must contain your aio_username and aio_key + +import time +import board +import busio +from digitalio import DigitalInOut +from digitalio import Direction +import neopixel + +# ESP32 AT +from adafruit_espatcontrol import ( + adafruit_espatcontrol, + adafruit_espatcontrol_wifimanager, +) + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +pixel_status = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3, auto_write=True) + +# Debug Level +# Change the Debug Flag if you have issues with AT commands +debugflag = False + +if board.board_id == "challenger_rp2040_wifi": + RX = board.ESP_RX + TX = board.ESP_TX + resetpin = DigitalInOut(board.WIFI_RESET) + rtspin = False + uart = busio.UART(TX, RX, baudrate=11520, receiver_buffer_size=2048) + esp_boot = DigitalInOut(board.WIFI_MODE) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None +else: + RX = board.TX + TX = board.RX + resetpin = DigitalInOut(board.D4) + rtspin = DigitalInOut(board.D5) + uart = busio.UART(board.TX, board.RX, timeout=0.1) + esp_boot = DigitalInOut(board.D9) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = pixel_status + +print("ESP AT commands") +esp = adafruit_espatcontrol.ESP_ATcontrol( + uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag +) +wifi = adafruit_espatcontrol_wifimanager.ESPAT_WiFiManager(esp, secrets, status_light, enterprise=True, debug=debugflag) +wifi.disconnect() +wifi.reset(soft_reset=True) + +counter = 0 + +while True: + try: + print("Posting data...", end="") + data = counter + feed = "example" + payload = {"value": data} + response = wifi.post( + "https://io.adafruit.com/api/v2/" + + secrets["aio_username"] + + "/feeds/" + + feed + + "/data", + json=payload, + headers={"X-AIO-KEY": secrets["aio_key"]}, + ) + print(response.json()) + response.close() + + counter = counter + 1 + print("OK") + wifi.disconnect() + + except (ValueError, RuntimeError, adafruit_espatcontrol.OKError) as e: + print("Failed to get data, retrying\n", e) + wifi.reset(soft_reset=True) + continue + response = None + time.sleep(15) + diff --git a/fork-examples/code-countviewer-enterprise.py b/fork-examples/code-countviewer-enterprise.py new file mode 100644 index 0000000..a5faae9 --- /dev/null +++ b/fork-examples/code-countviewer-enterprise.py @@ -0,0 +1,200 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +""" +This example will access an API, grab a number like hackaday skulls, github +stars, price of bitcoin, twitter followers... if you can find something that +spits out JSON data, we can display it! +""" +import gc +import time +import board +import busio +from digitalio import DigitalInOut +from digitalio import Direction +import neopixel +import adafruit_requests as requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +from adafruit_espatcontrol import adafruit_espatcontrol + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +# CONFIGURATION +PLAY_SOUND_ON_CHANGE = False +NEOPIXELS_ON_CHANGE = True +DISPLAY_ATTACHED = False +TIME_BETWEEN_QUERY = 60 # in seconds + +# Some data sources and JSON locations to try out + +# Bitcoin value in USD +# DATA_SOURCE = "http://api.coindesk.com/v1/bpi/currentprice.json" +# DATA_LOCATION = ["bpi", "USD", "rate_float"] + +# Github stars! You can query 1ce a minute without an API key token +# DATA_SOURCE = "https://api.github.com/repos/adafruit/circuitpython" +# if 'github_token' in secrets: +# DATA_SOURCE += "?access_token="+secrets['github_token'] +# DATA_LOCATION = ["stargazers_count"] + +# Youtube stats +# CHANNEL_ID = "UCpOlOeQjj7EsVnDh3zuCgsA" # this isn't a secret but you have to look it up +# DATA_SOURCE = "https://www.googleapis.com/youtube/v3/channels/?part=statistics&id=" \ +# + CHANNEL_ID +"&key="+secrets['youtube_token'] +# #try also 'viewCount' or 'videoCount +# DATA_LOCATION = ["items", 0, "statistics", "subscriberCount"] + + +# # Subreddit subscribers +# DATA_SOURCE = "https://www.reddit.com/r/circuitpython/about.json" +# DATA_LOCATION = ["data", "subscribers"] + +# Hackaday Skulls (likes), requires an API key +# DATA_SOURCE = "https://api.hackaday.io/v1/projects/1340?api_key="+secrets['hackaday_token'] +# DATA_LOCATION = ["skulls"] + +# Twitter followers +DATA_SOURCE = "http://cdn.syndication.twimg.com/widgets/followbutton/info.json?" + \ + "screen_names=adafruit" +DATA_LOCATION = [0, "followers_count"] + +# Debug Level +# Change the Debug Flag if you have issues with AT commands +debugflag = True + +if board.board_id == "challenger_rp2040_wifi": + RX = board.ESP_RX + TX = board.ESP_TX + resetpin = DigitalInOut(board.WIFI_RESET) + rtspin = False + uart = busio.UART(TX, RX, baudrate=11520, receiver_buffer_size=2048) + esp_boot = DigitalInOut(board.WIFI_MODE) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None + pixel_pin = board.NEOPIXEL + num_pixels = 1 + pixel_type = "RGBW/GRBW" +else: + RX = board.TX + TX = board.RX + resetpin = DigitalInOut(board.D4) + rtspin = DigitalInOut(board.D5) + uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + esp_boot = DigitalInOut(board.D9) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None + pixel_pin = board.A1 + num_pixels = 16 + pixel_type = "RGB/GRB" + +# Create the connection to the co-processor and reset +esp = adafruit_espatcontrol.ESP_ATcontrol( + uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag +) +esp.soft_reset() +esp.disconnect() + +requests.set_socket(socket, esp) +# display +if DISPLAY_ATTACHED: + # Create the I2C interface. + i2c = busio.I2C(board.SCL, board.SDA) + # Attach a 7 segment display and display -'s so we know its not live yet + display = segments.Seg7x4(i2c) + display.print("----") + +# neopixels +if NEOPIXELS_ON_CHANGE: + pixels = neopixel.NeoPixel( + pixel_pin, num_pixels, brightness=0.4, pixel_order=(1, 0, 2, 3) + ) + pixels.fill(20) + +# music! +if PLAY_SOUND_ON_CHANGE: + import audioio + + wave_file = open("coin.wav", "rb") # pylint: disable=consider-using-with + wave = audioio.WaveFile(wave_file) + +# we'll save the value in question +last_value = value = None +the_time = None +times = 0 + + +def chime_light(): + """Light up LEDs and play a tune""" + if NEOPIXELS_ON_CHANGE: + for i in range(0, 100, 10): + if pixel_type == "RGB/GRB": + pixels.fill((i, i, i)) + elif pixel_type == "RGBW/GRBW": + pixels.fill((i, i, i, i)) + pixels.show() + time.sleep(1) + if PLAY_SOUND_ON_CHANGE: + with audioio.AudioOut(board.A0) as audio: + audio.play(wave) + while audio.playing: + pass + if NEOPIXELS_ON_CHANGE: + for i in range(100, 0, -10): + if pixel_type == "RGB/GRB": + pixels.fill((i, i, i)) + elif pixel_type == "RGBW/GRBW": + pixels.fill((i, i, i, i)) + pixels.show() + time.sleep(1) + pixels.fill(0) + + +while True: + try: + while not esp.is_connected: + # secrets dictionary must contain 'ssid' and 'password' at a minimum + esp.connect_enterprise(secrets) + + the_time = esp.sntp_time + + # great, lets get the data + print("Retrieving data source...", end="") + r = requests.get(DATA_SOURCE) + print("Reply is OK!") + except (ValueError, RuntimeError, adafruit_espatcontrol.OKError) as e: + print("Failed to get data, retrying\n", e) + continue + # print('-'*40,) + # print("Headers: ", r.headers) + # print("Text:", r.text) + # print('-'*40) + + value = r.json() + for x in DATA_LOCATION: + value = value[x] + if not value: + continue + print("Times:{0}. The Time:{1}. Value: {2}".format(times, the_time, value)) + if DISPLAY_ATTACHED: + display.print(int(value)) + else: + print("INT Value:{0}".format(int(value))) + + if last_value != value: + chime_light() # animate the neopixels + last_value = value + times += 1 + + # normally we wouldn't have to do this, but we get bad fragments + r = value = None + gc.collect() + print("GC MEM:{0}".format(gc.mem_free())) # pylint: disable=no-member + print("Sleeping for: {0} Seconds".format(TIME_BETWEEN_QUERY)) + time.sleep(TIME_BETWEEN_QUERY) diff --git a/fork-examples/code-simple-enterprise.py b/fork-examples/code-simple-enterprise.py new file mode 100644 index 0000000..8d6d1ab --- /dev/null +++ b/fork-examples/code-simple-enterprise.py @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time +import board +import busio +from digitalio import DigitalInOut +from digitalio import Direction +import adafruit_requests as requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +from adafruit_espatcontrol import adafruit_espatcontrol + + +# Get wifi details and more from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +# Debug Level +# Change the Debug Flag if you have issues with AT commands +debugflag = False + +# How long between queries +TIME_BETWEEN_QUERY = 60 # in seconds + +if board.board_id == "challenger_rp2040_wifi": + RX = board.ESP_RX + TX = board.ESP_TX + resetpin = DigitalInOut(board.WIFI_RESET) + rtspin = False + uart = busio.UART(TX, RX, baudrate=11520, receiver_buffer_size=2048) + esp_boot = DigitalInOut(board.WIFI_MODE) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None +else: + RX = board.TX + TX = board.RX + resetpin = DigitalInOut(board.D4) + rtspin = DigitalInOut(board.D5) + uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + esp_boot = DigitalInOut(board.D9) + esp_boot.direction = Direction.OUTPUT + esp_boot.value = True + status_light = None + +print("ESP AT commands") +esp = adafruit_espatcontrol.ESP_ATcontrol( + uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag +) + +URL = "http://wifitest.adafruit.com/testwifi/index.html" +print("ESP AT GET URL", URL) + +print("Resetting ESP module") +esp.hard_reset() +esp.soft_reset() +esp.disconnect() +#time.sleep(20) +esp.set_autoconnect(False) + +requests.set_socket(socket, esp) + + +while True: + try: + print(f"Checking connection to {secrets['ssid']}...") + while not esp.is_connected: + print("Connecting...") + esp.connect_enterprise(secrets) + # great, lets get the data + print("Retrieving URL...", end="") + r = requests.get(URL) + print("Status:", r.status_code) + print("Content type:", r.headers["content-type"]) + print("Content size:", r.headers["content-length"]) + print("Encoding:", r.encoding) + print("Text:", r.text) + print("Disconnecting from WiFi") + esp.disconnect() + esp.disconnect() + print("Sleeping for: {0} Seconds".format(TIME_BETWEEN_QUERY)) + time.sleep(TIME_BETWEEN_QUERY) + except (ValueError, RuntimeError, adafruit_espatcontrol.OKError) as e: + print("Failed to get data, retrying\n", e) + continue diff --git a/fork-examples/secrets.py b/fork-examples/secrets.py new file mode 100644 index 0000000..b5060aa --- /dev/null +++ b/fork-examples/secrets.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# This file is where you keep secret settings, passwords, and tokens! +# If you put them in the code you risk committing that info or sharing it + +secrets = { + "ssid": "your-ssid", + "password": "your-password", + "identity": "your-identity", + "username": "your-username", + "method": 1, # 0 = EAP-TLS, 1=EAP-PEAP, 2=EAP-TTLSs + "timezone": -2, # this is offset from UTC + "github_token": "abcdefghij0123456789", + "aio_username": "your-aio-username", + "aio_key": "your-aio-key", +} From 60ce4e3a5c9480f24c5bc5d81531411bba6bb8d2 Mon Sep 17 00:00:00 2001 From: scogswell Date: Thu, 20 Oct 2022 16:52:29 -0300 Subject: [PATCH 04/33] Support CIPSTATUS for older boards (eg ESP8285) --- .../adafruit_espatcontrol.py | 106 +++++++++++++----- 1 file changed, 81 insertions(+), 25 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index b397ebb..65f39b2 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -65,16 +65,16 @@ class ESP_ATcontrol: TYPE_UDP = "UDP" TYPE_SSL = "SSL" TLS_MODE = "SSL" - #STATUS_APCONNECTED = 2 # CIPSTATUS method + STATUS_APCONNECTED = 2 # CIPSTATUS method STATUS_WIFI_APCONNECTED = 2 # CWSTATE method - #STATUS_SOCKETOPEN = 3 # CIPSTATUS method + STATUS_SOCKETOPEN = 3 # CIPSTATUS method STATUS_SOCKET_OPEN = 3 # CIPSTATE method - #STATUS_SOCKETCLOSED = 4 # CIPSTATUS method + STATUS_SOCKETCLOSED = 4 # CIPSTATUS method STATUS_SOCKET_CLOSED = 4 # CIPSTATE method - #STATUS_NOTCONNECTED = 5 # CIPSTATUS method + STATUS_NOTCONNECTED = 5 # CIPSTATUS method STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method @@ -88,7 +88,8 @@ def __init__( run_baudrate: Optional[int] = None, rts_pin: Optional[DigitalInOut] = None, reset_pin: Optional[DigitalInOut] = None, - debug: bool = False + debug: bool = False, + use_cipstatus: bool= False ): """This function doesn't try to do any sync'ing, just sets up # the hardware, that way nothing can unexpectedly fail!""" @@ -116,6 +117,8 @@ def __init__( self._initialized = False self._conntype = None + self._use_cipstatus = use_cipstatus + def begin(self) -> None: """Initialize the module by syncing, resetting if necessary, setting up the desired baudrate, turning on single-socket mode, and configuring @@ -139,6 +142,15 @@ def begin(self) -> None: except OKError: # ESP32 doesnt use CIPSSLSIZE, its ok! self.at_response("AT+CIPSSLCCONF?") + + try: + self.at_response("AT+CWSTATE?", retries=1, timeout=3) + except OKError: + # ESP8285's use CIPSTATUS and have no CWSTATE or CWIPSTATUS functions + self._use_cipstatus=True + if self._debug: + print("No CWSTATE support, using CIPSTATUS, it's ok!") + self._initialized = True return except OKError: @@ -253,14 +265,23 @@ def socket_connect( # always disconnect for TYPE_UDP self.socket_disconnect() while True: - stat_wifi = self.status_wifi - stat_socket = self.status_socket - if stat_wifi == self.STATUS_WIFI_APCONNECTED or stat_socket == self.STATUS_SOCKET_CLOSED: + if self._use_cipstatus: + stat = self.status + if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): break - if stat_socket == self.STATUS_SOCKET_OPEN: - self.socket_disconnect() + if stat == self.STATUS_SOCKETOPEN: + self.socket_disconnect() + else: + time.sleep(1) else: - time.sleep(1) + stat_wifi = self.status_wifi + stat_socket = self.status_socket + if stat_wifi == self.STATUS_WIFI_APCONNECTED or stat_socket == self.STATUS_SOCKET_CLOSED: + break + if stat_socket == self.STATUS_SOCKET_OPEN: + self.socket_disconnect() + else: + time.sleep(1) if not conntype in (self.TYPE_TCP, self.TYPE_UDP, self.TYPE_SSL): raise RuntimeError("Connection type must be TCP, UDL or SSL") cmd = ( @@ -279,7 +300,11 @@ def socket_connect( for reply in replies: if reply == b"CONNECT" and ( (conntype == self.TYPE_TCP or conntype == self.TYPE_SSL) - and self.status_socket == self.STATUS_SOCKET_OPEN + and ( + (not self._use_cipstatus and self.status_socket == self.STATUS_SOCKET_OPEN) + or + (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) + ) or conntype == self.TYPE_UDP ): self._conntype = conntype @@ -435,10 +460,21 @@ def is_connected(self) -> bool: try: self.echo(False) self.baudrate = self.baudrate - if self.status_wifi == self.STATUS_WIFI_APCONNECTED or self.status_socket == self.STATUS_SOCKET_OPEN: - if self._debug: - print("is_connected(): status says connected") - return True + if self._use_cipstatus: + stat = self.status + if stat in ( + self.STATUS_APCONNECTED, + self.STATUS_SOCKETOPEN, + self.STATUS_SOCKETCLOSED, + ): + if self._debug: + print("is_connected(): status says connected") + return True + else: + if self.status_wifi == self.STATUS_WIFI_APCONNECTED or self.status_socket == self.STATUS_SOCKET_OPEN: + if self._debug: + print("is_connected(): status says connected") + return True except (OKError, RuntimeError): pass if self._debug: @@ -454,7 +490,8 @@ def status(self) -> Union[int, None]: # This muddies things with regards to how the original version of this routine # ran the status responses since wifi + socket were mixed, so this has been broken # out in to status_wifi() and status_socket() with their own constants for status - # This is here for historial reasons but isn't (shouldn't) be in use in the code now. + # This is here for legacy reasons like the the ESP8285 version of the espressif + # AT commands which is on the Challenger RP2040 Wifi feather. replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"STATUS:"): @@ -563,9 +600,14 @@ def nslookup(self, host: str) -> Union[str, None]: @property def remote_AP(self) -> List[Union[int, str, None]]: # pylint: disable=invalid-name """The name of the access point we're connected to, as a string""" - stat = self.status_wifi - if stat != self.STATUS_WIFI_APCONNECTED: - return [None] * 4 + if self._use_cipstatus: + stat = self.status + if stat != self.STATUS_APCONNECTED: + return [None] * 4 + else: + stat = self.status_wifi + if stat != self.STATUS_WIFI_APCONNECTED: + return [None] * 4 replies = self.at_response("AT+CWJAP?", timeout=10).split(b"\r\n") for reply in replies: if not reply.startswith("+CWJAP:"): @@ -645,12 +687,26 @@ def disconnect(self, timeout: int = 5, retries: int = 3 ): """Disconnect from the AP. Tries whether connected or not.""" # If we're not connected we likely don't get a "WIFI DISCONNECT" and just get the OK # Note it still tries to disconnect even if it says we're not connected. - if self.status_wifi == self.STATUS_WIFI_APCONNECTED: - wait_for_disconnect = True + if not self._initialized: + self.begin() + if self._use_cipstatus: + stat = self.status + if stat in ( + self.STATUS_APCONNECTED, + self.STATUS_SOCKETOPEN, + self.STATUS_SOCKETCLOSED, + ): + wait_for_disconnect = True + else: + if self._debug == True: + print("disconnect(): Not connected, not waiting for disconnect message") else: - wait_for_disconnect = False - if self._debug == True: - print("disconnect(): Not connected, not waiting for disconnect message") + if self.status_wifi == self.STATUS_WIFI_APCONNECTED: + wait_for_disconnect = True + else: + wait_for_disconnect = False + if self._debug == True: + print("disconnect(): Not connected, not waiting for disconnect message") reply = self.at_response( 'AT+CWQAP', timeout=timeout, retries=retries ) From 05b484c75f809e3065bcefe09b31145000bc52fd Mon Sep 17 00:00:00 2001 From: scogswell Date: Thu, 20 Oct 2022 16:56:03 -0300 Subject: [PATCH 05/33] fix README to reflect CIPSTATUS fallback mode --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index e657dcd..b6536de 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,8 @@ This is a fork of the Adafruit_CircuitPython_ESP_ATcontrol library with changes: * WPA Enterprise WPA-PEAP support in espatcontrol and wifimanager (must be compiled in your esp AT firmware) * Moving from deprecated AT+CIPSTATUS to AT+CWSTATE for wifi status and AT+CIPSTATE for socket status. See https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/TCP-IP_AT_Commands.html#cmd-status + This will fall back to using CIPSTATUS if CWSTATE isn't available (ie AT command set on the ESP8285 + that's on the Challenger RP2040 Wifi which can only use up to firmware 2.2) * Fixes for reconnection issues with adafruit_requests and sockets in the wifimanager * Changed handling for soft_reset(). Added soft_reset() as an option in lots of places hard_reset() is being used. From b542d4d6191c0db297f95b16c54d47b63a46a45f Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 21 Oct 2022 15:46:13 -0300 Subject: [PATCH 06/33] missing statement in disconnect --- adafruit_espatcontrol/adafruit_espatcontrol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 65f39b2..46a7ffc 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -698,6 +698,7 @@ def disconnect(self, timeout: int = 5, retries: int = 3 ): ): wait_for_disconnect = True else: + wait_for_disconnect = False if self._debug == True: print("disconnect(): Not connected, not waiting for disconnect message") else: From 9135d4b9548b9d30a855c829c348000eb4994349 Mon Sep 17 00:00:00 2001 From: scogswell Date: Mon, 24 Oct 2022 10:43:10 -0300 Subject: [PATCH 07/33] Move examples to examples directory, better names --- .../esp_atcontrol_AIO_no_wifimanager-enterprise.py | 0 .../esp_atcontrol_AIO_wifimanager_enterprise.py | 0 .../esp_atcontrol_countviewer_enterprise.py | 0 .../esp_atcontrol_simple_enterprise.py | 0 fork-examples/secrets.py => examples/secrets_enterprise.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename fork-examples/code-AIO-no-wifimanager-enterprise.py => examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py (100%) rename fork-examples/code-AIO-wifimanager-enterprise.py => examples/esp_atcontrol_AIO_wifimanager_enterprise.py (100%) rename fork-examples/code-countviewer-enterprise.py => examples/esp_atcontrol_countviewer_enterprise.py (100%) rename fork-examples/code-simple-enterprise.py => examples/esp_atcontrol_simple_enterprise.py (100%) rename fork-examples/secrets.py => examples/secrets_enterprise.py (99%) diff --git a/fork-examples/code-AIO-no-wifimanager-enterprise.py b/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py similarity index 100% rename from fork-examples/code-AIO-no-wifimanager-enterprise.py rename to examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py diff --git a/fork-examples/code-AIO-wifimanager-enterprise.py b/examples/esp_atcontrol_AIO_wifimanager_enterprise.py similarity index 100% rename from fork-examples/code-AIO-wifimanager-enterprise.py rename to examples/esp_atcontrol_AIO_wifimanager_enterprise.py diff --git a/fork-examples/code-countviewer-enterprise.py b/examples/esp_atcontrol_countviewer_enterprise.py similarity index 100% rename from fork-examples/code-countviewer-enterprise.py rename to examples/esp_atcontrol_countviewer_enterprise.py diff --git a/fork-examples/code-simple-enterprise.py b/examples/esp_atcontrol_simple_enterprise.py similarity index 100% rename from fork-examples/code-simple-enterprise.py rename to examples/esp_atcontrol_simple_enterprise.py diff --git a/fork-examples/secrets.py b/examples/secrets_enterprise.py similarity index 99% rename from fork-examples/secrets.py rename to examples/secrets_enterprise.py index b5060aa..40e254f 100644 --- a/fork-examples/secrets.py +++ b/examples/secrets_enterprise.py @@ -14,4 +14,4 @@ "github_token": "abcdefghij0123456789", "aio_username": "your-aio-username", "aio_key": "your-aio-key", -} +} \ No newline at end of file From e4cb4e2908d0507dbd4d91feaf1ad3bd9c516cde Mon Sep 17 00:00:00 2001 From: scogswell Date: Mon, 24 Oct 2022 10:56:20 -0300 Subject: [PATCH 08/33] code syntax changes from workflow recommendations --- .../adafruit_espatcontrol.py | 20 ++++++++++++------- .../adafruit_espatcontrol_wifimanager.py | 6 +++--- .../esp_atcontrol_countviewer_enterprise.py | 6 ++++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 46a7ffc..ab7c00a 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -212,7 +212,13 @@ def connect_enterprise( if AP[0] != secrets["ssid"]: if self._debug: print("Doing Enterprise connection sequence") - self.join_AP_Enterprise(secrets["ssid"], secrets["username"],secrets["identity"],secrets["password"],secrets["method"]) + self.join_AP_Enterprise(secrets["ssid"], + secrets["username"], + secrets["identity"], + secrets["password"], + secrets["method"], + timeout=timeout, + retries=retries,) if "timezone" in secrets: tzone = secrets["timezone"] ntp = None @@ -230,7 +236,7 @@ def connect_enterprise( def set_autoconnect(self, autoconnect: bool) -> None: """Set the auto connection status if the wifi connects automatically on powerup""" - if autoconnect == True: + if autoconnect is True: auto_flag = "1" else: auto_flag = "0" @@ -294,7 +300,7 @@ def socket_connect( + "," + str(keepalive) ) - if self._debug == True: + if self._debug is True: print("socket_connect(): Going to send command") replies = self.at_response(cmd, timeout=10, retries=retries).split(b"\r\n") for reply in replies: @@ -699,23 +705,23 @@ def disconnect(self, timeout: int = 5, retries: int = 3 ): wait_for_disconnect = True else: wait_for_disconnect = False - if self._debug == True: + if self._debug is True: print("disconnect(): Not connected, not waiting for disconnect message") else: if self.status_wifi == self.STATUS_WIFI_APCONNECTED: wait_for_disconnect = True else: wait_for_disconnect = False - if self._debug == True: + if self._debug is True: print("disconnect(): Not connected, not waiting for disconnect message") reply = self.at_response( 'AT+CWQAP', timeout=timeout, retries=retries ) # Don't bother waiting for disconnect message if we weren't connected already # sometimes the "WIFI DISCONNECT" shows up in the reply and sometimes it doesn't. - if wait_for_disconnect == True: + if wait_for_disconnect is True: if b'WIFI DISCONNECT' in reply: - if self._debug == True: + if self._debug is True: print(f"disconnect(): Got WIFI DISCONNECT: {reply}") else: stamp = time.monotonic() diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index 56f0d18..26c1566 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -62,9 +62,9 @@ def reset(self, hard_reset: bool = True, soft_reset: bool = False) -> None: self.pixel_status((100,100,100)) if self.debug: print("Resetting ESP") - if hard_reset == True: + if hard_reset is True: self._esp.hard_reset() - if soft_reset == True: + if soft_reset is True: self._esp.soft_reset() self.pixel_status(0) @@ -76,7 +76,7 @@ def connect(self, timeout: int = 15, retries: int = 3) -> None: if self.debug: print("Connecting to AP...") self.pixel_status((100, 0, 0)) - if self.enterprise == False: + if self.enterprise is False: self._esp.connect(self.secrets, timeout=timeout, retries=retries) else: self._esp.connect_enterprise(self.secrets, timeout=timeout, retries=retries) diff --git a/examples/esp_atcontrol_countviewer_enterprise.py b/examples/esp_atcontrol_countviewer_enterprise.py index a5faae9..2e65279 100644 --- a/examples/esp_atcontrol_countviewer_enterprise.py +++ b/examples/esp_atcontrol_countviewer_enterprise.py @@ -17,6 +17,12 @@ import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket from adafruit_espatcontrol import adafruit_espatcontrol +try: + from adafruit_ht16k33 import segments +except ImportError: + pass + + # Get wifi details and more from a secrets.py file try: from secrets import secrets From ce757d3a4164adef1c6626885bacffd42116b12c Mon Sep 17 00:00:00 2001 From: scogswell Date: Mon, 24 Oct 2022 14:54:24 -0300 Subject: [PATCH 09/33] formatting with black --- .../adafruit_espatcontrol.py | 158 +++++++++++------- .../adafruit_espatcontrol_wifimanager.py | 20 ++- 2 files changed, 105 insertions(+), 73 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index ab7c00a..2389481 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -65,16 +65,16 @@ class ESP_ATcontrol: TYPE_UDP = "UDP" TYPE_SSL = "SSL" TLS_MODE = "SSL" - STATUS_APCONNECTED = 2 # CIPSTATUS method + STATUS_APCONNECTED = 2 # CIPSTATUS method STATUS_WIFI_APCONNECTED = 2 # CWSTATE method - STATUS_SOCKETOPEN = 3 # CIPSTATUS method - STATUS_SOCKET_OPEN = 3 # CIPSTATE method + STATUS_SOCKETOPEN = 3 # CIPSTATUS method + STATUS_SOCKET_OPEN = 3 # CIPSTATE method - STATUS_SOCKETCLOSED = 4 # CIPSTATUS method - STATUS_SOCKET_CLOSED = 4 # CIPSTATE method + STATUS_SOCKETCLOSED = 4 # CIPSTATUS method + STATUS_SOCKET_CLOSED = 4 # CIPSTATE method - STATUS_NOTCONNECTED = 5 # CIPSTATUS method + STATUS_NOTCONNECTED = 5 # CIPSTATUS method STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method @@ -89,7 +89,7 @@ def __init__( rts_pin: Optional[DigitalInOut] = None, reset_pin: Optional[DigitalInOut] = None, debug: bool = False, - use_cipstatus: bool= False + use_cipstatus: bool = False, ): """This function doesn't try to do any sync'ing, just sets up # the hardware, that way nothing can unexpectedly fail!""" @@ -147,7 +147,7 @@ def begin(self) -> None: self.at_response("AT+CWSTATE?", retries=1, timeout=3) except OKError: # ESP8285's use CIPSTATUS and have no CWSTATE or CWIPSTATUS functions - self._use_cipstatus=True + self._use_cipstatus = True if self._debug: print("No CWSTATE support, using CIPSTATUS, it's ok!") @@ -194,8 +194,8 @@ def connect_enterprise( self, secrets: Dict[str, Union[str, int]], timeout: int = 15, retries: int = 3 ) -> None: """Repeatedly try to connect to an enterprise access point with the details in - the passed in 'secrets' dictionary. Be sure 'ssid','password','username','identity' - and 'method' are defined in the secrets dict! If 'timezone' is set, we'll also + the passed in 'secrets' dictionary. Be sure 'ssid','password','username','identity' + and 'method' are defined in the secrets dict! If 'timezone' is set, we'll also configure SNTP""" # Connect to WiFi if not already retries = 3 @@ -212,20 +212,22 @@ def connect_enterprise( if AP[0] != secrets["ssid"]: if self._debug: print("Doing Enterprise connection sequence") - self.join_AP_Enterprise(secrets["ssid"], - secrets["username"], - secrets["identity"], - secrets["password"], - secrets["method"], - timeout=timeout, - retries=retries,) + self.join_AP_Enterprise( + secrets["ssid"], + secrets["username"], + secrets["identity"], + secrets["password"], + secrets["method"], + timeout=timeout, + retries=retries, + ) if "timezone" in secrets: tzone = secrets["timezone"] ntp = None if "ntp_server" in secrets: ntp = secrets["ntp_server"] self.sntp_config(True, tzone, ntp) - print("Connected to",self.remote_AP[0]) + print("Connected to", self.remote_AP[0]) print("My IP Address:", self.local_ip) return # yay! except (RuntimeError, OKError) as exp: @@ -233,15 +235,13 @@ def connect_enterprise( retries -= 1 continue - def set_autoconnect(self, autoconnect: bool) -> None: """Set the auto connection status if the wifi connects automatically on powerup""" if autoconnect is True: auto_flag = "1" else: auto_flag = "0" - self.at_response("AT+CWAUTOCONN="+auto_flag) - + self.at_response("AT+CWAUTOCONN=" + auto_flag) # *************************** SOCKET SETUP **************************** @@ -261,7 +261,7 @@ def socket_connect( remote_port: int, *, keepalive: int = 10, - retries: int = 1 + retries: int = 1, ) -> bool: """Open a socket. conntype can be TYPE_TCP, TYPE_UDP, or TYPE_SSL. Remote can be an IP address or DNS (we'll do the lookup for you. Remote port @@ -271,7 +271,7 @@ def socket_connect( # always disconnect for TYPE_UDP self.socket_disconnect() while True: - if self._use_cipstatus: + if self._use_cipstatus: stat = self.status if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): break @@ -282,8 +282,11 @@ def socket_connect( else: stat_wifi = self.status_wifi stat_socket = self.status_socket - if stat_wifi == self.STATUS_WIFI_APCONNECTED or stat_socket == self.STATUS_SOCKET_CLOSED: - break + if ( + stat_wifi == self.STATUS_WIFI_APCONNECTED + or stat_socket == self.STATUS_SOCKET_CLOSED + ): + break if stat_socket == self.STATUS_SOCKET_OPEN: self.socket_disconnect() else: @@ -307,14 +310,16 @@ def socket_connect( if reply == b"CONNECT" and ( (conntype == self.TYPE_TCP or conntype == self.TYPE_SSL) and ( - (not self._use_cipstatus and self.status_socket == self.STATUS_SOCKET_OPEN) - or - (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) + ( + not self._use_cipstatus + and self.status_socket == self.STATUS_SOCKET_OPEN + ) + or (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) ) or conntype == self.TYPE_UDP ): self._conntype = conntype - return True + return True return False @@ -476,8 +481,11 @@ def is_connected(self) -> bool: if self._debug: print("is_connected(): status says connected") return True - else: - if self.status_wifi == self.STATUS_WIFI_APCONNECTED or self.status_socket == self.STATUS_SOCKET_OPEN: + else: + if ( + self.status_wifi == self.STATUS_WIFI_APCONNECTED + or self.status_socket == self.STATUS_SOCKET_OPEN + ): if self._debug: print("is_connected(): status says connected") return True @@ -491,13 +499,13 @@ def is_connected(self) -> bool: def status(self) -> Union[int, None]: """The IP connection status number (see AT+CIPSTATUS datasheet for meaning)""" # Note that CIPSTATUS, at least in the esp32-c3 version of espressif AT firmware - # is considered deprecated and you should use AT+CWSTATE for wifi state - # and AT+CIPSTATE for socket connection statuses. + # is considered deprecated and you should use AT+CWSTATE for wifi state + # and AT+CIPSTATE for socket connection statuses. # This muddies things with regards to how the original version of this routine # ran the status responses since wifi + socket were mixed, so this has been broken # out in to status_wifi() and status_socket() with their own constants for status # This is here for legacy reasons like the the ESP8285 version of the espressif - # AT commands which is on the Challenger RP2040 Wifi feather. + # AT commands which is on the Challenger RP2040 Wifi feather. replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"STATUS:"): @@ -510,25 +518,27 @@ def status(self) -> Union[int, None]: def status_wifi(self) -> Union[int, None]: """The WIFI connection status number (see AT+CWSTATE datasheet for meaning)""" # Note that as of 2022-Nov CIPSTATUS is deprecated and replaced with CWSTATE and CIPSTATE - # and the CWSTATE codes are different than the old CIPSTATUS codes. - # CWSTATE: + # and the CWSTATE codes are different than the old CIPSTATUS codes. + # CWSTATE: # : current Wi-Fi state. # 0: ESP32-C3 station has not started any Wi-Fi connection. # 1: ESP32-C3 station has connected to an AP, but does not get an IPv4 address yet. # 2: ESP32-C3 station has connected to an AP, and got an IPv4 address. # 3: ESP32-C3 station is in Wi-Fi connecting or reconnecting state. # 4: ESP32-C3 station is in Wi-Fi disconnected state. - # <”ssid”>: the SSID of the target AP. + # <”ssid”>: the SSID of the target AP. replies = self.at_response("AT+CWSTATE?", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"+CWSTATE:"): state_info = reply.split(b",") if self._debug: - print(f"State reply is {reply}, state_info[1] is {int(state_info[0][9:10])}") + print( + f"State reply is {reply}, state_info[1] is {int(state_info[0][9:10])}" + ) return int(state_info[0][9:10]) return None - @property + @property def status_socket(self) -> Union[int, None]: """The Socket connection status number (see AT+CIPSTATE for meaning)""" # +CIPSTATE:,<"type">,<"remote IP">,,, @@ -537,7 +547,8 @@ def status_socket(self) -> Union[int, None]: # OK # Parameters # : ID of the connection (0~4), used for multiple connections. - # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, “UDP”, “UDPv6”, “SSL”, or “SSLv6”. + # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, + # “UDP”, “UDPv6”, “SSL”, or “SSLv6”. # <”remote IP”>: string parameter showing the remote IPv4 address or IPv6 address. # : the remote port number. # : the local port number. @@ -656,13 +667,19 @@ def join_AP( # pylint: disable=invalid-name return def join_AP_Enterprise( - self, ssid: str, username: str, identity: str, password: str, method: int, - timeout: int = 30, retries: int = 3 + self, + ssid: str, + username: str, + identity: str, + password: str, + method: int, + timeout: int = 30, + retries: int = 3, ) -> None: # pylint: disable=invalid-name """Try to join an Enterprise access point by name and password, will return immediately if we're already connected and won't try to reconnect""" - # Not sure how to verify certificates so we set that to not verify. - certificate_security = 0 # Bit0: Client certificate.Bit1: Server certificate. + # Not sure how to verify certificates so we set that to not verify. + certificate_security = 0 # Bit0: Client certificate.Bit1: Server certificate. # First make sure we're in 'station' mode so we can connect to AP's if self._debug: @@ -676,10 +693,20 @@ def join_AP_Enterprise( reply = self.at_response( # from https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-jeap # AT+CWJEAP=,,,,,[,] - 'AT+CWJEAP="' - + ssid + '",' + str(method) + ',"' + identity + '","' - + username + '","' + password + '",' + str(certificate_security), - timeout=timeout, retries=retries + 'AT+CWJEAP="' + + ssid + + '",' + + str(method) + + ',"' + + identity + + '","' + + username + + '","' + + password + + '",' + + str(certificate_security), + timeout=timeout, + retries=retries, ) if b"WIFI CONNECTED" not in reply: print("no CONNECTED") @@ -689,39 +716,41 @@ def join_AP_Enterprise( raise RuntimeError("Didn't get IP address") return - def disconnect(self, timeout: int = 5, retries: int = 3 ): + def disconnect(self, timeout: int = 5, retries: int = 3): """Disconnect from the AP. Tries whether connected or not.""" # If we're not connected we likely don't get a "WIFI DISCONNECT" and just get the OK - # Note it still tries to disconnect even if it says we're not connected. + # Note it still tries to disconnect even if it says we're not connected. if not self._initialized: self.begin() - if self._use_cipstatus: + if self._use_cipstatus: stat = self.status if stat in ( self.STATUS_APCONNECTED, self.STATUS_SOCKETOPEN, self.STATUS_SOCKETCLOSED, ): - wait_for_disconnect = True + wait_for_disconnect = True else: wait_for_disconnect = False if self._debug is True: - print("disconnect(): Not connected, not waiting for disconnect message") + print( + "disconnect(): Not connected, not waiting for disconnect message" + ) else: if self.status_wifi == self.STATUS_WIFI_APCONNECTED: wait_for_disconnect = True else: - wait_for_disconnect = False + wait_for_disconnect = False if self._debug is True: - print("disconnect(): Not connected, not waiting for disconnect message") - reply = self.at_response( - 'AT+CWQAP', timeout=timeout, retries=retries - ) + print( + "disconnect(): Not connected, not waiting for disconnect message" + ) + reply = self.at_response("AT+CWQAP", timeout=timeout, retries=retries) # Don't bother waiting for disconnect message if we weren't connected already # sometimes the "WIFI DISCONNECT" shows up in the reply and sometimes it doesn't. if wait_for_disconnect is True: - if b'WIFI DISCONNECT' in reply: - if self._debug is True: + if b"WIFI DISCONNECT" in reply: + if self._debug is True: print(f"disconnect(): Got WIFI DISCONNECT: {reply}") else: stamp = time.monotonic() @@ -737,11 +766,12 @@ def disconnect(self, timeout: int = 5, retries: int = 3 ): if self._debug: if response[-15:] == b"WIFI DISCONNECT": print(f"disconnect(): Got WIFI DISCONNECT: {response}") - else: - print(f"disconnect(): Timed out wating for WIFI DISCONNECT: {response}") + else: + print( + f"disconnect(): Timed out wating for WIFI DISCONNECT: {response}" + ) return - def scan_APs( # pylint: disable=invalid-name self, retries: int = 3 ) -> Union[List[List[bytes]], None]: @@ -914,7 +944,7 @@ def soft_reset(self, timeout: int = 5) -> bool: if self._debug: if response[-5:] == b"ready": print(f"soft_reset(): Got ready: {response}") - else: + else: print(f"soft_reset(): imed out waiting for ready: {response}") self._uart.reset_input_buffer() self.sync() diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index 26c1566..ff4bab3 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -35,8 +35,8 @@ def __init__( secrets: Dict[str, Union[str, int]], status_pixel: Optional[FillBasedLED] = None, attempts: int = 2, - enterprise: bool=False, - debug: bool=False, + enterprise: bool = False, + debug: bool = False, ): """ :param ESP_SPIcontrol esp: The ESP object we are using @@ -59,7 +59,7 @@ def reset(self, hard_reset: bool = True, soft_reset: bool = False) -> None: """ Perform a hard reset on the ESP """ - self.pixel_status((100,100,100)) + self.pixel_status((100, 100, 100)) if self.debug: print("Resetting ESP") if hard_reset is True: @@ -76,10 +76,12 @@ def connect(self, timeout: int = 15, retries: int = 3) -> None: if self.debug: print("Connecting to AP...") self.pixel_status((100, 0, 0)) - if self.enterprise is False: + if self.enterprise is False: self._esp.connect(self.secrets, timeout=timeout, retries=retries) else: - self._esp.connect_enterprise(self.secrets, timeout=timeout, retries=retries) + self._esp.connect_enterprise( + self.secrets, timeout=timeout, retries=retries + ) self.pixel_status((0, 100, 0)) except (ValueError, RuntimeError) as error: print("Failed to connect\n", error) @@ -130,7 +132,7 @@ def post(self, url: str, **kw: Any) -> requests.Response: print("post(): not connected, trying to connect") self.connect() self.pixel_status((0, 0, 100)) - requests.set_socket(socket, self._esp) + requests.set_socket(socket, self._esp) return_val = requests.post(url, **kw) self.pixel_status(0) @@ -151,7 +153,7 @@ def put(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) - requests.set_socket(socket, self._esp) + requests.set_socket(socket, self._esp) return_val = requests.put(url, **kw) self.pixel_status(0) return return_val @@ -171,7 +173,7 @@ def patch(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) - requests.set_socket(socket, self._esp) + requests.set_socket(socket, self._esp) return_val = requests.patch(url, **kw) self.pixel_status(0) return return_val @@ -191,7 +193,7 @@ def delete(self, url: str, **kw: Any) -> requests.Response: if not self._esp.is_connected: self.connect() self.pixel_status((0, 0, 100)) - requests.set_socket(socket, self._esp) + requests.set_socket(socket, self._esp) return_val = requests.delete(url, **kw) self.pixel_status(0) return return_val From 1c5709af2fabfb80a139e01e71bfec9dd0616948 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 28 Oct 2022 11:50:54 -0300 Subject: [PATCH 10/33] emulating CIPSTATUS responses with CWSTATE --- .../adafruit_espatcontrol.py | 124 +++++++++++++++--- 1 file changed, 105 insertions(+), 19 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 2389481..c1cd704 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -118,6 +118,7 @@ def __init__( self._conntype = None self._use_cipstatus = use_cipstatus + self._force_status = True def begin(self) -> None: """Initialize the module by syncing, resetting if necessary, setting up @@ -212,7 +213,7 @@ def connect_enterprise( if AP[0] != secrets["ssid"]: if self._debug: print("Doing Enterprise connection sequence") - self.join_AP_Enterprise( + self.join_APEnterprise( secrets["ssid"], secrets["username"], secrets["identity"], @@ -271,7 +272,7 @@ def socket_connect( # always disconnect for TYPE_UDP self.socket_disconnect() while True: - if self._use_cipstatus: + if self._use_cipstatus or self._force_status: stat = self.status if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): break @@ -309,13 +310,14 @@ def socket_connect( for reply in replies: if reply == b"CONNECT" and ( (conntype == self.TYPE_TCP or conntype == self.TYPE_SSL) - and ( - ( - not self._use_cipstatus - and self.status_socket == self.STATUS_SOCKET_OPEN - ) - or (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) - ) + and self.status == self.STATUS_SOCKETOPEN + # and ( + # ( + # not self._use_cipstatus + # and self.status_socket == self.STATUS_SOCKET_OPEN + # ) + # or (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) + # ) or conntype == self.TYPE_UDP ): self._conntype = conntype @@ -471,7 +473,7 @@ def is_connected(self) -> bool: try: self.echo(False) self.baudrate = self.baudrate - if self._use_cipstatus: + if self._use_cipstatus or self._force_status: stat = self.status if stat in ( self.STATUS_APCONNECTED, @@ -506,12 +508,96 @@ def status(self) -> Union[int, None]: # out in to status_wifi() and status_socket() with their own constants for status # This is here for legacy reasons like the the ESP8285 version of the espressif # AT commands which is on the Challenger RP2040 Wifi feather. - replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") - for reply in replies: - if reply.startswith(b"STATUS:"): + # CIPSTATUS status messages: + #: status of the ESP32-C3 station interface. + # 0: The ESP32-C3 station is not initialized. + # 1: The ESP32-C3 station is initialized, but not started a Wi-Fi connection yet. + # 2: The ESP32-C3 station is connected to an AP and its IP address is obtained. + # 3: The ESP32-C3 station has created a TCP/SSL transmission. + # 4: All of the TCP/UDP/SSL connections of the ESP32-C3 station are disconnected. + # 5: The ESP32-C3 station started a Wi-Fi connection, but was not connected to an AP or disconnected from an AP. + # STATUS_APCONNECTED = 2 # CIPSTATUS method + # STATUS_WIFI_APCONNECTED = 2 # CWSTATE method + + # STATUS_SOCKETOPEN = 3 # CIPSTATUS method + # STATUS_SOCKET_OPEN = 3 # CIPSTATE method + + # STATUS_SOCKETCLOSED = 4 # CIPSTATUS method + # STATUS_SOCKET_CLOSED = 4 # CIPSTATE method + + # STATUS_NOTCONNECTED = 5 # CIPSTATUS method + # STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method + # STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method + # CIPSTATUS without magic codes: + # 0 + # 1 + + # STATUS: CWSTATE: 2, CIPSTATUS: 4, CIPSTATE: 4 + # STATUS returning 2 + + if self._use_cipstatus: + replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") + for reply in replies: + if reply.startswith(b"STATUS:"): + if self._debug: + print(f"CIPSTATUS state is {int(reply[7:8])}") + return int(reply[7:8]) + else: + status_w = self.status_wifi + status_s = self.status_socket + + # Check CIPSTATUS messages against CWSTATE/CIPSTATE + if self._debug: + replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") + for reply in replies: + if reply.startswith(b"STATUS:"): + cipstatus = int(reply[7:8]) + print(f"STATUS: CWSTATE: {status_w}, CIPSTATUS: {cipstatus}, CIPSTATE: {status_s}") + + # Produce a cipstatus-compatible status code + if status_w in (self.STATUS_WIFI_NOTCONNECTED, self.STATUS_WIFI_DISCONNECTED): + if self._debug: + print(f"STATUS returning {self.STATUS_NOTCONNECTED}") + return self.STATUS_NOTCONNECTED + + if status_s == self.STATUS_SOCKET_OPEN: if self._debug: - print(f"CIPSTATUS state is {int(reply[7:8])}") - return int(reply[7:8]) + print(f"STATUS returning {self.STATUS_SOCKETOPEN}") + return self.STATUS_SOCKETOPEN + + # Sometimes you get a CIPSTATUS=4 when CWSTATE=2/CIPSTATE=4 and sometimes you + # get CIPSTATUS=2 when CWSTATE=2/CIPSTATE=4 + # if status_w == self.STATUS_WIFI_APCONNECTED and status_s == self.STATUS_SOCKET_CLOSED: + # if self._debug: + # print(f"STATUS returning {self.STATUS_SOCKETCLOSED}") + # return self.STATUS_SOCKETCLOSED + + if status_w == self.STATUS_WIFI_APCONNECTED: + if self._debug: + print(f"STATUS returning {self.STATUS_APCONNECTED}") + return self.STATUS_APCONNECTED + + # handle extra codes from CWSTATE + if status_w == 0: # station has not started any Wi-Fi connection. + if self._debug: + print("STATUS returning 1") + return 1 + + if status_w == 1: # station has connected to an AP, but does not get an IPv4 address yet. + if self._debug: + print("STATUS returning 1") + return 1 + + if status_w == 3: # station is in Wi-Fi connecting or reconnecting state. + if self._debug: + print(f"STATUS returning {self.STATUS_NOTCONNECTED}") + return self.STATUS_NOTCONNECTED + + if status_s == self.STATUS_SOCKET_CLOSED: + if self._debug: + print(f"STATUS returning {self.STATUS_SOCKET_CLOSED}") + return self.STATUS_SOCKET_CLOSED + return None @property @@ -617,7 +703,7 @@ def nslookup(self, host: str) -> Union[str, None]: @property def remote_AP(self) -> List[Union[int, str, None]]: # pylint: disable=invalid-name """The name of the access point we're connected to, as a string""" - if self._use_cipstatus: + if self._use_cipstatus or self._force_status: stat = self.status if stat != self.STATUS_APCONNECTED: return [None] * 4 @@ -666,7 +752,7 @@ def join_AP( # pylint: disable=invalid-name raise RuntimeError("Didn't get IP address") return - def join_AP_Enterprise( + def join_APEnterprise( self, ssid: str, username: str, @@ -683,7 +769,7 @@ def join_AP_Enterprise( # First make sure we're in 'station' mode so we can connect to AP's if self._debug: - print("In join_AP_Enterprise()") + print("In join_APEnterprise()") if self.mode != self.MODE_STATION: self.mode = self.MODE_STATION @@ -722,7 +808,7 @@ def disconnect(self, timeout: int = 5, retries: int = 3): # Note it still tries to disconnect even if it says we're not connected. if not self._initialized: self.begin() - if self._use_cipstatus: + if self._use_cipstatus or self._force_status: stat = self.status if stat in ( self.STATUS_APCONNECTED, From 5a0cdbe826f739b1200ab3d258451538b5ae29d8 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 28 Oct 2022 13:03:17 -0300 Subject: [PATCH 11/33] cleanup of status function changes --- .../adafruit_espatcontrol.py | 138 ++++++------------ 1 file changed, 47 insertions(+), 91 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index c1cd704..2c70019 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -118,7 +118,6 @@ def __init__( self._conntype = None self._use_cipstatus = use_cipstatus - self._force_status = True def begin(self) -> None: """Initialize the module by syncing, resetting if necessary, setting up @@ -213,7 +212,7 @@ def connect_enterprise( if AP[0] != secrets["ssid"]: if self._debug: print("Doing Enterprise connection sequence") - self.join_APEnterprise( + self.join_AP_Enterprise( secrets["ssid"], secrets["username"], secrets["identity"], @@ -272,26 +271,13 @@ def socket_connect( # always disconnect for TYPE_UDP self.socket_disconnect() while True: - if self._use_cipstatus or self._force_status: - stat = self.status - if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): - break - if stat == self.STATUS_SOCKETOPEN: - self.socket_disconnect() - else: - time.sleep(1) + stat = self.status + if stat in (self.STATUS_APCONNECTED, self.STATUS_SOCKETCLOSED): + break + if stat == self.STATUS_SOCKETOPEN: + self.socket_disconnect() else: - stat_wifi = self.status_wifi - stat_socket = self.status_socket - if ( - stat_wifi == self.STATUS_WIFI_APCONNECTED - or stat_socket == self.STATUS_SOCKET_CLOSED - ): - break - if stat_socket == self.STATUS_SOCKET_OPEN: - self.socket_disconnect() - else: - time.sleep(1) + time.sleep(1) if not conntype in (self.TYPE_TCP, self.TYPE_UDP, self.TYPE_SSL): raise RuntimeError("Connection type must be TCP, UDL or SSL") cmd = ( @@ -311,13 +297,6 @@ def socket_connect( if reply == b"CONNECT" and ( (conntype == self.TYPE_TCP or conntype == self.TYPE_SSL) and self.status == self.STATUS_SOCKETOPEN - # and ( - # ( - # not self._use_cipstatus - # and self.status_socket == self.STATUS_SOCKET_OPEN - # ) - # or (self._use_cipstatus and self.status == self.STATUS_SOCKETOPEN) - # ) or conntype == self.TYPE_UDP ): self._conntype = conntype @@ -473,24 +452,15 @@ def is_connected(self) -> bool: try: self.echo(False) self.baudrate = self.baudrate - if self._use_cipstatus or self._force_status: - stat = self.status - if stat in ( - self.STATUS_APCONNECTED, - self.STATUS_SOCKETOPEN, - self.STATUS_SOCKETCLOSED, - ): - if self._debug: - print("is_connected(): status says connected") - return True - else: - if ( - self.status_wifi == self.STATUS_WIFI_APCONNECTED - or self.status_socket == self.STATUS_SOCKET_OPEN - ): - if self._debug: - print("is_connected(): status says connected") - return True + stat = self.status + if stat in ( + self.STATUS_APCONNECTED, + self.STATUS_SOCKETOPEN, + self.STATUS_SOCKETCLOSED, + ): + if self._debug: + print("is_connected(): status says connected") + return True except (OKError, RuntimeError): pass if self._debug: @@ -508,14 +478,15 @@ def status(self) -> Union[int, None]: # out in to status_wifi() and status_socket() with their own constants for status # This is here for legacy reasons like the the ESP8285 version of the espressif # AT commands which is on the Challenger RP2040 Wifi feather. - # CIPSTATUS status messages: + # CIPSTATUS status messages: #: status of the ESP32-C3 station interface. # 0: The ESP32-C3 station is not initialized. # 1: The ESP32-C3 station is initialized, but not started a Wi-Fi connection yet. # 2: The ESP32-C3 station is connected to an AP and its IP address is obtained. # 3: The ESP32-C3 station has created a TCP/SSL transmission. # 4: All of the TCP/UDP/SSL connections of the ESP32-C3 station are disconnected. - # 5: The ESP32-C3 station started a Wi-Fi connection, but was not connected to an AP or disconnected from an AP. + # 5: The ESP32-C3 station started a Wi-Fi connection, but was not connected + # to an AP or disconnected from an AP. # STATUS_APCONNECTED = 2 # CIPSTATUS method # STATUS_WIFI_APCONNECTED = 2 # CWSTATE method @@ -528,14 +499,11 @@ def status(self) -> Union[int, None]: # STATUS_NOTCONNECTED = 5 # CIPSTATUS method # STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method # STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method - # CIPSTATUS without magic codes: + # CIPSTATUS responses without magic codes: # 0 - # 1 + # 1 - # STATUS: CWSTATE: 2, CIPSTATUS: 4, CIPSTATE: 4 - # STATUS returning 2 - - if self._use_cipstatus: + if self._use_cipstatus: replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"STATUS:"): @@ -553,7 +521,7 @@ def status(self) -> Union[int, None]: if reply.startswith(b"STATUS:"): cipstatus = int(reply[7:8]) print(f"STATUS: CWSTATE: {status_w}, CIPSTATUS: {cipstatus}, CIPSTATE: {status_s}") - + # Produce a cipstatus-compatible status code if status_w in (self.STATUS_WIFI_NOTCONNECTED, self.STATUS_WIFI_DISCONNECTED): if self._debug: @@ -566,7 +534,7 @@ def status(self) -> Union[int, None]: return self.STATUS_SOCKETOPEN # Sometimes you get a CIPSTATUS=4 when CWSTATE=2/CIPSTATE=4 and sometimes you - # get CIPSTATUS=2 when CWSTATE=2/CIPSTATE=4 + # get CIPSTATUS=2 when CWSTATE=2/CIPSTATE=4 # if status_w == self.STATUS_WIFI_APCONNECTED and status_s == self.STATUS_SOCKET_CLOSED: # if self._debug: # print(f"STATUS returning {self.STATUS_SOCKETCLOSED}") @@ -583,6 +551,7 @@ def status(self) -> Union[int, None]: print("STATUS returning 1") return 1 + # pylint: disable=line-too-long if status_w == 1: # station has connected to an AP, but does not get an IPv4 address yet. if self._debug: print("STATUS returning 1") @@ -633,7 +602,7 @@ def status_socket(self) -> Union[int, None]: # OK # Parameters # : ID of the connection (0~4), used for multiple connections. - # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, + # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, # “UDP”, “UDPv6”, “SSL”, or “SSLv6”. # <”remote IP”>: string parameter showing the remote IPv4 address or IPv6 address. # : the remote port number. @@ -703,14 +672,9 @@ def nslookup(self, host: str) -> Union[str, None]: @property def remote_AP(self) -> List[Union[int, str, None]]: # pylint: disable=invalid-name """The name of the access point we're connected to, as a string""" - if self._use_cipstatus or self._force_status: - stat = self.status - if stat != self.STATUS_APCONNECTED: - return [None] * 4 - else: - stat = self.status_wifi - if stat != self.STATUS_WIFI_APCONNECTED: - return [None] * 4 + stat = self.status + if stat != self.STATUS_APCONNECTED: + return [None] * 4 replies = self.at_response("AT+CWJAP?", timeout=10).split(b"\r\n") for reply in replies: if not reply.startswith("+CWJAP:"): @@ -752,7 +716,8 @@ def join_AP( # pylint: disable=invalid-name raise RuntimeError("Didn't get IP address") return - def join_APEnterprise( + # pylint: disable=invalid-name + def join_AP_Enterprise( self, ssid: str, username: str, @@ -761,7 +726,7 @@ def join_APEnterprise( method: int, timeout: int = 30, retries: int = 3, - ) -> None: # pylint: disable=invalid-name + ) -> None: """Try to join an Enterprise access point by name and password, will return immediately if we're already connected and won't try to reconnect""" # Not sure how to verify certificates so we set that to not verify. @@ -769,7 +734,7 @@ def join_APEnterprise( # First make sure we're in 'station' mode so we can connect to AP's if self._debug: - print("In join_APEnterprise()") + print("In join_AP_Enterprise()") if self.mode != self.MODE_STATION: self.mode = self.MODE_STATION @@ -777,7 +742,8 @@ def join_APEnterprise( if router and router[0] == ssid: return # we're already connected! reply = self.at_response( - # from https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-jeap + # from https://docs.espressif.com/projects/esp-at/en/latest/ + # esp32c3/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-jeap # AT+CWJEAP=,,,,,[,] 'AT+CWJEAP="' + ssid @@ -808,29 +774,19 @@ def disconnect(self, timeout: int = 5, retries: int = 3): # Note it still tries to disconnect even if it says we're not connected. if not self._initialized: self.begin() - if self._use_cipstatus or self._force_status: - stat = self.status - if stat in ( - self.STATUS_APCONNECTED, - self.STATUS_SOCKETOPEN, - self.STATUS_SOCKETCLOSED, - ): - wait_for_disconnect = True - else: - wait_for_disconnect = False - if self._debug is True: - print( - "disconnect(): Not connected, not waiting for disconnect message" - ) + stat = self.status + if stat in ( + self.STATUS_APCONNECTED, + self.STATUS_SOCKETOPEN, + self.STATUS_SOCKETCLOSED, + ): + wait_for_disconnect = True else: - if self.status_wifi == self.STATUS_WIFI_APCONNECTED: - wait_for_disconnect = True - else: - wait_for_disconnect = False - if self._debug is True: - print( - "disconnect(): Not connected, not waiting for disconnect message" - ) + wait_for_disconnect = False + if self._debug is True: + print( + "disconnect(): Not connected, not waiting for disconnect message" + ) reply = self.at_response("AT+CWQAP", timeout=timeout, retries=retries) # Don't bother waiting for disconnect message if we weren't connected already # sometimes the "WIFI DISCONNECT" shows up in the reply and sometimes it doesn't. From 56756422369d00bf0d3be4da85a93fa5b6be3245 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 28 Oct 2022 13:10:07 -0300 Subject: [PATCH 12/33] remove README changes --- README.rst | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/README.rst b/README.rst index b6536de..489a996 100644 --- a/README.rst +++ b/README.rst @@ -1,28 +1,3 @@ -This is a fork of the Adafruit_CircuitPython_ESP_ATcontrol library with changes: - -* WPA Enterprise WPA-PEAP support in espatcontrol and wifimanager (must be compiled in your esp AT firmware) -* Moving from deprecated AT+CIPSTATUS to AT+CWSTATE for wifi status and AT+CIPSTATE for socket status. - See https://docs.espressif.com/projects/esp-at/en/latest/esp32c3/AT_Command_Set/TCP-IP_AT_Commands.html#cmd-status - This will fall back to using CIPSTATUS if CWSTATE isn't available (ie AT command set on the ESP8285 - that's on the Challenger RP2040 Wifi which can only use up to firmware 2.2) -* Fixes for reconnection issues with adafruit_requests and sockets in the wifimanager -* Changed handling for soft_reset(). Added soft_reset() - as an option in lots of places hard_reset() is being used. - For boards that don't have a hardware reset line - (e.g. - a repurposed Adafruit QT Py ESP32-C3 I tested this with https://www.adafruit.com/product/5405) -* Added function to disconnect wifi -* Added function to change autoreconnect status -* Bug fixes for SSL connection replies not being accounted for in socket_connect() - -My test module is an Adafruit Qt Py ESP32-C3 connected via UART to an Adafruit Feather M4 Express -Using circuitpython 7.3.3 and compiled espressif AT command firmware for the ESP32-C3. I haven't made any -code changes to the espressif AT firmware other than to enable WPA enterprise support in menuconfig -and recompile/flash the firmware onto the ESP32-C3. "Your mileage may vary" - -I've provided some updated examples in ``fork-examples`` - -Original README follows: - Introduction ============ From 3934aac4109b375dda62f811285f564f9430df19 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 28 Oct 2022 13:24:43 -0300 Subject: [PATCH 13/33] comments reflect current state of status function --- .../adafruit_espatcontrol.py | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 2c70019..5024385 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -473,11 +473,13 @@ def status(self) -> Union[int, None]: # Note that CIPSTATUS, at least in the esp32-c3 version of espressif AT firmware # is considered deprecated and you should use AT+CWSTATE for wifi state # and AT+CIPSTATE for socket connection statuses. - # This muddies things with regards to how the original version of this routine - # ran the status responses since wifi + socket were mixed, so this has been broken - # out in to status_wifi() and status_socket() with their own constants for status - # This is here for legacy reasons like the the ESP8285 version of the espressif - # AT commands which is on the Challenger RP2040 Wifi feather. + # + # if CWSTATE/CIPSTATE are available, this function uses those and generates + # a return code compatible with CIPSTATUS. For more fine grain control + # you can use status_wifi and status_socket + # if CWSTATE/CIPSTATE are not available, this falls back to using CIPSTATUS + # (e.g. - ILabs Challenger RP2040 Wifi which has an onboard ESP8285 with older + # firmware) # CIPSTATUS status messages: #: status of the ESP32-C3 station interface. # 0: The ESP32-C3 station is not initialized. @@ -487,21 +489,6 @@ def status(self) -> Union[int, None]: # 4: All of the TCP/UDP/SSL connections of the ESP32-C3 station are disconnected. # 5: The ESP32-C3 station started a Wi-Fi connection, but was not connected # to an AP or disconnected from an AP. - # STATUS_APCONNECTED = 2 # CIPSTATUS method - # STATUS_WIFI_APCONNECTED = 2 # CWSTATE method - - # STATUS_SOCKETOPEN = 3 # CIPSTATUS method - # STATUS_SOCKET_OPEN = 3 # CIPSTATE method - - # STATUS_SOCKETCLOSED = 4 # CIPSTATUS method - # STATUS_SOCKET_CLOSED = 4 # CIPSTATE method - - # STATUS_NOTCONNECTED = 5 # CIPSTATUS method - # STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method - # STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method - # CIPSTATUS responses without magic codes: - # 0 - # 1 if self._use_cipstatus: replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") @@ -514,7 +501,7 @@ def status(self) -> Union[int, None]: status_w = self.status_wifi status_s = self.status_socket - # Check CIPSTATUS messages against CWSTATE/CIPSTATE + # debug only, Check CIPSTATUS messages against CWSTATE/CIPSTATE if self._debug: replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: @@ -523,6 +510,8 @@ def status(self) -> Union[int, None]: print(f"STATUS: CWSTATE: {status_w}, CIPSTATUS: {cipstatus}, CIPSTATE: {status_s}") # Produce a cipstatus-compatible status code + # Codes are not the same between CWSTATE/CIPSTATUS so in some combinations + # we just pick what we hope is best. if status_w in (self.STATUS_WIFI_NOTCONNECTED, self.STATUS_WIFI_DISCONNECTED): if self._debug: print(f"STATUS returning {self.STATUS_NOTCONNECTED}") @@ -549,13 +538,13 @@ def status(self) -> Union[int, None]: if status_w == 0: # station has not started any Wi-Fi connection. if self._debug: print("STATUS returning 1") - return 1 + return 1 # this cipstatus had no previous handler variable # pylint: disable=line-too-long if status_w == 1: # station has connected to an AP, but does not get an IPv4 address yet. if self._debug: print("STATUS returning 1") - return 1 + return 1 # this cipstatus had no previous handler variable if status_w == 3: # station is in Wi-Fi connecting or reconnecting state. if self._debug: From 69ecf36594d6767b0e958ec7da2f8e2a3227288b Mon Sep 17 00:00:00 2001 From: scogswell Date: Thu, 3 Nov 2022 12:49:33 -0300 Subject: [PATCH 14/33] fixes from merge with main --- adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index 37aeafb..babe644 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -117,6 +117,7 @@ def get(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) + requests.set_socket(socket, self._esp) return_val = requests.get(url, **kw) self.pixel_status(0) return return_val @@ -141,6 +142,7 @@ def post(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) + requests.set_socket(socket, self._esp) return_val = requests.post(url, **kw) self.pixel_status(0) @@ -162,6 +164,7 @@ def put(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) + requests.set_socket(socket, self._esp) return_val = requests.put(url, **kw) self.pixel_status(0) return return_val @@ -182,6 +185,7 @@ def patch(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) + requests.set_socket(socket, self._esp) return_val = requests.patch(url, **kw) self.pixel_status(0) return return_val @@ -202,6 +206,7 @@ def delete(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) + requests.set_socket(socket, self._esp) return_val = requests.delete(url, **kw) self.pixel_status(0) return return_val From d9f79927683be9f4fef26c547d059ddc1c1c57d0 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:02:49 -0400 Subject: [PATCH 15/33] Switching to composite actions --- .github/workflows/build.yml | 67 +---------------------- .github/workflows/release.yml | 88 ------------------------------ .github/workflows/release_gh.yml | 14 +++++ .github/workflows/release_pypi.yml | 14 +++++ 4 files changed, 30 insertions(+), 153 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/release_gh.yml create mode 100644 .github/workflows/release_pypi.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb2f60e..041a337 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,68 +10,5 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install dependencies - # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) - run: | - source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit - run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - - name: Library version - run: git describe --dirty --always --tags - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 - - name: Pre-commit hooks - run: | - pre-commit run --all-files - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Archive bundles - uses: actions/upload-artifact@v2 - with: - name: bundles - path: ${{ github.workspace }}/bundles/ - - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Build Python package - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - pip install --upgrade build twine - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/1.2.3/" $file; - done; - python -m build - twine check dist/* + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f3a0325..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -name: Release Actions - -on: - release: - types: [published] - -jobs: - upload-release-assets: - runs-on: ubuntu-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install deps - run: | - source actions-ci/install.sh - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Upload Release Assets - # the 'official' actions version does not yet support dynamically - # supplying asset names to upload. @csexton's version chosen based on - # discussion in the issue below, as its the simplest to implement and - # allows for selecting files with a pattern. - # https://github.com/actions/upload-release-asset/issues/4 - #uses: actions/upload-release-asset@v1.0.1 - uses: csexton/release-asset-action@master - with: - pattern: "bundles/*" - github-token: ${{ secrets.GITHUB_TOKEN }} - - upload-pypi: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Set up Python - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - python -m pip install --upgrade pip - pip install --upgrade build twine - - name: Build and publish - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - env: - TWINE_USERNAME: ${{ secrets.pypi_username }} - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: | - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" $file; - done; - python -m build - twine upload dist/* diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main From 680d61c5907ed1e0b1650ecdd3647b11c271398a Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:46:59 -0400 Subject: [PATCH 16/33] Updated pylint version to 2.13.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3343606..4c43710 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.11.1 + rev: v2.13.0 hooks: - id: pylint name: pylint (library code) From 4519b05c5fee95012235f81de3c0d1292f3d4d9a Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 08:15:19 -0400 Subject: [PATCH 17/33] Update pylint to 2.15.5 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c43710..0e5fccc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.13.0 + rev: v2.15.5 hooks: - id: pylint name: pylint (library code) From 1d962886ef6d5d3fc414f576e7877307d4107dc6 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:12:43 -0400 Subject: [PATCH 18/33] Fix release CI files --- .github/workflows/release_gh.yml | 14 +++++++++----- .github/workflows/release_pypi.yml | 15 ++++++++++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index 041a337..b8aa8d6 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -2,13 +2,17 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: GitHub Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml index 041a337..65775b7 100644 --- a/.github/workflows/release_pypi.yml +++ b/.github/workflows/release_pypi.yml @@ -2,13 +2,18 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: PyPI Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} From 4ee7567af0da49fcffa7d2e1c2ce2e150ccab117 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 4 Nov 2022 15:21:40 -0300 Subject: [PATCH 19/33] changes for pylint and black --- .../esp_atcontrol_AIO_no_wifimanager-enterprise.py | 7 ++++--- examples/esp_atcontrol_AIO_wifimanager_enterprise.py | 5 +++-- examples/esp_atcontrol_countviewer_enterprise.py | 10 +++++++--- examples/esp_atcontrol_simple_enterprise.py | 6 ++++-- examples/secrets_enterprise.py | 2 +- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py b/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py index 1be981f..b55a131 100644 --- a/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py +++ b/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py @@ -14,8 +14,7 @@ # ESP32 AT -from adafruit_espatcontrol import ( - adafruit_espatcontrol) +from adafruit_espatcontrol import adafruit_espatcontrol # Get wifi details and more from a secrets.py file @@ -44,7 +43,9 @@ TX = board.RX resetpin = DigitalInOut(board.D4) rtspin = DigitalInOut(board.D5) - uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + uart = busio.UART( + board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512 + ) esp_boot = DigitalInOut(board.D9) esp_boot.direction = Direction.OUTPUT esp_boot.value = True diff --git a/examples/esp_atcontrol_AIO_wifimanager_enterprise.py b/examples/esp_atcontrol_AIO_wifimanager_enterprise.py index 4e9a84f..ba19417 100644 --- a/examples/esp_atcontrol_AIO_wifimanager_enterprise.py +++ b/examples/esp_atcontrol_AIO_wifimanager_enterprise.py @@ -55,7 +55,9 @@ esp = adafruit_espatcontrol.ESP_ATcontrol( uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag ) -wifi = adafruit_espatcontrol_wifimanager.ESPAT_WiFiManager(esp, secrets, status_light, enterprise=True, debug=debugflag) +wifi = adafruit_espatcontrol_wifimanager.ESPAT_WiFiManager( + esp, secrets, status_light, enterprise=True, debug=debugflag +) wifi.disconnect() wifi.reset(soft_reset=True) @@ -89,4 +91,3 @@ continue response = None time.sleep(15) - diff --git a/examples/esp_atcontrol_countviewer_enterprise.py b/examples/esp_atcontrol_countviewer_enterprise.py index 2e65279..a13c793 100644 --- a/examples/esp_atcontrol_countviewer_enterprise.py +++ b/examples/esp_atcontrol_countviewer_enterprise.py @@ -65,8 +65,10 @@ # DATA_LOCATION = ["skulls"] # Twitter followers -DATA_SOURCE = "http://cdn.syndication.twimg.com/widgets/followbutton/info.json?" + \ - "screen_names=adafruit" +DATA_SOURCE = ( + "http://cdn.syndication.twimg.com/widgets/followbutton/info.json?" + + "screen_names=adafruit" +) DATA_LOCATION = [0, "followers_count"] # Debug Level @@ -91,7 +93,9 @@ TX = board.RX resetpin = DigitalInOut(board.D4) rtspin = DigitalInOut(board.D5) - uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + uart = busio.UART( + board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512 + ) esp_boot = DigitalInOut(board.D9) esp_boot.direction = Direction.OUTPUT esp_boot.value = True diff --git a/examples/esp_atcontrol_simple_enterprise.py b/examples/esp_atcontrol_simple_enterprise.py index 8d6d1ab..5cc2844 100644 --- a/examples/esp_atcontrol_simple_enterprise.py +++ b/examples/esp_atcontrol_simple_enterprise.py @@ -40,7 +40,9 @@ TX = board.RX resetpin = DigitalInOut(board.D4) rtspin = DigitalInOut(board.D5) - uart = busio.UART(board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512) + uart = busio.UART( + board.TX, board.RX, baudrate=11520, timeout=0.1, receiver_buffer_size=512 + ) esp_boot = DigitalInOut(board.D9) esp_boot.direction = Direction.OUTPUT esp_boot.value = True @@ -58,7 +60,7 @@ esp.hard_reset() esp.soft_reset() esp.disconnect() -#time.sleep(20) +# time.sleep(20) esp.set_autoconnect(False) requests.set_socket(socket, esp) diff --git a/examples/secrets_enterprise.py b/examples/secrets_enterprise.py index 40e254f..ceae755 100644 --- a/examples/secrets_enterprise.py +++ b/examples/secrets_enterprise.py @@ -14,4 +14,4 @@ "github_token": "abcdefghij0123456789", "aio_username": "your-aio-username", "aio_key": "your-aio-key", -} \ No newline at end of file +} From 45dd1989113afc0d283febe4b9bce80a35384216 Mon Sep 17 00:00:00 2001 From: scogswell Date: Fri, 4 Nov 2022 15:35:08 -0300 Subject: [PATCH 20/33] making pylint happy the hard way --- .../adafruit_espatcontrol.py | 87 ++++--------------- 1 file changed, 17 insertions(+), 70 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index 84dc176..5557691 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -67,13 +67,10 @@ class ESP_ATcontrol: TLS_MODE = "SSL" STATUS_APCONNECTED = 2 # CIPSTATUS method STATUS_WIFI_APCONNECTED = 2 # CWSTATE method - STATUS_SOCKETOPEN = 3 # CIPSTATUS method STATUS_SOCKET_OPEN = 3 # CIPSTATE method - STATUS_SOCKETCLOSED = 4 # CIPSTATUS method STATUS_SOCKET_CLOSED = 4 # CIPSTATE method - STATUS_NOTCONNECTED = 5 # CIPSTATUS method STATUS_WIFI_NOTCONNECTED = 1 # CWSTATE method STATUS_WIFI_DISCONNECTED = 4 # CWSTATE method @@ -116,7 +113,6 @@ def __init__( self._ifconfig = [] self._initialized = False self._conntype = None - self._use_cipstatus = use_cipstatus def begin(self) -> None: @@ -400,7 +396,6 @@ def socket_receive(self, timeout: int = 5) -> bytearray: i = 0 # reset the input buffer now that we know the size elif i > 20: i = 0 # Hmm we somehow didnt get a proper +IPD packet? start over - else: self.hw_flow(False) # stop the flow # read as much as we can! @@ -491,29 +486,11 @@ def is_connected(self) -> bool: print("is_connected(): status says not connected") return False + # pylint: disable=too-many-branches + # pylint: disable=too-many-return-statements @property def status(self) -> Union[int, None]: """The IP connection status number (see AT+CIPSTATUS datasheet for meaning)""" - # Note that CIPSTATUS, at least in the esp32-c3 version of espressif AT firmware - # is considered deprecated and you should use AT+CWSTATE for wifi state - # and AT+CIPSTATE for socket connection statuses. - # - # if CWSTATE/CIPSTATE are available, this function uses those and generates - # a return code compatible with CIPSTATUS. For more fine grain control - # you can use status_wifi and status_socket - # if CWSTATE/CIPSTATE are not available, this falls back to using CIPSTATUS - # (e.g. - ILabs Challenger RP2040 Wifi which has an onboard ESP8285 with older - # firmware) - # CIPSTATUS status messages: - #: status of the ESP32-C3 station interface. - # 0: The ESP32-C3 station is not initialized. - # 1: The ESP32-C3 station is initialized, but not started a Wi-Fi connection yet. - # 2: The ESP32-C3 station is connected to an AP and its IP address is obtained. - # 3: The ESP32-C3 station has created a TCP/SSL transmission. - # 4: All of the TCP/UDP/SSL connections of the ESP32-C3 station are disconnected. - # 5: The ESP32-C3 station started a Wi-Fi connection, but was not connected - # to an AP or disconnected from an AP. - if self._use_cipstatus: replies = self.at_response("AT+CIPSTATUS", timeout=5).split(b"\r\n") for reply in replies: @@ -531,12 +508,17 @@ def status(self) -> Union[int, None]: for reply in replies: if reply.startswith(b"STATUS:"): cipstatus = int(reply[7:8]) - print(f"STATUS: CWSTATE: {status_w}, CIPSTATUS: {cipstatus}, CIPSTATE: {status_s}") + print( + f"STATUS: CWSTATE: {status_w}, CIPSTATUS: {cipstatus}, CIPSTATE: {status_s}" + ) # Produce a cipstatus-compatible status code # Codes are not the same between CWSTATE/CIPSTATUS so in some combinations # we just pick what we hope is best. - if status_w in (self.STATUS_WIFI_NOTCONNECTED, self.STATUS_WIFI_DISCONNECTED): + if status_w in ( + self.STATUS_WIFI_NOTCONNECTED, + self.STATUS_WIFI_DISCONNECTED, + ): if self._debug: print(f"STATUS returning {self.STATUS_NOTCONNECTED}") return self.STATUS_NOTCONNECTED @@ -546,31 +528,26 @@ def status(self) -> Union[int, None]: print(f"STATUS returning {self.STATUS_SOCKETOPEN}") return self.STATUS_SOCKETOPEN - # Sometimes you get a CIPSTATUS=4 when CWSTATE=2/CIPSTATE=4 and sometimes you - # get CIPSTATUS=2 when CWSTATE=2/CIPSTATE=4 - # if status_w == self.STATUS_WIFI_APCONNECTED and status_s == self.STATUS_SOCKET_CLOSED: - # if self._debug: - # print(f"STATUS returning {self.STATUS_SOCKETCLOSED}") - # return self.STATUS_SOCKETCLOSED - if status_w == self.STATUS_WIFI_APCONNECTED: if self._debug: print(f"STATUS returning {self.STATUS_APCONNECTED}") return self.STATUS_APCONNECTED # handle extra codes from CWSTATE - if status_w == 0: # station has not started any Wi-Fi connection. + if status_w == 0: # station has not started any Wi-Fi connection. if self._debug: print("STATUS returning 1") return 1 # this cipstatus had no previous handler variable # pylint: disable=line-too-long - if status_w == 1: # station has connected to an AP, but does not get an IPv4 address yet. + if ( + status_w == 1 + ): # station has connected to an AP, but does not get an IPv4 address yet. if self._debug: print("STATUS returning 1") - return 1 # this cipstatus had no previous handler variable + return 1 # this cipstatus had no previous handler variable - if status_w == 3: # station is in Wi-Fi connecting or reconnecting state. + if status_w == 3: # station is in Wi-Fi connecting or reconnecting state. if self._debug: print(f"STATUS returning {self.STATUS_NOTCONNECTED}") return self.STATUS_NOTCONNECTED @@ -585,16 +562,6 @@ def status(self) -> Union[int, None]: @property def status_wifi(self) -> Union[int, None]: """The WIFI connection status number (see AT+CWSTATE datasheet for meaning)""" - # Note that as of 2022-Nov CIPSTATUS is deprecated and replaced with CWSTATE and CIPSTATE - # and the CWSTATE codes are different than the old CIPSTATUS codes. - # CWSTATE: - # : current Wi-Fi state. - # 0: ESP32-C3 station has not started any Wi-Fi connection. - # 1: ESP32-C3 station has connected to an AP, but does not get an IPv4 address yet. - # 2: ESP32-C3 station has connected to an AP, and got an IPv4 address. - # 3: ESP32-C3 station is in Wi-Fi connecting or reconnecting state. - # 4: ESP32-C3 station is in Wi-Fi disconnected state. - # <”ssid”>: the SSID of the target AP. replies = self.at_response("AT+CWSTATE?", timeout=5).split(b"\r\n") for reply in replies: if reply.startswith(b"+CWSTATE:"): @@ -609,20 +576,6 @@ def status_wifi(self) -> Union[int, None]: @property def status_socket(self) -> Union[int, None]: """The Socket connection status number (see AT+CIPSTATE for meaning)""" - # +CIPSTATE:,<"type">,<"remote IP">,,, - # OK - # When there is no connection, AT returns: - # OK - # Parameters - # : ID of the connection (0~4), used for multiple connections. - # <”type”>: string parameter showing the type of transmission: “TCP”, “TCPv6”, - # “UDP”, “UDPv6”, “SSL”, or “SSLv6”. - # <”remote IP”>: string parameter showing the remote IPv4 address or IPv6 address. - # : the remote port number. - # : the local port number. - # : - # 0: ESP32-C3 runs as a client. - # 1: ESP32-C3 runs as a server. replies = self.at_response("AT+CIPSTATE?", timeout=5).split(b"\r\n") for reply in replies: # If there are any +CIPSTATE lines that means it's an open socket @@ -740,6 +693,7 @@ def join_AP( # pylint: disable=invalid-name return # pylint: disable=invalid-name + # pylint: disable=too-many-arguments def join_AP_Enterprise( self, ssid: str, @@ -755,7 +709,6 @@ def join_AP_Enterprise( # Not sure how to verify certificates so we set that to not verify. certificate_security = 0 # Bit0: Client certificate.Bit1: Server certificate. - # First make sure we're in 'station' mode so we can connect to AP's if self._debug: print("In join_AP_Enterprise()") if self.mode != self.MODE_STATION: @@ -765,9 +718,6 @@ def join_AP_Enterprise( if router and router[0] == ssid: return # we're already connected! reply = self.at_response( - # from https://docs.espressif.com/projects/esp-at/en/latest/ - # esp32c3/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-jeap - # AT+CWJEAP=,,,,,[,] 'AT+CWJEAP="' + ssid + '",' @@ -807,9 +757,7 @@ def disconnect(self, timeout: int = 5, retries: int = 3): else: wait_for_disconnect = False if self._debug is True: - print( - "disconnect(): Not connected, not waiting for disconnect message" - ) + print("disconnect(): Not connected, not waiting for disconnect message") reply = self.at_response("AT+CWQAP", timeout=timeout, retries=retries) # Don't bother waiting for disconnect message if we weren't connected already # sometimes the "WIFI DISCONNECT" shows up in the reply and sometimes it doesn't. @@ -835,7 +783,6 @@ def disconnect(self, timeout: int = 5, retries: int = 3): print( f"disconnect(): Timed out wating for WIFI DISCONNECT: {response}" ) - return def scan_APs( # pylint: disable=invalid-name self, retries: int = 3 From c59a01498507f25643a4fb8b75401b67f0ccb99a Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 18:34:32 -0400 Subject: [PATCH 21/33] Update .pylintrc for v2.15.5 --- .pylintrc | 45 ++++----------------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/.pylintrc b/.pylintrc index f772971..40208c3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # # SPDX-License-Identifier: Unlicense @@ -26,7 +26,7 @@ jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -54,8 +54,8 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,unspecified-encoding +# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call +disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -225,12 +225,6 @@ max-line-length=100 # Maximum number of lines in a module max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -257,38 +251,22 @@ min-similarity-lines=12 [BASIC] -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct argument names argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct attribute names attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - # Regular expression matching correct class names # class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9_]+$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ @@ -296,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # ones are exempt. docstring-min-length=-1 -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct function names function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -309,21 +284,12 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ # Include a hint for the correct naming format with invalid-name include-naming-hint=no -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct method names method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -339,9 +305,6 @@ no-docstring-rgx=^_ # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct variable names variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ From c3ac58cc99c5b8eea3282182fbbc5e49f3caf940 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 20:36:36 -0400 Subject: [PATCH 22/33] Fix pylint errors --- adafruit_espatcontrol/adafruit_espatcontrol.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol.py b/adafruit_espatcontrol/adafruit_espatcontrol.py index e0edc78..cf8aed4 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol.py @@ -340,15 +340,14 @@ def socket_receive(self, timeout: int = 5) -> bytearray: break # We've received all the data. Don't wait until timeout. else: # no data waiting self.hw_flow(True) # start the floooow - totalsize = sum([len(x) for x in bundle]) + totalsize = sum(len(x) for x in bundle) ret = bytearray(totalsize) i = 0 for x in bundle: for char in x: ret[i] = char i += 1 - for x in bundle: - del x + del bundle gc.collect() return ret From 5d92bfe0b0e66e4294ba6cd19d0e7f7f8b108be1 Mon Sep 17 00:00:00 2001 From: scogswell Date: Sun, 13 Nov 2022 09:51:04 -0400 Subject: [PATCH 23/33] docstrings and pylint override in wifimanager --- adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index babe644..c1d5e3f 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -29,6 +29,7 @@ class ESPAT_WiFiManager: A class to help manage the Wifi connection """ + # pylint: disable=too-many-arguments def __init__( self, esp: ESP_ATcontrol, @@ -44,6 +45,8 @@ def __init__( :param status_pixel: (Optional) The pixel device - A NeoPixel or DotStar (default=None) :type status_pixel: NeoPixel or DotStar :param int attempts: (Optional) Unused, only for compatibility for old code + :param bool enterprise: (Optional) If True, try to connect to Enterprise AP + :param bool debug: (Optional) Print debug messages during operation """ # Read the settings self._esp = esp From 5fcf207a90b7e4daf4de453c1dbbfd5a4937af7c Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:16:31 -0400 Subject: [PATCH 24/33] Add .venv to .gitignore Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 544ec4a..db3d538 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ _build # Virtual environment-specific files .env +.venv # MacOS-specific files *.DS_Store From 21c0c7bd7e06eca79af34db3d2d1447e9468ee7b Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:39:55 -0500 Subject: [PATCH 25/33] Add upload url to release action Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .github/workflows/release_gh.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index b8aa8d6..9acec60 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -16,3 +16,4 @@ jobs: uses: adafruit/workflows-circuitpython-libs/release-gh@main with: github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} From e0f11542c420ddfa0d870bf45ace368fbdeb0e74 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Tue, 9 May 2023 20:26:25 -0400 Subject: [PATCH 26/33] Update pre-commit hooks Signed-off-by: Tekktrik --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e5fccc..70ade69 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,21 +4,21 @@ repos: - repo: https://github.com/python/black - rev: 22.3.0 + rev: 23.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v0.14.0 + rev: v1.1.2 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.4.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.15.5 + rev: v2.17.4 hooks: - id: pylint name: pylint (library code) From bc8c68ee6068ea9b2dafca025e28395af82a12d4 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Wed, 10 May 2023 22:32:24 -0400 Subject: [PATCH 27/33] Run pre-commit --- adafruit_espatcontrol/adafruit_espatcontrol_socket.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_socket.py b/adafruit_espatcontrol/adafruit_espatcontrol_socket.py index d063552..e8a1c8b 100644 --- a/adafruit_espatcontrol/adafruit_espatcontrol_socket.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_socket.py @@ -23,6 +23,7 @@ def set_interface(iface: ESP_ATcontrol) -> None: SOCK_STREAM = const(1) AF_INET = const(2) + # pylint: disable=too-many-arguments, unused-argument def getaddrinfo( host: str, @@ -64,7 +65,8 @@ def __init__( def connect(self, address: Tuple[str, int], conntype: Optional[str] = None) -> None: """Connect the socket to the 'address' (which should be dotted quad IP). 'conntype' - is an extra that may indicate SSL or not, depending on the underlying interface""" + is an extra that may indicate SSL or not, depending on the underlying interface + """ host, port = address if not _the_interface.socket_connect( From af3c795df3c52119e0a3ec1152b179de10d0445a Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Sun, 14 May 2023 13:00:32 -0400 Subject: [PATCH 28/33] Update .pylintrc, fix jQuery for docs Signed-off-by: Tekktrik --- .pylintrc | 2 +- docs/conf.py | 1 + docs/requirements.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 40208c3..f945e92 100644 --- a/.pylintrc +++ b/.pylintrc @@ -396,4 +396,4 @@ min-public-methods=1 # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/docs/conf.py b/docs/conf.py index f7b483f..793842e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ # ones. extensions = [ "sphinx.ext.autodoc", + "sphinxcontrib.jquery", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinx.ext.todo", diff --git a/docs/requirements.txt b/docs/requirements.txt index 88e6733..797aa04 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense sphinx>=4.0.0 +sphinxcontrib-jquery From 1d2b928e08867a9ffb38fb5b1bf0ba2e673b5d34 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 18 Sep 2023 16:23:49 -0500 Subject: [PATCH 29/33] "fix rtd theme " --- docs/conf.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 793842e..ec7e7dd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -101,19 +101,10 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] - except: - html_theme = "default" - html_theme_path = ["."] -else: - html_theme_path = ["."] +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 77055961788f4b8d46a25f7793c59f28cb6767c6 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 16 Oct 2023 14:30:31 -0500 Subject: [PATCH 30/33] unpin sphinx and add sphinx-rtd-theme to docs reqs Signed-off-by: foamyguy --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 797aa04..979f568 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,6 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.0.0 +sphinx sphinxcontrib-jquery +sphinx-rtd-theme From 61834c3f095f737e81f18716f6a57cfbcc0339cc Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Thu, 29 Feb 2024 12:13:57 -0800 Subject: [PATCH 31/33] Remove legacy requests.set_socket --- README.rst | 2 + .../adafruit_espatcontrol_wifimanager.py | 37 ++++++++++--------- ...atcontrol_AIO_no_wifimanager-enterprise.py | 8 ++-- examples/esp_atcontrol_countviewer.py | 9 +++-- .../esp_atcontrol_countviewer_enterprise.py | 9 +++-- examples/esp_atcontrol_simple_enterprise.py | 9 +++-- examples/esp_atcontrol_webclient.py | 8 ++-- requirements.txt | 2 + 8 files changed, 50 insertions(+), 34 deletions(-) diff --git a/README.rst b/README.rst index 489a996..2cf7cad 100644 --- a/README.rst +++ b/README.rst @@ -32,6 +32,8 @@ Dependencies This driver depends on: * `Adafruit CircuitPython `_ +* `Adafruit CircuitPython ConnectionManager `_ +* `Adafruit CircuitPython Requests `_ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading diff --git a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py index c1d5e3f..1bbda76 100755 --- a/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py +++ b/adafruit_espatcontrol/adafruit_espatcontrol_wifimanager.py @@ -13,8 +13,9 @@ # pylint: disable=no-name-in-module -import adafruit_requests as requests -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_connection_manager +import adafruit_requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool from adafruit_espatcontrol.adafruit_espatcontrol import ESP_ATcontrol try: @@ -53,11 +54,16 @@ def __init__( self.debug = debug self.secrets = secrets self.attempts = attempts - requests.set_socket(socket, esp) self.statuspix = status_pixel self.pixel_status(0) self.enterprise = enterprise + # create requests session + ssl_context = adafruit_connection_manager.create_fake_ssl_context( + pool, self._esp + ) + self._requests = adafruit_requests.Session(pool, ssl_context) + def reset(self, hard_reset: bool = True, soft_reset: bool = False) -> None: """ Perform a hard reset on the ESP @@ -104,7 +110,7 @@ def disconnect(self) -> None: """ self._esp.disconnect() - def get(self, url: str, **kw: Any) -> requests.Response: + def get(self, url: str, **kw: Any) -> adafruit_requests.Response: """ Pass the Get request to requests and update Status NeoPixel @@ -120,12 +126,11 @@ def get(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) - requests.set_socket(socket, self._esp) - return_val = requests.get(url, **kw) + return_val = self._requests.get(url, **kw) self.pixel_status(0) return return_val - def post(self, url: str, **kw: Any) -> requests.Response: + def post(self, url: str, **kw: Any) -> adafruit_requests.Response: """ Pass the Post request to requests and update Status NeoPixel @@ -145,13 +150,12 @@ def post(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) - requests.set_socket(socket, self._esp) - return_val = requests.post(url, **kw) + return_val = self._requests.post(url, **kw) self.pixel_status(0) return return_val - def put(self, url: str, **kw: Any) -> requests.Response: + def put(self, url: str, **kw: Any) -> adafruit_requests.Response: """ Pass the put request to requests and update Status NeoPixel @@ -167,12 +171,11 @@ def put(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) - requests.set_socket(socket, self._esp) - return_val = requests.put(url, **kw) + return_val = self._requests.put(url, **kw) self.pixel_status(0) return return_val - def patch(self, url: str, **kw: Any) -> requests.Response: + def patch(self, url: str, **kw: Any) -> adafruit_requests.Response: """ Pass the patch request to requests and update Status NeoPixel @@ -188,12 +191,11 @@ def patch(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) - requests.set_socket(socket, self._esp) - return_val = requests.patch(url, **kw) + return_val = self._requests.patch(url, **kw) self.pixel_status(0) return return_val - def delete(self, url: str, **kw: Any) -> requests.Response: + def delete(self, url: str, **kw: Any) -> adafruit_requests.Response: """ Pass the delete request to requests and update Status NeoPixel @@ -209,8 +211,7 @@ def delete(self, url: str, **kw: Any) -> requests.Response: self.connect() self.pixel_status((0, 0, 100)) self.set_conntype(url) - requests.set_socket(socket, self._esp) - return_val = requests.delete(url, **kw) + return_val = self._requests.delete(url, **kw) self.pixel_status(0) return return_val diff --git a/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py b/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py index b55a131..ca096e8 100644 --- a/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py +++ b/examples/esp_atcontrol_AIO_no_wifimanager-enterprise.py @@ -7,10 +7,11 @@ import time import board import busio -import adafruit_requests as requests +import adafruit_connection_manager +import adafruit_requests from digitalio import DigitalInOut from digitalio import Direction -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool # ESP32 AT @@ -56,7 +57,8 @@ uart, 115200, reset_pin=resetpin, rts_pin=rtspin, debug=debugflag ) -requests.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) +requests = adafruit_requests.Session(pool, ssl_context) counter = 0 diff --git a/examples/esp_atcontrol_countviewer.py b/examples/esp_atcontrol_countviewer.py index 671fb4c..aab3b64 100644 --- a/examples/esp_atcontrol_countviewer.py +++ b/examples/esp_atcontrol_countviewer.py @@ -14,8 +14,9 @@ from digitalio import Direction import neopixel from adafruit_ht16k33 import segments -import adafruit_requests as requests -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_connection_manager +import adafruit_requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool from adafruit_espatcontrol import adafruit_espatcontrol # Get wifi details and more from a secrets.py file @@ -101,7 +102,9 @@ ) esp.hard_reset() -requests.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) +requests = adafruit_requests.Session(pool, ssl_context) + # display if DISPLAY_ATTACHED: # Create the I2C interface. diff --git a/examples/esp_atcontrol_countviewer_enterprise.py b/examples/esp_atcontrol_countviewer_enterprise.py index a13c793..749a68a 100644 --- a/examples/esp_atcontrol_countviewer_enterprise.py +++ b/examples/esp_atcontrol_countviewer_enterprise.py @@ -13,8 +13,9 @@ from digitalio import DigitalInOut from digitalio import Direction import neopixel -import adafruit_requests as requests -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_connection_manager +import adafruit_requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool from adafruit_espatcontrol import adafruit_espatcontrol try: @@ -111,7 +112,9 @@ esp.soft_reset() esp.disconnect() -requests.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) +requests = adafruit_requests.Session(pool, ssl_context) + # display if DISPLAY_ATTACHED: # Create the I2C interface. diff --git a/examples/esp_atcontrol_simple_enterprise.py b/examples/esp_atcontrol_simple_enterprise.py index 5cc2844..bfaabaa 100644 --- a/examples/esp_atcontrol_simple_enterprise.py +++ b/examples/esp_atcontrol_simple_enterprise.py @@ -6,8 +6,9 @@ import busio from digitalio import DigitalInOut from digitalio import Direction -import adafruit_requests as requests -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_connection_manager +import adafruit_requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool from adafruit_espatcontrol import adafruit_espatcontrol @@ -63,8 +64,8 @@ # time.sleep(20) esp.set_autoconnect(False) -requests.set_socket(socket, esp) - +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) +requests = adafruit_requests.Session(pool, ssl_context) while True: try: diff --git a/examples/esp_atcontrol_webclient.py b/examples/esp_atcontrol_webclient.py index da50e7f..00b5811 100644 --- a/examples/esp_atcontrol_webclient.py +++ b/examples/esp_atcontrol_webclient.py @@ -6,8 +6,9 @@ import busio from digitalio import DigitalInOut from digitalio import Direction -import adafruit_requests as requests -import adafruit_espatcontrol.adafruit_espatcontrol_socket as socket +import adafruit_connection_manager +import adafruit_requests +import adafruit_espatcontrol.adafruit_espatcontrol_socket as pool from adafruit_espatcontrol import adafruit_espatcontrol @@ -57,7 +58,8 @@ print("Resetting ESP module") esp.hard_reset() -requests.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) +requests = adafruit_requests.Session(pool, ssl_context) while True: try: diff --git a/requirements.txt b/requirements.txt index 45266c4..13401af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,6 @@ Adafruit-Blinka adafruit-circuitpython-typing>=1.4.0 +adafruit-circuitpython-connectionmanager +adafruit-circuitpython-requests pyserial From 13d8d88ce2d730968d2af2a0372381046c439c3a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 7 Oct 2024 09:24:05 -0500 Subject: [PATCH 32/33] remove deprecated get_html_theme_path() call Signed-off-by: foamyguy --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index ec7e7dd..9663e07 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -104,7 +104,6 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 5f95932cb2fc787f17c9a973784b1340baa2f55e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Jan 2025 11:32:34 -0600 Subject: [PATCH 33/33] add sphinx configuration to rtd.yaml Signed-off-by: foamyguy --- .readthedocs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 33c2a61..88bca9f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,6 +8,9 @@ # Required version: 2 +sphinx: + configuration: docs/conf.py + build: os: ubuntu-20.04 tools: