Coverage for src / bluetooth_sig / gatt / characteristics / object_type.py: 97%

32 statements  

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

1"""Object Type characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5from ..context import CharacteristicContext 

6from .base import BaseCharacteristic 

7from .utils import DataParser 

8 

9_UUID_16BIT_BYTES = 2 

10_UUID_128BIT_BYTES = 16 

11_UUID_16BIT_HEX_CHARS = 4 

12_UUID_128BIT_HEX_CHARS = 32 

13 

14 

15class ObjectTypeCharacteristic(BaseCharacteristic[str]): 

16 """Object Type characteristic (0x2ABF). 

17 

18 org.bluetooth.characteristic.object_type 

19 

20 A GATT UUID identifying the type of an object in the Object Transfer 

21 Service (OTS). May be a 16-bit (2 bytes) or 128-bit (16 bytes) UUID. 

22 """ 

23 

24 min_length: int = 2 

25 max_length: int = 16 

26 

27 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True) -> str: 

28 """Parse object type UUID. 

29 

30 Args: 

31 data: Raw bytes (2 or 16 bytes). 

32 ctx: Optional CharacteristicContext. 

33 validate: Whether to validate ranges (default True). 

34 

35 Returns: 

36 UUID as uppercase hex string (e.g. "2AC3" or full 128-bit). 

37 

38 """ 

39 if len(data) == _UUID_16BIT_BYTES: 

40 raw = DataParser.parse_int16(data, 0, signed=False) 

41 return f"{raw:04X}" 

42 

43 # 128-bit UUID: formatted as standard UUID string 

44 # Bytes are little-endian, standard UUID format is big-endian groups 

45 parts = [ 

46 data[3::-1].hex(), # time_low (4 bytes, reversed) 

47 data[5:3:-1].hex(), # time_mid (2 bytes, reversed) 

48 data[7:5:-1].hex(), # time_hi_and_version (2 bytes, reversed) 

49 data[8:10].hex(), # clock_seq (2 bytes, big-endian) 

50 data[10:16].hex(), # node (6 bytes, big-endian) 

51 ] 

52 return "-".join(parts).upper() 

53 

54 def _encode_value(self, data: str) -> bytearray: 

55 """Encode object type UUID to bytes. 

56 

57 Args: 

58 data: UUID as hex string (4 chars for 16-bit, or 

59 standard UUID format for 128-bit). 

60 

61 Returns: 

62 Encoded bytes (2 or 16 bytes). 

63 

64 """ 

65 clean = data.replace("-", "").replace(" ", "").upper() 

66 

67 if len(clean) == _UUID_16BIT_HEX_CHARS: 

68 # 16-bit UUID 

69 value = int(clean, 16) 

70 return DataParser.encode_int16(value, signed=False) 

71 

72 if len(clean) == _UUID_128BIT_HEX_CHARS: 

73 # 128-bit UUID: reverse byte order for BLE little-endian groups 

74 raw = bytes.fromhex(clean) 

75 result = bytearray() 

76 result.extend(raw[0:4][::-1]) # time_low reversed 

77 result.extend(raw[4:6][::-1]) # time_mid reversed 

78 result.extend(raw[6:8][::-1]) # time_hi_and_version reversed 

79 result.extend(raw[8:10]) # clock_seq big-endian 

80 result.extend(raw[10:16]) # node big-endian 

81 return result 

82 

83 raise ValueError(f"UUID must be 4 hex chars (16-bit) or 32 hex chars (128-bit), got {len(clean)} chars")