Coverage for src/bluetooth_sig/registry/mesh_profiles.py: 75%

48 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 00:10 +0000

1"""Mesh profiles registry for Bluetooth SIG mesh profile definitions.""" 

2 

3from __future__ import annotations 

4 

5import msgspec 

6 

7from bluetooth_sig.registry.base import BaseRegistry 

8from bluetooth_sig.registry.utils import find_bluetooth_sig_path, load_yaml_uuids, parse_bluetooth_uuid 

9from bluetooth_sig.types.uuid import BluetoothUUID 

10 

11 

12class MeshProfileInfo(msgspec.Struct, frozen=True, kw_only=True): 

13 """Information about a Bluetooth SIG mesh profile.""" 

14 

15 uuid: BluetoothUUID 

16 name: str 

17 id: str 

18 

19 

20class MeshProfilesRegistry(BaseRegistry[MeshProfileInfo]): 

21 """Registry for Bluetooth SIG mesh profile definitions.""" 

22 

23 def __init__(self) -> None: 

24 """Initialize the mesh profiles registry.""" 

25 super().__init__() 

26 self._mesh_profiles: dict[str, MeshProfileInfo] = {} 

27 self._name_to_info: dict[str, MeshProfileInfo] = {} 

28 self._id_to_info: dict[str, MeshProfileInfo] = {} 

29 self._load_mesh_profiles() 

30 

31 def _load_mesh_profiles(self) -> None: 

32 """Load mesh profiles from the Bluetooth SIG YAML file.""" 

33 base_path = find_bluetooth_sig_path() 

34 if not base_path: 

35 return 

36 

37 # Load mesh profile UUIDs 

38 mesh_profiles_yaml = base_path / "mesh_profiles.yaml" 

39 if mesh_profiles_yaml.exists(): 

40 for item in load_yaml_uuids(mesh_profiles_yaml): 

41 try: 

42 uuid = parse_bluetooth_uuid(item["uuid"]) 

43 name = item["name"] 

44 mesh_profile_id = item["id"] 

45 

46 info = MeshProfileInfo(uuid=uuid, name=name, id=mesh_profile_id) 

47 

48 # Store by UUID string for fast lookup 

49 self._mesh_profiles[uuid.short_form.upper()] = info 

50 self._name_to_info[name.lower()] = info 

51 self._id_to_info[mesh_profile_id] = info 

52 

53 except (KeyError, ValueError): 

54 # Skip malformed entries 

55 continue 

56 

57 def get_mesh_profile_info(self, uuid: str | int | BluetoothUUID) -> MeshProfileInfo | None: 

58 """Get mesh profile information by UUID. 

59 

60 Args: 

61 uuid: The UUID to look up (string, int, or BluetoothUUID) 

62 

63 Returns: 

64 MeshProfileInfo if found, None otherwise 

65 """ 

66 try: 

67 bt_uuid = parse_bluetooth_uuid(uuid) 

68 return self._mesh_profiles.get(bt_uuid.short_form.upper()) 

69 except ValueError: 

70 return None 

71 

72 def get_mesh_profile_info_by_name(self, name: str) -> MeshProfileInfo | None: 

73 """Get mesh profile information by name (case insensitive). 

74 

75 Args: 

76 name: The mesh profile name to look up 

77 

78 Returns: 

79 MeshProfileInfo if found, None otherwise 

80 """ 

81 return self._name_to_info.get(name.lower()) 

82 

83 def get_mesh_profile_info_by_id(self, mesh_profile_id: str) -> MeshProfileInfo | None: 

84 """Get mesh profile information by mesh profile ID. 

85 

86 Args: 

87 mesh_profile_id: The mesh profile ID to look up 

88 

89 Returns: 

90 MeshProfileInfo if found, None otherwise 

91 """ 

92 return self._id_to_info.get(mesh_profile_id) 

93 

94 def is_mesh_profile_uuid(self, uuid: str | int | BluetoothUUID) -> bool: 

95 """Check if a UUID corresponds to a known mesh profile. 

96 

97 Args: 

98 uuid: The UUID to check 

99 

100 Returns: 

101 True if the UUID is a known mesh profile, False otherwise 

102 """ 

103 return self.get_mesh_profile_info(uuid) is not None 

104 

105 def get_all_mesh_profiles(self) -> list[MeshProfileInfo]: 

106 """Get all mesh profiles in the registry. 

107 

108 Returns: 

109 List of all MeshProfileInfo objects 

110 """ 

111 return list(self._mesh_profiles.values()) 

112 

113 

114# Global instance for convenience 

115mesh_profiles_registry = MeshProfilesRegistry.get_instance()