xref: /btstack/tool/bluetooth_sdp.py (revision b436f1776a131c1d5e1d70a894a657d41cf7fe1d)
1d1935f69SMatthias Ringwald#!/usr/bin/env python
2d1935f69SMatthias Ringwald#
3d1935f69SMatthias Ringwald# Scrape SDP UUIDs from Bluetooth SIG page
4d1935f69SMatthias Ringwald# Copyright 2017 BlueKitchen GmbH
5d1935f69SMatthias Ringwald#
6d1935f69SMatthias Ringwald
7d1935f69SMatthias Ringwaldfrom lxml import html
8d1935f69SMatthias Ringwaldimport datetime
9d1935f69SMatthias Ringwaldimport requests
10d1935f69SMatthias Ringwaldimport sys
11d1935f69SMatthias Ringwaldimport os
12d1935f69SMatthias Ringwaldimport codecs
13d1935f69SMatthias Ringwaldimport re
14d1935f69SMatthias Ringwald
15d1935f69SMatthias Ringwaldprogram_info = '''
16d1935f69SMatthias RingwaldBTstack SDP UUID Scraper for BTstack
17d1935f69SMatthias RingwaldCopyright 2017, BlueKitchen GmbH
18d1935f69SMatthias Ringwald'''
19d1935f69SMatthias Ringwald
20b816bb66SMatthias Ringwaldheader = '''/**
21b816bb66SMatthias Ringwald * bluetooth_sdp.h generated from Bluetooth SIG website for BTstack by tool/bluetooth_sdp.py
22b816bb66SMatthias Ringwald * {page}
23*b436f177SMatthias Ringwald * {datetime}
24d1935f69SMatthias Ringwald */
25d1935f69SMatthias Ringwald
26d1935f69SMatthias Ringwald#ifndef __BLUETOOTH_SDP_H
27d1935f69SMatthias Ringwald#define __BLUETOOTH_SDP_H
28d1935f69SMatthias Ringwald
29d1935f69SMatthias Ringwald'''
30d1935f69SMatthias Ringwald
31d1935f69SMatthias Ringwaldtrailer = '''
32d1935f69SMatthias Ringwald#endif
33d1935f69SMatthias Ringwald'''
34d1935f69SMatthias Ringwald
35b816bb66SMatthias Ringwalddefines = []
36b816bb66SMatthias Ringwald
37d1935f69SMatthias Ringwald# Convert CamelCase to snake_case from http://stackoverflow.com/a/1176023
38d1935f69SMatthias Ringwalddef camel_to_underscore(name):
39d1935f69SMatthias Ringwald    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
40d1935f69SMatthias Ringwald    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).upper()
41d1935f69SMatthias Ringwald
42d1935f69SMatthias Ringwalddef create_pretty_define(name):
43b816bb66SMatthias Ringwald    name = name.lstrip()
44b816bb66SMatthias Ringwald    to_delete = [ '(FTP v1.2 and later)', '(Deprecated)', '(FTP v1.2 and later)', '(GOEP v2.0 and later)',
45b816bb66SMatthias Ringwald     '(BIP v1.1 and later)', '(MAP v1.2 and later)', '(OPP v1.2 and later)', '(Not used in PAN v1.0)', '(PBAP v1.2 and later)']
46b816bb66SMatthias Ringwald    for item in to_delete:
47b816bb66SMatthias Ringwald        name = name.replace(item, '')
48b816bb66SMatthias Ringwald    name = name.rstrip()
49d1935f69SMatthias Ringwald    name = name.replace(' - ', '_')
50d1935f69SMatthias Ringwald    name = name.replace(' ', '_')
51d1935f69SMatthias Ringwald    name = name.replace('/','')
52d1935f69SMatthias Ringwald    name = name.replace('(','_')
53d1935f69SMatthias Ringwald    name = name.replace(')','')
54d1935f69SMatthias Ringwald    name = name.replace('-','_')
55d1935f69SMatthias Ringwald    name = name.replace('PnP', 'PNP')
56b816bb66SMatthias Ringwald    name = name.replace('IPv', 'IPV')
57d38c4adfSMatthias Ringwald    name = name.replace('ServiceDiscoveryServerServiceClassID', 'ServiceDiscoveryServer')
58d38c4adfSMatthias Ringwald    name = name.replace('BrowseGroupDescriptorServiceClassID', 'BrowseGroupDescriptor')
59b816bb66SMatthias Ringwald    name = name.replace('&','and')
60d1935f69SMatthias Ringwald    return camel_to_underscore(name).replace('__','_').replace('3_D','3D').replace('L2_CAP','L2CAP')
61d1935f69SMatthias Ringwald
62b816bb66SMatthias Ringwalddef remove_newlines(remark):
63d1935f69SMatthias Ringwald    return " ".join(remark.split())
64d1935f69SMatthias Ringwald
653860edc8SMatthias Ringwalddef process_table(fout, tbody, pattern):
663860edc8SMatthias Ringwald    rows = tbody.getchildren()
67d1935f69SMatthias Ringwald    for row in rows:
68d1935f69SMatthias Ringwald        columns = row.getchildren()
69d1935f69SMatthias Ringwald        name = columns[0].text_content().encode('ascii','ignore')
70d1935f69SMatthias Ringwald        value = columns[1].text_content().encode('ascii','ignore')
71b816bb66SMatthias Ringwald        remark = ''
72b816bb66SMatthias Ringwald        if (len(columns) > 2):
73d1935f69SMatthias Ringwald            remark = columns[2].text_content().encode('ascii','ignore')
743860edc8SMatthias Ringwald        # skip tbody headers
7546b21291SMatthias Ringwald        if name in ["Protocol Name", "Service Class Name", "Attribute Name", "UUID Name",
76b816bb66SMatthias Ringwald            "Reserved", 'Reserved for HID Attributes', 'Available for HID Language Strings']:
77d1935f69SMatthias Ringwald            continue
783860edc8SMatthias Ringwald        # skip tbody footers
79d1935f69SMatthias Ringwald        if value.startswith('(Max value '):
80d1935f69SMatthias Ringwald            continue
81d1935f69SMatthias Ringwald        name = create_pretty_define(name)
82b816bb66SMatthias Ringwald        # skip duplicate attributes
83b816bb66SMatthias Ringwald        if name in defines:
84b816bb66SMatthias Ringwald            continue
85b816bb66SMatthias Ringwald        value = remove_newlines(value)
86b816bb66SMatthias Ringwald        remark = remove_newlines(remark)
87d1935f69SMatthias Ringwald        fout.write(pattern % (name, value, remark))
88b816bb66SMatthias Ringwald        defines.append(name)
89b816bb66SMatthias Ringwald
90b816bb66SMatthias Ringwalddef scrape_attributes(fout, tree, table_name):
91b816bb66SMatthias Ringwald    tables = tree.xpath("//table[preceding-sibling::h3 = '" + table_name +"']")
92b816bb66SMatthias Ringwald    tbody = tables[0].getchildren()[0]
9346b21291SMatthias Ringwald    process_table(fout, tbody, '#define BLUETOOTH_ATTRIBUTE_%-54s %s // %s\n')
94d1935f69SMatthias Ringwald
95d1935f69SMatthias Ringwalddef scrape_page(fout, url):
96d1935f69SMatthias Ringwald    print("Parsing %s" % url)
97*b436f177SMatthias Ringwald    fout.write(header.format(page=url,datetime=str(datetime.datetime.now())))
98d1935f69SMatthias Ringwald
99d1935f69SMatthias Ringwald    # get from web
100b816bb66SMatthias Ringwald    r = requests.get(url)
101b816bb66SMatthias Ringwald    content = r.text
1023860edc8SMatthias Ringwald
103d1935f69SMatthias Ringwald    # test: fetch from local file 'service-discovery.html'
104b816bb66SMatthias Ringwald    # f = codecs.open("service-discovery.html", "r", "utf-8")
105b816bb66SMatthias Ringwald    # content = f.read();
106d1935f69SMatthias Ringwald
107d1935f69SMatthias Ringwald    tree = html.fromstring(content)
108d1935f69SMatthias Ringwald
109b816bb66SMatthias Ringwald    # # Protocol Identifiers
110b816bb66SMatthias Ringwald    fout.write('/**\n')
111b816bb66SMatthias Ringwald    fout.write(' * Protocol Identifiers\n')
112b816bb66SMatthias Ringwald    fout.write(' */\n')
1133860edc8SMatthias Ringwald    tables = tree.xpath("//table[preceding-sibling::h3 = 'Protocol Identifiers']")
1143860edc8SMatthias Ringwald    tbody = tables[0].getchildren()[0]
1153860edc8SMatthias Ringwald    process_table(fout, tbody, '#define BLUETOOTH_PROTOCOL_%-55s %s // %s\n')
116b816bb66SMatthias Ringwald    fout.write('\n')
117d1935f69SMatthias Ringwald
118b816bb66SMatthias Ringwald    # # Service Classes
119b816bb66SMatthias Ringwald    fout.write('/**\n')
120b816bb66SMatthias Ringwald    fout.write(' * Service Classes\n')
121b816bb66SMatthias Ringwald    fout.write(' */\n')
1223860edc8SMatthias Ringwald    tables = tree.xpath("//table[preceding-sibling::h3 = 'Protocol Identifiers']")
1233860edc8SMatthias Ringwald    tbody = tables[1].getchildren()[0]
124b816bb66SMatthias Ringwald    process_table(fout, tbody, '#define BLUETOOTH_SERVICE_CLASS_%-50s %s // %s\n')
125b816bb66SMatthias Ringwald    fout.write('\n')
126b816bb66SMatthias Ringwald
127b816bb66SMatthias Ringwald    # Attributes
128b816bb66SMatthias Ringwald    fout.write('/**\n')
129b816bb66SMatthias Ringwald    fout.write(' * Attributes\n')
130b816bb66SMatthias Ringwald    fout.write(' */\n')
131b816bb66SMatthias Ringwald    table_names = [
132b816bb66SMatthias Ringwald        # 'Base Universally Unique Identifier (UUID)',
13346b21291SMatthias Ringwald        'Browse Group Identifiers',
134b816bb66SMatthias Ringwald        'Attribute Identifiers',
135b816bb66SMatthias Ringwald        # 'Audio/Video Remote Control Profile (AVRCP)',
136b816bb66SMatthias Ringwald        'Basic Imaging Profile (BIP)',
137b816bb66SMatthias Ringwald        'Basic Printing Profile (BPP)',
138b816bb66SMatthias Ringwald        'Bluetooth Core Specification: Universal Attributes',
139b816bb66SMatthias Ringwald        'Bluetooth Core Specification: Service Discovery Service',
140b816bb66SMatthias Ringwald        # 'Bluetooth Core Specification: Browse Group Descriptor Service',
141b816bb66SMatthias Ringwald        # 'Cordless Telephony Profile [DEPRECATED]',
142b816bb66SMatthias Ringwald        'Device Identification Profile',
143b816bb66SMatthias Ringwald        # 'Fax Profile [DEPRECATED]',
144b816bb66SMatthias Ringwald        'File Transfer Profile',
145b816bb66SMatthias Ringwald        'Generic Object Exchange Profile',
146b816bb66SMatthias Ringwald        # 'Global Navigation Satellite System Profile (GNSS)', -- note: SupportedFeatures, but different UUID
147b816bb66SMatthias Ringwald        'Hands-Free Profile',
148b816bb66SMatthias Ringwald        'Hardcopy Replacement Profile ',
149b816bb66SMatthias Ringwald        'Headset Profile',
150b816bb66SMatthias Ringwald        'Health Device Profile',
151b816bb66SMatthias Ringwald        'Human Interface Device Profile',
152b816bb66SMatthias Ringwald        # 'Interoperability Requirements for Bluetooth technology as a WAP Bearer [DEPRECATED]',
153b816bb66SMatthias Ringwald        'Message Access Profile',
154b816bb66SMatthias Ringwald        'Object Push Profile',
155b816bb66SMatthias Ringwald        'Personal Area Networking Profile',
156b816bb66SMatthias Ringwald        'Phone Book Access Profile',
157b816bb66SMatthias Ringwald        'Synchronization Profile',
158b816bb66SMatthias Ringwald        # 'Attribute ID Offsets for Strings',
159b816bb66SMatthias Ringwald        # 'Protocol Parameters',
160b816bb66SMatthias Ringwald        'Multi-Profile',
161b816bb66SMatthias Ringwald        'Calendar Tasks and Notes',
162b816bb66SMatthias Ringwald    ]
163b816bb66SMatthias Ringwald    for table_name in table_names:
164b816bb66SMatthias Ringwald        scrape_attributes(fout, tree, table_name)
165b816bb66SMatthias Ringwald    # see above
166b816bb66SMatthias Ringwald    fout.write('#define BLUETOOTH_ATTRIBUTE_GNSS_SUPPORTED_FEATURES                                0x0200\n');
167b816bb66SMatthias Ringwald
168b816bb66SMatthias Ringwald
169d1935f69SMatthias Ringwald
170d1935f69SMatthias Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
171d1935f69SMatthias Ringwaldgen_path = btstack_root + '/src/bluetooth_sdp.h'
172d1935f69SMatthias Ringwald
173d1935f69SMatthias Ringwaldprint(program_info)
174d1935f69SMatthias Ringwald
175d1935f69SMatthias Ringwaldwith open(gen_path, 'wt') as fout:
176d1935f69SMatthias Ringwald    scrape_page(fout, 'https://www.bluetooth.com/specifications/assigned-numbers/service-discovery')
177d1935f69SMatthias Ringwald    fout.write(trailer)
178d1935f69SMatthias Ringwald
179d1935f69SMatthias Ringwaldprint('Scraping successful!\n')
180