15c544019SMatthias Ringwald#!/usr/bin/env python3 2779b256eSMatthias Ringwald# 3779b256eSMatthias Ringwald# Scrape GATT UUIDs from Bluetooth SIG page 4779b256eSMatthias Ringwald# https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers 5779b256eSMatthias Ringwald# 6779b256eSMatthias Ringwald# Copyright 2017 BlueKitchen GmbH 7779b256eSMatthias Ringwald# 8779b256eSMatthias Ringwald 9779b256eSMatthias Ringwaldfrom lxml import html 10779b256eSMatthias Ringwaldimport datetime 11779b256eSMatthias Ringwaldimport requests 12779b256eSMatthias Ringwaldimport sys 13779b256eSMatthias Ringwaldimport codecs 14779b256eSMatthias Ringwaldimport os 15779b256eSMatthias Ringwaldimport re 16779b256eSMatthias Ringwald 17*77bd0381SMilanka Ringwaldimport csv 18*77bd0381SMilanka Ringwaldimport getopt 19*77bd0381SMilanka Ringwald 20ff7cea0bSMatthias Ringwald 21779b256eSMatthias Ringwaldprogram_info = ''' 22779b256eSMatthias RingwaldBTstack Company ID Scraper for BTstack 23*77bd0381SMilanka RingwaldCopyright 2022, BlueKitchen GmbH 24779b256eSMatthias Ringwald''' 25779b256eSMatthias Ringwald 26779b256eSMatthias Ringwaldheader = ''' 27779b256eSMatthias Ringwald/** 28b436f177SMatthias Ringwald * bluetooth_company_id.h generated from Bluetooth SIG website for BTstack by tool/bluetooth_company_id.py 29b436f177SMatthias Ringwald * {datetime} 30779b256eSMatthias Ringwald */ 31779b256eSMatthias Ringwald 3280e33422SMatthias Ringwald#ifndef BLUETOOTH_COMPANY_ID_H 3380e33422SMatthias Ringwald#define BLUETOOTH_COMPANY_ID_H 34779b256eSMatthias Ringwald''' 35779b256eSMatthias Ringwald 36779b256eSMatthias Ringwaldpage_info = ''' 37779b256eSMatthias Ringwald/** 38779b256eSMatthias Ringwald * Assigned numbers from {page} 39779b256eSMatthias Ringwald */ 40779b256eSMatthias Ringwald''' 41779b256eSMatthias Ringwald 42779b256eSMatthias Ringwaldtrailer = ''' 43779b256eSMatthias Ringwald#endif 44779b256eSMatthias Ringwald''' 45779b256eSMatthias Ringwald 46779b256eSMatthias Ringwaldtags = [] 47779b256eSMatthias Ringwald 48ff7cea0bSMatthias Ringwalddef strip_non_ascii(string): 49ff7cea0bSMatthias Ringwald stripped = (c for c in string if 0 < ord(c) < 127) 50ff7cea0bSMatthias Ringwald return ''.join(stripped) 51ff7cea0bSMatthias Ringwald 52779b256eSMatthias Ringwalddef create_name(company): 53ff7cea0bSMatthias Ringwald # limit to ascii 54ff7cea0bSMatthias Ringwald company = strip_non_ascii(company) 55779b256eSMatthias Ringwald # remove parts in braces 56779b256eSMatthias Ringwald p = re.compile('\(.*\)') 57779b256eSMatthias Ringwald tag = p.sub('',company).rstrip().upper() 58779b256eSMatthias Ringwald tag = tag.replace('&',' AND ') 59779b256eSMatthias Ringwald tag = tag.replace(''','') 60779b256eSMatthias Ringwald tag = tag.replace('"',' ') 61779b256eSMatthias Ringwald tag = tag.replace('+',' AND ') 62779b256eSMatthias Ringwald tag = tag.replace(' - ', ' ') 63779b256eSMatthias Ringwald tag = tag.replace('/', ' ') 64779b256eSMatthias Ringwald tag = tag.replace(';',' ') 65779b256eSMatthias Ringwald tag = tag.replace(',','') 66779b256eSMatthias Ringwald tag = tag.replace('.', '') 67779b256eSMatthias Ringwald tag = tag.replace('-','_') 68779b256eSMatthias Ringwald tag = tag.replace(' ',' ') 69779b256eSMatthias Ringwald tag = tag.replace(' ','_') 70ff7cea0bSMatthias Ringwald tag = tag.replace('&','AND') 71ff7cea0bSMatthias Ringwald tag = tag.replace("'","_") 72ff7cea0bSMatthias Ringwald tag = tag.replace('"','_') 73ff7cea0bSMatthias Ringwald tag = tag.replace('!','_') 74*77bd0381SMilanka Ringwald tag = tag.replace('|','_') 75*77bd0381SMilanka Ringwald tag = tag.replace('[','') 76*77bd0381SMilanka Ringwald tag = tag.replace(']','') 77779b256eSMatthias Ringwald return "BLUETOOTH_COMPANY_ID_" + tag 78779b256eSMatthias Ringwald 79*77bd0381SMilanka Ringwalddef parse_cvs(csv_file): 80*77bd0381SMilanka Ringwald cvsreader = csv.reader(csv_file, delimiter=',', quotechar='\"') 81*77bd0381SMilanka Ringwald # Skip header ['"Decimal","Hexadecimal","Company"'] 82*77bd0381SMilanka Ringwald next(cvsreader) 83ff7cea0bSMatthias Ringwald 84*77bd0381SMilanka Ringwald companies = {} 85*77bd0381SMilanka Ringwald for row in cvsreader: 86*77bd0381SMilanka Ringwald id_dec = int(row[0]) 87*77bd0381SMilanka Ringwald companies[id_dec] = (row[1],row[2]) 88*77bd0381SMilanka Ringwald return companies 89*77bd0381SMilanka Ringwald 90*77bd0381SMilanka Ringwald 91*77bd0381SMilanka Ringwalddef write_cvs(fout, companies, url): 92*77bd0381SMilanka Ringwald global tags 93d918542bSMatthias Ringwald fout.write(page_info.format(page=url.replace('https://',''))) 94779b256eSMatthias Ringwald 95*77bd0381SMilanka Ringwald company_ids = sorted(list(companies.keys())) 96779b256eSMatthias Ringwald 97*77bd0381SMilanka Ringwald for id_dec in company_ids: 98*77bd0381SMilanka Ringwald id_hex = companies[id_dec][0] 99*77bd0381SMilanka Ringwald company = create_name(companies[id_dec][1]) 100779b256eSMatthias Ringwald 101779b256eSMatthias Ringwald if company in tags: 102779b256eSMatthias Ringwald company = company+"2" 103779b256eSMatthias Ringwald else: 104779b256eSMatthias Ringwald tags.append(company) 105*77bd0381SMilanka Ringwald 106779b256eSMatthias Ringwald fout.write("#define %-80s %s\n" % (company, id_hex)) 107779b256eSMatthias Ringwald 108ff7cea0bSMatthias Ringwald # map CSR onto QTIL 109ff7cea0bSMatthias Ringwald fout.write("#define BLUETOOTH_COMPANY_ID_CAMBRIDGE_SILICON_RADIO BLUETOOTH_COMPANY_ID_QUALCOMM_TECHNOLOGIES_INTERNATIONAL_LTD\n") 110ff7cea0bSMatthias Ringwald 111*77bd0381SMilanka Ringwalddef main(argv): 112*77bd0381SMilanka Ringwald url = "https://www.bluetooth.com/de/specifications/assigned-numbers/company-identifiers/#" 113779b256eSMatthias Ringwald btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 114*77bd0381SMilanka Ringwald tools_root = btstack_root + '/tool' 115*77bd0381SMilanka Ringwald src_root = btstack_root + '/src/' 116*77bd0381SMilanka Ringwald 117*77bd0381SMilanka Ringwald header_filename = "bluetooth_company_id.h" 118*77bd0381SMilanka Ringwald header_path = btstack_root + "/src/" + header_filename 119*77bd0381SMilanka Ringwald 120*77bd0381SMilanka Ringwald cvs_filename = "CompanyIdentfiers - CSV.csv" 121*77bd0381SMilanka Ringwald cvs_path = tools_root + "/" + cvs_filename 122779b256eSMatthias Ringwald 123779b256eSMatthias Ringwald print(program_info) 124779b256eSMatthias Ringwald 125*77bd0381SMilanka Ringwald try: 126*77bd0381SMilanka Ringwald header_file = open(header_path, "w") 127*77bd0381SMilanka Ringwald except FileNotFoundError: 128*77bd0381SMilanka Ringwald print("\nFile \'%s\' cannot be created in \'%s\' directory." % (header_filename, src_root)) 129*77bd0381SMilanka Ringwald exit(1) 130779b256eSMatthias Ringwald 131*77bd0381SMilanka Ringwald try: 132*77bd0381SMilanka Ringwald with open(cvs_path, "r") as csv_file: 133*77bd0381SMilanka Ringwald companies = parse_cvs(csv_file) 134*77bd0381SMilanka Ringwald 135*77bd0381SMilanka Ringwald header_file.write(header.format(datetime=str(datetime.datetime.now()))) 136*77bd0381SMilanka Ringwald write_cvs(header_file, companies, url) 137*77bd0381SMilanka Ringwald header_file.write(trailer) 138*77bd0381SMilanka Ringwald 139*77bd0381SMilanka Ringwald print("Company IDs are stored in \'%s\'\n" % (header_path)) 140*77bd0381SMilanka Ringwald except FileNotFoundError: 141*77bd0381SMilanka Ringwald print("\nCVS file \'%s\' not found in \'%s\'." % (cvs_filename, tools_root)) 142*77bd0381SMilanka Ringwald print("Please download the CVS file, then start the skript again.\n") 143*77bd0381SMilanka Ringwald print("The CVS file can be downloaded from:") 144*77bd0381SMilanka Ringwald print(" %s\n" % url) 145*77bd0381SMilanka Ringwald exit(1) 146*77bd0381SMilanka Ringwald 147*77bd0381SMilanka Ringwaldif __name__ == "__main__": 148*77bd0381SMilanka Ringwald main(sys.argv[1:]) 149*77bd0381SMilanka Ringwald 150*77bd0381SMilanka Ringwald 151