xref: /btstack/tool/bluetooth_psm.py (revision b08c8c4999ec5e3c10fa971e116981dc486c4a6c)
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