--- a/src/eric7/MicroPython/Devices/RP2040Devices.py Wed Aug 02 17:22:20 2023 +0200 +++ b/src/eric7/MicroPython/Devices/RP2040Devices.py Thu Aug 03 17:33:07 2023 +0200 @@ -428,6 +428,9 @@ menu.addAction(self.tr("Set Country"), self.__setCountry).setEnabled( self._deviceData["wifi_type"] == "picow" ) + menu.addAction(self.tr("Reset Country"), self.__resetCountry).setEnabled( + self._deviceData["wifi_type"] == "picow" + ) def hasWifi(self): """ @@ -473,6 +476,15 @@ return False, "" return ast.literal_eval(out.decode("utf-8")) + def hasWifiCountry(self): + """ + Public method to check, if the device has support to set the WiFi country. + + @return flag indicating the support of WiFi country + @rtype bool + """ + return self._deviceData["wifi_type"] == "picow" + def getWifiData(self): """ Public method to get data related to the current WiFi status. @@ -499,7 +511,6 @@ 'mac': ubinascii.hexlify(wifi.config('mac'), ':').decode(), 'channel': wifi.config('channel'), 'txpower': wifi.config('txpower'), - 'country': rp2.country(), } print(ujson.dumps(station)) @@ -513,13 +524,20 @@ 'channel': wifi.config('channel'), 'txpower': wifi.config('txpower'), 'essid': wifi.config('essid'), - 'country': rp2.country(), } print(ujson.dumps(ap)) overall = { 'active': station['active'] or ap['active'] } + try: + overall['country'] = network.country() + except AttributeError: + overall['country'] = rp2.country() + try: + overall['hostname'] = network.hostname() + except AttributeError: + pass print(ujson.dumps(overall)) wifi_status() @@ -619,7 +637,7 @@ ) return station, ap, overall - def connectWifi(self, ssid, password): + def connectWifi(self, ssid, password, hostname): """ Public method to connect a device to a WiFi network. @@ -627,13 +645,15 @@ @type str @param password password needed to connect @type str + @param hostname host name of the device + @type str @return tuple containing the connection status and an error string @rtype tuple of (bool, str) """ if self._deviceData["wifi_type"] == "picow": country = Preferences.getMicroPython("WifiCountry").upper() command = """ -def connect_wifi(ssid, password, country): +def connect_wifi(ssid, password, hostname, country): import network import rp2 import ujson @@ -641,6 +661,12 @@ rp2.country(country) + if hostname: + try: + network.hostname(hostname) + except AttributeError: + pass + wifi = network.WLAN(network.STA_IF) wifi.active(False) wifi.active(True) @@ -654,11 +680,12 @@ status = wifi.status() print(ujson.dumps({{'connected': wifi.isconnected(), 'status': status}})) -connect_wifi({0}, {1}, {2}) +connect_wifi({0}, {1}, {2}, {3}) del connect_wifi """.format( repr(ssid), repr(password if password else ""), + repr(hostname), repr(country if country else "XX"), ) elif self._deviceData["wifi_type"] == "picowireless": @@ -694,7 +721,7 @@ repr(password if password else ""), ) else: - return super().connectWifi(ssid, password) + return super().connectWifi(ssid, password, hostname) with EricOverrideCursor(): out, err = self.executeCommands( @@ -835,7 +862,7 @@ return out.strip() == b"True" - def writeCredentials(self, ssid, password): + def writeCredentials(self, ssid, password, hostname, country): """ Public method to write the given credentials to the connected device and modify the start script to connect automatically. @@ -844,6 +871,10 @@ @type str @param password password needed to authenticate @type str + @param hostname host name of the device + @type str + @param country WiFi country code + @type str @return tuple containing a flag indicating success and an error message @rtype tuple of (bool, str) """ @@ -868,11 +899,14 @@ """ if self._deviceData["wifi_type"] == "picow": - country = Preferences.getMicroPython("WifiCountry").upper() - secrets = "WIFI_SSID = {0}\nWIFI_KEY = {1}\nWIFI_COUNTRY={2}".format( + secrets = ( + "WIFI_SSID = {0}\nWIFI_KEY = {1}\nWIFI_COUNTRY={2}\n" + "WIFI_HOSTNAME = {3}\n" + ).format( repr(ssid), repr(password) if password else '""', - repr(country) if country else '""', + repr(country.upper()) if country else '""', + repr(hostname) if hostname else '""', ) wifiConnectFile = "picowWiFiConnect.py" else: @@ -883,6 +917,9 @@ if self._deviceData["wifi_type"] == "picowireless": wifiConnectFile = "pimoroniWiFiConnect.py" else: + secrets += "WIFI_HOSTNAME = {0}\n".format( + repr(hostname if hostname else '""') + ) wifiConnectFile = "mpyWiFiConnect.py" try: # write secrets file @@ -1106,7 +1143,14 @@ else: return out.decode("utf-8").strip() == "True", "" - def startAccessPoint(self, ssid, security=None, password=None, ifconfig=None): + def startAccessPoint( + self, + ssid, + security=None, + password=None, + hostname=None, + ifconfig=None, + ): """ Public method to start the access point interface. @@ -1116,6 +1160,8 @@ @type int (optional) @param password password (defaults to None) @type str (optional) + @param hostname host name of the device (defaults to None) + @type str (optional) @param ifconfig IPv4 configuration for the access point if not default (IPv4 address, netmask, gateway address, DNS server address) @type tuple of (str, str, str, str) @@ -1131,13 +1177,19 @@ if security: security = 4 # Pico W supports just WPA/WPA2 command = """ -def start_ap(ssid, security, password, ifconfig, country): +def start_ap(ssid, security, password, hostname, ifconfig, country): import network import rp2 from time import sleep rp2.country(country) + if hostname: + try: + network.hostname(hostname) + except AttributeError: + pass + ap = network.WLAN(network.AP_IF) ap.active(True) if ifconfig: @@ -1146,12 +1198,13 @@ sleep(0.1) print(ap.isconnected()) -start_ap({0}, {1}, {2}, {3}, {4}) +start_ap({0}, {1}, {2}, {3}, {4}, {5}) del start_ap """.format( repr(ssid), security, repr(password), + repr(hostname), ifconfig, repr(country if country else "XX"), ) @@ -1189,7 +1242,13 @@ repr(password if password else ""), ) else: - return super().startAccessPoint(ssid, security=security, password=password) + return super().startAccessPoint( + ssid, + security=security, + password=password, + hostname=hostname, + ifconfig=ifconfig, + ) out, err = self.executeCommands(command, mode=self._submitMode, timeout=15000) if err: @@ -1339,6 +1398,56 @@ return out.decode("utf-8").strip() == "True", "" + @pyqtSlot() + def __setCountry(self): + """ + Private slot to configure the country of the connected RP2040 device. + + The country is the two-letter ISO 3166-1 Alpha-2 country code. + """ + from ..WifiDialogs.WifiCountryDialog import WifiCountryDialog + + dlg = WifiCountryDialog() + if dlg.exec() == QDialog.DialogCode.Accepted: + country, remember = dlg.getCountry() + if remember: + Preferences.setMicroPython("WifiCountry", country) + + command = """ +try: + import network + network.country({0}) +except AttributeError: + import rp2 + rp2.country({0}) +""".format( + repr(country) + ) + + out, err = self.executeCommands(command, mode=self._submitMode) + if err: + self.microPython.showError("country()", err) + + @pyqtSlot() + def __resetCountry(self): + """ + Private slot to reset the country of the connected ESP32 device. + + The country is the two-letter ISO 3166-1 Alpha-2 country code. This method + resets it to the default code 'XX' representing the "worldwide" region. + """ + command = """ +try: + import network + network.country('XX') +except AttributeError: + pass +""" + + out, err = self.executeCommands(command, mode=self._submitMode) + if err: + self.microPython.showError("country()", err) + ################################################################## ## Methods below implement Bluetooth related methods ################################################################## @@ -1610,8 +1719,11 @@ 'status': nic.status(), 'ifconfig': nic.ifconfig(), 'mac': ubinascii.hexlify(nic.config('mac'), ':').decode(), - 'hostname': network.hostname(), }} + try: + res['hostname'] = network.hostname() + except AttributeError: + res['hostname'] = '' print(ujson.dumps(res)) ethernet_status() @@ -1634,7 +1746,12 @@ self.__statusTranslations["picowiz"][ethStatus["status"]], ) ) - status.append((self.tr("Hostname"), ethStatus["hostname"])) + status.append( + ( + self.tr("Hostname"), + ethStatus["hostname"] if ethStatus["hostname"] else self.tr("unknown"), + ) + ) status.append((self.tr("IPv4 Address"), ethStatus["ifconfig"][0])) status.append((self.tr("Netmask"), ethStatus["ifconfig"][1])) status.append((self.tr("Gateway"), ethStatus["ifconfig"][2])) @@ -1643,20 +1760,29 @@ return status - def connectToLan(self, config): + def connectToLan(self, config, hostname): """ Public method to connect the connected device to the LAN. @param config configuration for the connection (either the string 'dhcp' for a dynamic address or a tuple of four strings with the IPv4 parameters. @type str or tuple of (str, str, str, str) + @param hostname host name of the device + @type str @return tuple containing a flag indicating success and an error message @rtype tuple of (bool, str) """ command = """{0} -def connect_lan(config): +def connect_lan(config, hostname): + import network import time + if hostname: + try: + network.hostname(hostname) + except AttributeError: + pass + w5x00_init() nic.active(False) @@ -1670,10 +1796,12 @@ time.sleep(0.1) print(nic.isconnected()) -connect_lan({1}) +connect_lan({1}, {2}) del connect_lan, w5x00_init """.format( - WiznetUtilities.mpyWiznetInit(), "'dhcp'" if config == "dhcp" else config + WiznetUtilities.mpyWiznetInit(), + "'dhcp'" if config == "dhcp" else config, + repr(hostname) if hostname else "''", ) with EricOverrideCursor(): @@ -1793,7 +1921,7 @@ return self.disconnectFromLan() - def writeLanAutoConnect(self, config): + def writeLanAutoConnect(self, config, hostname): """ Public method to generate a script and associated configuration to connect the device to the LAN during boot time. @@ -1801,6 +1929,8 @@ @param config configuration for the connection (either the string 'dhcp' for a dynamic address or a tuple of four strings with the IPv4 parameters. @type str or tuple of (str, str, str, str) + @param hostname host name of the device + @type str @return tuple containing a flag indicating success and an error message @rtype tuple of (bool, str) """ @@ -1825,10 +1955,13 @@ modify_boot() del modify_boot """ - ifconfig = "ifconfig = {0}\n".format("'dhcp'" if config == "dhcp" else config) + devconfig = "ifconfig = {0}\nhostname = {1}".format( + "'dhcp'" if config == "dhcp" else config, + repr(hostname) if hostname else "''", + ) try: # write secrets file - self.putData("/wiznet_config.py", ifconfig.encode("utf-8")) + self.putData("/wiznet_config.py", devconfig.encode("utf-8")) # copy auto-connect file self.put( os.path.join( @@ -1955,36 +2088,6 @@ res = ast.literal_eval(out.decode("utf-8")) return res["result"], res["error"] - ############################################################################ - ## RP2 only methods below - ############################################################################ - - @pyqtSlot() - def __setCountry(self): - """ - Private slot to configure the country of the connected RP2040 device. - - The country is the two letter country code. - """ - from ..WifiDialogs.WifiCountryDialog import WifiCountryDialog - - dlg = WifiCountryDialog() - if dlg.exec() == QDialog.DialogCode.Accepted: - country, remember = dlg.getCountry() - if remember: - Preferences.setMicroPython("WifiCountry", country) - - command = """ -import rp2 -rp2.country({0}) -""".format( - repr(country) - ) - - out, err = self.executeCommands(command, mode=self._submitMode) - if err: - self.microPython.showError("rp2.country()", err) - def createDevice( microPythonWidget, deviceType, vid, pid, boardName, serialNumber # noqa: U100