1*b3fcedb9SMatthias Ringwald#!/usr/bin/env python 2*b3fcedb9SMatthias Ringwald# 3*b3fcedb9SMatthias Ringwald# BLE GATT configuration generator for use with BTstack, v0.1 4*b3fcedb9SMatthias Ringwald# Copyright 2011 Matthias Ringwald 5*b3fcedb9SMatthias Ringwald# 6*b3fcedb9SMatthias Ringwald# Format of input file: 7*b3fcedb9SMatthias Ringwald# PRIMARY_SERVICE, SERVICE_UUID 8*b3fcedb9SMatthias Ringwald# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9*b3fcedb9SMatthias Ringwald 10*b3fcedb9SMatthias Ringwaldimport re 11*b3fcedb9SMatthias Ringwaldimport io 12*b3fcedb9SMatthias Ringwaldimport csv 13*b3fcedb9SMatthias Ringwaldimport string 14*b3fcedb9SMatthias Ringwaldimport codecs 15*b3fcedb9SMatthias Ringwald 16*b3fcedb9SMatthias Ringwaldheader = ''' 17*b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 18*b3fcedb9SMatthias Ringwald 19*b3fcedb9SMatthias Ringwald// binary representation 20*b3fcedb9SMatthias Ringwald// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 21*b3fcedb9SMatthias Ringwald 22*b3fcedb9SMatthias Ringwald#include <stdint.h> 23*b3fcedb9SMatthias Ringwald 24*b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 25*b3fcedb9SMatthias Ringwald''' 26*b3fcedb9SMatthias Ringwald 27*b3fcedb9SMatthias Ringwaldusage = ''' 28*b3fcedb9SMatthias RingwaldUsage: ./compile_gatt.py profile.gatt profile.h 29*b3fcedb9SMatthias Ringwald''' 30*b3fcedb9SMatthias Ringwald 31*b3fcedb9SMatthias Ringwaldimport re 32*b3fcedb9SMatthias Ringwaldimport sys 33*b3fcedb9SMatthias Ringwald 34*b3fcedb9SMatthias Ringwaldprint(''' 35*b3fcedb9SMatthias RingwaldBLE configuration generator for use with BTstack, v0.1 36*b3fcedb9SMatthias RingwaldCopyright 2011 Matthias Ringwald 37*b3fcedb9SMatthias Ringwald''') 38*b3fcedb9SMatthias Ringwald 39*b3fcedb9SMatthias Ringwaldassigned_uuids = { 40*b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 41*b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 42*b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 43*b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 44*b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 45*b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 46*b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 47*b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 48*b3fcedb9SMatthias Ringwald} 49*b3fcedb9SMatthias Ringwald 50*b3fcedb9SMatthias Ringwaldproperty_flags = { 51*b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 52*b3fcedb9SMatthias Ringwald 'READ' : 0x02, 53*b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 54*b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 55*b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 56*b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 57*b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 58*b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 59*b3fcedb9SMatthias Ringwald # custom BTstack extension 60*b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 61*b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 62*b3fcedb9SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x400, 63*b3fcedb9SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x800, 64*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 65*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 66*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 67*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 68*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 69*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 70*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 71*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 72*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 73*b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 74*b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 75*b3fcedb9SMatthias Ringwald # Extended Properties 76*b3fcedb9SMatthias Ringwald 'RELIABLE_WRITE': 0x10000, 77*b3fcedb9SMatthias Ringwald} 78*b3fcedb9SMatthias Ringwald 79*b3fcedb9SMatthias Ringwaldservices = dict() 80*b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 81*b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 82*b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 83*b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 84*b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 85*b3fcedb9SMatthias Ringwalddefines = [] 86*b3fcedb9SMatthias Ringwald 87*b3fcedb9SMatthias Ringwaldhandle = 1 88*b3fcedb9SMatthias Ringwaldtotal_size = 0 89*b3fcedb9SMatthias Ringwald 90*b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 91*b3fcedb9SMatthias Ringwald keyUUID = "" 92*b3fcedb9SMatthias Ringwald for i in uuid: 93*b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 94*b3fcedb9SMatthias Ringwald return keyUUID 95*b3fcedb9SMatthias Ringwald 96*b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 97*b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 98*b3fcedb9SMatthias Ringwald 99*b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 100*b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 101*b3fcedb9SMatthias Ringwald 102*b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 103*b3fcedb9SMatthias Ringwald 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): 104*b3fcedb9SMatthias Ringwald return True 105*b3fcedb9SMatthias Ringwald return False 106*b3fcedb9SMatthias Ringwald 107*b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 108*b3fcedb9SMatthias Ringwald 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) 109*b3fcedb9SMatthias Ringwald uuid_bytes = [] 110*b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 111*b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 112*b3fcedb9SMatthias Ringwald return uuid_bytes 113*b3fcedb9SMatthias Ringwald 114*b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 115*b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 116*b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 117*b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 118*b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 119*b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 120*b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 121*b3fcedb9SMatthias Ringwald 122*b3fcedb9SMatthias Ringwalddef parseProperties(properties): 123*b3fcedb9SMatthias Ringwald value = 0 124*b3fcedb9SMatthias Ringwald parts = properties.split("|") 125*b3fcedb9SMatthias Ringwald for property in parts: 126*b3fcedb9SMatthias Ringwald property = property.strip() 127*b3fcedb9SMatthias Ringwald if property in property_flags: 128*b3fcedb9SMatthias Ringwald value |= property_flags[property] 129*b3fcedb9SMatthias Ringwald else: 130*b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 131*b3fcedb9SMatthias Ringwald return value 132*b3fcedb9SMatthias Ringwald 133*b3fcedb9SMatthias Ringwalddef write_8(fout, value): 134*b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 135*b3fcedb9SMatthias Ringwald 136*b3fcedb9SMatthias Ringwalddef write_16(fout, value): 137*b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 138*b3fcedb9SMatthias Ringwald 139*b3fcedb9SMatthias Ringwalddef write_uuid(uuid): 140*b3fcedb9SMatthias Ringwald for byte in uuid: 141*b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 142*b3fcedb9SMatthias Ringwald 143*b3fcedb9SMatthias Ringwalddef write_string(fout, text): 144*b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 145*b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 146*b3fcedb9SMatthias Ringwald 147*b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 148*b3fcedb9SMatthias Ringwald parts = text.split() 149*b3fcedb9SMatthias Ringwald for part in parts: 150*b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 151*b3fcedb9SMatthias Ringwald 152*b3fcedb9SMatthias Ringwalddef write_indent(fout): 153*b3fcedb9SMatthias Ringwald fout.write(" ") 154*b3fcedb9SMatthias Ringwald 155*b3fcedb9SMatthias Ringwalddef is_string(text): 156*b3fcedb9SMatthias Ringwald for item in text.split(" "): 157*b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 158*b3fcedb9SMatthias Ringwald return True 159*b3fcedb9SMatthias Ringwald return False 160*b3fcedb9SMatthias Ringwald 161*b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 162*b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 163*b3fcedb9SMatthias Ringwald 164*b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 165*b3fcedb9SMatthias Ringwald global handle 166*b3fcedb9SMatthias Ringwald global total_size 167*b3fcedb9SMatthias Ringwald global current_service_uuid_string 168*b3fcedb9SMatthias Ringwald global current_service_start_handle 169*b3fcedb9SMatthias Ringwald global services 170*b3fcedb9SMatthias Ringwald 171*b3fcedb9SMatthias Ringwald if current_service_uuid_string: 172*b3fcedb9SMatthias Ringwald fout.write("\n") 173*b3fcedb9SMatthias Ringwald # print("append service %s = [%d, %d]\n" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 174*b3fcedb9SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 175*b3fcedb9SMatthias Ringwald 176*b3fcedb9SMatthias Ringwald property = property_flags['READ']; 177*b3fcedb9SMatthias Ringwald 178*b3fcedb9SMatthias Ringwald write_indent(fout) 179*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 180*b3fcedb9SMatthias Ringwald 181*b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 182*b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 183*b3fcedb9SMatthias Ringwald 184*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 185*b3fcedb9SMatthias Ringwald 186*b3fcedb9SMatthias Ringwald if service_type == 0x2802: 187*b3fcedb9SMatthias Ringwald size += 4 188*b3fcedb9SMatthias Ringwald 189*b3fcedb9SMatthias Ringwald write_indent(fout) 190*b3fcedb9SMatthias Ringwald write_16(fout, size) 191*b3fcedb9SMatthias Ringwald write_16(fout, property) 192*b3fcedb9SMatthias Ringwald write_16(fout, handle) 193*b3fcedb9SMatthias Ringwald write_16(fout, service_type) 194*b3fcedb9SMatthias Ringwald write_uuid(uuid) 195*b3fcedb9SMatthias Ringwald fout.write("\n") 196*b3fcedb9SMatthias Ringwald 197*b3fcedb9SMatthias Ringwald current_service_uuid_string = keyForUUID(uuid) 198*b3fcedb9SMatthias Ringwald current_service_start_handle = handle 199*b3fcedb9SMatthias Ringwald handle = handle + 1 200*b3fcedb9SMatthias Ringwald total_size = total_size + size 201*b3fcedb9SMatthias Ringwald 202*b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 203*b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 204*b3fcedb9SMatthias Ringwald 205*b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 206*b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 207*b3fcedb9SMatthias Ringwald 208*b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 209*b3fcedb9SMatthias Ringwald global handle 210*b3fcedb9SMatthias Ringwald global total_size 211*b3fcedb9SMatthias Ringwald 212*b3fcedb9SMatthias Ringwald property = property_flags['READ']; 213*b3fcedb9SMatthias Ringwald 214*b3fcedb9SMatthias Ringwald write_indent(fout) 215*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 216*b3fcedb9SMatthias Ringwald 217*b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 218*b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 219*b3fcedb9SMatthias Ringwald if uuid_size > 2: 220*b3fcedb9SMatthias Ringwald uuid_size = 0 221*b3fcedb9SMatthias Ringwald # print("Include Service ", keyForUUID(uuid)) 222*b3fcedb9SMatthias Ringwald 223*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 224*b3fcedb9SMatthias Ringwald 225*b3fcedb9SMatthias Ringwald keyUUID = keyForUUID(uuid) 226*b3fcedb9SMatthias Ringwald 227*b3fcedb9SMatthias Ringwald write_indent(fout) 228*b3fcedb9SMatthias Ringwald write_16(fout, size) 229*b3fcedb9SMatthias Ringwald write_16(fout, property) 230*b3fcedb9SMatthias Ringwald write_16(fout, handle) 231*b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 232*b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 233*b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 234*b3fcedb9SMatthias Ringwald if uuid_size > 0: 235*b3fcedb9SMatthias Ringwald write_uuid(uuid) 236*b3fcedb9SMatthias Ringwald fout.write("\n") 237*b3fcedb9SMatthias Ringwald 238*b3fcedb9SMatthias Ringwald handle = handle + 1 239*b3fcedb9SMatthias Ringwald total_size = total_size + size 240*b3fcedb9SMatthias Ringwald 241*b3fcedb9SMatthias Ringwald 242*b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 243*b3fcedb9SMatthias Ringwald global handle 244*b3fcedb9SMatthias Ringwald global total_size 245*b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 246*b3fcedb9SMatthias Ringwald global characteristic_indices 247*b3fcedb9SMatthias Ringwald 248*b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 249*b3fcedb9SMatthias Ringwald 250*b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 251*b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 252*b3fcedb9SMatthias Ringwald index = 1 253*b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 254*b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 255*b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 256*b3fcedb9SMatthias Ringwald if len(parts) > 4: 257*b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 258*b3fcedb9SMatthias Ringwald else: 259*b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 260*b3fcedb9SMatthias Ringwald 261*b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 262*b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 263*b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 264*b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 265*b3fcedb9SMatthias Ringwald 266*b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 267*b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 268*b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 269*b3fcedb9SMatthias Ringwald 270*b3fcedb9SMatthias Ringwald write_indent(fout) 271*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 272*b3fcedb9SMatthias Ringwald 273*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 274*b3fcedb9SMatthias Ringwald write_indent(fout) 275*b3fcedb9SMatthias Ringwald write_16(fout, size) 276*b3fcedb9SMatthias Ringwald write_16(fout, property_read) 277*b3fcedb9SMatthias Ringwald write_16(fout, handle) 278*b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 279*b3fcedb9SMatthias Ringwald write_8(fout, properties) 280*b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 281*b3fcedb9SMatthias Ringwald write_uuid(uuid) 282*b3fcedb9SMatthias Ringwald fout.write("\n") 283*b3fcedb9SMatthias Ringwald handle = handle + 1 284*b3fcedb9SMatthias Ringwald total_size = total_size + size 285*b3fcedb9SMatthias Ringwald 286*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 287*b3fcedb9SMatthias Ringwald if is_string(value): 288*b3fcedb9SMatthias Ringwald size = size + len(value) 289*b3fcedb9SMatthias Ringwald else: 290*b3fcedb9SMatthias Ringwald size = size + len(value.split()) 291*b3fcedb9SMatthias Ringwald 292*b3fcedb9SMatthias Ringwald if uuid_size == 16: 293*b3fcedb9SMatthias Ringwald properties = properties | property_flags['LONG_UUID']; 294*b3fcedb9SMatthias Ringwald 295*b3fcedb9SMatthias Ringwald write_indent(fout) 296*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 297*b3fcedb9SMatthias Ringwald write_indent(fout) 298*b3fcedb9SMatthias Ringwald write_16(fout, size) 299*b3fcedb9SMatthias Ringwald write_16(fout, properties) 300*b3fcedb9SMatthias Ringwald write_16(fout, handle) 301*b3fcedb9SMatthias Ringwald write_uuid(uuid) 302*b3fcedb9SMatthias Ringwald if is_string(value): 303*b3fcedb9SMatthias Ringwald write_string(fout, value) 304*b3fcedb9SMatthias Ringwald else: 305*b3fcedb9SMatthias Ringwald write_sequence(fout,value) 306*b3fcedb9SMatthias Ringwald 307*b3fcedb9SMatthias Ringwald fout.write("\n") 308*b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 309*b3fcedb9SMatthias Ringwald handle = handle + 1 310*b3fcedb9SMatthias Ringwald 311*b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 312*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 313*b3fcedb9SMatthias Ringwald write_indent(fout) 314*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 315*b3fcedb9SMatthias Ringwald write_indent(fout) 316*b3fcedb9SMatthias Ringwald write_16(fout, size) 317*b3fcedb9SMatthias Ringwald write_16(fout, property_flags['READ'] | property_flags['WRITE'] | property_flags['DYNAMIC']) 318*b3fcedb9SMatthias Ringwald write_16(fout, handle) 319*b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 320*b3fcedb9SMatthias Ringwald write_16(fout, 0) 321*b3fcedb9SMatthias Ringwald fout.write("\n") 322*b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 323*b3fcedb9SMatthias Ringwald handle = handle + 1 324*b3fcedb9SMatthias Ringwald 325*b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 326*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 327*b3fcedb9SMatthias Ringwald write_indent(fout) 328*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 329*b3fcedb9SMatthias Ringwald write_indent(fout) 330*b3fcedb9SMatthias Ringwald write_16(fout, size) 331*b3fcedb9SMatthias Ringwald write_16(fout, property_flags['READ']) 332*b3fcedb9SMatthias Ringwald write_16(fout, handle) 333*b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 334*b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 335*b3fcedb9SMatthias Ringwald fout.write("\n") 336*b3fcedb9SMatthias Ringwald handle = handle + 1 337*b3fcedb9SMatthias Ringwald 338*b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 339*b3fcedb9SMatthias Ringwald global handle 340*b3fcedb9SMatthias Ringwald global total_size 341*b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 342*b3fcedb9SMatthias Ringwald 343*b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 344*b3fcedb9SMatthias Ringwald value = parts[2] 345*b3fcedb9SMatthias Ringwald 346*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 347*b3fcedb9SMatthias Ringwald if is_string(value): 348*b3fcedb9SMatthias Ringwald size = size + len(value) - 2 349*b3fcedb9SMatthias Ringwald else: 350*b3fcedb9SMatthias Ringwald size = size + len(value.split()) 351*b3fcedb9SMatthias Ringwald 352*b3fcedb9SMatthias Ringwald write_indent(fout) 353*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 354*b3fcedb9SMatthias Ringwald write_indent(fout) 355*b3fcedb9SMatthias Ringwald write_16(fout, size) 356*b3fcedb9SMatthias Ringwald write_16(fout, properties) 357*b3fcedb9SMatthias Ringwald write_16(fout, handle) 358*b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 359*b3fcedb9SMatthias Ringwald if is_string(value): 360*b3fcedb9SMatthias Ringwald write_string(fout, value) 361*b3fcedb9SMatthias Ringwald else: 362*b3fcedb9SMatthias Ringwald write_sequence(fout,value) 363*b3fcedb9SMatthias Ringwald fout.write("\n") 364*b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 365*b3fcedb9SMatthias Ringwald handle = handle + 1 366*b3fcedb9SMatthias Ringwald 367*b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 368*b3fcedb9SMatthias Ringwald global handle 369*b3fcedb9SMatthias Ringwald global total_size 370*b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 371*b3fcedb9SMatthias Ringwald 372*b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 373*b3fcedb9SMatthias Ringwald properties = properties | property_flags['DYNAMIC'] 374*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 375*b3fcedb9SMatthias Ringwald 376*b3fcedb9SMatthias Ringwald write_indent(fout) 377*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 378*b3fcedb9SMatthias Ringwald write_indent(fout) 379*b3fcedb9SMatthias Ringwald write_16(fout, size) 380*b3fcedb9SMatthias Ringwald write_16(fout, properties) 381*b3fcedb9SMatthias Ringwald write_16(fout, handle) 382*b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 383*b3fcedb9SMatthias Ringwald fout.write("\n") 384*b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 385*b3fcedb9SMatthias Ringwald handle = handle + 1 386*b3fcedb9SMatthias Ringwald 387*b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 388*b3fcedb9SMatthias Ringwald global handle 389*b3fcedb9SMatthias Ringwald global total_size 390*b3fcedb9SMatthias Ringwald 391*b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 392*b3fcedb9SMatthias Ringwald 393*b3fcedb9SMatthias Ringwald identifier = parts[1] 394*b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 395*b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 396*b3fcedb9SMatthias Ringwald 397*b3fcedb9SMatthias Ringwald format = parts[2] 398*b3fcedb9SMatthias Ringwald exponent = parts[3] 399*b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 400*b3fcedb9SMatthias Ringwald name_space = parts[5] 401*b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 402*b3fcedb9SMatthias Ringwald 403*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 404*b3fcedb9SMatthias Ringwald 405*b3fcedb9SMatthias Ringwald write_indent(fout) 406*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 407*b3fcedb9SMatthias Ringwald write_indent(fout) 408*b3fcedb9SMatthias Ringwald write_16(fout, size) 409*b3fcedb9SMatthias Ringwald write_16(fout, property_read) 410*b3fcedb9SMatthias Ringwald write_16(fout, handle) 411*b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 412*b3fcedb9SMatthias Ringwald write_sequence(fout, format) 413*b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 414*b3fcedb9SMatthias Ringwald write_uuid(unit) 415*b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 416*b3fcedb9SMatthias Ringwald write_uuid(description) 417*b3fcedb9SMatthias Ringwald fout.write("\n") 418*b3fcedb9SMatthias Ringwald handle = handle + 1 419*b3fcedb9SMatthias Ringwald 420*b3fcedb9SMatthias Ringwald 421*b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 422*b3fcedb9SMatthias Ringwald global handle 423*b3fcedb9SMatthias Ringwald global total_size 424*b3fcedb9SMatthias Ringwald 425*b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 426*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 427*b3fcedb9SMatthias Ringwald 428*b3fcedb9SMatthias Ringwald write_indent(fout) 429*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 430*b3fcedb9SMatthias Ringwald write_indent(fout) 431*b3fcedb9SMatthias Ringwald write_16(fout, size) 432*b3fcedb9SMatthias Ringwald write_16(fout, property_read) 433*b3fcedb9SMatthias Ringwald write_16(fout, handle) 434*b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 435*b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 436*b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 437*b3fcedb9SMatthias Ringwald if format == 0: 438*b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 439*b3fcedb9SMatthias Ringwald sys.exit(1) 440*b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 441*b3fcedb9SMatthias Ringwald fout.write("\n") 442*b3fcedb9SMatthias Ringwald handle = handle + 1 443*b3fcedb9SMatthias Ringwald 444*b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 445*b3fcedb9SMatthias Ringwald global handle 446*b3fcedb9SMatthias Ringwald global total_size 447*b3fcedb9SMatthias Ringwald 448*b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 449*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 450*b3fcedb9SMatthias Ringwald 451*b3fcedb9SMatthias Ringwald report_id = parts[1] 452*b3fcedb9SMatthias Ringwald report_type = parts[2] 453*b3fcedb9SMatthias Ringwald 454*b3fcedb9SMatthias Ringwald write_indent(fout) 455*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 456*b3fcedb9SMatthias Ringwald write_indent(fout) 457*b3fcedb9SMatthias Ringwald write_16(fout, size) 458*b3fcedb9SMatthias Ringwald write_16(fout, property_read) 459*b3fcedb9SMatthias Ringwald write_16(fout, handle) 460*b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 461*b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 462*b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 463*b3fcedb9SMatthias Ringwald fout.write("\n") 464*b3fcedb9SMatthias Ringwald handle = handle + 1 465*b3fcedb9SMatthias Ringwald 466*b3fcedb9SMatthias Ringwald 467*b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 468*b3fcedb9SMatthias Ringwald global handle 469*b3fcedb9SMatthias Ringwald global total_size 470*b3fcedb9SMatthias Ringwald 471*b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 472*b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 473*b3fcedb9SMatthias Ringwald 474*b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 475*b3fcedb9SMatthias Ringwald 476*b3fcedb9SMatthias Ringwald write_indent(fout) 477*b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 478*b3fcedb9SMatthias Ringwald write_indent(fout) 479*b3fcedb9SMatthias Ringwald write_16(fout, size) 480*b3fcedb9SMatthias Ringwald write_16(fout, property_read) 481*b3fcedb9SMatthias Ringwald write_16(fout, handle) 482*b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 483*b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 484*b3fcedb9SMatthias Ringwald fout.write("\n") 485*b3fcedb9SMatthias Ringwald handle = handle + 1 486*b3fcedb9SMatthias Ringwald 487*b3fcedb9SMatthias Ringwald 488*b3fcedb9SMatthias Ringwalddef parse(fname_in, fin, fname_out, fout): 489*b3fcedb9SMatthias Ringwald global handle 490*b3fcedb9SMatthias Ringwald global total_size 491*b3fcedb9SMatthias Ringwald 492*b3fcedb9SMatthias Ringwald fout.write(header.format(fname_out, fname_in)) 493*b3fcedb9SMatthias Ringwald fout.write('{\n') 494*b3fcedb9SMatthias Ringwald 495*b3fcedb9SMatthias Ringwald for line in fin: 496*b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 497*b3fcedb9SMatthias Ringwald 498*b3fcedb9SMatthias Ringwald if line.startswith("#") or line.startswith("//"): 499*b3fcedb9SMatthias Ringwald fout.write("//" + line.lstrip('/#') + '\n') 500*b3fcedb9SMatthias Ringwald continue 501*b3fcedb9SMatthias Ringwald 502*b3fcedb9SMatthias Ringwald if len(line) == 0: 503*b3fcedb9SMatthias Ringwald continue 504*b3fcedb9SMatthias Ringwald 505*b3fcedb9SMatthias Ringwald f = io.StringIO(line) 506*b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 507*b3fcedb9SMatthias Ringwald 508*b3fcedb9SMatthias Ringwald for parts in parts_list: 509*b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 510*b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 511*b3fcedb9SMatthias Ringwald 512*b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 513*b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 514*b3fcedb9SMatthias Ringwald continue 515*b3fcedb9SMatthias Ringwald 516*b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 517*b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 518*b3fcedb9SMatthias Ringwald continue 519*b3fcedb9SMatthias Ringwald 520*b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 521*b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 522*b3fcedb9SMatthias Ringwald continue 523*b3fcedb9SMatthias Ringwald 524*b3fcedb9SMatthias Ringwald # 2803 525*b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 526*b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 527*b3fcedb9SMatthias Ringwald continue 528*b3fcedb9SMatthias Ringwald 529*b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 530*b3fcedb9SMatthias Ringwald 531*b3fcedb9SMatthias Ringwald # 2901 532*b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 533*b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 534*b3fcedb9SMatthias Ringwald continue 535*b3fcedb9SMatthias Ringwald 536*b3fcedb9SMatthias Ringwald # 2902 Client Characteristic Configuration - included in Characteristic if 537*b3fcedb9SMatthias Ringwald # notification / indication is supported 538*b3fcedb9SMatthias Ringwald 539*b3fcedb9SMatthias Ringwald # 2903 540*b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 541*b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 542*b3fcedb9SMatthias Ringwald continue 543*b3fcedb9SMatthias Ringwald 544*b3fcedb9SMatthias Ringwald # 2904 545*b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 546*b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 547*b3fcedb9SMatthias Ringwald continue 548*b3fcedb9SMatthias Ringwald 549*b3fcedb9SMatthias Ringwald # 2905 550*b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 551*b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 552*b3fcedb9SMatthias Ringwald continue 553*b3fcedb9SMatthias Ringwald 554*b3fcedb9SMatthias Ringwald # 2906 555*b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 556*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 557*b3fcedb9SMatthias Ringwald continue 558*b3fcedb9SMatthias Ringwald 559*b3fcedb9SMatthias Ringwald # 2907 560*b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 561*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 562*b3fcedb9SMatthias Ringwald continue 563*b3fcedb9SMatthias Ringwald 564*b3fcedb9SMatthias Ringwald # 2908 565*b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 566*b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 567*b3fcedb9SMatthias Ringwald continue 568*b3fcedb9SMatthias Ringwald 569*b3fcedb9SMatthias Ringwald # 2909 570*b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 571*b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 572*b3fcedb9SMatthias Ringwald continue 573*b3fcedb9SMatthias Ringwald 574*b3fcedb9SMatthias Ringwald # 290A 575*b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 576*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 577*b3fcedb9SMatthias Ringwald continue 578*b3fcedb9SMatthias Ringwald 579*b3fcedb9SMatthias Ringwald # 290B 580*b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 581*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 582*b3fcedb9SMatthias Ringwald continue 583*b3fcedb9SMatthias Ringwald 584*b3fcedb9SMatthias Ringwald # 290C 585*b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 586*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 587*b3fcedb9SMatthias Ringwald continue 588*b3fcedb9SMatthias Ringwald 589*b3fcedb9SMatthias Ringwald # 290D 590*b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 591*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 592*b3fcedb9SMatthias Ringwald continue 593*b3fcedb9SMatthias Ringwald 594*b3fcedb9SMatthias Ringwald # 2906 595*b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 596*b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 597*b3fcedb9SMatthias Ringwald continue 598*b3fcedb9SMatthias Ringwald 599*b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 600*b3fcedb9SMatthias Ringwald 601*b3fcedb9SMatthias Ringwald write_indent(fout) 602*b3fcedb9SMatthias Ringwald fout.write("// END\n"); 603*b3fcedb9SMatthias Ringwald write_indent(fout) 604*b3fcedb9SMatthias Ringwald write_16(fout,0) 605*b3fcedb9SMatthias Ringwald fout.write("\n") 606*b3fcedb9SMatthias Ringwald total_size = total_size + 2 607*b3fcedb9SMatthias Ringwald 608*b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 609*b3fcedb9SMatthias Ringwald 610*b3fcedb9SMatthias Ringwalddef listHandles(fout): 611*b3fcedb9SMatthias Ringwald fout.write('\n\n') 612*b3fcedb9SMatthias Ringwald fout.write('//\n') 613*b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 614*b3fcedb9SMatthias Ringwald fout.write('//\n') 615*b3fcedb9SMatthias Ringwald for define in defines: 616*b3fcedb9SMatthias Ringwald fout.write(define) 617*b3fcedb9SMatthias Ringwald fout.write('\n') 618*b3fcedb9SMatthias Ringwald 619*b3fcedb9SMatthias Ringwaldif (len(sys.argv) < 3): 620*b3fcedb9SMatthias Ringwald print(usage) 621*b3fcedb9SMatthias Ringwald sys.exit(1) 622*b3fcedb9SMatthias Ringwaldtry: 623*b3fcedb9SMatthias Ringwald filename = sys.argv[2] 624*b3fcedb9SMatthias Ringwald fin = codecs.open (sys.argv[1], encoding='utf-8') 625*b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 626*b3fcedb9SMatthias Ringwald parse(sys.argv[1], fin, filename, fout) 627*b3fcedb9SMatthias Ringwald listHandles(fout) 628*b3fcedb9SMatthias Ringwald fout.close() 629*b3fcedb9SMatthias Ringwald print('Created', filename) 630*b3fcedb9SMatthias Ringwald 631*b3fcedb9SMatthias Ringwaldexcept IOError as e: 632*b3fcedb9SMatthias Ringwald print(usage) 633*b3fcedb9SMatthias Ringwald sys.exit(1) 634*b3fcedb9SMatthias Ringwald 635*b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 636