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

19 statements  

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

1"""Service Changed characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5import msgspec 

6 

7from ..context import CharacteristicContext 

8from .base import BaseCharacteristic 

9from .utils import DataParser 

10 

11 

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

13 """Service Changed characteristic data. 

14 

15 Attributes: 

16 start_handle: Starting handle of the affected service range 

17 end_handle: Ending handle of the affected service range 

18 """ 

19 

20 start_handle: int 

21 end_handle: int 

22 

23 

24class ServiceChangedCharacteristic(BaseCharacteristic[ServiceChangedData]): 

25 """Service Changed characteristic (0x2A05). 

26 

27 org.bluetooth.characteristic.gatt.service_changed 

28 

29 Service Changed characteristic. 

30 """ 

31 

32 expected_length = 4 

33 

34 def _decode_value( 

35 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True 

36 ) -> ServiceChangedData: 

37 """Parse service changed value. 

38 

39 Args: 

40 data: Raw bytearray from BLE characteristic. 

41 ctx: Optional CharacteristicContext providing surrounding context (may be None). 

42 validate: Whether to validate ranges (default True) 

43 

44 Returns: 

45 ServiceChangedData with start_handle and end_handle. 

46 """ 

47 start_handle = DataParser.parse_int16(data, 0, signed=False) 

48 end_handle = DataParser.parse_int16(data, 2, signed=False) 

49 return ServiceChangedData(start_handle=start_handle, end_handle=end_handle) 

50 

51 def _encode_value(self, data: ServiceChangedData) -> bytearray: 

52 """Encode service changed value back to bytes. 

53 

54 Args: 

55 data: ServiceChangedData with start_handle and end_handle 

56 

57 Returns: 

58 Encoded bytes 

59 """ 

60 result = bytearray() 

61 result.extend(DataParser.encode_int16(data.start_handle, signed=False)) 

62 result.extend(DataParser.encode_int16(data.end_handle, signed=False)) 

63 return result