importargparseimportasyncioimportloggingfrombleakimportBleakScannerfrombleak.backends.deviceimportBLEDevicefrombleak.backends.scannerimportAdvertisementDatalogger=logging.getLogger(__name__)printed_devices={}defsimple_callback(device:BLEDevice,advertisement_data:AdvertisementData):ifdevice.addressnotinprinted_devices:printed_devices[device.address]=advertisement_datalogger.info("{}: {}".format(device.address,advertisement_data))asyncdefmain(args:argparse.Namespace):scanner=BleakScanner(simple_callback,args.services,cb=dict(use_bdaddr=args.macos_use_bdaddr))whileTrue:#logger.info("(re)starting scanner")
awaitscanner.start()awaitasyncio.sleep(5.0)awaitscanner.stop()#Print New BLE Devices
if__name__=="__main__":parser=argparse.ArgumentParser()parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("--services",metavar="<uuid>",nargs="*",help="UUIDs of one or more services to filter for",)parser.add_argument("-d","--debug",action="store_true",help="sets the logging level to debug",)args=parser.parse_args()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(main(args))
service_explorer.py
importargparseimportasyncioimportloggingimportcollectionsfrombleakimportBleakClient,BleakScannerlogger=logging.getLogger(__name__)asyncdefmain(args:argparse.Namespace):output={}logger.info("starting scan...")ifargs.address:device=awaitBleakScanner.find_device_by_address(args.address,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with address '%s'",args.address)returnelse:device=awaitBleakScanner.find_device_by_name(args.name,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with name '%s'",args.name)returnlogger.info("connecting to device...")async withBleakClient(device,services=args.services)asclient:logger.info("connected")forserviceinclient.services:#logger.info("[Service] %s", service)
output[service.handle]={"info":"[Service] {}".format(service)}output[service.handle]["characteristics"]={}forcharinservice.characteristics:if"read"inchar.properties:try:value=awaitclient.read_gatt_char(char.uuid)output[service.handle]["characteristics"][char.handle]={"info":" [Characteristic] {} ({}), Value: {}".format(char,",".join(char.properties),value)}exceptExceptionase:output[service.handle]["characteristics"][char.handle]={"info":" [Characteristic] {} ({}), Error: {}".format(char,",".join(char.properties),e)}else:output[service.handle]["characteristics"][char.handle]={"info":" [Characteristic] {} ({})".format(char,",".join(char.properties))}#init descriptors
output[service.handle]["characteristics"][char.handle]["descriptors"]={}fordescriptorinchar.descriptors:try:value=awaitclient.read_gatt_descriptor(descriptor.handle)output[service.handle]["characteristics"][char.handle]["descriptors"][descriptor.handle]={"info":" [Descriptor] {}, Value: {}".format(descriptor,value)}exceptExceptionase:output[service.handle]["characteristics"][char.handle]["descriptors"][descriptor.handle]={"info":" [Descriptor] {}, Error: {}".format(descriptor,e)}#Sort Descriptors
output[service.handle]["characteristics"][char.handle]["descriptors"]=dict(sorted(output[service.handle]["characteristics"][char.handle]["descriptors"].items()))#Sort Characteristics
output[service.handle]["characteristics"]=dict(sorted(output[service.handle]["characteristics"].items()))#Sort Services
output=dict(sorted(output.items()))#Print Services
forservice_handleinoutput:logger.info(output[service_handle]["info"])#Print Characteristics
forcharacteristicinoutput[service_handle]["characteristics"]:logger.info(output[service_handle]["characteristics"][characteristic]["info"])#Print Descriptors
fordescriptorinoutput[service_handle]["characteristics"][characteristic]["descriptors"]:logger.info(output[service_handle]["characteristics"][characteristic]["descriptors"][descriptor]["info"])logger.info("disconnecting...")logger.info("disconnected")if__name__=="__main__":parser=argparse.ArgumentParser()device_group=parser.add_mutually_exclusive_group(required=True)device_group.add_argument("--name",metavar="<name>",help="the name of the bluetooth device to connect to",)device_group.add_argument("--address",metavar="<address>",help="the address of the bluetooth device to connect to",)parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("--services",nargs="+",metavar="<uuid>",help="if provided, only enumerate matching service(s)",)parser.add_argument("-d","--debug",action="store_true",help="sets the log level to debug",)args=parser.parse_args()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(main(args))
service_writer.py:
importasyncio,string,platform,sys,argparsefrombleakimportBleakClientfrombleak.uuidsimportnormalize_uuid_16,uuid16_dictasyncdefmain(args:argparse.Namespace):async withBleakClient(args.device,winrt=dict(use_cached_services=True))asclient:print(f"Connected: {client.is_connected}")#Read UUID or handle
ifargs.debug:read_data=awaitclient.read_gatt_char(args.service)print(f"Read[{args.service}]: {read_data}")#Write data to UUID or handle
print(f"Write[{args.service}]: {args.data}")value=awaitclient.read_gatt_char(args.service)print(f"Returned Data: {value}")if__name__=="__main__":parser=argparse.ArgumentParser()parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("--device",required=True,help="Which Device MAC to connect to",)parser.add_argument("--service",required=True,help="UUID or Handle to write to",)parser.add_argument("--data",required=True,help="the Data to write. Can be 0x02, or \"test\"",)parser.add_argument("-v",dest="debug",action="store_true",help="sets the logging level to debug",)args=parser.parse_args()print(args)#Convert to bytes
ifargs.data.starts_with("0x")andall(cinstring.hexdigitsforcinargs.data):args.data=int(args.data,16)else:args.data=args.data.encode()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(main(args))
yay -S bleah bleaksudo systemctl start bluetooth.service# You might need to edit the service file and add a -E to the end of the ExecStart#ExecStart=/usr/lib/bluetooth/bluetoothd -E
importasyncio,string,platform,sys,argparse,loggingfrombleakimportBleakClientfrombleak.uuidsimportnormalize_uuid_16,uuid16_dictlogger=logging.getLogger(__name__)asyncdefflag9(args:argparse.Namespace):async withBleakClient(args.device,winrt=dict(use_cached_services=True))asclient:logger.info(f"Connected: {client.is_connected}")foriinrange(255):write_data=bytearray([i])#Read UUID or handle
read_data=awaitclient.read_gatt_char(args.service)logger.info(f"Read[{args.service}]: {read_data}")#Write data to UUID or handle
logger.info(f"Write[{args.service}]: {write_data}")value=awaitclient.write_gatt_char(args.service,write_data,response=True)logger.info(f"Returned Data: {value}")asyncdefmain(args:argparse.Namespace):async withBleakClient(args.device,winrt=dict(use_cached_services=True))asclient:logger.info(f"Connected: {client.is_connected}")#Read UUID or handle
read_data=awaitclient.read_gatt_char(args.service)logger.info(f"Read[{args.service}]: {read_data}")#Write data to UUID or handle
logger.info(f"Write[{args.service}]: {args.data}")value=awaitclient.write_gatt_char(args.service,args.data,response=True)logger.info(f"Returned Data: {value}")if__name__=="__main__":parser=argparse.ArgumentParser()parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("--device",required=True,help="Which Device MAC to connect to",)parser.add_argument("--service",required=True,help="UUID or Handle to write to",)parser.add_argument("--data",required=True,help="the Data to write. Can be 0x02, or \"test\"",)parser.add_argument("-v",dest="debug",#default=True,
action="store_true",help="sets the logging level to debug",)args=parser.parse_args()#Convert to bytes
ifargs.data.startswith("0x")andall(cinstring.hexdigitsforcinargs.data):args.data=int(args.data,16)else:args.data=args.data.encode()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)#asyncio.run(main(args))
asyncio.run(flag9(args))
importasyncio,string,platform,sys,argparse,loggingfrombleakimportBleakClientfrombleak.uuidsimportnormalize_uuid_16,uuid16_dictlogger=logging.getLogger(__name__)asyncdefflag(args:argparse.Namespace):async withBleakClient(args.device,winrt=dict(use_cached_services=True))asclient:logger.info(f"Connected: {client.is_connected}")foriinrange(1000):#Read UUID or handle
read_data=awaitclient.read_gatt_char(args.service)logger.info(f"Read[{args.service}]: {read_data}")if__name__=="__main__":parser=argparse.ArgumentParser()parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("--device",required=True,help="Which Device MAC to connect to",)parser.add_argument("--service",required=True,help="UUID or Handle to write to",)parser.add_argument("--data",required=True,help="the Data to write. Can be 0x02, or \"test\"",)parser.add_argument("-v",dest="debug",#default=True,
action="store_true",help="sets the logging level to debug",)args=parser.parse_args()#Convert to bytes
ifargs.data.startswith("0x")andall(cinstring.hexdigitsforcinargs.data):args.data=int(args.data,16)else:args.data=args.data.encode()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(flag(args))
importargparseimportasyncioimportloggingfrombleakimportBleakClient,BleakScannerfrombleak.backends.characteristicimportBleakGATTCharacteristiclogger=logging.getLogger(__name__)defnotification_handler(characteristic:BleakGATTCharacteristic,data:bytearray):"""Simple notification handler which prints the data received."""logger.info(f"{characteristic.description}: {data}")asyncdefmain(args:argparse.Namespace):logger.info("starting scan...")ifargs.address:device=awaitBleakScanner.find_device_by_address(args.address,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with address '%s'",args.address)returnelse:device=awaitBleakScanner.find_device_by_name(args.name,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with name '%s'",args.name)returnlogger.info("connecting to device...")async withBleakClient(device)asclient:logger.info("Connected")awaitclient.start_notify(args.characteristic,notification_handler)read_data=awaitclient.read_gatt_char(args.characteristic)logger.info(f"Read[{args.characteristic}]: {read_data}")read_data=awaitclient.write_gatt_char(args.characteristic,bytearray([0x01]),response=True)logger.info(f"Write[{args.characteristic}]: 0x01 {read_data}")awaitasyncio.sleep(20.0)awaitclient.stop_notify(args.characteristic)if__name__=="__main__":parser=argparse.ArgumentParser()device_group=parser.add_mutually_exclusive_group(required=True)device_group.add_argument("--name",metavar="<name>",help="the name of the bluetooth device to connect to",)device_group.add_argument("--address",metavar="<address>",help="the address of the bluetooth device to connect to",)parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("characteristic",metavar="<notify uuid>",help="UUID of a characteristic that supports notifications",)parser.add_argument("-d","--debug",action="store_true",help="sets the log level to debug",)args=parser.parse_args()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(main(args))
Run the script:
>>> python enable_notifications.py --address"64:E8:33:82:AF:6A" 0000ff0c-0000-1000-8000-00805f9b34fb2023-11-14 00:01:32,679 __main__ INFO: starting scan...2023-11-14 00:01:32,824 __main__ INFO: connecting to device...2023-11-14 00:01:34,198 __main__ INFO: Connected2023-11-14 00:01:34,765 __main__ INFO: Vendor specific: bytearray(b'Listen to me for a single notification')2023-11-14 00:01:34,765 __main__ INFO: Read[0000ff0c-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to me for a single notification')2023-11-14 00:01:34,848 __main__ INFO: Write[0000ff0c-0000-1000-8000-00805f9b34fb]: 0x01 None2023-11-14 00:01:34,849 __main__ INFO: Vendor specific: bytearray(b'5ec3772bcd00cf06d8eb')
importargparseimportasyncioimportloggingfrombleakimportBleakClient,BleakScannerfrombleak.backends.characteristicimportBleakGATTCharacteristiclogger=logging.getLogger(__name__)defnotification_handler(characteristic:BleakGATTCharacteristic,data:bytearray):"""Simple notification handler which prints the data received."""logger.info(f"{characteristic.description}: {data}")asyncdefmain(args:argparse.Namespace):logger.info("starting scan...")ifargs.address:device=awaitBleakScanner.find_device_by_address(args.address,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with address '%s'",args.address)returnelse:device=awaitBleakScanner.find_device_by_name(args.name,cb=dict(use_bdaddr=args.macos_use_bdaddr))ifdeviceisNone:logger.error("could not find device with name '%s'",args.name)returnlogger.info("connecting to device...")async withBleakClient(device)asclient:logger.info("Connected")awaitclient.start_notify(args.characteristic,notification_handler)read_data=awaitclient.read_gatt_char(args.characteristic)logger.info(f"Read[{args.characteristic}]: {read_data}")read_data=awaitclient.write_gatt_char(args.characteristic,bytearray([0x01]),response=True)logger.info(f"Write[{args.characteristic}]: 0x01 {read_data}")awaitasyncio.sleep(20.0)awaitclient.stop_notify(args.characteristic)if__name__=="__main__":parser=argparse.ArgumentParser()device_group=parser.add_mutually_exclusive_group(required=True)device_group.add_argument("--name",metavar="<name>",help="the name of the bluetooth device to connect to",)device_group.add_argument("--address",metavar="<address>",help="the address of the bluetooth device to connect to",)parser.add_argument("--macos-use-bdaddr",action="store_true",help="when true use Bluetooth address instead of UUID on macOS",)parser.add_argument("characteristic",metavar="<notify uuid>",help="UUID of a characteristic that supports notifications",)parser.add_argument("-d","--debug",action="store_true",help="sets the log level to debug",)args=parser.parse_args()log_level=logging.DEBUGifargs.debugelselogging.INFOlogging.basicConfig(level=log_level,format="%(asctime)-15s%(name)-8s%(levelname)s: %(message)s",)asyncio.run(main(args))
Run the script:
>>> python enable_notifications.py --address"64:E8:33:82:AF:6A" 0000ff0e-0000-1000-8000-00805f9b34fb2023-11-14 00:10:18,764 __main__ INFO: starting scan...2023-11-14 00:10:18,923 __main__ INFO: connecting to device...2023-11-14 00:10:20,253 __main__ INFO: Connected2023-11-14 00:10:20,612 __main__ INFO: Vendor specific: bytearray(b'Listen to handle 0x0044 for a single indication\x00')2023-11-14 00:10:20,612 __main__ INFO: Read[0000ff0e-0000-1000-8000-00805f9b34fb]: bytearray(b'Listen to handle 0x0044 for a single indication\x00')2023-11-14 00:10:20,695 __main__ INFO: Write[0000ff0e-0000-1000-8000-00805f9b34fb]: 0x01 None2023-11-14 00:10:20,695 __main__ INFO: Vendor specific: bytearray(b'c7b86dd121848c77c113')