--- a/src/eric7/MicroPython/BluetoothDialogs/BluetoothAdvertisement.py Thu Jun 15 17:11:14 2023 +0200 +++ b/src/eric7/MicroPython/BluetoothDialogs/BluetoothAdvertisement.py Thu Jun 15 19:13:51 2023 +0200 @@ -7,9 +7,13 @@ Module implementing a class to parse and store the Bluetooth device advertisement data. """ +import contextlib +import os import struct import uuid +import yaml + ADV_IND = 0 ADV_DIRECT_IND = 1 ADV_SCAN_IND = 2 @@ -28,15 +32,43 @@ ADV_TYPE_SVC_DATA = 0x16 ADV_TYPE_MANUFACTURER = 0xFF -ManufacturerId = { - 0x0006: "Microsoft", - 0x004C: "Apple, Inc.", - 0x0075: "Samsung Electronics Co. Ltd.", - 0x0087: "Garmin International Inc.", - 0x00E0: "Google", - 0x0822: "adafruit industries", - 0xC688: "Logitech, Inc.", -} +ManufacturerIDs = None +ServiceIDs = None + + +def _loadManufacturerIDs(): + """ + Function to load the manufacturer IDs. + """ + global ManufacturerIDs + + idsFile = os.path.join( + os.path.dirname(__file__), "data", "company_identifiers.yaml" + ) + with contextlib.suppress(OSError): + with open(idsFile, "r") as f: + idsDict = yaml.safe_load(f) + + ManufacturerIDs = { + entry["value"]: entry["name"] for entry in idsDict["company_identifiers"] + } + + +def _loadServiceUUIDs(): + """ + Function to load the service UUIDs. + """ + global ServiceIDs + + ServiceIDs = {} + + for uuidFilename in ("member_uuids.yaml", "sdo_uuids.yaml", "service_uuids.yaml"): + uuidFilepath = os.path.join(os.path.dirname(__file__), "data", uuidFilename) + with contextlib.suppress(OSError): + with open(uuidFilepath, "r") as f: + uuidDict = yaml.safe_load(f) + + ServiceIDs.update({u["uuid"]: u["name"] for u in uuidDict["uuids"]}) class BluetoothAdvertisement: @@ -206,34 +238,39 @@ """ Public method to get the service IDs. - @return list of tuples containing the advertised service ID and a - flag indicating a complete ID + @return list of tuples containing the advertised service ID, the associated + service name (if available) and a flag indicating a complete ID @rtype list of tuple of (str, bool) """ + if ServiceIDs is None: + _loadServiceUUIDs() + result = [] for u in self.__decodeField(ADV_TYPE_UUID16_INCOMPLETE): for v in self.__splitBytes(u, 2): - result.append((hex(struct.unpack("<H", v)[0]), False)) + uid = struct.unpack("<H", v)[0] + result.append((hex(uid), ServiceIDs.get(uid, ""), False)) for u in self.__decodeField(ADV_TYPE_UUID16_COMPLETE): for v in self.__splitBytes(u, 2): - result.append((hex(struct.unpack("<H", v)[0]), True)) + uid = struct.unpack("<H", v)[0] + result.append((hex(uid), ServiceIDs.get(uid, ""), False)) for u in self.__decodeField(ADV_TYPE_UUID32_INCOMPLETE): for v in self.__splitBytes(u, 4): - result.append((hex(struct.unpack("<I", v)), False)) + result.append((hex(struct.unpack("<I", v)), "", False)) for u in self.__decodeField(ADV_TYPE_UUID32_COMPLETE): for v in self.__splitBytes(u, 4): - result.append((hex(struct.unpack("<I", v)), True)) + result.append((hex(struct.unpack("<I", v)), "", True)) for u in self.__decodeField(ADV_TYPE_UUID128_INCOMPLETE): for v in self.__splitBytes(u, 16): uid = uuid.UUID(bytes=bytes(reversed(v))) - result.append((str(uid), False)) + result.append((str(uid), "", False)) for u in self.__decodeField(ADV_TYPE_UUID128_COMPLETE): for v in self.__splitBytes(u, 16): uid = uuid.UUID(bytes=bytes(reversed(v))) - result.append((str(uid), True)) + result.append((str(uid), "", True)) return result @@ -250,6 +287,9 @@ name @rtype tuple of (int, bytes, str) """ + if ManufacturerIDs is None: + _loadManufacturerIDs() + result = [] for u in self.__decodeField(ADV_TYPE_MANUFACTURER): if len(u) < 2: @@ -257,7 +297,7 @@ m = struct.unpack("<H", u[0:2])[0] if filterId is None or m == filterId: - name = ManufacturerId.get(m, "") if withName else None + name = ManufacturerIDs.get(m, "") if withName else None result.append((m, u[2:], name)) return result