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

61 statements  

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

1"""IDD History Data characteristic (0x2B28). 

2 

3Contains historical event data from the Insulin Delivery Device. 

4 

5References: 

6 Bluetooth SIG Insulin Delivery Service 1.0 

7""" 

8 

9from __future__ import annotations 

10 

11from enum import IntEnum 

12 

13import msgspec 

14 

15from ..context import CharacteristicContext 

16from .base import BaseCharacteristic 

17from .utils import DataParser 

18 

19 

20class IDDHistoryEventType(IntEnum): 

21 """IDD history event types (Table 4.95, Hamming codes).""" 

22 

23 REFERENCE_TIME = 0x000F 

24 REFERENCE_TIME_BASE_OFFSET = 0x0033 

25 BOLUS_CALCULATED_PART_1 = 0x003C 

26 BOLUS_CALCULATED_PART_2 = 0x0055 

27 BOLUS_PROGRAMMED_PART_1 = 0x005A 

28 BOLUS_PROGRAMMED_PART_2 = 0x0066 

29 BOLUS_DELIVERED_PART_1 = 0x0069 

30 BOLUS_DELIVERED_PART_2 = 0x0096 

31 DELIVERED_BASAL_RATE_CHANGED = 0x0099 

32 TBR_ADJUSTMENT_STARTED = 0x00A5 

33 TBR_ADJUSTMENT_ENDED = 0x00AA 

34 TBR_ADJUSTMENT_CHANGED = 0x00C3 

35 PROFILE_TEMPLATE_ACTIVATED = 0x00CC 

36 BASAL_RATE_PROFILE_TEMPLATE_TIME_BLOCK_CHANGED = 0x00F0 

37 TOTAL_DAILY_INSULIN_DELIVERY = 0x00FF 

38 THERAPY_CONTROL_STATE_CHANGED = 0x0303 

39 OPERATIONAL_STATE_CHANGED = 0x030C 

40 RESERVOIR_REMAINING_AMOUNT_CHANGED = 0x0330 

41 ANNUNCIATION_STATUS_CHANGED_PART_1 = 0x033F 

42 ANNUNCIATION_STATUS_CHANGED_PART_2 = 0x0356 

43 ISF_PROFILE_TEMPLATE_TIME_BLOCK_CHANGED = 0x0359 

44 I2CHO_RATIO_PROFILE_TEMPLATE_TIME_BLOCK_CHANGED = 0x0365 

45 TARGET_GLUCOSE_RANGE_PROFILE_TEMPLATE_TIME_BLOCK_CHANGED = 0x036A 

46 PRIMING_STARTED = 0x0395 

47 PRIMING_DONE = 0x039A 

48 DATA_CORRUPTION = 0x03A6 

49 POINTER_EVENT = 0x03A9 

50 BOLUS_TEMPLATE_CHANGED_PART_1 = 0x03C0 

51 BOLUS_TEMPLATE_CHANGED_PART_2 = 0x03CF 

52 TBR_TEMPLATE_CHANGED = 0x03F3 

53 MAX_BOLUS_AMOUNT_CHANGED = 0x03FC 

54 MANUFACTURER_RESERVED_START = 0xF000 

55 MANUFACTURER_RESERVED_END = 0xFFF0 

56 

57 

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

59 """Parsed data from IDD History Data characteristic. 

60 

61 Attributes: 

62 event_type: Type of the history event (uint16). 

63 sequence_number: Sequence number of this history event (uint32). 

64 relative_offset: Relative time offset in seconds (uint16). 

65 event_data: Raw event-specific data bytes. 

66 

67 """ 

68 

69 event_type: IDDHistoryEventType 

70 sequence_number: int 

71 relative_offset: int 

72 event_data: bytes = b"" 

73 

74 

75class IDDHistoryDataCharacteristic(BaseCharacteristic[IDDHistoryDataPayload]): 

76 """IDD History Data characteristic (0x2B28). 

77 

78 org.bluetooth.characteristic.idd_history_data 

79 

80 Contains historical event data from the Insulin Delivery Device. 

81 """ 

82 

83 min_length = 8 # event_type(2) + sequence_number(4) + relative_offset(2) 

84 allow_variable_length = True 

85 

86 def _decode_value( 

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

88 ) -> IDDHistoryDataPayload: 

89 """Parse IDD History Data. 

90 

91 Format: EventType (uint16 LE) + SequenceNumber (uint32 LE) + RelativeOffset (uint16 LE) + EventData (variable). 

92 """ 

93 event_type = IDDHistoryEventType(DataParser.parse_int16(data, 0, signed=False)) 

94 sequence_number = DataParser.parse_int32(data, 2, signed=False) 

95 relative_offset = DataParser.parse_int16(data, 6, signed=False) 

96 event_data = bytes(data[8:]) 

97 

98 return IDDHistoryDataPayload( 

99 event_type=event_type, 

100 sequence_number=sequence_number, 

101 relative_offset=relative_offset, 

102 event_data=event_data, 

103 ) 

104 

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

106 """Encode IDD History Data.""" 

107 result = bytearray() 

108 result.extend(DataParser.encode_int16(int(data.event_type), signed=False)) 

109 result.extend(DataParser.encode_int32(data.sequence_number, signed=False)) 

110 result.extend(DataParser.encode_int16(data.relative_offset, signed=False)) 

111 result.extend(data.event_data) 

112 return result