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

38 statements  

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

1"""Physical Activity Current Session characteristic (0x2B44). 

2 

3Contains current session data including activity type and session start indicator. 

4 

5References: 

6 Bluetooth SIG Physical Activity Monitor Service 1.0 

7""" 

8 

9from __future__ import annotations 

10 

11from enum import IntEnum, IntFlag 

12 

13import msgspec 

14 

15from ..context import CharacteristicContext 

16from .base import BaseCharacteristic 

17from .utils import DataParser 

18 

19 

20class PAMSessionFlags(IntFlag): 

21 """Physical Activity Current Session flags.""" 

22 

23 SESSION_ACTIVE = 0x01 

24 ACTIVITY_TYPE_PRESENT = 0x02 

25 

26 

27class PAMActivityType(IntEnum): 

28 """Physical Activity Monitor activity types.""" 

29 

30 NO_ACTIVITY = 0x00 

31 WALKING = 0x01 

32 RUNNING = 0x02 

33 CYCLING = 0x03 

34 SWIMMING = 0x04 

35 GENERIC = 0xFF 

36 

37 

38_ACTIVITY_TYPE_MINIMUM_LENGTH = 4 

39 

40 

41class PhysicalActivityCurrentSessionData(msgspec.Struct, frozen=True, kw_only=True): 

42 """Parsed data from Physical Activity Current Session characteristic. 

43 

44 Attributes: 

45 flags: Session presence flags. 

46 session_id: Session identifier. 

47 activity_type: Type of physical activity. None if not present. 

48 

49 """ 

50 

51 flags: PAMSessionFlags 

52 session_id: int 

53 activity_type: PAMActivityType | None = None 

54 

55 

56class PhysicalActivityCurrentSessionCharacteristic(BaseCharacteristic[PhysicalActivityCurrentSessionData]): 

57 """Physical Activity Current Session characteristic (0x2B44). 

58 

59 org.bluetooth.characteristic.physical_activity_current_session 

60 

61 Reports the current session information for the Physical Activity Monitor. 

62 """ 

63 

64 min_length = 3 # flags(1) + session_id(2) 

65 allow_variable_length = True 

66 

67 def _decode_value( 

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

69 ) -> PhysicalActivityCurrentSessionData: 

70 """Parse Physical Activity Current Session data. 

71 

72 Format: Flags (uint8) + SessionID (uint16 LE) + [ActivityType (uint8)]. 

73 """ 

74 flags = PAMSessionFlags(DataParser.parse_int8(data, 0, signed=False)) 

75 session_id = DataParser.parse_int16(data, 1, signed=False) 

76 

77 activity_type: PAMActivityType | None = None 

78 if flags & PAMSessionFlags.ACTIVITY_TYPE_PRESENT and len(data) >= _ACTIVITY_TYPE_MINIMUM_LENGTH: 

79 activity_type = PAMActivityType(DataParser.parse_int8(data, 3, signed=False)) 

80 

81 return PhysicalActivityCurrentSessionData( 

82 flags=flags, 

83 session_id=session_id, 

84 activity_type=activity_type, 

85 ) 

86 

87 def _encode_value(self, data: PhysicalActivityCurrentSessionData) -> bytearray: 

88 """Encode Physical Activity Current Session data.""" 

89 result = bytearray() 

90 result.extend(DataParser.encode_int8(int(data.flags), signed=False)) 

91 result.extend(DataParser.encode_int16(data.session_id, signed=False)) 

92 if data.activity_type is not None: 

93 result.extend(DataParser.encode_int8(int(data.activity_type), signed=False)) 

94 return result