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

47 statements  

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

1"""IDD Record Access Control Point characteristic (0x2B27). 

2 

3IDD-specific record access control point for managing insulin delivery 

4history records. Same pattern as generic RACP (0x2A52) but IDD-specific. 

5 

6References: 

7 Bluetooth SIG Insulin Delivery Service 1.0.1, Table 3.21, 3.25 

8""" 

9 

10from __future__ import annotations 

11 

12from enum import IntEnum 

13 

14import msgspec 

15 

16from ..context import CharacteristicContext 

17from .base import BaseCharacteristic 

18from .utils import DataParser 

19 

20 

21class IDDRACPOpCode(IntEnum): 

22 """IDD Record Access Control Point Op Codes (Table 3.25, Hamming codes).""" 

23 

24 RESPONSE_CODE = 0x0F 

25 REPORT_STORED_RECORDS = 0x33 

26 DELETE_STORED_RECORDS = 0x3C 

27 ABORT_OPERATION = 0x55 

28 REPORT_NUMBER_OF_STORED_RECORDS = 0x5A 

29 NUMBER_OF_STORED_RECORDS_RESPONSE = 0x66 

30 

31 

32class IDDRACPOperator(IntEnum): 

33 """IDD Record Access Control Point Operators (Table 3.26, Hamming codes).""" 

34 

35 NULL = 0x0F 

36 ALL_RECORDS = 0x33 

37 LESS_THAN_OR_EQUAL = 0x3C 

38 GREATER_THAN_OR_EQUAL = 0x55 

39 RANGE_INCLUSIVE = 0x5A 

40 FIRST_RECORD = 0x66 

41 LAST_RECORD = 0x69 

42 

43 

44class IDDRACPResponseCode(IntEnum): 

45 """IDD RACP response codes (Table 3.27).""" 

46 

47 # Error codes 0x02-0x09 range (from generic RACP) 

48 UNSUPPORTED_OPERATOR = 0x02 

49 INVALID_OPERATOR = 0x03 

50 UNSUPPORTED_OPERAND = 0x04 

51 INVALID_OPERAND = 0x05 

52 UNCLEAR_OPERATOR = 0x06 

53 # IDD-specific codes 

54 PROCEDURE_NOT_APPLICABLE = 0x0A 

55 SUCCESS = 0xF0 

56 

57 

58class IDDRecordAccessControlPointData(msgspec.Struct, frozen=True, kw_only=True): 

59 """Parsed data from IDD Record Access Control Point. 

60 

61 Attributes: 

62 opcode: The operation code. 

63 operator: The operator for the operation. 

64 operand: Raw operand bytes. Empty if none. 

65 

66 """ 

67 

68 opcode: IDDRACPOpCode 

69 operator: IDDRACPOperator 

70 operand: bytes = b"" 

71 

72 

73class IDDRecordAccessControlPointCharacteristic(BaseCharacteristic[IDDRecordAccessControlPointData]): 

74 """IDD Record Access Control Point characteristic (0x2B27). 

75 

76 org.bluetooth.characteristic.idd_record_access_control_point 

77 

78 IDD-specific record access control point. 

79 ROLE: CONTROL 

80 """ 

81 

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

83 allow_variable_length = True 

84 

85 def _decode_value( 

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

87 ) -> IDDRecordAccessControlPointData: 

88 """Parse IDD RACP data. 

89 

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

91 """ 

92 opcode = IDDRACPOpCode(DataParser.parse_int8(data, 0, signed=False)) 

93 operator = IDDRACPOperator(DataParser.parse_int8(data, 1, signed=False)) 

94 operand = bytes(data[2:]) 

95 

96 return IDDRecordAccessControlPointData( 

97 opcode=opcode, 

98 operator=operator, 

99 operand=operand, 

100 ) 

101 

102 def _encode_value(self, data: IDDRecordAccessControlPointData) -> bytearray: 

103 """Encode IDD RACP data.""" 

104 result = bytearray() 

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

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

107 result.extend(data.operand) 

108 return result