src/eric7/MicroPython/Devices/GenericMicroPythonDevices.py

branch
eric7
changeset 9906
39daf45010c8
parent 9763
52f982c08301
child 9989
286c2a21f36f
diff -r 189b7a23c3c6 -r 39daf45010c8 src/eric7/MicroPython/Devices/GenericMicroPythonDevices.py
--- a/src/eric7/MicroPython/Devices/GenericMicroPythonDevices.py	Thu Mar 16 09:20:00 2023 +0100
+++ b/src/eric7/MicroPython/Devices/GenericMicroPythonDevices.py	Thu Mar 16 12:03:23 2023 +0100
@@ -10,12 +10,18 @@
 
 import os
 
-from eric7 import Preferences
+from PyQt6.QtCore import QUrl, pyqtSlot
+from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest
+from PyQt6.QtWidgets import QMenu
+
+from eric7 import Globals, Preferences
 from eric7.EricWidgets import EricMessageBox
+from eric7.EricWidgets.EricApplication import ericApp
 from eric7.SystemUtilities import FileSystemUtilities
 
+from ..MicroPythonWidget import HAS_QTCHART
+from . import FirmwareGithubUrls
 from .DeviceBase import BaseDevice
-from .MicroPythonWidget import HAS_QTCHART
 
 
 class GenericMicroPythonDevice(BaseDevice):
@@ -40,6 +46,8 @@
         """
         super().__init__(microPythonWidget, deviceType, parent)
 
+        self.__createGenericMenu()
+
         self.__directAccess = False
         self.__deviceVolumeName = ""
         self.__workspace = ""
@@ -123,6 +131,39 @@
         """
         return True, ""
 
+    def __createGenericMenu(self):
+        """
+        Private method to create the Generic submenu.
+        """
+        self.__genericMenu = QMenu(self.tr("Generic Device Functions"))
+
+        self.__showMpyAct = self.__genericMenu.addAction(
+            self.tr("Show MicroPython Versions"), self.__showFirmwareVersions
+        )
+        self.__genericMenu.addSeparator()
+        self.__bootloaderAct = self.__genericMenu.addAction(
+            self.tr("Activate Bootloader"), self.__activateBootloader
+        )
+        self.__genericMenu.addSeparator()
+        self.__resetAct = self.__genericMenu.addAction(
+            self.tr("Reset Device"), self.__resetDevice
+        )
+
+    def addDeviceMenuEntries(self, menu):
+        """
+        Public method to add device specific entries to the given menu.
+
+        @param menu reference to the context menu
+        @type QMenu
+        """
+        connected = self.microPython.isConnected()
+
+        self.__showMpyAct.setEnabled(connected)
+        self.__bootloaderAct.setEnabled(connected)
+        self.__resetAct.setEnabled(connected)
+
+        menu.addMenu(self.__genericMenu)
+
     def supportsLocalFileAccess(self):
         """
         Public method to indicate file access via a local directory.
@@ -202,6 +243,156 @@
 
             return super().getWorkspace()
 
+    @pyqtSlot()
+    def __activateBootloader(self):
+        """
+        Private slot to switch the board into 'bootloader' mode.
+        """
+        if self.microPython.isConnected():
+            self.microPython.deviceInterface().execute(
+                "import machine\nmachine.bootloader()\n", mode=self._submitMode
+            )
+            # simulate pressing the disconnect button
+            self.microPython.on_connectButton_clicked()
+
+    @pyqtSlot()
+    def __showFirmwareVersions(self):
+        """
+        Private slot to show the firmware version of the connected device and the
+        available firmware version.
+        """
+        if self.microPython.isConnected():
+            if self._deviceData["mpy_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:
+                ui = ericApp().getObject("UserInterface")
+                request = QNetworkRequest(QUrl(FirmwareGithubUrls["micropython"]))
+                reply = ui.networkAccessManager().head(request)
+                reply.finished.connect(lambda: self.__firmwareVersionResponse(reply))
+
+    @pyqtSlot(QNetworkReply)
+    def __firmwareVersionResponse(self, reply):
+        """
+        Private slot handling the response of the latest version request.
+
+        @param reply reference to the reply object
+        @type QNetworkReply
+        """
+        latestUrl = reply.url().toString()
+        tag = latestUrl.rsplit("/", 1)[-1]
+        while tag and not tag[0].isdecimal():
+            # get rid of leading non-decimal characters
+            tag = tag[1:]
+        latestVersion = Globals.versionToTuple(tag)
+
+        if self._deviceData["mpy_version"] == "unknown":
+            currentVersionStr = self.tr("unknown")
+            currentVersion = (0, 0, 0)
+        else:
+            currentVersionStr = (
+                self._deviceData["mpy_variant_version"]
+                if bool(self._deviceData["mpy_variant_version"])
+                else self._deviceData["mpy_version"]
+            )
+            currentVersion = Globals.versionToTuple(currentVersionStr)
+
+        msg = self.tr(
+            "<h4>MicroPython Version Information</h4>"
+            "<table>"
+            "<tr><td>Installed:</td><td>{0}</td></tr>"
+            "<tr><td>Available:</td><td>{1}</td></tr>"
+            "{2}"
+            "</table>"
+        ).format(
+            currentVersionStr,
+            tag,
+            self.tr("<tr><td>Variant:</td><td>{0}</td></tr>").format(
+                self._deviceData["mpy_variant"]
+            )
+            if self._deviceData["mpy_variant"]
+            else "",
+        )
+        if currentVersion < latestVersion:
+            msg += self.tr("<p><b>Update available!</b></p>")
+
+        EricMessageBox.information(
+            None,
+            self.tr("MicroPython Version"),
+            msg,
+        )
+
+    @pyqtSlot()
+    def __resetDevice(self):
+        """
+        Private slot to reset the connected device.
+        """
+        self.microPython.deviceInterface().execute(
+            "import machine\nmachine.reset()\n", mode=self._submitMode
+        )
+        # simulate pressing the disconnect button
+        self.microPython.on_connectButton_clicked()
+
+    def getDocumentationUrl(self):
+        """
+        Public method to get the device documentation URL.
+
+        @return documentation URL of the device
+        @rtype str
+        """
+        return Preferences.getMicroPython("MicroPythonDocuUrl")
+
+    def getFirmwareUrl(self):
+        """
+        Public method to get the device firmware download URL.
+
+        @return firmware download URL of the device
+        @rtype str
+        """
+        return Preferences.getMicroPython("MicroPythonFirmwareUrl")
+
+    ##################################################################
+    ## time related methods below
+    ##################################################################
+
+    def _getSetTimeCode(self):
+        """
+        Protected method to get the device code to set the time.
+
+        Note: This method must be implemented in the various device specific
+        subclasses.
+
+        @return code to be executed on the connected device to set the time
+        @rtype str
+        """
+        # rtc_time[0] - year    4 digit
+        # rtc_time[1] - month   1..12
+        # rtc_time[2] - day     1..31
+        # rtc_time[3] - weekday 1..7 1=Monday
+        # rtc_time[4] - hour    0..23
+        # rtc_time[5] - minute  0..59
+        # rtc_time[6] - second  0..59
+        # rtc_time[7] - yearday 1..366
+        # rtc_time[8] - isdst   0, 1, or -1
+        #
+        # https://docs.micropython.org/en/latest/library/machine.RTC.html#machine-rtc
+        return """
+def set_time(rtc_time):
+    try:
+        import machine
+        rtc = machine.RTC()
+        rtc.datetime(rtc_time[:7] + (0,))
+    except Exception:
+        pass
+"""
+
 
 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber):
     """

eric ide

mercurial