| #!/usr/bin/env python3 | |
| import dbus | |
| try: | |
| from gi.repository import GObject | |
| except ImportError: | |
| import gobject as GObject | |
| import sys | |
| from dbus.mainloop.glib import DBusGMainLoop | |
| bus = None | |
| mainloop = None | |
| BLUEZ_SERVICE_NAME = 'org.bluez' | |
| DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' | |
| DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' | |
| GATT_SERVICE_IFACE = 'org.bluez.GattService1' | |
| GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1' | |
| HR_SVC_UUID = '0000180d-0000-1000-8000-00805f9b34fb' | |
| HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb' | |
| BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb' | |
| HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb' | |
| # The objects that we interact with. | |
| hr_service = None | |
| hr_msrmt_chrc = None | |
| body_snsr_loc_chrc = None | |
| hr_ctrl_pt_chrc = None | |
| def generic_error_cb(error): | |
| print('D-Bus call failed: ' + str(error)) | |
| mainloop.quit() | |
| def body_sensor_val_to_str(val): | |
| if val == 0: | |
| return 'Other' | |
| if val == 1: | |
| return 'Chest' | |
| if val == 2: | |
| return 'Wrist' | |
| if val == 3: | |
| return 'Finger' | |
| if val == 4: | |
| return 'Hand' | |
| if val == 5: | |
| return 'Ear Lobe' | |
| if val == 6: | |
| return 'Foot' | |
| return 'Reserved value' | |
| def sensor_contact_val_to_str(val): | |
| if val == 0 or val == 1: | |
| return 'not supported' | |
| if val == 2: | |
| return 'no contact detected' | |
| if val == 3: | |
| return 'contact detected' | |
| return 'invalid value' | |
| def body_sensor_val_cb(value): | |
| if len(value) != 1: | |
| print('Invalid body sensor location value: ' + repr(value)) | |
| return | |
| print('Body sensor location value: ' + body_sensor_val_to_str(value[0])) | |
| def hr_msrmt_start_notify_cb(): | |
| print('HR Measurement notifications enabled') | |
| def hr_msrmt_changed_cb(iface, changed_props, invalidated_props): | |
| if iface != GATT_CHRC_IFACE: | |
| return | |
| if not len(changed_props): | |
| return | |
| value = changed_props.get('Value', None) | |
| if not value: | |
| return | |
| print('New HR Measurement') | |
| flags = value[0] | |
| value_format = flags & 0x01 | |
| sc_status = (flags >> 1) & 0x03 | |
| ee_status = flags & 0x08 | |
| if value_format == 0x00: | |
| hr_msrmt = value[1] | |
| next_ind = 2 | |
| else: | |
| hr_msrmt = value[1] | (value[2] << 8) | |
| next_ind = 3 | |
| print('\tHR: ' + str(int(hr_msrmt))) | |
| print('\tSensor Contact status: ' + | |
| sensor_contact_val_to_str(sc_status)) | |
| if ee_status: | |
| print('\tEnergy Expended: ' + str(int(value[next_ind]))) | |
| def start_client(): | |
| # Read the Body Sensor Location value and print it asynchronously. | |
| body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb, | |
| error_handler=generic_error_cb, | |
| dbus_interface=GATT_CHRC_IFACE) | |
| # Listen to PropertiesChanged signals from the Heart Measurement | |
| # Characteristic. | |
| hr_msrmt_prop_iface = dbus.Interface(hr_msrmt_chrc[0], DBUS_PROP_IFACE) | |
| hr_msrmt_prop_iface.connect_to_signal("PropertiesChanged", | |
| hr_msrmt_changed_cb) | |
| # Subscribe to Heart Rate Measurement notifications. | |
| hr_msrmt_chrc[0].StartNotify(reply_handler=hr_msrmt_start_notify_cb, | |
| error_handler=generic_error_cb, | |
| dbus_interface=GATT_CHRC_IFACE) | |
| def process_chrc(chrc_path): | |
| chrc = bus.get_object(BLUEZ_SERVICE_NAME, chrc_path) | |
| chrc_props = chrc.GetAll(GATT_CHRC_IFACE, | |
| dbus_interface=DBUS_PROP_IFACE) | |
| uuid = chrc_props['UUID'] | |
| if uuid == HR_MSRMT_UUID: | |
| global hr_msrmt_chrc | |
| hr_msrmt_chrc = (chrc, chrc_props) | |
| elif uuid == BODY_SNSR_LOC_UUID: | |
| global body_snsr_loc_chrc | |
| body_snsr_loc_chrc = (chrc, chrc_props) | |
| elif uuid == HR_CTRL_PT_UUID: | |
| global hr_ctrl_pt_chrc | |
| hr_ctrl_pt_chrc = (chrc, chrc_props) | |
| else: | |
| print('Unrecognized characteristic: ' + uuid) | |
| return True | |
| def process_hr_service(service_path, chrc_paths): | |
| service = bus.get_object(BLUEZ_SERVICE_NAME, service_path) | |
| service_props = service.GetAll(GATT_SERVICE_IFACE, | |
| dbus_interface=DBUS_PROP_IFACE) | |
| uuid = service_props['UUID'] | |
| if uuid != HR_SVC_UUID: | |
| return False | |
| print('Heart Rate Service found: ' + service_path) | |
| # Process the characteristics. | |
| for chrc_path in chrc_paths: | |
| process_chrc(chrc_path) | |
| global hr_service | |
| hr_service = (service, service_props, service_path) | |
| return True | |
| def interfaces_removed_cb(object_path, interfaces): | |
| if not hr_service: | |
| return | |
| if object_path == hr_service[2]: | |
| print('Service was removed') | |
| mainloop.quit() | |
| def main(): | |
| # Set up the main loop. | |
| DBusGMainLoop(set_as_default=True) | |
| global bus | |
| bus = dbus.SystemBus() | |
| global mainloop | |
| mainloop = GObject.MainLoop() | |
| om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE) | |
| om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb) | |
| print('Getting objects...') | |
| objects = om.GetManagedObjects() | |
| chrcs = [] | |
| # List characteristics found | |
| for path, interfaces in objects.items(): | |
| if GATT_CHRC_IFACE not in interfaces.keys(): | |
| continue | |
| chrcs.append(path) | |
| # List sevices found | |
| for path, interfaces in objects.items(): | |
| if GATT_SERVICE_IFACE not in interfaces.keys(): | |
| continue | |
| chrc_paths = [d for d in chrcs if d.startswith(path + "/")] | |
| if process_hr_service(path, chrc_paths): | |
| break | |
| if not hr_service: | |
| print('No Heart Rate Service found') | |
| sys.exit(1) | |
| start_client() | |
| mainloop.run() | |
| if __name__ == '__main__': | |
| main() |