1# Copyright 2024 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 logging
20import sys
21import os
22
23from bumble.core import AdvertisingData
24from bumble.device import Device
25from bumble import att
26from bumble.profiles.hap import (
27    HearingAccessService,
28    HearingAidFeatures,
29    HearingAidType,
30    PresetSynchronizationSupport,
31    IndependentPresets,
32    DynamicPresets,
33    WritablePresetsSupport,
34    PresetRecord,
35)
36
37from bumble.transport import open_transport_or_link
38
39server_features = HearingAidFeatures(
40    HearingAidType.MONAURAL_HEARING_AID,
41    PresetSynchronizationSupport.PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED,
42    IndependentPresets.IDENTICAL_PRESET_RECORD,
43    DynamicPresets.PRESET_RECORDS_DOES_NOT_CHANGE,
44    WritablePresetsSupport.WRITABLE_PRESET_RECORDS_SUPPORTED,
45)
46
47foo_preset = PresetRecord(1, "foo preset")
48bar_preset = PresetRecord(50, "bar preset")
49foobar_preset = PresetRecord(5, "foobar preset")
50
51
52# -----------------------------------------------------------------------------
53async def main() -> None:
54    if len(sys.argv) < 3:
55        print('Usage: run_hap_server.py <config-file> <transport-spec-for-device>')
56        print('example: run_hap_server.py device1.json pty:hci_pty')
57        return
58
59    print('<<< connecting to HCI...')
60    async with await open_transport_or_link(sys.argv[2]) as hci_transport:
61        print('<<< connected')
62
63        device = Device.from_config_file_with_hci(
64            sys.argv[1], hci_transport.source, hci_transport.sink
65        )
66
67        await device.power_on()
68
69        hap = HearingAccessService(
70            device, server_features, [foo_preset, bar_preset, foobar_preset]
71        )
72        device.add_service(hap)
73
74        advertising_data = bytes(
75            AdvertisingData(
76                [
77                    (
78                        AdvertisingData.COMPLETE_LOCAL_NAME,
79                        bytes('Bumble HearingAccessService', 'utf-8'),
80                    ),
81                    (
82                        AdvertisingData.FLAGS,
83                        bytes(
84                            [
85                                AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
86                                | AdvertisingData.BR_EDR_HOST_FLAG
87                                | AdvertisingData.BR_EDR_CONTROLLER_FLAG
88                            ]
89                        ),
90                    ),
91                    (
92                        AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
93                        bytes(HearingAccessService.UUID),
94                    ),
95                ]
96            )
97        )
98
99        await device.create_advertising_set(
100            advertising_data=advertising_data,
101            auto_restart=True,
102        )
103
104
105# -----------------------------------------------------------------------------
106logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
107asyncio.run(main())
108