@@ -211,18 +211,54 @@ def __str__(self):
211
211
return "<ManufacturerData company_id={:04x} data={} >" .format (self .company_id , hex_data )
212
212
213
213
class ManufacturerDataField :
214
- """A single piece of data within the manufacturer specific data."""
215
- def __init__ (self , key , key_format ):
214
+ """A single piece of data within the manufacturer specific data. The format can be repeated. """
215
+ def __init__ (self , key , value_format , field_names = None ):
216
216
self ._key = key
217
- self ._format = key_format
217
+ self ._format = value_format
218
+ # TODO: Support format strings that use numbers to repeat a given type. For now, we strip
219
+ # numbers because Radio specifies string length with it.
220
+ self .element_count = len (value_format .strip ("><!=@0123456789" ).replace ("x" , "" ))
221
+ if self .element_count > 1 and (not field_names or len (field_names ) != self .element_count ):
222
+ raise ValueError ("Provide field_names when multiple values are in the format" )
223
+ self ._entry_length = struct .calcsize (value_format )
224
+ self .field_names = field_names
218
225
219
226
def __get__ (self , obj , cls ):
220
- return struct .unpack_from (self ._format , obj .manufacturer_data .data [self ._key ])[0 ]
227
+ if self ._key not in obj .manufacturer_data .data :
228
+ return None
229
+ packed = obj .manufacturer_data .data [self ._key ]
230
+ if self ._entry_length == len (packed ):
231
+ unpacked = struct .unpack_from (self ._format , packed )
232
+ if self .element_count == 1 :
233
+ unpacked = unpacked [0 ]
234
+ return unpacked
235
+ if len (packed ) % self ._entry_length != 0 :
236
+ raise RuntimeError ("Invalid data length" )
237
+ entry_count = len (packed ) // self ._entry_length
238
+ unpacked = [None ] * entry_count
239
+ for i in range (entry_count ):
240
+ offset = i * self ._entry_length
241
+ unpacked [i ] = struct .unpack_from (self ._format , packed , offset = offset )
242
+ if self .element_count == 1 :
243
+ unpacked [i ] = unpacked [i ][0 ]
244
+ return tuple (unpacked )
221
245
222
246
def __set__ (self , obj , value ):
223
247
if not obj .mutable :
224
248
raise AttributeError ()
225
- obj .manufacturer_data .data [self ._key ] = struct .pack (self ._format , value )
249
+ if isinstance (value , tuple ) and (self .element_count == 1 or isinstance (value [0 ], tuple )):
250
+ packed = bytearray (self ._entry_length * len (value ))
251
+ for i , entry in enumerate (value ):
252
+ offset = i * self ._entry_length
253
+ if self .element_count > 1 :
254
+ struct .pack_into (self ._format , packed , offset , * entry )
255
+ else :
256
+ struct .pack_into (self ._format , packed , offset , entry )
257
+ obj .manufacturer_data .data [self ._key ] = bytes (packed )
258
+ elif self .element_count == 1 :
259
+ obj .manufacturer_data .data [self ._key ] = struct .pack (self ._format , value )
260
+ else :
261
+ obj .manufacturer_data .data [self ._key ] = struct .pack (self ._format , * value )
226
262
227
263
# TODO: Handle service data.
228
264
0 commit comments