Coverage for src / bluetooth_sig / gatt / characteristics / idd_annunciation_status.py: 100%
78 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""IDD Annunciation Status characteristic (0x2B22).
3Reports alarm/reminder/status-change annunciations from the
4Insulin Delivery Device.
6References:
7 Bluetooth SIG Insulin Delivery Service 1.0.1, Table 4.7
8"""
10from __future__ import annotations
12from enum import IntEnum, IntFlag
14import msgspec
16from ..context import CharacteristicContext
17from .base import BaseCharacteristic
18from .utils import DataParser
21class IDDAnnunciationFlags(IntFlag):
22 """Flags indicating which fields are present."""
24 ANNUNCIATION_PRESENT = 0x01
25 AUX_INFO_1_PRESENT = 0x02
26 AUX_INFO_2_PRESENT = 0x04
27 AUX_INFO_3_PRESENT = 0x08
28 AUX_INFO_4_PRESENT = 0x10
29 AUX_INFO_5_PRESENT = 0x20
32class IDDAnnunciationType(IntEnum):
33 """IDD annunciation type (uint16 Hamming codes)."""
35 SYSTEM_ISSUE = 0x000F
36 MECHANICAL_ISSUE = 0x0033
37 OCCLUSION_DETECTED = 0x003C
38 RESERVOIR_ISSUE = 0x0055
39 RESERVOIR_EMPTY = 0x005A
40 RESERVOIR_LOW = 0x0066
41 PRIMING_ISSUE = 0x0069
42 INFUSION_SET_INCOMPLETE = 0x0096
43 INFUSION_SET_DETACHED = 0x0099
44 POWER_SOURCE_INSUFFICIENT = 0x00A5
45 BATTERY_EMPTY = 0x00AA
46 BATTERY_LOW = 0x00C3
47 BATTERY_MEDIUM = 0x00CC
48 BATTERY_FULL = 0x00F0
49 TEMPERATURE_OUT_OF_RANGE = 0x00FF
50 AIR_PRESSURE_OUT_OF_RANGE = 0x0303
51 BOLUS_CANCELED = 0x030C
52 TBR_OVER = 0x0330
53 TBR_CANCELED = 0x033F
54 MAX_DELIVERY = 0x0356
55 DATE_TIME_ISSUE = 0x0359
56 TEMPERATURE = 0x0365
59class IDDAnnunciationStatus(IntEnum):
60 """IDD annunciation status (uint8 Hamming codes)."""
62 UNDETERMINED = 0x0F
63 PENDING = 0x33
64 SNOOZED = 0x3C
65 CONFIRMED = 0x55
68class IDDAnnunciationStatusData(msgspec.Struct, frozen=True, kw_only=True):
69 """Parsed data from IDD Annunciation Status characteristic.
71 Attributes:
72 flags: Bit field indicating which optional fields are present.
73 annunciation_instance_id: Instance identifier for this annunciation.
74 annunciation_type: Type of annunciation (Hamming-coded).
75 annunciation_status: Current status of the annunciation.
76 aux_info: Up to 5 auxiliary info uint16 values.
78 """
80 flags: IDDAnnunciationFlags
81 annunciation_instance_id: int | None = None
82 annunciation_type: IDDAnnunciationType | None = None
83 annunciation_status: IDDAnnunciationStatus | None = None
84 aux_info: list[int] = []
87_AUX_FLAGS = [
88 IDDAnnunciationFlags.AUX_INFO_1_PRESENT,
89 IDDAnnunciationFlags.AUX_INFO_2_PRESENT,
90 IDDAnnunciationFlags.AUX_INFO_3_PRESENT,
91 IDDAnnunciationFlags.AUX_INFO_4_PRESENT,
92 IDDAnnunciationFlags.AUX_INFO_5_PRESENT,
93]
96class IDDAnnunciationStatusCharacteristic(BaseCharacteristic[IDDAnnunciationStatusData]):
97 """IDD Annunciation Status characteristic (0x2B22).
99 org.bluetooth.characteristic.idd_annunciation_status
101 Reports annunciation (alert/reminder/status) information
102 from the Insulin Delivery Device.
103 """
105 min_length = 1 # flags only
106 allow_variable_length = True
108 def _decode_value(
109 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
110 ) -> IDDAnnunciationStatusData:
111 """Parse IDD Annunciation Status data.
113 Format: Flags(uint8) + [InstanceID(uint16) + Type(uint16) + Status(uint8)]
114 + [AuxInfo1(uint16)] ... [AuxInfo5(uint16)]
115 """
116 flags = IDDAnnunciationFlags(DataParser.parse_int8(data, 0, signed=False))
117 offset = 1
119 instance_id: int | None = None
120 annunciation_type: IDDAnnunciationType | None = None
121 annunciation_status: IDDAnnunciationStatus | None = None
123 if flags & IDDAnnunciationFlags.ANNUNCIATION_PRESENT:
124 instance_id = DataParser.parse_int16(data, offset, signed=False)
125 annunciation_type = IDDAnnunciationType(DataParser.parse_int16(data, offset + 2, signed=False))
126 annunciation_status = IDDAnnunciationStatus(DataParser.parse_int8(data, offset + 4, signed=False))
127 offset += 5
129 aux_info: list[int] = []
130 for aux_flag in _AUX_FLAGS:
131 if flags & aux_flag:
132 aux_info.append(DataParser.parse_int16(data, offset, signed=False))
133 offset += 2
135 return IDDAnnunciationStatusData(
136 flags=flags,
137 annunciation_instance_id=instance_id,
138 annunciation_type=annunciation_type,
139 annunciation_status=annunciation_status,
140 aux_info=aux_info,
141 )
143 def _encode_value(self, data: IDDAnnunciationStatusData) -> bytearray:
144 """Encode IDD Annunciation Status data."""
145 result = bytearray()
146 result.extend(DataParser.encode_int8(int(data.flags), signed=False))
148 if data.flags & IDDAnnunciationFlags.ANNUNCIATION_PRESENT:
149 result.extend(DataParser.encode_int16(data.annunciation_instance_id or 0, signed=False))
150 result.extend(DataParser.encode_int16(int(data.annunciation_type or 0), signed=False))
151 result.extend(DataParser.encode_int8(int(data.annunciation_status or 0), signed=False))
153 for value in data.aux_info:
154 result.extend(DataParser.encode_int16(value, signed=False))
156 return result