diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv/ diff --git a/README.md b/README.md index 3e3007c..ffa2b2f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ sudo pip install pybluez` You've installed the Python 2 version of the bluez bindings. Either run the script using python2 or install the Python 3 bindings. Since they aren't packaged, you would need to install them using pip: ``` -sudo python3 -m pip install pybluez` +sudo python3 -m pip install git+https://github.com/pybluez/pybluez.git ``` ### Setup your Raspberry Pi diff --git a/bleClient.py b/bleClient.py index 7271494..86df3ad 100755 --- a/bleClient.py +++ b/bleClient.py @@ -4,151 +4,250 @@ # Auth: P Srinivas Rao # Desc: Bluetooth client application that uses RFCOMM sockets # intended for use with rfcomm-server -import os import sys +import os import time import logging import logging.config -import json #Uses JSON package -import cPickle as pickle #Serializing and de-serializing a Python object structure -from bluetooth import * #Python Bluetooth library +import json # Uses JSON package + +# import cPickle as pickle # Serializing and de-serializing a Python object structure +import pickle + +import bluetooth # Python Bluetooth library + +logger = logging.getLogger("bleClientLogger") -logger = logging.getLogger('bleClientLogger') def startLogging( - default_path='configLogger.json', - default_level=logging.INFO, - env_key='LOG_CFG' + default_path="configLogger.json", default_level=logging.INFO, env_key="LOG_CFG" ): - #Setup logging configuration + """Init the logging subsystem""" + # Setup logging configuration path = default_path value = os.getenv(env_key, None) if value: path = value if os.path.exists(path): - with open(path, 'rt') as f: - config = json.load(f) + with open(path, "rt", encoding="utf-8") as file_handle: + config = json.load(file_handle) logging.config.dictConfig(config) else: logging.basicConfig(level=default_level) + class bleClient: - def __init__(self, clientSocket=None): - if clientSocket is None: - self.clientSocket = clientSocket - self.bleService = None + """Provide Bluetooth Client interface""" + + def __init__(self, client_socket=None): + if client_socket is None: + self.client_socket = client_socket + self.ble_service = None self.addr = None self.uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee" - self.jsonFile ="text.json" - self.jsonObj = None + self.json_file = "text.json" + self.json_obj = None else: - self.clientSocket = clientSocket + self.client_socket = client_socket def getBluetoothServices(self): + """Get BT service""" try: - logger.info("Searching for Bluetooth services ...") - for reConnect in range(2, 4): - bleService = find_service( uuid = self.uuid, address = self.addr ) - if len(bleService) == 0: - logger.info("Re-connecting Bluetooth services : %d attempt", reConnect) + logger.info("Searching for Bluetooth services ...") + for reconnect in range(2, 4): + ble_service = bluetooth.find_service(uuid=self.uuid, address=self.addr) + if len(ble_service) == 0: + logger.info( + "Re-connecting Bluetooth services : %d attempt", reconnect + ) else: break - if not bleService: raise SystemExit(), KeyboardInterrupt() + if not ble_service: + raise Exception([SystemExit(), KeyboardInterrupt()]) else: logger.info("Found Bluetooth services ..") - logger.info("Protocol\t: %s", bleService[0]['protocol']) - logger.info("Name\t\t: %s", bleService[0]['name']) - logger.info("Service-id\t: %s", bleService[0]['service-id']) - logger.info("Profiles\t: %s", bleService[0]['profiles']) - logger.info("Service-class\t: %s", bleService[0]['service-classes']) - logger.info("Host\t\t: %s", bleService[0]['host']) - logger.info("Provider\t: %s", bleService[0]['provider']) - logger.info("Port\t\t: %s", bleService[0]['port']) - logger.info("Description\t: %s", bleService[0]['description']) - self.bleService = bleService - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Couldn't find the RaspberryPi Bluetooth service : Invalid uuid", exc_info=True) + logger.info("Protocol\t: %s", ble_service[0]["protocol"]) + logger.info("Name\t\t: %s", ble_service[0]["name"]) + logger.info("Service-id\t: %s", ble_service[0]["service-id"]) + logger.info("Profiles\t: %s", ble_service[0]["profiles"]) + logger.info("Service-class\t: %s", ble_service[0]["service-classes"]) + logger.info("Host\t\t: %s", ble_service[0]["host"]) + logger.info("Provider\t: %s", ble_service[0]["provider"]) + logger.info("Port\t\t: %s", ble_service[0]["port"]) + logger.info("Description\t: %s", ble_service[0]["description"]) + self.ble_service = ble_service + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + logger.error( + "Couldn't find the RaspberryPi Bluetooth service : Invalid uuid", + exc_info=True, + ) + return False + + return True def getBluetoothSocket(self): + """Get the BT socket""" try: - self.clientSocket=BluetoothSocket( RFCOMM ) - logger.info("Bluetooth client socket successfully created for RFCOMM service ...") - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Failed to create the bluetooth client socket for RFCOMM service ... ", exc_info=True) + self.client_socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM) + logger.info( + "Bluetooth client socket successfully created for RFCOMM service ... " + ) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + logger.error( + "Failed to create the bluetooth client socket for RFCOMM service ... ", + exc_info=True, + ) def getBluetoothConnection(self): - try: - bleServiceInfo = self.bleService[0] - logger.info("Connecting to \"%s\" on %s with port %s" % (bleServiceInfo['name'], bleServiceInfo['host'], bleServiceInfo['port'])) - self.clientSocket.connect((bleServiceInfo['host'], bleServiceInfo['port'])) - logger.info("Connected successfully to %s "% (bleServiceInfo['name'])) - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Failed to connect to \"%s\" on address %s with port %s" % (bleServiceInfo['name'], bleServiceInfo['host'], bleServiceInfo['port']), exc_info=True) + """Get the BT connection""" + ble_service_info = self.ble_service[0] + msg = ( + f'Connecting to "{ble_service_info["name"]}" on ' + f'{ble_service_info["host"]} with port {ble_service_info["port"]}' + ) + logger.info(msg) + + attempt = 0 + # Retry a few times + while True: + attempt += 1 + + try: + self.client_socket.connect( + (ble_service_info["host"], ble_service_info["port"]) + ) + msg = f"Connected successfully to {ble_service_info['name']}" + logger.info(msg) + break + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + msg = ( + f'Failed to connect to "{ble_service_info["name"]}" on ' + f'address {ble_service_info["host"]} with port {ble_service_info["port"]}' + ) + logger.error( + msg, + exc_info=True, + ) + + time.sleep(1) + if attempt > 5: + return False + + return True def readJsonFile(self): + """Read JSON file""" try: - jsonFileObj = open(self.lsonFile,"r") - logger.info("File successfully uploaded to %s" % (jsonFileObj)) - self.jsonObj = json.load(jsonFileObj) - logger.info("Content loaded successfully from the %s file" %(self.jsonFile)) - jsonFileObj.close() - except (Exception, IOError) as e: - logger.error("Failed to load content from the %s" % (self.jsonFile), exc_info=True) + json_file_obj = open(self.json_file, "r", encoding="utf-8") + msg = "File successfully uploaded to %s" % (json_file_obj) + logger.info(msg) + self.json_obj = json.load(json_file_obj) + msg = "Content loaded successfully from the %s file" % (self.json_file) + logger.info(msg) + json_file_obj.close() + except (Exception, IOError) as _: + msg = "Failed to load content from the %s" % (self.json_file) + logger.error(msg, exc_info=True) def serializeData(self): + """Serialize the data""" try: - serializedData = pickle.dumps(self.jsonObj) + serialized_data = pickle.dumps(self.json_obj) logger.info("Object successfully converted to a serialized string") - return serializedData - except (Exception, pickle.PicklingError) as e: - logger.error("Failed to convert json object to serialized string", exc_info=True) + return serialized_data + except (Exception, pickle.PicklingError) as _: + logger.error( + "Failed to convert json object to serialized string", exc_info=True + ) - def sendData(self, _serializedData): + def sendData(self, serialized_data): + """Send data via BT""" try: logger.info("Sending data over bluetooth connection") - _serializedData =str(len(_serializedData))+ ":"+_serializedData - self.clientSocket.send(_serializedData) + _serialized_data = b"" + _serialized_data += len(serialized_data).to_bytes(2, "little") + _serialized_data += b":" + _serialized_data += serialized_data + self.client_socket.send(_serialized_data) time.sleep(0.5) while True: - dataRecv= self.clientSocket.recv(1024) - if dataRecv in ['EmptyBufferResend', 'CorruptedBufferResend', 'DelimiterMissingBufferResend']: - self.clientSocket.send(_serializedData) + data_recv = self.client_socket.recv(1024) + if data_recv in [ + "EmptyBufferResend", + "CorruptedBufferResend", + "DelimiterMissingBufferResend", + ]: + self.client_socket.send(_serialized_data) time.sleep(0.5) - logger.info("%s : Re-sending data over bluetooth connection" %(dataRecv)) + msg = f"{data_recv} : Re-sending data over bluetooth connection" + logger.info(msg) else: break logger.info("Data sent successfully over bluetooth connection") - except (Exception, IOError) as e: + except (Exception, IOError) as _: logger.error("Failed to send data over bluetooth connection", exc_info=True) def closeBluetoothSocket(self): + """Close BT socket""" try: - self.clientSocket.close() + self.client_socket.close() logger.info("Bluetooth client socket successfully closed ...") - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: logger.error("Failed to close the bluetooth client socket ", exc_info=True) def start(self): + """Start the BT interface""" # Search for the RaspberryPi Bluetooth service - self.getBluetoothServices() + res = self.getBluetoothServices() + if not res: + logger.error("Failed to get bluetooth services") + sys.exit(0) # Create the client socket self.getBluetoothSocket() # Connect to bluetooth service - self.getBluetoothConnection() + res = self.getBluetoothConnection() + if not res: + logger.error("Failed to get bluetooth connection") + sys.exit(0) def send(self): + """Send content""" + # Load the contents from the file, which creates a new json object self.readJsonFile() # Convert the json object to a serialized string - serializedData = self.serializeData() + serialized_data = self.serializeData() # Sending data over bluetooth connection - self.sendData(serializedData) + self.sendData(serialized_data) def stop(self): + """Stop the BT interface""" + # Disconnecting bluetooth service self.closeBluetoothSocket() -if __name__ == '__main__': + +if __name__ == "__main__": startLogging() logger.info("Setup logging configuration") bleClnt = bleClient() diff --git a/bleServer.py b/bleServer.py index 04ae77b..f16efe9 100755 --- a/bleServer.py +++ b/bleServer.py @@ -3,22 +3,19 @@ # File: bleServer.py # Auth: P Srinivas Rao # Desc: Bluetooth server application that uses RFCOMM sockets - -import os import sys -import time +import os import logging import logging.config -import json # Uses JSON package -import cPickle as pickle # Serializing and de-serializing a Python object structure -from bluetooth import * # Python Bluetooth library +import json # Uses JSON package +import pickle # Serializing and de-serializing a Python object structure +import bluetooth # Python Bluetooth library + +logger = logging.getLogger("bleServerLogger") -logger = logging.getLogger('bleServerLogger') def startLogging( - default_path='configLogger.json', - default_level=logging.INFO, - env_key='LOG_CFG' + default_path="configLogger.json", default_level=logging.INFO, env_key="LOG_CFG" ): # Setup logging configuration path = default_path @@ -26,141 +23,212 @@ def startLogging( if value: path = value if os.path.exists(path): - with open(path, 'rt') as f: - config = json.load(f) + with open(path, "rt", encoding="utf-8") as file_handle: + config = json.load(file_handle) logging.config.dictConfig(config) else: logging.basicConfig(level=default_level) + class bleServer: - def __init__(self, serverSocket=None, clientSocket=None): - if serverSocket is None: - self.dataObj = None - self.serverSocket = serverSocket - self.clientSocket = clientSocket - self.serviceName="BluetoothServices" - self.jsonFile ="text.json" + def __init__(self, server_socket=None, client_socket=None): + if server_socket is None: + self.data_obj = None + self.server_socket = server_socket + self.client_socket = client_socket + self.service_name = "BluetoothServices" + self.json_file = "text.json" self.uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee" else: - self.serverSocket = serverSocket - self.clientSocket = clientSocket + self.server_socket = server_socket + self.client_socket = client_socket def getBluetoothSocket(self): try: - self.serverSocket=BluetoothSocket( RFCOMM ) - logger.info("Bluetooth server socket successfully created for RFCOMM service...") - except (BluetoothError, SystemExit, KeyboardInterrupt) as e: + self.server_socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM) + logger.info( + "Bluetooth server socket successfully created for RFCOMM service..." + ) + except (bluetooth.BluetoothError, SystemExit, KeyboardInterrupt) as _: logger.error("Failed to create the bluetooth server socket ", exc_info=True) + return False + return True def getBluetoothConnection(self): try: - self.serverSocket.bind(("",PORT_ANY)) - logger.info("Bluetooth server socket bind successfully on host "" to PORT_ANY...") - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Failed to bind server socket on host to PORT_ANY ... ", exc_info=True) + self.server_socket.bind(("", bluetooth.PORT_ANY)) + logger.info( + "Bluetooth server socket bind successfully on host '' to PORT_ANY..." + ) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + logger.error( + "Failed to bind server socket on host to PORT_ANY ... ", exc_info=True + ) + return False + try: - self.serverSocket.listen(1) - logger.info("Bluetooth server socket put to listening mode successfully ...") - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Failed to put server socket to listening mode ... ", exc_info=True) + self.server_socket.listen(1) + logger.info( + "Bluetooth server socket put to listening mode successfully ..." + ) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + logger.error( + "Failed to put server socket to listening mode ... ", exc_info=True + ) + return False try: - port=self.serverSocket.getsockname()[1] - logger.info("Waiting for connection on RFCOMM channel %d" % (port)) - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: - logger.error("Failed to get connection on RFCOMM channel ... ", exc_info=True) + port = self.server_socket.getsockname()[1] + msg = "Waiting for connection on RFCOMM channel %d" % (port) + logger.info(msg) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: + logger.error( + "Failed to get connection on RFCOMM channel ... ", exc_info=True + ) + return False + + return True def advertiseBluetoothService(self): + """Advertise BT service""" try: - advertise_service( self.serverSocket, self.serviceName, - service_id = self.uuid, - service_classes = [ self.uuid, SERIAL_PORT_CLASS ], - profiles = [ SERIAL_PORT_PROFILE ], - # protocols = [ OBEX_UUID ] - ) - logger.info("%s advertised successfully ..." % (self.serviceName)) - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: + bluetooth.advertise_service( + self.server_socket, + self.service_name, + service_id=self.uuid, + service_classes=[self.uuid, bluetooth.SERIAL_PORT_CLASS], + profiles=[bluetooth.SERIAL_PORT_PROFILE], + # protocols = [ OBEX_UUID ] + ) + msg = "%s advertised successfully ..." % (self.service_name) + logger.info(msg) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: logger.error("Failed to advertise bluetooth services ... ", exc_info=True) + return False + + return True def acceptBluetoothConnection(self): try: - self.clientSocket, clientInfo = self.serverSocket.accept() - logger.info("Accepted bluetooth connection from %s", clientInfo) - except (Exception, BluetoothError, SystemExit, KeyboardInterrupt) as e: + self.client_socket, client_info = self.server_socket.accept() + logger.info("Accepted bluetooth connection from %s", client_info) + except ( + Exception, + bluetooth.BluetoothError, + SystemExit, + KeyboardInterrupt, + ) as _: logger.error("Failed to accept bluetooth connection ... ", exc_info=True) def recvData(self): try: while True: - data= self.clientSocket.recv(1024) + data = self.client_socket.recv(1024) if not data: - self.clientSocket.send("EmptyBufferResend") + self.client_socket.send("EmptyBufferResend") # remove the length bytes from the front of buffer # leave any remaining bytes in the buffer! - dataSizeStr, ignored, data = data.partition(':') - dataSize = int(dataSizeStr) - if len(data) < dataSize: - self.clientSocket.send("CorruptedBufferResend") + data_size_str, _, data = data.partition(b":") + data_size = int.from_bytes(data_size_str, "little") + if len(data) < data_size: + self.client_socket.send("CorruptedBufferResend") else: - self.clientSocket.send("DataRecived") + self.client_socket.send("DataRecived") break logger.info("Data received successfully over bluetooth connection") return data - except (Exception, IOError, BluetoothError) as e: + except (Exception, IOError, bluetooth.BluetoothError) as _: pass - def deserializedData(self, _dataRecv): + def deserializedData(self, _data_recv): try: - self.dataObj=pickle.loads(_dataRecv) + self.data_obj = pickle.loads(_data_recv) logger.info("Serialized string converted successfully to object") - except (Exception, pickle.UnpicklingError) as e: + except (Exception, pickle.UnpicklingError) as _: logger.error("Failed to de-serialized string ... ", exc_info=True) def writeJsonFile(self): try: # Open a file for writing - jsonFileObj = open(self.jsonFile,"w") - logger.info("%s file successfully opened to %s" % (self.jsonFile, jsonFileObj)) + json_file_obj = open(self.json_file, "w", encoding="utf-8") + msg = "%s file successfully opened to %s" % (self.json_file, json_file_obj) + logger.info(msg) # Save the dictionary into this file # (the 'indent=4' is optional, but makes it more readable) - json.dump(self.dataObj,jsonFileObj, indent=4) - logger.info("Content dumped successfully to the %s file" %(self.jsonFile)) + json.dump(self.data_obj, json_file_obj, indent=4) + msg = "Content dumped successfully to the %s file" % (self.json_file) + logger.info(msg) # Close the file - jsonFileObj.close() - logger.info("%s file successfully closed" %(self.jsonFile)) - except (Exception, IOError) as e: - logger.error("Failed to write json contents to the file ... ", exc_info=True) + json_file_obj.close() + msg = "%s file successfully closed" % (self.json_file) + logger.info(msg) + except (Exception, IOError) as _: + logger.error( + "Failed to write json contents to the file ... ", exc_info=True + ) def closeBluetoothSocket(self): try: - self.clientSocket.close() - self.serverSocket.close() + self.client_socket.close() + self.server_socket.close() logger.info("Bluetooth sockets successfully closed ...") - except (Exception, BluetoothError) as e: + except (Exception, bluetooth.BluetoothError) as _: logger.error("Failed to close the bluetooth sockets ", exc_info=True) def start(self): - # Create the server socket - self.getBluetoothSocket() - # get bluetooth connection to port # of the first available - self.getBluetoothConnection() - # advertising bluetooth services - self.advertiseBluetoothService() - # Accepting bluetooth connection - self.acceptBluetoothConnection() + # Create the server socket + res = self.getBluetoothSocket() + if not res: + sys.exit(0) + # get bluetooth connection to port # of the first available + res = self.getBluetoothConnection() + if not res: + sys.exit(0) + # advertising bluetooth services + res = self.advertiseBluetoothService() + if not res: + sys.exit(0) + # Accepting bluetooth connection + self.acceptBluetoothConnection() def receive(self): - # receive data - dataRecv=self.recvData() - # de-serializing data - self.deserializedData(dataRecv) - # Writing json object to the file - self.writeJsonFile() + # receive data + data_recv = self.recvData() + # print(f"{data_recv=}") + + if data_recv is None: + logger.error("Failed to handle incoming data") + return + # de-serializing data + self.deserializedData(data_recv) + # Writing json object to the file + self.writeJsonFile() def stop(self): - # Disconnecting bluetooth sockets - self.closeBluetoothSocket() + # Disconnecting bluetooth sockets + self.closeBluetoothSocket() + -if __name__ == '__main__': +if __name__ == "__main__": startLogging() bleSvr = bleServer() bleSvr.start()