some more work on python plugins
This commit is contained in:
parent
a21315efc5
commit
380e962bd1
69
libnymea-core/integrations/python/pything.h
Normal file
69
libnymea-core/integrations/python/pything.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef PYTHING_H
|
||||||
|
#define PYTHING_H
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
|
#include "integrations/thing.h"
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
Thing* ptrObj;
|
||||||
|
} PyThing;
|
||||||
|
|
||||||
|
|
||||||
|
static int PyThing_init(PyThing */*self*/, PyObject */*args*/, PyObject */*kwds*/)
|
||||||
|
// initialize PyVoice Object
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void PyThing_dealloc(PyThing * self)
|
||||||
|
// destruct the object
|
||||||
|
{
|
||||||
|
// FIXME: Why is this not called? Seems we're leaking...
|
||||||
|
Q_ASSERT(false);
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyMethodDef PyThing_methods[] = {
|
||||||
|
// { "addDescriptor", (PyCFunction)PyThing_addDescriptor, METH_VARARGS, "Add a new descriptor to the discovery" },
|
||||||
|
// { "finish", (PyCFunction)PyThing_finish, METH_VARARGS, "finish a discovery" },
|
||||||
|
{nullptr, nullptr, 0, nullptr} // sentinel
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject PyThingDiscoveryInfoType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"nymea.Thing", /* tp_name */
|
||||||
|
sizeof(PyThing), /* tp_basicsize */
|
||||||
|
0, /* tp_itemsize */
|
||||||
|
0, /* 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 */
|
||||||
|
"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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#endif // PYTHING_H
|
||||||
233
libnymea-core/integrations/python/pythingdiscoveryinfo.h
Normal file
233
libnymea-core/integrations/python/pythingdiscoveryinfo.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#ifndef PYTHINGDISCOVERYINFO_H
|
||||||
|
#define PYTHINGDISCOVERYINFO_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
|
#include "integrations/thingdiscoveryinfo.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject* thingClassId;
|
||||||
|
PyObject* name;
|
||||||
|
PyObject* description;
|
||||||
|
ThingDescriptor descriptor;
|
||||||
|
} PyThingDescriptor;
|
||||||
|
|
||||||
|
static PyMethodDef PyThingDescriptor_methods[] = {
|
||||||
|
// { "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" },
|
||||||
|
{nullptr, nullptr, 0, nullptr} // sentinel
|
||||||
|
};
|
||||||
|
|
||||||
|
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"},
|
||||||
|
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &thingClassId, &name, &description))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (thingClassId) {
|
||||||
|
tmp = self->thingClassId;
|
||||||
|
Py_INCREF(thingClassId);
|
||||||
|
self->thingClassId = thingClassId;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
|
}
|
||||||
|
if (name) {
|
||||||
|
tmp = self->name;
|
||||||
|
Py_INCREF(name);
|
||||||
|
self->name = name;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
|
}
|
||||||
|
if (description) {
|
||||||
|
tmp = self->description;
|
||||||
|
Py_INCREF(description);
|
||||||
|
self->description = description;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static PyGetSetDef PyThingDescriptor_getsetters[] = {
|
||||||
|
// {"name", (getter) PyThingDescriptor_getName, (setter) PyThingDescriptir_setName,
|
||||||
|
// "Descriptor name", NULL},
|
||||||
|
// {"last", (getter) Custom_getlast, (setter) Custom_setlast,
|
||||||
|
// "last name", NULL},
|
||||||
|
// {NULL} /* Sentinel */
|
||||||
|
//};
|
||||||
|
|
||||||
|
static PyTypeObject PyThingDescriptorType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"nymea.ThingDescriptor", /* tp_name */
|
||||||
|
sizeof(PyThingDescriptor), /* tp_basicsize */
|
||||||
|
0, /* tp_itemsize */
|
||||||
|
0, /* 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 */
|
||||||
|
"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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
ThingDiscoveryInfo* ptrObj;
|
||||||
|
} PyThingDiscoveryInfo;
|
||||||
|
|
||||||
|
|
||||||
|
static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/)
|
||||||
|
// initialize PyVoice Object
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self)
|
||||||
|
// destruct the object
|
||||||
|
{
|
||||||
|
// FIXME: Why is this not called? Seems we're leaking...
|
||||||
|
Q_ASSERT(false);
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
char *message;
|
||||||
|
|
||||||
|
if (PyArg_ParseTuple(args, "is", &status, &message)) {
|
||||||
|
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status), QString(message));
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyArg_ParseTuple(args, "i", &status)) {
|
||||||
|
(self->ptrObj)->finish(static_cast<Thing::ThingError>(status));
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject * PyThingDiscoveryInfo_addDescriptor(PyThingDiscoveryInfo* self, PyObject* args) {
|
||||||
|
|
||||||
|
PyObject *pyObj = nullptr;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &pyObj)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid argument. Not a ThingDescriptor.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (pyObj->ob_type != &PyThingDescriptorType) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid argument. Not a ThingDescriptor.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
PyThingDescriptor *pyDescriptor = (PyThingDescriptor*)pyObj;
|
||||||
|
|
||||||
|
ThingClassId thingClassId;
|
||||||
|
if (pyDescriptor->thingClassId) {
|
||||||
|
thingClassId = ThingClassId(PyUnicode_AsUTF8(pyDescriptor->thingClassId));
|
||||||
|
}
|
||||||
|
QString name;
|
||||||
|
if (pyDescriptor->name) {
|
||||||
|
name = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->name));
|
||||||
|
}
|
||||||
|
QString description;
|
||||||
|
if (pyDescriptor->description) {
|
||||||
|
description = QString::fromUtf8(PyUnicode_AsUTF8(pyDescriptor->description));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThingDescriptor descriptor(thingClassId, name, description);
|
||||||
|
|
||||||
|
self->ptrObj->addThingDescriptor(descriptor);
|
||||||
|
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef PyThingDiscoveryInfo_methods[] = {
|
||||||
|
{ "addDescriptor", (PyCFunction)PyThingDiscoveryInfo_addDescriptor, METH_VARARGS, "Add a new descriptor to the discovery" },
|
||||||
|
{ "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" },
|
||||||
|
{nullptr, nullptr, 0, nullptr} // sentinel
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject PyThingDiscoveryInfoType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"nymea.ThingDiscoveryInfo", /* tp_name */
|
||||||
|
sizeof(PyThingDiscoveryInfo), /* tp_basicsize */
|
||||||
|
0, /* tp_itemsize */
|
||||||
|
0, /* 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 */
|
||||||
|
"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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#endif // PYTHINGDISCOVERYINFO_H
|
||||||
@ -1,27 +1,31 @@
|
|||||||
#ifndef PYTHINGDISCOVERYINFO_H
|
#ifndef PYTHINGSETUPINFO_H
|
||||||
#define PYTHINGDISCOVERYINFO_H
|
#define PYTHINGSETUPINFO_H
|
||||||
|
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
#include "integrations/thingdiscoveryinfo.h"
|
#include "pything.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include "integrations/thingsetupinfo.h"
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
ThingDiscoveryInfo* ptrObj;
|
ThingSetupInfo* ptrObj;
|
||||||
} PyThingDiscoveryInfo;
|
} PyThingSetupInfo;
|
||||||
|
|
||||||
|
|
||||||
static int PyThingDiscoveryInfo_init(PyThingDiscoveryInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/)
|
static int PyThingSetupInfo_init(PyThingSetupInfo */*self*/, PyObject */*args*/, PyObject */*kwds*/)
|
||||||
// initialize PyVoice Object
|
// initialize PyVoice Object
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self)
|
static void PyThingSetupInfo_dealloc(PyThingSetupInfo * self)
|
||||||
// destruct the object
|
// destruct the object
|
||||||
{
|
{
|
||||||
// FIXME: Why is this not called? Seems we're leaking...
|
// FIXME: Why is this not called? Seems we're leaking...
|
||||||
@ -29,7 +33,7 @@ static void PyThingDiscoveryInfo_dealloc(PyThingDiscoveryInfo * self)
|
|||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObject* args)
|
static PyObject * PyThingSetupInfo_finish(PyThingSetupInfo* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
char *message;
|
char *message;
|
||||||
@ -44,18 +48,19 @@ static PyObject * PyThingDiscoveryInfo_finish(PyThingDiscoveryInfo* self, PyObje
|
|||||||
return Py_BuildValue("");
|
return Py_BuildValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Py_False;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef PyThingDiscoveryInfo_methods[] = {
|
static PyMethodDef PyThingDiscoveryInfo_methods[] = {
|
||||||
{ "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" },
|
{ "finish", (PyCFunction)PyThingDiscoveryInfo_finish, METH_VARARGS, "finish a discovery" },
|
||||||
{nullptr, nullptr, 0, nullptr} // sentinel
|
{nullptr, nullptr, 0, nullptr} // sentinel
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject PyThingDiscoveryInfoType = {
|
static PyTypeObject PyThingSetupInfoType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
"nymea.ThingDiscoveryInfo", /* tp_name */
|
"nymea.ThingSetupInfo", /* tp_name */
|
||||||
sizeof(PyThingDiscoveryInfo), /* tp_basicsize */
|
sizeof(PyThingSetupInfo), /* tp_basicsize */
|
||||||
0, /* tp_itemsize */
|
0, /* tp_itemsize */
|
||||||
0, /* tp_dealloc */
|
0, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
0, /* tp_print */
|
||||||
@ -76,4 +81,9 @@ static PyTypeObject PyThingDiscoveryInfoType = {
|
|||||||
"Noddy objects", /* tp_doc */
|
"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
|
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
|
||||||
};
|
};
|
||||||
#endif // PYTHINGDISCOVERYINFO_H
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#endif // PYTHINGSETUPINFO_H
|
||||||
@ -1,15 +1,20 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include "python/PyThingDiscoveryInfo.h"
|
#include "python/pythingdiscoveryinfo.h"
|
||||||
|
|
||||||
#include "pythonintegrationplugin.h"
|
#include "pythonintegrationplugin.h"
|
||||||
|
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
QHash<PyObject*, PyThreadState*> s_modules;
|
||||||
|
|
||||||
// Write to stdout/stderr
|
// Write to stdout/stderr
|
||||||
PyObject* nymea_write(PyObject* /*self*/, PyObject* args)
|
PyObject* nymea_write(PyObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
qWarning() << "write called" << self;
|
||||||
const char *what;
|
const char *what;
|
||||||
if (!PyArg_ParseTuple(args, "s", &what))
|
if (!PyArg_ParseTuple(args, "s", &what))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -50,19 +55,40 @@ PyMODINIT_FUNC PyInit_nymea(void)
|
|||||||
PySys_SetObject("stderr", m);
|
PySys_SetObject("stderr", m);
|
||||||
|
|
||||||
|
|
||||||
|
QMetaEnum thingErrorEnum = QMetaEnum::fromType<Thing::ThingError>();
|
||||||
|
for (int i = 0; i < thingErrorEnum.keyCount(); i++) {
|
||||||
|
PyModule_AddObject(m, thingErrorEnum.key(i), PyLong_FromLong(thingErrorEnum.value(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyThingDiscoveryInfoType.tp_new = PyType_GenericNew;
|
PyThingDiscoveryInfoType.tp_new = PyType_GenericNew;
|
||||||
PyThingDiscoveryInfoType.tp_basicsize=sizeof(PyThingDiscoveryInfo);
|
PyThingDiscoveryInfoType.tp_basicsize=sizeof(PyThingDiscoveryInfo);
|
||||||
PyThingDiscoveryInfoType.tp_dealloc=(destructor) PyThingDiscoveryInfo_dealloc;
|
PyThingDiscoveryInfoType.tp_dealloc=(destructor) PyThingDiscoveryInfo_dealloc;
|
||||||
PyThingDiscoveryInfoType.tp_flags=Py_TPFLAGS_DEFAULT;
|
PyThingDiscoveryInfoType.tp_flags=Py_TPFLAGS_DEFAULT;
|
||||||
PyThingDiscoveryInfoType.tp_doc="ThingDiscoveryInfo class";
|
PyThingDiscoveryInfoType.tp_doc="ThingDiscoveryInfo class";
|
||||||
PyThingDiscoveryInfoType.tp_methods=PyThingDiscoveryInfo_methods;
|
PyThingDiscoveryInfoType.tp_methods=PyThingDiscoveryInfo_methods;
|
||||||
//~ PyVoiceType.tp_members=Noddy_members;
|
|
||||||
PyThingDiscoveryInfoType.tp_init=(initproc)PyThingDiscoveryInfo_init;
|
PyThingDiscoveryInfoType.tp_init=(initproc)PyThingDiscoveryInfo_init;
|
||||||
|
|
||||||
if (PyType_Ready(&PyThingDiscoveryInfoType) < 0)
|
if (PyType_Ready(&PyThingDiscoveryInfoType) < 0) {
|
||||||
return NULL;
|
qCWarning(dcThingManager()) << "Error creating PyThingDiscoveryInfo";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
PyModule_AddObject(m, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType);
|
||||||
|
|
||||||
|
PyThingDescriptorType.tp_new = PyType_GenericNew;
|
||||||
|
PyThingDescriptorType.tp_basicsize = sizeof(PyThingDescriptor);
|
||||||
|
PyThingDescriptorType.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||||
|
PyThingDescriptorType.tp_doc = "ThingDescriptor class";
|
||||||
|
PyThingDescriptorType.tp_methods = PyThingDescriptor_methods;
|
||||||
|
PyThingDescriptorType.tp_members = PyThingDescriptor_members;
|
||||||
|
PyThingDescriptorType.tp_init = (initproc)PyThingDescriptor_init;
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyThingDescriptorType) < 0) {
|
||||||
|
qCWarning(dcThingManager()) << "Error creating PyThingDescriptor";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
PyModule_AddObject(m, "ThingDescriptor", (PyObject *)&PyThingDescriptorType);
|
||||||
|
|
||||||
PyModule_AddObject(m, "ThingDiscoveryInfo", (PyObject *)&PyThingDiscoveryInfoType); // Add Voice object to the module
|
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
@ -72,114 +98,87 @@ PythonIntegrationPlugin::PythonIntegrationPlugin(QObject *parent) : IntegrationP
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PythonIntegrationPlugin::loadScript(const QString &scriptFile)
|
void PythonIntegrationPlugin::initPython()
|
||||||
{
|
{
|
||||||
// TODO: Call this just once and call Py_Finalize()
|
// TODO: Call this just once and call Py_Finalize()
|
||||||
PyImport_AppendInittab("nymea", PyInit_nymea);
|
PyImport_AppendInittab("nymea", PyInit_nymea);
|
||||||
Py_Initialize();
|
Py_InitializeEx(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PythonIntegrationPlugin::loadScript(const QString &scriptFile)
|
||||||
|
{
|
||||||
|
QFileInfo fi(scriptFile);
|
||||||
|
|
||||||
|
QFile metaData(fi.absolutePath() + "/" + fi.baseName() + ".json");
|
||||||
|
if (!metaData.open(QFile::ReadOnly)) {
|
||||||
|
qCWarning(dcThingManager()) << "Error opening metadata file:" << metaData.fileName();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(metaData.readAll(), &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
qCWarning(dcThingManager()) << "Error parsing metadata file:" << error.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_metaData = jsonDoc.toVariant().toMap();
|
||||||
|
|
||||||
|
|
||||||
|
// Create a new sub-interpreter for this plugin
|
||||||
|
m_interpreter = Py_NewInterpreter();
|
||||||
|
PyThreadState_Swap(m_interpreter);
|
||||||
|
|
||||||
|
|
||||||
|
// Import nymea module into this interpreter
|
||||||
PyImport_ImportModule("nymea");
|
PyImport_ImportModule("nymea");
|
||||||
|
|
||||||
QFileInfo fi(scriptFile);
|
// Add this plugin's locatioon to the import path
|
||||||
qCDebug(dcThingManager()) << "Importing" << fi.absolutePath() << fi.fileName() << fi.baseName();
|
|
||||||
|
|
||||||
PyObject* sysPath = PySys_GetObject("path");
|
PyObject* sysPath = PySys_GetObject("path");
|
||||||
PyList_Append(sysPath, PyUnicode_FromString(fi.absolutePath().toUtf8()));
|
PyList_Append(sysPath, PyUnicode_FromString(fi.absolutePath().toUtf8()));
|
||||||
|
|
||||||
|
// Finally, import the plugin
|
||||||
PyObject *pName = PyUnicode_FromString(fi.baseName().toUtf8());
|
m_module = PyImport_ImportModule(fi.baseName().toUtf8());
|
||||||
qCDebug(dcThingManager()) << "Importing python plugin from" << scriptFile;
|
|
||||||
m_module = PyImport_Import(pName);
|
|
||||||
|
|
||||||
if (!m_module) {
|
if (!m_module) {
|
||||||
qCWarning(dcThingManager()) << "Error importing plugin";
|
dumpError();
|
||||||
|
qCWarning(dcThingManager()) << "Error importing python plugin from:" << fi.absoluteFilePath();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
qCDebug(dcThingManager()) << "Imported python plugin from" << fi.absoluteFilePath();
|
||||||
|
|
||||||
|
|
||||||
|
exportIds();
|
||||||
|
|
||||||
|
|
||||||
|
s_modules.insert(m_module, m_interpreter);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject PythonIntegrationPlugin::metaData() const
|
QJsonObject PythonIntegrationPlugin::metaData() const
|
||||||
{
|
{
|
||||||
QVariantMap pluginMetaData;
|
return QJsonObject::fromVariantMap(m_metaData);
|
||||||
pluginMetaData.insert("id", "ccc6dbc8-e352-48a1-8e87-3c89a4669fc2");
|
|
||||||
pluginMetaData.insert("name", "CloudNotifications");
|
|
||||||
pluginMetaData.insert("displayName", tr("Cloud Notifications"));
|
|
||||||
|
|
||||||
QVariantList interfaces;
|
|
||||||
// interfaces.append("notifications");
|
|
||||||
interfaces.append("connectable");
|
|
||||||
|
|
||||||
QVariantList createMethods;
|
|
||||||
createMethods.append("discovery");
|
|
||||||
|
|
||||||
QVariantMap testActionParamTitle;
|
|
||||||
testActionParamTitle.insert("id", "c9545e1c-55cd-42ca-a00f-43f21dfdf05a");
|
|
||||||
testActionParamTitle.insert("name", "title");
|
|
||||||
testActionParamTitle.insert("displayName", tr("Title"));
|
|
||||||
testActionParamTitle.insert("type", "QString");
|
|
||||||
|
|
||||||
QVariantList notifyActionParamTypes;
|
|
||||||
notifyActionParamTypes.append(testActionParamTitle);
|
|
||||||
|
|
||||||
QVariantMap notifyAction;
|
|
||||||
notifyAction.insert("id", "cc6ad463-0a63-4570-ae13-956f50faa3a6");
|
|
||||||
notifyAction.insert("name", "notify");
|
|
||||||
notifyAction.insert("displayName", tr("Send notification"));
|
|
||||||
notifyAction.insert("paramTypes", notifyActionParamTypes);
|
|
||||||
|
|
||||||
QVariantList actionTypes;
|
|
||||||
actionTypes.append(notifyAction);
|
|
||||||
|
|
||||||
QVariantMap connectedState;
|
|
||||||
connectedState.insert("id", "292b5c5d-a6fc-43b6-a59e-f3e1a3ab42b4");
|
|
||||||
connectedState.insert("name", "connected");
|
|
||||||
connectedState.insert("displayName", tr("connected"));
|
|
||||||
connectedState.insert("type", "bool");
|
|
||||||
connectedState.insert("displayNameEvent", tr("Connected changed"));
|
|
||||||
connectedState.insert("defaultValue", false);
|
|
||||||
|
|
||||||
QVariantList stateTypes;
|
|
||||||
stateTypes.append(connectedState);
|
|
||||||
|
|
||||||
|
|
||||||
QVariantMap cloudNotificationsThingClass;
|
|
||||||
cloudNotificationsThingClass.insert("id", "0f1a441a-3793-4a1c-91fc-35d752443aff");
|
|
||||||
cloudNotificationsThingClass.insert("name", "PyTest");
|
|
||||||
cloudNotificationsThingClass.insert("displayName", tr("Python test"));
|
|
||||||
cloudNotificationsThingClass.insert("createMethods", createMethods);
|
|
||||||
cloudNotificationsThingClass.insert("interfaces", interfaces);
|
|
||||||
cloudNotificationsThingClass.insert("actionTypes", actionTypes);
|
|
||||||
cloudNotificationsThingClass.insert("stateTypes", stateTypes);
|
|
||||||
|
|
||||||
QVariantList thingClasses;
|
|
||||||
thingClasses.append(cloudNotificationsThingClass);
|
|
||||||
|
|
||||||
QVariantMap guhVendor;
|
|
||||||
guhVendor.insert("id", "2062d64d-3232-433c-88bc-0d33c0ba2ba6"); // nymea's id
|
|
||||||
guhVendor.insert("name", "nymea");
|
|
||||||
guhVendor.insert("displayName", "nymea");
|
|
||||||
guhVendor.insert("thingClasses", thingClasses);
|
|
||||||
|
|
||||||
QVariantList vendors;
|
|
||||||
vendors.append(guhVendor);
|
|
||||||
pluginMetaData.insert("vendors", vendors);
|
|
||||||
|
|
||||||
return QJsonObject::fromVariantMap(pluginMetaData);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PythonIntegrationPlugin::init()
|
void PythonIntegrationPlugin::init()
|
||||||
{
|
{
|
||||||
qCDebug(dcThingManager()) << "Python wrapper: init()";
|
PyThreadState_Swap(m_interpreter);
|
||||||
|
|
||||||
|
qCDebug(dcThingManager()) << "Python wrapper: init()" << m_interpreter;
|
||||||
PyObject *pFunc = PyObject_GetAttrString(m_module, "init");
|
PyObject *pFunc = PyObject_GetAttrString(m_module, "init");
|
||||||
if(!pFunc || !PyCallable_Check(pFunc)) {
|
if(!pFunc || !PyCallable_Check(pFunc)) {
|
||||||
qCDebug(dcThingManager()) << "Python plugin does not implement \"init()\" method.";
|
qCDebug(dcThingManager()) << "Python plugin does not implement \"init()\" method.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PyObject_CallObject(pFunc, nullptr);
|
PyObject_CallObject(pFunc, nullptr);
|
||||||
|
dumpError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info)
|
void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info)
|
||||||
{
|
{
|
||||||
|
PyThreadState_Swap(m_interpreter);
|
||||||
|
|
||||||
qCDebug(dcThingManager()) << "Python wrapper: discoverThings()" << info;
|
qCDebug(dcThingManager()) << "Python wrapper: discoverThings()" << info;
|
||||||
PyObject *pFunc = PyObject_GetAttrString(m_module, "discoverThings");
|
PyObject *pFunc = PyObject_GetAttrString(m_module, "discoverThings");
|
||||||
if(!pFunc || !PyCallable_Check(pFunc)) {
|
if(!pFunc || !PyCallable_Check(pFunc)) {
|
||||||
@ -194,21 +193,64 @@ void PythonIntegrationPlugin::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
PyObject_Free(pyInfo);
|
PyObject_Free(pyInfo);
|
||||||
});
|
});
|
||||||
|
|
||||||
PyObject_CallFunction(pFunc, "O", pyInfo);
|
|
||||||
|
|
||||||
if (PyErr_Occurred()) {
|
// PyObject *loop = PyObject_GetAttrString(m_module, "_loop");
|
||||||
PyObject *ptype, *pvalue, *ptraceback;
|
// dumpError();
|
||||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
// qWarning() << "loop:" << loop;
|
||||||
if(pvalue) {
|
|
||||||
PyObject *pstr = PyObject_Str(pvalue);
|
|
||||||
if(pstr) {
|
|
||||||
const char* err_msg = PyUnicode_AsUTF8(pstr);
|
|
||||||
if(pstr) {
|
|
||||||
qWarning() << QString(err_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *future = PyObject_CallFunctionObjArgs(pFunc, pyInfo, nullptr);
|
||||||
|
dumpError();
|
||||||
|
|
||||||
|
qWarning() << "future:" << future;
|
||||||
|
|
||||||
|
PyObject *asyncio = PyObject_GetAttrString(m_module, "asyncio");
|
||||||
|
qWarning() << "ayncio:" << asyncio;
|
||||||
|
|
||||||
|
PyObject *loop = PyObject_GetAttrString(m_module, "loop");
|
||||||
|
PyObject *create_task = PyObject_GetAttrString(loop, "create_task");
|
||||||
|
PyObject *task = PyObject_CallFunctionObjArgs(create_task, future, nullptr);
|
||||||
|
|
||||||
|
dumpError();
|
||||||
|
qWarning() << "Create task:" << task;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonIntegrationPlugin::dumpError()
|
||||||
|
{
|
||||||
|
if (!PyErr_Occurred()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *ptype, *pvalue, *ptraceback;
|
||||||
|
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||||
|
if (pvalue) {
|
||||||
|
PyObject *pstr = PyObject_Str(pvalue);
|
||||||
|
if (pstr) {
|
||||||
|
const char* err_msg = PyUnicode_AsUTF8(pstr);
|
||||||
|
if (pstr) {
|
||||||
|
qCWarning(dcThingManager()) << QString(err_msg);
|
||||||
}
|
}
|
||||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
|
||||||
|
}
|
||||||
|
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PythonIntegrationPlugin::exportIds()
|
||||||
|
{
|
||||||
|
foreach (const QVariant &vendorVariant, m_metaData.value("vendors").toList()) {
|
||||||
|
QVariantMap vendor = vendorVariant.toMap();
|
||||||
|
QString vendorIdName = vendor.value("name").toString() + "VendorId";
|
||||||
|
QString vendorId = vendor.value("id").toString();
|
||||||
|
qCDebug(dcThingManager()) << "Exporting:" << vendorIdName;
|
||||||
|
PyModule_AddStringConstant(m_module, vendorIdName.toUtf8(), vendorId.toUtf8());
|
||||||
|
|
||||||
|
foreach (const QVariant &thingClassVariant, vendor.value("thingClasses").toList()) {
|
||||||
|
QVariantMap thingClass = thingClassVariant.toMap();
|
||||||
|
QString thingClassIdName = thingClass.value("name").toString() + "ThingClassId";
|
||||||
|
QString thingClassId = thingClass.value("id").toString();
|
||||||
|
PyModule_AddStringConstant(m_module, thingClassIdName.toUtf8(), thingClassId.toUtf8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
typedef struct _object PyObject;
|
typedef struct _object PyObject;
|
||||||
|
typedef struct _ts PyThreadState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ class PythonIntegrationPlugin : public IntegrationPlugin
|
|||||||
public:
|
public:
|
||||||
explicit PythonIntegrationPlugin(QObject *parent = nullptr);
|
explicit PythonIntegrationPlugin(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static void initPython();
|
||||||
|
|
||||||
bool loadScript(const QString &scriptFile);
|
bool loadScript(const QString &scriptFile);
|
||||||
|
|
||||||
QJsonObject metaData() const;
|
QJsonObject metaData() const;
|
||||||
@ -27,7 +30,16 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void dumpError();
|
||||||
|
|
||||||
|
void exportIds();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// static QHash<PyObject*, PyThreadState*> s_modules;
|
||||||
|
|
||||||
|
QVariantMap m_metaData;
|
||||||
PyObject *m_module = nullptr;
|
PyObject *m_module = nullptr;
|
||||||
|
PyThreadState *m_interpreter = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1343,17 +1343,43 @@ void ThingManagerImplementation::loadPlugins()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PythonIntegrationPlugin *p = new PythonIntegrationPlugin(this);
|
PythonIntegrationPlugin::initPython();
|
||||||
p->loadScript("/home/micha/Develop/nymea-plugin-pytest/pytest.py");
|
|
||||||
PluginMetadata metaData(p->metaData());
|
{
|
||||||
if (!metaData.isValid()) {
|
PythonIntegrationPlugin *p = new PythonIntegrationPlugin(this);
|
||||||
qCWarning(dcThingManager()) << "Not loading Python plugin. Invalid metadata.";
|
bool ok = p->loadScript("/home/micha/Develop/nymea-plugin-pytest/integrationpluginpytest.py");
|
||||||
foreach (const QString &error, metaData.validationErrors()) {
|
if (!ok) {
|
||||||
qCWarning(dcThingManager()) << error;
|
qCWarning(dcThingManager()) << "Error loading plugin";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
PluginMetadata metaData(p->metaData());
|
||||||
|
if (!metaData.isValid()) {
|
||||||
|
qCWarning(dcThingManager()) << "Not loading Python plugin. Invalid metadata.";
|
||||||
|
foreach (const QString &error, metaData.validationErrors()) {
|
||||||
|
qCWarning(dcThingManager()) << error;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadPlugin(p, metaData);
|
||||||
}
|
}
|
||||||
loadPlugin(p, metaData);
|
// {
|
||||||
|
// PythonIntegrationPlugin *p = new PythonIntegrationPlugin(this);
|
||||||
|
// bool ok = p->loadScript("/home/micha/Develop/nymea-plugin-pytest2/integrationpluginpytest2.py");
|
||||||
|
// if (!ok) {
|
||||||
|
// qCWarning(dcThingManager()) << "Error loading plugin";
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// PluginMetadata metaData(p->metaData());
|
||||||
|
// if (!metaData.isValid()) {
|
||||||
|
// qCWarning(dcThingManager()) << "Not loading Python plugin. Invalid metadata.";
|
||||||
|
// foreach (const QString &error, metaData.validationErrors()) {
|
||||||
|
// qCWarning(dcThingManager()) << error;
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// loadPlugin(p, metaData);
|
||||||
|
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThingManagerImplementation::loadPlugin(IntegrationPlugin *pluginIface, const PluginMetadata &metaData)
|
void ThingManagerImplementation::loadPlugin(IntegrationPlugin *pluginIface, const PluginMetadata &metaData)
|
||||||
|
|||||||
@ -20,7 +20,9 @@ RESOURCES += $$top_srcdir/icons.qrc \
|
|||||||
|
|
||||||
HEADERS += nymeacore.h \
|
HEADERS += nymeacore.h \
|
||||||
integrations/plugininfocache.h \
|
integrations/plugininfocache.h \
|
||||||
integrations/python/PyThingDiscoveryInfo.h \
|
integrations/python/pything.h \
|
||||||
|
integrations/python/pythingdiscoveryinfo.h \
|
||||||
|
integrations/python/pythingsetupinfo.h \
|
||||||
integrations/thingmanagerimplementation.h \
|
integrations/thingmanagerimplementation.h \
|
||||||
integrations/translator.h \
|
integrations/translator.h \
|
||||||
integrations/pythonintegrationplugin.h \
|
integrations/pythonintegrationplugin.h \
|
||||||
|
|||||||
Reference in New Issue
Block a user