Coverage for src / bluetooth_sig / types / registry / common.py: 97%
78 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
1"""Core common types for Bluetooth SIG registry data structures."""
3from __future__ import annotations
5import msgspec
7from bluetooth_sig.types.registry.gss_characteristic import FieldSpec
8from bluetooth_sig.types.uuid import BluetoothUUID
11class FieldInfo(msgspec.Struct, frozen=True, kw_only=True):
12 """Field-related metadata from YAML."""
14 name: str | None = None
15 data_type: str | None = None
16 field_size: str | None = None
17 description: str | None = None
20class UnitMetadata(msgspec.Struct, frozen=True, kw_only=True):
21 """Unit-related metadata from characteristic YAML specifications.
23 This is embedded metadata within characteristic specs, distinct from
24 the Units registry which uses UUID-based entries.
25 """
27 unit_id: str | None = None
28 unit_symbol: str | None = None
29 base_unit: str | None = None
30 resolution_text: str | None = None
33class CharacteristicSpec(msgspec.Struct, kw_only=True):
34 """Characteristic specification from cross-file YAML references."""
36 uuid: BluetoothUUID
37 name: str
38 field_info: FieldInfo = msgspec.field(default_factory=FieldInfo)
39 unit_info: UnitMetadata = msgspec.field(default_factory=UnitMetadata)
40 description: str | None = None
41 structure: list[FieldSpec] = msgspec.field(default_factory=list)
43 @property
44 def data_type(self) -> str | None:
45 """Get data type from field info."""
46 return self.field_info.data_type if self.field_info else None
48 @property
49 def field_size(self) -> str | None:
50 """Get field size from field info."""
51 return self.field_info.field_size if self.field_info else None
53 @property
54 def unit_id(self) -> str | None:
55 """Get unit ID from unit info."""
56 return self.unit_info.unit_id if self.unit_info else None
58 @property
59 def unit_symbol(self) -> str | None:
60 """Get unit symbol from unit info."""
61 return self.unit_info.unit_symbol if self.unit_info else None
63 @property
64 def base_unit(self) -> str | None:
65 """Get base unit from unit info."""
66 return self.unit_info.base_unit if self.unit_info else None
68 @property
69 def resolution_text(self) -> str | None:
70 """Get resolution text from unit info."""
71 return self.unit_info.resolution_text if self.unit_info else None
74class BaseUuidInfo(msgspec.Struct, frozen=True, kw_only=True):
75 """Minimal base info for all UUID-based registry entries.
77 Child classes should add an id field as needed (required or optional).
78 """
80 uuid: BluetoothUUID
81 name: str
84def generate_basic_aliases(info: BaseUuidInfo) -> set[str]:
85 """Generate a small set of common alias keys for a BaseUuidInfo.
87 Domain-specific heuristics remain the responsibility of the registry.
88 """
89 aliases: set[str] = set()
90 if info.name:
91 aliases.add(info.name.lower())
92 aliases.add(info.name.replace("_", " ").replace("-", " ").title())
93 id_val = getattr(info, "id", None)
94 if id_val:
95 aliases.add(id_val)
96 return {a for a in aliases if a and a.strip()}
99# Generic reusable Info classes for common YAML patterns
102class UuidIdInfo(BaseUuidInfo, frozen=True, kw_only=True):
103 """Standard registry info for simple UUID-based registries with org.bluetooth ID.
105 Extends BaseUuidInfo (uuid, name) with an id field for org.bluetooth identifiers.
106 Used by registries with uuid, name, id fields:
107 - browse_group_identifiers (uuid, name, id)
108 - declarations (uuid, name, id)
109 ...
110 """
112 id: str
115class ValueNameInfo(msgspec.Struct, frozen=True, kw_only=True):
116 """Generic info for registries with value and name fields.
118 Used by: coding_format, core_version, diacs, mws_channel_type,
119 namespace, namespaces, pcm_data_format, transport_layers, uri_schemes,
120 company_identifiers, and many others.
121 """
123 value: int
124 name: str
126 @property
127 def bit(self) -> int:
128 """Alias for value when used as bit position."""
129 return self.value
132class ValueNameReferenceInfo(msgspec.Struct, frozen=True, kw_only=True):
133 """Generic info for registries with value, name, and reference fields.
135 Used by: ad_types and similar registries with specification references.
136 """
138 value: int
139 name: str
140 reference: str
143class NameValueInfo(msgspec.Struct, frozen=True, kw_only=True):
144 """Generic info for registries with name and value fields (reversed order).
146 Used by: psm and similar registries where name comes before numeric value.
147 """
149 name: str
150 value: int
152 @property
153 def psm(self) -> int:
154 """Alias for value when used as PSM."""
155 return self.value
158class KeyNameInfo(msgspec.Struct, frozen=True, kw_only=True):
159 """Generic info for registries with key and name fields.
161 Used by: security_keyIDs and similar registries with non-numeric keys.
162 """
164 key: str
165 name: str
168class NameUuidTypeInfo(BaseUuidInfo, frozen=True, kw_only=True):
169 """Generic info for registries with name, uuid, and type fields.
171 Used by: mesh model UUIDs and similar registries with type classification.
172 Extends BaseUuidInfo to inherit uuid and name fields.
173 """
175 type: str
178class NameOpcodeTypeInfo(msgspec.Struct, frozen=True, kw_only=True):
179 """Generic info for registries with name, opcode, and type fields.
181 Used by: mesh opcodes and similar registries with opcode classification.
182 """
184 name: str
185 opcode: int
186 type: str