mirror of https://github.com/labapart/gattlib
Initial Python support
parent
a1ad48f20c
commit
e74543fa56
|
@ -44,6 +44,8 @@ script:
|
||||||
#
|
#
|
||||||
# Setup build for ARM 32-bit
|
# Setup build for ARM 32-bit
|
||||||
#
|
#
|
||||||
|
# Ensure folder is empty
|
||||||
|
- rm -Rf $PWD/debian-stable-arm/*
|
||||||
- cross-sysroot --distribution debian --distribution-version stable --architecture armhf --build-root $PWD/debian-stable-arm requirements.dep
|
- cross-sysroot --distribution debian --distribution-version stable --architecture armhf --build-root $PWD/debian-stable-arm requirements.dep
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
|
@ -58,6 +60,8 @@ script:
|
||||||
#
|
#
|
||||||
# Setup build for ARM 64-bit
|
# Setup build for ARM 64-bit
|
||||||
#
|
#
|
||||||
|
# Ensure folder is empty
|
||||||
|
- rm -Rf $PWD/debian-stable-arm64/*
|
||||||
- cross-sysroot --distribution debian --distribution-version stable --architecture arm64 --build-root $PWD/debian-stable-arm64 requirements.dep
|
- cross-sysroot --distribution debian --distribution-version stable --architecture arm64 --build-root $PWD/debian-stable-arm64 requirements.dep
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
|
|
|
@ -1,13 +1,95 @@
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
#include <Python.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "gattlib_internal.h"
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
void gattlib_register_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data) {
|
void gattlib_register_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data) {
|
||||||
connection->notification_handler = notification_handler;
|
connection->notification.type = NATIVE_NOTIFICATION;
|
||||||
connection->notification_user_data = user_data;
|
connection->notification.notification_handler = notification_handler;
|
||||||
|
connection->notification.user_data = user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gattlib_register_indication(gatt_connection_t* connection, gattlib_event_handler_t indication_handler, void* user_data) {
|
void gattlib_register_indication(gatt_connection_t* connection, gattlib_event_handler_t indication_handler, void* user_data) {
|
||||||
connection->indication_handler = indication_handler;
|
connection->indication.type = NATIVE_NOTIFICATION;
|
||||||
connection->indication_user_data = user_data;
|
connection->indication.notification_handler = indication_handler;
|
||||||
|
connection->indication.user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_register_on_disconnect(gatt_connection_t *connection, gattlib_disconnection_handler_t handler, void* user_data) {
|
||||||
|
connection->disconnection.type = NATIVE_DISCONNECTION;
|
||||||
|
connection->disconnection.disconnection_handler = handler;
|
||||||
|
connection->disconnection.user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
void gattlib_register_notification_python(gatt_connection_t* connection, PyObject *notification_handler, PyObject *user_data) {
|
||||||
|
connection->notification.type = PYTHON;
|
||||||
|
connection->notification.python_handler = notification_handler;
|
||||||
|
connection->notification.user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_register_indication_python(gatt_connection_t* connection, PyObject *indication_handler, PyObject *user_data) {
|
||||||
|
connection->indication.type = PYTHON;
|
||||||
|
connection->indication.python_handler = indication_handler;
|
||||||
|
connection->indication.user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_register_on_disconnect_python(gatt_connection_t *connection, PyObject *handler, PyObject *user_data) {
|
||||||
|
connection->disconnection.type = PYTHON;
|
||||||
|
connection->disconnection.python_handler = handler;
|
||||||
|
connection->disconnection.user_data = user_data;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool gattlib_has_valid_handler(struct gattlib_handler *handler) {
|
||||||
|
return ((handler->type != UNKNOWN) && (handler->notification_handler != NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_call_notification_handler(struct gattlib_handler *handler, const uuid_t* uuid, const uint8_t* data, size_t data_length) {
|
||||||
|
if (handler->type == NATIVE_NOTIFICATION) {
|
||||||
|
handler->notification_handler(uuid, data, data_length, handler->user_data);
|
||||||
|
}
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
else if (handler->type == PYTHON) {
|
||||||
|
char uuid_str[MAX_LEN_UUID_STR + 1];
|
||||||
|
PyGILState_STATE d_gstate;
|
||||||
|
|
||||||
|
gattlib_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
|
||||||
|
|
||||||
|
d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
PyObject *arglist = Py_BuildValue("(sIIO)", uuid_str, data, data_length, handler->user_data);
|
||||||
|
PyEval_CallObject((PyObject *)handler->notification_handler, arglist);
|
||||||
|
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Invalid notification handler.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_call_disconnection_handler(struct gattlib_handler *handler) {
|
||||||
|
if (handler->type == NATIVE_NOTIFICATION) {
|
||||||
|
handler->disconnection_handler(handler->user_data);
|
||||||
|
}
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
else if (handler->type == PYTHON) {
|
||||||
|
PyGILState_STATE d_gstate;
|
||||||
|
d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
PyObject *arglist = Py_BuildValue("(O)", handler->user_data);
|
||||||
|
PyEval_CallObject((PyObject *)handler->disconnection_handler, arglist);
|
||||||
|
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Invalid disconnection handler.\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bt_uuid_to_uuid(bt_uuid_t* bt_uuid, uuid_t* uuid) {
|
void bt_uuid_to_uuid(bt_uuid_t* bt_uuid, uuid_t* uuid) {
|
||||||
|
|
|
@ -1,19 +1,32 @@
|
||||||
#ifndef __GATTLIB_INTERNAL_DEFS_H__
|
#ifndef __GATTLIB_INTERNAL_DEFS_H__
|
||||||
#define __GATTLIB_INTERNAL_DEFS_H__
|
#define __GATTLIB_INTERNAL_DEFS_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "gattlib.h"
|
#include "gattlib.h"
|
||||||
|
|
||||||
|
enum handler_type { UNKNOWN = 0, NATIVE_NOTIFICATION, NATIVE_DISCONNECTION, PYTHON };
|
||||||
|
|
||||||
|
struct gattlib_handler {
|
||||||
|
enum handler_type type;
|
||||||
|
union {
|
||||||
|
gattlib_event_handler_t notification_handler;
|
||||||
|
gattlib_disconnection_handler_t disconnection_handler;
|
||||||
|
void* python_handler;
|
||||||
|
};
|
||||||
|
void* user_data;
|
||||||
|
};
|
||||||
|
|
||||||
struct _gatt_connection_t {
|
struct _gatt_connection_t {
|
||||||
void* context;
|
void* context;
|
||||||
|
|
||||||
gattlib_event_handler_t notification_handler;
|
struct gattlib_handler notification;
|
||||||
void* notification_user_data;
|
struct gattlib_handler indication;
|
||||||
|
struct gattlib_handler disconnection;
|
||||||
gattlib_event_handler_t indication_handler;
|
|
||||||
void* indication_user_data;
|
|
||||||
|
|
||||||
gattlib_disconnection_handler_t disconnection_handler;
|
|
||||||
void* disconnection_user_data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool gattlib_has_valid_handler(struct gattlib_handler *handler);
|
||||||
|
void gattlib_call_disconnection_handler(struct gattlib_handler *handler);
|
||||||
|
void gattlib_call_notification_handler(struct gattlib_handler *handler, const uuid_t* uuid, const uint8_t* data, size_t data_length);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -93,6 +93,21 @@ endif()
|
||||||
|
|
||||||
set(gattlib_LIBS ${GLIB_LDFLAGS} ${GIO_UNIX_LDFLAGS})
|
set(gattlib_LIBS ${GLIB_LDFLAGS} ${GIO_UNIX_LDFLAGS})
|
||||||
|
|
||||||
|
#
|
||||||
|
# Add Python Support
|
||||||
|
#
|
||||||
|
pkg_search_module(PYTHON python3)
|
||||||
|
if (NOT PYTHON_FOUND)
|
||||||
|
pkg_search_module(PYTHON python)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (PYTHON_FOUND)
|
||||||
|
include_directories(${PYTHON_INCLUDE_DIRS})
|
||||||
|
list(APPEND gattlib_LIBS ${PYTHON_LDFLAGS})
|
||||||
|
|
||||||
|
add_definitions(-DWITH_PYTHON)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Gattlib
|
# Gattlib
|
||||||
add_library(gattlib SHARED ${gattlib_SRCS})
|
add_library(gattlib SHARED ${gattlib_SRCS})
|
||||||
target_link_libraries(gattlib ${gattlib_LIBS})
|
target_link_libraries(gattlib ${gattlib_LIBS})
|
||||||
|
|
|
@ -280,11 +280,9 @@ gboolean on_handle_device_property_change(
|
||||||
while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
|
while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
|
||||||
if (strcmp(key, "Connected") == 0) {
|
if (strcmp(key, "Connected") == 0) {
|
||||||
if (!g_variant_get_boolean(value)) {
|
if (!g_variant_get_boolean(value)) {
|
||||||
printf("on_handle_device_property_change: Connected FALSE\n");
|
|
||||||
// Disconnection case
|
// Disconnection case
|
||||||
if (connection->disconnection_handler) {
|
if (gattlib_has_valid_handler(&connection->disconnection)) {
|
||||||
printf("[C] Call disconnection handler:%p\n", connection->disconnection_handler);
|
gattlib_call_disconnection_handler(&connection->disconnection);
|
||||||
connection->disconnection_handler(connection->disconnection_user_data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (strcmp(key, "ServicesResolved") == 0) {
|
} else if (strcmp(key, "ServicesResolved") == 0) {
|
||||||
|
@ -447,11 +445,6 @@ int gattlib_disconnect(gatt_connection_t* connection) {
|
||||||
return GATTLIB_SUCCESS;
|
return GATTLIB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gattlib_register_on_disconnect(gatt_connection_t *connection, gattlib_disconnection_handler_t handler, void* user_data) {
|
|
||||||
connection->disconnection_handler = handler;
|
|
||||||
connection->disconnection_user_data = user_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bluez was using org.bluez.Device1.GattServices until 5.37 to expose the list of available GATT Services
|
// Bluez was using org.bluez.Device1.GattServices until 5.37 to expose the list of available GATT Services
|
||||||
#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 38)
|
#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 38)
|
||||||
int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count) {
|
int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count) {
|
||||||
|
@ -1417,7 +1410,7 @@ gboolean on_handle_battery_level_property_change(
|
||||||
static guint8 percentage;
|
static guint8 percentage;
|
||||||
gatt_connection_t* connection = user_data;
|
gatt_connection_t* connection = user_data;
|
||||||
|
|
||||||
if (connection->notification_handler) {
|
if (gattlib_has_valid_handler(&connection->notification)) {
|
||||||
// Retrieve 'Value' from 'arg_changed_properties'
|
// Retrieve 'Value' from 'arg_changed_properties'
|
||||||
if (g_variant_n_children (arg_changed_properties) > 0) {
|
if (g_variant_n_children (arg_changed_properties) > 0) {
|
||||||
GVariantIter *iter;
|
GVariantIter *iter;
|
||||||
|
@ -1431,9 +1424,9 @@ gboolean on_handle_battery_level_property_change(
|
||||||
// GATT connection notifiying to Battery level
|
// GATT connection notifiying to Battery level
|
||||||
percentage = g_variant_get_byte(value);
|
percentage = g_variant_get_byte(value);
|
||||||
|
|
||||||
connection->notification_handler(&m_battery_level_uuid,
|
gattlib_call_notification_handler(&connection->notification,
|
||||||
(const uint8_t*)&percentage, sizeof(percentage),
|
&m_battery_level_uuid,
|
||||||
connection->notification_user_data);
|
(const uint8_t*)&percentage, sizeof(percentage));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1451,7 +1444,7 @@ static gboolean on_handle_characteristic_property_change(
|
||||||
{
|
{
|
||||||
gatt_connection_t* connection = user_data;
|
gatt_connection_t* connection = user_data;
|
||||||
|
|
||||||
if (connection->notification_handler) {
|
if (gattlib_has_valid_handler(&connection->notification)) {
|
||||||
// Retrieve 'Value' from 'arg_changed_properties'
|
// Retrieve 'Value' from 'arg_changed_properties'
|
||||||
if (g_variant_n_children (arg_changed_properties) > 0) {
|
if (g_variant_n_children (arg_changed_properties) > 0) {
|
||||||
GVariantIter *iter;
|
GVariantIter *iter;
|
||||||
|
@ -1470,7 +1463,8 @@ static gboolean on_handle_characteristic_property_change(
|
||||||
MAX_LEN_UUID_STR + 1,
|
MAX_LEN_UUID_STR + 1,
|
||||||
&uuid);
|
&uuid);
|
||||||
|
|
||||||
connection->notification_handler(&uuid, data, data_length, connection->notification_user_data);
|
gattlib_call_notification_handler(&connection->notification,
|
||||||
|
&uuid, data, data_length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# export LD_LIBRARY_PATH=/home/olivier/dev/gattlib/build/dbus/:$LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
from gattlib import adapter
|
||||||
|
|
||||||
|
adapters = adapter.Adapter.list()
|
||||||
|
print("BLE Adapters: %s" % adapters)
|
||||||
|
|
||||||
|
|
||||||
|
def on_discovered_device(device):
|
||||||
|
print("Discovered '%s'" % device)
|
||||||
|
# device.connect()
|
||||||
|
# device.discover()
|
||||||
|
|
||||||
|
|
||||||
|
default_adapter = adapter.Adapter()
|
||||||
|
|
||||||
|
default_adapter.open()
|
||||||
|
default_adapter.scan_enable(on_discovered_device, 10)
|
|
@ -0,0 +1,4 @@
|
||||||
|
- Add advertisement data
|
||||||
|
|
||||||
|
- Add C function to list BLE adapters
|
||||||
|
- Add indication support
|
|
@ -0,0 +1,193 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from dbus.mainloop.glib import DBusGMainLoop
|
||||||
|
try:
|
||||||
|
from gi.repository import GLib, GObject
|
||||||
|
except ImportError:
|
||||||
|
import gobject as GObject
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
from matplotlib.pylab import *
|
||||||
|
from mpl_toolkits.axes_grid1 import host_subplot
|
||||||
|
import matplotlib.animation as animation
|
||||||
|
|
||||||
|
from gattlib import device, uuid
|
||||||
|
|
||||||
|
last_measures = {
|
||||||
|
'temperature': { 'value': None, 'min': None, 'max': None },
|
||||||
|
'pressure': { 'value': None, 'min': None, 'max': None },
|
||||||
|
'humidity': { 'value': None, 'min': None, 'max': None },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def temperature_notification(value, user_data):
|
||||||
|
last_measures['temperature']['value'] = float("%d.%d" % (value[0], value[1]))
|
||||||
|
print("Temperature: %f" % last_measures['temperature']['value'])
|
||||||
|
|
||||||
|
|
||||||
|
def pressure_notification(value, user_data):
|
||||||
|
(pressure_integer, pressure_decimal) = struct.unpack("<IB", value)
|
||||||
|
last_measures['pressure']['value'] = float("%d.%d" % (pressure_integer, pressure_decimal))
|
||||||
|
print("Pressure: %f" % last_measures['pressure']['value'])
|
||||||
|
|
||||||
|
|
||||||
|
def humidity_notification(value, user_data):
|
||||||
|
last_measures['humidity']['value'] = value[0]
|
||||||
|
print("Humidity: %d%%" % last_measures['humidity']['value'])
|
||||||
|
|
||||||
|
|
||||||
|
# Data Placeholders
|
||||||
|
temperature = zeros(0)
|
||||||
|
humidity = zeros(0)
|
||||||
|
t = zeros(0)
|
||||||
|
x = 0.0
|
||||||
|
xmax = 1000.0
|
||||||
|
temp_line = None
|
||||||
|
hum_line = None
|
||||||
|
ax_temp = None
|
||||||
|
ax_hum = None
|
||||||
|
simulation = None
|
||||||
|
|
||||||
|
|
||||||
|
def graph_init():
|
||||||
|
global x
|
||||||
|
global temperature, humidity, t
|
||||||
|
global temp_line, hum_line
|
||||||
|
global ax_temp, ax_hum
|
||||||
|
global simulation
|
||||||
|
|
||||||
|
font = {'size' : 9}
|
||||||
|
matplotlib.rc('font', **font)
|
||||||
|
|
||||||
|
# Setup figure and subplots
|
||||||
|
f0 = figure(num=0, figsize=(12, 8)) # , dpi = 100)
|
||||||
|
f0.suptitle("Nordic Thingy", fontsize=12)
|
||||||
|
ax_temp = host_subplot(111)
|
||||||
|
ax_hum = ax_temp.twinx()
|
||||||
|
|
||||||
|
# Set titles of subplots
|
||||||
|
ax_temp.set_title('Temperature/Humidity vs Time')
|
||||||
|
|
||||||
|
# set y-limits
|
||||||
|
ax_temp.set_ylim(0, 45)
|
||||||
|
ax_hum.set_ylim(0, 100)
|
||||||
|
|
||||||
|
# sex x-limits
|
||||||
|
ax_temp.set_xlim(0, xmax)
|
||||||
|
ax_hum.set_xlim(0, xmax)
|
||||||
|
|
||||||
|
# Turn on grids
|
||||||
|
ax_temp.grid(True)
|
||||||
|
|
||||||
|
# set label names
|
||||||
|
ax_temp.set_xlabel("t")
|
||||||
|
ax_temp.set_ylabel("temperature")
|
||||||
|
ax_hum.set_ylabel("humidity")
|
||||||
|
|
||||||
|
temp_line, = ax_temp.plot(t, temperature, 'b-', label="temperature")
|
||||||
|
hum_line, = ax_hum.plot(t, humidity, 'g-', label="humidity")
|
||||||
|
|
||||||
|
# set lagends
|
||||||
|
ax_temp.legend([temp_line, hum_line], [temp_line.get_label(), hum_line.get_label()])
|
||||||
|
|
||||||
|
# interval: draw new frame every 'interval' ms
|
||||||
|
# Note: We expose simulation to prevent Python garbage collector to rmeove it!
|
||||||
|
simulation = animation.FuncAnimation(f0, graph_update, blit=False, interval=20)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def graph_update(self):
|
||||||
|
global x, xmax
|
||||||
|
global temperature, humidity, t
|
||||||
|
global temp_line, hum_line
|
||||||
|
|
||||||
|
if last_measures['temperature']['value']:
|
||||||
|
temperature = append(temperature, last_measures['temperature']['value'])
|
||||||
|
if last_measures['temperature']['min']:
|
||||||
|
last_measures['temperature']['min'] = min(last_measures['temperature']['min'], last_measures['temperature']['value'] - 5)
|
||||||
|
else:
|
||||||
|
last_measures['temperature']['min'] = last_measures['temperature']['value'] - 5
|
||||||
|
if last_measures['temperature']['max']:
|
||||||
|
last_measures['temperature']['max'] = max(last_measures['temperature']['max'], last_measures['temperature']['value'] + 5)
|
||||||
|
else:
|
||||||
|
last_measures['temperature']['max'] = last_measures['temperature']['value'] + 5
|
||||||
|
|
||||||
|
ax_temp.set_ylim(last_measures['temperature']['min'], last_measures['temperature']['max'])
|
||||||
|
|
||||||
|
if last_measures['humidity']['value']:
|
||||||
|
humidity = append(humidity, last_measures['humidity']['value'])
|
||||||
|
if last_measures['humidity']['min']:
|
||||||
|
last_measures['humidity']['min'] = min(last_measures['humidity']['min'], last_measures['humidity']['value'] - 20)
|
||||||
|
else:
|
||||||
|
last_measures['humidity']['min'] = last_measures['humidity']['value'] - 20
|
||||||
|
if last_measures['humidity']['max']:
|
||||||
|
last_measures['humidity']['max'] = max(last_measures['humidity']['max'], last_measures['humidity']['value'] + 20)
|
||||||
|
else:
|
||||||
|
last_measures['humidity']['max'] = last_measures['humidity']['value'] + 20
|
||||||
|
|
||||||
|
ax_hum.set_ylim(last_measures['humidity']['min'], last_measures['humidity']['max'])
|
||||||
|
|
||||||
|
t = append(t, x)
|
||||||
|
|
||||||
|
x += 0.05
|
||||||
|
|
||||||
|
temp_line.set_data(t, temperature)
|
||||||
|
hum_line.set_data(t, humidity)
|
||||||
|
|
||||||
|
if x >= xmax - 1.00:
|
||||||
|
temp_line.axes.set_xlim(x - xmax + 1.0, x + 1.0)
|
||||||
|
hum_line.axes.set_xlim(x - xmax + 1.0, x + 1.0)
|
||||||
|
|
||||||
|
return temp_line, hum_line
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Gattlib example for Nordic Thingy')
|
||||||
|
parser.add_argument('mac', type=str, help='Mac Address of the GATT device to connect')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
NORDIC_THINGY_WEATHER_STATION_SERVICE = uuid.gattlib_uuid_str_to_int("EF680200-9B35-4933-9B10-52FFA9740042")
|
||||||
|
NORDIC_THINGY_TEMPERATURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680201-9B35-4933-9B10-52FFA9740042")
|
||||||
|
NORDIC_THINGY_PRESSURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680202-9B35-4933-9B10-52FFA9740042")
|
||||||
|
NORDIC_THINGY_HUMIDITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680203-9B35-4933-9B10-52FFA9740042")
|
||||||
|
NORDIC_THINGY_AIR_QUALITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680204-9B35-4933-9B10-52FFA9740042")
|
||||||
|
|
||||||
|
gatt_device = device.Device(adapter=None, addr=args.mac)
|
||||||
|
gatt_device.connect()
|
||||||
|
|
||||||
|
temperature_characteristic = gatt_device.characteristics[NORDIC_THINGY_TEMPERATURE_CHAR]
|
||||||
|
pressure_characteristic = gatt_device.characteristics[NORDIC_THINGY_PRESSURE_CHAR]
|
||||||
|
humidity_characteristic = gatt_device.characteristics[NORDIC_THINGY_HUMIDITY_CHAR]
|
||||||
|
air_quality_characteristic = gatt_device.characteristics[NORDIC_THINGY_AIR_QUALITY_CHAR]
|
||||||
|
|
||||||
|
# Initialize graph
|
||||||
|
threading.Thread(target=graph_init).start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
DBusGMainLoop(set_as_default=True)
|
||||||
|
mainloop = GLib.MainLoop()
|
||||||
|
|
||||||
|
temperature_characteristic.register_notification(temperature_notification)
|
||||||
|
temperature_characteristic.notification_start()
|
||||||
|
|
||||||
|
pressure_characteristic.register_notification(pressure_notification)
|
||||||
|
pressure_characteristic.notification_start()
|
||||||
|
|
||||||
|
humidity_characteristic.register_notification(humidity_notification)
|
||||||
|
humidity_characteristic.notification_start()
|
||||||
|
|
||||||
|
mainloop.run()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
mainloop.quit()
|
||||||
|
finally:
|
||||||
|
humidity_characteristic.notification_stop()
|
||||||
|
pressure_characteristic.notification_stop()
|
||||||
|
temperature_characteristic.notification_stop()
|
||||||
|
gatt_device.disconnect()
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from gattlib import device, uuid
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Gattlib read_write example')
|
||||||
|
parser.add_argument('mac', type=str, help='Mac Address of the GATT device to connect')
|
||||||
|
parser.add_argument('action', choices=['read', 'write'], help='Tell if we want to read/write the GATT characteristic')
|
||||||
|
parser.add_argument('uuid', type=str, help='UUID of the GATT Characteristic')
|
||||||
|
parser.add_argument('value', type=str, nargs='?', help='Value to write to the GATT characteristic')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
gatt_device = device.Device(adapter=None, addr=args.mac)
|
||||||
|
gatt_device.connect()
|
||||||
|
|
||||||
|
uuid = uuid.gattlib_uuid_str_to_int(args.uuid)
|
||||||
|
if uuid not in gatt_device.characteristics:
|
||||||
|
raise RuntimeError("Failed to find GATT characteristic '%s'" % args.uuid)
|
||||||
|
|
||||||
|
characteristic = gatt_device.characteristics[uuid]
|
||||||
|
|
||||||
|
if args.action == "read":
|
||||||
|
value = characteristic.read()
|
||||||
|
print(value)
|
||||||
|
elif args.action == "write":
|
||||||
|
characteristic.write(value)
|
||||||
|
|
||||||
|
gatt_device.disconnect()
|
|
@ -0,0 +1,90 @@
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
gattlib = CDLL("libgattlib.so")
|
||||||
|
|
||||||
|
|
||||||
|
# typedef struct {
|
||||||
|
# uint8_t data[16];
|
||||||
|
# } uint128_t;
|
||||||
|
class GattlibUuid128(Structure):
|
||||||
|
_fields_ = [("data", c_byte * 16)]
|
||||||
|
|
||||||
|
|
||||||
|
# typedef struct {
|
||||||
|
# uint8_t type;
|
||||||
|
# union {
|
||||||
|
# uint16_t uuid16;
|
||||||
|
# uint32_t uuid32;
|
||||||
|
# uint128_t uuid128;
|
||||||
|
# } value;
|
||||||
|
# } uuid_t;
|
||||||
|
class GattlibUuidValue(Union):
|
||||||
|
_fields_ = [("uuid16", c_ushort), ("uuid32", c_uint), ("uuid128", GattlibUuid128)]
|
||||||
|
|
||||||
|
|
||||||
|
class GattlibUuid(Structure):
|
||||||
|
_fields_ = [("type", c_byte), ("value", GattlibUuidValue)]
|
||||||
|
|
||||||
|
|
||||||
|
# typedef struct {
|
||||||
|
# uint16_t attr_handle_start;
|
||||||
|
# uint16_t attr_handle_end;
|
||||||
|
# uuid_t uuid;
|
||||||
|
# } gattlib_primary_service_t;
|
||||||
|
class GattlibPrimaryService(Structure):
|
||||||
|
_fields_ = [("attr_handle_start", c_ushort),
|
||||||
|
("attr_handle_end", c_ushort),
|
||||||
|
("uuid", GattlibUuid)]
|
||||||
|
|
||||||
|
|
||||||
|
# typedef struct {
|
||||||
|
# uint16_t handle;
|
||||||
|
# uint8_t properties;
|
||||||
|
# uint16_t value_handle;
|
||||||
|
# uuid_t uuid;
|
||||||
|
# } gattlib_characteristic_t;
|
||||||
|
class GattlibCharacteristic(Structure):
|
||||||
|
_fields_ = [("handle", c_ushort),
|
||||||
|
("properties", c_byte),
|
||||||
|
("value_handle", c_ushort),
|
||||||
|
("uuid", GattlibUuid)]
|
||||||
|
|
||||||
|
|
||||||
|
# int gattlib_adapter_open(const char* adapter_name, void** adapter);
|
||||||
|
gattlib_adapter_open = gattlib.gattlib_adapter_open
|
||||||
|
gattlib_adapter_open.argtypes = [c_char_p, POINTER(c_void_p)]
|
||||||
|
|
||||||
|
# typedef void (*gattlib_discovered_device_t)(const char* addr, const char* name)
|
||||||
|
gattlib_discovered_device_type = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||||
|
|
||||||
|
# int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count);
|
||||||
|
gattlib_discover_primary = gattlib.gattlib_discover_primary
|
||||||
|
gattlib_discover_primary.argtypes = [c_void_p, POINTER(POINTER(GattlibPrimaryService)), POINTER(c_int)]
|
||||||
|
|
||||||
|
# int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristic_count);
|
||||||
|
gattlib_discover_char = gattlib.gattlib_discover_char
|
||||||
|
gattlib_discover_char.argtypes = [c_void_p, POINTER(POINTER(GattlibCharacteristic)), POINTER(c_int)]
|
||||||
|
|
||||||
|
# int gattlib_read_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, void** buffer, size_t* buffer_len);
|
||||||
|
gattlib_read_char_by_uuid = gattlib.gattlib_read_char_by_uuid
|
||||||
|
gattlib_read_char_by_uuid.argtypes = [c_void_p, POINTER(GattlibUuid), POINTER(c_void_p), POINTER(c_size_t)]
|
||||||
|
|
||||||
|
# int gattlib_write_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len)
|
||||||
|
gattlib_write_char_by_uuid = gattlib.gattlib_write_char_by_uuid
|
||||||
|
gattlib_write_char_by_uuid.argtypes = [c_void_p, POINTER(GattlibUuid), c_void_p, c_size_t]
|
||||||
|
|
||||||
|
# int gattlib_notification_start(gatt_connection_t* connection, const uuid_t* uuid);
|
||||||
|
gattlib_notification_start = gattlib.gattlib_notification_start
|
||||||
|
gattlib_notification_start.argtypes = [c_void_p, POINTER(GattlibUuid)]
|
||||||
|
|
||||||
|
# int gattlib_notification_stop(gatt_connection_t* connection, const uuid_t* uuid);
|
||||||
|
gattlib_notification_stop = gattlib.gattlib_notification_stop
|
||||||
|
gattlib_notification_stop.argtypes = [c_void_p, POINTER(GattlibUuid)]
|
||||||
|
|
||||||
|
# void gattlib_register_notification_python(gatt_connection_t* connection, PyObject *notification_handler, PyObject *user_data)
|
||||||
|
gattlib_register_notification = gattlib.gattlib_register_notification_python
|
||||||
|
gattlib_register_notification.argtypes = [c_void_p, py_object, py_object]
|
||||||
|
|
||||||
|
# void gattlib_register_on_disconnect_python(gatt_connection_t *connection, PyObject *handler, PyObject *user_data)
|
||||||
|
gattlib_register_on_disconnect = gattlib.gattlib_register_on_disconnect_python
|
||||||
|
gattlib_register_on_disconnect.argtypes = [c_void_p, py_object, py_object]
|
|
@ -0,0 +1,38 @@
|
||||||
|
from gattlib import *
|
||||||
|
from .device import Device
|
||||||
|
from .exception import handle_return
|
||||||
|
|
||||||
|
class Adapter:
|
||||||
|
def __init__(self, name=c_char_p(None)):
|
||||||
|
self._name = name
|
||||||
|
self._adapter = c_void_p(None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list():
|
||||||
|
#TODO: Add support
|
||||||
|
return []
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
return gattlib_adapter_open(self._name, byref(self._adapter))
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return gattlib.gattlib_adapter_close(self._adapter)
|
||||||
|
|
||||||
|
def on_discovered_device(self, addr, name):
|
||||||
|
device = Device(self, addr, name)
|
||||||
|
self.on_discovered_device_callback(device)
|
||||||
|
|
||||||
|
def scan_enable(self, on_discovered_device_callback, timeout):
|
||||||
|
assert on_discovered_device_callback != None
|
||||||
|
self.on_discovered_device_callback = on_discovered_device_callback
|
||||||
|
|
||||||
|
ret = gattlib.gattlib_adapter_scan_enable(self._adapter, gattlib_discovered_device_type(self.on_discovered_device), timeout)
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def scan_disable(self):
|
||||||
|
ret = gattlib.gattlib_adapter_scan_disable(self._adapter)
|
||||||
|
handle_return(ret)
|
|
@ -0,0 +1,164 @@
|
||||||
|
import logging
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from gattlib import *
|
||||||
|
from .exception import handle_return, DeviceError
|
||||||
|
from .gatt import GattService, GattCharacteristic
|
||||||
|
from .uuid import gattlib_uuid_to_int
|
||||||
|
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_PUBLIC = (1 << 0)
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM = (1 << 1)
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW = (1 << 2)
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BT_SEC_MEDIUM = (1 << 3)
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BT_SEC_HIGH = (1 << 4)
|
||||||
|
|
||||||
|
CONNECTION_OPTIONS_LEGACY_DEFAULT = \
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_PUBLIC | \
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM | \
|
||||||
|
CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW
|
||||||
|
|
||||||
|
|
||||||
|
class Device:
|
||||||
|
|
||||||
|
def __init__(self, adapter, addr, name=None):
|
||||||
|
self._adapter = adapter
|
||||||
|
if type(addr) == str:
|
||||||
|
self._addr = addr.encode("utf-8")
|
||||||
|
else:
|
||||||
|
self._addr = addr
|
||||||
|
self._name = name
|
||||||
|
self._connection = c_void_p(None)
|
||||||
|
|
||||||
|
# Keep track if notification handler has been initialized
|
||||||
|
self._is_notification_init = False
|
||||||
|
|
||||||
|
# Dictionnary for GATT characteristic callback
|
||||||
|
self._gatt_characteristic_callbacks = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._addr.decode("utf-8")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connection(self):
|
||||||
|
return self._connection
|
||||||
|
|
||||||
|
def connect(self, options=CONNECTION_OPTIONS_LEGACY_DEFAULT):
|
||||||
|
if self._adapter:
|
||||||
|
adapter_name = self._adapter.name
|
||||||
|
else:
|
||||||
|
adapter_name = None
|
||||||
|
|
||||||
|
self._connection = gattlib.gattlib_connect(adapter_name, self._addr, options)
|
||||||
|
if self._connection == 0:
|
||||||
|
raise DeviceError()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def disconnection_callback(user_data):
|
||||||
|
this = user_data
|
||||||
|
|
||||||
|
this.disconnection_callback(this.disconnection_user_data)
|
||||||
|
|
||||||
|
def register_on_disconnect(self, callback, user_data):
|
||||||
|
self.disconnection_callback = callback
|
||||||
|
self.disconnection_user_data = user_data
|
||||||
|
|
||||||
|
gattlib.gattlib_register_on_disconnect(self._connection, Device.disconnection_callback, self)
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
ret = gattlib.gattlib_disconnect(self._connection)
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def discover(self):
|
||||||
|
#
|
||||||
|
# Discover GATT Services
|
||||||
|
#
|
||||||
|
_services = POINTER(GattlibPrimaryService)()
|
||||||
|
_services_count = c_int(0)
|
||||||
|
ret = gattlib_discover_primary(self._connection, byref(_services), byref(_services_count))
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
self._services = {}
|
||||||
|
for i in range(0, _services_count.value):
|
||||||
|
service = GattService(self, _services[i])
|
||||||
|
self._services[service.short_uuid] = service
|
||||||
|
|
||||||
|
logging.debug("Service UUID:0x%x" % service.short_uuid)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Discover GATT Characteristics
|
||||||
|
#
|
||||||
|
_characteristics = POINTER(GattlibCharacteristic)()
|
||||||
|
_characteristics_count = c_int(0)
|
||||||
|
ret = gattlib_discover_char(self._connection, byref(_characteristics), byref(_characteristics_count))
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
self._characteristics = {}
|
||||||
|
for i in range(0, _characteristics_count.value):
|
||||||
|
characteristic = GattCharacteristic(self, _characteristics[i])
|
||||||
|
self._characteristics[characteristic.short_uuid] = characteristic
|
||||||
|
|
||||||
|
logging.debug("Characteristic UUID:0x%x" % characteristic.short_uuid)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def services(self):
|
||||||
|
if not hasattr(self, '_services'):
|
||||||
|
logging.warning("Start GATT discovery implicitly")
|
||||||
|
self.discover()
|
||||||
|
|
||||||
|
return self._services
|
||||||
|
|
||||||
|
@property
|
||||||
|
def characteristics(self):
|
||||||
|
if not hasattr(self, '_characteristics'):
|
||||||
|
logging.warning("Start GATT discovery implicitly")
|
||||||
|
self.discover()
|
||||||
|
|
||||||
|
return self._characteristics
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def notification_callback(uuid_str, data, data_len, user_data):
|
||||||
|
this = user_data
|
||||||
|
|
||||||
|
notification_uuid = uuid.UUID(uuid_str)
|
||||||
|
|
||||||
|
short_uuid = notification_uuid.int
|
||||||
|
if short_uuid not in this._gatt_characteristic_callbacks:
|
||||||
|
raise RuntimeError("UUID '%s' is expected to be part of the notification list")
|
||||||
|
else:
|
||||||
|
characteristic_callback = this._gatt_characteristic_callbacks[short_uuid]
|
||||||
|
|
||||||
|
# value = bytearray(data_len)
|
||||||
|
# for i in range(data_len):
|
||||||
|
# value[i] = data[i]
|
||||||
|
|
||||||
|
pointer_type = POINTER(c_byte * data_len)
|
||||||
|
c_bytearray = cast(data, pointer_type)
|
||||||
|
|
||||||
|
value = bytearray(data_len)
|
||||||
|
for i in range(data_len):
|
||||||
|
value[i] = c_bytearray.contents[i] & 0xFF
|
||||||
|
|
||||||
|
# Call GATT characteristic Notification callback
|
||||||
|
characteristic_callback['callback'](value, characteristic_callback['user_data'])
|
||||||
|
|
||||||
|
def _notification_init(self):
|
||||||
|
if self._is_notification_init:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._is_notification_init = True
|
||||||
|
|
||||||
|
gattlib_register_notification(self._connection, Device.notification_callback, self)
|
||||||
|
|
||||||
|
def _notification_add_gatt_characteristic_callback(self, gatt_characteristic, callback, user_data):
|
||||||
|
if not self._is_notification_init:
|
||||||
|
self._notification_init()
|
||||||
|
|
||||||
|
self._gatt_characteristic_callbacks[gatt_characteristic.short_uuid] = { 'callback': callback, 'user_data': user_data }
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
name = self._name
|
||||||
|
if name:
|
||||||
|
return str(name)
|
||||||
|
else:
|
||||||
|
return str(self._addr)
|
|
@ -0,0 +1,44 @@
|
||||||
|
GATTLIB_SUCCESS = 0
|
||||||
|
GATTLIB_INVALID_PARAMETER = 1
|
||||||
|
GATTLIB_NOT_FOUND = 2
|
||||||
|
GATTLIB_OUT_OF_MEMORY = 3
|
||||||
|
GATTLIB_NOT_SUPPORTED = 4
|
||||||
|
GATTLIB_DEVICE_ERROR = 5
|
||||||
|
GATTLIB_ERROR_DBUS = 6
|
||||||
|
|
||||||
|
class GattlibException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InvalidParameter(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotFound(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class OutOfMemory(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotSupported(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DeviceError(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DBusError(GattlibException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_return(ret):
|
||||||
|
if ret == GATTLIB_INVALID_PARAMETER:
|
||||||
|
raise InvalidParameter()
|
||||||
|
elif ret == GATTLIB_NOT_FOUND:
|
||||||
|
raise NotFound()
|
||||||
|
elif ret == GATTLIB_OUT_OF_MEMORY:
|
||||||
|
raise OutOfMemory()
|
||||||
|
elif ret == GATTLIB_NOT_SUPPORTED:
|
||||||
|
raise NotSupported()
|
||||||
|
elif ret == GATTLIB_DEVICE_ERROR:
|
||||||
|
raise DeviceError()
|
||||||
|
elif ret == GATTLIB_ERROR_DBUS:
|
||||||
|
raise DBusError()
|
||||||
|
elif ret != 0:
|
||||||
|
raise RuntimeError("Gattlib exception %d" % ret)
|
|
@ -0,0 +1,77 @@
|
||||||
|
from gattlib import *
|
||||||
|
from .uuid import gattlib_uuid_to_uuid, gattlib_uuid_to_int
|
||||||
|
from .exception import handle_return
|
||||||
|
|
||||||
|
|
||||||
|
class GattService():
|
||||||
|
|
||||||
|
def __init__(self, device, gattlib_primary_service):
|
||||||
|
self._device = device
|
||||||
|
self._gattlib_primary_service = gattlib_primary_service
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uuid(self):
|
||||||
|
return gattlib_uuid_to_uuid(self._gattlib_primary_service.uuid)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def short_uuid(self):
|
||||||
|
return gattlib_uuid_to_int(self._gattlib_primary_service.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
class GattCharacteristic():
|
||||||
|
|
||||||
|
def __init__(self, device, gattlib_characteristic):
|
||||||
|
self._device = device
|
||||||
|
self._gattlib_characteristic = gattlib_characteristic
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uuid(self):
|
||||||
|
return gattlib_uuid_to_uuid(self._gattlib_characteristic.uuid)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def short_uuid(self):
|
||||||
|
return gattlib_uuid_to_int(self._gattlib_characteristic.uuid)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connection(self):
|
||||||
|
return self._device.connection
|
||||||
|
|
||||||
|
def read(self, callback=None):
|
||||||
|
if callback:
|
||||||
|
raise RuntimeError("Not supported yet")
|
||||||
|
else:
|
||||||
|
_buffer = c_void_p(None)
|
||||||
|
_buffer_len = c_size_t(0)
|
||||||
|
|
||||||
|
ret = gattlib_read_char_by_uuid(self.connection, self._gattlib_characteristic.uuid, byref(_buffer), byref(_buffer_len))
|
||||||
|
|
||||||
|
pointer_type = POINTER(c_byte * _buffer_len.value)
|
||||||
|
c_bytearray = cast(_buffer, pointer_type)
|
||||||
|
|
||||||
|
value = bytearray(_buffer_len.value)
|
||||||
|
for i in range(_buffer_len.value):
|
||||||
|
value[i] = c_bytearray.contents[i]
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
buffer_type = c_char * len(data)
|
||||||
|
buffer = data
|
||||||
|
buffer_len = len(data)
|
||||||
|
|
||||||
|
ret = gattlib_write_char_by_uuid(self.connection, self._gattlib_characteristic.uuid, buffer_type.from_buffer_copy(buffer), buffer_len)
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def register_notification(self, callback, user_data=None):
|
||||||
|
self._device._notification_add_gatt_characteristic_callback(self, callback, user_data)
|
||||||
|
|
||||||
|
def notification_start(self):
|
||||||
|
ret = gattlib_notification_start(self.connection, self._gattlib_characteristic.uuid)
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def notification_stop(self):
|
||||||
|
ret = gattlib_notification_stop(self.connection, self._gattlib_characteristic.uuid)
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.uuid)
|
|
@ -0,0 +1,47 @@
|
||||||
|
import re
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from gattlib import *
|
||||||
|
|
||||||
|
SDP_UUID16 = 0x19
|
||||||
|
SDP_UUID32 = 0x1A
|
||||||
|
SDP_UUID128 = 0x1C
|
||||||
|
|
||||||
|
GATT_STANDARD_UUID_FORMAT = re.compile("(\S+)-0000-1000-8000-00805f9b34fb", flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
|
def gattlib_uuid_to_uuid(gattlib_uuid):
|
||||||
|
if gattlib_uuid.type == SDP_UUID16:
|
||||||
|
return UUID(fields=(gattlib_uuid.value.uuid16, 0x0000, 0x1000, 0x80, 0x00, 0x00805f9b34fb))
|
||||||
|
elif gattlib_uuid.type == SDP_UUID32:
|
||||||
|
return UUID(fields=(gattlib_uuid.value.uuid32, 0x0000, 0x1000, 0x80, 0x00, 0x00805f9b34fb))
|
||||||
|
elif gattlib_uuid.type == SDP_UUID128:
|
||||||
|
data = bytes(gattlib_uuid.value.uuid128.data)
|
||||||
|
return UUID(bytes=data)
|
||||||
|
else:
|
||||||
|
return ValueError("Gattlib UUID not recognized (type:0x%x)" % gattlib_uuid.type)
|
||||||
|
|
||||||
|
|
||||||
|
def gattlib_uuid_to_int(gattlib_uuid):
|
||||||
|
if gattlib_uuid.type == SDP_UUID16:
|
||||||
|
return gattlib_uuid.value.uuid16
|
||||||
|
elif gattlib_uuid.type == SDP_UUID32:
|
||||||
|
return gattlib_uuid.value.uuid32
|
||||||
|
elif gattlib_uuid.type == SDP_UUID128:
|
||||||
|
data = bytes(gattlib_uuid.value.uuid128.data)
|
||||||
|
return int.from_bytes(data, byteorder='big')
|
||||||
|
else:
|
||||||
|
return ValueError("Gattlib UUID not recognized (type:0x%x)" % gattlib_uuid.type)
|
||||||
|
|
||||||
|
|
||||||
|
def gattlib_uuid_str_to_int(uuid_str):
|
||||||
|
# Check if the string could already encode a UUID16 or UUID32
|
||||||
|
if len(uuid_str) <= 8:
|
||||||
|
return int(uuid_str, 16)
|
||||||
|
|
||||||
|
# Check if it is a standard UUID or not
|
||||||
|
match = GATT_STANDARD_UUID_FORMAT.search(uuid_str)
|
||||||
|
if match:
|
||||||
|
return int(match.group(1), 16)
|
||||||
|
else:
|
||||||
|
return UUID(uuid_str).int
|
|
@ -1,3 +1,5 @@
|
||||||
libbluetooth3
|
libbluetooth3
|
||||||
libbluetooth-dev
|
libbluetooth-dev
|
||||||
libglib2.0-dev
|
libglib2.0-dev
|
||||||
|
python3-dev
|
||||||
|
libpython3-dev
|
Loading…
Reference in New Issue