Coverage for src/bluetooth_sig/gatt/descriptors/report_reference.py: 79%
33 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""Report Reference Descriptor implementation."""
3from __future__ import annotations
5from enum import IntEnum
7import msgspec
9from ..characteristics.utils import DataParser
10from .base import BaseDescriptor
13class ReportType(IntEnum):
14 """Report type values for Report Reference descriptor."""
16 INPUT_REPORT = 0x01
17 OUTPUT_REPORT = 0x02
18 FEATURE_REPORT = 0x03
21class ReportReferenceData(msgspec.Struct, frozen=True, kw_only=True):
22 """Report Reference descriptor data."""
24 report_id: int
25 report_type: int
28class ReportReferenceDescriptor(BaseDescriptor):
29 """Report Reference Descriptor (0x2908).
31 Contains report ID and report type information.
32 Used in HID (Human Interface Device) profiles.
33 """
35 def _has_structured_data(self) -> bool:
36 return True
38 def _get_data_format(self) -> str:
39 return "struct"
41 def _parse_descriptor_value(self, data: bytes) -> ReportReferenceData:
42 """Parse Report Reference value.
44 Format: 2 bytes
45 - Report ID (1 byte)
46 - Report Type (1 byte)
48 Args:
49 data: Raw bytes (should be 2 bytes)
51 Returns:
52 ReportReferenceData with report ID and type
54 Raises:
55 ValueError: If data is not exactly 2 bytes
56 """
57 if len(data) != 2:
58 raise ValueError(f"Report Reference data must be exactly 2 bytes, got {len(data)}")
60 return ReportReferenceData(
61 report_id=DataParser.parse_int8(data, offset=0),
62 report_type=DataParser.parse_int8(data, offset=1),
63 )
65 def get_report_id(self, data: bytes) -> int:
66 """Get the report ID."""
67 parsed = self._parse_descriptor_value(data)
68 return parsed.report_id
70 def get_report_type(self, data: bytes) -> int:
71 """Get the report type."""
72 parsed = self._parse_descriptor_value(data)
73 return parsed.report_type
75 def is_input_report(self, data: bytes) -> bool:
76 """Check if this is an input report."""
77 return self.get_report_type(data) == ReportType.INPUT_REPORT
79 def is_output_report(self, data: bytes) -> bool:
80 """Check if this is an output report."""
81 return self.get_report_type(data) == ReportType.OUTPUT_REPORT
83 def is_feature_report(self, data: bytes) -> bool:
84 """Check if this is a feature report."""
85 return self.get_report_type(data) == ReportType.FEATURE_REPORT