Coverage for src/bluetooth_sig/registry/service_classes.py: 75%
48 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""Service classes registry for Bluetooth SIG service class definitions."""
3from __future__ import annotations
5import msgspec
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
12class ServiceClassInfo(msgspec.Struct, frozen=True, kw_only=True):
13 """Information about a Bluetooth SIG service class."""
15 uuid: BluetoothUUID
16 name: str
17 id: str
20class ServiceClassesRegistry(BaseRegistry[ServiceClassInfo]):
21 """Registry for Bluetooth SIG service class definitions."""
23 def __init__(self) -> None:
24 """Initialize the service classes registry."""
25 super().__init__()
26 self._service_classes: dict[str, ServiceClassInfo] = {}
27 self._name_to_info: dict[str, ServiceClassInfo] = {}
28 self._id_to_info: dict[str, ServiceClassInfo] = {}
29 self._load_service_classes()
31 def _load_service_classes(self) -> None:
32 """Load service classes from the Bluetooth SIG YAML file."""
33 base_path = find_bluetooth_sig_path()
34 if not base_path:
35 return
37 # Load service class UUIDs
38 service_classes_yaml = base_path / "service_classes.yaml"
39 if service_classes_yaml.exists():
40 for item in load_yaml_uuids(service_classes_yaml):
41 try:
42 uuid = parse_bluetooth_uuid(item["uuid"])
43 name = item["name"]
44 service_class_id = item["id"]
46 info = ServiceClassInfo(uuid=uuid, name=name, id=service_class_id)
48 # Store by UUID string for fast lookup
49 self._service_classes[uuid.short_form.upper()] = info
50 self._name_to_info[name.lower()] = info
51 self._id_to_info[service_class_id] = info
53 except (KeyError, ValueError):
54 # Skip malformed entries
55 continue
57 def get_service_class_info(self, uuid: str | int | BluetoothUUID) -> ServiceClassInfo | None:
58 """Get service class information by UUID.
60 Args:
61 uuid: The UUID to look up (string, int, or BluetoothUUID)
63 Returns:
64 ServiceClassInfo if found, None otherwise
65 """
66 try:
67 bt_uuid = parse_bluetooth_uuid(uuid)
68 return self._service_classes.get(bt_uuid.short_form.upper())
69 except ValueError:
70 return None
72 def get_service_class_info_by_name(self, name: str) -> ServiceClassInfo | None:
73 """Get service class information by name (case insensitive).
75 Args:
76 name: The service class name to look up
78 Returns:
79 ServiceClassInfo if found, None otherwise
80 """
81 return self._name_to_info.get(name.lower())
83 def get_service_class_info_by_id(self, service_class_id: str) -> ServiceClassInfo | None:
84 """Get service class information by service class ID.
86 Args:
87 service_class_id: The service class ID to look up
89 Returns:
90 ServiceClassInfo if found, None otherwise
91 """
92 return self._id_to_info.get(service_class_id)
94 def is_service_class_uuid(self, uuid: str | int | BluetoothUUID) -> bool:
95 """Check if a UUID corresponds to a known service class.
97 Args:
98 uuid: The UUID to check
100 Returns:
101 True if the UUID is a known service class, False otherwise
102 """
103 return self.get_service_class_info(uuid) is not None
105 def get_all_service_classes(self) -> list[ServiceClassInfo]:
106 """Get all service classes in the registry.
108 Returns:
109 List of all ServiceClassInfo objects
110 """
111 return list(self._service_classes.values())
114# Global instance for convenience
115service_classes_registry = ServiceClassesRegistry.get_instance()