1*b08c8c49SMatthias Ringwald#!/usr/bin/env python 2*b08c8c49SMatthias Ringwald# 3*b08c8c49SMatthias Ringwald# Scrape GATT UUIDs from Bluetooth SIG page 4*b08c8c49SMatthias Ringwald# https://www.bluetooth.com/specifications/assigned-numbers/logical-link-control/ 5*b08c8c49SMatthias Ringwald# 6*b08c8c49SMatthias Ringwald# Copyright 2019 BlueKitchen GmbH 7*b08c8c49SMatthias Ringwald# 8*b08c8c49SMatthias Ringwald 9*b08c8c49SMatthias Ringwaldfrom lxml import html 10*b08c8c49SMatthias Ringwaldimport datetime 11*b08c8c49SMatthias Ringwaldimport requests 12*b08c8c49SMatthias Ringwaldimport sys 13*b08c8c49SMatthias Ringwaldimport codecs 14*b08c8c49SMatthias Ringwaldimport os 15*b08c8c49SMatthias Ringwaldimport re 16*b08c8c49SMatthias Ringwald 17*b08c8c49SMatthias Ringwaldheaders = {'user-agent': 'curl/7.63.0'} 18*b08c8c49SMatthias Ringwald 19*b08c8c49SMatthias Ringwaldprogram_info = ''' 20*b08c8c49SMatthias RingwaldBTstack PSM Scraper 21*b08c8c49SMatthias RingwaldCopyright 2019, BlueKitchen GmbH 22*b08c8c49SMatthias Ringwald''' 23*b08c8c49SMatthias Ringwald 24*b08c8c49SMatthias Ringwaldheader = ''' 25*b08c8c49SMatthias Ringwald/** 26*b08c8c49SMatthias Ringwald * bluetooth_psm.h generated from Bluetooth SIG website for BTstack by tool/bluetooth_psm.py 27*b08c8c49SMatthias Ringwald * {datetime} 28*b08c8c49SMatthias Ringwald */ 29*b08c8c49SMatthias Ringwald 30*b08c8c49SMatthias Ringwald#ifndef BLUETOOTH_PSM_H 31*b08c8c49SMatthias Ringwald#define BLUETOOTH_PSM_H 32*b08c8c49SMatthias Ringwald''' 33*b08c8c49SMatthias Ringwald 34*b08c8c49SMatthias Ringwaldpage_info = ''' 35*b08c8c49SMatthias Ringwald/** 36*b08c8c49SMatthias Ringwald * Assigned numbers from {page} 37*b08c8c49SMatthias Ringwald */ 38*b08c8c49SMatthias Ringwald''' 39*b08c8c49SMatthias Ringwald 40*b08c8c49SMatthias Ringwaldtrailer = ''' 41*b08c8c49SMatthias Ringwald#endif 42*b08c8c49SMatthias Ringwald''' 43*b08c8c49SMatthias Ringwald 44*b08c8c49SMatthias Ringwaldtags = [] 45*b08c8c49SMatthias Ringwald 46*b08c8c49SMatthias Ringwalddef strip_non_ascii(string): 47*b08c8c49SMatthias Ringwald stripped = (c for c in string if 0 < ord(c) < 127) 48*b08c8c49SMatthias Ringwald return ''.join(stripped) 49*b08c8c49SMatthias Ringwald 50*b08c8c49SMatthias Ringwalddef create_name(psm): 51*b08c8c49SMatthias Ringwald # limit to ascii 52*b08c8c49SMatthias Ringwald psm = strip_non_ascii(psm) 53*b08c8c49SMatthias Ringwald # remove parts in braces 54*b08c8c49SMatthias Ringwald p = re.compile('\(.*\)') 55*b08c8c49SMatthias Ringwald tag = p.sub('',psm).rstrip().upper() 56*b08c8c49SMatthias Ringwald tag = tag.replace('-', '_') 57*b08c8c49SMatthias Ringwald return "BLUETOOTH_PSM_" + tag 58*b08c8c49SMatthias Ringwald 59*b08c8c49SMatthias Ringwalddef scrape_page(fout, url): 60*b08c8c49SMatthias Ringwald global headers 61*b08c8c49SMatthias Ringwald 62*b08c8c49SMatthias Ringwald print("Parsing %s" % url) 63*b08c8c49SMatthias Ringwald fout.write(page_info.format(page=url.replace('https://',''))) 64*b08c8c49SMatthias Ringwald 65*b08c8c49SMatthias Ringwald # get from web 66*b08c8c49SMatthias Ringwald r = requests.get(url, headers=headers) 67*b08c8c49SMatthias Ringwald content = r.text 68*b08c8c49SMatthias Ringwald 69*b08c8c49SMatthias Ringwald # test: fetch from local file 'index.html' 70*b08c8c49SMatthias Ringwald # f = codecs.open("index.html", "r", "utf-8") 71*b08c8c49SMatthias Ringwald # content = f.read(); 72*b08c8c49SMatthias Ringwald 73*b08c8c49SMatthias Ringwald tree = html.fromstring(content) 74*b08c8c49SMatthias Ringwald rows = tree.xpath('//table/tbody/tr') 75*b08c8c49SMatthias Ringwald for row in rows: 76*b08c8c49SMatthias Ringwald children = row.getchildren() 77*b08c8c49SMatthias Ringwald psm = children[0].text_content() 78*b08c8c49SMatthias Ringwald 79*b08c8c49SMatthias Ringwald # abort when second table starts 80*b08c8c49SMatthias Ringwald if (psm == '0x0000-0xFFFF'): 81*b08c8c49SMatthias Ringwald break 82*b08c8c49SMatthias Ringwald 83*b08c8c49SMatthias Ringwald id_hex = children[1].text_content().replace(u'\u200b','') 84*b08c8c49SMatthias Ringwald fout.write("#define %-80s %s\n" % (create_name(psm), id_hex)) 85*b08c8c49SMatthias Ringwald 86*b08c8c49SMatthias Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 87*b08c8c49SMatthias Ringwaldgen_path = btstack_root + '/src/bluetooth_psm.h' 88*b08c8c49SMatthias Ringwald 89*b08c8c49SMatthias Ringwaldprint(program_info) 90*b08c8c49SMatthias Ringwald 91*b08c8c49SMatthias Ringwaldwith open(gen_path, 'wt') as fout: 92*b08c8c49SMatthias Ringwald fout.write(header.format(datetime=str(datetime.datetime.now()))) 93*b08c8c49SMatthias Ringwald scrape_page(fout, 'https://www.bluetooth.com/specifications/assigned-numbers/logical-link-control/') 94*b08c8c49SMatthias Ringwald fout.write(trailer) 95*b08c8c49SMatthias Ringwald 96*b08c8c49SMatthias Ringwaldprint('Scraping successful!\n') 97