• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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