--- a/src/eric7/MicroPython/MicrobitDevices.py Thu Feb 09 09:55:57 2023 +0100 +++ b/src/eric7/MicroPython/MicrobitDevices.py Sat Feb 11 16:59:50 2023 +0100 @@ -8,6 +8,7 @@ Calliope mini boards. """ +import contextlib import os import shutil @@ -29,7 +30,7 @@ Class implementing the device for BBC micro:bit and Calliope mini boards. """ - def __init__(self, microPythonWidget, deviceType, parent=None): + def __init__(self, microPythonWidget, deviceType, serialNumber, parent=None): """ Constructor @@ -37,11 +38,18 @@ @type MicroPythonWidget @param deviceType type of the device @type str + @param serialNumber serial number of the board + @type str @param parent reference to the parent object @type QObject """ super().__init__(microPythonWidget, deviceType, parent) + self.__boardId = 0 # illegal ID + if serialNumber: + with contextlib.suppress(ValueError): + self.__boardId = int(serialNumber[:4], 16) + def setButtons(self): """ Public method to enable the supported action buttons. @@ -134,8 +142,42 @@ @return flag indicating support for time commands @rtype bool """ + if ( + self.microPython.isConnected() + and self.checkDeviceData() + and self._deviceData["mpy_name"] == "circuitpython" + ): + return True + return False + def __isMicroBitV1(self): + """ + Private method to check, if the device is a BBC micro:bit v1. + + @return falg indicating a BBC micro:bit v1 + @rtype bool + """ + return self.__boardId in (0x9900, 0x9901) + + def __isMicroBitV2(self): + """ + Private method to check, if the device is a BBC micro:bit v2. + + @return falg indicating a BBC micro:bit v2 + @rtype bool + """ + return self.__boardId in (0x9903, 0x9904, 0x9905, 0x9906) + + def __isCalliope(self): + """ + Private method to check, if the device is a Calliope mini. + + @return flag indicating a Calliope mini + @rtype bool + """ + return self.__boardId in (0x12A0,) + def addDeviceMenuEntries(self, menu): """ Public method to add device specific entries to the given menu. @@ -300,27 +342,23 @@ Private slot to show the firmware version of the connected device and the available firmware version. """ - if self.microPython.isConnected(): - interface = self.microPython.commandsInterface() - if interface is not None: - impInfo = interface.getImplementation() - versionInfo = interface.version() - if impInfo["name"] != "micropython": - EricMessageBox.critical( - None, - self.tr("Show MicroPython Versions"), - self.tr( - """The firmware of the connected device cannot be""" - """ determined or the board does not run MicroPython.""" - """ Aborting...""" - ), - ) - else: - impInfo["version"] = versionInfo["release"] - if self.getDeviceType() == "bbc_microbit": - if "nRF51" in versionInfo["machine"]: + if self.microPython.isConnected() and self.checkDeviceData(): + if self._deviceData["mpy_name"] not in ("micropython", "circuitpython"): + EricMessageBox.critical( + None, + self.tr("Show MicroPython Versions"), + self.tr( + """The firmware of the connected device cannot be""" + """ determined or the board does not run MicroPython""" + """ or CircuitPython. Aborting...""" + ), + ) + else: + if self.getDeviceType() == "bbc_microbit": + if self._deviceData["mpy_name"] == "micropython": + if self.__isMicroBitV1(): url = QUrl(FirmwareGithubUrls["microbit_v1"]) - elif "nRF52" in versionInfo["machine"]: + elif self.__isMicroBitV2(): url = QUrl(FirmwareGithubUrls["microbit_v2"]) else: EricMessageBox.critical( @@ -332,33 +370,30 @@ ), ) return - else: - EricMessageBox.critical( - None, - self.tr("Show MicroPython Versions"), - self.tr( - """<p>The firmware URL for the device type <b>{0}</b>""" - """ is not known. Aborting...</p>""" - ).format(self.getDeviceType()), - ) - return + elif self._deviceData["mpy_name"] == "circuitpython": + url = QUrl(FirmwareGithubUrls["circuitpython"]) + else: + EricMessageBox.critical( + None, + self.tr("Show MicroPython Versions"), + self.tr( + """<p>The firmware URL for the device type <b>{0}</b>""" + """ is not known. Aborting...</p>""" + ).format(self.getDeviceType()), + ) + return - ui = ericApp().getObject("UserInterface") - request = QNetworkRequest(url) - reply = ui.networkAccessManager().head(request) - reply.finished.connect( - lambda: self.__firmwareVersionResponse(reply, impInfo) - ) + ui = ericApp().getObject("UserInterface") + request = QNetworkRequest(url) + reply = ui.networkAccessManager().head(request) + reply.finished.connect(lambda: self.__firmwareVersionResponse(reply)) - def __firmwareVersionResponse(self, reply, implementation): + def __firmwareVersionResponse(self, reply): """ Private method handling the response of the latest version request. @param reply reference to the reply object @type QNetworkReply - @param implementation dictionary containing the implementation data of the - connected device - @type dict """ latestUrl = reply.url().toString() tag = latestUrl.rsplit("/", 1)[-1] @@ -367,27 +402,40 @@ tag = tag[1:] latestVersion = Globals.versionToTuple(tag) - if implementation["version"] == "unknown": + if self._deviceData["release"] == "unknown": currentVersionStr = self.tr("unknown") currentVersion = (0, 0, 0) else: - currentVersionStr = implementation["version"] + currentVersionStr = self._deviceData["release"] currentVersion = Globals.versionToTuple(currentVersionStr) + if self._deviceData["mpy_name"] == "circuitpython": + kind = "CircuitPython" + microbitVersion = "2" # only v2 device can run CircuitPython + elif self._deviceData["mpy_name"] == "micropython": + kind = "MicroPython" + if self.__isMicroBitV1(): + microbitVersion = "1" + elif self.__isMicroBitV2(): + microbitVersion = "2" + else: + kind = self.tr("Firmware") + microbitVersion = "?" + msg = self.tr( - "<h4>MicroPython Version Information<br/>" - "(BBC micro:bit v{2})</h4>" + "<h4>{0} Version Information<br/>" + "(BBC micro:bit v{1})</h4>" "<table>" - "<tr><td>Installed:</td><td>{0}</td></tr>" - "<tr><td>Available:</td><td>{1}</td></tr>" + "<tr><td>Installed:</td><td>{2}</td></tr>" + "<tr><td>Available:</td><td>{3}</td></tr>" "</table>" - ).format(currentVersionStr, tag, currentVersionStr[0]) + ).format(kind, microbitVersion, currentVersionStr, tag) if currentVersion < latestVersion: msg += self.tr("<p><b>Update available!</b></p>") EricMessageBox.information( None, - self.tr("MicroPython Version"), + self.tr("{0} Version").format(kind), msg, ) @@ -503,7 +551,10 @@ """ if self.getDeviceType() == "bbc_microbit": # BBC micro:bit - return Preferences.getMicroPython("MicrobitDocuUrl") + if self._deviceData and self._deviceData["mpy_name"] == "circuitpython": + return Preferences.getMicroPython("CircuitPythonDocuUrl") + else: + return Preferences.getMicroPython("MicrobitDocuUrl") else: # Calliope mini return Preferences.getMicroPython("CalliopeDocuUrl") @@ -517,20 +568,34 @@ @rtype list of tuple of (str, str) """ if self.getDeviceType() == "bbc_microbit": - return [ - ( - self.tr("MicroPython Firmware for BBC micro:bit V1"), - Preferences.getMicroPython("MicrobitMicroPythonUrl"), - ), - ( - self.tr("MicroPython Firmware for BBC micro:bit V2"), - Preferences.getMicroPython("MicrobitV2MicroPythonUrl"), - ), - ( - self.tr("DAPLink Firmware"), - Preferences.getMicroPython("MicrobitFirmwareUrl"), - ), - ] + if self.__isMicroBitV1(): + return [ + ( + self.tr("MicroPython Firmware for BBC micro:bit V1"), + Preferences.getMicroPython("MicrobitMicroPythonUrl"), + ), + ( + self.tr("DAPLink Firmware"), + Preferences.getMicroPython("MicrobitFirmwareUrl"), + ), + ] + elif self.__isMicroBitV2(): + return [ + ( + self.tr("MicroPython Firmware for BBC micro:bit V2"), + Preferences.getMicroPython("MicrobitV2MicroPythonUrl"), + ), + ( + self.tr("CircuitPython Firmware for BBC micro:bit V2"), + "https://circuitpython.org/board/microbit_v2/", + ), + ( + self.tr("DAPLink Firmware"), + Preferences.getMicroPython("MicrobitFirmwareUrl"), + ), + ] + else: + return [] else: return [ ( @@ -544,7 +609,7 @@ ] -def createDevice(microPythonWidget, deviceType, vid, pid, boardName): +def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): """ Function to instantiate a MicroPython device object. @@ -558,7 +623,9 @@ @type int @param boardName name of the board @type str + @param serialNumber serial number of the board + @type str @return reference to the instantiated device object @rtype MicrobitDevice """ - return MicrobitDevice(microPythonWidget, deviceType) + return MicrobitDevice(microPythonWidget, deviceType, serialNumber)