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

42 statements  

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

1"""Object Action Control Point characteristic implementation.""" 

2 

3from __future__ import annotations 

4 

5from enum import IntEnum 

6 

7import msgspec 

8 

9from ...types.gatt_enums import CharacteristicRole 

10from ..context import CharacteristicContext 

11from .base import BaseCharacteristic 

12from .utils import DataParser 

13 

14 

15class OACPOpcode(IntEnum): 

16 """Object Action Control Point opcodes per OTS specification.""" 

17 

18 CREATE = 0x01 

19 DELETE = 0x02 

20 CALCULATE_CHECKSUM = 0x03 

21 EXECUTE = 0x04 

22 READ = 0x05 

23 WRITE = 0x06 

24 ABORT = 0x07 

25 RESPONSE = 0x60 

26 

27 

28class OACPResultCode(IntEnum): 

29 """OACP response result codes per OTS specification.""" 

30 

31 SUCCESS = 0x01 

32 OPCODE_NOT_SUPPORTED = 0x02 

33 INVALID_PARAMETER = 0x03 

34 INSUFFICIENT_RESOURCES = 0x04 

35 INVALID_OBJECT = 0x05 

36 CHANNEL_UNAVAILABLE = 0x06 

37 UNSUPPORTED_TYPE = 0x07 

38 PROCEDURE_NOT_PERMITTED = 0x08 

39 OBJECT_LOCKED = 0x09 

40 OPERATION_FAILED = 0x0A 

41 

42 

43class OACPData(msgspec.Struct, frozen=True, kw_only=True): 

44 """Parsed data from Object Action Control Point characteristic. 

45 

46 Attributes: 

47 opcode: The OACP opcode. 

48 parameters: Raw parameter bytes (opcode-dependent). 

49 

50 """ 

51 

52 opcode: OACPOpcode 

53 parameters: bytes = b"" 

54 

55 

56class ObjectActionControlPointCharacteristic(BaseCharacteristic[OACPData]): 

57 """Object Action Control Point characteristic (0x2AC5). 

58 

59 org.bluetooth.characteristic.object_action_control_point 

60 

61 Control point for object actions in the Object Transfer Service (OTS). 

62 Opcodes include Create, Delete, Calculate Checksum, Execute, Read, 

63 Write, Abort, and Response. 

64 """ 

65 

66 _manual_role = CharacteristicRole.CONTROL 

67 min_length: int = 1 # At minimum: opcode (1 byte) 

68 allow_variable_length: bool = True 

69 

70 def _decode_value( 

71 self, 

72 data: bytearray, 

73 ctx: CharacteristicContext | None = None, 

74 *, 

75 validate: bool = True, 

76 ) -> OACPData: 

77 """Parse OACP data. 

78 

79 Args: 

80 data: Raw bytes (1+ bytes). 

81 ctx: Optional CharacteristicContext. 

82 validate: Whether to validate ranges (default True). 

83 

84 Returns: 

85 OACPData with opcode and parameters. 

86 

87 """ 

88 opcode = OACPOpcode(DataParser.parse_int8(data, 0, signed=False)) 

89 parameters = bytes(data[1:]) 

90 return OACPData(opcode=opcode, parameters=parameters) 

91 

92 def _encode_value(self, data: OACPData) -> bytearray: 

93 """Encode OACP data to bytes. 

94 

95 Args: 

96 data: OACPData to encode. 

97 

98 Returns: 

99 Encoded bytes. 

100 

101 """ 

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

103 result.extend(data.parameters) 

104 return result