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

32 statements  

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

1"""CardioRespiratory Activity Instantaneous Data characteristic (0x2B3E).""" 

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 CardioRespiratoryInstantaneousFlags(IntFlag): 

15 """Flags for CardioRespiratory Activity Instantaneous Data (Table 3.11).""" 

16 

17 VO2_MAX_PRESENT = 0x0001 

18 HEART_RATE_PRESENT = 0x0002 

19 PULSE_INTER_BEAT_INTERVAL_PRESENT = 0x0004 

20 RESTING_HEART_RATE_PRESENT = 0x0008 

21 HEART_RATE_VARIABILITY_PRESENT = 0x0010 

22 RESPIRATION_RATE_PRESENT = 0x0020 

23 RESTING_RESPIRATION_RATE_PRESENT = 0x0040 

24 DEVICE_WORN = 0x8000 

25 

26 

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

28 """Parsed data from CardioRespiratory Activity Instantaneous Data. 

29 

30 Contains flags and any additional field data as raw bytes. 

31 The flags field indicates which optional fields are present. 

32 """ 

33 

34 flags: CardioRespiratoryInstantaneousFlags 

35 additional_data: bytes = b"" 

36 

37 

38_FLAGS_SIZE = 2 # flags field (uint16) 

39 

40 

41class CardioRespiratoryActivityInstantaneousDataCharacteristic( 

42 BaseCharacteristic[CardioRespiratoryActivityInstantaneousData], 

43): 

44 """CardioRespiratory Activity Instantaneous Data characteristic (0x2B3E). 

45 

46 org.bluetooth.characteristic.cardiorespiratory_activity_instantaneous_data 

47 

48 Instantaneous cardiorespiratory activity data from the Physical 

49 Activity Monitor service. Flags indicate which optional fields 

50 (heart rate, resting heart rate, cadence, distance, etc.) are present. 

51 """ 

52 

53 _characteristic_name = "CardioRespiratory Activity Instantaneous Data" 

54 min_length = 2 # flags (uint16) 

55 allow_variable_length = True 

56 

57 def _decode_value( 

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

59 ) -> CardioRespiratoryActivityInstantaneousData: 

60 """Parse CardioRespiratory Activity Instantaneous Data. 

61 

62 Format: flags (uint16) + variable optional fields. 

63 """ 

64 flags = CardioRespiratoryInstantaneousFlags(DataParser.parse_int16(data, 0, signed=False)) 

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

66 

67 return CardioRespiratoryActivityInstantaneousData( 

68 flags=flags, 

69 additional_data=additional_data, 

70 ) 

71 

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

73 """Encode CardioRespiratory Activity Instantaneous Data to bytes.""" 

74 result = bytearray() 

75 result += DataParser.encode_int16(int(data.flags), signed=False) 

76 result += bytearray(data.additional_data) 

77 return result