mirror of https://github.com/labapart/gattlib
python: Fix callback back to native gattlib
parent
ec9e5cd38a
commit
1d80061bf2
|
@ -2,7 +2,7 @@
|
||||||
*
|
*
|
||||||
* GattLib - GATT Library
|
* GattLib - GATT Library
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016-2021 Olivier Martin <olivier@labapart.org>
|
* Copyright (C) 2016-2024 Olivier Martin <olivier@labapart.org>
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -69,12 +69,12 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
|
||||||
switch (pdu[0]) {
|
switch (pdu[0]) {
|
||||||
case ATT_OP_HANDLE_NOTIFY:
|
case ATT_OP_HANDLE_NOTIFY:
|
||||||
if (gattlib_has_valid_handler(&conn->notification)) {
|
if (gattlib_has_valid_handler(&conn->notification)) {
|
||||||
gattlib_call_notification_handler(&conn->notification, &uuid, &pdu[3], len - 3);
|
gattlib_on_gatt_notification(&conn, &uuid, &pdu[3], len - 3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ATT_OP_HANDLE_IND:
|
case ATT_OP_HANDLE_IND:
|
||||||
if (gattlib_has_valid_handler(&conn->indication)) {
|
if (gattlib_has_valid_handler(&conn->indication)) {
|
||||||
gattlib_call_notification_handler(&conn->notification, &uuid, &pdu[3], len - 3);
|
gattlib_on_gatt_notification(&conn, &uuid, &pdu[3], len - 3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -417,7 +417,7 @@ static gatt_connection_t *gattlib_connect_with_options(const char *src, const ch
|
||||||
while ((io_connect_arg.connected == FALSE) && (io_connect_arg.timeout == FALSE)) {
|
while ((io_connect_arg.connected == FALSE) && (io_connect_arg.timeout == FALSE)) {
|
||||||
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
|
g_main_context_iteration(g_gattlib_thread.loop_context, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect the timeout source if connection success
|
// Disconnect the timeout source if connection success
|
||||||
if (io_connect_arg.connected) g_source_destroy(timeout);
|
if (io_connect_arg.connected) g_source_destroy(timeout);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Olivier Martin <olivier@labapart.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
|
void gattlib_connected_device_python_callback(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data) {
|
||||||
|
struct gattlib_python_args* args = user_data;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
// In case of Python support, we ensure we acquire the GIL (Global Intepreter Lock) to have
|
||||||
|
// a thread-safe Python execution.
|
||||||
|
PyGILState_STATE d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
const char* argument_string;
|
||||||
|
// We pass pointer into integer/long parameter. We need to check the address size of the platform
|
||||||
|
// arguments: (void *adapter, const char *dst, gatt_connection_t* connection, void* user_data)
|
||||||
|
if (sizeof(void*) == 8) {
|
||||||
|
argument_string = "(LsLIO)";
|
||||||
|
} else {
|
||||||
|
argument_string = "(IsIIO)";
|
||||||
|
}
|
||||||
|
PyObject *arglist = Py_BuildValue(argument_string, adapter, dst, connection, error, args->args);
|
||||||
|
if (arglist == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Could not convert argument list to Python arguments");
|
||||||
|
PyErr_Print();
|
||||||
|
goto ON_ERROR;
|
||||||
|
}
|
||||||
|
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
||||||
|
result = PyObject_Call(args->callback, arglist, NULL);
|
||||||
|
#else
|
||||||
|
result = PyEval_CallObject(args->callback, arglist);
|
||||||
|
#endif
|
||||||
|
Py_DECREF(arglist);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Python connection device handler has raised an exception.");
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer _gattlib_connected_device_thread(gpointer data) {
|
||||||
|
gatt_connection_t* connection = data;
|
||||||
|
gattlib_context_t* conn_context = connection->context;
|
||||||
|
const gchar *device_mac_address = org_bluez_device1_get_address(conn_context->device);
|
||||||
|
|
||||||
|
connection->on_connection.callback.connection_handler(
|
||||||
|
conn_context->adapter, device_mac_address, connection, 0 /* no error */,
|
||||||
|
connection->on_connection.user_data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* _connected_device_thread_args_allocator(va_list args) {
|
||||||
|
gatt_connection_t* connection = va_arg(args, gatt_connection_t*);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_on_connected_device(gatt_connection_t* connection) {
|
||||||
|
gattlib_handler_dispatch_to_thread(
|
||||||
|
&connection->on_connection,
|
||||||
|
gattlib_connected_device_python_callback /* python_callback */,
|
||||||
|
_gattlib_connected_device_thread /* thread_func */,
|
||||||
|
"gattlib_connected_device" /* thread_name */,
|
||||||
|
_connected_device_thread_args_allocator /* thread_args_allocator */,
|
||||||
|
connection);
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Olivier Martin <olivier@labapart.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
|
void gattlib_disconnected_device_python_callback(gatt_connection_t* connection, void *user_data) {
|
||||||
|
struct gattlib_python_args* args = user_data;
|
||||||
|
PyObject *result;
|
||||||
|
PyGILState_STATE d_gstate;
|
||||||
|
d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
PyObject *arglist = Py_BuildValue("(O)", args->args);
|
||||||
|
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
||||||
|
result = PyObject_Call(args->callback, arglist, NULL);
|
||||||
|
#else
|
||||||
|
result = PyEval_CallObject(args->callback, arglist);
|
||||||
|
#endif
|
||||||
|
Py_DECREF(arglist);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Python disconnection handler has raised an exception.");
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer _gattlib_disconnected_device_thread(gpointer data) {
|
||||||
|
gatt_connection_t* connection = data;
|
||||||
|
|
||||||
|
connection->on_disconnection.callback.disconnection_handler(connection, connection->on_disconnection.user_data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* _disconnected_device_thread_args_allocator(va_list args) {
|
||||||
|
gatt_connection_t* connection = va_arg(args, gatt_connection_t*);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_on_disconnected_device(gatt_connection_t* connection) {
|
||||||
|
gattlib_handler_dispatch_to_thread(
|
||||||
|
&connection->on_disconnection,
|
||||||
|
gattlib_disconnected_device_python_callback /* python_callback */,
|
||||||
|
_gattlib_disconnected_device_thread /* thread_func */,
|
||||||
|
"gattlib_disconnected_device" /* thread_name */,
|
||||||
|
_disconnected_device_thread_args_allocator /* thread_args_allocator */,
|
||||||
|
connection);
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Olivier Martin <olivier@labapart.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
|
void gattlib_discovered_device_python_callback(void *adapter, const char* addr, const char* name, void *user_data) {
|
||||||
|
struct gattlib_python_args* args = user_data;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
// In case of Python support, we ensure we acquire the GIL (Global Intepreter Lock) to have
|
||||||
|
// a thread-safe Python execution.
|
||||||
|
PyGILState_STATE d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
const char* argument_string;
|
||||||
|
// We pass pointer into integer/long parameter. We need to check the address size of the platform
|
||||||
|
if (sizeof(void*) == 8) {
|
||||||
|
argument_string = "(LssO)";
|
||||||
|
} else {
|
||||||
|
argument_string = "(IssO)";
|
||||||
|
}
|
||||||
|
PyObject *arglist = Py_BuildValue(argument_string, adapter, addr, name, args->args);
|
||||||
|
if (arglist == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Could not convert argument list to Python arguments");
|
||||||
|
PyErr_Print();
|
||||||
|
goto ON_ERROR;
|
||||||
|
}
|
||||||
|
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
||||||
|
result = PyObject_Call(args->callback, arglist, NULL);
|
||||||
|
#else
|
||||||
|
result = PyEval_CallObject(args->callback, arglist);
|
||||||
|
#endif
|
||||||
|
Py_DECREF(arglist);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Python discovered device handler has raised an exception.");
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_ERROR:
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gattlib_discovered_device_thread_args {
|
||||||
|
struct gattlib_adapter* gattlib_adapter;
|
||||||
|
char* mac_address;
|
||||||
|
char* name;
|
||||||
|
OrgBluezDevice1* device1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static gpointer _gattlib_discovered_device_thread(gpointer data) {
|
||||||
|
struct gattlib_discovered_device_thread_args* args = data;
|
||||||
|
|
||||||
|
args->gattlib_adapter->ble_scan.discovered_device_callback.callback.discovered_device(
|
||||||
|
args->gattlib_adapter,
|
||||||
|
args->mac_address, args->name,
|
||||||
|
args->gattlib_adapter->ble_scan.discovered_device_callback.user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
free(args->mac_address);
|
||||||
|
if (args->name != NULL) {
|
||||||
|
free(args->name);
|
||||||
|
}
|
||||||
|
free(args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* _discovered_device_thread_args_allocator(va_list args) {
|
||||||
|
struct gattlib_adapter* gattlib_adapter = va_arg(args, struct gattlib_adapter*);
|
||||||
|
OrgBluezDevice1* device1 = va_arg(args, OrgBluezDevice1*);
|
||||||
|
|
||||||
|
struct gattlib_discovered_device_thread_args* thread_args = malloc(sizeof(struct gattlib_discovered_device_thread_args));
|
||||||
|
thread_args->gattlib_adapter = gattlib_adapter;
|
||||||
|
thread_args->mac_address = strdup(org_bluez_device1_get_address(device1));
|
||||||
|
const char* device_name = org_bluez_device1_get_name(device1);
|
||||||
|
if (device_name != NULL) {
|
||||||
|
thread_args->name = strdup(device_name);
|
||||||
|
} else {
|
||||||
|
thread_args->name = NULL;
|
||||||
|
}
|
||||||
|
return thread_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_on_discovered_device(struct gattlib_adapter* gattlib_adapter, OrgBluezDevice1* device1) {
|
||||||
|
gattlib_handler_dispatch_to_thread(
|
||||||
|
&gattlib_adapter->ble_scan.discovered_device_callback,
|
||||||
|
gattlib_discovered_device_python_callback /* python_callback */,
|
||||||
|
_gattlib_discovered_device_thread /* thread_func */,
|
||||||
|
"gattlib_discovered_device" /* thread_name */,
|
||||||
|
_discovered_device_thread_args_allocator /* thread_args_allocator */,
|
||||||
|
gattlib_adapter, device1);
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Olivier Martin <olivier@labapart.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
|
void gattlib_notification_device_python_callback(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data) {
|
||||||
|
struct gattlib_python_args* args = user_data;
|
||||||
|
char uuid_str[MAX_LEN_UUID_STR + 1];
|
||||||
|
PyGILState_STATE d_gstate;
|
||||||
|
PyObject *result;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gattlib_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
d_gstate = PyGILState_Ensure();
|
||||||
|
|
||||||
|
const char* argument_string;
|
||||||
|
// We pass pointer into integer/long parameter. We need to check the address size of the platform
|
||||||
|
if (sizeof(void*) == 8) {
|
||||||
|
argument_string = "(sLIO)";
|
||||||
|
} else {
|
||||||
|
argument_string = "(sIIO)";
|
||||||
|
}
|
||||||
|
PyObject *arglist = Py_BuildValue(argument_string, uuid_str, data, data_length, args->args);
|
||||||
|
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
||||||
|
result = PyObject_Call(args->callback, arglist, NULL);
|
||||||
|
#else
|
||||||
|
result = PyEval_CallObject(args->callback, arglist);
|
||||||
|
#endif
|
||||||
|
Py_DECREF(arglist);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Python notification handler has raised an exception.");
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
PyGILState_Release(d_gstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gattlib_notification_device_thread_args {
|
||||||
|
gatt_connection_t* connection;
|
||||||
|
uuid_t* uuid;
|
||||||
|
uint8_t* data;
|
||||||
|
size_t data_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
static gpointer _gattlib_notification_device_thread(gpointer data) {
|
||||||
|
struct gattlib_notification_device_thread_args* args = data;
|
||||||
|
|
||||||
|
args->connection->notification.callback.notification_handler(
|
||||||
|
args->uuid, args->data, args->data_length,
|
||||||
|
args->connection->notification.user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (args->uuid != NULL) {
|
||||||
|
free(args->uuid);
|
||||||
|
}
|
||||||
|
if (args->data != NULL) {
|
||||||
|
free(args->data);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* _notification_device_thread_args_allocator(va_list args) {
|
||||||
|
gatt_connection_t* connection = va_arg(args, gatt_connection_t*);
|
||||||
|
const uuid_t* uuid = va_arg(args, const uuid_t*);
|
||||||
|
const uint8_t* data = va_arg(args, const uint8_t*);
|
||||||
|
size_t data_length = va_arg(args, size_t);
|
||||||
|
|
||||||
|
struct gattlib_notification_device_thread_args* thread_args = malloc(sizeof(struct gattlib_notification_device_thread_args));
|
||||||
|
thread_args->connection = connection;
|
||||||
|
thread_args->uuid = malloc(sizeof(uuid_t));
|
||||||
|
if (thread_args->uuid != NULL) {
|
||||||
|
memcpy(thread_args->uuid, uuid, sizeof(uuid_t));
|
||||||
|
}
|
||||||
|
thread_args->data = malloc(data_length);
|
||||||
|
if (thread_args->data != NULL) {
|
||||||
|
memcpy(thread_args->data, data, data_length);
|
||||||
|
}
|
||||||
|
thread_args->data_length = data_length;
|
||||||
|
|
||||||
|
return thread_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_on_gatt_notification(gatt_connection_t* connection, const uuid_t* uuid, const uint8_t* data, size_t data_length) {
|
||||||
|
gattlib_handler_dispatch_to_thread(
|
||||||
|
&connection->on_connection,
|
||||||
|
gattlib_notification_device_python_callback /* python_callback */,
|
||||||
|
_gattlib_notification_device_thread /* thread_func */,
|
||||||
|
"gattlib_notification_device" /* thread_name */,
|
||||||
|
_notification_device_thread_args_allocator /* thread_args_allocator */,
|
||||||
|
connection, uuid, data, data_length);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Olivier Martin <olivier@labapart.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gattlib_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
void* gattlib_python_callback_args(PyObject* python_callback, PyObject* python_args) {
|
||||||
|
assert(python_callback != NULL);
|
||||||
|
assert(python_args != NULL);
|
||||||
|
|
||||||
|
struct gattlib_python_args* args = malloc(sizeof(struct gattlib_python_args));
|
||||||
|
if (args == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Failed to allocate Python arguments for Python callback.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(python_callback);
|
||||||
|
Py_INCREF(python_args);
|
||||||
|
|
||||||
|
args->callback = python_callback;
|
||||||
|
args->args = python_args;
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
|
@ -9,115 +9,18 @@
|
||||||
#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.type = NATIVE_NOTIFICATION;
|
connection->notification.callback.notification_handler = notification_handler;
|
||||||
connection->notification.notification_handler = notification_handler;
|
|
||||||
connection->notification.user_data = user_data;
|
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.type = NATIVE_NOTIFICATION;
|
connection->indication.callback.notification_handler = indication_handler;
|
||||||
connection->indication.notification_handler = indication_handler;
|
|
||||||
connection->indication.user_data = user_data;
|
connection->indication.user_data = user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gattlib_register_on_disconnect(gatt_connection_t *connection, gattlib_disconnection_handler_t handler, void* 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->on_disconnection.callback.disconnection_handler = handler;
|
||||||
connection->disconnection.disconnection_handler = handler;
|
connection->on_disconnection.user_data = user_data;
|
||||||
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;
|
|
||||||
PyObject *result;
|
|
||||||
|
|
||||||
gattlib_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
|
|
||||||
|
|
||||||
d_gstate = PyGILState_Ensure();
|
|
||||||
|
|
||||||
const char* argument_string;
|
|
||||||
if (sizeof(void*) == 8) {
|
|
||||||
argument_string = "(sLIO)";
|
|
||||||
} else {
|
|
||||||
argument_string = "(sIIO)";
|
|
||||||
}
|
|
||||||
PyObject *arglist = Py_BuildValue(argument_string, uuid_str, data, data_length, handler->user_data);
|
|
||||||
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
|
||||||
result = PyObject_Call((PyObject *)handler->notification_handler, arglist, NULL);
|
|
||||||
#else
|
|
||||||
result = PyEval_CallObject((PyObject *)handler->notification_handler, arglist);
|
|
||||||
#endif
|
|
||||||
Py_DECREF(arglist);
|
|
||||||
|
|
||||||
if (result == NULL) {
|
|
||||||
GATTLIB_LOG(GATTLIB_ERROR, "Python notification handler has raised an exception.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PyGILState_Release(d_gstate);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else {
|
|
||||||
GATTLIB_LOG(GATTLIB_ERROR, "Invalid notification handler.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gattlib_call_disconnection_handler(struct gattlib_handler *handler) {
|
|
||||||
if (handler->type == NATIVE_DISCONNECTION) {
|
|
||||||
handler->disconnection_handler(handler->user_data);
|
|
||||||
}
|
|
||||||
#if defined(WITH_PYTHON)
|
|
||||||
else if (handler->type == PYTHON) {
|
|
||||||
PyObject *result;
|
|
||||||
PyGILState_STATE d_gstate;
|
|
||||||
d_gstate = PyGILState_Ensure();
|
|
||||||
|
|
||||||
PyObject *arglist = Py_BuildValue("(O)", handler->user_data);
|
|
||||||
#if PYTHON_VERSION >= PYTHON_VERSIONS(3, 9)
|
|
||||||
result = PyObject_Call((PyObject *)handler->disconnection_handler, arglist, NULL);
|
|
||||||
#else
|
|
||||||
result = PyEval_CallObject((PyObject *)handler->disconnection_handler, arglist);
|
|
||||||
#endif
|
|
||||||
Py_DECREF(arglist);
|
|
||||||
|
|
||||||
if (result == NULL) {
|
|
||||||
GATTLIB_LOG(GATTLIB_ERROR, "Python handler has raised an exception.");
|
|
||||||
}
|
|
||||||
|
|
||||||
PyGILState_Release(d_gstate);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else {
|
|
||||||
GATTLIB_LOG(GATTLIB_ERROR, "Invalid disconnection handler.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -199,3 +102,51 @@ int gattlib_uuid_cmp(const uuid_t *uuid1, const uuid_t *uuid2) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gattlib_handler_free(struct gattlib_handler* handler) {
|
||||||
|
// Reset callback to stop calling it after we stopped
|
||||||
|
handler->callback.callback = NULL;
|
||||||
|
|
||||||
|
if (handler->python_args == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gattlib_python_args* args = handler->python_args;
|
||||||
|
Py_DECREF(args->callback);
|
||||||
|
Py_DECREF(args->args);
|
||||||
|
free(args);
|
||||||
|
|
||||||
|
handler->python_args = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gattlib_has_valid_handler(struct gattlib_handler* handler) {
|
||||||
|
return (handler->callback.callback != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gattlib_handler_dispatch_to_thread(struct gattlib_handler* handler, void (*python_callback)(),
|
||||||
|
GThreadFunc thread_func, const char* thread_name, void* (*thread_args_allocator)(va_list args), ...) {
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (handler->callback.callback == NULL) {
|
||||||
|
// We do not have (anymore) a callback, nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are using the Python callback, in case of Python argument we keep track of the argument to free them
|
||||||
|
// once we are done with the handler.
|
||||||
|
if (handler->callback.callback == python_callback) {
|
||||||
|
handler->python_args = handler->user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We create a thread to ensure the callback is not blocking the mainloop
|
||||||
|
va_list args;
|
||||||
|
va_start(args, thread_args_allocator);
|
||||||
|
void* thread_args = thread_args_allocator(args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
handler->thread = g_thread_try_new(thread_name, thread_func, thread_args, &error);
|
||||||
|
if (handler->thread == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create thread '%s': %s", thread_name, error->message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,38 +1,68 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
|
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021, Olivier Martin <olivier@labapart.org>
|
* Copyright (c) 2021-2024, Olivier Martin <olivier@labapart.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GATTLIB_INTERNAL_DEFS_H__
|
#ifndef __GATTLIB_INTERNAL_DEFS_H__
|
||||||
#define __GATTLIB_INTERNAL_DEFS_H__
|
#define __GATTLIB_INTERNAL_DEFS_H__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
#include <Python.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "gattlib.h"
|
#include "gattlib.h"
|
||||||
|
|
||||||
enum handler_type { UNKNOWN = 0, NATIVE_NOTIFICATION, NATIVE_DISCONNECTION, PYTHON };
|
struct gattlib_python_args {
|
||||||
|
PyObject* callback;
|
||||||
|
PyObject* args;
|
||||||
|
};
|
||||||
|
|
||||||
struct gattlib_handler {
|
struct gattlib_handler {
|
||||||
enum handler_type type;
|
|
||||||
union {
|
union {
|
||||||
|
gattlib_discovered_device_t discovered_device;
|
||||||
|
gatt_connect_cb_t connection_handler;
|
||||||
gattlib_event_handler_t notification_handler;
|
gattlib_event_handler_t notification_handler;
|
||||||
gattlib_disconnection_handler_t disconnection_handler;
|
gattlib_disconnection_handler_t disconnection_handler;
|
||||||
void* python_handler;
|
void (*callback)(void);
|
||||||
};
|
} callback;
|
||||||
|
|
||||||
void* user_data;
|
void* user_data;
|
||||||
|
// We create a thread to ensure the callback is not blocking the mainloop
|
||||||
|
GThread *thread;
|
||||||
|
// In case of Python callback and argument, we keep track to free it when we stopped to discover BLE devices
|
||||||
|
void* python_args;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _gatt_connection_t {
|
struct _gatt_connection_t {
|
||||||
void* context;
|
void* context;
|
||||||
|
|
||||||
|
struct gattlib_handler on_connection;
|
||||||
|
struct gattlib_handler on_connection_error;
|
||||||
struct gattlib_handler notification;
|
struct gattlib_handler notification;
|
||||||
struct gattlib_handler indication;
|
struct gattlib_handler indication;
|
||||||
struct gattlib_handler disconnection;
|
struct gattlib_handler on_disconnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool gattlib_has_valid_handler(struct gattlib_handler *handler);
|
void gattlib_handler_dispatch_to_thread(struct gattlib_handler* handler, void (*python_callback)(),
|
||||||
void gattlib_call_disconnection_handler(struct gattlib_handler *handler);
|
GThreadFunc thread_func, const char* thread_name, void* (*thread_args_allocator)(va_list args), ...);
|
||||||
void gattlib_call_notification_handler(struct gattlib_handler *handler, const uuid_t* uuid, const uint8_t* data, size_t data_length);
|
void gattlib_handler_free(struct gattlib_handler* handler);
|
||||||
|
bool gattlib_has_valid_handler(struct gattlib_handler* handler);
|
||||||
|
|
||||||
|
#if defined(WITH_PYTHON)
|
||||||
|
// Callback used by Python to create arguments used by native callback
|
||||||
|
void* gattlib_python_callback_args(PyObject* python_callback, PyObject* python_args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These functions are called by Python wrapper
|
||||||
|
*/
|
||||||
|
void gattlib_discovered_device_python_callback(void *adapter, const char* addr, const char* name, void *user_data);
|
||||||
|
void gattlib_connected_device_python_callback(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data);
|
||||||
|
void gattlib_disconnected_device_python_callback(gatt_connection_t* connection, void *user_data);
|
||||||
|
void gattlib_notification_device_python_callback(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -74,6 +74,11 @@ set(gattlib_SRCS gattlib.c
|
||||||
bluez5/lib/uuid.c
|
bluez5/lib/uuid.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_common.c
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_common.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_eddystone.c
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_eddystone.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_callback_connected_device.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_callback_disconnected_device.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_callback_discovered_device.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_callback_notification_device.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_callback_python.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/logging_backend/${GATTLIB_LOG_BACKEND}/gattlib_logging.c
|
${CMAKE_CURRENT_LIST_DIR}/../common/logging_backend/${GATTLIB_LOG_BACKEND}/gattlib_logging.c
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/org-bluez-adaptater1.c
|
${CMAKE_CURRENT_BINARY_DIR}/org-bluez-adaptater1.c
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/org-bluez-device1.c
|
${CMAKE_CURRENT_BINARY_DIR}/org-bluez-device1.c
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2021, Olivier Martin <olivier@labapart.org>
|
* Copyright (c) 2016-2024, Olivier Martin <olivier@labapart.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
@ -21,6 +21,29 @@ static void* glib_event_thread(void* main_loop_p) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _on_device_connect(gatt_connection_t* connection) {
|
||||||
|
gattlib_context_t* conn_context = connection->context;
|
||||||
|
GDBusObjectManager *device_manager;
|
||||||
|
|
||||||
|
// Stop the timeout for connection
|
||||||
|
if (conn_context->connection_timeout) {
|
||||||
|
g_source_remove(conn_context->connection_timeout);
|
||||||
|
conn_context->connection_timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get list of objects belonging to Device Manager
|
||||||
|
device_manager = get_device_manager_from_adapter(conn_context->adapter);
|
||||||
|
if (device_manager == NULL) {
|
||||||
|
GATTLIB_LOG(GATTLIB_DEBUG, "gattlib_connect: Failed to get device manager from adapter");
|
||||||
|
|
||||||
|
//TODO: Free device
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn_context->dbus_objects = g_dbus_object_manager_get_objects(device_manager);
|
||||||
|
|
||||||
|
gattlib_on_connected_device(connection);
|
||||||
|
}
|
||||||
|
|
||||||
gboolean on_handle_device_property_change(
|
gboolean on_handle_device_property_change(
|
||||||
OrgBluezGattCharacteristic1 *object,
|
OrgBluezGattCharacteristic1 *object,
|
||||||
GVariant *arg_changed_properties,
|
GVariant *arg_changed_properties,
|
||||||
|
@ -28,7 +51,6 @@ gboolean on_handle_device_property_change(
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
gatt_connection_t* connection = user_data;
|
gatt_connection_t* connection = user_data;
|
||||||
gattlib_context_t* conn_context = connection->context;
|
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -41,18 +63,11 @@ gboolean on_handle_device_property_change(
|
||||||
GATTLIB_LOG(GATTLIB_DEBUG, "DBUS: device_property_change: %s: %s", key, g_variant_print(value, TRUE));
|
GATTLIB_LOG(GATTLIB_DEBUG, "DBUS: device_property_change: %s: %s", key, g_variant_print(value, TRUE));
|
||||||
if (strcmp(key, "Connected") == 0) {
|
if (strcmp(key, "Connected") == 0) {
|
||||||
if (!g_variant_get_boolean(value)) {
|
if (!g_variant_get_boolean(value)) {
|
||||||
// Disconnection case
|
gattlib_on_disconnected_device(connection);
|
||||||
if (gattlib_has_valid_handler(&connection->disconnection)) {
|
|
||||||
gattlib_call_disconnection_handler(&connection->disconnection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (strcmp(key, "ServicesResolved") == 0) {
|
} else if (strcmp(key, "ServicesResolved") == 0) {
|
||||||
if (g_variant_get_boolean(value)) {
|
if (g_variant_get_boolean(value)) {
|
||||||
// Stop the timeout for connection
|
_on_device_connect(connection);
|
||||||
g_source_remove(conn_context->connection_timeout);
|
|
||||||
|
|
||||||
// Tell we are now connected
|
|
||||||
g_main_loop_quit(conn_context->connection_loop);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,10 +175,10 @@ gatt_connection_t *gattlib_connect(void* adapter, const char *dst, unsigned long
|
||||||
if (connection == NULL) {
|
if (connection == NULL) {
|
||||||
GATTLIB_LOG(GATTLIB_DEBUG, "gattlib_connect: Cannot allocate connection");
|
GATTLIB_LOG(GATTLIB_DEBUG, "gattlib_connect: Cannot allocate connection");
|
||||||
goto FREE_CONN_CONTEXT;
|
goto FREE_CONN_CONTEXT;
|
||||||
} else {
|
|
||||||
connection->context = conn_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection->context = conn_context;
|
||||||
|
|
||||||
OrgBluezDevice1* device = org_bluez_device1_proxy_new_for_bus_sync(
|
OrgBluezDevice1* device = org_bluez_device1_proxy_new_for_bus_sync(
|
||||||
G_BUS_TYPE_SYSTEM,
|
G_BUS_TYPE_SYSTEM,
|
||||||
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
||||||
|
@ -258,7 +273,7 @@ gatt_connection_t *gattlib_connect_async(void *adapter, const char *dst,
|
||||||
|
|
||||||
connection = gattlib_connect(adapter, dst, options);
|
connection = gattlib_connect(adapter, dst, options);
|
||||||
if ((connection != NULL) && (connect_cb != NULL)) {
|
if ((connection != NULL) && (connect_cb != NULL)) {
|
||||||
connect_cb(connection, data);
|
connect_cb(adapter, dst, connection, 0 /* error */, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
|
|
|
@ -134,22 +134,7 @@ static void device_manager_on_device1_signal(const char* device1_path, struct ga
|
||||||
g_mutex_unlock(&gattlib_adapter->ble_scan.discovered_devices_mutex);
|
g_mutex_unlock(&gattlib_adapter->ble_scan.discovered_devices_mutex);
|
||||||
|
|
||||||
if ((item == NULL) || (gattlib_adapter->ble_scan.enabled_filters & GATTLIB_DISCOVER_FILTER_NOTIFY_CHANGE)) {
|
if ((item == NULL) || (gattlib_adapter->ble_scan.enabled_filters & GATTLIB_DISCOVER_FILTER_NOTIFY_CHANGE)) {
|
||||||
#if defined(WITH_PYTHON)
|
gattlib_on_discovered_device(gattlib_adapter, device1);
|
||||||
// In case of Python support, we ensure we acquire the GIL (Global Intepreter Lock) to have
|
|
||||||
// a thread-safe Python execution.
|
|
||||||
PyGILState_STATE d_gstate;
|
|
||||||
d_gstate = PyGILState_Ensure();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gattlib_adapter->ble_scan.discovered_device_callback(
|
|
||||||
gattlib_adapter,
|
|
||||||
org_bluez_device1_get_address(device1),
|
|
||||||
org_bluez_device1_get_name(device1),
|
|
||||||
gattlib_adapter->ble_scan.discovered_device_user_data);
|
|
||||||
|
|
||||||
#if defined(WITH_PYTHON)
|
|
||||||
PyGILState_Release(d_gstate);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
g_object_unref(device1);
|
g_object_unref(device1);
|
||||||
}
|
}
|
||||||
|
@ -217,7 +202,7 @@ static gboolean _stop_scan_func(gpointer data) {
|
||||||
|
|
||||||
g_mutex_unlock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
g_mutex_unlock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
||||||
|
|
||||||
GATTLIB_LOG(GATTLIB_DEBUG, "BLE scan is stopped after sacnning time has expired.");
|
GATTLIB_LOG(GATTLIB_DEBUG, "BLE scan is stopped after scanning time has expired.");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,8 +299,8 @@ static int _gattlib_adapter_scan_enable_with_filter(void *adapter, uuid_t **uuid
|
||||||
memset(&gattlib_adapter->ble_scan, 0, sizeof(gattlib_adapter->ble_scan));
|
memset(&gattlib_adapter->ble_scan, 0, sizeof(gattlib_adapter->ble_scan));
|
||||||
gattlib_adapter->ble_scan.enabled_filters = enabled_filters;
|
gattlib_adapter->ble_scan.enabled_filters = enabled_filters;
|
||||||
gattlib_adapter->ble_scan.ble_scan_timeout = timeout;
|
gattlib_adapter->ble_scan.ble_scan_timeout = timeout;
|
||||||
gattlib_adapter->ble_scan.discovered_device_callback = discovered_device_cb;
|
gattlib_adapter->ble_scan.discovered_device_callback.callback.discovered_device = discovered_device_cb;
|
||||||
gattlib_adapter->ble_scan.discovered_device_user_data = user_data;
|
gattlib_adapter->ble_scan.discovered_device_callback.user_data = user_data;
|
||||||
|
|
||||||
gattlib_adapter->ble_scan.added_signal_id = g_signal_connect(G_DBUS_OBJECT_MANAGER(device_manager),
|
gattlib_adapter->ble_scan.added_signal_id = g_signal_connect(G_DBUS_OBJECT_MANAGER(device_manager),
|
||||||
"object-added",
|
"object-added",
|
||||||
|
@ -388,6 +373,12 @@ int gattlib_adapter_scan_disable(void* adapter) {
|
||||||
struct gattlib_adapter *gattlib_adapter = adapter;
|
struct gattlib_adapter *gattlib_adapter = adapter;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
|
org_bluez_adapter1_call_stop_discovery_sync(gattlib_adapter->adapter_proxy, NULL, &error);
|
||||||
|
// Ignore the error
|
||||||
|
|
||||||
|
// Free and reset callback to stop calling it after we stopped
|
||||||
|
gattlib_handler_free(&gattlib_adapter->ble_scan.discovered_device_callback);
|
||||||
|
|
||||||
if (gattlib_adapter->ble_scan.is_scanning) {
|
if (gattlib_adapter->ble_scan.is_scanning) {
|
||||||
g_mutex_lock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
g_mutex_lock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
||||||
gattlib_adapter->ble_scan.is_scanning = false;
|
gattlib_adapter->ble_scan.is_scanning = false;
|
||||||
|
@ -395,9 +386,6 @@ int gattlib_adapter_scan_disable(void* adapter) {
|
||||||
g_mutex_unlock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
g_mutex_unlock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
org_bluez_adapter1_call_stop_discovery_sync(gattlib_adapter->adapter_proxy, NULL, &error);
|
|
||||||
// Ignore the error
|
|
||||||
|
|
||||||
// Remove timeout
|
// Remove timeout
|
||||||
if (gattlib_adapter->ble_scan.ble_scan_timeout_id) {
|
if (gattlib_adapter->ble_scan.ble_scan_timeout_id) {
|
||||||
g_source_remove(gattlib_adapter->ble_scan.ble_scan_timeout_id);
|
g_source_remove(gattlib_adapter->ble_scan.ble_scan_timeout_id);
|
||||||
|
|
|
@ -81,8 +81,8 @@ struct gattlib_adapter {
|
||||||
GCond scan_loop_cond;
|
GCond scan_loop_cond;
|
||||||
|
|
||||||
uint32_t enabled_filters;
|
uint32_t enabled_filters;
|
||||||
gattlib_discovered_device_t discovered_device_callback;
|
|
||||||
void *discovered_device_user_data;
|
struct gattlib_handler discovered_device_callback;
|
||||||
} ble_scan;
|
} ble_scan;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +111,15 @@ int get_bluez_device_from_mac(struct gattlib_adapter *adapter, const char *mac_a
|
||||||
|
|
||||||
struct dbus_characteristic get_characteristic_from_uuid(gatt_connection_t* connection, const uuid_t* uuid);
|
struct dbus_characteristic get_characteristic_from_uuid(gatt_connection_t* connection, const uuid_t* uuid);
|
||||||
|
|
||||||
|
// Invoke when a new device has been discovered
|
||||||
|
void gattlib_on_discovered_device(struct gattlib_adapter* gattlib_adapter, OrgBluezDevice1* device1);
|
||||||
|
// Invoke when a new device is being connected
|
||||||
|
void gattlib_on_connected_device(gatt_connection_t* connection);
|
||||||
|
// Invoke when a new device is being disconnected
|
||||||
|
void gattlib_on_disconnected_device(gatt_connection_t* connection);
|
||||||
|
// Invoke when a new device receive a GATT notification
|
||||||
|
void gattlib_on_gatt_notification(gatt_connection_t* connection, const uuid_t* uuid, const uint8_t* data, size_t data_length);
|
||||||
|
|
||||||
void disconnect_all_notifications(gattlib_context_t* conn_context);
|
void disconnect_all_notifications(gattlib_context_t* conn_context);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -45,7 +45,7 @@ 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);
|
||||||
|
|
||||||
gattlib_call_notification_handler(&connection->notification,
|
gattlib_on_gatt_notification(connection,
|
||||||
&m_battery_level_uuid,
|
&m_battery_level_uuid,
|
||||||
(const uint8_t*)&percentage, sizeof(percentage));
|
(const uint8_t*)&percentage, sizeof(percentage));
|
||||||
break;
|
break;
|
||||||
|
@ -88,8 +88,7 @@ static gboolean on_handle_characteristic_property_change(
|
||||||
MAX_LEN_UUID_STR + 1,
|
MAX_LEN_UUID_STR + 1,
|
||||||
&uuid);
|
&uuid);
|
||||||
|
|
||||||
gattlib_call_notification_handler(&connection->notification,
|
gattlib_on_gatt_notification(connection, &uuid, data, data_length);
|
||||||
&uuid, data, data_length);
|
|
||||||
|
|
||||||
// As per https://developer.gnome.org/glib/stable/glib-GVariant.html#g-variant-iter-loop, clean up `key` and `value`.
|
// As per https://developer.gnome.org/glib/stable/glib-GVariant.html#g-variant-iter-loop, clean up `key` and `value`.
|
||||||
g_variant_unref(value);
|
g_variant_unref(value);
|
||||||
|
@ -135,8 +134,7 @@ static gboolean on_handle_characteristic_indication(
|
||||||
MAX_LEN_UUID_STR + 1,
|
MAX_LEN_UUID_STR + 1,
|
||||||
&uuid);
|
&uuid);
|
||||||
|
|
||||||
gattlib_call_notification_handler(&connection->indication,
|
gattlib_on_gatt_notification(connection, &uuid, data, data_length);
|
||||||
&uuid, data, data_length);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,26 +74,45 @@ class GattlibAdvertisementData(Structure):
|
||||||
gattlib_adapter_open = gattlib.gattlib_adapter_open
|
gattlib_adapter_open = gattlib.gattlib_adapter_open
|
||||||
gattlib_adapter_open.argtypes = [c_char_p, POINTER(c_void_p)]
|
gattlib_adapter_open.argtypes = [c_char_p, POINTER(c_void_p)]
|
||||||
|
|
||||||
# typedef void (*gattlib_discovered_device_t)(void *adapter, const char* addr, const char* name, void *user_data)
|
# const char *gattlib_adapter_get_name(void* adapter)
|
||||||
gattlib_discovered_device_type = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p, py_object)
|
gattlib_adapter_get_name = gattlib.gattlib_adapter_get_name
|
||||||
|
gattlib_adapter_get_name.argtypes = [c_void_p]
|
||||||
|
gattlib_adapter_get_name.restype = c_char_p
|
||||||
|
|
||||||
# typedef void (*gattlib_discovered_device_with_data_t)(void *adapter, const char* addr, const char* name,
|
# void gattlib_discovered_device_python_callback(void *adapter, const char* addr, const char* name, void *user_data)
|
||||||
# gattlib_advertisement_data_t *advertisement_data, size_t advertisement_data_count,
|
gattlib_discovered_device_python_callback = gattlib.gattlib_discovered_device_python_callback
|
||||||
# uint16_t manufacturer_id, uint8_t *manufacturer_data, size_t manufacturer_data_size,
|
gattlib_discovered_device_python_callback.argtypes = [c_void_p, c_char_p, c_char_p, py_object]
|
||||||
# void *user_data);
|
gattlib_discovered_device_python_callback.restype = c_void_p
|
||||||
gattlib_discovered_device_with_data_type = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p,
|
|
||||||
POINTER(GattlibAdvertisementData), c_size_t, c_uint16, c_void_p, c_size_t,
|
# void gattlib_connected_device_python_callback(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data);
|
||||||
py_object)
|
gattlib_connected_device_python_callback = gattlib.gattlib_connected_device_python_callback
|
||||||
|
gattlib_connected_device_python_callback.argtypes = [c_void_p, c_char_p, c_void_p, c_int, py_object]
|
||||||
|
gattlib_connected_device_python_callback.restype = c_void_p
|
||||||
|
|
||||||
|
# void gattlib_disconnected_device_python_callback(void *user_data)
|
||||||
|
gattlib_disconnected_device_python_callback = gattlib.gattlib_disconnected_device_python_callback
|
||||||
|
gattlib_disconnected_device_python_callback.argtypes = [py_object]
|
||||||
|
gattlib_disconnected_device_python_callback.restype = c_void_p
|
||||||
|
|
||||||
|
# void gattlib_notification_device_python_callback(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data);
|
||||||
|
gattlib_notification_device_python_callback = gattlib.gattlib_notification_device_python_callback
|
||||||
|
gattlib_notification_device_python_callback.argtypes = [c_void_p, c_void_p, c_int, c_void_p]
|
||||||
|
gattlib_notification_device_python_callback.restype = c_void_p
|
||||||
|
|
||||||
|
# void* gattlib_python_callback_args(PyObject* python_callback, PyObject* python_args) {
|
||||||
|
gattlib_python_callback_args = gattlib.gattlib_python_callback_args
|
||||||
|
gattlib_python_callback_args.argtypes = [py_object, py_object]
|
||||||
|
gattlib_python_callback_args.restype = c_void_p
|
||||||
|
|
||||||
# int gattlib_adapter_scan_enable_with_filter_non_blocking(void *adapter, uuid_t **uuid_list, int16_t rssi_threshold, uint32_t enabled_filters,
|
# int gattlib_adapter_scan_enable_with_filter_non_blocking(void *adapter, uuid_t **uuid_list, int16_t rssi_threshold, uint32_t enabled_filters,
|
||||||
# gattlib_discovered_device_t discovered_device_cb, size_t timeout, void *user_data)
|
# gattlib_discovered_device_t discovered_device_cb, size_t timeout, void *user_data)
|
||||||
gattlib_adapter_scan_enable_with_filter_non_blocking = gattlib.gattlib_adapter_scan_enable_with_filter_non_blocking
|
gattlib_adapter_scan_enable_with_filter_non_blocking = gattlib.gattlib_adapter_scan_enable_with_filter_non_blocking
|
||||||
gattlib_adapter_scan_enable_with_filter_non_blocking.argtypes = [c_void_p, POINTER(POINTER(GattlibUuid)), c_int16, c_uint32, gattlib_discovered_device_type, c_size_t, py_object]
|
gattlib_adapter_scan_enable_with_filter_non_blocking.argtypes = [c_void_p, POINTER(POINTER(GattlibUuid)), c_int16, c_uint32, c_void_p, c_size_t, c_void_p]
|
||||||
|
|
||||||
# int gattlib_adapter_scan_eddystone(void *adapter, int16_t rssi_threshold, uint32_t eddsytone_types,
|
# int gattlib_adapter_scan_eddystone(void *adapter, int16_t rssi_threshold, uint32_t eddsytone_types,
|
||||||
# gattlib_discovered_device_with_data_t discovered_device_cb, size_t timeout, void *user_data)
|
# gattlib_discovered_device_with_data_t discovered_device_cb, size_t timeout, void *user_data)
|
||||||
gattlib_adapter_scan_eddystone = gattlib.gattlib_adapter_scan_eddystone
|
gattlib_adapter_scan_eddystone = gattlib.gattlib_adapter_scan_eddystone
|
||||||
gattlib_adapter_scan_eddystone.argtypes = [c_void_p, c_int16, c_uint32, gattlib_discovered_device_with_data_type, c_size_t, py_object]
|
gattlib_adapter_scan_eddystone.argtypes = [c_void_p, c_int16, c_uint32, c_void_p, c_size_t, c_void_p]
|
||||||
|
|
||||||
# gatt_connection_t *gattlib_connect(const char *src, const char *dst, unsigned long options);
|
# gatt_connection_t *gattlib_connect(const char *src, const char *dst, unsigned long options);
|
||||||
gattlib_connect = gattlib.gattlib_connect
|
gattlib_connect = gattlib.gattlib_connect
|
||||||
|
@ -140,13 +159,13 @@ gattlib_notification_start.argtypes = [c_void_p, POINTER(GattlibUuid)]
|
||||||
gattlib_notification_stop = gattlib.gattlib_notification_stop
|
gattlib_notification_stop = gattlib.gattlib_notification_stop
|
||||||
gattlib_notification_stop.argtypes = [c_void_p, POINTER(GattlibUuid)]
|
gattlib_notification_stop.argtypes = [c_void_p, POINTER(GattlibUuid)]
|
||||||
|
|
||||||
# void gattlib_register_notification_python(gatt_connection_t* connection, PyObject *notification_handler, PyObject *user_data)
|
# void gattlib_register_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data);
|
||||||
gattlib_register_notification = gattlib.gattlib_register_notification_python
|
gattlib_register_notification = gattlib.gattlib_register_notification
|
||||||
gattlib_register_notification.argtypes = [c_void_p, py_object, py_object]
|
gattlib_register_notification.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||||
|
|
||||||
# void gattlib_register_on_disconnect_python(gatt_connection_t *connection, PyObject *handler, PyObject *user_data)
|
# void gattlib_register_on_disconnect(gatt_connection_t *connection, PyObject *handler, PyObject *user_data)
|
||||||
gattlib_register_on_disconnect = gattlib.gattlib_register_on_disconnect_python
|
gattlib_register_on_disconnect = gattlib.gattlib_register_on_disconnect
|
||||||
gattlib_register_on_disconnect.argtypes = [c_void_p, py_object, py_object]
|
gattlib_register_on_disconnect.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||||
|
|
||||||
# int gattlib_get_rssi(gatt_connection_t *connection, int16_t *rssi)
|
# int gattlib_get_rssi(gatt_connection_t *connection, int16_t *rssi)
|
||||||
gattlib_get_rssi = gattlib.gattlib_get_rssi
|
gattlib_get_rssi = gattlib.gattlib_get_rssi
|
||||||
|
|
|
@ -135,8 +135,9 @@ class Adapter:
|
||||||
|
|
||||||
ret = gattlib_adapter_scan_enable_with_filter_non_blocking(self._adapter,
|
ret = gattlib_adapter_scan_enable_with_filter_non_blocking(self._adapter,
|
||||||
uuid_list, rssi, enabled_filters,
|
uuid_list, rssi, enabled_filters,
|
||||||
self.on_discovered_device_callback,
|
gattlib_discovered_device_python_callback,
|
||||||
timeout, user_data)
|
timeout,
|
||||||
|
gattlib_python_callback_args(self.on_discovered_device_callback, user_data))
|
||||||
handle_return(ret)
|
handle_return(ret)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -4,14 +4,19 @@
|
||||||
# Copyright (c) 2016-2024, Olivier Martin <olivier@labapart.org>
|
# Copyright (c) 2016-2024, Olivier Martin <olivier@labapart.org>
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from gattlib import *
|
from gattlib import *
|
||||||
from .exception import handle_return, DeviceError
|
from .exception import handle_return, DeviceError, InvalidParameter
|
||||||
from .gatt import GattService, GattCharacteristic
|
from .gatt import GattService, GattCharacteristic
|
||||||
from .uuid import gattlib_uuid_to_int
|
from .uuid import gattlib_uuid_to_int
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .adapter import Adapter
|
||||||
|
|
||||||
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_PUBLIC = (1 << 0)
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_PUBLIC = (1 << 0)
|
||||||
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM = (1 << 1)
|
CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM = (1 << 1)
|
||||||
CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW = (1 << 2)
|
CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW = (1 << 2)
|
||||||
|
@ -26,7 +31,7 @@ CONNECTION_OPTIONS_LEGACY_DEFAULT = \
|
||||||
|
|
||||||
class Device:
|
class Device:
|
||||||
|
|
||||||
def __init__(self, adapter, addr, name=None):
|
def __init__(self, adapter: Adapter, addr: str, name: str = None):
|
||||||
self._adapter = adapter
|
self._adapter = adapter
|
||||||
if type(addr) == str:
|
if type(addr) == str:
|
||||||
self._addr = addr.encode("utf-8")
|
self._addr = addr.encode("utf-8")
|
||||||
|
@ -34,6 +39,7 @@ class Device:
|
||||||
self._addr = addr
|
self._addr = addr
|
||||||
self._name = name
|
self._name = name
|
||||||
self._connection = None
|
self._connection = None
|
||||||
|
self.on_connection_callback = None
|
||||||
|
|
||||||
# Keep track if notification handler has been initialized
|
# Keep track if notification handler has been initialized
|
||||||
self._is_notification_init = False
|
self._is_notification_init = False
|
||||||
|
@ -58,14 +64,23 @@ class Device:
|
||||||
return (self._connection is not None)
|
return (self._connection is not None)
|
||||||
|
|
||||||
def connect(self, options=CONNECTION_OPTIONS_LEGACY_DEFAULT):
|
def connect(self, options=CONNECTION_OPTIONS_LEGACY_DEFAULT):
|
||||||
if self._adapter:
|
def _on_connection(adapter: c_void_p, mac_address: c_char_p, connection: c_void_p, error: c_int, user_data: py_object):
|
||||||
adapter_name = self._adapter.name
|
self._connection = connection
|
||||||
else:
|
self.on_connection(user_data)
|
||||||
adapter_name = None
|
|
||||||
|
|
||||||
self._connection = gattlib_connect(adapter_name, self._addr, options)
|
if self._adapter is None:
|
||||||
if self._connection is None:
|
adapter = None
|
||||||
raise DeviceError(adapter=adapter_name, mac_address=self._addr)
|
else:
|
||||||
|
adapter = self._adapter._adapter
|
||||||
|
|
||||||
|
ret = gattlib_connect(adapter, self._addr, options,
|
||||||
|
gattlib_connected_device_python_callback,
|
||||||
|
gattlib_python_callback_args(_on_connection, self))
|
||||||
|
handle_return(ret)
|
||||||
|
|
||||||
|
def on_connection(self, user_data: py_object):
|
||||||
|
if self.on_connection_callback:
|
||||||
|
self.on_connection_callback(self, user_data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rssi(self):
|
def rssi(self):
|
||||||
|
@ -87,7 +102,9 @@ class Device:
|
||||||
self.disconnection_callback = callback
|
self.disconnection_callback = callback
|
||||||
self.disconnection_user_data = user_data
|
self.disconnection_user_data = user_data
|
||||||
|
|
||||||
gattlib_register_on_disconnect(self.connection, Device.on_disconnection, self)
|
gattlib_register_on_disconnect(self.connection,
|
||||||
|
gattlib_disconnected_device_python_callback,
|
||||||
|
gattlib_python_callback_args(Device.on_disconnection, user_data))
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
if self._connection:
|
if self._connection:
|
||||||
|
@ -220,9 +237,14 @@ class Device:
|
||||||
|
|
||||||
self._is_notification_init = True
|
self._is_notification_init = True
|
||||||
|
|
||||||
gattlib_register_notification(self._connection, Device.notification_callback, self)
|
gattlib_register_notification(self._connection,
|
||||||
|
gattlib_notification_device_python_callback,
|
||||||
|
gattlib_python_callback_args(Device.notification_callback, self))
|
||||||
|
|
||||||
def _notification_add_gatt_characteristic_callback(self, gatt_characteristic, callback, user_data):
|
def _notification_add_gatt_characteristic_callback(self, gatt_characteristic, callback, user_data):
|
||||||
|
if not callable(callback):
|
||||||
|
raise InvalidParameter("Notification callback is not callable.")
|
||||||
|
|
||||||
if not self._is_notification_init:
|
if not self._is_notification_init:
|
||||||
self._notification_init()
|
self._notification_init()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
|
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2021, Olivier Martin <olivier@labapart.org>
|
* Copyright (c) 2016-2024, Olivier Martin <olivier@labapart.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GATTLIB_H__
|
#ifndef __GATTLIB_H__
|
||||||
|
@ -147,7 +147,7 @@ typedef void (*gattlib_event_handler_t)(const uuid_t* uuid, const uint8_t* data,
|
||||||
* @param connection Connection that is disconnecting
|
* @param connection Connection that is disconnecting
|
||||||
* @param user_data Data defined when calling `gattlib_register_on_disconnect()`
|
* @param user_data Data defined when calling `gattlib_register_on_disconnect()`
|
||||||
*/
|
*/
|
||||||
typedef void (*gattlib_disconnection_handler_t)(void* user_data);
|
typedef void (*gattlib_disconnection_handler_t)(gatt_connection_t* connection, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handler called on new discovered BLE device
|
* @brief Handler called on new discovered BLE device
|
||||||
|
@ -178,12 +178,15 @@ typedef void (*gattlib_discovered_device_with_data_t)(void *adapter, const char*
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handler called on asynchronous connection when connection is ready
|
* @brief Handler called on asynchronous connection when connection is ready or on connection error
|
||||||
*
|
*
|
||||||
|
* @param adapter Local Adaptater interface. When passing NULL, we use default adapter.
|
||||||
|
* @param dst Remote Bluetooth address
|
||||||
* @param connection Connection that is disconnecting
|
* @param connection Connection that is disconnecting
|
||||||
|
* @param error Connection error code
|
||||||
* @param user_data Data defined when calling `gattlib_register_on_disconnect()`
|
* @param user_data Data defined when calling `gattlib_register_on_disconnect()`
|
||||||
*/
|
*/
|
||||||
typedef void (*gatt_connect_cb_t)(gatt_connection_t* connection, void* user_data);
|
typedef void (*gatt_connect_cb_t)(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback called when GATT characteristic read value has been received
|
* @brief Callback called when GATT characteristic read value has been received
|
||||||
|
@ -707,6 +710,13 @@ int gattlib_string_to_uuid(const char *str, size_t size, uuid_t *uuid);
|
||||||
*/
|
*/
|
||||||
int gattlib_uuid_cmp(const uuid_t *uuid1, const uuid_t *uuid2);
|
int gattlib_uuid_cmp(const uuid_t *uuid1, const uuid_t *uuid2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logging function used by Gattlib
|
||||||
|
*
|
||||||
|
* @param level is the logging level of the message
|
||||||
|
* @param format is the message format
|
||||||
|
*
|
||||||
|
*/
|
||||||
void gattlib_log(int level, const char *format, ...);
|
void gattlib_log(int level, const char *format, ...);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
Loading…
Reference in New Issue