716 if self.hasCircuitPython(): |
723 if self.hasCircuitPython(): |
717 return super()._getSetTimeCode() |
724 return super()._getSetTimeCode() |
718 else: |
725 else: |
719 return "" |
726 return "" |
720 |
727 |
|
728 ################################################################## |
|
729 ## Methods below implement Bluetooth related methods |
|
730 ## |
|
731 ## Note: These functions are only available on BBC micro:bit v2 |
|
732 ## with CircuitPython firmware loaded. This is handled |
|
733 ## through the 'hasBluetooth()' method. |
|
734 ## |
|
735 ## The Bluetooth related code below is a copy of the one found in |
|
736 ## the CircuitPythonDevices.py module with modifications to cope |
|
737 ## with the limited set of available modules (e.g. no binascii |
|
738 ## or json). |
|
739 ################################################################## |
|
740 |
|
741 def hasBluetooth(self): |
|
742 """ |
|
743 Public method to check the availability of Bluetooth. |
|
744 |
|
745 @return flag indicating the availability of Bluetooth |
|
746 @rtype bool |
|
747 @exception OSError raised to indicate an issue with the device |
|
748 """ |
|
749 if not self.hasCircuitPython(): |
|
750 return False |
|
751 |
|
752 command = """ |
|
753 def has_bt(): |
|
754 try: |
|
755 import _bleio |
|
756 if hasattr(_bleio, 'adapter'): |
|
757 return True |
|
758 except ImportError: |
|
759 pass |
|
760 |
|
761 return False |
|
762 |
|
763 print(has_bt()) |
|
764 del has_bt |
|
765 """ |
|
766 out, err = self._interface.execute( |
|
767 command, mode=self._submitMode, timeout=10000 |
|
768 ) |
|
769 if err: |
|
770 raise OSError(self._shortError(err)) |
|
771 return out.strip() == b"True" |
|
772 |
|
773 def getBluetoothStatus(self): |
|
774 """ |
|
775 Public method to get Bluetooth status data of the connected board. |
|
776 |
|
777 @return list of tuples containing the translated status data label and |
|
778 the associated value |
|
779 @rtype list of tuples of (str, str) |
|
780 @exception OSError raised to indicate an issue with the device |
|
781 """ |
|
782 command = """ |
|
783 def ble_status(): |
|
784 import _bleio |
|
785 |
|
786 def address2str(address): |
|
787 return ':'.join('{0:02x}'.format(x) for x in address) |
|
788 |
|
789 a = _bleio.adapter |
|
790 |
|
791 ble_enabled = a.enabled |
|
792 if not ble_enabled: |
|
793 a.enabled = True |
|
794 |
|
795 res = { |
|
796 'active': ble_enabled, |
|
797 'mac': address2str(bytes(reversed(a.address.address_bytes))), |
|
798 'addr_type': a.address.type, |
|
799 'name': a.name, |
|
800 'advertising': a.advertising, |
|
801 'connected': a.connected, |
|
802 } |
|
803 |
|
804 if not ble_enabled: |
|
805 a.enabled = False |
|
806 |
|
807 print(res) |
|
808 |
|
809 ble_status() |
|
810 del ble_status |
|
811 """ |
|
812 out, err = self._interface.execute(command, mode=self._submitMode) |
|
813 if err: |
|
814 raise OSError(self._shortError(err)) |
|
815 |
|
816 status = [] |
|
817 bleStatus = ast.literal_eval(out.decode("utf-8")) |
|
818 status.append((self.tr("Active"), self.bool2str(bleStatus["active"]))) |
|
819 status.append((self.tr("Name"), bleStatus["name"])) |
|
820 status.append((self.tr("MAC-Address"), bleStatus["mac"])) |
|
821 status.append( |
|
822 (self.tr("Address Type"), self.__bleAddressType[bleStatus["addr_type"]]) |
|
823 ) |
|
824 status.append((self.tr("Connected"), self.bool2str(bleStatus["connected"]))) |
|
825 status.append((self.tr("Advertising"), self.bool2str(bleStatus["advertising"]))) |
|
826 |
|
827 return status |
|
828 |
|
829 def activateBluetoothInterface(self): |
|
830 """ |
|
831 Public method to activate the Bluetooth interface. |
|
832 |
|
833 @return flag indicating the new state of the Bluetooth interface |
|
834 @rtype bool |
|
835 @exception OSError raised to indicate an issue with the device |
|
836 """ |
|
837 command = """ |
|
838 def activate_ble(): |
|
839 import _bleio |
|
840 |
|
841 a = _bleio.adapter |
|
842 if not a.enabled: |
|
843 a.enabled = True |
|
844 print(a.enabled) |
|
845 |
|
846 activate_ble() |
|
847 del activate_ble |
|
848 """ |
|
849 out, err = self._interface.execute(command, mode=self._submitMode) |
|
850 if err: |
|
851 raise OSError(self._shortError(err)) |
|
852 |
|
853 return out.strip() == b"True" |
|
854 |
|
855 def deactivateBluetoothInterface(self): |
|
856 """ |
|
857 Public method to deactivate the Bluetooth interface. |
|
858 |
|
859 @return flag indicating the new state of the Bluetooth interface |
|
860 @rtype bool |
|
861 @exception OSError raised to indicate an issue with the device |
|
862 """ |
|
863 command = """ |
|
864 def deactivate_ble(): |
|
865 import _bleio |
|
866 |
|
867 a = _bleio.adapter |
|
868 if a.enabled: |
|
869 a.enabled = False |
|
870 print(a.enabled) |
|
871 |
|
872 deactivate_ble() |
|
873 del deactivate_ble |
|
874 """ |
|
875 out, err = self._interface.execute(command, mode=self._submitMode) |
|
876 if err: |
|
877 raise OSError(self._shortError(err)) |
|
878 |
|
879 return out.strip() == b"True" |
|
880 |
|
881 def getDeviceScan(self, timeout=10): |
|
882 """ |
|
883 Public method to perform a Bluetooth device scan. |
|
884 |
|
885 @param timeout duration of the device scan in seconds (defaults |
|
886 to 10) |
|
887 @type int (optional) |
|
888 @return tuple containing a dictionary with the scan results and |
|
889 an error string |
|
890 @rtype tuple of (dict, str) |
|
891 """ |
|
892 from ..BluetoothDialogs.BluetoothAdvertisement import ( |
|
893 ADV_IND, |
|
894 ADV_SCAN_IND, |
|
895 SCAN_RSP, |
|
896 BluetoothAdvertisement, |
|
897 ) |
|
898 |
|
899 command = """ |
|
900 def ble_scan(): |
|
901 import _bleio |
|
902 import time |
|
903 |
|
904 def address2str(address): |
|
905 return ':'.join('{{0:02x}}'.format(x) for x in address) |
|
906 |
|
907 a = _bleio.adapter |
|
908 |
|
909 ble_enabled = a.enabled |
|
910 if not ble_enabled: |
|
911 a.enabled = True |
|
912 |
|
913 scanResults = a.start_scan( |
|
914 buffer_size=1024, extended=True, timeout={0}, minimum_rssi=-120, active=True |
|
915 ) |
|
916 time.sleep({0}) |
|
917 a.stop_scan() |
|
918 |
|
919 for res in scanResults: |
|
920 print({{ |
|
921 'address': address2str(bytes(reversed(res.address.address_bytes))), |
|
922 'advertisement': res.advertisement_bytes, |
|
923 'connectable': res.connectable, |
|
924 'rssi': res.rssi, |
|
925 'scan_response': res.scan_response, |
|
926 }}) |
|
927 |
|
928 if not ble_enabled: |
|
929 a.enabled = False |
|
930 |
|
931 ble_scan() |
|
932 del ble_scan |
|
933 """.format( |
|
934 timeout |
|
935 ) |
|
936 out, err = self._interface.execute( |
|
937 command, mode=self._submitMode, timeout=(timeout + 5) * 1000 |
|
938 ) |
|
939 if err: |
|
940 return {}, err |
|
941 |
|
942 scanResults = {} |
|
943 for line in out.decode("utf-8").splitlines(): |
|
944 res = ast.literal_eval(line) |
|
945 address = res["address"] |
|
946 if address not in scanResults: |
|
947 scanResults[address] = BluetoothAdvertisement(address) |
|
948 if res["scan_response"]: |
|
949 advType = SCAN_RSP |
|
950 elif res["connectable"]: |
|
951 advType = ADV_IND |
|
952 else: |
|
953 advType = ADV_SCAN_IND |
|
954 scanResults[address].update(advType, res["rssi"], res["advertisement"]) |
|
955 |
|
956 return scanResults, "" |
|
957 |
721 |
958 |
722 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): |
959 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): |
723 """ |
960 """ |
724 Function to instantiate a MicroPython device object. |
961 Function to instantiate a MicroPython device object. |
725 |
962 |