Coverage for src / bluetooth_sig / gatt / characteristics / bearer_list_current_calls.py: 98%
60 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"""Bearer List Current Calls characteristic (0x2BB9)."""
3from __future__ import annotations
5from enum import IntEnum, IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class CallState(IntEnum):
15 """Call state values per TBS specification."""
17 INCOMING = 0x00
18 DIALING = 0x01
19 ALERTING = 0x02
20 ACTIVE = 0x03
21 LOCALLY_HELD = 0x04
22 REMOTELY_HELD = 0x05
23 LOCALLY_AND_REMOTELY_HELD = 0x06
26class CallFlags(IntFlag):
27 """Call flags per TBS specification.
29 Bit 0: Incoming/Outgoing — 0 = incoming call, 1 = outgoing call (Table 3.7).
30 """
32 OUTGOING = 0x01
33 WITHHELD = 0x02
34 WITHHELD_BY_NETWORK = 0x04
37class CallListItem(msgspec.Struct, frozen=True, kw_only=True):
38 """A single call entry in the Bearer List Current Calls."""
40 call_index: int
41 state: CallState
42 call_flags: CallFlags
43 uri: str
46class BearerListCurrentCallsData(msgspec.Struct, frozen=True, kw_only=True):
47 """Parsed data from Bearer List Current Calls characteristic."""
49 calls: tuple[CallListItem, ...]
52_MIN_ITEM_LENGTH = 3 # call_index (uint8) + call_state (uint8) + call_flags (uint8)
55class BearerListCurrentCallsCharacteristic(BaseCharacteristic[BearerListCurrentCallsData]):
56 """Bearer List Current Calls characteristic (0x2BB9).
58 org.bluetooth.characteristic.bearer_list_current_calls
60 List of current calls on the telephone bearer. Each list item
61 contains a length prefix, call index, state, flags, and URI.
62 """
64 min_length = 0
65 allow_variable_length = True
67 def _decode_value(
68 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
69 ) -> BearerListCurrentCallsData:
70 calls: list[CallListItem] = []
71 offset = 0
73 while offset < len(data):
74 if offset + 1 > len(data):
75 break
76 item_length = DataParser.parse_int8(data, offset, signed=False)
77 offset += 1
79 if item_length < _MIN_ITEM_LENGTH or offset + item_length > len(data):
80 break
82 call_index = DataParser.parse_int8(data, offset, signed=False)
83 state = CallState(DataParser.parse_int8(data, offset + 1, signed=False))
84 call_flags = CallFlags(DataParser.parse_int8(data, offset + 2, signed=False))
86 uri_length = item_length - 3
87 uri = ""
88 if uri_length > 0:
89 uri = DataParser.parse_utf8_string(data[offset + 3 : offset + 3 + uri_length])
91 calls.append(
92 CallListItem(
93 call_index=call_index,
94 state=state,
95 call_flags=call_flags,
96 uri=uri,
97 )
98 )
99 offset += item_length
101 return BearerListCurrentCallsData(calls=tuple(calls))
103 def _encode_value(self, data: BearerListCurrentCallsData) -> bytearray:
104 result = bytearray()
105 for call in data.calls:
106 uri_bytes = call.uri.encode("utf-8")
107 item_length = 3 + len(uri_bytes)
108 result.append(item_length)
109 result.extend(DataParser.encode_int8(call.call_index, signed=False))
110 result.extend(DataParser.encode_int8(int(call.state), signed=False))
111 result.extend(DataParser.encode_int8(int(call.call_flags), signed=False))
112 result.extend(uri_bytes)
113 return result