Coverage for src / bluetooth_sig / gatt / characteristics / templates / numeric.py: 75%
133 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-18 11:17 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-18 11:17 +0000
1"""Basic integer templates for unsigned and signed integer parsing.
3Covers Uint8, Sint8, Uint16, Sint16, Uint24, Uint32, Uint48 templates.
4"""
6from __future__ import annotations
8from ...constants import (
9 SINT8_MAX,
10 SINT8_MIN,
11 SINT16_MAX,
12 SINT16_MIN,
13 UINT8_MAX,
14 UINT16_MAX,
15 UINT24_MAX,
16 UINT32_MAX,
17 UINT48_MAX,
18)
19from ...context import CharacteristicContext
20from ...exceptions import InsufficientDataError
21from ..utils.extractors import (
22 SINT8,
23 SINT16,
24 UINT8,
25 UINT16,
26 UINT24,
27 UINT32,
28 UINT48,
29 RawExtractor,
30)
31from ..utils.translators import (
32 IDENTITY,
33 ValueTranslator,
34)
35from .base import CodingTemplate
38class Uint8Template(CodingTemplate[int]):
39 """Template for 8-bit unsigned integer parsing (0-255)."""
41 @property
42 def data_size(self) -> int:
43 """Size: 1 byte."""
44 return 1
46 @property
47 def extractor(self) -> RawExtractor:
48 """Get uint8 extractor."""
49 return UINT8
51 @property
52 def translator(self) -> ValueTranslator[int]:
53 """Return identity translator for no scaling."""
54 return IDENTITY
56 def decode_value(
57 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
58 ) -> int:
59 """Parse 8-bit unsigned integer."""
60 if validate and len(data) < offset + 1:
61 raise InsufficientDataError("uint8", data[offset:], 1)
62 return self.extractor.extract(data, offset)
64 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
65 """Encode uint8 value to bytes."""
66 if validate and not 0 <= value <= UINT8_MAX:
67 raise ValueError(f"Value {value} out of range for uint8 (0-{UINT8_MAX})")
68 return self.extractor.pack(value)
71class Sint8Template(CodingTemplate[int]):
72 """Template for 8-bit signed integer parsing (-128 to 127)."""
74 @property
75 def data_size(self) -> int:
76 """Size: 1 byte."""
77 return 1
79 @property
80 def extractor(self) -> RawExtractor:
81 """Get sint8 extractor."""
82 return SINT8
84 @property
85 def translator(self) -> ValueTranslator[int]:
86 """Return identity translator for no scaling."""
87 return IDENTITY
89 def decode_value(
90 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
91 ) -> int:
92 """Parse 8-bit signed integer."""
93 if validate and len(data) < offset + 1:
94 raise InsufficientDataError("sint8", data[offset:], 1)
95 return self.extractor.extract(data, offset)
97 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
98 """Encode sint8 value to bytes."""
99 if validate and not SINT8_MIN <= value <= SINT8_MAX:
100 raise ValueError(f"Value {value} out of range for sint8 ({SINT8_MIN} to {SINT8_MAX})")
101 return self.extractor.pack(value)
104class Uint16Template(CodingTemplate[int]):
105 """Template for 16-bit unsigned integer parsing (0-65535)."""
107 @property
108 def data_size(self) -> int:
109 """Size: 2 bytes."""
110 return 2
112 @property
113 def extractor(self) -> RawExtractor:
114 """Get uint16 extractor."""
115 return UINT16
117 @property
118 def translator(self) -> ValueTranslator[int]:
119 """Return identity translator for no scaling."""
120 return IDENTITY
122 def decode_value(
123 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
124 ) -> int:
125 """Parse 16-bit unsigned integer."""
126 if validate and len(data) < offset + 2:
127 raise InsufficientDataError("uint16", data[offset:], 2)
128 return self.extractor.extract(data, offset)
130 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
131 """Encode uint16 value to bytes."""
132 if validate and not 0 <= value <= UINT16_MAX:
133 raise ValueError(f"Value {value} out of range for uint16 (0-{UINT16_MAX})")
134 return self.extractor.pack(value)
137class Sint16Template(CodingTemplate[int]):
138 """Template for 16-bit signed integer parsing (-32768 to 32767)."""
140 @property
141 def data_size(self) -> int:
142 """Size: 2 bytes."""
143 return 2
145 @property
146 def extractor(self) -> RawExtractor:
147 """Get sint16 extractor."""
148 return SINT16
150 @property
151 def translator(self) -> ValueTranslator[int]:
152 """Return identity translator for no scaling."""
153 return IDENTITY
155 def decode_value(
156 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
157 ) -> int:
158 """Parse 16-bit signed integer."""
159 if validate and len(data) < offset + 2:
160 raise InsufficientDataError("sint16", data[offset:], 2)
161 return self.extractor.extract(data, offset)
163 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
164 """Encode sint16 value to bytes."""
165 if validate and not SINT16_MIN <= value <= SINT16_MAX:
166 raise ValueError(f"Value {value} out of range for sint16 ({SINT16_MIN} to {SINT16_MAX})")
167 return self.extractor.pack(value)
170class Uint24Template(CodingTemplate[int]):
171 """Template for 24-bit unsigned integer parsing (0-16777215)."""
173 @property
174 def data_size(self) -> int:
175 """Size: 3 bytes."""
176 return 3
178 @property
179 def extractor(self) -> RawExtractor:
180 """Get uint24 extractor."""
181 return UINT24
183 @property
184 def translator(self) -> ValueTranslator[int]:
185 """Return identity translator for no scaling."""
186 return IDENTITY
188 def decode_value(
189 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
190 ) -> int:
191 """Parse 24-bit unsigned integer."""
192 if validate and len(data) < offset + 3:
193 raise InsufficientDataError("uint24", data[offset:], 3)
194 return self.extractor.extract(data, offset)
196 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
197 """Encode uint24 value to bytes."""
198 if validate and not 0 <= value <= UINT24_MAX:
199 raise ValueError(f"Value {value} out of range for uint24 (0-{UINT24_MAX})")
200 return self.extractor.pack(value)
203class Uint32Template(CodingTemplate[int]):
204 """Template for 32-bit unsigned integer parsing."""
206 @property
207 def data_size(self) -> int:
208 """Size: 4 bytes."""
209 return 4
211 @property
212 def extractor(self) -> RawExtractor:
213 """Get uint32 extractor."""
214 return UINT32
216 @property
217 def translator(self) -> ValueTranslator[int]:
218 """Return identity translator for no scaling."""
219 return IDENTITY
221 def decode_value(
222 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
223 ) -> int:
224 """Parse 32-bit unsigned integer."""
225 if validate and len(data) < offset + 4:
226 raise InsufficientDataError("uint32", data[offset:], 4)
227 return self.extractor.extract(data, offset)
229 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
230 """Encode uint32 value to bytes."""
231 if validate and not 0 <= value <= UINT32_MAX:
232 raise ValueError(f"Value {value} out of range for uint32 (0-{UINT32_MAX})")
233 return self.extractor.pack(value)
236class Uint48Template(CodingTemplate[int]):
237 """Template for 48-bit unsigned integer parsing (0-281474976710655)."""
239 @property
240 def data_size(self) -> int:
241 """Size: 6 bytes."""
242 return 6
244 @property
245 def extractor(self) -> RawExtractor:
246 """Get uint48 extractor."""
247 return UINT48
249 @property
250 def translator(self) -> ValueTranslator[int]:
251 """Return identity translator for no scaling."""
252 return IDENTITY
254 def decode_value(
255 self, data: bytearray, offset: int = 0, ctx: CharacteristicContext | None = None, *, validate: bool = True
256 ) -> int:
257 """Parse 48-bit unsigned integer."""
258 if validate and len(data) < offset + 6:
259 raise InsufficientDataError("uint48", data[offset:], 6)
260 return self.extractor.extract(data, offset)
262 def encode_value(self, value: int, *, validate: bool = True) -> bytearray:
263 """Encode uint48 value to bytes."""
264 if validate and not 0 <= value <= UINT48_MAX:
265 raise ValueError(f"Value {value} out of range for uint48 (0-{UINT48_MAX})")
266 return self.extractor.pack(value)