diff --git a/tool/convert_gatt_service.py b/tool/convert_gatt_service.py
new file mode 100755
index 000000000..05ede0b5f
--- /dev/null
+++ b/tool/convert_gatt_service.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+#
+# Convert offical Bluetooth GATT Service definitions into BTstack's .gatt format
+# Copyright 2016 BlueKitchen GmbH
+#
+
+from lxml import html
+import codecs
+import datetime
+import os
+import requests
+import sys
+import xml.etree.ElementTree as ET
+
+
+def indent(elem, level=0):
+ i = "\n" + level*" "
+ j = "\n" + (level-1)*" "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for subelem in elem:
+ indent(subelem, level+1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = j
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = j
+ return elem
+
+def list_services():
+ url = 'https://www.bluetooth.com/specifications/gatt/services'
+ print("Fetching list of services from %s" % url)
+ print('')
+
+ page = requests.get(url)
+ tree = html.fromstring(page.content)
+ # get all
elements in
+ rows = tree.xpath('//table[@id="gattTable"]/tr')
+ print("%-55s| %-30s| %s" % ('Service ID', 'Summary', 'UUID'))
+ print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10)
+ maxlen_type = 0
+ maxlen_name = 0
+ services = []
+ for row in rows:
+ children = row.getchildren()
+ summary = children[0].text_content()
+ id = children[1].text_content()
+ uuid = children[2].text_content()
+ if (len(id)):
+ services.append((id, summary, uuid))
+ # sort
+ services.sort(key=lambda tup: tup[1])
+ for service in services:
+ if (len(id) > maxlen_type):
+ maxlen_type = len(id)
+ if (len(summary) > maxlen_name):
+ maxlen_name = len(summary)
+ print("%-55s| %-30s| %s" % service)
+
+def parse_properties(element):
+ properties = element.find('Properties')
+ property_list_human = []
+ for property in properties:
+ property_name = property.tag
+ property_requirement = property.text
+ if (property_requirement != "Excluded"):
+ property_list_human.append(property_name)
+ return (property_list_human)
+
+ print("Characteristic %s - properties %s" % (name, property_list_human))
+ fout.write('CHARACTERISTIC, %s, %s,\n' % (type, ' | '.join(property_list)))
+
+def handle_todo(fout, name):
+ print('-- Descriptor %-40s - TODO: Please set values' % name)
+ fout.write('// TODO: %s: please set values\n' % name)
+
+
+def convert_service(fout, specification_type):
+ url = 'https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=%s.xml' % specification_type
+
+ print('Fetching %s from %s' %(specification_type, url))
+
+ try:
+ page = requests.get(url)
+ tree = ET.fromstring(page.content)
+ service_attributes = tree.attrib
+ except ET.ParseError:
+ print('Failed to download file')
+ sys.exit(10)
+
+ print('')
+
+ # indent(tree)
+ # ET.dump(tree)
+ # return
+
+ service_name = service_attributes['name']
+ service_uuid = service_attributes['uuid']
+
+ print("Service %s" % service_name)
+ fout.write('// Specification Type %s\n' % specification_type)
+ fout.write('// %s\n' % url)
+ fout.write('\n')
+ fout.write('// %s %s\n' % (service_name, service_uuid))
+ fout.write('PRIMARY_SERVICE, %s\n' % specification_type)
+
+ characteristics = tree.find('Characteristics')
+ for characteristic in characteristics:
+ type = characteristic.attrib['type']
+ name = characteristic.attrib['name']
+ property_list_human = parse_properties(characteristic)
+ properties = ' | '.join( ['DYNAMIC'] + property_list_human).upper()
+ print("- Characteristic %s - properties %s" % (name, property_list_human))
+ fout.write('CHARACTERISTIC, %s, %s,\n' % (type, properties))
+
+ descriptors = characteristic.find('Descriptors')
+ for descriptor in descriptors:
+ type = descriptor.attrib['type']
+ name = descriptor.attrib['name']
+ property_list_human = parse_properties(descriptor)
+ properties = ' | '.join(property_list_human).upper()
+
+ if (type == 'org.bluetooth.descriptor.gatt.client_characteristic_configuration'):
+ print('-- Descriptor %-40s - SKIPPED: automatically generated currently by compile_gatt.py' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.gatt.characteristic_presentation_format'):
+ handle_todo(fout, 'Characteristic Presentation Format')
+ fout.write('CHARACTERISTIC_FORMAT, %s, _format_, _exponent_, _unit_, _name_space_, _description_\n' % properties)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.gatt.characteristic_user_description'):
+ handle_todo(fout, 'Characteristic User Description')
+ fout.write('CHARACTERISTIC_USER_DESCRIPTION, %s, _user_description_\n' % properties)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.gatt.server_characteristic_configuration'):
+ handle_todo(fout, 'Server Characteristic Configuration')
+ fout.write('SERVER_CHARACTERISTIC_CONFIGURATION, %s,\n' % properties)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.gatt.characteristic_aggregate_format'):
+ handle_todo(fout, 'Characteristic Aggregate Format')
+ fout.write('CHARACTERISTIC_AGGREGATE_FORMAT, %s, _list_of_handles_\n' % properties)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.valid_range'):
+ print('-- Descriptor %-40s - WARNING: VALID_RANGE not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.external_report_reference'):
+ print('-- Descriptor %-40s - WARNING: EXTERNAL_REPORT_REFERENCE not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.report_reference'):
+ print('-- Descriptor %-40s - WARNING: REPORT_REFERENCE not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.number_of_digitals'):
+ print('-- Descriptor %-40s - WARNING: NUMBER_OF_DIGITALS not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.value_trigger_setting'):
+ print('-- Descriptor %-40s - WARNING: VALUE_TRIGGER_SETTING not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.es_configuration'):
+ print('-- Descriptor %-40s - WARNING: ENVIRONMENTAL_SENSING_CONFIGURATION not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.es_measurement'):
+ print('-- Descriptor %-40s - WARNING: ENVIRONMENTAL_SENSING_MEASUREMENT not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.es_trigger_setting'):
+ print('-- Descriptor %-40s - WARNING: ENVIRONMENTAL_SENSING_TRIGGER_SETTING not supported yet' % name)
+ continue
+
+ if (type == 'org.bluetooth.descriptor.gatt.characteristic_extended_properties'):
+ print('-- Descriptor %-40s - WARNING: not supported yet' % name)
+ continue
+
+ print('-- Descriptor %-40s -- WARNING: not supported yet' % name)
+
+
+if (len(sys.argv) < 2):
+ list_services()
+ print('\n')
+ # print(maxlen_type, maxlen_name)
+ print('To convert service into .gatt file, call %s with the requested service id' % sys.argv[0])
+else:
+ specification_type = sys.argv[1]
+ filename = '%s.gatt' % specification_type
+ with open(filename, 'wt') as fout:
+ convert_service(fout, specification_type)
+ print('')
+ print('Service successfully converted into %s' % filename)
+ print('Please check for TODOs in the .gatt file')
\ No newline at end of file