1#!/usr/bin/env python 2# 3# BLE GATT configuration generator for use with BTstack, v0.1 4# Copyright 2011 Matthias Ringwald 5# 6# Format of input file: 7# PRIMARY_SERVICE, SERVICE_UUID 8# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9 10import codecs 11import csv 12import io 13import os 14import re 15import string 16import sys 17 18header = ''' 19// {0} generated from {1} for BTstack 20 21// binary representation 22// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 23 24#include <stdint.h> 25 26const uint8_t profile_data[] = 27''' 28 29usage = ''' 30Usage: ./compile_gatt.py profile.gatt profile.h 31''' 32 33 34print(''' 35BLE configuration generator for use with BTstack, v0.1 36Copyright 2011 Matthias Ringwald 37''') 38 39assigned_uuids = { 40 'GAP_SERVICE' : 0x1800, 41 'GATT_SERVICE' : 0x1801, 42 'GAP_DEVICE_NAME' : 0x2a00, 43 'GAP_APPEARANCE' : 0x2a01, 44 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 45 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 46 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 47 'GATT_SERVICE_CHANGED' : 0x2a05, 48} 49 50property_flags = { 51 # GATT Characteristic Properties 52 'BROADCAST' : 0x01, 53 'READ' : 0x02, 54 'WRITE_WITHOUT_RESPONSE' : 0x04, 55 'WRITE' : 0x08, 56 'NOTIFY': 0x10, 57 'INDICATE' : 0x20, 58 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 59 'EXTENDED_PROPERTIES' : 0x80, 60 # custom BTstack extension 61 'DYNAMIC': 0x100, 62 'LONG_UUID': 0x200, 63 64 # read permissions 65 'READ_PERMISSION_BIT_0': 0x400, 66 'READ_PERMISSION_BIT_1': 0x800, 67 68 # 69 'ENCRYPTION_KEY_SIZE_7': 0x6000, 70 'ENCRYPTION_KEY_SIZE_8': 0x7000, 71 'ENCRYPTION_KEY_SIZE_9': 0x8000, 72 'ENCRYPTION_KEY_SIZE_10': 0x9000, 73 'ENCRYPTION_KEY_SIZE_11': 0xa000, 74 'ENCRYPTION_KEY_SIZE_12': 0xb000, 75 'ENCRYPTION_KEY_SIZE_13': 0xc000, 76 'ENCRYPTION_KEY_SIZE_14': 0xd000, 77 'ENCRYPTION_KEY_SIZE_15': 0xe000, 78 'ENCRYPTION_KEY_SIZE_16': 0xf000, 79 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 80 81 # only used by gatt compiler >= 0xffff 82 # Extended Properties 83 'RELIABLE_WRITE': 0x0010000, 84 'AUTHENTICATION_REQUIRED': 0x0020000, 85 'AUTHORIZATION_REQUIRED': 0x0040000, 86 'READ_ANYBODY': 0x0080000, 87 'READ_ENCRYPTED': 0x0100000, 88 'READ_AUTHENTICATED': 0x0200000, 89 'READ_AUTHORIZED': 0x0400000, 90 'WRITE_ANYBODY': 0x0800000, 91 'WRITE_ENCRYPTED': 0x1000000, 92 'WRITE_AUTHENTICATED': 0x2000000, 93 'WRITE_AUTHORIZED': 0x4000000, 94 95 # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 96 97 # write permissions 98 'WRITE_PERMISSION_BIT_0': 0x01, 99 'WRITE_PERMISSION_BIT_1': 0x10, 100 # 0x20 101 # 0x80 102} 103 104btstack_root = '' 105services = dict() 106characteristic_indices = dict() 107presentation_formats = dict() 108current_service_uuid_string = "" 109current_service_start_handle = 0 110current_characteristic_uuid_string = "" 111defines_for_characteristics = [] 112defines_for_services = [] 113 114handle = 1 115total_size = 0 116 117def read_defines(infile): 118 defines = dict() 119 with open (infile, 'rt') as fin: 120 for line in fin: 121 parts = re.match('#define\s+(\w+)\s+(\w+)',line) 122 if parts and len(parts.groups()) == 2: 123 (key, value) = parts.groups() 124 defines[key] = int(value, 16) 125 return defines 126 127def keyForUUID(uuid): 128 keyUUID = "" 129 for i in uuid: 130 keyUUID += "%02x" % i 131 return keyUUID 132 133def c_string_for_uuid(uuid): 134 return uuid.replace('-', '_') 135 136def twoByteLEFor(value): 137 return [ (value & 0xff), (value >> 8)] 138 139def is_128bit_uuid(text): 140 if re.match("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", text): 141 return True 142 return False 143 144def parseUUID128(uuid): 145 parts = re.match("([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})", uuid) 146 uuid_bytes = [] 147 for i in range(8, 0, -1): 148 uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 149 return uuid_bytes 150 151def parseUUID(uuid): 152 if uuid in assigned_uuids: 153 return twoByteLEFor(assigned_uuids[uuid]) 154 uuid_upper = uuid.upper().replace('.','_') 155 if uuid_upper in bluetooth_gatt: 156 return twoByteLEFor(bluetooth_gatt[uuid_upper]) 157 if is_128bit_uuid(uuid): 158 return parseUUID128(uuid) 159 uuidInt = int(uuid, 16) 160 return twoByteLEFor(uuidInt) 161 162def parseProperties(properties): 163 value = 0 164 parts = properties.split("|") 165 for property in parts: 166 property = property.strip() 167 if property in property_flags: 168 value |= property_flags[property] 169 else: 170 print("WARNING: property %s undefined" % (property)) 171 172 return value; 173 174def gatt_characteristic_properties(properties): 175 return properties & 0xff 176 177def att_flags(properties): 178 print("in %x" % properties) 179 # drop Broadcast (0x01), Notify (0x10), Indicate (0x20)- not used for flags 180 properties &= 0x1ffce 181 182 # rw permissions distinct 183 distinct_permissions_used = properties & ( 184 property_flags['READ_AUTHORIZED'] | 185 property_flags['READ_AUTHENTICATED'] | 186 property_flags['READ_ENCRYPTED'] | 187 property_flags['READ_ANYBODY'] | 188 property_flags['WRITE_AUTHORIZED'] | 189 property_flags['WRITE_AUTHENTICATED'] | 190 property_flags['WRITE_ENCRYPTED'] | 191 property_flags['WRITE_ANYBODY'] 192 ) != 0 193 194 # post process properties 195 encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 196 197 # set encrypted for both, if distinct permissions not used 198 if encryption_key_size_specified and not distinct_permissions_used: 199 properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 200 201 # convert user permissions to att db flags 202 if properties & property_flags['READ_AUTHORIZED']: 203 properties |= property_flags['READ_PERMISSION_BIT_0'] | property_flags['READ_PERMISSION_BIT_1'] 204 elif properties & property_flags['READ_AUTHENTICATED']: 205 properties |= property_flags['READ_PERMISSION_BIT_1'] 206 elif properties & property_flags['READ_ENCRYPTED']: 207 properties |= property_flags['READ_PERMISSION_BIT_0'] 208 if properties & property_flags['WRITE_AUTHORIZED']: 209 properties |= property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'] 210 elif properties & property_flags['WRITE_AUTHENTICATED']: 211 properties |= property_flags['WRITE_PERMISSION_BIT_1'] 212 elif properties & property_flags['WRITE_ENCRYPTED']: 213 properties |= property_flags['WRITE_PERMISSION_BIT_0'] 214 print("out %x" % properties) 215 216 return properties 217 218def write_permissions_and_key_size(properties): 219 return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 220 221def write_8(fout, value): 222 fout.write( "0x%02x, " % (value & 0xff)) 223 224def write_16(fout, value): 225 fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 226 227def write_uuid(uuid): 228 for byte in uuid: 229 fout.write( "0x%02x, " % byte) 230 231def write_string(fout, text): 232 for l in text.lstrip('"').rstrip('"'): 233 write_8(fout, ord(l)) 234 235def write_sequence(fout, text): 236 parts = text.split() 237 for part in parts: 238 fout.write("0x%s, " % (part.strip())) 239 240def write_indent(fout): 241 fout.write(" ") 242 243def is_string(text): 244 for item in text.split(" "): 245 if not all(c in string.hexdigits for c in item): 246 return True 247 return False 248 249def add_client_characteristic_configuration(properties): 250 return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 251 252def serviceDefinitionComplete(fout): 253 global services 254 if current_service_uuid_string: 255 fout.write("\n") 256 # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 257 defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 258 defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 259 services[current_service_uuid_string] = [current_service_start_handle, handle-1] 260 261def parseService(fout, parts, service_type): 262 global handle 263 global total_size 264 global current_service_uuid_string 265 global current_service_start_handle 266 267 serviceDefinitionComplete(fout) 268 269 property = property_flags['READ']; 270 271 write_indent(fout) 272 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 273 274 uuid = parseUUID(parts[1]) 275 uuid_size = len(uuid) 276 277 size = 2 + 2 + 2 + uuid_size + 2 278 279 if service_type == 0x2802: 280 size += 4 281 282 write_indent(fout) 283 write_16(fout, size) 284 write_16(fout, property) 285 write_16(fout, handle) 286 write_16(fout, service_type) 287 write_uuid(uuid) 288 fout.write("\n") 289 290 current_service_uuid_string = c_string_for_uuid(parts[1]) 291 current_service_start_handle = handle 292 handle = handle + 1 293 total_size = total_size + size 294 295def parsePrimaryService(fout, parts): 296 parseService(fout, parts, 0x2800) 297 298def parseSecondaryService(fout, parts): 299 parseService(fout, parts, 0x2801) 300 301def parseIncludeService(fout, parts): 302 global handle 303 global total_size 304 305 read_only_anybody_flags = property_flags['READ']; 306 307 write_indent(fout) 308 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 309 310 uuid = parseUUID(parts[1]) 311 uuid_size = len(uuid) 312 if uuid_size > 2: 313 uuid_size = 0 314 # print("Include Service ", c_string_for_uuid(uuid)) 315 316 size = 2 + 2 + 2 + 2 + 4 + uuid_size 317 318 keyUUID = c_string_for_uuid(parts[1]) 319 320 write_indent(fout) 321 write_16(fout, size) 322 write_16(fout, read_only_anybody_flags) 323 write_16(fout, handle) 324 write_16(fout, 0x2802) 325 write_16(fout, services[keyUUID][0]) 326 write_16(fout, services[keyUUID][1]) 327 if uuid_size > 0: 328 write_uuid(uuid) 329 fout.write("\n") 330 331 handle = handle + 1 332 total_size = total_size + size 333 334 335def parseCharacteristic(fout, parts): 336 global handle 337 global total_size 338 global current_characteristic_uuid_string 339 global characteristic_indices 340 341 read_only_anybody_flags = property_flags['READ']; 342 343 # enumerate characteristics with same UUID, using optional name tag if available 344 current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 345 index = 1 346 if current_characteristic_uuid_string in characteristic_indices: 347 index = characteristic_indices[current_characteristic_uuid_string] + 1 348 characteristic_indices[current_characteristic_uuid_string] = index 349 if len(parts) > 4: 350 current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 351 else: 352 current_characteristic_uuid_string += ('_%02x' % index) 353 354 uuid = parseUUID(parts[1]) 355 uuid_size = len(uuid) 356 properties = parseProperties(parts[2]) 357 value = ', '.join([str(x) for x in parts[3:]]) 358 359 # reliable writes is defined in an extended properties 360 if (properties & property_flags['RELIABLE_WRITE']): 361 properties = properties | property_flags['EXTENDED_PROPERTIES'] 362 363 write_indent(fout) 364 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 365 366 367 characteristic_properties = gatt_characteristic_properties(properties) 368 size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 369 write_indent(fout) 370 write_16(fout, size) 371 write_16(fout, read_only_anybody_flags) 372 write_16(fout, handle) 373 write_16(fout, 0x2803) 374 write_8(fout, characteristic_properties) 375 write_16(fout, handle+1) 376 write_uuid(uuid) 377 fout.write("\n") 378 handle = handle + 1 379 total_size = total_size + size 380 381 size = 2 + 2 + 2 + uuid_size 382 if is_string(value): 383 size = size + len(value) 384 else: 385 size = size + len(value.split()) 386 387 value_flags = att_flags(properties) 388 389 # add UUID128 flag for value handle 390 if uuid_size == 16: 391 value_flags = value_flags | property_flags['LONG_UUID']; 392 393 write_indent(fout) 394 fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 395 write_indent(fout) 396 write_16(fout, size) 397 write_16(fout, value_flags) 398 write_16(fout, handle) 399 write_uuid(uuid) 400 if is_string(value): 401 write_string(fout, value) 402 else: 403 write_sequence(fout,value) 404 405 fout.write("\n") 406 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 407 handle = handle + 1 408 409 if add_client_characteristic_configuration(properties): 410 # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 411 flags = write_permissions_and_key_size(properties) 412 flags |= property_flags['READ'] 413 flags |= property_flags['WRITE'] 414 flags |= property_flags['DYNAMIC'] 415 size = 2 + 2 + 2 + 2 + 2 416 write_indent(fout) 417 fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 418 write_indent(fout) 419 write_16(fout, size) 420 write_16(fout, flags) 421 write_16(fout, handle) 422 write_16(fout, 0x2902) 423 write_16(fout, 0) 424 fout.write("\n") 425 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 426 handle = handle + 1 427 428 if properties & property_flags['RELIABLE_WRITE']: 429 size = 2 + 2 + 2 + 2 + 2 430 write_indent(fout) 431 fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 432 write_indent(fout) 433 write_16(fout, size) 434 write_16(fout, read_only_anybody_flags) 435 write_16(fout, handle) 436 write_16(fout, 0x2900) 437 write_16(fout, 1) # Reliable Write 438 fout.write("\n") 439 handle = handle + 1 440 441def parseCharacteristicUserDescription(fout, parts): 442 global handle 443 global total_size 444 global current_characteristic_uuid_string 445 446 properties = parseProperties(parts[1]) 447 value = parts[2] 448 449 size = 2 + 2 + 2 + 2 450 if is_string(value): 451 size = size + len(value) - 2 452 else: 453 size = size + len(value.split()) 454 455 # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 456 flags = write_permissions_and_key_size(properties) 457 flags |= properties & property_flags['WRITE'] 458 flags |= property_flags['READ'] 459 460 write_indent(fout) 461 fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 462 write_indent(fout) 463 write_16(fout, size) 464 write_16(fout, flags) 465 write_16(fout, handle) 466 write_16(fout, 0x2901) 467 if is_string(value): 468 write_string(fout, value) 469 else: 470 write_sequence(fout,value) 471 fout.write("\n") 472 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 473 handle = handle + 1 474 475def parseServerCharacteristicConfiguration(fout, parts): 476 global handle 477 global total_size 478 global current_characteristic_uuid_string 479 480 properties = parseProperties(parts[1]) 481 size = 2 + 2 + 2 + 2 482 483 # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 484 flags = write_permissions_and_key_size(properties) 485 flags |= property_flags['READ'] 486 flags |= property_flags['WRITE'] 487 flags |= property_flags['DYNAMIC'] 488 489 write_indent(fout) 490 fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 491 write_indent(fout) 492 write_16(fout, size) 493 write_16(fout, flags) 494 write_16(fout, handle) 495 write_16(fout, 0x2903) 496 fout.write("\n") 497 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 498 handle = handle + 1 499 500def parseCharacteristicFormat(fout, parts): 501 global handle 502 global total_size 503 504 read_only_anybody_flags = property_flags['READ']; 505 506 identifier = parts[1] 507 presentation_formats[identifier] = handle 508 # print("format '%s' with handle %d\n" % (identifier, handle)) 509 510 format = parts[2] 511 exponent = parts[3] 512 unit = parseUUID(parts[4]) 513 name_space = parts[5] 514 description = parseUUID(parts[6]) 515 516 size = 2 + 2 + 2 + 2 + 7 517 518 write_indent(fout) 519 fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 520 write_indent(fout) 521 write_16(fout, size) 522 write_16(fout, read_only_anybody_flags) 523 write_16(fout, handle) 524 write_16(fout, 0x2904) 525 write_sequence(fout, format) 526 write_sequence(fout, exponent) 527 write_uuid(unit) 528 write_sequence(fout, name_space) 529 write_uuid(description) 530 fout.write("\n") 531 handle = handle + 1 532 533 534def parseCharacteristicAggregateFormat(fout, parts): 535 global handle 536 global total_size 537 538 read_only_anybody_flags = property_flags['READ']; 539 size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 540 541 write_indent(fout) 542 fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 543 write_indent(fout) 544 write_16(fout, size) 545 write_16(fout, read_only_anybody_flags) 546 write_16(fout, handle) 547 write_16(fout, 0x2905) 548 for identifier in parts[1:]: 549 format_handle = presentation_formats[identifier] 550 if format == 0: 551 print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 552 sys.exit(1) 553 write_16(fout, format_handle) 554 fout.write("\n") 555 handle = handle + 1 556 557def parseReportReference(fout, parts): 558 global handle 559 global total_size 560 561 read_only_anybody_flags = property_flags['READ']; 562 size = 2 + 2 + 2 + 2 + 1 + 1 563 564 report_id = parts[2] 565 report_type = parts[3] 566 567 write_indent(fout) 568 fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 569 write_indent(fout) 570 write_16(fout, size) 571 write_16(fout, read_only_anybody_flags) 572 write_16(fout, handle) 573 write_16(fout, 0x2908) 574 write_sequence(fout, report_id) 575 write_sequence(fout, report_type) 576 fout.write("\n") 577 handle = handle + 1 578 579 580def parseNumberOfDigitals(fout, parts): 581 global handle 582 global total_size 583 584 read_only_anybody_flags = property_flags['READ']; 585 size = 2 + 2 + 2 + 2 + 1 586 587 no_of_digitals = parts[1] 588 589 write_indent(fout) 590 fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 591 write_indent(fout) 592 write_16(fout, size) 593 write_16(fout, read_only_anybody_flags) 594 write_16(fout, handle) 595 write_16(fout, 0x2909) 596 write_sequence(fout, no_of_digitals) 597 fout.write("\n") 598 handle = handle + 1 599 600def parseLines(fname_in, fin, fout): 601 global handle 602 global total_size 603 604 line_count = 0; 605 for line in fin: 606 line = line.strip("\n\r ") 607 line_count += 1 608 609 if line.startswith("//"): 610 fout.write(" //" + line.lstrip('/') + '\n') 611 continue 612 613 if line.startswith("#import"): 614 imported_file = '' 615 parts = re.match('#import\s+<(.*)>\w*',line) 616 if parts and len(parts.groups()) == 1: 617 imported_file = btstack_root+'/src/ble/gatt-service/' + parts.groups()[0] 618 parts = re.match('#import\s+"(.*)"\w*',line) 619 if parts and len(parts.groups()) == 1: 620 imported_file = os.path.abspath(os.path.dirname(fname_in) + '/'+parts.groups()[0]) 621 if len(imported_file) == 0: 622 print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 623 continue 624 625 print("Importing %s" % imported_file) 626 try: 627 imported_fin = codecs.open (imported_file, encoding='utf-8') 628 fout.write(' // ' + line + ' -- BEGIN\n') 629 parseLines(imported_file, imported_fin, fout) 630 fout.write(' // ' + line + ' -- END\n') 631 except IOError as e: 632 print('ERROR: Import failed. Please check path.') 633 634 continue 635 636 if line.startswith("#TODO"): 637 print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 638 print ("'%s'" % line) 639 fout.write("// " + line + '\n') 640 continue 641 642 if len(line) == 0: 643 continue 644 645 f = io.StringIO(line) 646 parts_list = csv.reader(f, delimiter=',', quotechar='"') 647 648 for parts in parts_list: 649 for index, object in enumerate(parts): 650 parts[index] = object.strip().lstrip('"').rstrip('"') 651 652 if parts[0] == 'PRIMARY_SERVICE': 653 parsePrimaryService(fout, parts) 654 continue 655 656 if parts[0] == 'SECONDARY_SERVICE': 657 parseSecondaryService(fout, parts) 658 continue 659 660 if parts[0] == 'INCLUDE_SERVICE': 661 parseIncludeService(fout, parts) 662 continue 663 664 # 2803 665 if parts[0] == 'CHARACTERISTIC': 666 parseCharacteristic(fout, parts) 667 continue 668 669 # 2900 Characteristic Extended Properties 670 671 # 2901 672 if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 673 parseCharacteristicUserDescription(fout, parts) 674 continue 675 676 677 # 2902 Client Characteristic Configuration - automatically included in Characteristic if 678 # notification / indication is supported 679 if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 680 continue 681 682 # 2903 683 if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 684 parseServerCharacteristicConfiguration(fout, parts) 685 continue 686 687 # 2904 688 if parts[0] == 'CHARACTERISTIC_FORMAT': 689 parseCharacteristicFormat(fout, parts) 690 continue 691 692 # 2905 693 if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 694 parseCharacteristicAggregateFormat(fout, parts) 695 continue 696 697 # 2906 698 if parts[0] == 'VALID_RANGE': 699 print("WARNING: %s not implemented yet\n" % (parts[0])) 700 continue 701 702 # 2907 703 if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 704 print("WARNING: %s not implemented yet\n" % (parts[0])) 705 continue 706 707 # 2908 708 if parts[0] == 'REPORT_REFERENCE': 709 parseReportReference(fout, parts) 710 continue 711 712 # 2909 713 if parts[0] == 'NUMBER_OF_DIGITALS': 714 parseNumberOfDigitals(fout, parts) 715 continue 716 717 # 290A 718 if parts[0] == 'VALUE_TRIGGER_SETTING': 719 print("WARNING: %s not implemented yet\n" % (parts[0])) 720 continue 721 722 # 290B 723 if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 724 print("WARNING: %s not implemented yet\n" % (parts[0])) 725 continue 726 727 # 290C 728 if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 729 print("WARNING: %s not implemented yet\n" % (parts[0])) 730 continue 731 732 # 290D 733 if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 734 print("WARNING: %s not implemented yet\n" % (parts[0])) 735 continue 736 737 # 2906 738 if parts[0] == 'VALID_RANGE': 739 print("WARNING: %s not implemented yet\n" % (parts[0])) 740 continue 741 742 print("WARNING: unknown token: %s\n" % (parts[0])) 743 744def parse(fname_in, fin, fname_out, fout): 745 global handle 746 global total_size 747 748 fout.write(header.format(fname_out, fname_in)) 749 fout.write('{\n') 750 751 parseLines(fname_in, fin, fout) 752 753 serviceDefinitionComplete(fout) 754 write_indent(fout) 755 fout.write("// END\n"); 756 write_indent(fout) 757 write_16(fout,0) 758 fout.write("\n") 759 total_size = total_size + 2 760 761 fout.write("}; // total size %u bytes \n" % total_size); 762 763def listHandles(fout): 764 fout.write('\n\n') 765 fout.write('//\n') 766 fout.write('// list service handle ranges\n') 767 fout.write('//\n') 768 for define in defines_for_services: 769 fout.write(define) 770 fout.write('\n') 771 fout.write('\n') 772 fout.write('//\n') 773 fout.write('// list mapping between characteristics and handles\n') 774 fout.write('//\n') 775 for define in defines_for_characteristics: 776 fout.write(define) 777 fout.write('\n') 778 779if (len(sys.argv) < 3): 780 print(usage) 781 sys.exit(1) 782try: 783 # read defines from bluetooth_gatt.h 784 btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 785 gen_path = btstack_root + '/src/bluetooth_gatt.h' 786 bluetooth_gatt = read_defines(gen_path) 787 788 filename = sys.argv[2] 789 fin = codecs.open (sys.argv[1], encoding='utf-8') 790 fout = open (filename, 'w') 791 parse(sys.argv[1], fin, filename, fout) 792 listHandles(fout) 793 fout.close() 794 print('Created %s' % filename) 795 796except IOError as e: 797 798 print(usage) 799 sys.exit(1) 800 801print('Compilation successful!\n') 802