31
31
32
32
import struct
33
33
34
- class Advertisement :
35
- """Build up a BLE advertising data packet."""
34
+ class AdvertisingPacket :
35
+ """Build up a BLE advertising data or scan response packet."""
36
36
# BR/EDR flags not included here, since we don't support BR/EDR.
37
37
FLAG_LIMITED_DISCOVERY = 0x01
38
38
"""Discoverable only for a limited time period."""
@@ -81,28 +81,38 @@ class Advertisement:
81
81
MAX_DATA_SIZE = 31
82
82
"""Data size in a regular BLE packet."""
83
83
84
- def __init__ (self , flags = ( FLAG_GENERAL_DISCOVERY | FLAG_LE_ONLY ) , max_length = MAX_DATA_SIZE ):
85
- """Initalize an advertising packet, with the given flags , no larger than max_length."""
86
- self .data = bytearray (( 2 , self . FLAGS , flags ) )
84
+ def __init__ (self , * , max_length = MAX_DATA_SIZE ):
85
+ """Create an empty advertising packet, no larger than max_length."""
86
+ self ._packet_bytes = bytearray ()
87
87
self ._max_length = max_length
88
88
self ._check_length ()
89
89
90
+ @property
91
+ def packet_bytes (self ):
92
+ """The raw packet bytes."""
93
+ return self ._packet_bytes
94
+
90
95
@property
91
96
def bytes_remaining (self ):
92
- return self ._max_length - len (self .data )
97
+ """Number of bytes still available for use in the packet."""
98
+ return self ._max_length - len (self ._packet_bytes )
93
99
94
100
def _check_length (self ):
95
- if len (self .data ) > self ._max_length :
101
+ if len (self ._packet_bytes ) > self ._max_length :
96
102
raise IndexError ("Advertising data too long" )
97
103
98
104
def add_field (self , field_type , field_data ):
99
105
"""Append an advertising data field to the current packet, of the given type.
100
106
The length field is calculated from the length of field_data."""
101
- self .data .append (1 + len (field_data ))
102
- self .data .append (field_type )
103
- self .data .extend (field_data )
107
+ self ._packet_bytes .append (1 + len (field_data ))
108
+ self ._packet_bytes .append (field_type )
109
+ self ._packet_bytes .extend (field_data )
104
110
self ._check_length ()
105
111
112
+ def add_flags (self , flags = (FLAG_GENERAL_DISCOVERY | FLAG_LE_ONLY )):
113
+ """Add default or custom advertising flags."""
114
+ self .add_field (self .FLAGS , struct .pack ("<B" , flags ))
115
+
106
116
def add_16_bit_uuids (self , uuids ):
107
117
"""Add a complete list of 16 bit service UUIDs."""
108
118
for uuid in uuids :
@@ -119,43 +129,64 @@ def add_mfr_specific_data(self, mfr_id, data):
119
129
120
130
121
131
class ServerAdvertisement :
122
- def __init__ ( self , peripheral , * , tx_power = 0 ):
123
- """Create an advertisement to advertise a peripheral's services.
132
+ """
133
+ Data to advertise a peripheral's services.
124
134
125
- :param peripheral Peripheral the Peripheral to advertise. Use its services and name
126
- :param int tx_power: transmit power in dBm at 0 meters (8 bit signed value). Default 0 dBm
127
- """
135
+ The advertisement consists of an advertising data packet and an optional scan response packet,
136
+ The scan response packet is created only if there is not room in the
137
+ advertising data packet for the complete peripheral name.
128
138
139
+ :param peripheral Peripheral the Peripheral to advertise. Use its services and name
140
+ :param int tx_power: transmit power in dBm at 0 meters (8 bit signed value). Default 0 dBm
141
+ """
142
+
143
+ def __init__ (self , peripheral , * , tx_power = 0 ):
129
144
self ._peripheral = peripheral
130
145
131
- adv = Advertisement ()
146
+ packet = AdvertisingPacket ()
147
+ packet .add_flags ()
148
+ self ._scan_response_packet = None
132
149
133
150
# Need to check service.secondary
134
151
uuids_16_bits = [service .uuid for service in peripheral .services
135
152
if service .uuid .size == 16 and not service .secondary ]
136
153
if uuids_16_bits :
137
- adv .add_16_bit_uuids (uuids_16_bits )
154
+ packet .add_16_bit_uuids (uuids_16_bits )
138
155
139
156
uuids_128_bits = [service .uuid for service in peripheral .services
140
- if service .uuid .size == 128 and not service .secondary ]
157
+ if service .uuid .size == 128 and not service .secondary ]
141
158
if uuids_128_bits :
142
- adv .add_128_bit_uuids (uuids_128_bits )
159
+ packet .add_128_bit_uuids (uuids_128_bits )
143
160
144
- adv .add_field (Advertisement .TX_POWER , struct .pack ("<b" , tx_power ))
161
+ packet .add_field (AdvertisingPacket .TX_POWER , struct .pack ("<b" , tx_power ))
145
162
146
163
# 2 bytes needed for field length and type.
147
- bytes_available = adv .bytes_remaining - 2
164
+ bytes_available = packet .bytes_remaining - 2
148
165
if bytes_available <= 0 :
149
166
raise IndexError ("No room for name" )
150
167
151
168
name_bytes = bytes (peripheral .name , 'utf-8' )
152
169
if bytes_available >= len (name_bytes ):
153
- adv .add_field (Advertisement .COMPLETE_LOCAL_NAME , name_bytes )
170
+ packet .add_field (AdvertisingPacket .COMPLETE_LOCAL_NAME , name_bytes )
154
171
else :
155
- adv .add_field (Advertisement .SHORT_LOCAL_NAME , name_bytes [:bytes_available ])
172
+ packet .add_field (AdvertisingPacket .SHORT_LOCAL_NAME , name_bytes [:bytes_available ])
173
+ self ._scan_response_packet = AdvertisingPacket ()
174
+ try :
175
+ self ._scan_response_packet .add_field (AdvertisingPacket .COMPLETE_LOCAL_NAME ,
176
+ name_bytes )
177
+ except IndexError :
178
+ raise IndexError ("Name too long" )
156
179
157
- self ._advertisement = adv
180
+ self ._advertising_data_packet = packet
181
+
182
+ @property
183
+ def advertising_data_bytes (self ):
184
+ """The raw bytes for the initial advertising data packet."""
185
+ return self ._advertising_data_packet .packet_bytes
158
186
159
187
@property
160
- def data (self ):
161
- return self ._advertisement .data
188
+ def scan_response_bytes (self ):
189
+ """The raw bytes for the scan response packet. None if there is no response packet."""
190
+ if self ._scan_response_packet :
191
+ return self ._scan_response_packet .packet_bytes
192
+ return None
0 commit comments