From 22e75b08df22eebc24e5c662dbd5cfd402268c6f Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 25 Sep 2022 00:58:24 +0200 Subject: [PATCH] Add Thing and Things types to script engine --- .../thingmanagerimplementation.cpp | 7 +- libnymea-core/jsonrpc/scriptshandler.cpp | 4 +- libnymea-core/jsonrpc/scriptshandler.h | 4 +- libnymea-core/libnymea-core.pro | 4 + libnymea-core/nymeacore.cpp | 2 +- libnymea-core/nymeacore.h | 6 +- libnymea-core/scriptengine/script.cpp | 5 + libnymea-core/scriptengine/script.h | 7 +- libnymea-core/scriptengine/scriptaction.cpp | 5 +- libnymea-core/scriptengine/scriptaction.h | 2 + libnymea-core/scriptengine/scriptalarm.cpp | 9 +- libnymea-core/scriptengine/scriptalarm.h | 5 + libnymea-core/scriptengine/scriptengine.cpp | 8 + libnymea-core/scriptengine/scriptengine.h | 2 + libnymea-core/scriptengine/scriptevent.cpp | 3 +- libnymea-core/scriptengine/scriptevent.h | 2 + .../scriptengine/scriptinterfaceaction.cpp | 5 +- .../scriptengine/scriptinterfaceaction.h | 2 + .../scriptengine/scriptinterfaceevent.cpp | 3 +- .../scriptengine/scriptinterfaceevent.h | 2 + .../scriptengine/scriptinterfacestate.cpp | 3 +- .../scriptengine/scriptinterfacestate.h | 2 + libnymea-core/scriptengine/scriptstate.cpp | 12 +- libnymea-core/scriptengine/scriptstate.h | 4 + libnymea-core/scriptengine/scriptthing.cpp | 195 ++++++++++++++++++ libnymea-core/scriptengine/scriptthing.h | 84 ++++++++ .../scriptengine/scriptthingmanager.cpp | 22 ++ .../scriptengine/scriptthingmanager.h | 31 +++ libnymea-core/scriptengine/scriptthings.cpp | 162 +++++++++++++++ libnymea-core/scriptengine/scriptthings.h | 110 ++++++++++ .../scriptengine/scriptthingsfilter.cpp | 7 + .../scriptengine/scriptthingsfilter.h | 17 ++ libnymea/loggingcategories.cpp | 1 - libnymea/loggingcategories.h | 1 - tests/auto/scripts/testhelper.cpp | 5 + tests/auto/scripts/testhelper.h | 5 + tests/auto/scripts/testscripts.cpp | 181 ++++++++++++++++ 37 files changed, 904 insertions(+), 25 deletions(-) create mode 100644 libnymea-core/scriptengine/scriptthing.cpp create mode 100644 libnymea-core/scriptengine/scriptthing.h create mode 100644 libnymea-core/scriptengine/scriptthingmanager.cpp create mode 100644 libnymea-core/scriptengine/scriptthingmanager.h create mode 100644 libnymea-core/scriptengine/scriptthings.cpp create mode 100644 libnymea-core/scriptengine/scriptthings.h create mode 100644 libnymea-core/scriptengine/scriptthingsfilter.cpp create mode 100644 libnymea-core/scriptengine/scriptthingsfilter.h diff --git a/libnymea-core/integrations/thingmanagerimplementation.cpp b/libnymea-core/integrations/thingmanagerimplementation.cpp index 537c1703..c869929a 100644 --- a/libnymea-core/integrations/thingmanagerimplementation.cpp +++ b/libnymea-core/integrations/thingmanagerimplementation.cpp @@ -1235,12 +1235,7 @@ Vendor ThingManagerImplementation::translateVendor(const Vendor &vendor, const Q Thing *ThingManagerImplementation::findConfiguredThing(const ThingId &id) const { - foreach (Thing *thing, m_configuredThings) { - if (thing->id() == id) { - return thing; - } - } - return nullptr; + return m_configuredThings.value(id); } Things ThingManagerImplementation::configuredThings() const diff --git a/libnymea-core/jsonrpc/scriptshandler.cpp b/libnymea-core/jsonrpc/scriptshandler.cpp index 05c5bde7..79469f99 100644 --- a/libnymea-core/jsonrpc/scriptshandler.cpp +++ b/libnymea-core/jsonrpc/scriptshandler.cpp @@ -36,6 +36,8 @@ namespace nymeaserver { +using namespace scriptengine; + ScriptsHandler::ScriptsHandler(ScriptEngine *scriptEngine, QObject *parent): JsonHandler(parent), m_engine(scriptEngine) @@ -43,7 +45,7 @@ ScriptsHandler::ScriptsHandler(ScriptEngine *scriptEngine, QObject *parent): registerEnum(); registerEnum(); - registerObject(); + registerObject(); QVariantMap params, returns; QString description; diff --git a/libnymea-core/jsonrpc/scriptshandler.h b/libnymea-core/jsonrpc/scriptshandler.h index cf48b50e..e4893ff7 100644 --- a/libnymea-core/jsonrpc/scriptshandler.h +++ b/libnymea-core/jsonrpc/scriptshandler.h @@ -42,7 +42,7 @@ class ScriptsHandler : public JsonHandler { Q_OBJECT public: - explicit ScriptsHandler(ScriptEngine *scriptEngine, QObject *parent = nullptr); + explicit ScriptsHandler(scriptengine::ScriptEngine *scriptEngine, QObject *parent = nullptr); QString name() const override; @@ -61,7 +61,7 @@ signals: void ScriptLogMessage(const QVariantMap ¶ms); private: - ScriptEngine *m_engine = nullptr; + scriptengine::ScriptEngine *m_engine = nullptr; }; } diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index 6d73c17f..ab92a509 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -61,6 +61,8 @@ HEADERS += nymeacore.h \ hardware/network/macaddressdatabasereplyimpl.h \ hardware/serialport/serialportmonitor.h \ hardware/zwave/zwavehardwareresourceimplementation.h \ + scriptengine/scriptthing.h \ + scriptengine/scriptthings.h \ zwave/zwavedevicedatabase.h \ zwave/zwavemanagerreply.h \ zwave/zwavenodeimplementation.h \ @@ -174,6 +176,8 @@ SOURCES += nymeacore.cpp \ hardware/network/macaddressdatabasereplyimpl.cpp \ hardware/serialport/serialportmonitor.cpp \ hardware/zwave/zwavehardwareresourceimplementation.cpp \ + scriptengine/scriptthing.cpp \ + scriptengine/scriptthings.cpp \ zwave/zwavedevicedatabase.cpp \ zwave/zwavemanagerreply.cpp \ zwave/zwavenodeimplementation.cpp \ diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index e6075be6..c8fe4316 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -135,7 +135,7 @@ void NymeaCore::init(const QStringList &additionalInterfaces) { m_logger->setThingManager(m_thingManager); qCDebug(dcCore()) << "Creating Script Engine"; - m_scriptEngine = new ScriptEngine(m_thingManager, this); + m_scriptEngine = new scriptengine::ScriptEngine(m_thingManager, this); m_serverManager->jsonServer()->registerHandler(new ScriptsHandler(m_scriptEngine, m_scriptEngine)); qCDebug(dcCore()) << "Creating Tags Storage"; diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h index 871050a5..7b421223 100644 --- a/libnymea-core/nymeacore.h +++ b/libnymea-core/nymeacore.h @@ -64,13 +64,17 @@ class UserManager; class Platform; class System; class ExperienceManager; -class ScriptEngine; class CloudManager; class ZigbeeManager; class ZWaveManager; class ModbusRtuManager; class SerialPortMonitor; +namespace scriptengine { +class ScriptEngine; +} +using namespace scriptengine; + class NymeaCore : public QObject { Q_OBJECT diff --git a/libnymea-core/scriptengine/script.cpp b/libnymea-core/scriptengine/script.cpp index f82c3034..67a78a3c 100644 --- a/libnymea-core/scriptengine/script.cpp +++ b/libnymea-core/scriptengine/script.cpp @@ -30,7 +30,11 @@ #include "script.h" +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) + namespace nymeaserver { +namespace scriptengine { Script::Script() { @@ -79,3 +83,4 @@ void Scripts::put(const QVariant &value) } } +} diff --git a/libnymea-core/scriptengine/script.h b/libnymea-core/scriptengine/script.h index 7372bd98..baee7b50 100644 --- a/libnymea-core/scriptengine/script.h +++ b/libnymea-core/scriptengine/script.h @@ -38,6 +38,7 @@ #include namespace nymeaserver { +namespace scriptengine { class Script { @@ -75,9 +76,9 @@ public: Q_INVOKABLE QVariant get(int index); Q_INVOKABLE void put(const QVariant &value); }; - } -Q_DECLARE_METATYPE(nymeaserver::Script) -Q_DECLARE_METATYPE(nymeaserver::Scripts) +} +Q_DECLARE_METATYPE(nymeaserver::scriptengine::Script) +Q_DECLARE_METATYPE(nymeaserver::scriptengine::Scripts) #endif // SCRIPT_H diff --git a/libnymea-core/scriptengine/scriptaction.cpp b/libnymea-core/scriptengine/scriptaction.cpp index 6d599d27..b7687e5d 100644 --- a/libnymea-core/scriptengine/scriptaction.cpp +++ b/libnymea-core/scriptengine/scriptaction.cpp @@ -36,9 +36,11 @@ #include #include -#include "loggingcategories.h" +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) namespace nymeaserver { +namespace scriptengine { ScriptAction::ScriptAction(QObject *parent) : QObject(parent) { @@ -159,3 +161,4 @@ void ScriptAction::execute(const QVariantMap ¶ms) } } +} diff --git a/libnymea-core/scriptengine/scriptaction.h b/libnymea-core/scriptengine/scriptaction.h index 3786b290..5c663504 100644 --- a/libnymea-core/scriptengine/scriptaction.h +++ b/libnymea-core/scriptengine/scriptaction.h @@ -38,6 +38,7 @@ class ThingManager; namespace nymeaserver { +namespace scriptengine { class ScriptAction : public QObject, public QQmlParserStatus { @@ -82,6 +83,7 @@ public: QString m_actionName; }; +} } #endif // SCRIPTACTION_H diff --git a/libnymea-core/scriptengine/scriptalarm.cpp b/libnymea-core/scriptengine/scriptalarm.cpp index c0674deb..56f0c492 100644 --- a/libnymea-core/scriptengine/scriptalarm.cpp +++ b/libnymea-core/scriptengine/scriptalarm.cpp @@ -29,9 +29,13 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "scriptalarm.h" -#include "loggingcategories.h" #include +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) + +namespace nymeaserver { +namespace scriptengine { ScriptAlarm::ScriptAlarm(QObject *parent) : QObject(parent) { @@ -151,3 +155,6 @@ void ScriptAlarm::updateActive() emit activeChanged(); } } + +} +} diff --git a/libnymea-core/scriptengine/scriptalarm.h b/libnymea-core/scriptengine/scriptalarm.h index a69d162d..3c10dbe6 100644 --- a/libnymea-core/scriptengine/scriptalarm.h +++ b/libnymea-core/scriptengine/scriptalarm.h @@ -34,6 +34,8 @@ #include #include #include +namespace nymeaserver { +namespace scriptengine { class ScriptAlarm : public QObject { @@ -97,4 +99,7 @@ private: int m_timerId = 0; }; +} +} + #endif // SCRIPTALARM_H diff --git a/libnymea-core/scriptengine/scriptengine.cpp b/libnymea-core/scriptengine/scriptengine.cpp index 902e198d..64f34bcc 100644 --- a/libnymea-core/scriptengine/scriptengine.cpp +++ b/libnymea-core/scriptengine/scriptengine.cpp @@ -38,6 +38,8 @@ #include "scriptinterfaceaction.h" #include "scriptinterfacestate.h" #include "scriptinterfaceevent.h" +#include "scriptthing.h" +#include "scriptthings.h" #include "nymeasettings.h" @@ -51,7 +53,10 @@ #include +NYMEA_LOGGING_CATEGORY(dcScriptEngine, "ScriptEngine") + namespace nymeaserver { +namespace scriptengine { QList ScriptEngine::s_engines; QtMessageHandler ScriptEngine::s_upstreamMessageHandler; @@ -68,6 +73,8 @@ ScriptEngine::ScriptEngine(ThingManager *thingManager, QObject *parent) : QObjec qmlRegisterType("nymea", 1, 0, "InterfaceState"); qmlRegisterType("nymea", 1, 0, "InterfaceEvent"); qmlRegisterType("nymea", 1, 0, "Alarm"); + qmlRegisterType("nymea", 1, 0, "Thing"); + qmlRegisterType("nymea", 1, 0, "Things"); m_engine = new QQmlEngine(this); m_engine->setProperty("thingManager", reinterpret_cast(m_thingManager)); @@ -486,3 +493,4 @@ void ScriptEngine::logCategoryFilter(QLoggingCategory *category) } } +} diff --git a/libnymea-core/scriptengine/scriptengine.h b/libnymea-core/scriptengine/scriptengine.h index 8e3017d1..6754baeb 100644 --- a/libnymea-core/scriptengine/scriptengine.h +++ b/libnymea-core/scriptengine/scriptengine.h @@ -42,6 +42,7 @@ #include "script.h" namespace nymeaserver { +namespace scriptengine { class ScriptEngine : public QObject { @@ -115,6 +116,7 @@ private: static QMutex s_loggerMutex; }; +} } #endif // SCRIPTENGINE_H diff --git a/libnymea-core/scriptengine/scriptevent.cpp b/libnymea-core/scriptengine/scriptevent.cpp index dd27d5cd..5c4b004c 100644 --- a/libnymea-core/scriptengine/scriptevent.cpp +++ b/libnymea-core/scriptengine/scriptevent.cpp @@ -35,6 +35,7 @@ #include namespace nymeaserver { +namespace scriptengine { ScriptEvent::ScriptEvent(QObject *parent) : QObject(parent) { @@ -117,4 +118,4 @@ void ScriptEvent::onEventTriggered(const Event &event) } } - +} diff --git a/libnymea-core/scriptengine/scriptevent.h b/libnymea-core/scriptengine/scriptevent.h index da2aba46..75014d6b 100644 --- a/libnymea-core/scriptengine/scriptevent.h +++ b/libnymea-core/scriptengine/scriptevent.h @@ -39,6 +39,7 @@ #include "integrations/thingmanager.h" namespace nymeaserver { +namespace scriptengine { class ScriptParams; @@ -82,6 +83,7 @@ private: QString m_eventName; }; +} } #endif // SCRIPTEVENT_H diff --git a/libnymea-core/scriptengine/scriptinterfaceaction.cpp b/libnymea-core/scriptengine/scriptinterfaceaction.cpp index b69bc6fb..57961b05 100644 --- a/libnymea-core/scriptengine/scriptinterfaceaction.cpp +++ b/libnymea-core/scriptengine/scriptinterfaceaction.cpp @@ -36,9 +36,11 @@ #include #include -#include "loggingcategories.h" +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) namespace nymeaserver { +namespace scriptengine { ScriptInterfaceAction::ScriptInterfaceAction(QObject *parent) : QObject(parent) { @@ -124,3 +126,4 @@ void ScriptInterfaceAction::execute(const QVariantMap ¶ms) } } +} diff --git a/libnymea-core/scriptengine/scriptinterfaceaction.h b/libnymea-core/scriptengine/scriptinterfaceaction.h index de2e0633..52314579 100644 --- a/libnymea-core/scriptengine/scriptinterfaceaction.h +++ b/libnymea-core/scriptengine/scriptinterfaceaction.h @@ -37,6 +37,7 @@ class ThingManager; namespace nymeaserver { +namespace scriptengine { class ScriptInterfaceAction : public QObject, public QQmlParserStatus { @@ -68,6 +69,7 @@ public: QString m_actionName; }; +} } #endif // SCRIPTINTERFACEACTION_H diff --git a/libnymea-core/scriptengine/scriptinterfaceevent.cpp b/libnymea-core/scriptengine/scriptinterfaceevent.cpp index 72b820c8..c0c85b7c 100644 --- a/libnymea-core/scriptengine/scriptinterfaceevent.cpp +++ b/libnymea-core/scriptengine/scriptinterfaceevent.cpp @@ -35,6 +35,7 @@ #include namespace nymeaserver { +namespace scriptengine { ScriptInterfaceEvent::ScriptInterfaceEvent(QObject *parent) : QObject(parent) { @@ -100,4 +101,4 @@ void ScriptInterfaceEvent::onEventTriggered(const Event &event) } } - +} diff --git a/libnymea-core/scriptengine/scriptinterfaceevent.h b/libnymea-core/scriptengine/scriptinterfaceevent.h index 1271245e..b5777a61 100644 --- a/libnymea-core/scriptengine/scriptinterfaceevent.h +++ b/libnymea-core/scriptengine/scriptinterfaceevent.h @@ -39,6 +39,7 @@ #include "integrations/thingmanager.h" namespace nymeaserver { +namespace scriptengine { class ScriptParams; @@ -75,6 +76,7 @@ private: QString m_eventName; }; +} } #endif // SCRIPTINTERFACEEVENT_H diff --git a/libnymea-core/scriptengine/scriptinterfacestate.cpp b/libnymea-core/scriptengine/scriptinterfacestate.cpp index 9b5ace72..43f8ae27 100644 --- a/libnymea-core/scriptengine/scriptinterfacestate.cpp +++ b/libnymea-core/scriptengine/scriptinterfacestate.cpp @@ -35,6 +35,7 @@ #include namespace nymeaserver { +namespace scriptengine { ScriptInterfaceState::ScriptInterfaceState(QObject *parent) : QObject(parent) { @@ -91,4 +92,4 @@ void ScriptInterfaceState::onStateChanged(Thing *thing, const StateTypeId &state } } - +} diff --git a/libnymea-core/scriptengine/scriptinterfacestate.h b/libnymea-core/scriptengine/scriptinterfacestate.h index 923b445c..14f302b1 100644 --- a/libnymea-core/scriptengine/scriptinterfacestate.h +++ b/libnymea-core/scriptengine/scriptinterfacestate.h @@ -39,6 +39,7 @@ #include "integrations/thingmanager.h" namespace nymeaserver { +namespace scriptengine { class ScriptParams; @@ -75,6 +76,7 @@ private: QString m_stateName; }; +} } #endif // SCRIPTINTERFACESTATE_H diff --git a/libnymea-core/scriptengine/scriptstate.cpp b/libnymea-core/scriptengine/scriptstate.cpp index f3445e1e..c3136995 100644 --- a/libnymea-core/scriptengine/scriptstate.cpp +++ b/libnymea-core/scriptengine/scriptstate.cpp @@ -30,13 +30,15 @@ #include "scriptstate.h" -#include "loggingcategories.h" - #include #include #include +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) + namespace nymeaserver { +namespace scriptengine { ScriptState::ScriptState(QObject *parent) : QObject(parent) { @@ -238,6 +240,9 @@ void ScriptState::onThingStateChanged(Thing *thing, const StateTypeId &stateType void ScriptState::connectToThing() { + if (m_connection) { + disconnect(m_connection); + } Thing *thing = m_thingManager->findConfiguredThing(ThingId(m_thingId)); if (!thing) { qCDebug(dcScriptEngine()) << "Can't find thing with id" << m_thingId << "(yet)"; @@ -252,7 +257,7 @@ void ScriptState::connectToThing() qCDebug(dcScriptEngine()) << "Thing setup for" << thing->name() << "not complete yet"; } - connect(thing, &Thing::setupStatusChanged, this, [this, thing](){ + m_connection = connect(thing, &Thing::setupStatusChanged, this, [this, thing](){ if (thing->setupStatus() == Thing::ThingSetupStatusComplete) { qCDebug(dcScriptEngine()) << "Thing setup for" << thing->name() << "completed"; if (!m_valueCache.isNull()) { @@ -263,3 +268,4 @@ void ScriptState::connectToThing() } } +} diff --git a/libnymea-core/scriptengine/scriptstate.h b/libnymea-core/scriptengine/scriptstate.h index 2913504a..a6f6b1e0 100644 --- a/libnymea-core/scriptengine/scriptstate.h +++ b/libnymea-core/scriptengine/scriptstate.h @@ -39,6 +39,7 @@ #include "integrations/thingactioninfo.h" namespace nymeaserver { +namespace scriptengine { class ScriptState : public QObject, public QQmlParserStatus { @@ -98,8 +99,11 @@ private: QVariant m_valueCache; QVariant m_valueStore; + + QMetaObject::Connection m_connection; }; +} } #endif // SCRIPTSTATE_H diff --git a/libnymea-core/scriptengine/scriptthing.cpp b/libnymea-core/scriptengine/scriptthing.cpp new file mode 100644 index 00000000..0d5edfe6 --- /dev/null +++ b/libnymea-core/scriptengine/scriptthing.cpp @@ -0,0 +1,195 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2022, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "scriptthing.h" + +#include +#include +#include + +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) + +namespace nymeaserver { +namespace scriptengine { + +ScriptThing::ScriptThing(QObject *parent) + : QObject{parent} +{ + +} + +ScriptThing::ScriptThing(ThingManager *thingManager, QObject *parent) + : QObject{parent} +{ + init(thingManager); +} + +void ScriptThing::classBegin() +{ + init(reinterpret_cast(qmlEngine(this)->property("thingManager").toULongLong())); +} + +void ScriptThing::componentComplete() +{ + +} + +QString ScriptThing::thingId() const +{ + return m_thingId.toString(); +} + +void ScriptThing::setThingId(const QString &thingId) +{ + if (m_thingId != ThingId(thingId)) { + m_thingId = ThingId(thingId); + emit thingIdChanged(); + emit nameChanged(); + connectToThing(); + } +} + +QString ScriptThing::name() const +{ + Thing *thing = m_thingManager->findConfiguredThing(m_thingId); + if (!thing) { + return QString(); + } + return thing->name(); +} + +QVariant ScriptThing::stateValue(const QString &stateName) const +{ + Thing *thing = m_thingManager->findConfiguredThing(m_thingId); + if (!thing) { + return QVariant(); + } + return thing->stateValue(stateName); +} + +void ScriptThing::setStateValue(const QString &stateName, const QVariant &value) +{ + executeAction(stateName, {{stateName, value}}); +} + +void ScriptThing::executeAction(const QString &actionName, const QVariantMap ¶ms) +{ + Thing *thing = m_thingManager->findConfiguredThing(m_thingId); + if (!thing) { + return; + } + + ActionType actionType = thing->thingClass().actionTypes().findByName(actionName); + if (actionType.id().isNull()) { // Try to find by id for now, for compatiblity sake + actionType = thing->thingClass().actionTypes().findById(QUuid(actionName)); + } + if (actionType.id().isNull()) { + qCWarning(dcScriptEngine()) << "Thing" << thing->name() << "does not have action" << actionName; + return; + } + + Action action(actionType.id(), thing->id(), Action::TriggeredByScript); + ParamList paramList; + foreach (const QString ¶mNameOrId, params.keys()) { + ParamType paramType; + if (!ParamTypeId(paramNameOrId).isNull()) { + paramType = actionType.paramTypes().findById(ParamTypeId(paramNameOrId)); + } else { + paramType = actionType.paramTypes().findByName(paramNameOrId); + } + if (paramType.id().isNull()) { + qCWarning(dcScriptEngine()) << "Invalid param id or name"; + continue; + } + paramList << Param(paramType.id(), params.value(paramNameOrId)); + } + action.setParams(paramList); + qCDebug(dcScriptEngine()) << "Executing action:" << action.thingId().toString() << action.actionTypeId().toString() << action.params(); + m_thingManager->executeAction(action); + +} + +void ScriptThing::init(ThingManager *thingManager) +{ + m_thingManager = thingManager; + connect(m_thingManager, &ThingManager::thingAdded, this, [this](Thing *newThing){ + if (newThing->id() == m_thingId) { + qCDebug(dcScriptEngine()) << "Thing" << newThing->name() << "appeared in system"; + connectToThing(); + } + }); + connect(m_thingManager, &ThingManager::thingStateChanged, this, [=](Thing *thing, const StateTypeId &stateTypeId, const QVariant &value, const QVariant &minValue, const QVariant &maxValue){ + Q_UNUSED(minValue) + Q_UNUSED(maxValue) + if (m_thingId != thing->id()) { + return; + } + emit stateValueChanged(thing->thingClass().getStateType(stateTypeId).name(), value); + }); + connect(m_thingManager, &ThingManager::eventTriggered, this, [=](const Event &event){ + if (m_thingId != event.thingId()) { + return; + } + + Thing *thing = m_thingManager->findConfiguredThing(event.thingId()); + QVariantMap params; + foreach (const Param ¶m, event.params()) { + params.insert(param.paramTypeId().toString().remove(QRegExp("[{}]")), param.value().toByteArray()); + QString paramName = thing->thingClass().eventTypes().findById(event.eventTypeId()).paramTypes().findById(param.paramTypeId()).name(); + params.insert(paramName, param.value().toByteArray()); + } + + // Note: Explicitly convert the params to a Json document because auto-casting from QVariantMap to the JS engine might drop some values. + emit eventTriggered(thing->thingClass().eventTypes().findById(event.eventTypeId()).name(), QJsonDocument::fromVariant(params).toVariant().toMap()); + }); +} + +void ScriptThing::connectToThing() +{ + disconnect(m_nameConnection); + + Thing *thing = m_thingManager->findConfiguredThing(m_thingId); + if (!thing) { + qCDebug(dcScriptEngine()) << "Can't find thing with id" << m_thingId.toString() << "(yet)"; + return; + } + + + m_nameConnection = connect(thing, &Thing::nameChanged, this, [this, thing](){ + if (thing->setupStatus() == Thing::ThingSetupStatusComplete) { + qCDebug(dcScriptEngine()) << "Thing setup for" << thing->name() << "completed"; + emit nameChanged(); + } + }); +} + +} +} diff --git a/libnymea-core/scriptengine/scriptthing.h b/libnymea-core/scriptengine/scriptthing.h new file mode 100644 index 00000000..2f1ef79c --- /dev/null +++ b/libnymea-core/scriptengine/scriptthing.h @@ -0,0 +1,84 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2022, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef SCRIPTTHING_H +#define SCRIPTTHING_H + +#include +#include +#include +#include "integrations/thingmanager.h" + +namespace nymeaserver { +namespace scriptengine { + +class ScriptThing : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QString thingId READ thingId WRITE setThingId NOTIFY thingIdChanged) + Q_PROPERTY(QString name READ name NOTIFY nameChanged) +public: + explicit ScriptThing(QObject *parent = nullptr); + explicit ScriptThing(ThingManager *thingManager, QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + QString thingId() const; + void setThingId(const QString &thingId); + + QString name() const; + + Q_INVOKABLE QVariant stateValue(const QString &stateName) const; + + Q_INVOKABLE void setStateValue(const QString &stateName, const QVariant &value); + Q_INVOKABLE void executeAction(const QString &actionName, const QVariantMap ¶ms); + +signals: + void thingIdChanged(); + void nameChanged(); + + void stateValueChanged(const QString &stateName, const QVariant &value); + void eventTriggered(const QString &eventName, const QVariantMap ¶ms); + +private slots: + void init(ThingManager *thingManager); + void connectToThing(); + +private: + ThingId m_thingId; + ThingManager *m_thingManager = nullptr; + + QMetaObject::Connection m_nameConnection; +}; + +} +} + +#endif // SCRIPTTHING_H diff --git a/libnymea-core/scriptengine/scriptthingmanager.cpp b/libnymea-core/scriptengine/scriptthingmanager.cpp new file mode 100644 index 00000000..7f3e972a --- /dev/null +++ b/libnymea-core/scriptengine/scriptthingmanager.cpp @@ -0,0 +1,22 @@ +#include "scriptthingmanager.h" + +#include +#include + +namespace nymeaserver { +namespace scriptengine { + +ScriptThingManager::ScriptThingManager(QObject *parent) + : QObject{parent} +{ + +} + +void ScriptThingManager::classBegin() +{ + m_thingManager = reinterpret_cast(qmlEngine(this)->property("thingManager").toULongLong()); + +} + +} +} diff --git a/libnymea-core/scriptengine/scriptthingmanager.h b/libnymea-core/scriptengine/scriptthingmanager.h new file mode 100644 index 00000000..26604ed5 --- /dev/null +++ b/libnymea-core/scriptengine/scriptthingmanager.h @@ -0,0 +1,31 @@ +#ifndef SCRIPTTHINGMANAGER_H +#define SCRIPTTHINGMANAGER_H + +#include "integrations/thingmanager.h" + +#include +#include + +namespace nymeaserver { +namespace scriptengine { + +class ScriptThingManager : public QObject, public QQmlParserStatus +{ + Q_OBJECT +public: + explicit ScriptThingManager(QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + + +signals: + +private: + ThingManager *m_thingManager = nullptr; +}; + +} +} + +#endif // SCRIPTTHINGMANAGER_H diff --git a/libnymea-core/scriptengine/scriptthings.cpp b/libnymea-core/scriptengine/scriptthings.cpp new file mode 100644 index 00000000..7879eb15 --- /dev/null +++ b/libnymea-core/scriptengine/scriptthings.cpp @@ -0,0 +1,162 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2022, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "scriptthings.h" +#include "scriptthing.h" + +#include +#include + +#include +Q_DECLARE_LOGGING_CATEGORY(dcScriptEngine) + +namespace nymeaserver { +namespace scriptengine { + +ScriptThings::ScriptThings(QObject *parent) + : QSortFilterProxyModel{parent} +{ + +} + +void ScriptThings::classBegin() +{ + m_thingManager = reinterpret_cast(qmlEngine(this)->property("thingManager").toULongLong()); + m_model = new ThingsModel(m_thingManager, this); + setSourceModel(m_model); + + connect(m_thingManager, &ThingManager::thingAdded, this, [this](Thing *newThing){ + emit thingAdded(newThing->id().toString()); + emit countChanged(); + }); + connect(m_thingManager, &ThingManager::thingRemoved, this, [this](const ThingId &thingId){ + emit thingRemoved(thingId.toString()); + emit countChanged(); + }); +} + +void ScriptThings::componentComplete() +{ + +} + +QString ScriptThings::filterInterface() const +{ + return m_filterInterface; +} + +void ScriptThings::setFilterInterface(const QString &filterInterface) +{ + if (m_filterInterface != filterInterface) { + m_filterInterface = filterInterface; + emit filterInterfaceChanged(); + invalidateFilter(); + } +} + +ScriptThing *ScriptThings::get(int index) const +{ + Thing *thing = m_model->get(mapToSource(this->index(index, 0)).row()); + if (!thing) { + return nullptr; + } + ScriptThing *scriptThing = new ScriptThing(m_thingManager); + QQmlEngine::setObjectOwnership(scriptThing, QQmlEngine::JavaScriptOwnership); + scriptThing->setThingId(thing->id().toString()); + return scriptThing; + +} + +ScriptThing *ScriptThings::getThing(const QUuid &thingId) const +{ + Thing *thing = m_model->getThing(thingId); + if (!thing) { + return nullptr; + } + ScriptThing *scriptThing = new ScriptThing(m_thingManager); + QQmlEngine::setObjectOwnership(scriptThing, QQmlEngine::JavaScriptOwnership); + scriptThing->setThingId(thing->id().toString()); + return scriptThing; +} + +bool ScriptThings::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + Q_UNUSED(sourceParent) + Thing *thing = m_model->get(sourceRow); + if (!m_filterInterface.isEmpty() && !thing->thingClass().interfaces().contains(m_filterInterface)) { + return false; + } + + return true; +} + +ThingsModel::ThingsModel(ThingManager *thingManager, QObject *parent): + QAbstractListModel(parent), + m_thingManager(thingManager) +{ +} + +int ThingsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_thingManager->configuredThings().count(); +} + +QVariant ThingsModel::data(const QModelIndex &index, int role) const +{ + switch (role) { + case RoleId: + return m_thingManager->configuredThings().at(index.row())->id(); + case RoleName: + return m_thingManager->configuredThings().at(index.row())->name(); + } + return QVariant(); +} + +QHash ThingsModel::roleNames() const +{ + return { + {RoleId, "thingId"}, + {RoleName, "thingName"} + }; +} + +Thing *ThingsModel::get(int index) const +{ + return m_thingManager->configuredThings().at(index); +} + +Thing *ThingsModel::getThing(const QUuid &thingId) const +{ + return m_thingManager->findConfiguredThing(thingId); +} + +} +} diff --git a/libnymea-core/scriptengine/scriptthings.h b/libnymea-core/scriptengine/scriptthings.h new file mode 100644 index 00000000..c26ed66b --- /dev/null +++ b/libnymea-core/scriptengine/scriptthings.h @@ -0,0 +1,110 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2022, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef SCRIPTTHINGS_H +#define SCRIPTTHINGS_H + +#include "integrations/thingmanager.h" + +#include +#include +#include +#include + + +namespace nymeaserver { +namespace scriptengine { + +class ScriptThing; +class ThingsModel; + + +class ScriptThings : public QSortFilterProxyModel, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + Q_PROPERTY(QString filterInterface READ filterInterface WRITE setFilterInterface NOTIFY filterInterfaceChanged) + +public: + explicit ScriptThings(QObject *parent = nullptr); + void classBegin() override; + void componentComplete() override; + + QString filterInterface() const; + void setFilterInterface(const QString &filterInterface); + + Q_INVOKABLE nymeaserver::scriptengine::ScriptThing *get(int index) const; + Q_INVOKABLE nymeaserver::scriptengine::ScriptThing *getThing(const QUuid &thingId) const; + +signals: + void countChanged(); + void filterInterfaceChanged(); + void thingAdded(const QString &thingId); + void thingRemoved(const QString &thingId); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +private: + ThingManager *m_thingManager = nullptr; + ThingsModel *m_model = nullptr; + + QString m_filterInterface; +}; + +class ThingsModel: public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + RoleId, + RoleName + }; + ThingsModel(ThingManager *thingManager, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const override; + + Thing *get(int index) const; + Thing *getThing(const QUuid &thingId) const; + +signals: + void countChanged(); + +private: + ThingManager *m_thingManager = nullptr; + +}; + +} +} +#endif // SCRIPTTHINGS_H diff --git a/libnymea-core/scriptengine/scriptthingsfilter.cpp b/libnymea-core/scriptengine/scriptthingsfilter.cpp new file mode 100644 index 00000000..28f77e1b --- /dev/null +++ b/libnymea-core/scriptengine/scriptthingsfilter.cpp @@ -0,0 +1,7 @@ +#include "scriptthingsfilter.h" + +ScriptThingsFilter::ScriptThingsFilter(QObject *parent) + : QObject{parent} +{ + +} diff --git a/libnymea-core/scriptengine/scriptthingsfilter.h b/libnymea-core/scriptengine/scriptthingsfilter.h new file mode 100644 index 00000000..4e8486f8 --- /dev/null +++ b/libnymea-core/scriptengine/scriptthingsfilter.h @@ -0,0 +1,17 @@ +#ifndef SCRIPTTHINGSFILTER_H +#define SCRIPTTHINGSFILTER_H + +#include + +class ScriptThingsFilter : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY() +public: + explicit ScriptThingsFilter(QObject *parent = nullptr); + +signals: + +}; + +#endif // SCRIPTTHINGSFILTER_H diff --git a/libnymea/loggingcategories.cpp b/libnymea/loggingcategories.cpp index 95045a8a..ba5e3a20 100644 --- a/libnymea/loggingcategories.cpp +++ b/libnymea/loggingcategories.cpp @@ -50,7 +50,6 @@ NYMEA_LOGGING_CATEGORY(dcExperiences, "Experiences") NYMEA_LOGGING_CATEGORY(dcTimeManager, "TimeManager") NYMEA_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine") NYMEA_LOGGING_CATEGORY(dcRuleEngineDebug, "RuleEngineDebug") -NYMEA_LOGGING_CATEGORY(dcScriptEngine, "ScriptEngine") NYMEA_LOGGING_CATEGORY(dcHardware, "Hardware") NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine") NYMEA_LOGGING_CATEGORY(dcServerManager, "ServerManager") diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index 1fc74cec..0bdf9f25 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -58,7 +58,6 @@ 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/auto/scripts/testhelper.cpp b/tests/auto/scripts/testhelper.cpp index da6073c0..fcf86ab9 100644 --- a/tests/auto/scripts/testhelper.cpp +++ b/tests/auto/scripts/testhelper.cpp @@ -50,6 +50,11 @@ void TestHelper::logStateChange(const QString &thingId, const QString &stateId, emit stateChangeLogged(ThingId(thingId), stateId, value); } +void TestHelper::setTestResult(bool success) +{ + emit testResult(success); +} + TestHelper::TestHelper(QObject *parent) : QObject(parent) { diff --git a/tests/auto/scripts/testhelper.h b/tests/auto/scripts/testhelper.h index 81a1e5f7..1f113e00 100644 --- a/tests/auto/scripts/testhelper.h +++ b/tests/auto/scripts/testhelper.h @@ -34,12 +34,17 @@ public: Q_INVOKABLE void logEvent(const QString &thingId, const QString &eventId, const QVariantMap ¶ms); Q_INVOKABLE void logStateChange(const QString &thingId, const QString &stateId, const QVariant &value); + Q_INVOKABLE void setTestResult(bool success); + signals: void setState(const QVariant &value); void executeAction(const QVariantMap ¶ms); void eventLogged(const ThingId &thingId, const QString &eventId, const QVariantMap ¶ms); void stateChangeLogged(const ThingId &thingId, const QString stateId, const QVariant &value); + + void testResult(bool success); + private: explicit TestHelper(QObject *parent = nullptr); static TestHelper* s_instance; diff --git a/tests/auto/scripts/testscripts.cpp b/tests/auto/scripts/testscripts.cpp index 2de9e853..5d00c1b4 100644 --- a/tests/auto/scripts/testscripts.cpp +++ b/tests/auto/scripts/testscripts.cpp @@ -74,6 +74,13 @@ private slots: void testInterfaceEvent(); void testInterfaceState(); void testInterfaceAction(); + + void testScriptThingAction(); + void testScriptThingReadState(); + void testScriptThingWriteState(); + void testScriptThingEvent(); + + void testThingsFindThing(); }; @@ -518,6 +525,180 @@ void TestScripts::testInterfaceAction() } +void TestScripts::testScriptThingAction() +{ + QString script = QString("import QtQuick 2.0\n" + "import nymea 1.0\n" + "Item {\n" + " Thing {\n" + " id: thing\n" + " thingId: \"%1\"\n" + " }\n" + " Connections {\n" + " target: TestHelper\n" + " onExecuteAction: {\n" + " thing.executeAction(\"%2\", params)\n" + " }\n" + " }\n" + "}\n").arg(m_mockThingId.toString()).arg("power"); + + qCDebug(dcTests()) << "Adding script:\n" << qUtf8Printable(script); + ScriptEngine::AddScriptReply reply = NymeaCore::instance()->scriptEngine()->addScript("TestAction", script.toUtf8()); + QCOMPARE(reply.scriptError, ScriptEngine::ScriptErrorNoError); + + QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::thingStateChanged); + + QVariantMap params; + params.insert("power", true); + TestHelper::instance()->executeAction(params); + + spy.wait(1); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().at(0).value()->id(), m_mockThingId); + QCOMPARE(spy.first().at(1).value(), mockPowerStateTypeId); + QCOMPARE(spy.first().at(2).toBool(), true); + +} + +void TestScripts::testScriptThingReadState() +{ + QString script = QString("import QtQuick 2.0\n" + "import nymea 1.0\n" + "Item {\n" + " Thing {\n" + " thingId: \"%1\"\n" + " onStateValueChanged: {\n" + " TestHelper.logStateChange(thingId, stateName, value);\n" + " }\n" + " }\n" + "}\n").arg(m_mockThingId.toString()).arg("power"); + + qCDebug(dcTests()) << "Adding script:\n" << qUtf8Printable(script); + ScriptEngine::AddScriptReply reply = NymeaCore::instance()->scriptEngine()->addScript("TestState", script.toUtf8()); + QCOMPARE(reply.scriptError, ScriptEngine::ScriptErrorNoError); + + QSignalSpy spy(TestHelper::instance(), &TestHelper::stateChangeLogged); + + // Generate state change + Action action(mockPowerActionTypeId, m_mockThingId); + action.setParams(ParamList() << Param(mockPowerActionPowerParamTypeId, true)); + NymeaCore::instance()->thingManager()->executeAction(action); + + spy.wait(1); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().at(0).value(), m_mockThingId); + QCOMPARE(spy.first().at(1).toString(), QString("power")); + QCOMPARE(spy.first().at(2).toBool(), true); + +} + +void TestScripts::testScriptThingWriteState() +{ + QString script = QString("import QtQuick 2.0\n" + "import nymea 1.0\n" + "Item {\n" + " Thing {\n" + " id: thing\n" + " thingId: \"%1\"\n" + " }\n" + " Connections {\n" + " target: TestHelper\n" + " onSetState: {\n" + " thing.setStateValue(\"%2\", value)\n" + " }\n" + " }\n" + "}\n").arg(m_mockThingId.toString()).arg("power"); + + qCDebug(dcTests()) << "Adding script:\n" << qUtf8Printable(script); + ScriptEngine::AddScriptReply reply = NymeaCore::instance()->scriptEngine()->addScript("TestState", script.toUtf8()); + QCOMPARE(reply.scriptError, ScriptEngine::ScriptErrorNoError); + + QSignalSpy spy(NymeaCore::instance()->thingManager(), &ThingManager::thingStateChanged); + + TestHelper::instance()->setState(true); + + spy.wait(1); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().at(0).value()->id(), m_mockThingId); + QCOMPARE(spy.first().at(1).value(), mockPowerStateTypeId); + QCOMPARE(spy.first().at(2).toBool(), true); + +} + +void TestScripts::testScriptThingEvent() +{ + QString script = QString("import QtQuick 2.0\n" + "import nymea 1.0\n" + "Item {\n" + " Thing {\n" + " thingId: \"%1\"\n" + " onEventTriggered: {\n" + " TestHelper.logEvent(thingId, eventName, params);\n" + " }\n" + " }\n" + "}\n").arg(m_mockThingId.toString()); + + qCDebug(dcTests()) << "Adding script:\n" << qUtf8Printable(script); + ScriptEngine::AddScriptReply reply = NymeaCore::instance()->scriptEngine()->addScript("TestEvent", script.toUtf8()); + QCOMPARE(reply.scriptError, ScriptEngine::ScriptErrorNoError); + + QSignalSpy spy(TestHelper::instance(), &TestHelper::eventLogged); + + // trigger event in mock device + Thing* thing = NymeaCore::instance()->thingManager()->findConfiguredThing(m_mockThingId); + int port = thing->paramValue(mockThingHttpportParamTypeId).toInt(); + QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2&%3=%4") + .arg(port) + .arg(mockEvent2EventTypeId.toString()) + .arg(mockEvent2EventIntParamParamTypeId.toString()) + .arg(10))); + QNetworkAccessManager nam; + QNetworkReply *r = nam.get(request); + connect(r, &QNetworkReply::finished, r, &QNetworkReply::deleteLater); + + spy.wait(); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().at(0).value(), m_mockThingId); + QCOMPARE(spy.first().at(1).toString(), QString("event2")); + QVariantMap expectedParams; + expectedParams.insert(mockEvent2EventIntParamParamTypeId.toString().remove(QRegExp("[{}]")), 10); + expectedParams.insert("intParam", 10); + QCOMPARE(spy.first().at(2).toMap(), expectedParams); + +} + +void TestScripts::testThingsFindThing() +{ + QSignalSpy spy(TestHelper::instance(), &TestHelper::testResult); + + QString script = QString("import QtQuick 2.0\n" + "import nymea 1.0\n" + "Item {\n" + " id: root\n" + " property string thingId: \"%1\"\n" + " Things {\n" + " id: things\n" + " }\n" + " Component.onCompleted: {\n" + " var thing = things.getThing(root.thingId)\n" + " TestHelper.setTestResult(thing.thingId == root.thingId);\n" + " }\n" + "}\n").arg(m_mockThingId.toString()); + + qCDebug(dcTests()) << "Adding script:\n" << qUtf8Printable(script); + ScriptEngine::AddScriptReply reply = NymeaCore::instance()->scriptEngine()->addScript("TestEvent", script.toUtf8()); + QCOMPARE(reply.scriptError, ScriptEngine::ScriptErrorNoError); + + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.first().at(0).toBool(), true); + +} + + #include "testscripts.moc" QTEST_MAIN(TestScripts)