xref: /aosp_15_r20/external/openthread/tools/tcat_ble_client/bbtc.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker"""
2*cfb92d14SAndroid Build Coastguard Worker  Copyright (c) 2024, The OpenThread Authors.
3*cfb92d14SAndroid Build Coastguard Worker  All rights reserved.
4*cfb92d14SAndroid Build Coastguard Worker
5*cfb92d14SAndroid Build Coastguard Worker  Redistribution and use in source and binary forms, with or without
6*cfb92d14SAndroid Build Coastguard Worker  modification, are permitted provided that the following conditions are met:
7*cfb92d14SAndroid Build Coastguard Worker  1. Redistributions of source code must retain the above copyright
8*cfb92d14SAndroid Build Coastguard Worker     notice, this list of conditions and the following disclaimer.
9*cfb92d14SAndroid Build Coastguard Worker  2. Redistributions in binary form must reproduce the above copyright
10*cfb92d14SAndroid Build Coastguard Worker     notice, this list of conditions and the following disclaimer in the
11*cfb92d14SAndroid Build Coastguard Worker     documentation and/or other materials provided with the distribution.
12*cfb92d14SAndroid Build Coastguard Worker  3. Neither the name of the copyright holder nor the
13*cfb92d14SAndroid Build Coastguard Worker     names of its contributors may be used to endorse or promote products
14*cfb92d14SAndroid Build Coastguard Worker     derived from this software without specific prior written permission.
15*cfb92d14SAndroid Build Coastguard Worker
16*cfb92d14SAndroid Build Coastguard Worker  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17*cfb92d14SAndroid Build Coastguard Worker  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*cfb92d14SAndroid Build Coastguard Worker  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*cfb92d14SAndroid Build Coastguard Worker  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20*cfb92d14SAndroid Build Coastguard Worker  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*cfb92d14SAndroid Build Coastguard Worker  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*cfb92d14SAndroid Build Coastguard Worker  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*cfb92d14SAndroid Build Coastguard Worker  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*cfb92d14SAndroid Build Coastguard Worker  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*cfb92d14SAndroid Build Coastguard Worker  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*cfb92d14SAndroid Build Coastguard Worker  POSSIBILITY OF SUCH DAMAGE.
27*cfb92d14SAndroid Build Coastguard Worker"""
28*cfb92d14SAndroid Build Coastguard Worker
29*cfb92d14SAndroid Build Coastguard Workerimport asyncio
30*cfb92d14SAndroid Build Coastguard Workerimport argparse
31*cfb92d14SAndroid Build Coastguard Workerimport logging
32*cfb92d14SAndroid Build Coastguard Workerimport os
33*cfb92d14SAndroid Build Coastguard Worker
34*cfb92d14SAndroid Build Coastguard Workerfrom ble.ble_connection_constants import BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, \
35*cfb92d14SAndroid Build Coastguard Worker    BBTC_RX_CHAR_UUID
36*cfb92d14SAndroid Build Coastguard Workerfrom ble.ble_stream import BleStream
37*cfb92d14SAndroid Build Coastguard Workerfrom ble.ble_stream_secure import BleStreamSecure
38*cfb92d14SAndroid Build Coastguard Workerfrom ble.udp_stream import UdpStream
39*cfb92d14SAndroid Build Coastguard Workerfrom ble import ble_scanner
40*cfb92d14SAndroid Build Coastguard Workerfrom cli.cli import CLI
41*cfb92d14SAndroid Build Coastguard Workerfrom dataset.dataset import ThreadDataset
42*cfb92d14SAndroid Build Coastguard Workerfrom cli.command import CommandResult
43*cfb92d14SAndroid Build Coastguard Workerfrom utils import select_device_by_user_input, quit_with_reason
44*cfb92d14SAndroid Build Coastguard Worker
45*cfb92d14SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
46*cfb92d14SAndroid Build Coastguard Worker
47*cfb92d14SAndroid Build Coastguard Worker
48*cfb92d14SAndroid Build Coastguard Workerasync def main():
49*cfb92d14SAndroid Build Coastguard Worker    logging.basicConfig(level=logging.WARNING)
50*cfb92d14SAndroid Build Coastguard Worker
51*cfb92d14SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description='Device parameters')
52*cfb92d14SAndroid Build Coastguard Worker    parser.add_argument('--debug', help='Enable debug logs', action='store_true')
53*cfb92d14SAndroid Build Coastguard Worker    parser.add_argument('--info', help='Enable info logs', action='store_true')
54*cfb92d14SAndroid Build Coastguard Worker    parser.add_argument('--cert_path', help='Path to certificate chain and key', action='store', default='auth')
55*cfb92d14SAndroid Build Coastguard Worker    group = parser.add_mutually_exclusive_group()
56*cfb92d14SAndroid Build Coastguard Worker    group.add_argument('--mac', type=str, help='Device MAC address', action='store')
57*cfb92d14SAndroid Build Coastguard Worker    group.add_argument('--name', type=str, help='Device name', action='store')
58*cfb92d14SAndroid Build Coastguard Worker    group.add_argument('--scan', help='Scan all available devices', action='store_true')
59*cfb92d14SAndroid Build Coastguard Worker    group.add_argument('--simulation', help='Connect to simulation node id', action='store')
60*cfb92d14SAndroid Build Coastguard Worker    args = parser.parse_args()
61*cfb92d14SAndroid Build Coastguard Worker
62*cfb92d14SAndroid Build Coastguard Worker    if args.debug:
63*cfb92d14SAndroid Build Coastguard Worker        logger.setLevel(logging.DEBUG)
64*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.ble_stream').setLevel(logging.DEBUG)
65*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.ble_stream_secure').setLevel(logging.DEBUG)
66*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.udp_stream').setLevel(logging.DEBUG)
67*cfb92d14SAndroid Build Coastguard Worker    elif args.info:
68*cfb92d14SAndroid Build Coastguard Worker        logger.setLevel(logging.INFO)
69*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.ble_stream').setLevel(logging.INFO)
70*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.ble_stream_secure').setLevel(logging.INFO)
71*cfb92d14SAndroid Build Coastguard Worker        logging.getLogger('ble.udp_stream').setLevel(logging.INFO)
72*cfb92d14SAndroid Build Coastguard Worker
73*cfb92d14SAndroid Build Coastguard Worker    device = await get_device_by_args(args)
74*cfb92d14SAndroid Build Coastguard Worker
75*cfb92d14SAndroid Build Coastguard Worker    ble_sstream = None
76*cfb92d14SAndroid Build Coastguard Worker
77*cfb92d14SAndroid Build Coastguard Worker    if device is not None:
78*cfb92d14SAndroid Build Coastguard Worker        print(f'Connecting to {device}')
79*cfb92d14SAndroid Build Coastguard Worker        ble_sstream = BleStreamSecure(device)
80*cfb92d14SAndroid Build Coastguard Worker        ble_sstream.load_cert(
81*cfb92d14SAndroid Build Coastguard Worker            certfile=os.path.join(args.cert_path, 'commissioner_cert.pem'),
82*cfb92d14SAndroid Build Coastguard Worker            keyfile=os.path.join(args.cert_path, 'commissioner_key.pem'),
83*cfb92d14SAndroid Build Coastguard Worker            cafile=os.path.join(args.cert_path, 'ca_cert.pem'),
84*cfb92d14SAndroid Build Coastguard Worker        )
85*cfb92d14SAndroid Build Coastguard Worker        logger.info(f"Certificates and key loaded from '{args.cert_path}'")
86*cfb92d14SAndroid Build Coastguard Worker
87*cfb92d14SAndroid Build Coastguard Worker        print('Setting up secure TLS channel..', end='')
88*cfb92d14SAndroid Build Coastguard Worker        try:
89*cfb92d14SAndroid Build Coastguard Worker            await ble_sstream.do_handshake()
90*cfb92d14SAndroid Build Coastguard Worker            print('\nDone')
91*cfb92d14SAndroid Build Coastguard Worker            ble_sstream.log_cert_identities()
92*cfb92d14SAndroid Build Coastguard Worker        except Exception as e:
93*cfb92d14SAndroid Build Coastguard Worker            print('\nFailed')
94*cfb92d14SAndroid Build Coastguard Worker            logger.error(e)
95*cfb92d14SAndroid Build Coastguard Worker            ble_sstream.log_cert_identities()
96*cfb92d14SAndroid Build Coastguard Worker            quit_with_reason('TLS handshake failure')
97*cfb92d14SAndroid Build Coastguard Worker
98*cfb92d14SAndroid Build Coastguard Worker    ds = ThreadDataset()
99*cfb92d14SAndroid Build Coastguard Worker    cli = CLI(ds, args, ble_sstream)
100*cfb92d14SAndroid Build Coastguard Worker    loop = asyncio.get_running_loop()
101*cfb92d14SAndroid Build Coastguard Worker    print('Enter \'help\' to see available commands' ' or \'exit\' to exit the application.')
102*cfb92d14SAndroid Build Coastguard Worker    while True:
103*cfb92d14SAndroid Build Coastguard Worker        user_input = await loop.run_in_executor(None, lambda: input('> '))
104*cfb92d14SAndroid Build Coastguard Worker        if user_input.lower() == 'exit':
105*cfb92d14SAndroid Build Coastguard Worker            print('Disconnecting...')
106*cfb92d14SAndroid Build Coastguard Worker            if ble_sstream is not None:
107*cfb92d14SAndroid Build Coastguard Worker                await ble_sstream.close()
108*cfb92d14SAndroid Build Coastguard Worker            break
109*cfb92d14SAndroid Build Coastguard Worker        try:
110*cfb92d14SAndroid Build Coastguard Worker            result: CommandResult = await cli.evaluate_input(user_input)
111*cfb92d14SAndroid Build Coastguard Worker            if result:
112*cfb92d14SAndroid Build Coastguard Worker                result.pretty_print()
113*cfb92d14SAndroid Build Coastguard Worker        except Exception as e:
114*cfb92d14SAndroid Build Coastguard Worker            logger.error(e)
115*cfb92d14SAndroid Build Coastguard Worker
116*cfb92d14SAndroid Build Coastguard Worker
117*cfb92d14SAndroid Build Coastguard Workerasync def get_device_by_args(args):
118*cfb92d14SAndroid Build Coastguard Worker    device = None
119*cfb92d14SAndroid Build Coastguard Worker    if args.mac:
120*cfb92d14SAndroid Build Coastguard Worker        device = await ble_scanner.find_first_by_mac(args.mac)
121*cfb92d14SAndroid Build Coastguard Worker        device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
122*cfb92d14SAndroid Build Coastguard Worker    elif args.name:
123*cfb92d14SAndroid Build Coastguard Worker        device = await ble_scanner.find_first_by_name(args.name)
124*cfb92d14SAndroid Build Coastguard Worker        device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
125*cfb92d14SAndroid Build Coastguard Worker    elif args.scan:
126*cfb92d14SAndroid Build Coastguard Worker        tcat_devices = await ble_scanner.scan_tcat_devices()
127*cfb92d14SAndroid Build Coastguard Worker        device = select_device_by_user_input(tcat_devices)
128*cfb92d14SAndroid Build Coastguard Worker        if device:
129*cfb92d14SAndroid Build Coastguard Worker            device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
130*cfb92d14SAndroid Build Coastguard Worker    elif args.simulation:
131*cfb92d14SAndroid Build Coastguard Worker        device = UdpStream("127.0.0.1", int(args.simulation))
132*cfb92d14SAndroid Build Coastguard Worker
133*cfb92d14SAndroid Build Coastguard Worker    return device
134*cfb92d14SAndroid Build Coastguard Worker
135*cfb92d14SAndroid Build Coastguard Worker
136*cfb92d14SAndroid Build Coastguard Workerif __name__ == '__main__':
137*cfb92d14SAndroid Build Coastguard Worker    try:
138*cfb92d14SAndroid Build Coastguard Worker        asyncio.run(main())
139*cfb92d14SAndroid Build Coastguard Worker    except asyncio.CancelledError:
140*cfb92d14SAndroid Build Coastguard Worker        pass  # device disconnected
141