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

40 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 20:14 +0000

1"""Blood Pressure Feature characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ...types.gatt_enums import ValueType 

10from ..constants import UINT16_MAX 

11from ..context import CharacteristicContext 

12from .base import BaseCharacteristic 

13from .utils import DataParser 

14 

15 

16class BloodPressureFeatures(IntFlag): 

17 """Blood Pressure Feature flags as per Bluetooth SIG specification.""" 

18 

19 BODY_MOVEMENT_DETECTION = 0x01 

20 CUFF_FIT_DETECTION = 0x02 

21 IRREGULAR_PULSE_DETECTION = 0x04 

22 PULSE_RATE_RANGE_DETECTION = 0x08 

23 MEASUREMENT_POSITION_DETECTION = 0x10 

24 MULTIPLE_BOND_SUPPORT = 0x20 

25 

26 

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

28 """Parsed data from Blood Pressure Feature characteristic.""" 

29 

30 features_bitmap: int 

31 body_movement_detection_support: bool 

32 cuff_fit_detection_support: bool 

33 irregular_pulse_detection_support: bool 

34 pulse_rate_range_detection_support: bool 

35 measurement_position_detection_support: bool 

36 multiple_bond_support: bool 

37 

38 

39class BloodPressureFeatureCharacteristic(BaseCharacteristic[BloodPressureFeatureData]): 

40 """Blood Pressure Feature characteristic (0x2A49). 

41 

42 Used to expose the supported features of a blood pressure monitoring 

43 device. Indicates which optional measurements and capabilities are 

44 available. 

45 """ 

46 

47 _manual_value_type: ValueType | str | None = ValueType.DICT # Override since decode_value returns dataclass 

48 

49 # YAML has no range constraint; enforce full uint16 bitmap range. 

50 min_value: int = 0 

51 max_value: int = UINT16_MAX 

52 

53 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> BloodPressureFeatureData: 

54 """Parse blood pressure feature data according to Bluetooth specification. 

55 

56 Format: Features(2) - 16-bit bitmap indicating supported features. 

57 

58 Args: 

59 data: Raw bytearray from BLE characteristic. 

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

61 

62 Returns: 

63 BloodPressureFeatureData containing parsed feature bitmap and capabilities. 

64 

65 """ 

66 if len(data) < 2: 

67 raise ValueError("Blood Pressure Feature data must be at least 2 bytes") 

68 

69 features_bitmap = DataParser.parse_int16(data, 0, signed=False) 

70 

71 body_movement_detection = bool(features_bitmap & BloodPressureFeatures.BODY_MOVEMENT_DETECTION) 

72 cuff_fit_detection = bool(features_bitmap & BloodPressureFeatures.CUFF_FIT_DETECTION) 

73 irregular_pulse_detection = bool(features_bitmap & BloodPressureFeatures.IRREGULAR_PULSE_DETECTION) 

74 pulse_rate_range_detection = bool(features_bitmap & BloodPressureFeatures.PULSE_RATE_RANGE_DETECTION) 

75 measurement_position_detection = bool(features_bitmap & BloodPressureFeatures.MEASUREMENT_POSITION_DETECTION) 

76 multiple_bond_support = bool(features_bitmap & BloodPressureFeatures.MULTIPLE_BOND_SUPPORT) 

77 

78 return BloodPressureFeatureData( 

79 features_bitmap=features_bitmap, 

80 body_movement_detection_support=body_movement_detection, 

81 cuff_fit_detection_support=cuff_fit_detection, 

82 irregular_pulse_detection_support=irregular_pulse_detection, 

83 pulse_rate_range_detection_support=pulse_rate_range_detection, 

84 measurement_position_detection_support=measurement_position_detection, 

85 multiple_bond_support=multiple_bond_support, 

86 ) 

87 

88 def _encode_value(self, data: BloodPressureFeatureData) -> bytearray: 

89 """Encode BloodPressureFeatureData back to bytes. 

90 

91 Args: 

92 data: BloodPressureFeatureData instance to encode 

93 

94 Returns: 

95 Encoded bytes representing the blood pressure features 

96 

97 """ 

98 return DataParser.encode_int16(data.features_bitmap, signed=False)