From d4081195d4de7b3318c5dc4849ce527642966802 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 15 Nov 2019 10:59:37 +0100 Subject: [PATCH] Some more work on the script engine --- libnymea-core/libnymea-core.pro | 6 + libnymea-core/nymeacore.cpp | 1 + libnymea-core/scriptengine/scriptaction.cpp | 64 +++++++++++ libnymea-core/scriptengine/scriptaction.h | 42 +++++++ libnymea-core/scriptengine/scriptengine.cpp | 10 ++ libnymea-core/scriptengine/scriptengine.h | 4 +- libnymea-core/scriptengine/scriptevent.cpp | 78 +++++++++++++ libnymea-core/scriptengine/scriptevent.h | 54 +++++++++ libnymea-core/scriptengine/scriptstate.cpp | 116 ++++++++++++++++++++ libnymea-core/scriptengine/scriptstate.h | 63 +++++++++++ libnymea/loggingcategories.cpp | 1 + libnymea/loggingcategories.h | 1 + tests/scripts/getconfigureddevices.sh | 4 +- tests/scripts/getstatetypes.sh | 8 +- 14 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 libnymea-core/scriptengine/scriptaction.cpp create mode 100644 libnymea-core/scriptengine/scriptaction.h create mode 100644 libnymea-core/scriptengine/scriptevent.cpp create mode 100644 libnymea-core/scriptengine/scriptevent.h create mode 100644 libnymea-core/scriptengine/scriptstate.cpp create mode 100644 libnymea-core/scriptengine/scriptstate.h diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index f915cba1..c28efe27 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -25,7 +25,10 @@ HEADERS += nymeacore.h \ ruleengine/stateevaluator.h \ ruleengine/ruleaction.h \ ruleengine/ruleactionparam.h \ + scriptengine/scriptaction.h \ scriptengine/scriptengine.h \ + scriptengine/scriptevent.h \ + scriptengine/scriptstate.h \ transportinterface.h \ nymeaconfiguration.h \ servermanager.h \ @@ -100,7 +103,10 @@ SOURCES += nymeacore.cpp \ ruleengine/stateevaluator.cpp \ ruleengine/ruleaction.cpp \ ruleengine/ruleactionparam.cpp \ + scriptengine/scriptaction.cpp \ scriptengine/scriptengine.cpp \ + scriptengine/scriptevent.cpp \ + scriptengine/scriptstate.cpp \ transportinterface.cpp \ nymeaconfiguration.cpp \ servermanager.cpp \ diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 264b40d1..bf289dc4 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -663,6 +663,7 @@ QStringList NymeaCore::loggingFilters() "DeviceManager", "RuleEngine", "RuleEngineDebug", + "ScriptEngine", "Hardware", "Bluetooth", "LogEngine", diff --git a/libnymea-core/scriptengine/scriptaction.cpp b/libnymea-core/scriptengine/scriptaction.cpp new file mode 100644 index 00000000..da88df82 --- /dev/null +++ b/libnymea-core/scriptengine/scriptaction.cpp @@ -0,0 +1,64 @@ +#include "scriptaction.h" + +#include "devices/devicemanager.h" +#include "types/action.h" + +#include + +namespace nymeaserver { + +ScriptAction::ScriptAction(QObject *parent) : QObject(parent) +{ + +} + +void ScriptAction::classBegin() +{ + m_deviceManager = reinterpret_cast(qmlEngine(this)->property("deviceManager").toULongLong()); +} + +void ScriptAction::componentComplete() +{ + +} + +QString ScriptAction::deviceId() const +{ + return m_deviceId; +} + +void ScriptAction::setDeviceId(const QString &deviceId) +{ + if (m_deviceId != deviceId) { + m_deviceId = deviceId; + emit deviceIdChanged(); + } +} + +QString ScriptAction::actionTypeId() const +{ + return m_actionTypeId; +} + +void ScriptAction::setActionTypeId(const QString &actionTypeId) +{ + if (m_actionTypeId != actionTypeId) { + m_actionTypeId = actionTypeId; + emit actionTypeIdChanged(); + } +} + +void ScriptAction::execute(const QVariantList ¶ms) +{ + Action action; + action.setActionTypeId(ActionTypeId(m_actionTypeId)); + action.setDeviceId(DeviceId(m_deviceId)); + ParamList paramList; + foreach (const QVariant &p, params) { + paramList << Param(ParamTypeId(p.toMap().value("paramTypeId").toUuid()), p.toMap().value("value")); + } + action.setParams(paramList); + m_deviceManager->executeAction(action); +} + +} diff --git a/libnymea-core/scriptengine/scriptaction.h b/libnymea-core/scriptengine/scriptaction.h new file mode 100644 index 00000000..98baab7e --- /dev/null +++ b/libnymea-core/scriptengine/scriptaction.h @@ -0,0 +1,42 @@ +#ifndef SCRIPTACTION_H +#define SCRIPTACTION_H + +#include +#include + +class DeviceManager; + +namespace nymeaserver { + +class ScriptAction : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) + Q_PROPERTY(QString actionTypeId READ actionTypeId WRITE setActionTypeId NOTIFY actionTypeIdChanged) +public: + explicit ScriptAction(QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + QString deviceId() const; + void setDeviceId(const QString &deviceId); + + QString actionTypeId() const; + void setActionTypeId(const QString &actionTypeId); + +public slots: + void execute(const QVariantList ¶ms); + +signals: + void deviceIdChanged(); + void actionTypeIdChanged(); + +public: + DeviceManager *m_deviceManager = nullptr; + QString m_deviceId; + QString m_actionTypeId; +}; + +} + +#endif // SCRIPTACTION_H diff --git a/libnymea-core/scriptengine/scriptengine.cpp b/libnymea-core/scriptengine/scriptengine.cpp index 5dd19745..4f994a98 100644 --- a/libnymea-core/scriptengine/scriptengine.cpp +++ b/libnymea-core/scriptengine/scriptengine.cpp @@ -1,6 +1,10 @@ #include "scriptengine.h" #include "devices/devicemanager.h" +#include "scriptaction.h" +#include "scriptevent.h" +#include "scriptstate.h" + #include namespace nymeaserver { @@ -8,13 +12,19 @@ namespace nymeaserver { ScriptEngine::ScriptEngine(DeviceManager *deviceManager, QObject *parent) : QObject(parent), m_deviceManager(deviceManager) { + qmlRegisterType("nymea", 1, 0, "Event"); + qmlRegisterType("nymea", 1, 0, "Action"); + qmlRegisterType("nymea", 1, 0, "State"); + loadScripts(); } void ScriptEngine::loadScripts() { QString fileName = "/home/micha/Develop/nymea/tests/script.qml"; + QQmlApplicationEngine *engine = new QQmlApplicationEngine(this); + engine->setProperty("deviceManager", reinterpret_cast(m_deviceManager)); engine->load(fileName); } diff --git a/libnymea-core/scriptengine/scriptengine.h b/libnymea-core/scriptengine/scriptengine.h index a5f72844..806eb4aa 100644 --- a/libnymea-core/scriptengine/scriptengine.h +++ b/libnymea-core/scriptengine/scriptengine.h @@ -2,8 +2,10 @@ #define SCRIPTENGINE_H #include +#include +#include -class DeviceManager; +#include "devices/devicemanager.h" namespace nymeaserver { diff --git a/libnymea-core/scriptengine/scriptevent.cpp b/libnymea-core/scriptengine/scriptevent.cpp new file mode 100644 index 00000000..cb9a8cb0 --- /dev/null +++ b/libnymea-core/scriptengine/scriptevent.cpp @@ -0,0 +1,78 @@ +#include "scriptevent.h" + +namespace nymeaserver { + +ScriptEvent::ScriptEvent(QObject *parent) : QObject(parent) +{ +} + +void ScriptEvent::classBegin() +{ + m_deviceManager = reinterpret_cast(qmlEngine(this)->property("deviceManager").toULongLong()); + connect(m_deviceManager, &DeviceManager::eventTriggered, this, &ScriptEvent::onEventTriggered); +} + +void ScriptEvent::componentComplete() +{ + +} + +QString ScriptEvent::deviceId() const +{ + return m_deviceId; +} + +void ScriptEvent::setDeviceId(const QString &deviceId) +{ + if (m_deviceId != deviceId) { + m_deviceId = deviceId; + emit deviceIdChanged(); + } +} + +QString ScriptEvent::eventTypeId() const +{ + return m_eventTypeId; +} + +void ScriptEvent::setEventTypeId(const QString &eventTypeId) +{ + if (m_eventTypeId != eventTypeId) { + m_eventTypeId = eventTypeId; + emit eventTypeIdChanged(); + } +} + +QString ScriptEvent::eventName() const +{ + return m_eventName; +} + +void ScriptEvent::setEventName(const QString &eventName) +{ + if (m_eventName != eventName) { + m_eventName = eventName; + emit eventNameChanged(); + } +} + +void ScriptEvent::onEventTriggered(const Event &event) +{ + if (QUuid(m_deviceId) != event.deviceId()) { + return; + } + + if (!m_eventTypeId.isEmpty() && event.eventTypeId() != m_eventTypeId) { + return; + } + + Device *device = m_deviceManager->findConfiguredDevice(event.deviceId()); + if (!m_eventName.isEmpty() && device->deviceClass().eventTypes().findByName(m_eventName).id() != event.eventTypeId()) { + return; + } + + emit triggered(); +} + +} + diff --git a/libnymea-core/scriptengine/scriptevent.h b/libnymea-core/scriptengine/scriptevent.h new file mode 100644 index 00000000..92266c9c --- /dev/null +++ b/libnymea-core/scriptengine/scriptevent.h @@ -0,0 +1,54 @@ +#ifndef EVENTLISTENER_H +#define EVENTLISTENER_H + +#include +#include +#include + +#include "types/event.h" +#include "devices/devicemanager.h" + +namespace nymeaserver { + +class ScriptEvent: public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) + Q_PROPERTY(QString eventTypeId READ eventTypeId WRITE setEventTypeId NOTIFY eventTypeIdChanged) + Q_PROPERTY(QString eventName READ eventName WRITE setEventName NOTIFY eventNameChanged) +public: + ScriptEvent(QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + QString deviceId() const; + void setDeviceId(const QString &deviceId); + + QString eventTypeId() const; + void setEventTypeId(const QString &eventTypeId); + + QString eventName() const; + void setEventName(const QString &eventName); + +private slots: + void onEventTriggered(const Event &event); + +signals: + void deviceIdChanged(); + void eventTypeIdChanged(); + void eventNameChanged(); + + void triggered(); + +private: + DeviceManager *m_deviceManager = nullptr; + + QString m_deviceId; + QString m_eventTypeId; + QString m_eventName; +}; + +} + +#endif // EVENTLISTENER_H diff --git a/libnymea-core/scriptengine/scriptstate.cpp b/libnymea-core/scriptengine/scriptstate.cpp new file mode 100644 index 00000000..b161ffd8 --- /dev/null +++ b/libnymea-core/scriptengine/scriptstate.cpp @@ -0,0 +1,116 @@ +#include "scriptstate.h" + +#include "loggingcategories.h" + +#include + +namespace nymeaserver { + +ScriptState::ScriptState(QObject *parent) : QObject(parent) +{ + +} + +void ScriptState::classBegin() +{ + m_deviceManager = reinterpret_cast(qmlEngine(this)->property("deviceManager").toULongLong()); + connect(m_deviceManager, &DeviceManager::deviceStateChanged, this, &ScriptState::onDeviceStateChanged); +} + +void ScriptState::componentComplete() +{ + +} + +QString ScriptState::deviceId() const +{ + return m_deviceId; +} + +void ScriptState::setDeviceId(const QString &deviceId) +{ + if (m_deviceId != deviceId) { + m_deviceId = deviceId; + emit deviceIdChanged(); + store(); + } +} + +QString ScriptState::stateTypeId() const +{ + return m_stateTypeId; +} + +void ScriptState::setStateTypeId(const QString &stateTypeId) +{ + if (m_stateTypeId != stateTypeId) { + m_stateTypeId = stateTypeId; + emit stateTypeIdChanged(); + store(); + } +} + +QVariant ScriptState::value() const +{ + Device* device = m_deviceManager->findConfiguredDevice(DeviceId(m_deviceId)); + if (!device) { + return QVariant(); + } + return device->stateValue(StateTypeId(m_stateTypeId)); +} + +void ScriptState::setValue(const QVariant &value) +{ + qCDebug(dcScriptEngine()) << "setValueCalled1" << value; + if (m_pendingActionInfo) { + m_valueCache = value; + return; + } + + Device* device = m_deviceManager->findConfiguredDevice(DeviceId(m_deviceId)); + if (!device) { + qCWarning(dcScriptEngine()) << "No device with id" << m_deviceId << "found."; + return; + } + + if (device->deviceClass().stateTypes().findById(StateTypeId(m_stateTypeId)).id().isNull()) { + qCWarning(dcScriptEngine) << "Device" << device->name() << "does not have a state with type id" << m_stateTypeId; + return; + } + Action action; + action.setDeviceId(DeviceId(m_deviceId)); + action.setActionTypeId(ActionTypeId(m_stateTypeId)); + ParamList params = ParamList() << Param(ParamTypeId(m_stateTypeId), value); + action.setParams(params); + + qCDebug(dcScriptEngine()) << "setValueCalled2" << value; + m_valueCache = QVariant(); + m_pendingActionInfo = m_deviceManager->executeAction(action); + connect(m_pendingActionInfo, &DeviceActionInfo::finished, this, [this](){ + m_pendingActionInfo = nullptr; + if (!m_valueCache.isNull()) { + setValue(m_valueCache); + } + }); +} + +void ScriptState::store() +{ + m_valueStore = value(); + qCDebug(dcScriptEngine()) << "Storing value:" << m_valueStore; +} + +void ScriptState::restore() +{ + qCDebug(dcScriptEngine()) << "Restoring value:" << m_valueStore << m_valueStore.value().toRgb(); + setValue(m_valueStore); +} + +void nymeaserver::ScriptState::onDeviceStateChanged(Device *device, const StateTypeId &stateTypeId) +{ + if (device->id() == DeviceId(m_deviceId) && stateTypeId == StateTypeId(m_stateTypeId)) { + emit valueChanged(); + } +} + +} diff --git a/libnymea-core/scriptengine/scriptstate.h b/libnymea-core/scriptengine/scriptstate.h new file mode 100644 index 00000000..30bd28a6 --- /dev/null +++ b/libnymea-core/scriptengine/scriptstate.h @@ -0,0 +1,63 @@ +#ifndef SCRIPTSTATE_H +#define SCRIPTSTATE_H + +#include +#include +#include + +#include "devices/devicemanager.h" +#include "devices/deviceactioninfo.h" + +namespace nymeaserver { + +class ScriptState : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) + Q_PROPERTY(QString stateTypeId READ stateTypeId WRITE setStateTypeId NOTIFY stateTypeIdChanged) + Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit ScriptState(QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + QString deviceId() const; + void setDeviceId(const QString &deviceId); + + QString stateTypeId() const; + void setStateTypeId(const QString &stateTypeId); + + QVariant value() const; + void setValue(const QVariant &value); + +public slots: + void store(); + void restore(); + +signals: + void deviceIdChanged(); + void stateTypeIdChanged(); + void stateNameChanged(); + void valueChanged(); + +private slots: + void onDeviceStateChanged(Device *device, const StateTypeId &stateTypeId); + +private: + DeviceManager *m_deviceManager = nullptr; + + QString m_deviceId; + QString m_stateTypeId; + + DeviceActionInfo *m_pendingActionInfo = nullptr; + QVariant m_valueCache; + + QVariant m_valueStore; +}; + +} + +#endif // SCRIPTSTATE_H diff --git a/libnymea/loggingcategories.cpp b/libnymea/loggingcategories.cpp index 4b8cc66e..e34f511f 100644 --- a/libnymea/loggingcategories.cpp +++ b/libnymea/loggingcategories.cpp @@ -34,6 +34,7 @@ Q_LOGGING_CATEGORY(dcExperiences, "Experiences") Q_LOGGING_CATEGORY(dcTimeManager, "TimeManager") Q_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine") Q_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug") +Q_LOGGING_CATEGORY(dcScriptEngine, "ScriptEngine") Q_LOGGING_CATEGORY(dcHardware, "Hardware") Q_LOGGING_CATEGORY(dcLogEngine, "LogEngine") Q_LOGGING_CATEGORY(dcServerManager, "ServerManager") diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index fec359bc..a06ed85f 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -39,6 +39,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcExperiences) Q_DECLARE_LOGGING_CATEGORY(dcTimeManager) Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine) Q_DECLARE_LOGGING_CATEGORY(dcRuleEngineDebug) +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) Q_DECLARE_LOGGING_CATEGORY(dcHardware) Q_DECLARE_LOGGING_CATEGORY(dcLogEngine) Q_DECLARE_LOGGING_CATEGORY(dcServerManager) diff --git a/tests/scripts/getconfigureddevices.sh b/tests/scripts/getconfigureddevices.sh index 1d6a39dd..cbd8d221 100755 --- a/tests/scripts/getconfigureddevices.sh +++ b/tests/scripts/getconfigureddevices.sh @@ -6,14 +6,14 @@ if [ -z $1 ]; then fi if [ -z $2 ]; then -cat <