diff --git a/libnymea-core/integrations/python/pyparam.h b/libnymea-core/integrations/python/pyparam.h new file mode 100644 index 00000000..db6da3bd --- /dev/null +++ b/libnymea-core/integrations/python/pyparam.h @@ -0,0 +1,151 @@ +#ifndef PYPARAM_H +#define PYPARAM_H + +#include +#include "structmember.h" + +#include "types/param.h" + +#include "loggingcategories.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wwrite-strings" + + +typedef struct _pyparam { + PyObject_HEAD + PyObject* pyParamTypeId = nullptr; + PyObject* pyValue = nullptr; +} PyParam; + +static void PyParam_dealloc(PyParam * self) { + // FIXME: Why is this not called? Seems we're leaking... + Q_ASSERT(false); + Py_XDECREF(self->pyParamTypeId); + Py_XDECREF(self->pyValue); + Py_TYPE(self)->tp_free(self); +} + +static PyMethodDef PyParam_methods[] = { + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyMemberDef PyParam_members[] = { + {"paramTypeId", T_OBJECT_EX, offsetof(PyParam, pyParamTypeId), 0, "Param type ID"}, + {"value", T_OBJECT_EX, offsetof(PyParam, pyValue), 0, "Param value"}, + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ +}; + + +static int PyParam_init(PyParam *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"paramTypeId", "value", nullptr}; + PyObject *paramTypeId = nullptr, *value = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, ¶mTypeId, &value)) + return -1; + + if (paramTypeId) { + Py_XDECREF(self->pyParamTypeId); + Py_INCREF(paramTypeId); + self->pyParamTypeId = paramTypeId; + } + if (value) { + Py_XDECREF(self->pyValue); + Py_INCREF(value); + self->pyValue = value; + } + return 0; +} + +static PyTypeObject PyParamType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.Param", /* tp_name */ + sizeof(PyParam), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PyParam_dealloc,/* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static PyParam* PyParam_fromParam(const Param ¶m) +{ + PyParam *pyParam = PyObject_New(PyParam, &PyParamType); + pyParam->pyParamTypeId = PyUnicode_FromString(param.paramTypeId().toString().toUtf8()); + + switch (param.value().type()) { + case QVariant::Bool: + pyParam->pyValue = PyBool_FromLong(*(long*)param.value().data()); + break; + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + pyParam->pyValue = PyLong_FromLong(*(long*)param.value().data()); + break; + case QVariant::String: + case QVariant::ByteArray: + pyParam->pyValue = PyUnicode_FromString(param.value().toString().toUtf8()); + break; + case QVariant::Double: + pyParam->pyValue = PyFloat_FromDouble(param.value().toDouble()); + break; + case QVariant::Invalid: + pyParam->pyValue = Py_None; + Py_INCREF(pyParam->pyValue); + break; + default: + qCWarning(dcThingManager) << "Unhandled data type in conversion from Param to PyParam!"; + pyParam->pyValue = Py_None; + Py_INCREF(pyParam->pyValue); + break; + } + return pyParam; +} + +static PyObject* PyParam_FromParamList(const ParamList ¶ms) +{ + PyObject* result = PyTuple_New(params.count()); + for (int i = 0; i < params.count(); i++) { + PyTuple_SET_ITEM(result, i, (PyObject*)PyParam_fromParam(params.at(i))); + } + return result; +} + + +static void registerParamType(PyObject *module) +{ + PyParamType.tp_new = PyType_GenericNew; + PyParamType.tp_basicsize = sizeof(PyParam); + PyParamType.tp_dealloc=(destructor) PyParam_dealloc; + PyParamType.tp_flags = Py_TPFLAGS_DEFAULT; + PyParamType.tp_doc = "Param class"; + PyParamType.tp_methods = PyParam_methods; + PyParamType.tp_members = PyParam_members; + PyParamType.tp_init = reinterpret_cast(PyParam_init); + + if (PyType_Ready(&PyParamType) < 0) { + return; + } + PyModule_AddObject(module, "Param", reinterpret_cast(&PyParamType)); +} + +#pragma GCC diagnostic pop + +#endif // PYPARAM_H diff --git a/libnymea-core/integrations/python/pything.h b/libnymea-core/integrations/python/pything.h index 092118db..175b0177 100644 --- a/libnymea-core/integrations/python/pything.h +++ b/libnymea-core/integrations/python/pything.h @@ -4,6 +4,8 @@ #include #include "structmember.h" +#include "pyparam.h" + #include "integrations/thing.h" #include "loggingcategories.h" @@ -15,7 +17,7 @@ typedef struct _thing { PyObject_HEAD - Thing *ptrObj; + Thing *thing; } PyThing; @@ -25,26 +27,24 @@ static int PyThing_init(PyThing */*self*/, PyObject */*args*/, PyObject */*kwds* static void PyThing_dealloc(PyThing * self) { - // FIXME: Why is this not called? Seems we're leaking... - Q_ASSERT(false); Py_TYPE(self)->tp_free(self); } static PyObject *PyThing_getName(PyThing *self, void */*closure*/) { - if (!self->ptrObj) { + if (!self->thing) { PyErr_SetString(PyExc_ValueError, "Thing has been removed from the system."); return nullptr; } // FIXME: Needs blocking queued connection - PyObject *ret = PyUnicode_FromString(self->ptrObj->name().toUtf8().data()); + PyObject *ret = PyUnicode_FromString(self->thing->name().toUtf8().data()); Py_INCREF(ret); return ret; } static int PyThing_setName(PyThing *self, PyObject *value, void */*closure*/){ // FIXME: Needs queued connection - self->ptrObj->setName(QString(PyUnicode_AsUTF8(value))); + self->thing->setName(QString(PyUnicode_AsUTF8(value))); return 0; } @@ -67,8 +67,8 @@ static PyObject * PyThing_setStateValue(PyThing* self, PyObject* args) QVariant value(bytes); - if (self->ptrObj != nullptr) { - QMetaObject::invokeMethod(self->ptrObj, "setStateValue", Qt::QueuedConnection, Q_ARG(StateTypeId, stateTypeId), Q_ARG(QVariant, value)); + if (self->thing != nullptr) { + QMetaObject::invokeMethod(self->thing, "setStateValue", Qt::QueuedConnection, Q_ARG(StateTypeId, stateTypeId), Q_ARG(QVariant, value)); } Py_XDECREF(repr); @@ -88,22 +88,39 @@ static PyObject * PyThing_emitEvent(PyThing* self, PyObject* args) } EventTypeId eventTypeId = EventTypeId(eventTypeIdStr); + ParamList params; -// if (valueObj != nullptr) { -// PyObject* repr = PyObject_Repr(valueObj); -// PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~"); -// const char *bytes = PyBytes_AS_STRING(str); + if (valueObj != nullptr) { + PyObject *iter = PyObject_GetIter(valueObj); -// QVariant value(bytes); -// } + while (iter) { + PyObject *next = PyIter_Next(iter); + if (!next) { + break; + } + if (next->ob_type != &PyParamType) { + qCWarning(dcThingManager()) << "Invalid parameter passed in param list"; + continue; + } + PyParam *pyParam = reinterpret_cast(next); + ParamTypeId paramTypeId = ParamTypeId(PyUnicode_AsUTF8(pyParam->pyParamTypeId)); - if (self->ptrObj != nullptr) { - QMetaObject::invokeMethod(self->ptrObj, "emitEvent", Qt::QueuedConnection, Q_ARG(EventTypeId, eventTypeId)); + // Is there a better way to convert a PyObject to a QVariant? + PyObject* repr = PyObject_Repr(pyParam->pyValue); + PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~"); + const char *bytes = PyBytes_AS_STRING(str); + Py_XDECREF(repr); + Py_XDECREF(str); + + QVariant value(bytes); + params.append(Param(paramTypeId, value)); + } } -// Py_XDECREF(repr); -// Py_XDECREF(str); + if (self->thing != nullptr) { + QMetaObject::invokeMethod(self->thing, "emitEvent", Qt::QueuedConnection, Q_ARG(EventTypeId, eventTypeId), Q_ARG(ParamList, params)); + } Py_RETURN_NONE; } @@ -153,7 +170,7 @@ static void registerThingType(PyObject *module) PyThingType.tp_doc = "Thing class"; PyThingType.tp_methods = PyThing_methods; PyThingType.tp_getset = PyThing_getseters; -// PyThingType.tp_members = PyThingSetupInfo_members; + // PyThingType.tp_members = PyThingSetupInfo_members; PyThingType.tp_init = reinterpret_cast(PyThing_init); if (PyType_Ready(&PyThingType) < 0) { diff --git a/libnymea-core/integrations/python/pythingactioninfo.h b/libnymea-core/integrations/python/pythingactioninfo.h new file mode 100644 index 00000000..fbe98890 --- /dev/null +++ b/libnymea-core/integrations/python/pythingactioninfo.h @@ -0,0 +1,111 @@ +#ifndef PYTHINGACTIONINFO_H +#define PYTHINGACTIONINFO_H + +#include +#include "structmember.h" + +#include "pything.h" + +#include "integrations/thingactioninfo.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#pragma GCC diagnostic ignored "-Wwrite-strings" + +typedef struct { + PyObject_HEAD + ThingActionInfo* info; + PyThing *pyThing; + PyObject *pyActionTypeId; + PyObject *pyParams; +} PyThingActionInfo; + + +static int PyThingActionInfo_init(PyThingActionInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) { + return 0; +} + + +static void PyThingActionInfo_dealloc(PyThingActionInfo * self) { + // FIXME: Why is this not called? Seems we're leaking... + Q_ASSERT(false); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * PyThingActionInfo_finish(PyThingActionInfo* self, PyObject* args) { + int status; + char *message = nullptr; + + if (!PyArg_ParseTuple(args, "i|s", &status, &message)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\""); + return nullptr; + } + + Thing::ThingError thingError = static_cast(status); + QString displayMessage = message != nullptr ? QString(message) : QString(); + + if (self->info) { + QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage)); + } + + Py_RETURN_NONE; +} + +static PyMemberDef PyThingActionInfo_members[] = { + {"thing", T_OBJECT_EX, offsetof(PyThingActionInfo, pyThing), 0, "Thing this action is for"}, + {"actionTypeId", T_OBJECT_EX, offsetof(PyThingActionInfo, pyActionTypeId), 0, "The action type id for this action"}, + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ +}; + +static PyMethodDef PyThingActionInfo_methods[] = { + { "finish", (PyCFunction)PyThingActionInfo_finish, METH_VARARGS, "finish an action" }, + {nullptr, nullptr, 0, nullptr} // sentinel +}; + +static PyTypeObject PyThingActionInfoType = { + PyVarObject_HEAD_INIT(NULL, 0) + "nymea.ThingActionInfo", /* tp_name */ + sizeof(PyThingActionInfo), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Noddy objects", /* tp_doc */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static void registerThingActionInfoType(PyObject *module) { + PyThingActionInfoType.tp_new = PyType_GenericNew; + PyThingActionInfoType.tp_dealloc=(destructor) PyThingActionInfo_dealloc; + PyThingActionInfoType.tp_basicsize = sizeof(PyThingActionInfo); + PyThingActionInfoType.tp_flags = Py_TPFLAGS_DEFAULT; + PyThingActionInfoType.tp_doc = "ThingActionInfo class"; + PyThingActionInfoType.tp_methods = PyThingActionInfo_methods; + PyThingActionInfoType.tp_members = PyThingActionInfo_members; + PyThingActionInfoType.tp_init = (initproc)PyThingActionInfo_init; + + if (PyType_Ready(&PyThingActionInfoType) < 0) { + return; + } + PyModule_AddObject(module, "ThingActionInfo", (PyObject *)&PyThingActionInfoType); +} + + + + +#pragma GCC diagnostic pop + +#endif // PYTHINGACTIONINFO_H diff --git a/libnymea-core/integrations/python/pythingdescriptor.h b/libnymea-core/integrations/python/pythingdescriptor.h index 91fe2773..a57660b8 100644 --- a/libnymea-core/integrations/python/pythingdescriptor.h +++ b/libnymea-core/integrations/python/pythingdescriptor.h @@ -13,10 +13,9 @@ typedef struct { PyObject_HEAD - PyObject* thingClassId; - PyObject* name; - PyObject* description; - ThingDescriptor descriptor; + PyObject* pyThingClassId; + PyObject* pyName; + PyObject* pyDescription; } PyThingDescriptor; static PyMethodDef PyThingDescriptor_methods[] = { @@ -25,9 +24,9 @@ static PyMethodDef PyThingDescriptor_methods[] = { }; static PyMemberDef PyThingDescriptor_members[] = { - {"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, thingClassId), 0, "Descriptor thingClassId"}, - {"name", T_OBJECT_EX, offsetof(PyThingDescriptor, name), 0, "Descriptor name"}, - {"description", T_OBJECT_EX, offsetof(PyThingDescriptor, description), 0, "Descriptor description"}, + {"thingClassId", T_OBJECT_EX, offsetof(PyThingDescriptor, pyThingClassId), 0, "Descriptor thingClassId"}, + {"name", T_OBJECT_EX, offsetof(PyThingDescriptor, pyName), 0, "Descriptor name"}, + {"description", T_OBJECT_EX, offsetof(PyThingDescriptor, pyDescription), 0, "Descriptor description"}, {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; @@ -35,28 +34,25 @@ static PyMemberDef PyThingDescriptor_members[] = { static int PyThingDescriptor_init(PyThingDescriptor *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"thingClassId", "name", "description", nullptr}; - PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr, *tmp = nullptr; + PyObject *thingClassId = nullptr, *name = nullptr, *description = nullptr; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description)) return -1; if (thingClassId) { - tmp = self->thingClassId; + Py_XDECREF(self->pyThingClassId); Py_INCREF(thingClassId); - self->thingClassId = thingClassId; - Py_XDECREF(tmp); + self->pyThingClassId = thingClassId; } if (name) { - tmp = self->name; + Py_XDECREF(self->pyName); Py_INCREF(name); - self->name = name; - Py_XDECREF(tmp); + self->pyName = name; } if (description) { - tmp = self->description; + Py_XDECREF(self->pyDescription); Py_INCREF(description); - self->description = description; - Py_XDECREF(tmp); + self->pyDescription = description; } return 0; } diff --git a/libnymea-core/integrations/python/pythingdiscoveryinfo.h b/libnymea-core/integrations/python/pythingdiscoveryinfo.h index 8038411b..7037aaa6 100644 --- a/libnymea-core/integrations/python/pythingdiscoveryinfo.h +++ b/libnymea-core/integrations/python/pythingdiscoveryinfo.h @@ -19,20 +19,16 @@ typedef struct { PyObject_HEAD - ThingDiscoveryInfo* ptrObj; + ThingDiscoveryInfo* info; } PyThingDiscoveryInfo; -static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) -// initialize PyVoice Object -{ +static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) { return 0; } -static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) -// destruct the object -{ +static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) { // FIXME: Why is this not called? Seems we're leaking... Q_ASSERT(false); Py_TYPE(self)->tp_free(self); @@ -41,20 +37,20 @@ static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self) static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args) { int status; - char *message; + char *message = nullptr; - if (PyArg_ParseTuple(args, "is", &status, &message)) { - (self->ptrObj)->finish(static_cast(status), QString(message)); - Py_RETURN_NONE; + if (!PyArg_ParseTuple(args, "i|s", &status, &message)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\""); + return nullptr; } - if (PyArg_ParseTuple(args, "i", &status)) { - (self->ptrObj)->finish(static_cast(status)); - Py_RETURN_NONE; - } + Thing::ThingError thingError = static_cast(status); + QString displayMessage = message != nullptr ? QString(message) : QString(); - PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\""); - return nullptr; + if (self->info) { + QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage)); + } + Py_RETURN_NONE; } static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self, PyObject* args) { @@ -72,21 +68,23 @@ static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self, PyThingDescriptor *pyDescriptor = (PyThingDescriptor*)pyObj; ThingClassId thingClassId; - if (pyDescriptor->thingClassId) { - thingClassId = ThingClassId(PyUnicode_AsUTF8(pyDescriptor->thingClassId)); + if (pyDescriptor->pyThingClassId) { + thingClassId = ThingClassId(PyUnicode_AsUTF8(pyDescriptor->pyThingClassId)); } QString name; - if (pyDescriptor->name) { - name = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->name)); + if (pyDescriptor->pyName) { + name = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->pyName)); } QString description; - if (pyDescriptor->description) { - description = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->description)); + if (pyDescriptor->pyDescription) { + description = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->pyDescription)); } ThingDescriptor descriptor(thingClassId, name, description); - self->ptrObj->addThingDescriptor(descriptor); + if (self->info) { + QMetaObject::invokeMethod(self->info, "addThingDescriptor", Qt::QueuedConnection, Q_ARG(ThingDescriptor, descriptor)); + } return Py_BuildValue(""); } diff --git a/libnymea-core/integrations/python/pythingsetupinfo.h b/libnymea-core/integrations/python/pythingsetupinfo.h index 3b63bb1a..1623d4aa 100644 --- a/libnymea-core/integrations/python/pythingsetupinfo.h +++ b/libnymea-core/integrations/python/pythingsetupinfo.h @@ -14,45 +14,41 @@ typedef struct { PyObject_HEAD - ThingSetupInfo* ptrObj; - PyThing *thing; + ThingSetupInfo* info; + PyThing *pyThing; } PyThingSetupInfo; -static int PyThingSetupInfo_init(PyThingSetupInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) -{ +static int PyThingSetupInfo_init(PyThingSetupInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/) { return 0; } -static void PyThingSetupInfo_dealloc(PyThingSetupInfo * self) -{ - // FIXME: Why is this not called? Seems we're leaking... - Q_ASSERT(false); +static void PyThingSetupInfo_dealloc(PyThingSetupInfo * self) { Py_TYPE(self)->tp_free(self); } -static PyObject * PyThingSetupInfo_finish(PyThingSetupInfo* self, PyObject* args) -{ +static PyObject * PyThingSetupInfo_finish(PyThingSetupInfo* self, PyObject* args) { int status; - char *message; + char *message = nullptr; - if (PyArg_ParseTuple(args, "is", &status, &message)) { - (self->ptrObj)->finish(static_cast(status), QString(message)); - Py_RETURN_NONE; + if (!PyArg_ParseTuple(args, "i|s", &status, &message)) { + PyErr_SetString(PyExc_TypeError, "Invalid arguments in finish call. Expected: finish(ThingError, message = \"\""); + return nullptr; } - PyErr_Clear(); - if (PyArg_ParseTuple(args, "i", &status)) { - (self->ptrObj)->finish(static_cast(status)); - Py_RETURN_NONE; + Thing::ThingError thingError = static_cast(status); + QString displayMessage = message != nullptr ? QString(message) : QString(); + + if (self->info) { + QMetaObject::invokeMethod(self->info, "finish", Qt::QueuedConnection, Q_ARG(Thing::ThingError, thingError), Q_ARG(QString, displayMessage)); } Py_RETURN_NONE; } static PyMemberDef PyThingSetupInfo_members[] = { - {"thing", T_OBJECT_EX, offsetof(PyThingSetupInfo, thing), 0, "Thing being setup in this setup transaction"}, + {"thing", T_OBJECT_EX, offsetof(PyThingSetupInfo, pyThing), 0, "Thing being setup in this setup transaction"}, {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; diff --git a/libnymea-core/integrations/pythonintegrationplugin.cpp b/libnymea-core/integrations/pythonintegrationplugin.cpp index d007b616..85717e38 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.cpp +++ b/libnymea-core/integrations/pythonintegrationplugin.cpp @@ -4,6 +4,8 @@ #include "python/pything.h" #include "python/pythingdiscoveryinfo.h" #include "python/pythingsetupinfo.h" +#include "python/pyparam.h" +#include "python/pythingactioninfo.h" #include "pythonintegrationplugin.h" @@ -14,15 +16,11 @@ #include #include -QHash s_modules; PyThreadState* PythonIntegrationPlugin::s_mainThread = nullptr; PyObject* PythonIntegrationPlugin::s_nymeaModule = nullptr; PyObject* PythonIntegrationPlugin::s_asyncio = nullptr; - - - // Write to stdout/stderr PyObject* nymea_write(PyObject* /*self*/, PyObject* args) { @@ -98,10 +96,12 @@ PyMODINIT_FUNC PyInit_nymea(void) registerNymeaLoggingHandler(m); + registerParamType(m); registerThingType(m); registerThingDescriptorType(m); registerThingDiscoveryInfoType(m); registerThingSetupInfoType(m); + registerThingActionInfoType(m); return m; } @@ -192,11 +192,11 @@ void PythonIntegrationPlugin::init() void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info) { PyThingDiscoveryInfo *pyInfo = PyObject_New(PyThingDiscoveryInfo, &PyThingDiscoveryInfoType); - pyInfo->ptrObj = info; + pyInfo->info = info; connect(info, &ThingDiscoveryInfo::destroyed, this, [=](){ PyGILState_STATE s = PyGILState_Ensure(); - pyInfo->ptrObj = nullptr; + pyInfo->info = nullptr; PyObject_Del(pyInfo); PyGILState_Release(s); }); @@ -207,11 +207,11 @@ void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info) void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) { PyThing *pyThing = PyObject_New(PyThing, &PyThingType); - pyThing->ptrObj = info->thing(); + pyThing->thing = info->thing(); PyThingSetupInfo *pyInfo = PyObject_New(PyThingSetupInfo, &PyThingSetupInfoType); - pyInfo->ptrObj = info; - pyInfo->thing = pyThing; + pyInfo->info = info; + pyInfo->pyThing = pyThing; connect(info, &ThingSetupInfo::finished, this, [=](){ @@ -230,7 +230,8 @@ void PythonIntegrationPlugin::setupThing(ThingSetupInfo *info) }); connect(info, &ThingSetupInfo::destroyed, this, [=](){ PyGILState_STATE s = PyGILState_Ensure(); - PyObject_Del(pyInfo); + pyInfo->info = nullptr; + Py_DECREF(pyInfo); PyGILState_Release(s); }); @@ -244,6 +245,32 @@ void PythonIntegrationPlugin::postSetupThing(Thing *thing) callPluginFunction("postSetupThing", reinterpret_cast(pyThing)); } +void PythonIntegrationPlugin::executeAction(ThingActionInfo *info) +{ + PyThing *pyThing = m_things.value(info->thing()); + + PyGILState_STATE s = PyGILState_Ensure(); + + PyThingActionInfo *pyInfo = PyObject_New(PyThingActionInfo, &PyThingActionInfoType); + pyInfo->info = info; + pyInfo->pyThing = pyThing; + pyInfo->pyActionTypeId = PyUnicode_FromString(info->action().actionTypeId().toString().toUtf8()); + pyInfo->pyParams = PyParam_FromParamList(info->action().params()); + + PyGILState_Release(s); + + connect(info, &ThingActionInfo::destroyed, this, [=](){ + PyGILState_STATE s = PyGILState_Ensure(); + pyInfo->pyActionTypeId = nullptr; + Py_DECREF(pyInfo->pyActionTypeId); + pyInfo->info = nullptr; + Py_DECREF(pyInfo); + PyGILState_Release(s); + }); + + callPluginFunction("executeAction", reinterpret_cast(pyInfo)); +} + void PythonIntegrationPlugin::thingRemoved(Thing *thing) { PyThing *pyThing = m_things.value(thing); @@ -252,10 +279,9 @@ void PythonIntegrationPlugin::thingRemoved(Thing *thing) PyGILState_STATE s = PyGILState_Ensure(); - pyThing->ptrObj = nullptr; + pyThing->thing = nullptr; Py_DECREF(pyThing); - PyGILState_Release(s); m_things.remove(thing); diff --git a/libnymea-core/integrations/pythonintegrationplugin.h b/libnymea-core/integrations/pythonintegrationplugin.h index 1e57686d..a7cc47b2 100644 --- a/libnymea-core/integrations/pythonintegrationplugin.h +++ b/libnymea-core/integrations/pythonintegrationplugin.h @@ -29,6 +29,7 @@ public: void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; void postSetupThing(Thing *thing) override; + void executeAction(ThingActionInfo *info) override; void thingRemoved(Thing *thing) override; diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index f2c5bd87..d40891df 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -21,7 +21,9 @@ RESOURCES += $$top_srcdir/icons.qrc \ HEADERS += nymeacore.h \ integrations/plugininfocache.h \ integrations/python/pynymealogginghandler.h \ + integrations/python/pyparam.h \ integrations/python/pything.h \ + integrations/python/pythingactioninfo.h \ integrations/python/pythingdescriptor.h \ integrations/python/pythingdiscoveryinfo.h \ integrations/python/pythingsetupinfo.h \