Coverage for src / bluetooth_sig / gatt / characteristics / esl_address.py: 100%

22 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-03 16:41 +0000

1"""ESL Address characteristic implementation. 

2 

3Per ESL Service v1.0 §3.1.1, the ESL Address is a 16-bit value: 

4 - Bits 0-7: ESL_ID (8-bit, range 0x00-0xFE; 0xFF = Broadcast Address) 

5 - Bits 8-14: Group_ID (7-bit, range 0-127) 

6 - Bit 15: RFU (Reserved for Future Use) 

7""" 

8 

9from __future__ import annotations 

10 

11import msgspec 

12 

13from ...types.gatt_enums import CharacteristicRole 

14from ..context import CharacteristicContext 

15from .base import BaseCharacteristic 

16from .utils import DataParser 

17 

18ESL_ID_MASK = 0x00FF 

19GROUP_ID_MASK = 0x7F00 

20GROUP_ID_SHIFT = 8 

21 

22 

23class ESLAddressData(msgspec.Struct, frozen=True, kw_only=True): 

24 """Parsed ESL Address fields. 

25 

26 Attributes: 

27 esl_id: ESL identifier (bits 0-7, range 0x00-0xFF). 

28 group_id: Group identifier (bits 8-14, range 0-127). 

29 """ 

30 

31 esl_id: int 

32 group_id: int 

33 

34 

35class ESLAddressCharacteristic(BaseCharacteristic[ESLAddressData]): 

36 """ESL Address characteristic (0x2BF6). 

37 

38 org.bluetooth.characteristic.esl_address 

39 

40 A 16-bit ESL address where bits 0-7 are the ESL ID, bits 8-14 are 

41 the Group ID, and bit 15 is reserved for future use (RFU). 

42 """ 

43 

44 _manual_role = CharacteristicRole.INFO 

45 expected_length: int = 2 

46 min_length: int = 2 

47 

48 def _decode_value( 

49 self, 

50 data: bytearray, 

51 ctx: CharacteristicContext | None = None, 

52 *, 

53 validate: bool = True, 

54 ) -> ESLAddressData: 

55 """Parse ESL address into ESL_ID and Group_ID fields. 

56 

57 Args: 

58 data: Raw bytes (2 bytes, little-endian uint16). 

59 ctx: Optional CharacteristicContext. 

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

61 

62 Returns: 

63 ESLAddressData with esl_id and group_id. 

64 

65 """ 

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

67 return ESLAddressData( 

68 esl_id=raw & ESL_ID_MASK, 

69 group_id=(raw & GROUP_ID_MASK) >> GROUP_ID_SHIFT, 

70 ) 

71 

72 def _encode_value(self, data: ESLAddressData) -> bytearray: 

73 """Encode ESL address fields to bytes. 

74 

75 Args: 

76 data: ESLAddressData with esl_id and group_id. 

77 

78 Returns: 

79 Encoded bytes (2 bytes, little-endian uint16). 

80 

81 """ 

82 raw = (data.esl_id & ESL_ID_MASK) | ((data.group_id << GROUP_ID_SHIFT) & GROUP_ID_MASK) 

83 return DataParser.encode_int16(raw, signed=False)