mirror of https://github.com/nymea/nymea.git
Add PluginTimer API to python plugins
parent
67b097f2fe
commit
59011c0387
|
|
@ -17,6 +17,7 @@
|
||||||
#include "pybrowseritem.h"
|
#include "pybrowseritem.h"
|
||||||
#include "pybrowseractioninfo.h"
|
#include "pybrowseractioninfo.h"
|
||||||
#include "pybrowseritemresult.h"
|
#include "pybrowseritemresult.h"
|
||||||
|
#include "pyplugintimer.h"
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
|
@ -39,6 +40,7 @@ static int nymea_exec(PyObject *m) {
|
||||||
registerBrowserItemType(m);
|
registerBrowserItemType(m);
|
||||||
registerBrowserActionInfoType(m);
|
registerBrowserActionInfoType(m);
|
||||||
registerBrowserItemResultType(m);
|
registerBrowserItemResultType(m);
|
||||||
|
registerPluginTimerType(m);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
#ifndef PYPLUGINTIMER_H
|
||||||
|
#define PYPLUGINTIMER_H
|
||||||
|
|
||||||
|
#include <Python.h>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||||
|
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||||
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *pyTimeoutHandler = nullptr;
|
||||||
|
QTimer *timer;
|
||||||
|
int interval;
|
||||||
|
PyInterpreterState *interpreter;
|
||||||
|
} PyPluginTimer;
|
||||||
|
|
||||||
|
static int PyPluginTimer_init(PyPluginTimer *self, PyObject *args, PyObject */*kwds*/)
|
||||||
|
{
|
||||||
|
PyObject *handler;
|
||||||
|
|
||||||
|
qCDebug(dcPythonIntegrations()) << "+++ PyPluginTimer";
|
||||||
|
if (!PyArg_ParseTuple(args, "i|O", &self->interval, &handler)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// QTimer needs to be run in a thread that has a QEventLoop but we don't necessarily have one in
|
||||||
|
// python threads. So we're moving the timer to the main app thread.
|
||||||
|
self->timer = new QTimer();
|
||||||
|
self->timer->start(self->interval * 1000);
|
||||||
|
self->timer->moveToThread(QCoreApplication::instance()->thread());
|
||||||
|
|
||||||
|
self->pyTimeoutHandler = handler;
|
||||||
|
Py_XINCREF(handler);
|
||||||
|
|
||||||
|
// Remember the interpreter from the current thread so we can run the callback in the correct interpreter
|
||||||
|
self->interpreter = PyThreadState_GET()->interp;
|
||||||
|
|
||||||
|
|
||||||
|
QObject::connect(self->timer, &QTimer::timeout, [=](){
|
||||||
|
qCDebug(dcPythonIntegrations) << "Plugin timer timeout" << self->pyTimeoutHandler;
|
||||||
|
|
||||||
|
// Spawn a new thread for the callback of the timer (like we do for every python call).
|
||||||
|
// FIXME: Ideally we'd use the plugin's thread pool but we can't easily access that here.
|
||||||
|
// If the timer callback blocks for longer than the timer interval, we might end up with
|
||||||
|
// tons of threads...
|
||||||
|
QFuture<void> future = QtConcurrent::run([=](){
|
||||||
|
// Acquire GIL and make the new thread state the current one
|
||||||
|
PyThreadState *threadState = PyThreadState_New(self->interpreter);
|
||||||
|
PyEval_RestoreThread(threadState);
|
||||||
|
|
||||||
|
if (self->pyTimeoutHandler) {
|
||||||
|
|
||||||
|
PyObject *ret = PyObject_CallFunction(self->pyTimeoutHandler, nullptr);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
Py_XDECREF(ret);
|
||||||
|
}
|
||||||
|
PyThreadState_Clear(threadState);
|
||||||
|
PyEval_ReleaseThread(threadState);
|
||||||
|
PyThreadState_Delete(threadState);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PyPluginTimer_dealloc(PyPluginTimer * self)
|
||||||
|
{
|
||||||
|
qCDebug(dcPythonIntegrations()) << "--- PyPluginTimer";
|
||||||
|
Py_XDECREF(self->pyTimeoutHandler);
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(self->timer, "stop", Qt::QueuedConnection);
|
||||||
|
self->timer->deleteLater();
|
||||||
|
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *PyPluginTimer_getInterval(PyPluginTimer *self, void */*closure*/)
|
||||||
|
{
|
||||||
|
return PyLong_FromLong(self->interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int PyPluginTimer_setInterval(PyPluginTimer *self, PyObject *value, void */*closure*/){
|
||||||
|
self->interval = PyLong_AsLong(value);
|
||||||
|
QMetaObject::invokeMethod(self->timer, "start", Qt::QueuedConnection, Q_ARG(int, self->interval * 1000));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyGetSetDef PyPluginTimer_getset[] = {
|
||||||
|
{"interval", (getter)PyPluginTimer_getInterval, (setter)PyPluginTimer_setInterval, "Timer interval", nullptr},
|
||||||
|
{nullptr , nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static PyMemberDef PyPluginTimer_members[] = {
|
||||||
|
{"timeoutHandler", T_OBJECT_EX, offsetof(PyPluginTimer, pyTimeoutHandler), 0, "Set a callback for when the timer timeout triggers."},
|
||||||
|
{nullptr, 0, 0, 0, nullptr} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject PyPluginTimerType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"nymea.PluginTimer", /* tp_name */
|
||||||
|
sizeof(PyPluginTimer), /* tp_basicsize */
|
||||||
|
0, /* tp_itemsize */
|
||||||
|
(destructor)PyPluginTimer_dealloc, /* tp_dealloc */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void registerPluginTimerType(PyObject *module)
|
||||||
|
{
|
||||||
|
PyPluginTimerType.tp_new = PyType_GenericNew;
|
||||||
|
PyPluginTimerType.tp_members = PyPluginTimer_members;
|
||||||
|
PyPluginTimerType.tp_getset = PyPluginTimer_getset;
|
||||||
|
PyPluginTimerType.tp_init = reinterpret_cast<initproc>(PyPluginTimer_init);
|
||||||
|
PyPluginTimerType.tp_doc = "PluginTimers can be used to perform repeating tasks, such as polling a device or online service.";
|
||||||
|
PyPluginTimerType.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyPluginTimerType) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PyModule_AddObject(module, "PluginTimer", reinterpret_cast<PyObject*>(&PyPluginTimerType));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#endif // PYPLUGINTIMER_H
|
||||||
|
|
@ -50,6 +50,7 @@ public:
|
||||||
static PyObject* pyAutoThingDisappeared(PyObject *self, PyObject* args);
|
static PyObject* pyAutoThingDisappeared(PyObject *self, PyObject* args);
|
||||||
static PyObject* pyPluginStorage(PyObject* self, PyObject* args);
|
static PyObject* pyPluginStorage(PyObject* self, PyObject* args);
|
||||||
static PyObject* pyApiKeyStorage(PyObject* self, PyObject* args);
|
static PyObject* pyApiKeyStorage(PyObject* self, PyObject* args);
|
||||||
|
static PyObject* pyHardwareManager(PyObject* self, PyObject* args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void exportIds();
|
void exportIds();
|
||||||
|
|
|
||||||
|
|
@ -1370,7 +1370,7 @@ void ThingManagerImplementation::loadPlugins()
|
||||||
delete p;
|
delete p;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
qCWarning(dcThingManager()) << "Not loading Python plugin as Python plugin support is not included in this nymea instance.2";
|
qCWarning(dcThingManager()) << "Not loading Python plugin as Python plugin support is not included in this nymea instance.";
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
// Not a known plugin type
|
// Not a known plugin type
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ HEADERS += nymeacore.h \
|
||||||
integrations/python/pybrowseritem.h \
|
integrations/python/pybrowseritem.h \
|
||||||
integrations/python/pybrowseritemresult.h \
|
integrations/python/pybrowseritemresult.h \
|
||||||
integrations/python/pypluginstorage.h \
|
integrations/python/pypluginstorage.h \
|
||||||
|
integrations/python/pyplugintimer.h \
|
||||||
integrations/thingmanagerimplementation.h \
|
integrations/thingmanagerimplementation.h \
|
||||||
integrations/translator.h \
|
integrations/translator.h \
|
||||||
experiences/experiencemanager.h \
|
experiences/experiencemanager.h \
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,15 @@
|
||||||
"createMethods": ["user"],
|
"createMethods": ["user"],
|
||||||
"setupMethod": "justAdd",
|
"setupMethod": "justAdd",
|
||||||
"browsable": true,
|
"browsable": true,
|
||||||
|
"settingsTypes": [
|
||||||
|
{
|
||||||
|
"id": "f3ef303b-d719-4a64-a44c-6b1935bf0d4d",
|
||||||
|
"name": "interval",
|
||||||
|
"displayName": "Timer interval",
|
||||||
|
"type": "uint",
|
||||||
|
"defaultValue": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
"eventTypes": [
|
"eventTypes": [
|
||||||
{
|
{
|
||||||
"id": "de6c2425-0dee-413f-8f4c-bb0929e83c0d",
|
"id": "de6c2425-0dee-413f-8f4c-bb0929e83c0d",
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,24 @@
|
||||||
import nymea
|
import nymea
|
||||||
import time
|
import time
|
||||||
#from fastdotcom import fast_com
|
|
||||||
|
|
||||||
watchingAutoThings = False
|
globalPluginTimer = None
|
||||||
loopRunning = False
|
pluginTimers = {}
|
||||||
|
|
||||||
|
# Optional, for initialisation, if needed
|
||||||
def init():
|
def init():
|
||||||
global loopRunning
|
|
||||||
loopRunning = True
|
|
||||||
|
|
||||||
logger.log("Python mock plugin init")
|
logger.log("Python mock plugin init")
|
||||||
logger.warn("Python mock warning")
|
logger.warn("Python mock warning")
|
||||||
print("python stdout")
|
print("python stdout")
|
||||||
|
|
||||||
while loopRunning:
|
|
||||||
time.sleep(5);
|
|
||||||
for thing in myThings():
|
|
||||||
if thing.thingClassId == pyMockThingClassId:
|
|
||||||
logger.log("Emitting event 1 for", thing.name, "eventTypeId", pyMockEvent1EventTypeId)
|
|
||||||
thing.emitEvent(pyMockEvent1EventTypeId, [nymea.Param(pyMockEvent1EventParam1ParamTypeId, "Im an event")])
|
|
||||||
logger.log("Setting state 1 for", thing.name, "to", thing.stateValue(pyMockState1StateTypeId) + 1)
|
|
||||||
thing.setStateValue(pyMockState1StateTypeId, thing.stateValue(pyMockState1StateTypeId) + 1)
|
|
||||||
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
|
||||||
logger.log("Emitting event 1 for", thing.name)
|
|
||||||
thing.emitEvent(pyMockDiscoveryPairingEvent1EventTypeId, [nymea.Param(pyMockDiscoveryPairingEvent1EventParam1ParamTypeId, "Im an event")])
|
|
||||||
logger.log("Setting state 1 for", thing.name, "Old value is:", thing.stateValue(pyMockDiscoveryPairingState1StateTypeId))
|
|
||||||
thing.setStateValue(pyMockDiscoveryPairingState1StateTypeId, thing.stateValue(pyMockDiscoveryPairingState1StateTypeId) + 1)
|
|
||||||
logger.log("Bye bye")
|
|
||||||
|
|
||||||
|
|
||||||
|
# Optional, clean up stuff if needed
|
||||||
def deinit():
|
def deinit():
|
||||||
logger.log("shutting down")
|
logger.log("shutting down")
|
||||||
global loopRunning
|
|
||||||
loopRunning = False
|
|
||||||
|
|
||||||
|
|
||||||
def configValueChanged(paramTypeId, value):
|
|
||||||
logger.log("Plugin config value changed:", paramTypeId, value, watchingAutoThings)
|
|
||||||
if watchingAutoThings and paramTypeId == pyMockPluginAutoThingCountParamTypeId:
|
|
||||||
logger.log("Auto Thing Count plugin config changed:", value, "Currently there are:", len(autoThings()), "auto things")
|
|
||||||
things = autoThings();
|
|
||||||
for i in range(len(things), value):
|
|
||||||
logger.log("Creating new auto thing")
|
|
||||||
descriptor = nymea.ThingDescriptor(pyMockAutoThingClassId, "Python Mock auto thing")
|
|
||||||
descriptor.params = [nymea.Param(pyMockAutoThingParam1ParamTypeId, True)]
|
|
||||||
autoThingsAppeared([descriptor])
|
|
||||||
|
|
||||||
for i in range(value, len(things)):
|
|
||||||
logger.log("Removing auto thing")
|
|
||||||
autoThingDisappeared(things[i].id)
|
|
||||||
|
|
||||||
|
|
||||||
|
# Optional, if the plugin should create auto things, this is the right place to create them,
|
||||||
|
# or, start monitoring the network (or whatever) for them to appear
|
||||||
def startMonitoringAutoThings():
|
def startMonitoringAutoThings():
|
||||||
global watchingAutoThings
|
|
||||||
watchingAutoThings = True
|
|
||||||
logger.log("Start monitoring auto things. Have %i auto devices. Need %i." % (len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId)))
|
logger.log("Start monitoring auto things. Have %i auto devices. Need %i." % (len(autoThings()), configValue(pyMockPluginAutoThingCountParamTypeId)))
|
||||||
things = autoThings();
|
things = autoThings();
|
||||||
for i in range(len(things), configValue(pyMockPluginAutoThingCountParamTypeId)):
|
for i in range(len(things), configValue(pyMockPluginAutoThingCountParamTypeId)):
|
||||||
|
|
@ -68,13 +33,14 @@ def startMonitoringAutoThings():
|
||||||
logger.log("Done start monitoring auto things")
|
logger.log("Done start monitoring auto things")
|
||||||
|
|
||||||
|
|
||||||
|
# If the plugin supports things of createMethod "discovery", nymea will call this to discover things
|
||||||
def discoverThings(info):
|
def discoverThings(info):
|
||||||
logger.log("Discovery started for", info.thingClassId, "with result count:", info.params[0].value)
|
logger.log("Discovery started for", info.thingClassId, "with result count:", info.params[0].value)
|
||||||
time.sleep(10) # Some delay for giving a feeling of a discovery
|
time.sleep(5) # Some delay for giving a feeling of a real discovery
|
||||||
# Add 2 new discovery results
|
# Add discovery results (in this example the amount given by the discovery params)
|
||||||
for i in range(0, info.params[0].value):
|
for i in range(0, info.params[0].value):
|
||||||
info.addDescriptor(nymea.ThingDescriptor(pyMockDiscoveryPairingThingClassId, "Python mock thing %i" % i))
|
info.addDescriptor(nymea.ThingDescriptor(pyMockDiscoveryPairingThingClassId, "Python mock thing %i" % i))
|
||||||
# Also add existing ones again so reconfiguration is possible
|
# Also add existing ones again so reconfiguration is possible, setting the existing thing ID properly
|
||||||
for thing in myThings():
|
for thing in myThings():
|
||||||
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
||||||
info.addDescriptor(nymea.ThingDescriptor(pyMockDiscoveryPairingThingClassId, thing.name, thingId=thing.id))
|
info.addDescriptor(nymea.ThingDescriptor(pyMockDiscoveryPairingThingClassId, thing.name, thingId=thing.id))
|
||||||
|
|
@ -82,11 +48,13 @@ def discoverThings(info):
|
||||||
info.finish(nymea.ThingErrorNoError)
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
|
# If the plugin supports things with a setupMethod other than "justAdd", this will be called to initiate login/pairing
|
||||||
def startPairing(info):
|
def startPairing(info):
|
||||||
logger.log("startPairing for", info.thingName, info.thingId, info.params)
|
logger.log("startPairing for", info.thingName, info.thingId, info.params)
|
||||||
info.finish(nymea.ThingErrorNoError, "Log in as user \"john\" with password \"smith\".")
|
info.finish(nymea.ThingErrorNoError, "Log in as user \"john\" with password \"smith\".")
|
||||||
|
|
||||||
|
|
||||||
|
# If the plugin supports things with a setupMethod other than "justAdd", this will be called to complete login/pairing
|
||||||
def confirmPairing(info, username, secret):
|
def confirmPairing(info, username, secret):
|
||||||
logger.log("confirming pairing for", info.thingName, username, secret)
|
logger.log("confirming pairing for", info.thingName, username, secret)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
@ -96,16 +64,23 @@ def confirmPairing(info, username, secret):
|
||||||
info.finish(nymea.ThingErrorAuthenticationFailure, "Error logging in here!")
|
info.finish(nymea.ThingErrorAuthenticationFailure, "Error logging in here!")
|
||||||
|
|
||||||
|
|
||||||
|
# Mandatory, a new thing is being set up. Initialize (connect etc...) it
|
||||||
def setupThing(info):
|
def setupThing(info):
|
||||||
logger.log("setupThing for", info.thing.name)
|
logger.log("setupThing for", info.thing.name)
|
||||||
info.finish(nymea.ThingErrorNoError)
|
info.finish(nymea.ThingErrorNoError)
|
||||||
info.thing.nameChangedHandler = thingNameChanged
|
|
||||||
|
# Signal handlers
|
||||||
info.thing.settingChangedHandler = thingSettingChanged
|
info.thing.settingChangedHandler = thingSettingChanged
|
||||||
|
info.thing.nameChangedHandler = lambda info : logger.log("Thing name changed", info.thing.name)
|
||||||
|
|
||||||
|
|
||||||
|
# Optional, run additional code after a successful thing setup
|
||||||
def postSetupThing(thing):
|
def postSetupThing(thing):
|
||||||
logger.log("postSetupThing for", thing.name)
|
logger.log("postSetupThing for", thing.name)
|
||||||
thing.nameChangedHandler = lambda thing : logger.log("Thing name changed", thing.name)
|
|
||||||
|
global globalPluginTimer
|
||||||
|
if globalPluginTimer is None:
|
||||||
|
globalPluginTimer = nymea.PluginTimer(5, timerTriggered)
|
||||||
|
|
||||||
if thing.thingClassId == pyMockAutoThingClassId:
|
if thing.thingClassId == pyMockAutoThingClassId:
|
||||||
logger.log("State 1 value:", thing.stateValue(pyMockAutoState1StateTypeId))
|
logger.log("State 1 value:", thing.stateValue(pyMockAutoState1StateTypeId))
|
||||||
|
|
@ -114,7 +89,46 @@ def postSetupThing(thing):
|
||||||
logger.log("Param 1 value:", thing.paramValue(pyMockDiscoveryPairingThingParam1ParamTypeId))
|
logger.log("Param 1 value:", thing.paramValue(pyMockDiscoveryPairingThingParam1ParamTypeId))
|
||||||
logger.log("Setting 1 value:", thing.setting(pyMockDiscoveryPairingSettingsSetting1ParamTypeId))
|
logger.log("Setting 1 value:", thing.setting(pyMockDiscoveryPairingSettingsSetting1ParamTypeId))
|
||||||
|
|
||||||
|
if thing.thingClassId == pyMockThingClassId:
|
||||||
|
interval = thing.setting(pyMockSettingsIntervalParamTypeId)
|
||||||
|
|
||||||
|
pluginTimer = nymea.PluginTimer(interval, lambda thing=thing : logger.log("Timer triggered for %s. (Interval: %i)" % (thing.name, thing.setting(pyMockSettingsIntervalParamTypeId))))
|
||||||
|
|
||||||
|
logger.log("Thing timer interval for %s: %i" % (thing.name, pluginTimer.interval))
|
||||||
|
pluginTimers[thing] = pluginTimer
|
||||||
|
|
||||||
|
|
||||||
|
# Optional, do cleanups when a thing is removed
|
||||||
|
def thingRemoved(thing):
|
||||||
|
logger.log("thingRemoved for", thing.name)
|
||||||
|
logger.log("Remaining things:", len(myThings()))
|
||||||
|
|
||||||
|
if thing.thingClassId == pyMockThingClassId:
|
||||||
|
del pluginTimers[thing]
|
||||||
|
|
||||||
|
# Clean up the global plugin timer if there are no things left
|
||||||
|
if len(myThings()) == 0:
|
||||||
|
global globalPluginTimer
|
||||||
|
globalPluginTimer = None
|
||||||
|
|
||||||
|
|
||||||
|
# Callback for the plugin timer. If polling is needed, fetch values and set thing states accordingly
|
||||||
|
def timerTriggered():
|
||||||
|
logger.log("Timer triggered")
|
||||||
|
for thing in myThings():
|
||||||
|
if thing.thingClassId == pyMockThingClassId:
|
||||||
|
logger.log("Emitting event 1 for", thing.name, "eventTypeId", pyMockEvent1EventTypeId)
|
||||||
|
thing.emitEvent(pyMockEvent1EventTypeId, [nymea.Param(pyMockEvent1EventParam1ParamTypeId, "Im an event")])
|
||||||
|
logger.log("Setting state 1 for", thing.name, "to", thing.stateValue(pyMockState1StateTypeId) + 1)
|
||||||
|
thing.setStateValue(pyMockState1StateTypeId, thing.stateValue(pyMockState1StateTypeId) + 1)
|
||||||
|
if thing.thingClassId == pyMockDiscoveryPairingThingClassId:
|
||||||
|
logger.log("Emitting event 1 for", thing.name)
|
||||||
|
thing.emitEvent(pyMockDiscoveryPairingEvent1EventTypeId, [nymea.Param(pyMockDiscoveryPairingEvent1EventParam1ParamTypeId, "Im an event")])
|
||||||
|
logger.log("Setting state 1 for", thing.name, "Old value is:", thing.stateValue(pyMockDiscoveryPairingState1StateTypeId))
|
||||||
|
thing.setStateValue(pyMockDiscoveryPairingState1StateTypeId, thing.stateValue(pyMockDiscoveryPairingState1StateTypeId) + 1)
|
||||||
|
|
||||||
|
|
||||||
|
# If the plugin supports things with actions, nymea will call this to run actions
|
||||||
def executeAction(info):
|
def executeAction(info):
|
||||||
logger.log("executeAction for", info.thing.name, info.actionTypeId, "with params", info.params)
|
logger.log("executeAction for", info.thing.name, info.actionTypeId, "with params", info.params)
|
||||||
paramValueByIndex = info.params[0].value
|
paramValueByIndex = info.params[0].value
|
||||||
|
|
@ -123,22 +137,35 @@ def executeAction(info):
|
||||||
info.finish(nymea.ThingErrorNoError)
|
info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
def autoThings():
|
# Callback handler when the user changes settings for a particular thing
|
||||||
autoThings = []
|
|
||||||
for thing in myThings():
|
|
||||||
if thing.thingClassId == pyMockAutoThingClassId:
|
|
||||||
autoThings.append(thing)
|
|
||||||
return autoThings
|
|
||||||
|
|
||||||
|
|
||||||
def thingNameChanged(thing, name):
|
|
||||||
logger.log("Thing name changed:", thing.name)
|
|
||||||
|
|
||||||
|
|
||||||
def thingSettingChanged(thing, paramTypeId, value):
|
def thingSettingChanged(thing, paramTypeId, value):
|
||||||
logger.log("Thing setting changed:", thing.name, paramTypeId, value)
|
logger.log("Thing setting changed:", thing.name, paramTypeId, value)
|
||||||
|
|
||||||
|
if thing.thingClassId == pyMockThingClassId:
|
||||||
|
if paramTypeId == pyMockSettingsIntervalParamTypeId:
|
||||||
|
logger.log("Adjusting plugin timer to interval:", value)
|
||||||
|
timer = pluginTimers[thing]
|
||||||
|
timer.interval = value
|
||||||
|
|
||||||
|
|
||||||
|
# Callback handler when the user changes the global plugin configuration
|
||||||
|
def configValueChanged(paramTypeId, value):
|
||||||
|
logger.log("Plugin config value changed:", paramTypeId, value)
|
||||||
|
if paramTypeId == pyMockPluginAutoThingCountParamTypeId:
|
||||||
|
logger.log("Auto Thing Count plugin config changed:", value, "Currently there are:", len(autoThings()), "auto things")
|
||||||
|
things = autoThings();
|
||||||
|
for i in range(len(things), value):
|
||||||
|
logger.log("Creating new auto thing")
|
||||||
|
descriptor = nymea.ThingDescriptor(pyMockAutoThingClassId, "Python Mock auto thing")
|
||||||
|
descriptor.params = [nymea.Param(pyMockAutoThingParam1ParamTypeId, True)]
|
||||||
|
autoThingsAppeared([descriptor])
|
||||||
|
|
||||||
|
for i in range(value, len(things)):
|
||||||
|
logger.log("Removing auto thing")
|
||||||
|
autoThingDisappeared(things[i].id)
|
||||||
|
|
||||||
|
|
||||||
|
# If a plugin supports browsable things, nymea will call this to browse a thing
|
||||||
def browseThing(result):
|
def browseThing(result):
|
||||||
logger.log("browseThing called", result.thing.name, result.itemId)
|
logger.log("browseThing called", result.thing.name, result.itemId)
|
||||||
if result.itemId == "":
|
if result.itemId == "":
|
||||||
|
|
@ -155,11 +182,17 @@ def browseThing(result):
|
||||||
result.finish(nymea.ThingErrorNoError)
|
result.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
def executeBrowserItem(info):
|
# If a thingclass supports browser item actions, nymea will call this upon execution
|
||||||
logger.log("executeBrowserItem called for thing", info.thing.name, "and item", info.itemId)
|
|
||||||
info.finish(nymea.ThingErrorNoError)
|
|
||||||
|
|
||||||
|
|
||||||
# Intentionally commented out to also have a test case for unimplmented functions
|
# Intentionally commented out to also have a test case for unimplmented functions
|
||||||
# def thingRemoved(thing):
|
#def executeBrowserItem(info):
|
||||||
# logger.log("thingRemoved for", thing.name)
|
# logger.log("executeBrowserItem called for thing", info.thing.name, "and item", info.itemId)
|
||||||
|
# info.finish(nymea.ThingErrorNoError)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper functions can be added too
|
||||||
|
def autoThings():
|
||||||
|
autoThings = []
|
||||||
|
for thing in myThings():
|
||||||
|
if thing.thingClassId == pyMockAutoThingClassId:
|
||||||
|
autoThings.append(thing)
|
||||||
|
return autoThings
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue