Coverage for src / bluetooth_sig / types / scan_interval_window.py: 100%
24 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"""Scan Interval Window characteristic data types."""
3from __future__ import annotations
5import msgspec
8class ScanIntervalWindowData(msgspec.Struct, frozen=True, kw_only=True):
9 """Scan Interval Window characteristic data.
11 Contains scan interval and scan window parameters for BLE scanning.
13 Attributes:
14 scan_interval: Scan interval in units of 0.625ms (range: 0x0004-0x4000)
15 scan_window: Scan window in units of 0.625ms (range: 0x0004-0x4000)
16 Must be less than or equal to scan_interval
17 """
19 # BLE scan parameter constants (in units of 0.625ms)
20 SCAN_INTERVAL_MIN = 0x0004 # Minimum scan interval (2.5ms)
21 SCAN_INTERVAL_MAX = 0x4000 # Maximum scan interval (10.24s)
22 SCAN_WINDOW_MIN = 0x0004 # Minimum scan window (2.5ms)
23 SCAN_WINDOW_MAX = 0x4000 # Maximum scan window (10.24s)
25 # Time conversion constants
26 UNITS_TO_MS_FACTOR = 0.625 # Convert from 0.625ms units to milliseconds
27 HEX_FORMAT_WIDTH = 6 # Width for hex formatting (#06x)
29 scan_interval: int
30 scan_window: int
32 def __post_init__(self) -> None:
33 """Validate scan parameters after initialization."""
34 # Validate ranges
35 if not self.SCAN_INTERVAL_MIN <= self.scan_interval <= self.SCAN_INTERVAL_MAX:
36 raise ValueError(
37 f"Scan interval {self.scan_interval:#0{self.HEX_FORMAT_WIDTH}x} out of range "
38 f"({self.SCAN_INTERVAL_MIN:#0{self.HEX_FORMAT_WIDTH}x}-{self.SCAN_INTERVAL_MAX:#0{self.HEX_FORMAT_WIDTH}x})"
39 )
40 if not self.SCAN_WINDOW_MIN <= self.scan_window <= self.SCAN_WINDOW_MAX:
41 raise ValueError(
42 f"Scan window {self.scan_window:#0{self.HEX_FORMAT_WIDTH}x} out of range "
43 f"({self.SCAN_WINDOW_MIN:#0{self.HEX_FORMAT_WIDTH}x}-{self.SCAN_WINDOW_MAX:#0{self.HEX_FORMAT_WIDTH}x})"
44 )
45 if self.scan_window > self.scan_interval:
46 raise ValueError(
47 f"Scan window {self.scan_window:#0{self.HEX_FORMAT_WIDTH}x} must be <= scan interval "
48 f"{self.scan_interval:#0{self.HEX_FORMAT_WIDTH}x}"
49 )
51 @property
52 def scan_interval_ms(self) -> float:
53 """Get scan interval in milliseconds."""
54 return self.scan_interval * self.UNITS_TO_MS_FACTOR
56 @property
57 def scan_window_ms(self) -> float:
58 """Get scan window in milliseconds."""
59 return self.scan_window * self.UNITS_TO_MS_FACTOR