Coverage for src / bluetooth_sig / types / data_types.py: 95%

37 statements  

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

1"""Data types for Bluetooth SIG standards.""" 

2 

3from __future__ import annotations 

4 

5from datetime import datetime 

6 

7import msgspec 

8 

9from .base_types import SIGInfo 

10 

11 

12class ParseFieldError(msgspec.Struct, frozen=True, kw_only=True): 

13 """Represents a field-level parsing error with diagnostic information. 

14 

15 This provides structured error information similar to Pydantic's validation 

16 errors, making it easier to debug which specific field failed and why. 

17 

18 Attributes: 

19 field: Name of the field that failed (e.g., "temperature", "flags") 

20 reason: Human-readable description of why parsing failed 

21 offset: Optional byte offset where the field starts in raw data 

22 raw_slice: Optional raw bytes that were being parsed when error occurred 

23 

24 """ 

25 

26 field: str 

27 reason: str 

28 offset: int | None = None 

29 raw_slice: bytes | None = None 

30 

31 

32class DateData(msgspec.Struct, frozen=True, kw_only=True): 

33 """Shared data type for date values with year, month, and day fields.""" 

34 

35 year: int 

36 month: int 

37 day: int 

38 

39 def to_datetime(self) -> datetime: 

40 """Convert to Python datetime (time components set to 00:00:00). 

41 

42 Returns: 

43 datetime object with date components 

44 """ 

45 return datetime(self.year, self.month, self.day) 

46 

47 

48class CharacteristicInfo(SIGInfo): 

49 """Information about a Bluetooth characteristic from SIG/YAML specifications. 

50 

51 This contains only static metadata resolved from YAML or SIG specs. 

52 Runtime properties (read/write/notify capabilities) are stored separately 

53 on the BaseCharacteristic instance as they're discovered from the actual device. 

54 """ 

55 

56 python_type: type | str | None = None 

57 is_bitfield: bool = False 

58 unit: str = "" 

59 

60 

61class ServiceInfo(SIGInfo): 

62 """Information about a Bluetooth service.""" 

63 

64 characteristics: list[CharacteristicInfo] = msgspec.field(default_factory=list[CharacteristicInfo]) 

65 

66 

67class ValidationAccumulator: 

68 """Result of characteristic data validation with error/warning accumulation. 

69 

70 Used during parsing to accumulate validation issues from multiple validation steps. 

71 Provides methods to add errors/warnings and check overall validity. 

72 """ 

73 

74 def __init__(self) -> None: 

75 """Initialize empty validation result.""" 

76 self.errors: list[str] = [] 

77 self.warnings: list[str] = [] 

78 

79 def add_error(self, message: str) -> None: 

80 """Add a validation error message. 

81 

82 Args: 

83 message: Error message to add 

84 """ 

85 self.errors.append(message) 

86 

87 def add_warning(self, message: str) -> None: 

88 """Add a validation warning message. 

89 

90 Args: 

91 message: Warning message to add 

92 """ 

93 self.warnings.append(message) 

94 

95 @property 

96 def valid(self) -> bool: 

97 """Check if validation passed (no errors). 

98 

99 Returns: 

100 True if no errors, False otherwise 

101 """ 

102 return len(self.errors) == 0 

103 

104 

105class ValidationResult(msgspec.Struct, frozen=True, kw_only=True): 

106 """Summary of validation results for external API consumption. 

107 

108 Lightweight validation result for API responses, not for accumulation. 

109 """ 

110 

111 is_valid: bool 

112 actual_length: int 

113 expected_length: int | None = None 

114 error_message: str = ""