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
|
||||
*
|
||||
* 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
|
||||
|
@ -69,12 +69,12 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
|
|||
switch (pdu[0]) {
|
||||
case ATT_OP_HANDLE_NOTIFY:
|
||||
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;
|
||||
case ATT_OP_HANDLE_IND:
|
||||
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;
|
||||
default:
|
||||
|
|
|
@ -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"
|
||||
|
||||
void gattlib_register_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data) {
|
||||
connection->notification.type = NATIVE_NOTIFICATION;
|
||||
connection->notification.notification_handler = notification_handler;
|
||||
connection->notification.callback.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) {
|
||||
connection->indication.type = NATIVE_NOTIFICATION;
|
||||
connection->indication.notification_handler = indication_handler;
|
||||
connection->indication.callback.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;
|
||||
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.");
|
||||
}
|
||||
connection->on_disconnection.callback.disconnection_handler = handler;
|
||||
connection->on_disconnection.user_data = user_data;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* Copyright (c) 2021, Olivier Martin <olivier@labapart.org>
|
||||
* Copyright (c) 2021-2024, Olivier Martin <olivier@labapart.org>
|
||||
*/
|
||||
|
||||
#ifndef __GATTLIB_INTERNAL_DEFS_H__
|
||||
#define __GATTLIB_INTERNAL_DEFS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <glib.h>
|
||||
|
||||
#if defined(WITH_PYTHON)
|
||||
#include <Python.h>
|
||||
#endif
|
||||
|
||||
#include "gattlib.h"
|
||||
|
||||
enum handler_type { UNKNOWN = 0, NATIVE_NOTIFICATION, NATIVE_DISCONNECTION, PYTHON };
|
||||
struct gattlib_python_args {
|
||||
PyObject* callback;
|
||||
PyObject* args;
|
||||
};
|
||||
|
||||
struct gattlib_handler {
|
||||
enum handler_type type;
|
||||
union {
|
||||
gattlib_discovered_device_t discovered_device;
|
||||
gatt_connect_cb_t connection_handler;
|
||||
gattlib_event_handler_t notification_handler;
|
||||
gattlib_disconnection_handler_t disconnection_handler;
|
||||
void* python_handler;
|
||||
};
|
||||
void (*callback)(void);
|
||||
} callback;
|
||||
|
||||
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 {
|
||||
void* context;
|
||||
|
||||
struct gattlib_handler on_connection;
|
||||
struct gattlib_handler on_connection_error;
|
||||
struct gattlib_handler notification;
|
||||
struct gattlib_handler indication;
|
||||
struct gattlib_handler disconnection;
|
||||
struct gattlib_handler on_disconnection;
|
||||
};
|
||||
|
||||
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), ...);
|
||||
void gattlib_handler_free(struct gattlib_handler* handler);
|
||||
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);
|
||||
|
||||
#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
|
||||
|
|
|
@ -74,6 +74,11 @@ set(gattlib_SRCS gattlib.c
|
|||
bluez5/lib/uuid.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/../common/gattlib_common.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_BINARY_DIR}/org-bluez-adaptater1.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org-bluez-device1.c
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* 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>
|
||||
|
@ -21,6 +21,29 @@ static void* glib_event_thread(void* main_loop_p) {
|
|||
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(
|
||||
OrgBluezGattCharacteristic1 *object,
|
||||
GVariant *arg_changed_properties,
|
||||
|
@ -28,7 +51,6 @@ gboolean on_handle_device_property_change(
|
|||
gpointer user_data)
|
||||
{
|
||||
gatt_connection_t* connection = user_data;
|
||||
gattlib_context_t* conn_context = connection->context;
|
||||
|
||||
// Retrieve 'Value' from 'arg_changed_properties'
|
||||
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));
|
||||
if (strcmp(key, "Connected") == 0) {
|
||||
if (!g_variant_get_boolean(value)) {
|
||||
// Disconnection case
|
||||
if (gattlib_has_valid_handler(&connection->disconnection)) {
|
||||
gattlib_call_disconnection_handler(&connection->disconnection);
|
||||
}
|
||||
gattlib_on_disconnected_device(connection);
|
||||
}
|
||||
} else if (strcmp(key, "ServicesResolved") == 0) {
|
||||
if (g_variant_get_boolean(value)) {
|
||||
// Stop the timeout for connection
|
||||
g_source_remove(conn_context->connection_timeout);
|
||||
|
||||
// Tell we are now connected
|
||||
g_main_loop_quit(conn_context->connection_loop);
|
||||
_on_device_connect(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,10 +175,10 @@ gatt_connection_t *gattlib_connect(void* adapter, const char *dst, unsigned long
|
|||
if (connection == NULL) {
|
||||
GATTLIB_LOG(GATTLIB_DEBUG, "gattlib_connect: Cannot allocate connection");
|
||||
goto FREE_CONN_CONTEXT;
|
||||
} else {
|
||||
connection->context = conn_context;
|
||||
}
|
||||
|
||||
connection->context = conn_context;
|
||||
|
||||
OrgBluezDevice1* device = org_bluez_device1_proxy_new_for_bus_sync(
|
||||
G_BUS_TYPE_SYSTEM,
|
||||
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);
|
||||
if ((connection != NULL) && (connect_cb != NULL)) {
|
||||
connect_cb(connection, data);
|
||||
connect_cb(adapter, dst, connection, 0 /* error */, data);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if ((item == NULL) || (gattlib_adapter->ble_scan.enabled_filters & GATTLIB_DISCOVER_FILTER_NOTIFY_CHANGE)) {
|
||||
#if defined(WITH_PYTHON)
|
||||
// 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
|
||||
gattlib_on_discovered_device(gattlib_adapter, 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
gattlib_adapter->ble_scan.enabled_filters = enabled_filters;
|
||||
gattlib_adapter->ble_scan.ble_scan_timeout = timeout;
|
||||
gattlib_adapter->ble_scan.discovered_device_callback = discovered_device_cb;
|
||||
gattlib_adapter->ble_scan.discovered_device_user_data = user_data;
|
||||
gattlib_adapter->ble_scan.discovered_device_callback.callback.discovered_device = discovered_device_cb;
|
||||
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),
|
||||
"object-added",
|
||||
|
@ -388,6 +373,12 @@ int gattlib_adapter_scan_disable(void* adapter) {
|
|||
struct gattlib_adapter *gattlib_adapter = adapter;
|
||||
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) {
|
||||
g_mutex_lock(&gattlib_adapter->ble_scan.scan_loop_mutex);
|
||||
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);
|
||||
}
|
||||
|
||||
org_bluez_adapter1_call_stop_discovery_sync(gattlib_adapter->adapter_proxy, NULL, &error);
|
||||
// Ignore the error
|
||||
|
||||
// Remove timeout
|
||||
if (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;
|
||||
|
||||
uint32_t enabled_filters;
|
||||
gattlib_discovered_device_t discovered_device_callback;
|
||||
void *discovered_device_user_data;
|
||||
|
||||
struct gattlib_handler discovered_device_callback;
|
||||
} 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);
|
||||
|
||||
// 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);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,7 +45,7 @@ gboolean on_handle_battery_level_property_change(
|
|||
// GATT connection notifiying to Battery level
|
||||
percentage = g_variant_get_byte(value);
|
||||
|
||||
gattlib_call_notification_handler(&connection->notification,
|
||||
gattlib_on_gatt_notification(connection,
|
||||
&m_battery_level_uuid,
|
||||
(const uint8_t*)&percentage, sizeof(percentage));
|
||||
break;
|
||||
|
@ -88,8 +88,7 @@ static gboolean on_handle_characteristic_property_change(
|
|||
MAX_LEN_UUID_STR + 1,
|
||||
&uuid);
|
||||
|
||||
gattlib_call_notification_handler(&connection->notification,
|
||||
&uuid, data, data_length);
|
||||
gattlib_on_gatt_notification(connection, &uuid, data, data_length);
|
||||
|
||||
// As per https://developer.gnome.org/glib/stable/glib-GVariant.html#g-variant-iter-loop, clean up `key` and `value`.
|
||||
g_variant_unref(value);
|
||||
|
@ -135,8 +134,7 @@ static gboolean on_handle_characteristic_indication(
|
|||
MAX_LEN_UUID_STR + 1,
|
||||
&uuid);
|
||||
|
||||
gattlib_call_notification_handler(&connection->indication,
|
||||
&uuid, data, data_length);
|
||||
gattlib_on_gatt_notification(connection, &uuid, data, data_length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,26 +74,45 @@ class GattlibAdvertisementData(Structure):
|
|||
gattlib_adapter_open = gattlib.gattlib_adapter_open
|
||||
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)
|
||||
gattlib_discovered_device_type = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p, py_object)
|
||||
# const char *gattlib_adapter_get_name(void* adapter)
|
||||
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,
|
||||
# gattlib_advertisement_data_t *advertisement_data, size_t advertisement_data_count,
|
||||
# uint16_t manufacturer_id, uint8_t *manufacturer_data, size_t manufacturer_data_size,
|
||||
# void *user_data);
|
||||
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,
|
||||
py_object)
|
||||
# void gattlib_discovered_device_python_callback(void *adapter, const char* addr, const char* name, void *user_data)
|
||||
gattlib_discovered_device_python_callback = gattlib.gattlib_discovered_device_python_callback
|
||||
gattlib_discovered_device_python_callback.argtypes = [c_void_p, c_char_p, c_char_p, py_object]
|
||||
gattlib_discovered_device_python_callback.restype = c_void_p
|
||||
|
||||
# void gattlib_connected_device_python_callback(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data);
|
||||
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,
|
||||
# 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.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,
|
||||
# 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.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);
|
||||
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.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_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data);
|
||||
gattlib_register_notification = gattlib.gattlib_register_notification
|
||||
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)
|
||||
gattlib_register_on_disconnect = gattlib.gattlib_register_on_disconnect_python
|
||||
gattlib_register_on_disconnect.argtypes = [c_void_p, py_object, py_object]
|
||||
# void gattlib_register_on_disconnect(gatt_connection_t *connection, PyObject *handler, PyObject *user_data)
|
||||
gattlib_register_on_disconnect = gattlib.gattlib_register_on_disconnect
|
||||
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)
|
||||
gattlib_get_rssi = gattlib.gattlib_get_rssi
|
||||
|
|
|
@ -135,8 +135,9 @@ class Adapter:
|
|||
|
||||
ret = gattlib_adapter_scan_enable_with_filter_non_blocking(self._adapter,
|
||||
uuid_list, rssi, enabled_filters,
|
||||
self.on_discovered_device_callback,
|
||||
timeout, user_data)
|
||||
gattlib_discovered_device_python_callback,
|
||||
timeout,
|
||||
gattlib_python_callback_args(self.on_discovered_device_callback, user_data))
|
||||
handle_return(ret)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
# Copyright (c) 2016-2024, Olivier Martin <olivier@labapart.org>
|
||||
#
|
||||
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from gattlib import *
|
||||
from .exception import handle_return, DeviceError
|
||||
from .exception import handle_return, DeviceError, InvalidParameter
|
||||
from .gatt import GattService, GattCharacteristic
|
||||
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_RANDOM = (1 << 1)
|
||||
CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW = (1 << 2)
|
||||
|
@ -26,7 +31,7 @@ CONNECTION_OPTIONS_LEGACY_DEFAULT = \
|
|||
|
||||
class Device:
|
||||
|
||||
def __init__(self, adapter, addr, name=None):
|
||||
def __init__(self, adapter: Adapter, addr: str, name: str = None):
|
||||
self._adapter = adapter
|
||||
if type(addr) == str:
|
||||
self._addr = addr.encode("utf-8")
|
||||
|
@ -34,6 +39,7 @@ class Device:
|
|||
self._addr = addr
|
||||
self._name = name
|
||||
self._connection = None
|
||||
self.on_connection_callback = None
|
||||
|
||||
# Keep track if notification handler has been initialized
|
||||
self._is_notification_init = False
|
||||
|
@ -58,14 +64,23 @@ class Device:
|
|||
return (self._connection is not None)
|
||||
|
||||
def connect(self, options=CONNECTION_OPTIONS_LEGACY_DEFAULT):
|
||||
if self._adapter:
|
||||
adapter_name = self._adapter.name
|
||||
else:
|
||||
adapter_name = None
|
||||
def _on_connection(adapter: c_void_p, mac_address: c_char_p, connection: c_void_p, error: c_int, user_data: py_object):
|
||||
self._connection = connection
|
||||
self.on_connection(user_data)
|
||||
|
||||
self._connection = gattlib_connect(adapter_name, self._addr, options)
|
||||
if self._connection is None:
|
||||
raise DeviceError(adapter=adapter_name, mac_address=self._addr)
|
||||
if self._adapter is None:
|
||||
adapter = None
|
||||
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
|
||||
def rssi(self):
|
||||
|
@ -87,7 +102,9 @@ class Device:
|
|||
self.disconnection_callback = callback
|
||||
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):
|
||||
if self._connection:
|
||||
|
@ -220,9 +237,14 @@ class Device:
|
|||
|
||||
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):
|
||||
if not callable(callback):
|
||||
raise InvalidParameter("Notification callback is not callable.")
|
||||
|
||||
if not self._is_notification_init:
|
||||
self._notification_init()
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* 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__
|
||||
|
@ -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 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
|
||||
|
@ -178,12 +178,15 @@ typedef void (*gattlib_discovered_device_with_data_t)(void *adapter, const char*
|
|||
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 error Connection error code
|
||||
* @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
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* @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, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
Loading…
Reference in New Issue