Coverage for src / bluetooth_sig / gatt / characteristics / templates / numeric.py: 75%
151 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""Basic integer templates for unsigned and signed integer parsing.
3Covers Uint8, Sint8, Uint16, Sint16, Uint24, Uint32, Sint32, Uint48 templates.
4"""
6from __future__ import annotations
8from ...constants import (
9 SINT8_MAX,
10 SINT8_MIN,
11 SINT16_MAX,
12 SINT16_MIN,
13 SINT32_MAX,
14 SINT32_MIN,
15 UINT8_MAX,
16 UINT16_MAX,
17 UINT24_MAX,
18 UINT32_MAX,
19 UINT48_MAX,
20)
21from ...context import CharacteristicContext
22from ...exceptions import InsufficientDataError
23from ..utils.extractors import (
24 SINT8,
25 SINT16,
26 SINT32,
27 UINT8,
28 UINT16,
29 UINT24,
30 UINT32,
31 UINT48,
32 RawExtractor,
33)
34from ..utils.translators import (
35 IDENTITY,
36 ValueTranslator,
37)
38from .base import CodingTemplate
41class Uint8Template(CodingTemplate[int]):
42 """Template for 8-bit unsigned integer parsing (0-255)."""
44 @property
45 def data_size(self) -> int:
46 """Size: 1 byte."""
47 return 1
49 @property
50 def extractor(self) -> RawExtractor:
51 """Get uint8 extractor."""
52 return UINT8
54 @property
55 def translator(self) -> ValueTranslator[int]:
56 """Return identity translator for no scaling."""
57 return IDENTITY
59 def decode_value(
60 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
61 ) -> int:
62 """Parse 8-bit unsigned integer."""
63 if validate and len(data) < offset + 1:
64 raise InsufficientDataError("uint8", data[offset:], 1)
65 return self.extractor.extract(data, offset)
67 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
68 """Encode uint8 value to bytes."""
69 if validate and not 0 <= value <= UINT8_MAX:
70 raise ValueError(f"Value {value} out of range for uint8 (0-{UINT8_MAX})")
71 return self.extractor.pack(value)
74class Sint8Template(CodingTemplate[int]):
75 """Template for 8-bit signed integer parsing (-128 to 127)."""
77 @property
78 def data_size(self) -> int:
79 """Size: 1 byte."""
80 return 1
82 @property
83 def extractor(self) -> RawExtractor:
84 """Get sint8 extractor."""
85 return SINT8
87 @property
88 def translator(self) -> ValueTranslator[int]:
89 """Return identity translator for no scaling."""
90 return IDENTITY
92 def decode_value(
93 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
94 ) -> int:
95 """Parse 8-bit signed integer."""
96 if validate and len(data) < offset + 1:
97 raise InsufficientDataError("sint8", data[offset:], 1)
98 return self.extractor.extract(data, offset)
100 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
101 """Encode sint8 value to bytes."""
102 if validate and not SINT8_MIN <= value <= SINT8_MAX:
103 raise ValueError(f"Value {value} out of range for sint8 ({SINT8_MIN} to {SINT8_MAX})")
104 return self.extractor.pack(value)
107class Uint16Template(CodingTemplate[int]):
108 """Template for 16-bit unsigned integer parsing (0-65535)."""
110 @property
111 def data_size(self) -> int:
112 """Size: 2 bytes."""
113 return 2
115 @property
116 def extractor(self) -> RawExtractor:
117 """Get uint16 extractor."""
118 return UINT16
120 @property
121 def translator(self) -> ValueTranslator[int]:
122 """Return identity translator for no scaling."""
123 return IDENTITY
125 def decode_value(
126 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
127 ) -> int:
128 """Parse 16-bit unsigned integer."""
129 if validate and len(data) < offset + 2:
130 raise InsufficientDataError("uint16", data[offset:], 2)
131 return self.extractor.extract(data, offset)
133 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
134 """Encode uint16 value to bytes."""
135 if validate and not 0 <= value <= UINT16_MAX:
136 raise ValueError(f"Value {value} out of range for uint16 (0-{UINT16_MAX})")
137 return self.extractor.pack(value)
140class Sint16Template(CodingTemplate[int]):
141 """Template for 16-bit signed integer parsing (-32768 to 32767)."""
143 @property
144 def data_size(self) -> int:
145 """Size: 2 bytes."""
146 return 2
148 @property
149 def extractor(self) -> RawExtractor:
150 """Get sint16 extractor."""
151 return SINT16
153 @property
154 def translator(self) -> ValueTranslator[int]:
155 """Return identity translator for no scaling."""
156 return IDENTITY
158 def decode_value(
159 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
160 ) -> int:
161 """Parse 16-bit signed integer."""
162 if validate and len(data) < offset + 2:
163 raise InsufficientDataError("sint16", data[offset:], 2)
164 return self.extractor.extract(data, offset)
166 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
167 """Encode sint16 value to bytes."""
168 if validate and not SINT16_MIN <= value <= SINT16_MAX:
169 raise ValueError(f"Value {value} out of range for sint16 ({SINT16_MIN} to {SINT16_MAX})")
170 return self.extractor.pack(value)
173class Uint24Template(CodingTemplate[int]):
174 """Template for 24-bit unsigned integer parsing (0-16777215)."""
176 @property
177 def data_size(self) -> int:
178 """Size: 3 bytes."""
179 return 3
181 @property
182 def extractor(self) -> RawExtractor:
183 """Get uint24 extractor."""
184 return UINT24
186 @property
187 def translator(self) -> ValueTranslator[int]:
188 """Return identity translator for no scaling."""
189 return IDENTITY
191 def decode_value(
192 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
193 ) -> int:
194 """Parse 24-bit unsigned integer."""
195 if validate and len(data) < offset + 3:
196 raise InsufficientDataError("uint24", data[offset:], 3)
197 return self.extractor.extract(data, offset)
199 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
200 """Encode uint24 value to bytes."""
201 if validate and not 0 <= value <= UINT24_MAX:
202 raise ValueError(f"Value {value} out of range for uint24 (0-{UINT24_MAX})")
203 return self.extractor.pack(value)
206class Uint32Template(CodingTemplate[int]):
207 """Template for 32-bit unsigned integer parsing."""
209 @property
210 def data_size(self) -> int:
211 """Size: 4 bytes."""
212 return 4
214 @property
215 def extractor(self) -> RawExtractor:
216 """Get uint32 extractor."""
217 return UINT32
219 @property
220 def translator(self) -> ValueTranslator[int]:
221 """Return identity translator for no scaling."""
222 return IDENTITY
224 def decode_value(
225 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
226 ) -> int:
227 """Parse 32-bit unsigned integer."""
228 if validate and len(data) < offset + 4:
229 raise InsufficientDataError("uint32", data[offset:], 4)
230 return self.extractor.extract(data, offset)
232 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
233 """Encode uint32 value to bytes."""
234 if validate and not 0 <= value <= UINT32_MAX:
235 raise ValueError(f"Value {value} out of range for uint32 (0-{UINT32_MAX})")
236 return self.extractor.pack(value)
239class Sint32Template(CodingTemplate[int]):
240 """Template for 32-bit signed integer parsing."""
242 @property
243 def data_size(self) -> int:
244 """Size: 4 bytes."""
245 return 4
247 @property
248 def extractor(self) -> RawExtractor:
249 """Get sint32 extractor."""
250 return SINT32
252 @property
253 def translator(self) -> ValueTranslator[int]:
254 """Return identity translator for no scaling."""
255 return IDENTITY
257 def decode_value(
258 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
259 ) -> int:
260 """Parse 32-bit signed integer."""
261 if validate and len(data) < offset + 4:
262 raise InsufficientDataError("sint32", data[offset:], 4)
263 return self.extractor.extract(data, offset)
265 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
266 """Encode sint32 value to bytes."""
267 if validate and not SINT32_MIN <= value <= SINT32_MAX:
268 raise ValueError(f"Value {value} out of range for sint32 ({SINT32_MIN} to {SINT32_MAX})")
269 return self.extractor.pack(value)
272class Uint48Template(CodingTemplate[int]):
273 """Template for 48-bit unsigned integer parsing (0-281474976710655)."""
275 @property
276 def data_size(self) -> int:
277 """Size: 6 bytes."""
278 return 6
280 @property
281 def extractor(self) -> RawExtractor:
282 """Get uint48 extractor."""
283 return UINT48
285 @property
286 def translator(self) -> ValueTranslator[int]:
287 """Return identity translator for no scaling."""
288 return IDENTITY
290 def decode_value(
291 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
292 ) -> int:
293 """Parse 48-bit unsigned integer."""
294 if validate and len(data) < offset + 6:
295 raise InsufficientDataError("uint48", data[offset:], 6)
296 return self.extractor.extract(data, offset)
298 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
299 """Encode uint48 value to bytes."""
300 if validate and not 0 <= value <= UINT48_MAX:
301 raise ValueError(f"Value {value} out of range for uint48 (0-{UINT48_MAX})")
302 return self.extractor.pack(value)