Coverage for src / bluetooth_sig / types / registry / common.py: 96%

82 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +0000

1"""Core common types for Bluetooth SIG registry data structures.""" 

2 

3from __future__ import annotations 

4 

5import msgspec 

6 

7from bluetooth_sig.types.registry.gss_characteristic import FieldSpec 

8from bluetooth_sig.types.uuid import BluetoothUUID 

9 

10 

11class FieldInfo(msgspec.Struct, frozen=True, kw_only=True): 

12 """Field-related metadata from YAML.""" 

13 

14 name: str | None = None 

15 data_type: str | None = None 

16 field_size: str | None = None 

17 description: str | None = None 

18 

19 

20class UnitMetadata(msgspec.Struct, frozen=True, kw_only=True): 

21 """Unit-related metadata from characteristic YAML specifications. 

22 

23 This is embedded metadata within characteristic specs, distinct from 

24 the Units registry which uses UUID-based entries. 

25 

26 Attributes: 

27 unit_id: Full SIG unit identifier (e.g. ``org.bluetooth.unit.period.beats_per_minute``). 

28 unit_symbol: Short SI symbol (e.g. ``'bpm'``, ``'°C'``). 

29 unit_name: Human-readable long-form name (e.g. ``'beats per minute'``). 

30 base_unit: Base unit identifier. 

31 resolution_text: Resolution description from the GSS spec. 

32 

33 """ 

34 

35 unit_id: str | None = None 

36 unit_symbol: str | None = None 

37 unit_name: str | None = None 

38 base_unit: str | None = None 

39 resolution_text: str | None = None 

40 

41 

42class CharacteristicSpec(msgspec.Struct, kw_only=True): 

43 """Characteristic specification from cross-file YAML references.""" 

44 

45 uuid: BluetoothUUID 

46 name: str 

47 field_info: FieldInfo = msgspec.field(default_factory=FieldInfo) 

48 unit_info: UnitMetadata = msgspec.field(default_factory=UnitMetadata) 

49 description: str | None = None 

50 structure: list[FieldSpec] = msgspec.field(default_factory=list) 

51 

52 @property 

53 def data_type(self) -> str | None: 

54 """Get data type from field info.""" 

55 return self.field_info.data_type if self.field_info else None 

56 

57 @property 

58 def field_size(self) -> str | None: 

59 """Get field size from field info.""" 

60 return self.field_info.field_size if self.field_info else None 

61 

62 @property 

63 def unit_id(self) -> str | None: 

64 """Get unit ID from unit info.""" 

65 return self.unit_info.unit_id if self.unit_info else None 

66 

67 @property 

68 def unit_symbol(self) -> str | None: 

69 """Get unit symbol from unit info.""" 

70 return self.unit_info.unit_symbol if self.unit_info else None 

71 

72 @property 

73 def unit_name(self) -> str | None: 

74 """Get human-readable unit name from unit info.""" 

75 return self.unit_info.unit_name if self.unit_info else None 

76 

77 @property 

78 def base_unit(self) -> str | None: 

79 """Get base unit from unit info.""" 

80 return self.unit_info.base_unit if self.unit_info else None 

81 

82 @property 

83 def resolution_text(self) -> str | None: 

84 """Get resolution text from unit info.""" 

85 return self.unit_info.resolution_text if self.unit_info else None 

86 

87 

88class BaseUuidInfo(msgspec.Struct, frozen=True, kw_only=True): 

89 """Minimal base info for all UUID-based registry entries. 

90 

91 Child classes should add an id field as needed (required or optional). 

92 """ 

93 

94 uuid: BluetoothUUID 

95 name: str 

96 

97 

98def generate_basic_aliases(info: BaseUuidInfo) -> set[str]: 

99 """Generate a small set of common alias keys for a BaseUuidInfo. 

100 

101 Domain-specific heuristics remain the responsibility of the registry. 

102 """ 

103 aliases: set[str] = set() 

104 if info.name: 

105 aliases.add(info.name.lower()) 

106 aliases.add(info.name.replace("_", " ").replace("-", " ").title()) 

107 id_val = getattr(info, "id", None) 

108 if id_val: 

109 aliases.add(id_val) 

110 return {a for a in aliases if a and a.strip()} 

111 

112 

113# Generic reusable Info classes for common YAML patterns 

114 

115 

116class UuidIdInfo(BaseUuidInfo, frozen=True, kw_only=True): 

117 """Standard registry info for simple UUID-based registries with org.bluetooth ID. 

118 

119 Extends BaseUuidInfo (uuid, name) with an id field for org.bluetooth identifiers. 

120 Used by registries with uuid, name, id fields: 

121 - browse_group_identifiers (uuid, name, id) 

122 - declarations (uuid, name, id) 

123 ... 

124 """ 

125 

126 id: str 

127 

128 

129class ValueNameInfo(msgspec.Struct, frozen=True, kw_only=True): 

130 """Generic info for registries with value and name fields. 

131 

132 Used by: coding_format, core_version, diacs, mws_channel_type, 

133 namespace, namespaces, pcm_data_format, transport_layers, uri_schemes, 

134 company_identifiers, and many others. 

135 """ 

136 

137 value: int 

138 name: str 

139 

140 @property 

141 def bit(self) -> int: 

142 """Alias for value when used as bit position.""" 

143 return self.value 

144 

145 

146class ValueNameReferenceInfo(msgspec.Struct, frozen=True, kw_only=True): 

147 """Generic info for registries with value, name, and reference fields. 

148 

149 Used by: ad_types and similar registries with specification references. 

150 """ 

151 

152 value: int 

153 name: str 

154 reference: str 

155 

156 

157class NameValueInfo(msgspec.Struct, frozen=True, kw_only=True): 

158 """Generic info for registries with name and value fields (reversed order). 

159 

160 Used by: psm and similar registries where name comes before numeric value. 

161 """ 

162 

163 name: str 

164 value: int 

165 

166 @property 

167 def psm(self) -> int: 

168 """Alias for value when used as PSM.""" 

169 return self.value 

170 

171 

172class KeyNameInfo(msgspec.Struct, frozen=True, kw_only=True): 

173 """Generic info for registries with key and name fields. 

174 

175 Used by: security_keyIDs and similar registries with non-numeric keys. 

176 """ 

177 

178 key: str 

179 name: str 

180 

181 

182class NameUuidTypeInfo(BaseUuidInfo, frozen=True, kw_only=True): 

183 """Generic info for registries with name, uuid, and type fields. 

184 

185 Used by: mesh model UUIDs and similar registries with type classification. 

186 Extends BaseUuidInfo to inherit uuid and name fields. 

187 """ 

188 

189 type: str 

190 

191 

192class NameOpcodeTypeInfo(msgspec.Struct, frozen=True, kw_only=True): 

193 """Generic info for registries with name, opcode, and type fields. 

194 

195 Used by: mesh opcodes and similar registries with opcode classification. 

196 """ 

197 

198 name: str 

199 opcode: int 

200 type: str