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