From 5808da8f86fa8405751cfc8984cd5b18093367d2 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 26 Mar 2019 23:09:29 +0100 Subject: [PATCH 1/8] Make the initial handshake (JSONRPC.Hello) mandatory. --- libnymea-core/jsonrpc/devicehandler.cpp | 12 +- libnymea-core/jsonrpc/jsonrpcserver.cpp | 36 +++- libnymea-core/jsonrpc/jsonrpcserver.h | 1 + libnymea-core/jsonrpc/jsontypes.cpp | 1 - libnymea-core/servers/mocktcpserver.cpp | 9 + libnymea-core/servers/mocktcpserver.h | 2 + nymea.pro | 11 +- tests/auto/jsonrpc/testjsonrpc.cpp | 203 +++++++++++++----- tests/auto/nymeatestbase.cpp | 6 +- .../websocketserver/testwebsocketserver.cpp | 21 +- 10 files changed, 229 insertions(+), 73 deletions(-) diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 9bdee4bd..21b8ad28 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -298,6 +298,12 @@ DeviceHandler::DeviceHandler(QObject *parent) : params.insert("device", JsonTypes::deviceRef()); setParams("DeviceChanged", params); + params.clear(); returns.clear(); + setDescription("PluginConfigurationChanged", "Emitted whenever a plugin's configuration is changed."); + params.insert("pluginId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("configuration", QVariantList() << JsonTypes::paramRef()); + setParams("PluginConfigurationChanged", params); + connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &DeviceHandler::pluginConfigChanged); connect(NymeaCore::instance(), &NymeaCore::deviceStateChanged, this, &DeviceHandler::deviceStateChanged); connect(NymeaCore::instance(), &NymeaCore::deviceRemoved, this, &DeviceHandler::deviceRemovedNotification); @@ -637,11 +643,11 @@ void DeviceHandler::pluginConfigChanged(const PluginId &id, const ParamList &con { QVariantMap params; params.insert("pluginId", id); - QVariantMap configMap; + QVariantList configList; foreach (const Param ¶m, config) { - configMap.insert(param.paramTypeId().toString(), param.value()); + configList << JsonTypes::packParam(param); } - params.insert("configuration", configMap); + params.insert("configuration", configList); emit PluginConfigurationChanged(params); } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.cpp b/libnymea-core/jsonrpc/jsonrpcserver.cpp index a670638f..9625153b 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.cpp +++ b/libnymea-core/jsonrpc/jsonrpcserver.cpp @@ -248,6 +248,11 @@ JsonReply *JsonRPCServer::Hello(const QVariantMap ¶ms) qCDebug(dcJsonRpc()) << "Client" << clientId << "initiated handshake." << m_clientLocales.value(clientId); + // If we waited for the handshake, here it is. Remove the timer... + if (m_newConnectionWaitTimers.contains(clientId)) { + delete m_newConnectionWaitTimers.take(clientId); + } + return createReply(createWelcomeMessage(interface, clientId)); } @@ -285,6 +290,7 @@ JsonReply* JsonRPCServer::Version(const QVariantMap ¶ms) const JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms) { QUuid clientId = this->property("clientId").toUuid(); + Q_ASSERT_X(m_clientNotifications.contains(clientId), "JsonRPCServer", "SetNotificationStatus for an unknown client called"); m_clientNotifications[clientId] = params.value("enabled").toBool(); QVariantMap returns; returns.insert("enabled", m_clientNotifications[clientId]); @@ -585,12 +591,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid if (NymeaCore::instance()->userManager()->initRequired()) { if (!(targetNamespace == "JSONRPC" && authExemptMethodsNoUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) { sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call CreateUser first."); + qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection."; + interface->terminateClientConnection(clientId); return; } } else { // ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect Hello call if (!(targetNamespace == "JSONRPC" && authExemptMethodsWithUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) { sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token."); + qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection."; + interface->terminateClientConnection(clientId); return; } } @@ -622,7 +632,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace << method.toLatin1().data(); - if (targetNamespace != "JSONRPC" || method != "Hello") { + if (!(targetNamespace == "JSONRPC" && method == "Hello")) { + // This is not the handshake message. If we've waited for it, consider this a protocol violation and drop connection + if (m_newConnectionWaitTimers.contains(clientId)) { + sendErrorResponse(interface, clientId, commandId, "Handshake required. Call JSONRPC.Hello first."); + qCWarning(dcJsonRpc()) << "Connection requires a handshake but client did not initiate handshake. Dropping connection"; + interface->terminateClientConnection(clientId); + return; + } + + // Unless this is the Hello message, which allows setting the locale explicity, attach the locale // for this connection // If the client did request a locale in the Hello message, use that locale @@ -767,7 +786,17 @@ void JsonRPCServer::clientConnected(const QUuid &clientId) // Initialize the connection locale to the settings default m_clientLocales.insert(clientId, NymeaCore::instance()->configuration()->locale()); - interface->sendData(clientId, QJsonDocument::fromVariant(createWelcomeMessage(interface, clientId)).toJson(QJsonDocument::Compact)); + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [this, timer, clientId, interface](){ + // Client did not initiate handshake within timeout. Drop connection... + m_clientTransports.value(clientId)->disconnect(); + timer->deleteLater(); + m_newConnectionWaitTimers.remove(clientId); + qCDebug(dcJsonRpc()) << "Client" << clientId << "did not initiate the handshake within the required timeout. Dropping connection."; + interface->terminateClientConnection(clientId); + }); + m_newConnectionWaitTimers.insert(clientId, timer); + timer->start(10000); } void JsonRPCServer::clientDisconnected(const QUuid &clientId) @@ -780,6 +809,9 @@ void JsonRPCServer::clientDisconnected(const QUuid &clientId) if (m_pushButtonTransactions.values().contains(clientId)) { NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId)); } + if (m_newConnectionWaitTimers.contains(clientId)) { + delete m_newConnectionWaitTimers.take(clientId); + } } } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.h b/libnymea-core/jsonrpc/jsonrpcserver.h index e85a6da6..0730b3c0 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.h +++ b/libnymea-core/jsonrpc/jsonrpcserver.h @@ -107,6 +107,7 @@ private: QHash m_clientNotifications; QHash m_clientLocales; QHash m_pushButtonTransactions; + QHash m_newConnectionWaitTimers; QHash m_pairingRequests; diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 4b6e66ad..f8b87dc6 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -1372,7 +1372,6 @@ Rule JsonTypes::unpackRule(const QVariantMap &ruleMap) QList eventDescriptors; if (ruleMap.contains("eventDescriptors")) { QVariantList eventDescriptorVariantList = ruleMap.value("eventDescriptors").toList(); - qCDebug(dcJsonRpc) << "unpacking eventDescriptors:" << eventDescriptorVariantList; foreach (const QVariant &eventDescriptorVariant, eventDescriptorVariantList) { eventDescriptors.append(JsonTypes::unpackEventDescriptor(eventDescriptorVariant.toMap())); } diff --git a/libnymea-core/servers/mocktcpserver.cpp b/libnymea-core/servers/mocktcpserver.cpp index 5650034f..68f48901 100644 --- a/libnymea-core/servers/mocktcpserver.cpp +++ b/libnymea-core/servers/mocktcpserver.cpp @@ -33,6 +33,14 @@ MockTcpServer::MockTcpServer(QObject *parent): TransportInterface(ServerConfiguration(), parent) { s_allServers.append(this); + + connect(this, &TransportInterface::clientConnected, this, [this](const QUuid &clientId){ + m_connectedClients.append(clientId); + }); + + connect(this, &TransportInterface::clientDisconnected, this, [this](const QUuid &clientId){ + m_connectedClients.removeAll(clientId); + }); } MockTcpServer::~MockTcpServer() @@ -65,6 +73,7 @@ QList MockTcpServer::servers() void MockTcpServer::injectData(const QUuid &clientId, const QByteArray &data) { + Q_ASSERT_X(m_connectedClients.contains(clientId), "MockTcpServer", "Cannot inject data. Client is not connected"); emit dataAvailable(clientId, data); } diff --git a/libnymea-core/servers/mocktcpserver.h b/libnymea-core/servers/mocktcpserver.h index 47040a87..9253d719 100644 --- a/libnymea-core/servers/mocktcpserver.h +++ b/libnymea-core/servers/mocktcpserver.h @@ -57,6 +57,8 @@ public slots: private: static QList s_allServers; + + QList m_connectedClients; }; #endif // TCPSERVER_H diff --git a/nymea.pro b/nymea.pro index 60ecd7e9..d64e57d0 100644 --- a/nymea.pro +++ b/nymea.pro @@ -15,11 +15,10 @@ doc.commands += cd $$top_srcdir/doc; ./generate-interfaces-qdoc.py; doc.commands += cd $$top_srcdir/doc; ./generate-api-qdoc.py; doc.commands += cd $$top_srcdir/doc; qdoc --highlighting config.qdocconf; cp -r images/* html/images/; \ cp -r favicons/* html/; cp -r $$top_srcdir/doc/html $$top_builddir/ +QMAKE_EXTRA_TARGETS += doc licensecheck.commands = $$top_srcdir/tests/auto/checklicenseheaders.sh $$top_srcdir - -test.depends = licensecheck -test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check +QMAKE_EXTRA_TARGETS += licensecheck # Translations: # make lupdate to update .ts files @@ -42,7 +41,11 @@ translations.path = /usr/share/nymea/translations translations.depends = lrelease INSTALLS += translations -QMAKE_EXTRA_TARGETS += licensecheck doc test lupdate lrelease +QMAKE_EXTRA_TARGETS += lupdate lrelease + +test.depends = licensecheck lrelease +test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check +QMAKE_EXTRA_TARGETS += test # Show doc files in project tree OTHER_FILES += doc/*.qdoc* \ diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index 07c5e4cf..3c4c6f93 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -133,25 +133,22 @@ void TestJSONRPC::initTestCase() { NymeaTestBase::initTestCase(); QLoggingCategory::setFilterRules("*.debug=false\n" -// "JsonRpc*.debug=true\n" +// "JsonRpcTraffic.debug=true\n" + "JsonRpc.debug=true\n" "Translations.debug=true\n" "Tests.debug=true"); } void TestJSONRPC::testHandshake() { - // first test if the handshake message is auto-sent upon connecting - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); - QUuid newClientId = QUuid::createUuid(); m_mockTcpServer->clientConnected(newClientId); - QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect."); - QVERIFY2(spy.first().first() == newClientId, "Handshake message addressed at the wrong client."); + qApp->processEvents(); - QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); - QVariantMap handShake = jsonDoc.toVariant().toMap(); + // Check the Hello reply + QVariantMap handShake = injectAndWait("JSONRPC.Hello", QVariantMap(), newClientId).toMap(); QString nymeaVersionString(NYMEA_VERSION_STRING); - QVERIFY2(handShake.value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version."); + QVERIFY2(handShake.value("params").toMap().value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version."); // Check whether pushButtonAuth is disabled QCOMPARE(handShake.value("pushButtonAuthAvailable").toBool(), false); @@ -164,11 +161,12 @@ void TestJSONRPC::testHandshake() handShake = injectAndWait("JSONRPC.Hello").toMap(); QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); - m_mockTcpServer->clientDisconnected(newClientId); - - // Check whether pushButtonAuth is now + // Check whether pushButtonAuth is now enabled QCOMPARE(handShake.value("params").toMap().value("pushButtonAuthAvailable").toBool(), true); + emit m_mockTcpServer->clientDisconnected(newClientId); + + // And now check if it is sent again when calling JSONRPC.Hello handShake = injectAndWait("JSONRPC.Hello").toMap(); QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); @@ -222,8 +220,11 @@ void TestJSONRPC::testInitialSetup() QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); QVERIFY(spy.isValid()); + QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); // Introspect call should work in any case + qCDebug(dcTests()) << "Calling Introspect, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Introspect\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -231,12 +232,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariantMap response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling introspect on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Hello call should work in any case too spy.clear(); + qCDebug(dcTests()) << "Calling Hello, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -244,12 +246,14 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Hello on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), true); // Any other call should fail with "unauthorized" even if we use a previously valid token spy.clear(); + disconnectedSpy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting failure (unauthenticated)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -257,13 +261,31 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + // Connection should terminate + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client disconnected"; + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client connected"; + + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + // Except CreateUser // But it should still fail when giving a an invalid username spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad username)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -271,12 +293,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); // or when giving a bad password spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad password)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"weak\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -284,12 +307,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); // Now lets play by the rules (with an uppercase email) spy.clear(); + qCDebug(dcTests()) << "Calling CreateUser, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -297,12 +321,13 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 1); // Now that we have a user, initialSetup should be false in the Hello call spy.clear(); + qCDebug(dcTests()) << "Calling Hello, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -310,12 +335,14 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), false); // Calls should still fail, given we didn't get a new token yet spy.clear(); + disconnectedSpy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting failure (bad token)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -323,11 +350,30 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests) << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + // Connection should terminate + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client disconnected"; + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + qCDebug(dcTests()) << "Mock client connected"; + + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) { + spy.wait(); + } + QVERIFY(spy.count() == 1); + + // Now lets authenticate with a wrong user spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad user)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@wrong.domain\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -335,7 +381,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), false); QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty()); @@ -343,6 +389,7 @@ void TestJSONRPC::testInitialSetup() // Now lets authenticate with a wrong password spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -350,7 +397,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), false); QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty()); @@ -358,6 +405,7 @@ void TestJSONRPC::testInitialSetup() // Now lets authenticate for real (but intentionally use a lowercase email here, should still work) spy.clear(); + qCDebug(dcTests()) << "Calling Authenticate, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -365,7 +413,7 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), true); m_apiToken = response.value("params").toMap().value("token").toByteArray(); @@ -373,6 +421,7 @@ void TestJSONRPC::testInitialSetup() // Now do a Version call with the valid token and it should work spy.clear(); + qCDebug(dcTests()) << "Calling Version, expecting success"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -380,15 +429,19 @@ void TestJSONRPC::testInitialSetup() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); } void TestJSONRPC::testRevokeToken() { - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData); QVERIFY(spy.isValid()); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); + QVERIFY(disconnectedSpy.isValid()); + QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected); + QVERIFY(connectedSpy.isValid()); // Now get all the tokens spy.clear(); @@ -399,7 +452,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariantMap response = jsonDoc.toVariant().toMap(); - qWarning() << "Getting existing Tokens" << response.value("status").toString() << response; + qCDebug(dcTests()) << "Getting existing Tokens" << response.value("status").toString() << response; QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList(); QCOMPARE(tokenList.count(), 1); @@ -414,7 +467,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); + qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); QCOMPARE(response.value("params").toMap().value("success").toBool(), true); QByteArray newToken = response.value("params").toMap().value("token").toByteArray(); @@ -429,7 +482,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Now get all the tokens using the old token @@ -441,7 +494,7 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Tokens" << response.value("status").toString(); + qCDebug(dcTests()) << "Calling Tokens" << response.value("status").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("success")); tokenList = response.value("params").toMap().value("tokenInfoList").toList(); QCOMPARE(tokenList.count(), 2); @@ -464,11 +517,12 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling RemoveToken" << response.value("status").toString() << response; + qCDebug(dcTests()) << "Calling RemoveToken" << response.value("status").toString() << response; QCOMPARE(response.value("status").toString(), QStringLiteral("success")); // Do a call with the now removed token, it should be forbidden spy.clear(); + disconnectedSpy.clear(); m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -476,8 +530,19 @@ void TestJSONRPC::testRevokeToken() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant().toMap(); - qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); + qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString(); QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized")); + + // And connection should drop + if (disconnectedSpy.count() == 0) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + + // Connect again to not impact subsequent tests... + connectedSpy.clear(); + emit m_mockTcpServer->clientConnected(m_clientId); + if (connectedSpy.count() == 0) connectedSpy.wait(); + QCOMPARE(connectedSpy.count(), 1); + injectAndWait("JSONRPC.Hello"); } void TestJSONRPC::testBasicCall_data() @@ -492,9 +557,9 @@ void TestJSONRPC::testBasicCall_data() QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\"}\n") << false << false; QTest::newRow("missing method") << QByteArray("{\"id\":42}\n") << true << false; QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"JSO}\n") << false << false; - QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\"}\n") << true << false; - QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\"}\n") << true << false; - QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\"}\n") << true << false; + QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; + QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; + QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false; QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"params\":{\"törööö\":\"chooo-chooo\"}}\n") << true << false; } @@ -594,7 +659,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications() params.insert("name", "Mock device"); params.insert("deviceParams", deviceParams); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap(); @@ -614,7 +679,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("deviceId", deviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); checkNotification(clientSpy, "Devices.DeviceRemoved"); @@ -659,7 +724,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications() params.insert("stateEvaluator", stateEvaluator); QVariant response = injectAndWait("Rules.AddRule", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); QVariantMap notificationRuleMap = checkNotification(clientSpy, "Rules.RuleAdded").toMap().value("params").toMap().value("rule").toMap(); verifyRuleError(response); @@ -678,7 +743,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); checkNotification(clientSpy, "Devices.DeviceRemoved"); verifyRuleError(response); @@ -719,7 +784,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); response = injectAndWait("Rules.AddRule", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); QVariant notificationVariant = checkNotification(clientSpy, "Rules.RuleAdded"); verifyRuleError(response); @@ -743,7 +808,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() QNetworkReply *reply = nam.get(request); connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); - spy.wait(); + if (spy.count() == 0) spy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged"); verifyRuleError(response); @@ -756,12 +821,12 @@ void TestJSONRPC::ruleActiveChangedNotifications() qDebug() << "setting mock int state to 42"; QNetworkRequest request2(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateId.toString()).arg(42))); QNetworkReply *reply2 = nam.get(request2); - spy.wait(); + if (spy.count() == 0) spy.wait(); QCOMPARE(spy.count(), 1); connect(reply2, SIGNAL(finished()), reply2, SLOT(deleteLater())); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged"); verifyRuleError(response); @@ -773,7 +838,7 @@ void TestJSONRPC::ruleActiveChangedNotifications() params.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); notificationVariant = checkNotification(clientSpy, "Rules.RuleRemoved"); checkNotification(clientSpy, "Logging.LogDatabaseUpdated"); verifyRuleError(response); @@ -807,7 +872,7 @@ void TestJSONRPC::deviceChangedNotifications() response = injectAndWait("Devices.AddConfiguredDevice", params); DeviceId deviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!deviceId.isNull()); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap(); @@ -831,7 +896,7 @@ void TestJSONRPC::deviceChangedNotifications() params.insert("deviceId", deviceId); params.insert("deviceParams", newDeviceParams); response = injectAndWait("Devices.ReconfigureDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap reconfigureDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap(); QCOMPARE(reconfigureDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString()); @@ -848,7 +913,7 @@ void TestJSONRPC::deviceChangedNotifications() params.insert("deviceId", deviceId); params.insert("name", deviceName); response = injectAndWait("Devices.EditDevice", params); - clientSpy.wait(2000); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); QVariantMap editDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap(); QCOMPARE(editDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString()); @@ -860,7 +925,7 @@ void TestJSONRPC::deviceChangedNotifications() params.clear(); response.clear(); clientSpy.clear(); params.insert("deviceId", deviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); - clientSpy.wait(); + if (clientSpy.count() == 0) clientSpy.wait(); verifyDeviceError(response); checkNotification(clientSpy, "Devices.DeviceRemoved"); checkNotification(clientSpy, "Logging.LogDatabaseUpdated"); @@ -882,7 +947,7 @@ void TestJSONRPC::stateChangeEmitsNotifications() QNetworkReply *reply = nam.get(request); connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); QSignalSpy replySpy(reply, SIGNAL(finished())); - replySpy.wait(); + if (replySpy.count() == 0) replySpy.wait(); // Make sure the notification contains all the stuff we expect QVariantList stateChangedVariants = checkNotifications(clientSpy, "Devices.StateChanged"); @@ -1001,9 +1066,7 @@ void TestJSONRPC::testPushButtonAuth() pushButtonAgent.sendButtonPressed(); - if (clientSpy.count() == 0) { - clientSpy.wait(); - } + if (clientSpy.count() == 0) clientSpy.wait(); QVariantMap rsp = checkNotification(clientSpy, "JSONRPC.PushButtonAuthFinished").toMap(); QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId); @@ -1023,6 +1086,9 @@ void TestJSONRPC::testPushButtonAuthInterrupt() // Create a new clientId for mallory and connect it to the server QUuid malloryId = QUuid::createUuid(); m_mockTcpServer->clientConnected(malloryId); + QSignalSpy responseSpy(m_mockTcpServer, &MockTcpServer::outgoingData); + m_mockTcpServer->injectData(malloryId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (responseSpy.count() == 0) responseSpy.wait(); // Snoop in on everything the TCP server sends to its clients. QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); @@ -1120,11 +1186,13 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() pushButtonAgent.init(); // Snoop in on everything the TCP server sends to its clients. - QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData); // Create a new clientId for alice and connect it to the server QUuid aliceId = QUuid::createUuid(); m_mockTcpServer->clientConnected(aliceId); + m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (clientSpy.count() == 0) clientSpy.wait(); // request push button auth for client 1 (alice) and check for OK reply QVariantMap params; @@ -1139,6 +1207,9 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop() // Create a new clientId for bob and connect it to the server QUuid bobId = QUuid::createUuid(); m_mockTcpServer->clientConnected(bobId); + clientSpy.clear(); + m_mockTcpServer->injectData(bobId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (clientSpy.count() == 0) clientSpy.wait(); // request push button auth for client 2 (bob) and check for OK reply params.clear(); @@ -1175,7 +1246,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() NymeaCore::instance()->userManager()->removeUser(""); QVERIFY(NymeaCore::instance()->userManager()->initRequired()); - QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); + QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData); QVERIFY(spy.isValid()); PushButtonAgent pushButtonAgent; @@ -1183,6 +1254,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() // Hello call should work in any case, telling us initial setup is required spy.clear(); + qCDebug(dcTests()) << "Calling Hello on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -1190,13 +1262,16 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); QVariant response = jsonDoc.toVariant(); - qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success")); QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), true); // request push button auth for alice and check for OK reply QUuid aliceId = QUuid::createUuid(); m_mockTcpServer->clientConnected(aliceId); + spy.clear(); + m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.count() == 0) spy.wait(); QVariantMap params; params.insert("deviceName", "alice"); @@ -1223,6 +1298,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() // initialSetupRequired should be false in Hello call now spy.clear(); + qCDebug(dcTests()) << "Calling Hello on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n"); if (spy.count() == 0) { spy.wait(); @@ -1230,13 +1306,15 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant(); - qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success")); QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), false); // CreateUser without a token should fail now even though there are 0 users in the DB spy.clear(); + QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected); + qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance"; m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n"); if (spy.count() == 0) { spy.wait(); @@ -1244,10 +1322,19 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth() QVERIFY(spy.count() == 1); jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray()); response = jsonDoc.toVariant(); - qWarning() << "Calling CreateUser on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); + qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString(); QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("unauthorized")); QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0); + // Connection should drop + if (disconnectedSpy.isEmpty()) disconnectedSpy.wait(); + QCOMPARE(disconnectedSpy.count(), 1); + + // Reconnect to not impact subsequent tests + m_mockTcpServer->clientConnected(m_clientId); + spy.clear(); + m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}"); + if (spy.isEmpty()) spy.wait(); } void TestJSONRPC::testDataFragmentation_data() @@ -1302,12 +1389,10 @@ void TestJSONRPC::testGarbageData() for (int i = 0; i < 1024; i++) { data.append("a"); } - for (int i = 0; i < 11; i ++) { + for (int i = 0; i < 11 && spy.count() == 0; i ++) { m_mockTcpServer->injectData(m_clientId, data); } - QCOMPARE(spy.count(), 1); - } #include "testjsonrpc.moc" diff --git a/tests/auto/nymeatestbase.cpp b/tests/auto/nymeatestbase.cpp index 8064b733..1fbdf188 100644 --- a/tests/auto/nymeatestbase.cpp +++ b/tests/auto/nymeatestbase.cpp @@ -143,9 +143,11 @@ void NymeaTestBase::initTestCase() m_clientId = QUuid::createUuid(); m_mockTcpServer->clientConnected(m_clientId); + QVariant response = injectAndWait("JSONRPC.Hello"); + createMockDevice(); - QVariant response = injectAndWait("Devices.GetConfiguredDevices", {}); + response = injectAndWait("Devices.GetConfiguredDevices", {}); foreach (const QVariant &device, response.toMap().value("params").toMap().value("devices").toList()) { if (device.toMap().value("deviceClassId").toUuid() == mockDeviceAutoClassId) { m_mockDeviceAutoId = DeviceId(device.toMap().value("id").toString()); @@ -442,6 +444,8 @@ void NymeaTestBase::restartServer() coreSpy.wait(); m_mockTcpServer = MockTcpServer::servers().first(); m_mockTcpServer->clientConnected(m_clientId); + + injectAndWait("JSONRPC.Hello"); } void NymeaTestBase::clearLoggingDatabase() diff --git a/tests/auto/websocketserver/testwebsocketserver.cpp b/tests/auto/websocketserver/testwebsocketserver.cpp index 8b773185..3fd3403c 100644 --- a/tests/auto/websocketserver/testwebsocketserver.cpp +++ b/tests/auto/websocketserver/testwebsocketserver.cpp @@ -81,8 +81,14 @@ void TestWebSocketServer::testHandshake() { QWebSocket *socket = new QWebSocket("nymea tests", QWebSocketProtocol::Version13); connect(socket, &QWebSocket::sslErrors, this, &TestWebSocketServer::sslErrors); - QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + + QSignalSpy connectedSpy(socket, &QWebSocket::connected); socket->open(QUrl(QStringLiteral("wss://localhost:4444"))); + connectedSpy.wait(); + + QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect."); QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().first().toByteArray()); @@ -90,8 +96,8 @@ void TestWebSocketServer::testHandshake() QString nymeaVersionString(NYMEA_VERSION_STRING); QString jsonProtocolVersionString(JSON_PROTOCOL_VERSION); - QCOMPARE(handShake.value("version").toString(), nymeaVersionString); - QCOMPARE(handShake.value("protocol version").toString(), jsonProtocolVersionString); + QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString); + QCOMPARE(handShake.value("params").toMap().value("protocol version").toString(), jsonProtocolVersionString); socket->close(); socket->deleteLater(); @@ -173,6 +179,10 @@ QVariant TestWebSocketServer::injectSocketAndWait(const QString &method, const Q } QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); + + spy.clear(); socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact))); spy.wait(); @@ -214,6 +224,11 @@ QVariant TestWebSocketServer::injectSocketData(const QByteArray &data) } QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + + socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}"); + spy.wait(); + + spy.clear(); socket->sendTextMessage(QString(data)); spy.wait(); From 87595faf6d70c79b5cf5a7b9b7ba7d59eb4d962b Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Wed, 27 Mar 2019 14:10:27 +0100 Subject: [PATCH 2/8] bump version --- nymea.pri | 2 +- tests/auto/api.json | 11 ++++++++++- tests/auto/versioning/testversioning.cpp | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/nymea.pri b/nymea.pri index c80a1770..bb18123e 100644 --- a/nymea.pri +++ b/nymea.pri @@ -6,7 +6,7 @@ NYMEA_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/ # define protocol versions JSON_PROTOCOL_VERSION_MAJOR=1 -JSON_PROTOCOL_VERSION_MINOR=14 +JSON_PROTOCOL_VERSION_MINOR=15 REST_API_VERSION=1 DEFINES += NYMEA_VERSION_STRING=\\\"$${NYMEA_VERSION_STRING}\\\" \ diff --git a/tests/auto/api.json b/tests/auto/api.json index ec176e34..09637c65 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -1.14 +1.15 { "methods": { "Actions.ExecuteAction": { @@ -1008,6 +1008,15 @@ "deviceId": "Uuid" } }, + "Devices.PluginConfigurationChanged": { + "description": "Emitted whenever a plugin's configuration is changed.", + "params": { + "configuration": [ + "$ref:Param" + ], + "pluginId": "Uuid" + } + }, "Devices.StateChanged": { "description": "Emitted whenever a State of a device changes.", "params": { diff --git a/tests/auto/versioning/testversioning.cpp b/tests/auto/versioning/testversioning.cpp index 0a0b0ec9..3f96eab1 100644 --- a/tests/auto/versioning/testversioning.cpp +++ b/tests/auto/versioning/testversioning.cpp @@ -91,7 +91,7 @@ void TestVersioning::apiChangeBumpsVersion() p.waitForFinished(); QByteArray apiDiff = p.readAll(); - qDebug() << "API Differences:" << endl << qUtf8Printable(apiDiff); + qCDebug(dcTests()) << "API Differences:" << endl << qUtf8Printable(apiDiff); if (oldVersion == newVersionStripped && oldApi != newApi) { QVERIFY2(false, "JSONRPC API has changed but version is still the same. You need to bump the API version."); From 9283d31e737292b2d57afb8f33c24f931efad346 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 18 Mar 2019 23:04:11 +0100 Subject: [PATCH 3/8] drop basicTags and deviceIcon --- doc/jsonrpc-api.qdoc | 109 +--------------------- doc/plugin-json.qdoc | 31 +------ libnymea-core/jsonrpc/jsontypes.cpp | 26 ------ libnymea-core/jsonrpc/jsontypes.h | 2 - libnymea/plugin/deviceplugin.cpp | 74 --------------- libnymea/plugin/deviceplugin.h | 2 - libnymea/types/deviceclass.cpp | 135 +--------------------------- libnymea/types/deviceclass.h | 71 +-------------- plugins/mock/devicepluginmock.json | 38 -------- 9 files changed, 6 insertions(+), 482 deletions(-) diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index 4bb429ea..66861fed 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -134,13 +134,9 @@ See also: \l{Param} "actionTypes": [ "$ref:ActionType" ], - "basicTags": [ - "$ref:BasicTag" - ], "createMethods": [ "$ref:CreateMethod" ], - "deviceIcon": "$ref:DeviceIcon", "discoveryParamTypes": [ "$ref:ParamType" ], @@ -167,7 +163,7 @@ See also: \l{Param} "vendorId": "Uuid" } \endcode -See also: \l{ActionType}, \l{CreateMethod}, \l{DeviceIcon}, \l{BasicTag}, \l{ParamType}, \l{SetupMethod}, \l{StateType}, \l{EventType}, \l{ParamType} +See also: \l{ActionType}, \l{CreateMethod}, \l{ParamType}, \l{SetupMethod}, \l{StateType}, \l{EventType}, \l{ParamType} \section2 DeviceDescriptor \code { @@ -206,47 +202,6 @@ See also: \l{ActionType}, \l{CreateMethod}, \l{DeviceIcon}, \l{BasicTag}, \l{Par "DeviceErrorParameterNotWritable" ] \endcode -\section2 DeviceIcon -\code -[ - "DeviceIconNone", - "DeviceIconBed", - "DeviceIconBlinds", - "DeviceIconCeilingLamp", - "DeviceIconCouch", - "DeviceIconDeskLamp", - "DeviceIconDesk", - "DeviceIconHifi", - "DeviceIconPower", - "DeviceIconEnergy", - "DeviceIconRadio", - "DeviceIconSmartPhone", - "DeviceIconSocket", - "DeviceIconStandardLamp", - "DeviceIconSun", - "DeviceIconTablet", - "DeviceIconThermometer", - "DeviceIconTune", - "DeviceIconTv", - "DeviceIconBattery", - "DeviceIconDishwasher", - "DeviceIconWashingMachine", - "DeviceIconLaundryDryer", - "DeviceIconIrHeater", - "DeviceIconRadiator", - "DeviceIconSwitch", - "DeviceIconMotionDetectors", - "DeviceIconWeather", - "DeviceIconTime", - "DeviceIconLightBulb", - "DeviceIconGateway", - "DeviceIconMail", - "DeviceIconNetwork", - "DeviceIconCloud", - "DeviceIconGarage", - "DeviceIconRollerShutter" -] -\endcode \section2 Event \code { @@ -3403,26 +3358,6 @@ See also: \l{Tag} "$ref:ParamType" ] }, - "BasicTag": [ - "BasicTagService", - "BasicTagDevice", - "BasicTagSensor", - "BasicTagActuator", - "BasicTagLighting", - "BasicTagEnergy", - "BasicTagMultimedia", - "BasicTagWeather", - "BasicTagGateway", - "BasicTagHeating", - "BasicTagCooling", - "BasicTagNotification", - "BasicTagSecurity", - "BasicTagTime", - "BasicTagShading", - "BasicTagAppliance", - "BasicTagCamera", - "BasicTagLock" - ], "BasicType": [ "Uuid", "String", @@ -3482,13 +3417,9 @@ See also: \l{Tag} "actionTypes": [ "$ref:ActionType" ], - "basicTags": [ - "$ref:BasicTag" - ], "createMethods": [ "$ref:CreateMethod" ], - "deviceIcon": "$ref:DeviceIcon", "discoveryParamTypes": [ "$ref:ParamType" ], @@ -3545,44 +3476,6 @@ See also: \l{Tag} "DeviceErrorPairingTransactionIdNotFound", "DeviceErrorParameterNotWritable" ], - "DeviceIcon": [ - "DeviceIconNone", - "DeviceIconBed", - "DeviceIconBlinds", - "DeviceIconCeilingLamp", - "DeviceIconCouch", - "DeviceIconDeskLamp", - "DeviceIconDesk", - "DeviceIconHifi", - "DeviceIconPower", - "DeviceIconEnergy", - "DeviceIconRadio", - "DeviceIconSmartPhone", - "DeviceIconSocket", - "DeviceIconStandardLamp", - "DeviceIconSun", - "DeviceIconTablet", - "DeviceIconThermometer", - "DeviceIconTune", - "DeviceIconTv", - "DeviceIconBattery", - "DeviceIconDishwasher", - "DeviceIconWashingMachine", - "DeviceIconLaundryDryer", - "DeviceIconIrHeater", - "DeviceIconRadiator", - "DeviceIconSwitch", - "DeviceIconMotionDetectors", - "DeviceIconWeather", - "DeviceIconTime", - "DeviceIconLightBulb", - "DeviceIconGateway", - "DeviceIconMail", - "DeviceIconNetwork", - "DeviceIconCloud", - "DeviceIconGarage", - "DeviceIconRollerShutter" - ], "Event": { "deviceId": "Uuid", "eventTypeId": "Uuid", diff --git a/doc/plugin-json.qdoc b/doc/plugin-json.qdoc index f85ae1c3..e0c3a291 100644 --- a/doc/plugin-json.qdoc +++ b/doc/plugin-json.qdoc @@ -192,9 +192,7 @@ "displayName": "The name of the device class (translatable)", "o:createMethods": [ ], "o:setupMethod": "SetupMethod", - "o:deviceIcon": "Icon", "o:interfaces": [ "interfacename" ], - "o:basicTags": [ ], "o:pairingInfo": "Information how to pair the device. (translatable)", "o:criticalStateTypeId": "uuid", "o:primaryStateTypeId": "uuid", @@ -233,34 +231,9 @@ \li \tt interfaces \li \b O \li array - \li A string list of \l{Interfaces for DeviceClasses}{interfaces} this plugin implements. Interfaces show you how types - of this DeviceClass \underline{must} look like. + \li A string list of \l{Interfaces for DeviceClasses}{interfaces} this plugin implements. Interfaces define states, events and actions to + provide more defined ways of creating device class. A plugin developer should always try to follow interface definitions if possible. \row - \li \tt basicTags - \li \b O - \li array - \li A string list of \l{DeviceClass::BasicTag}{BasicTags} for this device \unicode{0x2192} \l{DeviceClass::basicTags()}. A \l{DeviceClass} can have - multiple \l{DeviceClass::BasicTag}{BasicTags} which describe the basic category of the DeviceClass. - A \l{DeviceClass} should be eighter a Service or a Device, never both. See enum \l{DeviceClass::BasicTag} for more information. - The expected value for the \e basicTags parameters matches the enum name like this: - - \tt {DeviceClass::BasicTagService} \unicode{0x2192} \tt {"basicTags": [ "Service" ]} - - \tt {DeviceClass::BasicTagLighting} \unicode{0x2192} \tt {"basicTags": [ "Lighting" ]} - - \tt ... - \row - \li \tt deviceIcon - \li \b O - \li string - \li Defines the icon for this \l{DeviceClass}. See enum \l{DeviceClass::DeviceIcon} for more information. The expected value for the \tt deviceIcon - parameters matches the enum name like this: - - \tt {DeviceClass::DeviceIconBed} \unicode{0x2192} \tt {"deviceIcon": "Bed"} - - \tt {DeviceClass::DeviceIconPower} \unicode{0x2192} \tt {"deviceIcon": "Power"} - - \tt ... \row \li \tt createMethods \li \b O diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index f8b87dc6..90b238bc 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -70,8 +70,6 @@ bool JsonTypes::s_initialized = false; QString JsonTypes::s_lastError; QVariantList JsonTypes::s_basicType; -QVariantList JsonTypes::s_basicTag; -QVariantList JsonTypes::s_deviceIcon; QVariantList JsonTypes::s_stateOperator; QVariantList JsonTypes::s_valueOperator; QVariantList JsonTypes::s_inputType; @@ -139,8 +137,6 @@ void JsonTypes::init() s_unit = enumToStrings(Types::staticMetaObject, "Unit"); s_createMethod = enumToStrings(DeviceClass::staticMetaObject, "CreateMethod"); s_setupMethod = enumToStrings(DeviceClass::staticMetaObject, "SetupMethod"); - s_basicTag = enumToStrings(DeviceClass::staticMetaObject, "BasicTag"); - s_deviceIcon = enumToStrings(DeviceClass::staticMetaObject, "DeviceIcon"); s_removePolicy = enumToStrings(RuleEngine::staticMetaObject, "RemovePolicy"); s_deviceError = enumToStrings(DeviceManager::staticMetaObject, "DeviceError"); s_ruleError = enumToStrings(RuleEngine::staticMetaObject, "RuleError"); @@ -277,9 +273,7 @@ void JsonTypes::init() s_deviceClass.insert("pluginId", basicTypeToString(Uuid)); s_deviceClass.insert("name", basicTypeToString(String)); s_deviceClass.insert("displayName", basicTypeToString(String)); - s_deviceClass.insert("deviceIcon", deviceIconRef()); s_deviceClass.insert("interfaces", QVariantList() << basicTypeToString(String)); - s_deviceClass.insert("basicTags", QVariantList() << basicTagRef()); s_deviceClass.insert("setupMethod", setupMethodRef()); s_deviceClass.insert("createMethods", QVariantList() << createMethodRef()); s_deviceClass.insert("o:criticalStateTypeId", basicTypeToString(Uuid)); @@ -437,13 +431,11 @@ QVariantMap JsonTypes::allTypes() { QVariantMap allTypes; allTypes.insert("BasicType", basicType()); - allTypes.insert("BasicTag", basicTag()); allTypes.insert("ParamType", paramTypeDescription()); allTypes.insert("InputType", inputType()); allTypes.insert("Unit", unit()); allTypes.insert("CreateMethod", createMethod()); allTypes.insert("SetupMethod", setupMethod()); - allTypes.insert("DeviceIcon", deviceIcon()); allTypes.insert("ValueOperator", valueOperator()); allTypes.insert("StateOperator", stateOperator()); allTypes.insert("RemovePolicy", removePolicy()); @@ -782,13 +774,8 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(deviceClass.pluginId(), deviceClass.displayName(), locale)); variant.insert("vendorId", deviceClass.vendorId().toString()); variant.insert("pluginId", deviceClass.pluginId().toString()); - variant.insert("deviceIcon", s_deviceIcon.at(deviceClass.deviceIcon())); variant.insert("interfaces", deviceClass.interfaces()); - QVariantList basicTags; - foreach (const DeviceClass::BasicTag &basicTag, deviceClass.basicTags()) - basicTags.append(s_basicTag.at(basicTag)); - QVariantList stateTypes; foreach (const StateType &stateType, deviceClass.stateTypes()) stateTypes.append(packStateType(stateType, deviceClass.pluginId(), locale)); @@ -818,7 +805,6 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo if (!deviceClass.primaryActionTypeId().isNull()) variant.insert("primaryActionTypeId", deviceClass.primaryActionTypeId().toString()); - variant.insert("basicTags", basicTags); variant.insert("paramTypes", paramTypes); variant.insert("discoveryParamTypes", discoveryParamTypes); variant.insert("stateTypes", stateTypes); @@ -2121,18 +2107,6 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(unitRef()); return result; } - } else if (refName == basicTagRef()) { - QPair result = validateEnum(s_basicTag, variant); - if (!result.first) { - qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(basicTagRef()); - return result; - } - } else if (refName == deviceIconRef()) { - QPair result = validateEnum(s_deviceIcon, variant); - if (!result.first) { - qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(deviceIconRef()); - return result; - } } else if (refName == repeatingModeRef()) { QPair result = validateEnum(s_repeatingMode, variant); if (!result.first) { diff --git a/libnymea-core/jsonrpc/jsontypes.h b/libnymea-core/jsonrpc/jsontypes.h index dda5c3cb..6fc417eb 100644 --- a/libnymea-core/jsonrpc/jsontypes.h +++ b/libnymea-core/jsonrpc/jsontypes.h @@ -120,14 +120,12 @@ public: static QVariantMap allTypes(); DECLARE_TYPE(basicType, "BasicType", JsonTypes, BasicType) - DECLARE_TYPE(basicTag, "BasicTag", DeviceClass, BasicTag) DECLARE_TYPE(stateOperator, "StateOperator", Types, StateOperator) DECLARE_TYPE(valueOperator, "ValueOperator", Types, ValueOperator) DECLARE_TYPE(inputType, "InputType", Types, InputType) DECLARE_TYPE(unit, "Unit", Types, Unit) DECLARE_TYPE(createMethod, "CreateMethod", DeviceClass, CreateMethod) DECLARE_TYPE(setupMethod, "SetupMethod", DeviceClass, SetupMethod) - DECLARE_TYPE(deviceIcon, "DeviceIcon", DeviceClass, DeviceIcon) DECLARE_TYPE(deviceError, "DeviceError", DeviceManager, DeviceError) DECLARE_TYPE(removePolicy, "RemovePolicy", RuleEngine, RemovePolicy) DECLARE_TYPE(ruleError, "RuleError", RuleEngine, RuleError) diff --git a/libnymea/plugin/deviceplugin.cpp b/libnymea/plugin/deviceplugin.cpp index ca7af802..4bc17924 100644 --- a/libnymea/plugin/deviceplugin.cpp +++ b/libnymea/plugin/deviceplugin.cpp @@ -594,15 +594,6 @@ void DevicePlugin::loadMetaData() } deviceClass.setCreateMethods(createMethods); - // Read device icon - QPair deviceIconVerification = loadAndVerifyDeviceIcon(deviceClassObject.value("deviceIcon").toString()); - if (!deviceIconVerification.first) { - broken = true; - break; - } else { - deviceClass.setDeviceIcon(deviceIconVerification.second); - } - // Read params QPair > paramTypesVerification = parseParamTypes(deviceClassObject.value("paramTypes").toArray()); if (!paramTypesVerification.first) { @@ -644,19 +635,6 @@ void DevicePlugin::loadMetaData() // Read pairing info deviceClass.setPairingInfo(deviceClassObject.value("pairingInfo").toString()); - // Read basic tags - QList basicTags; - foreach (const QJsonValue &basicTagJson, deviceClassObject.value("basicTags").toArray()) { - QPair basicTagVerification = loadAndVerifyBasicTag(basicTagJson.toString()); - if (!basicTagVerification.first) { - broken = true; - break; - } else { - basicTags.append(basicTagVerification.second); - } - } - deviceClass.setBasicTags(basicTags); - QList actionTypes; QList stateTypes; QList eventTypes; @@ -1053,58 +1031,6 @@ QPair DevicePlugin::loadAndVerifyInputType(const QString return QPair(true, (Types::InputType)enumValue); } -QPair DevicePlugin::loadAndVerifyBasicTag(const QString &basicTag) const -{ - if (basicTag.isEmpty()) - return QPair(true, DeviceClass::BasicTagDevice); - - QMetaObject metaObject = DeviceClass::staticMetaObject; - int enumIndex = metaObject.indexOfEnumerator(QString("BasicTag").toLatin1().data()); - QMetaEnum metaEnum = metaObject.enumerator(enumIndex); - - int enumValue = -1; - for (int i = 0; i < metaEnum.keyCount(); i++) { - if (QString(metaEnum.valueToKey(metaEnum.value(i))) == QString("BasicTag" + basicTag)) { - enumValue = metaEnum.value(i); - break; - } - } - - // inform the plugin developer about the error in the plugin json file - if (enumValue == -1) { - qCWarning(dcDeviceManager()) << QString("\"%1\" plugin:").arg(pluginName()).toLatin1().data() << QString("Invalid basicTag \"%1\" in json file.").arg(basicTag).toLatin1().data(); - return QPair(false, DeviceClass::BasicTagDevice); - } - - return QPair(true, (DeviceClass::BasicTag)enumValue); -} - -QPair DevicePlugin::loadAndVerifyDeviceIcon(const QString &deviceIcon) const -{ - if (deviceIcon.isEmpty()) - return QPair(true, DeviceClass::DeviceIconNone); - - QMetaObject metaObject = DeviceClass::staticMetaObject; - int enumIndex = metaObject.indexOfEnumerator(QString("DeviceIcon").toLatin1().data()); - QMetaEnum metaEnum = metaObject.enumerator(enumIndex); - - int enumValue = -1; - for (int i = 0; i < metaEnum.keyCount(); i++) { - if (QString(metaEnum.valueToKey(metaEnum.value(i))) == QString("DeviceIcon" + deviceIcon)) { - enumValue = metaEnum.value(i); - break; - } - } - - // inform the plugin developer about the error in the plugin json file - if (enumValue == -1) { - qCWarning(dcDeviceManager()) << QString("\"%1\" plugin:").arg(pluginName()).toLatin1().data() << QString("Invalid deviceIcon \"%1\" in json file.").arg(deviceIcon).toLatin1().data(); - return QPair(false, DeviceClass::DeviceIconNone); - } - - return QPair(true, (DeviceClass::DeviceIcon)enumValue); -} - Interfaces DevicePlugin::allInterfaces() { Interfaces ret; diff --git a/libnymea/plugin/deviceplugin.h b/libnymea/plugin/deviceplugin.h index b3a19389..1d5c7845 100644 --- a/libnymea/plugin/deviceplugin.h +++ b/libnymea/plugin/deviceplugin.h @@ -113,8 +113,6 @@ private: // load and verify enum values QPair loadAndVerifyUnit(const QString &unitString) const; QPair loadAndVerifyInputType(const QString &inputType) const; - QPair loadAndVerifyBasicTag(const QString &basicTag) const; - QPair loadAndVerifyDeviceIcon(const QString &deviceIcon) const; // FIXME: This is expensive because it will open all the files. // Once DeviceManager is in libnymea-core this should probably be there too. diff --git a/libnymea/types/deviceclass.cpp b/libnymea/types/deviceclass.cpp index 80b31f8d..9a8306d5 100644 --- a/libnymea/types/deviceclass.cpp +++ b/libnymea/types/deviceclass.cpp @@ -62,103 +62,6 @@ During the setup, a button has to be pushed in order to pair the \l{Device}. */ -/*! \enum DeviceClass::BasicTag - - This enum type specifies the basic tags which describe the categories of the DeviceClass. - A DeviceClass can have multiple tags. - - \value BasicTagService - The \l{DeviceClass} describes a service. - \value BasicTagDevice - The \l{DeviceClass} describes a real device. - \value BasicTagSensor - The \l{DeviceClass} describes a sensor. Any device which can measure or detect something is a sensor. - \value BasicTagActuator - The \l{DeviceClass} describes an actuator. Any device which can do something is an actuator. - \value BasicTagLighting - The \l{DeviceClass} describes a lighting device. - \value BasicTagEnergy - The \l{DeviceClass} describes an energy device. - \value BasicTagMultimedia - The \l{DeviceClass} describes a multimedia device/service. - \value BasicTagWeather - The \l{DeviceClass} describes a weather device/service. - \value BasicTagGateway - The \l{DeviceClass} describes a gateway device. - \value BasicTagHeating - The \l{DeviceClass} describes a heating device. - \value BasicTagCooling - The \l{DeviceClass} describes a cooling device. - \value BasicTagNotification - The \l{DeviceClass} describes a notification service. - \value BasicTagSecurity - The \l{DeviceClass} describes a security device/service. - \value BasicTagTime - The \l{DeviceClass} describes a time device/service. - \value BasicTagShading - The \l{DeviceClass} describes a shading device. - \value BasicTagAppliance - The \l{DeviceClass} describes an appliance. - \value BasicTagCamera - The \l{DeviceClass} describes a camera device. - \value BasicTagLock - The \l{DeviceClass} describes a lock device. - -*/ - -/*! \enum DeviceClass::DeviceIcon - - This enum type specifies the default device icon of the DeviceClass. - Each client should have an icon set containing this list of icon types. - If a client want's to offer special icons for a special DeviceClass or device type, - he can bring it's own icon for a special DeviceClassId. If there is no special icon - defined, he can fallback to the default icon. - - \value DeviceIconNone - \value DeviceIconBed - \value DeviceIconBlinds - \value DeviceIconCeilingLamp - \value DeviceIconCouch - \value DeviceIconDeskLamp - \value DeviceIconDesk - \value DeviceIconHifi - \value DeviceIconPower - \value DeviceIconEnergy - \value DeviceIconRadio - \value DeviceIconSmartPhone - \value DeviceIconSocket - \value DeviceIconStandardLamp - \value DeviceIconSun - \value DeviceIconTablet - \value DeviceIconThermometer - \value DeviceIconTune - \value DeviceIconTv - \value DeviceIconBattery - \value DeviceIconDishwasher - \value DeviceIconWashingMachine - \value DeviceIconLaundryDryer - \value DeviceIconIrHeater - \value DeviceIconRadiator - \value DeviceIconSwitch - \value DeviceIconMotionDetectors - \value DeviceIconWeather - \value DeviceIconTime - \value DeviceIconLightBulb - \value DeviceIconGateway - \value DeviceIconMail - \value DeviceIconNetwork - \value DeviceIconCloud - \value DeviceIconGarage, - \value DeviceIconRollerShutter -*/ - -/*! \fn void DeviceClass::setBasicTags(const QList &basicTags); - Set the list of \a basicTags of this DeviceClass. -*/ - -/*! \fn void DeviceClass::setDeviceIcon(const DeviceIcon &deviceIcon); - Set the \a deviceIcon of this DeviceClass. -*/ #include "deviceclass.h" @@ -173,7 +76,6 @@ DeviceClass::DeviceClass(const PluginId &pluginId, const VendorId &vendorId, con m_criticalStateTypeId(StateTypeId()), m_primaryStateTypeId(StateTypeId()), m_primaryActionTypeId(ActionTypeId()), - m_deviceIcon(DeviceIconPower), m_createMethods(CreateMethodUser), m_setupMethod(SetupMethodJustAdd) { @@ -265,39 +167,6 @@ void DeviceClass::setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId m_primaryActionTypeId = primaryActionTypeId; } -/*! Returns the default \l{DeviceIcon} of this \l{DeviceClass}. */ -DeviceClass::DeviceIcon DeviceClass::deviceIcon() const -{ - return m_deviceIcon; -} - -/*! Set the \a deviceIcon of this \l{DeviceClass}. - - \sa DeviceClass::DeviceIcon -*/ -void DeviceClass::setDeviceIcon(const DeviceClass::DeviceIcon &deviceIcon) -{ - m_deviceIcon = deviceIcon; -} - -/*! Returns the list of basicTags of this DeviceClass. - - \sa DeviceClass::BasicTag -*/ -QList DeviceClass::basicTags() const -{ - return m_basicTags; -} - -/*! Set the list of \a basicTags of this DeviceClass. - - \sa DeviceClass::BasicTag -*/ -void DeviceClass::setBasicTags(const QList &basicTags) -{ - m_basicTags = basicTags; -} - /*! Returns the statesTypes of this DeviceClass. \{Device}{Devices} created from this \l{DeviceClass} must have their states matching to this template. */ StateTypes DeviceClass::stateTypes() const @@ -474,8 +343,8 @@ bool DeviceClass::operator==(const DeviceClass &deviceClass) const /*! Returns a list of all valid JSON properties a DeviceClass JSON definition can have. */ QStringList DeviceClass::typeProperties() { - return QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" << "deviceIcon" - << "interfaces" << "basicTags" << "pairingInfo" << "criticalStateTypeId" + return QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" + << "interfaces" << "pairingInfo" << "criticalStateTypeId" << "primaryStateTypeId" << "primaryActionTypeId" << "discoveryParamTypes" << "discoveryParamTypes" << "paramTypes" << "stateTypes" << "actionTypes" << "eventTypes"; } diff --git a/libnymea/types/deviceclass.h b/libnymea/types/deviceclass.h index 43f1eb97..010d4b05 100644 --- a/libnymea/types/deviceclass.h +++ b/libnymea/types/deviceclass.h @@ -41,8 +41,7 @@ class LIBNYMEA_EXPORT DeviceClass Q_ENUMS(CreateMethod) Q_ENUMS(SetupMethod) Q_ENUMS(BasicTag) - Q_ENUMS(DeviceIcon) - Q_FLAGS(CreateMethods) + Q_ENUMS(CreateMethods) public: enum CreateMethod { @@ -59,66 +58,6 @@ public: SetupMethodPushButton }; - enum BasicTag { - BasicTagService, - BasicTagDevice, - BasicTagSensor, - BasicTagActuator, - BasicTagLighting, - BasicTagEnergy, - BasicTagMultimedia, - BasicTagWeather, - BasicTagGateway, - BasicTagHeating, - BasicTagCooling, - BasicTagNotification, - BasicTagSecurity, - BasicTagTime, - BasicTagShading, - BasicTagAppliance, - BasicTagCamera, - BasicTagLock - }; - - enum DeviceIcon { - DeviceIconNone, - DeviceIconBed, - DeviceIconBlinds, - DeviceIconCeilingLamp, - DeviceIconCouch, - DeviceIconDeskLamp, - DeviceIconDesk, - DeviceIconHifi, - DeviceIconPower, - DeviceIconEnergy, - DeviceIconRadio, - DeviceIconSmartPhone, - DeviceIconSocket, - DeviceIconStandardLamp, - DeviceIconSun, - DeviceIconTablet, - DeviceIconThermometer, - DeviceIconTune, - DeviceIconTv, - DeviceIconBattery, - DeviceIconDishwasher, - DeviceIconWashingMachine, - DeviceIconLaundryDryer, - DeviceIconIrHeater, - DeviceIconRadiator, - DeviceIconSwitch, - DeviceIconMotionDetectors, - DeviceIconWeather, - DeviceIconTime, - DeviceIconLightBulb, - DeviceIconGateway, - DeviceIconMail, - DeviceIconNetwork, - DeviceIconCloud, - DeviceIconGarage, - DeviceIconRollerShutter - }; - DeviceClass(const PluginId &pluginId = PluginId(), const VendorId &vendorId = VendorId(), const DeviceClassId &id = DeviceClassId()); DeviceClassId id() const; @@ -141,12 +80,6 @@ public: ActionTypeId primaryActionTypeId() const; void setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId); - DeviceIcon deviceIcon() const; - void setDeviceIcon(const DeviceIcon &deviceIcon); - - QList basicTags() const; - void setBasicTags(const QList &basicTags); - StateTypes stateTypes() const; StateType getStateType(const StateTypeId &stateTypeId); void setStateTypes(const QList &stateTypes); @@ -192,8 +125,6 @@ private: StateTypeId m_criticalStateTypeId; StateTypeId m_primaryStateTypeId; ActionTypeId m_primaryActionTypeId; - DeviceIcon m_deviceIcon; - QList m_basicTags; QList m_stateTypes; QList m_eventTypes; QList m_actionTypes; diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index 91a73636..28c6e657 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -30,13 +30,7 @@ "id": "753f0d32-0468-4d08-82ed-1964aab03298", "name": "mock", "displayName": "Mock Device", - "deviceIcon": "Tune", "interfaces": ["system", "light", "gateway", "battery"], - "basicTags": [ - "Device", - "Actuator", - "Gateway" - ], "createMethods": ["user", "discovery"], "primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935", "primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83", @@ -201,15 +195,9 @@ "name": "mockDeviceAuto", "displayName": "Mock Device (Auto created)", "interfaces": ["system"], - "basicTags": [ - "Device", - "Actuator", - "Gateway" - ], "createMethods": ["auto"], "primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935", "primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83", - "deviceIcon": "Tune", "paramTypes": [ { "id": "d4f06047-125e-4479-9810-b54c189917f5", @@ -321,14 +309,8 @@ "name": "mockPushButton", "displayName": "Mock Device (Push Button)", "interfaces": ["system"], - "basicTags": [ - "Device", - "Actuator", - "Gateway" - ], "createMethods": ["discovery"], "setupMethod": "pushButton", - "deviceIcon": "Tune", "pairingInfo": "Wait 3 second before you continue, the push button will be pressed automatically.", "paramTypes": [ ], "discoveryParamTypes": [ @@ -419,13 +401,7 @@ "id": "296f1fd4-e893-46b2-8a42-50d1bceb8730", "name": "mockDisplayPin", "displayName": "Mock Device (Display Pin)", - "deviceIcon": "Tune", "interfaces": ["system"], - "basicTags": [ - "Device", - "Actuator", - "Gateway" - ], "createMethods": ["discovery"], "setupMethod": "displayPin", "pairingInfo": "Please enter the secret which normaly will be displayed on the device. For the mockdevice the pin is 243681.", @@ -528,13 +504,7 @@ "id": "a71fbde9-9a38-4bf8-beab-c8aade2608ba", "name": "mockParent", "displayName": "Mock Device (Parent)", - "deviceIcon": "Tune", "interfaces": ["system"], - "basicTags": [ - "Device", - "Actuator", - "Gateway" - ], "createMethods": ["user"], "paramTypes": [ ], "stateTypes": [ @@ -556,10 +526,6 @@ "displayName": "Mock Device (Child)", "createMethods": ["auto"], "paramTypes": [], - "basicTags": [ - "Device", - "Actuator" - ], "stateTypes": [ { "id": "d24ede5f-4064-4898-bb84-cfb533b1fbc0", @@ -577,10 +543,6 @@ "id": "515ffdf1-55e5-498d-9abc-4e2fe768f3a9", "name": "mockInputType", "displayName": "Mock Device (InputTypes)", - "deviceIcon": "Tune", - "basicTags": [ - "Device" - ], "createMethods": ["user"], "paramTypes": [ { From bab59f6754c1d04717b855bf0da4fada3baa6e15 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 18 Mar 2019 23:09:19 +0100 Subject: [PATCH 4/8] drop criticalStateTypeId, primaryStateTypeId and primaryActionTypeId --- doc/jsonrpc-api.qdoc | 6 ---- doc/plugin-json.qdoc | 18 ------------ libnymea-core/jsonrpc/jsontypes.cpp | 12 -------- libnymea/plugin/deviceplugin.cpp | 35 ---------------------- libnymea/types/deviceclass.cpp | 45 ++--------------------------- libnymea/types/deviceclass.h | 12 -------- plugins/mock/devicepluginmock.json | 4 --- 7 files changed, 2 insertions(+), 130 deletions(-) diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index 66861fed..11737a89 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -149,9 +149,6 @@ See also: \l{Param} "String" ], "name": "String", - "o:criticalStateTypeId": "Uuid", - "o:primaryActionTypeId": "Uuid", - "o:primaryStateTypeId": "Uuid", "paramTypes": [ "$ref:ParamType" ], @@ -3432,9 +3429,6 @@ See also: \l{Tag} "String" ], "name": "String", - "o:criticalStateTypeId": "Uuid", - "o:primaryActionTypeId": "Uuid", - "o:primaryStateTypeId": "Uuid", "paramTypes": [ "$ref:ParamType" ], diff --git a/doc/plugin-json.qdoc b/doc/plugin-json.qdoc index e0c3a291..49e6323b 100644 --- a/doc/plugin-json.qdoc +++ b/doc/plugin-json.qdoc @@ -194,9 +194,6 @@ "o:setupMethod": "SetupMethod", "o:interfaces": [ "interfacename" ], "o:pairingInfo": "Information how to pair the device. (translatable)", - "o:criticalStateTypeId": "uuid", - "o:primaryStateTypeId": "uuid", - "o:primaryActionTypeId": "uuid", "o:discoveryParamTypes": [ ], "o:paramTypes": [ ], "o:stateTypes": [ ], @@ -268,21 +265,6 @@ \li The \l{DeviceClass::pairingInfo()}{pairingInfo} will inform the user how to pair the device \unicode{0x2192} \l{DeviceClass::setupMethod()}. This parameter will only be used for \l{DeviceClass::SetupMethodDisplayPin}{DisplayPin} and \l{DeviceClass::SetupMethodEnterPin}{EnterPin} and \l{DeviceClass::SetupMethodPushButton}{PushButton}. Example: "Please press the button on the device before continue." - \row - \li \tt criticalStateTypeId - \li \b O - \li string - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. - \row - \li \tt primaryStateTypeId - \li \b O - \li string - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. - \row - \li \tt primaryActionTypeId - \li \b O - \li string - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. \row \li \tt discoveryParamTypes \li \b O diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 90b238bc..65036248 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -276,9 +276,6 @@ void JsonTypes::init() s_deviceClass.insert("interfaces", QVariantList() << basicTypeToString(String)); s_deviceClass.insert("setupMethod", setupMethodRef()); s_deviceClass.insert("createMethods", QVariantList() << createMethodRef()); - s_deviceClass.insert("o:criticalStateTypeId", basicTypeToString(Uuid)); - s_deviceClass.insert("o:primaryStateTypeId", basicTypeToString(Uuid)); - s_deviceClass.insert("o:primaryActionTypeId", basicTypeToString(Uuid)); s_deviceClass.insert("stateTypes", QVariantList() << stateTypeRef()); s_deviceClass.insert("eventTypes", QVariantList() << eventTypeRef()); s_deviceClass.insert("actionTypes", QVariantList() << actionTypeRef()); @@ -796,15 +793,6 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo foreach (const ParamType ¶mType, deviceClass.discoveryParamTypes()) discoveryParamTypes.append(packParamType(paramType, deviceClass.pluginId(), locale)); - if (!deviceClass.criticalStateTypeId().isNull()) - variant.insert("criticalStateTypeId", deviceClass.criticalStateTypeId().toString()); - - if (!deviceClass.primaryStateTypeId().isNull()) - variant.insert("primaryStateTypeId", deviceClass.primaryStateTypeId().toString()); - - if (!deviceClass.primaryActionTypeId().isNull()) - variant.insert("primaryActionTypeId", deviceClass.primaryActionTypeId().toString()); - variant.insert("paramTypes", paramTypes); variant.insert("discoveryParamTypes", discoveryParamTypes); variant.insert("stateTypes", stateTypes); diff --git a/libnymea/plugin/deviceplugin.cpp b/libnymea/plugin/deviceplugin.cpp index 4bc17924..f257ba70 100644 --- a/libnymea/plugin/deviceplugin.cpp +++ b/libnymea/plugin/deviceplugin.cpp @@ -833,41 +833,6 @@ void DevicePlugin::loadMetaData() } deviceClass.setEventTypes(eventTypes); - // Note: keep this after the actionType / stateType / eventType parsing - if (deviceClassObject.contains("criticalStateTypeId")) { - StateTypeId criticalStateTypeId = StateTypeId(deviceClassObject.value("criticalStateTypeId").toString()); - if (!deviceClass.hasStateType(criticalStateTypeId)) { - qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend critical stateTypeId" << criticalStateTypeId.toString() << "does not match any StateType of this DeviceClass."; - broken = true; - } else if (deviceClass.getStateType(criticalStateTypeId).type() != QVariant::Bool) { - // Make sure the critical stateType is a bool state - qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend critical stateTypeId" << criticalStateTypeId.toString() << "is not a bool StateType."; - broken = true; - } else { - deviceClass.setCriticalStateTypeId(criticalStateTypeId); - } - } - - if (deviceClassObject.contains("primaryStateTypeId")) { - StateTypeId primaryStateTypeId = StateTypeId(deviceClassObject.value("primaryStateTypeId").toString()); - if (!deviceClass.hasStateType(primaryStateTypeId)) { - qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend primary stateTypeId" << primaryStateTypeId.toString() << "does not match any StateType of this DeviceClass."; - broken = true; - } else { - deviceClass.setPrimaryStateTypeId(primaryStateTypeId); - } - } - - if (deviceClassObject.contains("primaryActionTypeId")) { - ActionTypeId primaryActionTypeId = ActionTypeId(deviceClassObject.value("primaryActionTypeId").toString()); - if (!deviceClass.hasActionType(primaryActionTypeId)) { - qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend primary actionTypeId" << primaryActionTypeId.toString() << "does not match any ActionType of this DeviceClass."; - broken = true; - } else { - deviceClass.setPrimaryActionTypeId(primaryActionTypeId); - } - } - // Read interfaces QStringList interfaces; foreach (const QJsonValue &value, deviceClassObject.value("interfaces").toArray()) { diff --git a/libnymea/types/deviceclass.cpp b/libnymea/types/deviceclass.cpp index 9a8306d5..5c17e3f4 100644 --- a/libnymea/types/deviceclass.cpp +++ b/libnymea/types/deviceclass.cpp @@ -73,9 +73,6 @@ DeviceClass::DeviceClass(const PluginId &pluginId, const VendorId &vendorId, con m_id(id), m_vendorId(vendorId), m_pluginId(pluginId), - m_criticalStateTypeId(StateTypeId()), - m_primaryStateTypeId(StateTypeId()), - m_primaryActionTypeId(ActionTypeId()), m_createMethods(CreateMethodUser), m_setupMethod(SetupMethodJustAdd) { @@ -130,43 +127,6 @@ void DeviceClass::setDisplayName(const QString &displayName) m_displayName = displayName; } -/*! Returns the critical \l{StateTypeId} of this \l{DeviceClass}. - * A critical \l{State} describes the state which disables the whole device (i.e. connected, available or reachable). */ -StateTypeId DeviceClass::criticalStateTypeId() const -{ - return m_criticalStateTypeId; -} - -/*! Set the \a criticalStateTypeId of this \l{DeviceClass}. */ -void DeviceClass::setCriticalStateTypeId(const StateTypeId &criticalStateTypeId) -{ - m_criticalStateTypeId = criticalStateTypeId; -} - -/*! Returns the primary \l{StateTypeId} of this \l{DeviceClass}. */ -StateTypeId DeviceClass::primaryStateTypeId() const -{ - return m_primaryStateTypeId; -} - -/*! Set the \a primaryStateTypeId of this \l{DeviceClass}. */ -void DeviceClass::setPrimaryStateTypeId(const StateTypeId &primaryStateTypeId) -{ - m_primaryStateTypeId = primaryStateTypeId; -} - -/*! Returns the primary \l{ActionTypeId} of this \l{DeviceClass}. */ -ActionTypeId DeviceClass::primaryActionTypeId() const -{ - return m_primaryActionTypeId; -} - -/*! Set the \a primaryActionTypeId of this \l{DeviceClass}. */ -void DeviceClass::setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId) -{ - m_primaryActionTypeId = primaryActionTypeId; -} - /*! Returns the statesTypes of this DeviceClass. \{Device}{Devices} created from this \l{DeviceClass} must have their states matching to this template. */ StateTypes DeviceClass::stateTypes() const @@ -344,9 +304,8 @@ bool DeviceClass::operator==(const DeviceClass &deviceClass) const QStringList DeviceClass::typeProperties() { return QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" - << "interfaces" << "pairingInfo" << "criticalStateTypeId" - << "primaryStateTypeId" << "primaryActionTypeId" << "discoveryParamTypes" - << "discoveryParamTypes" << "paramTypes" << "stateTypes" << "actionTypes" << "eventTypes"; + << "interfaces" << "pairingInfo" << "discoveryParamTypes" << "discoveryParamTypes" + << "paramTypes" << "stateTypes" << "actionTypes" << "eventTypes"; } /*! Returns a list of mandatory JSON properties a DeviceClass JSON definition must have. */ diff --git a/libnymea/types/deviceclass.h b/libnymea/types/deviceclass.h index 010d4b05..1c95ca33 100644 --- a/libnymea/types/deviceclass.h +++ b/libnymea/types/deviceclass.h @@ -71,15 +71,6 @@ public: QString displayName() const; void setDisplayName(const QString &displayName); - StateTypeId criticalStateTypeId() const; - void setCriticalStateTypeId(const StateTypeId &criticalStateTypeId); - - StateTypeId primaryStateTypeId() const; - void setPrimaryStateTypeId(const StateTypeId &primaryStateTypeId); - - ActionTypeId primaryActionTypeId() const; - void setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId); - StateTypes stateTypes() const; StateType getStateType(const StateTypeId &stateTypeId); void setStateTypes(const QList &stateTypes); @@ -122,9 +113,6 @@ private: PluginId m_pluginId; QString m_name; QString m_displayName; - StateTypeId m_criticalStateTypeId; - StateTypeId m_primaryStateTypeId; - ActionTypeId m_primaryActionTypeId; QList m_stateTypes; QList m_eventTypes; QList m_actionTypes; diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index 28c6e657..6278cd82 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -32,8 +32,6 @@ "displayName": "Mock Device", "interfaces": ["system", "light", "gateway", "battery"], "createMethods": ["user", "discovery"], - "primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935", - "primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83", "discoveryParamTypes": [ { "id": "d222adb4-2f9c-4c3f-8655-76400d0fb6ce", @@ -196,8 +194,6 @@ "displayName": "Mock Device (Auto created)", "interfaces": ["system"], "createMethods": ["auto"], - "primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935", - "primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83", "paramTypes": [ { "id": "d4f06047-125e-4479-9810-b54c189917f5", From 78933285341b1d706424803e5c73066a82716cbb Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 18 Mar 2019 23:15:15 +0100 Subject: [PATCH 5/8] drop ruleRelevant, eventRuleRelevant and graphRelevant --- doc/jsonrpc-api.qdoc | 8 ------- doc/plugin-json.qdoc | 34 ----------------------------- libnymea-core/jsonrpc/jsontypes.cpp | 15 ------------- libnymea/plugin/deviceplugin.cpp | 14 ------------ libnymea/types/eventtype.cpp | 30 ++----------------------- libnymea/types/eventtype.h | 8 ------- libnymea/types/statetype.cpp | 28 ++---------------------- libnymea/types/statetype.h | 8 ------- plugins/mock/devicepluginmock.json | 11 +--------- 9 files changed, 5 insertions(+), 151 deletions(-) diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index 11737a89..a1b0d5b8 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -230,8 +230,6 @@ See also: \l{ParamDescriptor} "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", - "o:ruleRelevant": "Bool", "paramTypes": [ "$ref:ParamType" ] @@ -580,13 +578,11 @@ See also: \l{StateEvaluator}, \l{StateDescriptor}, \l{StateOperator} "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", "o:maxValue": "Variant", "o:minValue": "Variant", "o:possibleValues": [ "Variant" ], - "o:ruleRelevant": "Bool", "o:unit": "$ref:Unit", "type": "$ref:BasicType" } @@ -3491,8 +3487,6 @@ See also: \l{Tag} "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", - "o:ruleRelevant": "Bool", "paramTypes": [ "$ref:ParamType" ] @@ -3742,13 +3736,11 @@ See also: \l{Tag} "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", "o:maxValue": "Variant", "o:minValue": "Variant", "o:possibleValues": [ "Variant" ], - "o:ruleRelevant": "Bool", "o:unit": "$ref:Unit", "type": "$ref:BasicType" }, diff --git a/doc/plugin-json.qdoc b/doc/plugin-json.qdoc index 49e6323b..4dc9eec2 100644 --- a/doc/plugin-json.qdoc +++ b/doc/plugin-json.qdoc @@ -431,9 +431,6 @@ A \l{StateType} has following parameters: "type": "DataType", "defaultValue": "The state will be initialized with this value." "o:cached": "bool", - "o:ruleRelevant": "bool", - "o:eventRuleRelevant": "bool", - "o:graphRelevant": "bool", "o:unit": "The unit of the state value.", "o:minValue": "Numeric minimum value for this state.", "o:maxValue": "Numeric maximum value for this state.", @@ -492,23 +489,6 @@ A \l{StateType} has following parameters: \li Indicates if a state value should be cached over reboot of the server. The value will be initialized with the last known value. By default all states get chached. If you want to disable that behaviour you can set this property to \tt{false}. In that case the value will be initialized with the default value of the State. - \row - \li \tt ruleRelevant - \li \b O - \li bool - \li Since not all \l{State}{States} make sense for the user in a rule, with this flag can be specified if this state should be visible - in the rule engine for the user or not. This flag has no effect to the ruleengine mechanism and is only ment to filter out not - interesting \l{State}{States}. By default, every state is rule relevant. - \row - \li \tt eventRuleRelevant - \li \b O - \li bool - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. - \row - \li \tt graphRelevant - \li \b O - \li bool - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. \row \li \tt unit \li \b O @@ -665,8 +645,6 @@ A \l{StateType} has following parameters: "id": "uuid", "name": "eventName", "displayName": "Name of the event (translatable)", - "o:ruleRelevant": "bool", - "o:graphRelevant": "bool", "o:paramTypes": [ ... ] @@ -700,18 +678,6 @@ A \l{StateType} has following parameters: \li A list of \l{ParamType}{ParamTypes} which define the parameters of this event \unicode{0x2192} \l{EventType::paramTypes()}. \b{See also:} \l{The ParamType definition}" - \row - \li \tt ruleRelevant - \li \b O - \li bool - \li Since not all \l{Event}{Events} make sense for the user in a rule, with this flag can be specidied if this event should be visible in the rule engine - for the user or not. This flag has no effect to the ruleengine mechanism and is only ment to filter out not interesting \l{Event}{Events}. By default, - every event is rule relevant. - \row - \li \tt graphRelevant - \li \b O - \li bool - \li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead. \endtable */ diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 65036248..8daf0a52 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -199,8 +199,6 @@ void JsonTypes::init() s_stateType.insert("index", basicTypeToString(Int)); s_stateType.insert("defaultValue", basicTypeToString(Variant)); s_stateType.insert("o:unit", unitRef()); - s_stateType.insert("o:ruleRelevant", basicTypeToString(Bool)); - s_stateType.insert("o:graphRelevant", basicTypeToString(Bool)); s_stateType.insert("o:minValue", basicTypeToString(Variant)); s_stateType.insert("o:maxValue", basicTypeToString(Variant)); s_stateType.insert("o:possibleValues", QVariantList() << basicTypeToString(Variant)); @@ -229,8 +227,6 @@ void JsonTypes::init() s_eventType.insert("displayName", basicTypeToString(String)); s_eventType.insert("index", basicTypeToString(Int)); s_eventType.insert("paramTypes", QVariantList() << paramTypeRef()); - s_eventType.insert("o:ruleRelevant", basicTypeToString(Bool)); - s_eventType.insert("o:graphRelevant", basicTypeToString(Bool)); // Event s_event.insert("eventTypeId", basicTypeToString(Uuid)); @@ -496,11 +492,6 @@ QVariantMap JsonTypes::packEventType(const EventType &eventType, const PluginId variant.insert("name", eventType.name()); variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, eventType.displayName(), locale)); variant.insert("index", eventType.index()); - if (!eventType.ruleRelevant()) - variant.insert("ruleRelevant", false); - - if (eventType.graphRelevant()) - variant.insert("graphRelevant", true); QVariantList paramTypes; foreach (const ParamType ¶mType, eventType.paramTypes()) @@ -631,12 +622,6 @@ QVariantMap JsonTypes::packStateType(const StateType &stateType, const PluginId variantMap.insert("type", basicTypeToString(stateType.type())); variantMap.insert("defaultValue", stateType.defaultValue()); - if (!stateType.ruleRelevant()) - variantMap.insert("ruleRelevant", false); - - if (stateType.graphRelevant()) - variantMap.insert("graphRelevant", true); - if (stateType.maxValue().isValid()) variantMap.insert("maxValue", stateType.maxValue()); diff --git a/libnymea/plugin/deviceplugin.cpp b/libnymea/plugin/deviceplugin.cpp index f257ba70..8a767b4f 100644 --- a/libnymea/plugin/deviceplugin.cpp +++ b/libnymea/plugin/deviceplugin.cpp @@ -698,12 +698,6 @@ void DevicePlugin::loadMetaData() if (st.contains("maxValue")) stateType.setMaxValue(st.value("maxValue").toVariant()); - if (st.contains("ruleRelevant")) - stateType.setRuleRelevant(st.value("ruleRelevant").toBool()); - - if (st.contains("graphRelevant")) - stateType.setGraphRelevant(st.value("graphRelevant").toBool()); - if (st.contains("possibleValues")) { QVariantList possibleValues; foreach (const QJsonValue &possibleValueJson, st.value("possibleValues").toArray()) { @@ -726,9 +720,6 @@ void DevicePlugin::loadMetaData() // Events for state changed EventType eventType(EventTypeId(stateType.id().toString())); - if (st.contains("eventRuleRelevant")) - eventType.setRuleRelevant(st.value("eventRuleRelevant").toBool()); - eventType.setName(st.value("name").toString()); eventType.setDisplayName(st.value("displayNameEvent").toString()); ParamType paramType(ParamTypeId(stateType.id().toString()), st.value("name").toString(), stateType.type()); @@ -816,11 +807,6 @@ void DevicePlugin::loadMetaData() eventType.setName(et.value("name").toString()); eventType.setDisplayName(et.value("displayName").toString()); eventType.setIndex(index++); - if (et.contains("ruleRelevant")) - eventType.setRuleRelevant(et.value("ruleRelevant").toBool()); - - if (et.contains("graphRelevant")) - eventType.setGraphRelevant(et.value("graphRelevant").toBool()); QPair > paramVerification = parseParamTypes(et.value("paramTypes").toArray()); if (!paramVerification.first) { diff --git a/libnymea/types/eventtype.cpp b/libnymea/types/eventtype.cpp index 31468899..b7a10fa1 100644 --- a/libnymea/types/eventtype.cpp +++ b/libnymea/types/eventtype.cpp @@ -36,9 +36,7 @@ /*! Constructs a EventType object with the given \a id. */ EventType::EventType(const EventTypeId &id): m_id(id), - m_index(0), - m_ruleRelevant(true), - m_graphRelevant(false) + m_index(0) { } @@ -100,30 +98,6 @@ void EventType::setParamTypes(const ParamTypes ¶mTypes) m_paramTypes = paramTypes; } -/*! Returns true if this EventType is relevant for the rule from a user perspective. */ -bool EventType::ruleRelevant() const -{ - return m_ruleRelevant; -} - -/*! Sets this EventType relevant for the rule from a user perspective to \a ruleRelevant. */ -void EventType::setRuleRelevant(const bool &ruleRelevant) -{ - m_ruleRelevant = ruleRelevant; -} - -/*! Returns true if this EventType is interesting to visualize the logs in a graph/chart from a user perspective. */ -bool EventType::graphRelevant() const -{ - return m_graphRelevant; -} - -/*! Sets this EventType \a graphRelevant to inform the client application if this \l{EventType} is interesting to visualize the logs in a graph/chart. */ -void EventType::setGraphRelevant(const bool &graphRelevant) -{ - m_graphRelevant = graphRelevant; -} - /*! Returns true if this EventType has a valid id and name */ bool EventType::isValid() const { @@ -133,7 +107,7 @@ bool EventType::isValid() const /*! Returns a list of all valid JSON properties a EventType JSON definition can have. */ QStringList EventType::typeProperties() { - return QStringList() << "id" << "name" << "displayName" << "paramTypes" << "ruleRelevant" << "graphRelevant"; + return QStringList() << "id" << "name" << "displayName" << "paramTypes"; } /*! Returns a list of mandatory JSON properties a EventType JSON definition must have. */ diff --git a/libnymea/types/eventtype.h b/libnymea/types/eventtype.h index e0a06162..c50ec651 100644 --- a/libnymea/types/eventtype.h +++ b/libnymea/types/eventtype.h @@ -49,12 +49,6 @@ public: ParamTypes paramTypes() const; void setParamTypes(const ParamTypes ¶mTypes); - bool ruleRelevant() const; - void setRuleRelevant(const bool &ruleRelevant); - - bool graphRelevant() const; - void setGraphRelevant(const bool &graphRelevant); - bool isValid() const; static QStringList typeProperties(); @@ -66,8 +60,6 @@ private: QString m_displayName; int m_index; QList m_paramTypes; - bool m_ruleRelevant; - bool m_graphRelevant; }; class EventTypes: public QList diff --git a/libnymea/types/statetype.cpp b/libnymea/types/statetype.cpp index cad8e0d4..acbd55c9 100644 --- a/libnymea/types/statetype.cpp +++ b/libnymea/types/statetype.cpp @@ -160,30 +160,6 @@ void StateType::setUnit(const Types::Unit &unit) m_unit = unit; } -/*! Returns true if this StateType is relevant for the rule from a user perspective. */ -bool StateType::ruleRelevant() const -{ - return m_ruleRelevant; -} - -/*! Sets this StateType relevant for the rule from a user perspective to \a ruleRelevant. */ -void StateType::setRuleRelevant(const bool &ruleRelevant) -{ - m_ruleRelevant = ruleRelevant; -} - -/*! Returns true if this StateType is interesting to visualize the logs in a graph/chart from a user perspective. */ -bool StateType::graphRelevant() const -{ - return m_graphRelevant; -} - -/*! Sets this StateType \a graphRelevant to inform the client application if this \l{StateType} is interesting to visualize the logs in a graph/chart. */ -void StateType::setGraphRelevant(const bool &graphRelevant) -{ - m_graphRelevant = graphRelevant; -} - /*! Returns true if this StateType is to be cached. This means, the last state value will be stored to disk upon shutdown and restored on reboot. If this is false, states will be initialized with the default value on each boot. By default all states are cached by the system. */ bool StateType::cached() const { @@ -200,8 +176,8 @@ void StateType::setCached(bool cached) QStringList StateType::typeProperties() { return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue" - << "cached" << "ruleRelevant" << "eventRuleRelevant" << "graphRelevant" << "unit" - << "minValue" << "maxValue" << "possibleValues" << "writable" << "displayNameAction"; + << "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable" + << "displayNameAction"; } /*! Returns a list of mandatory properties a DeviceClass definition must have. */ diff --git a/libnymea/types/statetype.h b/libnymea/types/statetype.h index 278c4ac3..88d65a16 100644 --- a/libnymea/types/statetype.h +++ b/libnymea/types/statetype.h @@ -64,12 +64,6 @@ public: Types::Unit unit() const; void setUnit(const Types::Unit &unit); - bool ruleRelevant() const; - void setRuleRelevant(const bool &ruleRelevant); - - bool graphRelevant() const; - void setGraphRelevant(const bool &graphRelevant); - bool cached() const; void setCached(bool cached); @@ -87,8 +81,6 @@ private: QVariant m_maxValue; QVariantList m_possibleValues; Types::Unit m_unit = Types::UnitNone; - bool m_ruleRelevant = true; - bool m_graphRelevant = false; bool m_cached = true; }; diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index 6278cd82..bfc78cd4 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -72,7 +72,6 @@ "displayName": "Dummy int state", "displayNameEvent": "Dummy int state changed", "defaultValue": 10, - "graphRelevant": true, "type": "int" }, { @@ -128,8 +127,7 @@ { "id": "45bf3752-0fc6-46b9-89fd-ffd878b5b22b", "name": "mockEvent1", - "displayName": "Mock Event 1", - "graphRelevant": true + "displayName": "Mock Event 1" }, { "id": "863d5920-b1cf-4eb9-88bd-8f7b8583b1cf", @@ -224,7 +222,6 @@ "displayName": "Dummy int state", "displayNameEvent": "Dummy int state changed", "defaultValue": 10, - "graphRelevant": true, "type": "int" }, { @@ -328,8 +325,6 @@ "displayNameAction": "Set color", "type": "QColor", "defaultValue": "#000000", - "ruleRelevant": false, - "eventRuleRelevant": false, "writable": true }, { @@ -341,7 +336,6 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "ruleRelevant": false, "minValue": 0, "maxValue": 100, "writable": true @@ -431,8 +425,6 @@ "displayNameAction": "Set color", "type": "QColor", "defaultValue": "#000000", - "ruleRelevant": false, - "eventRuleRelevant": false, "writable": true }, { @@ -444,7 +436,6 @@ "type": "int", "unit": "Percentage", "defaultValue": 0, - "ruleRelevant": false, "minValue": 0, "maxValue": 100, "writable": true From 8e9b695eb6c04fdd8eb96bc97ce12a573b59cc87 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 18 Mar 2019 23:20:24 +0100 Subject: [PATCH 6/8] bump jsonrpc version --- nymea.pri | 4 +-- tests/auto/api.json | 71 +-------------------------------------------- 2 files changed, 3 insertions(+), 72 deletions(-) diff --git a/nymea.pri b/nymea.pri index bb18123e..77559dc7 100644 --- a/nymea.pri +++ b/nymea.pri @@ -5,8 +5,8 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p" NYMEA_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/nymea/plugins/ # define protocol versions -JSON_PROTOCOL_VERSION_MAJOR=1 -JSON_PROTOCOL_VERSION_MINOR=15 +JSON_PROTOCOL_VERSION_MAJOR=2 +JSON_PROTOCOL_VERSION_MINOR=0 REST_API_VERSION=1 DEFINES += NYMEA_VERSION_STRING=\\\"$${NYMEA_VERSION_STRING}\\\" \ diff --git a/tests/auto/api.json b/tests/auto/api.json index 09637c65..d6df40d4 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -1.15 +2.0 { "methods": { "Actions.ExecuteAction": { @@ -1164,26 +1164,6 @@ "$ref:ParamType" ] }, - "BasicTag": [ - "BasicTagService", - "BasicTagDevice", - "BasicTagSensor", - "BasicTagActuator", - "BasicTagLighting", - "BasicTagEnergy", - "BasicTagMultimedia", - "BasicTagWeather", - "BasicTagGateway", - "BasicTagHeating", - "BasicTagCooling", - "BasicTagNotification", - "BasicTagSecurity", - "BasicTagTime", - "BasicTagShading", - "BasicTagAppliance", - "BasicTagCamera", - "BasicTagLock" - ], "BasicType": [ "Uuid", "String", @@ -1244,13 +1224,9 @@ "actionTypes": [ "$ref:ActionType" ], - "basicTags": [ - "$ref:BasicTag" - ], "createMethods": [ "$ref:CreateMethod" ], - "deviceIcon": "$ref:DeviceIcon", "discoveryParamTypes": [ "$ref:ParamType" ], @@ -1263,9 +1239,6 @@ "String" ], "name": "String", - "o:criticalStateTypeId": "Uuid", - "o:primaryActionTypeId": "Uuid", - "o:primaryStateTypeId": "Uuid", "paramTypes": [ "$ref:ParamType" ], @@ -1311,44 +1284,6 @@ "DeviceErrorPairingTransactionIdNotFound", "DeviceErrorParameterNotWritable" ], - "DeviceIcon": [ - "DeviceIconNone", - "DeviceIconBed", - "DeviceIconBlinds", - "DeviceIconCeilingLamp", - "DeviceIconCouch", - "DeviceIconDeskLamp", - "DeviceIconDesk", - "DeviceIconHifi", - "DeviceIconPower", - "DeviceIconEnergy", - "DeviceIconRadio", - "DeviceIconSmartPhone", - "DeviceIconSocket", - "DeviceIconStandardLamp", - "DeviceIconSun", - "DeviceIconTablet", - "DeviceIconThermometer", - "DeviceIconTune", - "DeviceIconTv", - "DeviceIconBattery", - "DeviceIconDishwasher", - "DeviceIconWashingMachine", - "DeviceIconLaundryDryer", - "DeviceIconIrHeater", - "DeviceIconRadiator", - "DeviceIconSwitch", - "DeviceIconMotionDetectors", - "DeviceIconWeather", - "DeviceIconTime", - "DeviceIconLightBulb", - "DeviceIconGateway", - "DeviceIconMail", - "DeviceIconNetwork", - "DeviceIconCloud", - "DeviceIconGarage", - "DeviceIconRollerShutter" - ], "Event": { "deviceId": "Uuid", "eventTypeId": "Uuid", @@ -1370,8 +1305,6 @@ "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", - "o:ruleRelevant": "Bool", "paramTypes": [ "$ref:ParamType" ] @@ -1628,13 +1561,11 @@ "id": "Uuid", "index": "Int", "name": "String", - "o:graphRelevant": "Bool", "o:maxValue": "Variant", "o:minValue": "Variant", "o:possibleValues": [ "Variant" ], - "o:ruleRelevant": "Bool", "o:unit": "$ref:Unit", "type": "$ref:BasicType" }, From cbe046402a052fe95b1c602995569bec83263dee Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 19 Mar 2019 00:59:07 +0100 Subject: [PATCH 7/8] drop outdated docs piece --- doc/jsonrpc-api.qdoc | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/doc/jsonrpc-api.qdoc b/doc/jsonrpc-api.qdoc index a1b0d5b8..8a2e43ed 100644 --- a/doc/jsonrpc-api.qdoc +++ b/doc/jsonrpc-api.qdoc @@ -30,29 +30,6 @@ See also: \l{Param} } \endcode See also: \l{ParamType} -\section2 BasicTag -\code -[ - "BasicTagService", - "BasicTagDevice", - "BasicTagSensor", - "BasicTagActuator", - "BasicTagLighting", - "BasicTagEnergy", - "BasicTagMultimedia", - "BasicTagWeather", - "BasicTagGateway", - "BasicTagHeating", - "BasicTagCooling", - "BasicTagNotification", - "BasicTagSecurity", - "BasicTagTime", - "BasicTagShading", - "BasicTagAppliance", - "BasicTagCamera", - "BasicTagLock" -] -\endcode \section2 BasicType \code [ From 827b033da624a880e75b181a1bd5c3152a5648a1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 22 Mar 2019 00:49:53 +0100 Subject: [PATCH 8/8] bump version number because of the API break --- debian/changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/changelog b/debian/changelog index 64bf374a..b92ee7de 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +nymea (0.12.0) UNRELEASED; urgency=medium + + -- Michael Zanetti Fri, 22 Mar 2019 00:49:04 +0100 + nymea (0.11.1) xenial; urgency=medium [ Simon Stürz ]