Coverage for src / bluetooth_sig / types / peripheral_types.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +0000

1"""Data structures for BLE peripheral/GATT server definitions. 

2 

3This module provides the data structures used to define GATT services 

4and characteristics for peripheral devices. These are used by 

5PeripheralManagerProtocol implementations. 

6 

7Analogous to device_types.py and connected.py structures on the client side. 

8""" 

9 

10from __future__ import annotations 

11 

12from collections.abc import Callable 

13from dataclasses import dataclass, field 

14from typing import Any 

15 

16from bluetooth_sig.gatt.characteristics.base import BaseCharacteristic 

17from bluetooth_sig.types.gatt_enums import GattProperty 

18from bluetooth_sig.types.uuid import BluetoothUUID 

19 

20 

21@dataclass 

22class CharacteristicDefinition: 

23 """Definition for a GATT characteristic to be hosted on a peripheral. 

24 

25 This bridges bluetooth-sig characteristic classes with the peripheral backend. 

26 Use `from_characteristic()` to create from a BaseCharacteristic instance. 

27 

28 Attributes: 

29 uuid: The UUID of the characteristic (16-bit short or 128-bit full). 

30 properties: GATT properties (read, write, notify, indicate, etc.). 

31 initial_value: Initial value to serve when clients read the characteristic. 

32 readable: Whether clients can read this characteristic. 

33 writable: Whether clients can write to this characteristic. 

34 on_read: Optional callback to dynamically generate value on read requests. 

35 on_write: Optional callback when clients write to this characteristic. 

36 

37 """ 

38 

39 uuid: BluetoothUUID 

40 """The UUID of the characteristic (16-bit short or 128-bit full).""" 

41 

42 properties: GattProperty 

43 """GATT properties (read, write, notify, indicate, etc.).""" 

44 

45 initial_value: bytearray = field(default_factory=bytearray) 

46 """Initial value to serve when clients read the characteristic.""" 

47 

48 readable: bool = True 

49 """Whether clients can read this characteristic.""" 

50 

51 writable: bool = False 

52 """Whether clients can write to this characteristic.""" 

53 

54 on_read: Callable[[], bytearray] | None = None 

55 """Optional callback to dynamically generate value on read requests.""" 

56 

57 on_write: Callable[[bytearray], None] | None = None 

58 """Optional callback when clients write to this characteristic.""" 

59 

60 @classmethod 

61 def from_characteristic( 

62 cls, 

63 characteristic: BaseCharacteristic[Any], 

64 value: Any, # noqa: ANN401 

65 *, 

66 properties: GattProperty | None = None, 

67 on_read: Callable[[], bytearray] | None = None, 

68 on_write: Callable[[bytearray], None] | None = None, 

69 ) -> CharacteristicDefinition: 

70 """Create a CharacteristicDefinition from a bluetooth-sig characteristic. 

71 

72 This uses the characteristic's `build_value()` method to encode the 

73 initial value, demonstrating the library's encoding capabilities. 

74 

75 Args: 

76 characteristic: The bluetooth-sig characteristic class instance 

77 value: The Python value to encode as the initial characteristic value 

78 properties: GATT properties. If None, defaults to READ + NOTIFY. 

79 on_read: Optional callback for dynamic value generation 

80 on_write: Optional callback for write handling 

81 

82 Returns: 

83 CharacteristicDefinition ready for use with PeripheralManagerProtocol 

84 

85 Example:: 

86 >>> from bluetooth_sig.gatt.characteristics import BatteryLevelCharacteristic 

87 >>> char = BatteryLevelCharacteristic() 

88 >>> defn = CharacteristicDefinition.from_characteristic(char, 85) 

89 >>> defn.initial_value 

90 bytearray(b'U') # 85 encoded as single byte 

91 

92 """ 

93 # Encode the value using the characteristic's build_value method 

94 encoded = characteristic.build_value(value) 

95 

96 # Default properties: readable + notify 

97 if properties is None: 

98 properties = GattProperty.READ | GattProperty.NOTIFY 

99 

100 # Determine read/write from properties 

101 readable = bool(properties & GattProperty.READ) 

102 writable = bool(properties & (GattProperty.WRITE | GattProperty.WRITE_WITHOUT_RESPONSE)) 

103 

104 return cls( 

105 uuid=BluetoothUUID(str(characteristic.uuid)), 

106 properties=properties, 

107 initial_value=encoded, 

108 readable=readable, 

109 writable=writable, 

110 on_read=on_read, 

111 on_write=on_write, 

112 ) 

113 

114 

115@dataclass 

116class ServiceDefinition: 

117 """Definition for a GATT service to be hosted on a peripheral. 

118 

119 Attributes: 

120 uuid: The UUID of the service (16-bit short or 128-bit full). 

121 characteristics: List of characteristics in this service. 

122 primary: Whether this is a primary service (vs secondary/included). 

123 

124 """ 

125 

126 uuid: BluetoothUUID 

127 """The UUID of the service (16-bit short or 128-bit full).""" 

128 

129 characteristics: list[CharacteristicDefinition] = field(default_factory=list) 

130 """List of characteristics in this service.""" 

131 

132 primary: bool = True 

133 """Whether this is a primary service (vs secondary/included).""" 

134 

135 

136__all__ = [ 

137 "CharacteristicDefinition", 

138 "ServiceDefinition", 

139]