Coverage for src/bluetooth_sig/gatt/descriptors/characteristic_presentation_format.py: 100%
66 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"""Characteristic Presentation Format Descriptor implementation."""
3from __future__ import annotations
5from enum import IntEnum
7import msgspec
9from ..characteristics.utils import DataParser
10from .base import BaseDescriptor
13class FormatNamespace(IntEnum):
14 """Format namespace values for Characteristic Presentation Format."""
16 BLUETOOTH_SIG_ASSIGNED_NUMBERS = 0x01
17 RESERVED = 0x02
20class FormatType(IntEnum):
21 """Format type values for Characteristic Presentation Format."""
23 # Common Bluetooth SIG format types
24 BOOLEAN = 0x01
25 UINT2 = 0x02
26 UINT4 = 0x03
27 UINT8 = 0x04
28 UINT12 = 0x05
29 UINT16 = 0x06
30 UINT24 = 0x07
31 UINT32 = 0x08
32 UINT48 = 0x09
33 UINT64 = 0x0A
34 UINT128 = 0x0B
35 SINT8 = 0x0C
36 SINT12 = 0x0D
37 SINT16 = 0x0E
38 SINT24 = 0x0F
39 SINT32 = 0x10
40 SINT48 = 0x11
41 SINT64 = 0x12
42 SINT128 = 0x13
43 FLOAT32 = 0x14
44 FLOAT64 = 0x15
45 SFLOAT = 0x16
46 FLOAT = 0x17
47 DUINT16 = 0x18
48 UTF8S = 0x19
49 UTF16S = 0x1A
50 STRUCT = 0x1B
53class CharacteristicPresentationFormatData(msgspec.Struct, frozen=True, kw_only=True):
54 """Characteristic Presentation Format descriptor data."""
56 format: int
57 exponent: int
58 unit: int
59 namespace: int
60 description: int
63class CharacteristicPresentationFormatDescriptor(BaseDescriptor):
64 """Characteristic Presentation Format Descriptor (0x2904).
66 Describes how characteristic values should be presented to users.
67 Contains format, exponent, unit, namespace, and description information.
68 """
70 def _has_structured_data(self) -> bool:
71 return True
73 def _get_data_format(self) -> str:
74 return "struct"
76 def _parse_descriptor_value(self, data: bytes) -> CharacteristicPresentationFormatData:
77 """Parse Characteristic Presentation Format value.
79 Format: 7 bytes
80 - Format (1 byte): Data type format
81 - Exponent (1 byte): Base 10 exponent (-128 to 127)
82 - Unit (2 bytes): Unit of measurement (little-endian)
83 - Namespace (1 byte): Namespace for description
84 - Description (2 bytes): Description identifier (little-endian)
86 Args:
87 data: Raw bytes (should be 7 bytes)
89 Returns:
90 CharacteristicPresentationFormatData with format information
92 Raises:
93 ValueError: If data is not exactly 7 bytes
94 """
95 if len(data) != 7:
96 raise ValueError(f"Characteristic Presentation Format data must be exactly 7 bytes, got {len(data)}")
98 return CharacteristicPresentationFormatData(
99 format=DataParser.parse_int8(data, offset=0),
100 exponent=DataParser.parse_int8(data, offset=1, signed=True),
101 unit=DataParser.parse_int16(data, offset=2, endian="little"),
102 namespace=DataParser.parse_int8(data, offset=4),
103 description=DataParser.parse_int16(data, offset=5, endian="little"),
104 )
106 def get_format_type(self, data: bytes) -> int:
107 """Get the format type."""
108 parsed = self._parse_descriptor_value(data)
109 return parsed.format
111 def get_exponent(self, data: bytes) -> int:
112 """Get the exponent for scaling."""
113 parsed = self._parse_descriptor_value(data)
114 return parsed.exponent
116 def get_unit(self, data: bytes) -> int:
117 """Get the unit identifier."""
118 parsed = self._parse_descriptor_value(data)
119 return parsed.unit
121 def get_namespace(self, data: bytes) -> int:
122 """Get the namespace identifier."""
123 parsed = self._parse_descriptor_value(data)
124 return parsed.namespace
126 def get_description(self, data: bytes) -> int:
127 """Get the description identifier."""
128 parsed = self._parse_descriptor_value(data)
129 return parsed.description