src/eric7/MicroPython/Devices/MicrobitDevices.py

branch
mpy_network
changeset 9864
f848aacf3447
parent 9834
1fdaebde6316
child 9870
0399d3607829
--- a/src/eric7/MicroPython/Devices/MicrobitDevices.py	Thu Mar 09 11:13:35 2023 +0100
+++ b/src/eric7/MicroPython/Devices/MicrobitDevices.py	Thu Mar 09 14:53:36 2023 +0100
@@ -54,6 +54,13 @@
 
         self.__createMicrobitMenu()
 
+        self.__bleAddressType = {
+            0: self.tr("Public"),
+            1: self.tr("Random Static"),
+            2: self.tr("Random Private Resolvable"),
+            3: self.tr("Random Private Non-Resolvable"),
+        }
+
     def setConnected(self, connected):
         """
         Public method to set the connection state.
@@ -718,6 +725,236 @@
         else:
             return ""
 
+    ##################################################################
+    ## Methods below implement Bluetooth related methods
+    ## 
+    ## Note: These functions are only available on BBC micro:bit v2
+    ##       with CircuitPython firmware loaded. This is handled
+    ##       through the 'hasBluetooth()' method.
+    ##
+    ## The Bluetooth related code below is a copy of the one found in
+    ## the CircuitPythonDevices.py module with modifications to cope
+    ## with the limited set of available modules (e.g. no binascii
+    ## or json).
+    ##################################################################
+
+    def hasBluetooth(self):
+        """
+        Public method to check the availability of Bluetooth.
+
+        @return flag indicating the availability of Bluetooth
+        @rtype bool
+        @exception OSError raised to indicate an issue with the device
+        """
+        if not self.hasCircuitPython():
+            return False
+
+        command = """
+def has_bt():
+    try:
+        import _bleio
+        if hasattr(_bleio, 'adapter'):
+            return True
+    except ImportError:
+        pass
+
+    return False
+
+print(has_bt())
+del has_bt
+"""
+        out, err = self._interface.execute(
+            command, mode=self._submitMode, timeout=10000
+        )
+        if err:
+            raise OSError(self._shortError(err))
+        return out.strip() == b"True"
+
+    def getBluetoothStatus(self):
+        """
+        Public method to get Bluetooth status data of the connected board.
+
+        @return list of tuples containing the translated status data label and
+            the associated value
+        @rtype list of tuples of (str, str)
+        @exception OSError raised to indicate an issue with the device
+        """
+        command = """
+def ble_status():
+    import _bleio
+
+    def address2str(address):
+        return ':'.join('{0:02x}'.format(x) for x in address)
+
+    a = _bleio.adapter
+
+    ble_enabled = a.enabled
+    if not ble_enabled:
+        a.enabled = True
+
+    res = {
+        'active': ble_enabled,
+        'mac': address2str(bytes(reversed(a.address.address_bytes))),
+        'addr_type': a.address.type,
+        'name': a.name,
+        'advertising': a.advertising,
+        'connected': a.connected,
+    }
+
+    if not ble_enabled:
+        a.enabled = False
+
+    print(res)
+
+ble_status()
+del ble_status
+"""
+        out, err = self._interface.execute(command, mode=self._submitMode)
+        if err:
+            raise OSError(self._shortError(err))
+
+        status = []
+        bleStatus = ast.literal_eval(out.decode("utf-8"))
+        status.append((self.tr("Active"), self.bool2str(bleStatus["active"])))
+        status.append((self.tr("Name"), bleStatus["name"]))
+        status.append((self.tr("MAC-Address"), bleStatus["mac"]))
+        status.append(
+            (self.tr("Address Type"), self.__bleAddressType[bleStatus["addr_type"]])
+        )
+        status.append((self.tr("Connected"), self.bool2str(bleStatus["connected"])))
+        status.append((self.tr("Advertising"), self.bool2str(bleStatus["advertising"])))
+
+        return status
+
+    def activateBluetoothInterface(self):
+        """
+        Public method to activate the Bluetooth interface.
+
+        @return flag indicating the new state of the Bluetooth interface
+        @rtype bool
+        @exception OSError raised to indicate an issue with the device
+        """
+        command = """
+def activate_ble():
+    import _bleio
+
+    a = _bleio.adapter
+    if not a.enabled:
+        a.enabled = True
+    print(a.enabled)
+
+activate_ble()
+del activate_ble
+"""
+        out, err = self._interface.execute(command, mode=self._submitMode)
+        if err:
+            raise OSError(self._shortError(err))
+
+        return out.strip() == b"True"
+
+    def deactivateBluetoothInterface(self):
+        """
+        Public method to deactivate the Bluetooth interface.
+
+        @return flag indicating the new state of the Bluetooth interface
+        @rtype bool
+        @exception OSError raised to indicate an issue with the device
+        """
+        command = """
+def deactivate_ble():
+    import _bleio
+
+    a = _bleio.adapter
+    if a.enabled:
+        a.enabled = False
+    print(a.enabled)
+
+deactivate_ble()
+del deactivate_ble
+"""
+        out, err = self._interface.execute(command, mode=self._submitMode)
+        if err:
+            raise OSError(self._shortError(err))
+
+        return out.strip() == b"True"
+
+    def getDeviceScan(self, timeout=10):
+        """
+        Public method to perform a Bluetooth device scan.
+
+        @param timeout duration of the device scan in seconds (defaults
+            to 10)
+        @type int (optional)
+        @return tuple containing a dictionary with the scan results and
+            an error string
+        @rtype tuple of (dict, str)
+        """
+        from ..BluetoothDialogs.BluetoothAdvertisement import (
+            ADV_IND,
+            ADV_SCAN_IND,
+            SCAN_RSP,
+            BluetoothAdvertisement,
+        )
+
+        command = """
+def ble_scan():
+    import _bleio
+    import time
+
+    def address2str(address):
+        return ':'.join('{{0:02x}}'.format(x) for x in address)
+
+    a = _bleio.adapter
+
+    ble_enabled = a.enabled
+    if not ble_enabled:
+        a.enabled = True
+
+    scanResults = a.start_scan(
+        buffer_size=1024, extended=True, timeout={0}, minimum_rssi=-120, active=True
+    )
+    time.sleep({0})
+    a.stop_scan()
+
+    for res in scanResults:
+        print({{
+            'address': address2str(bytes(reversed(res.address.address_bytes))),
+            'advertisement': res.advertisement_bytes,
+            'connectable': res.connectable,
+            'rssi': res.rssi,
+            'scan_response': res.scan_response,
+        }})
+
+    if not ble_enabled:
+        a.enabled = False
+
+ble_scan()
+del ble_scan
+""".format(
+            timeout
+        )
+        out, err = self._interface.execute(
+            command, mode=self._submitMode, timeout=(timeout + 5) * 1000
+        )
+        if err:
+            return {}, err
+
+        scanResults = {}
+        for line in out.decode("utf-8").splitlines():
+            res = ast.literal_eval(line)
+            address = res["address"]
+            if address not in scanResults:
+                scanResults[address] = BluetoothAdvertisement(address)
+            if res["scan_response"]:
+                advType = SCAN_RSP
+            elif res["connectable"]:
+                advType = ADV_IND
+            else:
+                advType = ADV_SCAN_IND
+            scanResults[address].update(advType, res["rssi"], res["advertisement"])
+
+        return scanResults, ""
+
 
 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber):
     """

eric ide

mercurial