From 336a62a795d59a99f6046b7dbb30fb38a0215e72 Mon Sep 17 00:00:00 2001 From: Lucas Oskorep Date: Thu, 26 Sep 2019 17:04:20 -0500 Subject: [PATCH] Adding in working Kano Code samples using every library I could find for BLE on python BLEAK Is by far the best/most effective library I have seen for it so far. --- ConnectToKano.py | 0 DiscoverBLEDevices.py | 9 ++++ ReadCharacteristics.py | 37 ++++++++++++++ bleak-characteristic-notify.py | 0 sensor_information | 93 ++++++++++++++++++++++++++++++++++ subscribe_to_notification.py | 81 +++++++++++++++++++++++++++++ test2.py | 58 +++++++++++++++++++++ 7 files changed, 278 insertions(+) create mode 100644 ConnectToKano.py create mode 100644 DiscoverBLEDevices.py create mode 100644 ReadCharacteristics.py create mode 100644 bleak-characteristic-notify.py create mode 100644 sensor_information create mode 100644 subscribe_to_notification.py create mode 100644 test2.py diff --git a/ConnectToKano.py b/ConnectToKano.py new file mode 100644 index 0000000..e69de29 diff --git a/DiscoverBLEDevices.py b/DiscoverBLEDevices.py new file mode 100644 index 0000000..c5be575 --- /dev/null +++ b/DiscoverBLEDevices.py @@ -0,0 +1,9 @@ +import gatt + +class AnyDeviceManager(gatt.DeviceManager): + def device_discovered(self, device): + print("Discovered [%s] %s" % (device.mac_address, device.alias())) + +manager = AnyDeviceManager(adapter_name='hci0') +manager.start_discovery() +manager.run() \ No newline at end of file diff --git a/ReadCharacteristics.py b/ReadCharacteristics.py new file mode 100644 index 0000000..42522a7 --- /dev/null +++ b/ReadCharacteristics.py @@ -0,0 +1,37 @@ +import gatt + +manager = gatt.DeviceManager(adapter_name='hci0') + +class KanoWand(gatt.Device): + def services_resolved(self): + super().services_resolved() + print("Grabbing services") + print(self.is_connected()) + print(self.services) + + for service in self.services: + print("FOUND SERVICE") + # print(service) + # print(service.device) + print(service.uuid) + print(service.characteristics) + for char in service.characteristics: + print("FOUND CHARACTERISTIC") + print(char.uuid) + print(char.read_value()) + # device_information_service = next(s for s in self.services if s.uuid == '0000180a-0000-1000-8000-00805f9b34fb') + # + # firmware_version_characteristic = next( + # c for c in device_information_service.characteristics + # if c.uuid == '00002a26-0000-1000 -8000-00805f9b34fb') + # + # firmware_version_characteristic.read_value() + + def characteristic_value_updated(self, characteristic, value): + print("Firmware version:", value.decode("utf-8")) + + +device = KanoWand(mac_address='e3:ae:cd:af:28:e2', manager=manager) +device.connect() +manager.run() +manager.stop() \ No newline at end of file diff --git a/bleak-characteristic-notify.py b/bleak-characteristic-notify.py new file mode 100644 index 0000000..e69de29 diff --git a/sensor_information b/sensor_information new file mode 100644 index 0000000..ffecf75 --- /dev/null +++ b/sensor_information @@ -0,0 +1,93 @@ + +00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile + +00002a00-0000-1000-8000-00805f9b34fb: +['read', 'write'] + +00002a01-0000-1000-8000-00805f9b34fb: +['read'] + +00002a04-0000-1000-8000-00805f9b34fb: +['read'] + +00002aa6-0000-1000-8000-00805f9b34fb: +['read'] +Generic Access Profile +00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile + +00002a05-0000-1000-8000-00805f9b34fb: +['indicate'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 13) +Generic Attribute Profile +64a70010-f691-4b93-a6f4-0968f5b648f8: Unknown + +64a7000b-f691-4b93-a6f4-0968f5b648f8: +['read'] + +64a70013-f691-4b93-a6f4-0968f5b648f8: +['read'] + +64a70001-f691-4b93-a6f4-0968f5b648f8: +['read'] +Unknown +64a70012-f691-4b93-a6f4-0968f5b648f8: Unknown + +64a70007-f691-4b93-a6f4-0968f5b648f8: Battery. +['read', 'notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 24) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 25) + +64a70008-f691-4b93-a6f4-0968f5b648f8: Vibration motor. +['read', 'write'] +00002901-0000-1000-8000-00805f9b34fb: (Handle: 28) + +64a70009-f691-4b93-a6f4-0968f5b648f8: Led control. +['read', 'write'] +00002901-0000-1000-8000-00805f9b34fb: (Handle: 31) + +64a7000d-f691-4b93-a6f4-0968f5b648f8: User button. +['read', 'notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 34) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 35) + +64a7000f-f691-4b93-a6f4-0968f5b648f8: Keep alive. +['write'] +00002901-0000-1000-8000-00805f9b34fb: (Handle: 38) +Unknown +64a70011-f691-4b93-a6f4-0968f5b648f8: Unknown + +64a70002-f691-4b93-a6f4-0968f5b648f8: Keep alive. +['notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 42) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 43) + +64a70004-f691-4b93-a6f4-0968f5b648f8: Rst quaternions. +['write'] +00002901-0000-1000-8000-00805f9b34fb: (Handle: 46) + +64a7000a-f691-4b93-a6f4-0968f5b648f8: Raw 9-axis. +['notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 49) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 50) + +64a7000c-f691-4b93-a6f4-0968f5b648f8: Linear Acc. +['notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 53) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 54) + +64a70014-f691-4b93-a6f4-0968f5b648f8: Temperature. +['notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 57) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 58) + +64a70021-f691-4b93-a6f4-0968f5b648f8: Magnetometer calibration. +['write', 'notify'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 61) +00002901-0000-1000-8000-00805f9b34fb: (Handle: 62) +Unknown +0000fe59-0000-1000-8000-00805f9b34fb: Nordic Semiconductor ASA + +8ec90003-f315-4f60-9fb8-838830daea50: +['write', 'indicate'] +00002902-0000-1000-8000-00805f9b34fb: (Handle: 66) +Nordic Semiconductor ASA \ No newline at end of file diff --git a/subscribe_to_notification.py b/subscribe_to_notification.py new file mode 100644 index 0000000..95fdb85 --- /dev/null +++ b/subscribe_to_notification.py @@ -0,0 +1,81 @@ +""" +Notifications +------------- +Example showing how to add notifications to a characteristic and handle the responses. +Updated on 2019-07-03 by hbldh +""" + +import logging +import asyncio +import platform +import struct + +from bleak import BleakClient +from bleak import _logger as logger + +BUTTON = 1 +GYROSCOPE = 2 +ACCELEROMETER = 3 +CHARACTERISTIC_UUIDS = { + ("64a7000d-f691-4b93-a6f4-0968f5b648f8"):BUTTON,#Button + # ("64a7000a-f691-4b93-a6f4-0968f5b648f8"):GYROSCOPE,#9 axis + # ("64a7000c-f691-4b93-a6f4-0968f5b648f8"):ACCELEROMETER#Accel +} # <--- Change to the characteristic you want to enable notifications from. + +device_address = "e3:ae:cd:af:28:e2" + +def decode_button(data): + print(data) + print(struct.unpack('>H', data)) + +def notification_handler(sender, data): + """Simple notification handler which prints the data received.""" + sender = CHARACTERISTIC_UUIDS[sender] + if sender == BUTTON: + print(f"BUTTON PRESSED {data}") + decode_button(data) + elif sender == GYROSCOPE: + print(f"GYRO CHANGED - {data}") + elif sender == ACCELEROMETER: + print(f"ACCEL CHANGED - {data}") + + +async def start_notify(client): + for char in CHARACTERISTIC_UUIDS.keys(): + await client.start_notify(char, notification_handler) + +async def stop_notify(client): + for char in CHARACTERISTIC_UUIDS.keys(): + await client.stop_notify(char) + +async def run(address, loop, debug=False): + if debug: + import sys + + # loop.set_debug(True) + l = logging.getLogger("asyncio") + l.setLevel(logging.DEBUG) + h = logging.StreamHandler(sys.stdout) + h.setLevel(logging.DEBUG) + l.addHandler(h) + logger.addHandler(h) + + async with BleakClient(address, loop=loop) as client: + x = await client.is_connected() + logger.info("Connected: {0}".format(x)) + await start_notify(client) + await asyncio.sleep(60.0, loop=loop) + await stop_notify(client) + + +if __name__ == "__main__": + import os + + os.environ["PYTHONASYNCIODEBUG"] = str(1) + address = ( + device_address # <--- Change to your device's address here if you are using Windows or Linux + if platform.system() != "Darwin" + else "243E23AE-4A99-406C-B317-18F1BD7B4CBE" # <--- Change to your device's address here if you are using macOS + ) + loop = asyncio.get_event_loop() + loop.run_until_complete(run(address, loop, True)) \ No newline at end of file diff --git a/test2.py b/test2.py new file mode 100644 index 0000000..c85ebb6 --- /dev/null +++ b/test2.py @@ -0,0 +1,58 @@ +import asyncio +from bleak import discover + +from pprint import pprint + + +async def run(): + devices = await discover() + for d in devices: + print(d) + +loop = asyncio.get_event_loop() +loop.run_until_complete(run()) + +import asyncio +from bleak import BleakClient + +address = "e3:ae:cd:af:28:e2" +MODEL_NBR_UUID = "64a7000f-f691-4b93-a6f4-0968f5b648f8" + +async def run(address, loop): + async with BleakClient(address, loop=loop) as client: + # await client.connect() + print(await client.is_connected()) + print(await client.get_services()) + services = await client.get_services() + pprint(services.descriptors) + pprint(services.characteristics) + pprint(services.services) + # print(services.descriptors) + # for key, val in services.descriptors.items(): + # print(f"{key} + {val}") + # + # print(services.characteristics) + # for key, val in services.characteristics.items(): + # print(f"{key} + {val}") + + print(services) + for x in services: + print(x) + for characteristic in x.characteristics: + print("") + print(characteristic) + print(characteristic.properties) + for descriptor in characteristic.descriptors: + print(descriptor) + print(x.description) + # for i in range(10): + # x = await client.read_gatt_descriptor(i) + # print(x) + + # model_number = await client.read_gatt_char() + # print(model_number) + # print("Model Number: {0}".format("".join(map(chr, model_number)))) + + +loop = asyncio.get_event_loop() +loop.run_until_complete(run(address, loop)) \ No newline at end of file