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 os 20import logging 21from bumble.colors import color 22 23from bumble.core import ProtocolError 24from bumble.controller import Controller 25from bumble.device import Device, Peer 26from bumble.host import Host 27from bumble.link import LocalLink 28from bumble.gatt import ( 29 Service, 30 Characteristic, 31 Descriptor, 32 show_services, 33 GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, 34 GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, 35 GATT_DEVICE_INFORMATION_SERVICE, 36) 37 38 39# ----------------------------------------------------------------------------- 40class ServerListener(Device.Listener): 41 def on_connection(self, connection): 42 print(f'### Server: connected to {connection}') 43 44 45# ----------------------------------------------------------------------------- 46async def main(): 47 # Create a local link 48 link = LocalLink() 49 50 # Setup a stack for the client 51 client_controller = Controller("client controller", link=link) 52 client_host = Host() 53 client_host.controller = client_controller 54 client_device = Device("client", address='F0:F1:F2:F3:F4:F5', host=client_host) 55 await client_device.power_on() 56 57 # Setup a stack for the server 58 server_controller = Controller("server controller", link=link) 59 server_host = Host() 60 server_host.controller = server_controller 61 server_device = Device("server", address='F6:F7:F8:F9:FA:FB', host=server_host) 62 server_device.listener = ServerListener() 63 await server_device.power_on() 64 65 # Add a few entries to the device's GATT server 66 descriptor = Descriptor( 67 GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, 68 Descriptor.READABLE, 69 'My Description', 70 ) 71 manufacturer_name_characteristic = Characteristic( 72 GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, 73 Characteristic.READ, 74 Characteristic.READABLE, 75 "Fitbit", 76 [descriptor], 77 ) 78 device_info_service = Service( 79 GATT_DEVICE_INFORMATION_SERVICE, [manufacturer_name_characteristic] 80 ) 81 server_device.add_service(device_info_service) 82 83 # Connect the client to the server 84 connection = await client_device.connect(server_device.random_address) 85 print(f'=== Client: connected to {connection}') 86 87 # Discover all services 88 print('=== Discovering services') 89 peer = Peer(connection) 90 await peer.discover_services() 91 for service in peer.services: 92 await service.discover_characteristics() 93 for characteristic in service.characteristics: 94 await characteristic.discover_descriptors() 95 96 print('=== Services discovered') 97 show_services(peer.services) 98 99 # Discover all attributes 100 print('=== Discovering attributes') 101 attributes = await peer.discover_attributes() 102 for attribute in attributes: 103 print(attribute) 104 print('=== Attributes discovered') 105 106 # Read all attributes 107 for attribute in attributes: 108 try: 109 value = await attribute.read_value() 110 print(color(f'0x{attribute.handle:04X} = {value.hex()}', 'green')) 111 except ProtocolError as error: 112 print(color(f'cannot read {attribute.handle:04X}:', 'red'), error) 113 114 await asyncio.get_running_loop().create_future() 115 116 117# ----------------------------------------------------------------------------- 118logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) 119asyncio.run(main()) 120