1# Copyright 2021-2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# ----------------------------------------------------------------------------- 16# Imports 17# ----------------------------------------------------------------------------- 18import asyncio 19import sys 20import os 21import logging 22 23from bumble.device import Device 24from bumble.transport import open_transport_or_link 25from bumble.sdp import ( 26 DataElement, 27 ServiceAttribute, 28 SDP_PUBLIC_BROWSE_ROOT, 29 SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, 30 SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, 31 SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, 32 SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, 33 SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, 34) 35from bumble.core import ( 36 BT_AUDIO_SINK_SERVICE, 37 BT_L2CAP_PROTOCOL_ID, 38 BT_AVDTP_PROTOCOL_ID, 39 BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, 40) 41 42# ----------------------------------------------------------------------------- 43SDP_SERVICE_RECORDS = { 44 0x00010001: [ 45 ServiceAttribute( 46 SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, 47 DataElement.unsigned_integer_32(0x00010001), 48 ), 49 ServiceAttribute( 50 SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, 51 DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), 52 ), 53 ServiceAttribute( 54 SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, 55 DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)]), 56 ), 57 ServiceAttribute( 58 SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, 59 DataElement.sequence( 60 [ 61 DataElement.sequence( 62 [ 63 DataElement.uuid(BT_L2CAP_PROTOCOL_ID), 64 DataElement.unsigned_integer_16(25), 65 ] 66 ), 67 DataElement.sequence( 68 [ 69 DataElement.uuid(BT_AVDTP_PROTOCOL_ID), 70 DataElement.unsigned_integer_16(256), 71 ] 72 ), 73 ] 74 ), 75 ), 76 ServiceAttribute( 77 SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, 78 DataElement.sequence( 79 [ 80 DataElement.sequence( 81 [ 82 DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), 83 DataElement.unsigned_integer_16(256), 84 ] 85 ) 86 ] 87 ), 88 ), 89 ] 90} 91 92 93# ----------------------------------------------------------------------------- 94async def main() -> None: 95 if len(sys.argv) < 3: 96 print('Usage: run_classic_discoverable.py <device-config> <transport-spec>') 97 print('example: run_classic_discoverable.py classic1.json usb:04b4:f901') 98 return 99 100 print('<<< connecting to HCI...') 101 async with await open_transport_or_link(sys.argv[2]) as hci_transport: 102 print('<<< connected') 103 104 # Create a device 105 device = Device.from_config_file_with_hci( 106 sys.argv[1], hci_transport.source, hci_transport.sink 107 ) 108 device.classic_enabled = True 109 device.sdp_service_records = SDP_SERVICE_RECORDS 110 await device.power_on() 111 112 # Start being discoverable and connectable 113 await device.set_discoverable(True) 114 await device.set_connectable(True) 115 116 await hci_transport.source.wait_for_termination() 117 118 119# ----------------------------------------------------------------------------- 120logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) 121asyncio.run(main()) 122