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

51 statements  

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

1"""CardioRespiratory Activity Summary Data characteristic (0x2B3F).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class CardioRespiratorySummaryFlags(IntFlag): 

15 """Flags for CardioRespiratory Activity Summary Data (Table 3.13).""" 

16 

17 TIME_IN_HR_ZONE1_PRESENT = 0x00000001 

18 TIME_IN_HR_ZONE2_PRESENT = 0x00000002 

19 TIME_IN_HR_ZONE3_PRESENT = 0x00000004 

20 TIME_IN_HR_ZONE4_PRESENT = 0x00000008 

21 TIME_IN_HR_ZONE5_PRESENT = 0x00000010 

22 MIN_VO2_MAX_PRESENT = 0x00000020 

23 MAX_VO2_MAX_PRESENT = 0x00000040 

24 AVG_VO2_MAX_PRESENT = 0x00000080 

25 MIN_HEART_RATE_PRESENT = 0x00000100 

26 MAX_HEART_RATE_PRESENT = 0x00000200 

27 AVG_HEART_RATE_PRESENT = 0x00000400 

28 MIN_PULSE_IBI_PRESENT = 0x00000800 

29 MAX_PULSE_IBI_PRESENT = 0x00001000 

30 AVG_PULSE_IBI_PRESENT = 0x00002000 

31 MIN_RESTING_HR_PRESENT = 0x00004000 

32 MAX_RESTING_HR_PRESENT = 0x00008000 

33 AVG_RESTING_HR_PRESENT = 0x00010000 

34 MIN_HRV_PRESENT = 0x00020000 

35 MAX_HRV_PRESENT = 0x00040000 

36 AVG_HRV_PRESENT = 0x00080000 

37 MIN_RESPIRATION_RATE_PRESENT = 0x00100000 

38 MAX_RESPIRATION_RATE_PRESENT = 0x00200000 

39 AVG_RESPIRATION_RATE_PRESENT = 0x00400000 

40 MIN_RESTING_RESP_PRESENT = 0x00800000 

41 MAX_RESTING_RESP_PRESENT = 0x01000000 

42 AVG_RESTING_RESP_PRESENT = 0x02000000 

43 WORN_DURATION_PRESENT = 0x04000000 

44 

45 

46class CardioRespiratoryActivitySummaryData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods 

47 """Parsed data from CardioRespiratory Activity Summary Data. 

48 

49 Contains flags and any additional summary field data as raw bytes. 

50 The flags field indicates which optional fields are present. 

51 """ 

52 

53 flags: CardioRespiratorySummaryFlags 

54 additional_data: bytes = b"" 

55 

56 

57_FLAGS_SIZE = 4 # flags field (uint32) 

58 

59 

60class CardioRespiratoryActivitySummaryDataCharacteristic( 

61 BaseCharacteristic[CardioRespiratoryActivitySummaryData], 

62): 

63 """CardioRespiratory Activity Summary Data characteristic (0x2B3F). 

64 

65 org.bluetooth.characteristic.cardiorespiratory_activity_summary_data 

66 

67 Summary cardiorespiratory activity data from the Physical 

68 Activity Monitor service. Flags indicate which optional summary 

69 fields are present. 

70 """ 

71 

72 _characteristic_name = "CardioRespiratory Activity Summary Data" 

73 min_length = 4 # flags (uint32) 

74 allow_variable_length = True 

75 

76 def _decode_value( 

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

78 ) -> CardioRespiratoryActivitySummaryData: 

79 """Parse CardioRespiratory Activity Summary Data. 

80 

81 Format: flags (uint32) + variable optional fields. 

82 """ 

83 flags = CardioRespiratorySummaryFlags(DataParser.parse_int32(data, 0, signed=False)) 

84 additional_data = bytes(data[_FLAGS_SIZE:]) if len(data) > _FLAGS_SIZE else b"" 

85 

86 return CardioRespiratoryActivitySummaryData( 

87 flags=flags, 

88 additional_data=additional_data, 

89 ) 

90 

91 def _encode_value(self, data: CardioRespiratoryActivitySummaryData) -> bytearray: 

92 """Encode CardioRespiratory Activity Summary Data to bytes.""" 

93 result = bytearray() 

94 result += DataParser.encode_int32(int(data.flags), signed=False) 

95 result += bytearray(data.additional_data) 

96 return result