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

51 statements  

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

1"""Record Access Control Point characteristic (0x2A52). 

2 

3Generic control point for managing stored records in services like 

4Glucose, CGM, and Blood Pressure. 

5 

6References: 

7 Bluetooth SIG GATT Specification Supplement 

8 org.bluetooth.characteristic.record_access_control_point (GSS YAML) 

9""" 

10 

11from __future__ import annotations 

12 

13from enum import IntEnum 

14 

15import msgspec 

16 

17from ..context import CharacteristicContext 

18from .base import BaseCharacteristic 

19from .utils import DataParser 

20 

21 

22class RACPOpCode(IntEnum): 

23 """Record Access Control Point Op Codes.""" 

24 

25 REPORT_STORED_RECORDS = 0x01 

26 DELETE_STORED_RECORDS = 0x02 

27 ABORT_OPERATION = 0x03 

28 REPORT_NUMBER_OF_STORED_RECORDS = 0x04 

29 NUMBER_OF_STORED_RECORDS_RESPONSE = 0x05 

30 RESPONSE_CODE = 0x06 

31 COMBINED_REPORT = 0x07 

32 COMBINED_REPORT_RESPONSE = 0x08 

33 

34 

35class RACPOperator(IntEnum): 

36 """Record Access Control Point Operators.""" 

37 

38 NULL = 0x00 

39 ALL_RECORDS = 0x01 

40 LESS_THAN_OR_EQUAL_TO = 0x02 

41 GREATER_THAN_OR_EQUAL_TO = 0x03 

42 WITHIN_RANGE_OF = 0x04 

43 FIRST_RECORD = 0x05 

44 LAST_RECORD = 0x06 

45 

46 

47class RACPResponseCode(IntEnum): 

48 """Record Access Control Point Response Code Values.""" 

49 

50 SUCCESS = 0x01 

51 OP_CODE_NOT_SUPPORTED = 0x02 

52 INVALID_OPERATOR = 0x03 

53 OPERATOR_NOT_SUPPORTED = 0x04 

54 INVALID_OPERAND = 0x05 

55 NO_RECORDS_FOUND = 0x06 

56 ABORT_UNSUCCESSFUL = 0x07 

57 PROCEDURE_NOT_COMPLETED = 0x08 

58 OPERAND_NOT_SUPPORTED = 0x09 

59 

60 

61class RecordAccessControlPointData(msgspec.Struct, frozen=True, kw_only=True): 

62 """Parsed data from Record Access Control Point. 

63 

64 Attributes: 

65 opcode: The operation code. 

66 operator: The operator for the operation. 

67 operand: Raw operand bytes (service-specific). Empty if none. 

68 

69 """ 

70 

71 opcode: RACPOpCode 

72 operator: RACPOperator 

73 operand: bytes = b"" 

74 

75 

76class RecordAccessControlPointCharacteristic(BaseCharacteristic[RecordAccessControlPointData]): 

77 """Record Access Control Point characteristic (0x2A52). 

78 

79 org.bluetooth.characteristic.record_access_control_point 

80 

81 Generic control point for managing stored records. 

82 ROLE: CONTROL 

83 """ 

84 

85 min_length = 2 # OpCode(1) + Operator(1) 

86 allow_variable_length = True 

87 

88 def _decode_value( 

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

90 ) -> RecordAccessControlPointData: 

91 """Parse Record Access Control Point data. 

92 

93 Format: OpCode (uint8) + Operator (uint8) + Operand (variable). 

94 """ 

95 opcode = RACPOpCode(DataParser.parse_int8(data, 0, signed=False)) 

96 operator = RACPOperator(DataParser.parse_int8(data, 1, signed=False)) 

97 operand = bytes(data[2:]) 

98 

99 return RecordAccessControlPointData( 

100 opcode=opcode, 

101 operator=operator, 

102 operand=operand, 

103 ) 

104 

105 def _encode_value(self, data: RecordAccessControlPointData) -> bytearray: 

106 """Encode Record Access Control Point data.""" 

107 result = bytearray() 

108 result.extend(DataParser.encode_int8(int(data.opcode), signed=False)) 

109 result.extend(DataParser.encode_int8(int(data.operator), signed=False)) 

110 result.extend(data.operand) 

111 return result