diff --git a/libnymea-app-core/configuration/basicconfiguration.cpp b/libnymea-app-core/configuration/basicconfiguration.cpp deleted file mode 100644 index dc76ed2d..00000000 --- a/libnymea-app-core/configuration/basicconfiguration.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "basicconfiguration.h" -#include "serverconfiguration.h" - -#include "jsonrpc/jsonrpcclient.h" - -BasicConfiguration::BasicConfiguration(JsonRpcClient* client, QObject *parent) : - JsonHandler(parent), - m_client(client), - m_tcpServerConfigurations(new ServerConfigurations(this)), - m_websocketServerConfigurations(new ServerConfigurations(this)) -{ - client->registerNotificationHandler(this, "notificationReceived"); -} - -QString BasicConfiguration::nameSpace() const -{ - return "Configuration"; -} - -bool BasicConfiguration::debugServerEnabled() const -{ - return m_debugServerEnabled; -} - -void BasicConfiguration::setDebugServerEnabled(bool debugServerEnabled) -{ - QVariantMap params; - params.insert("enabled", debugServerEnabled); - m_client->sendCommand("Configuration.SetDebugServerEnabled", params, this, "setDebugServerEnabledResponse"); -} - -QString BasicConfiguration::serverName() const -{ - return m_serverName; -} - -void BasicConfiguration::setServerName(const QString &serverName) -{ - QVariantMap params; - params.insert("serverName", serverName); - m_client->sendCommand("Configuration.SetServerName", params, this, "setServerNameResponse"); -} - -bool BasicConfiguration::cloudEnabled() const -{ - return m_cloudEnabled; -} - -void BasicConfiguration::setCloudEnabled(bool cloudEnabled) -{ - QVariantMap params; - params.insert("enabled", cloudEnabled); - m_client->sendCommand("Configuration.SetCloudEnabled", params, this, "setCloudEnabledResponse"); -} - -QString BasicConfiguration::language() const -{ - return m_language; -} - -void BasicConfiguration::setLanguage(const QString &language) -{ - QVariantMap params; - params.insert("language", language); - m_client->sendCommand("Configuration.SetLanguage", params); -} - -QString BasicConfiguration::timezone() const -{ - return m_timezone; -} - -void BasicConfiguration::setTimezone(const QString &timezone) -{ - QVariantMap params; - params.insert("timeZone", timezone); - m_client->sendCommand("Configuration.SetTimeZone", params, this, "setTimezoneResponse"); -} - -QStringList BasicConfiguration::timezones() const -{ - return m_timezones; -} - -ServerConfigurations *BasicConfiguration::tcpServerConfigurations() const -{ - return m_tcpServerConfigurations; -} - -ServerConfigurations *BasicConfiguration::websocketServerConfigurations() const -{ - return m_websocketServerConfigurations; -} - -void BasicConfiguration::setTcpServerConfiguration(ServerConfiguration *configuration) const -{ - QVariantMap params; - params.insert("id", configuration->id()); - params.insert("address", configuration->address()); - params.insert("port", configuration->address()); - params.insert("authentiactionEnabled", configuration->authenticationEnabled()); - params.insert("sslEnabled", configuration->sslEnabled()); - m_client->sendCommand("Configuration.SetTcpServerConfiguration", params); -} - -void BasicConfiguration::deleteTcpServerConfiguration(const QString &id) -{ - QVariantMap params; - params.insert("id", id); - m_client->sendCommand("Configuration.DeleteTcpServerConfiguration", params, this, "deleteTcpConfigReply"); -} - -void BasicConfiguration::deleteWebsocketServerConfiguration(const QString &id) -{ - QVariantMap params; - params.insert("id", id); - m_client->sendCommand("Configuration.DeleteWebSocketServerConfiguration", params, this, "deleteWebSocketConfigReply"); -} - -QStringList BasicConfiguration::availableLanguages() const -{ - return m_availableLanguages; -} - -void BasicConfiguration::init() -{ - m_client->sendCommand("Configuration.GetConfigurations", this, "getConfigurationsResponse"); - m_client->sendCommand("Configuration.GetAvailableLanguages", this, "getAvailableLanguagesResponse"); - m_client->sendCommand("Configuration.GetTimeZones", this, "getTimezonesResponse"); -} - -void BasicConfiguration::getConfigurationsResponse(const QVariantMap ¶ms) -{ - qDebug() << "have config reply" << params; - QVariantMap basicConfig = params.value("params").toMap().value("basicConfiguration").toMap(); - m_debugServerEnabled = basicConfig.value("debugServerEnabled").toBool(); - emit debugServerEnabledChanged(); - m_serverName = basicConfig.value("serverName").toString(); - emit serverNameChanged(); - m_language = basicConfig.value("language").toString(); - emit languageChanged(); - m_timezone = basicConfig.value("timeZone").toString(); - emit timezoneChanged(); - QVariantMap cloudConfig = params.value("params").toMap().value("cloud").toMap(); - m_cloudEnabled = cloudConfig.value("enabled").toBool(); - emit cloudEnabledChanged(); - - tcpServerConfigurations()->clear(); - foreach (const QVariant &tcpServerVariant, params.value("params").toMap().value("tcpServerConfigurations").toList()) { - qDebug() << "tcp server config:" << tcpServerVariant; - QVariantMap tcpConfigMap = tcpServerVariant.toMap(); - ServerConfiguration *config = new ServerConfiguration(tcpConfigMap.value("id").toString(), QHostAddress(tcpConfigMap.value("address").toString()), tcpConfigMap.value("port").toInt(), tcpConfigMap.value("authenticationEnabled").toBool(), tcpConfigMap.value("sslEnabled").toBool()); - m_tcpServerConfigurations->addConfiguration(config); - } - websocketServerConfigurations()->clear(); - foreach (const QVariant &websocketServerVariant, params.value("params").toMap().value("webSocketServerConfigurations").toList()) { - QVariantMap websocketConfigMap = websocketServerVariant.toMap(); - ServerConfiguration *config = new ServerConfiguration(websocketConfigMap.value("id").toString(), QHostAddress(websocketConfigMap.value("address").toString()), websocketConfigMap.value("port").toInt(), websocketConfigMap.value("authenticationEnabled").toBool(), websocketConfigMap.value("sslEnabled").toBool()); - m_websocketServerConfigurations->addConfiguration(config); - } -} - -void BasicConfiguration::getCloudConfigurationResponse(const QVariantMap ¶ms) -{ - qDebug() << "Cloud config reply" << params; -} - -void BasicConfiguration::setDebugServerEnabledResponse(const QVariantMap ¶ms) -{ - qDebug() << "Debug server set:" << params; -} - -void BasicConfiguration::setServerNameResponse(const QVariantMap ¶ms) -{ - qDebug() << "Server name set:" << params; -} - -void BasicConfiguration::setCloudEnabledResponse(const QVariantMap ¶ms) -{ - qDebug() << "Set cloud enabled:" << params; -} - -void BasicConfiguration::getAvailableLanguagesResponse(const QVariantMap ¶ms) -{ -// qDebug() << "Get available languages response" << params; - m_availableLanguages = params.value("params").toMap().value("languages").toStringList(); - emit availableLanguagesChanged(); -} - -void BasicConfiguration::getTimezonesResponse(const QVariantMap ¶ms) -{ -// qDebug() << "Get timezones response" << params; - m_timezones = params.value("params").toMap().value("timeZones").toStringList(); - emit timezonesChanged(); -} - -void BasicConfiguration::setTimezoneResponse(const QVariantMap ¶ms) -{ - qDebug() << "Set timezones response" << params; -} - -void BasicConfiguration::deleteTcpConfigReply(const QVariantMap ¶ms) -{ - if (params.value("params").toMap().value("configurationError").toString() == "ConfigurationErrorNoError") { - } -} - -void BasicConfiguration::deleteWebSocketConfigReply(const QVariantMap ¶ms) -{ - -} - -void BasicConfiguration::notificationReceived(const QVariantMap ¬ification) -{ - QString notif = notification.value("notification").toString(); - if (notif == "Configuration.BasicConfigurationChanged") { - QVariantMap params = notification.value("params").toMap().value("basicConfiguration").toMap(); - qDebug() << "notif" << params; - m_debugServerEnabled = params.value("debugServerEnabled").toBool(); - emit debugServerEnabledChanged(); - m_serverName = params.value("serverName").toString(); - emit serverNameChanged(); - m_language = params.value("language").toString(); - emit languageChanged(); - m_timezone = params.value("timeZone").toString(); - emit timezoneChanged(); - return; - } - if (notif == "Configuration.CloudConfigurationChanged") { - QVariantMap params = notification.value("params").toMap().value("cloudConfiguration").toMap(); - qDebug() << "notif" << params; - m_cloudEnabled = params.value("enabled").toBool(); - emit cloudEnabledChanged(); - return; - } - qDebug() << "Unhandled Configuration notification" << notif; -} diff --git a/libnymea-app-core/configuration/mqttpolicies.cpp b/libnymea-app-core/configuration/mqttpolicies.cpp new file mode 100644 index 00000000..c03a5235 --- /dev/null +++ b/libnymea-app-core/configuration/mqttpolicies.cpp @@ -0,0 +1,109 @@ +#include "mqttpolicies.h" +#include "mqttpolicy.h" + +MqttPolicies::MqttPolicies(QObject *parent) : QAbstractListModel(parent) +{ + +} + +int MqttPolicies::rowCount(const QModelIndex &index) const +{ + Q_UNUSED(index) + return m_list.count(); +} + +QVariant MqttPolicies::data(const QModelIndex &index, int role) const +{ + switch (role) { + case RoleClientId: + return m_list.at(index.row())->clientId(); + case RoleUsername: + return m_list.at(index.row())->username(); + case RolePassword: + return m_list.at(index.row())->password(); + case RoleAllowedPublishTopicFilters: + return m_list.at(index.row())->allowedPublishTopicFilters(); + case RoleAllowedSubscribeTopicFilters: + return m_list.at(index.row())->allowedSubscribeTopicFilters(); + } + return QVariant(); +} + +QHash MqttPolicies::roleNames() const +{ + QHash roles; + roles.insert(RoleClientId, "clientId"); + roles.insert(RoleUsername, "username"); + roles.insert(RolePassword, "password"); + roles.insert(RoleAllowedPublishTopicFilters, "allowedPublishTopicFilters"); + roles.insert(RoleAllowedSubscribeTopicFilters, "allowedSubscribeTopicFilters"); + return roles; +} + +void MqttPolicies::addPolicy(MqttPolicy *policy) +{ + policy->setParent(this); + beginInsertRows(QModelIndex(), m_list.count(), m_list.count()); + m_list.append(policy); + + connect(policy, &MqttPolicy::clientIdChanged, this, [this, policy]() { + QModelIndex index = this->index(m_list.indexOf(policy)); + emit dataChanged(index, index, {RoleClientId}); + }); + connect(policy, &MqttPolicy::usernameChanged, this, [this, policy]() { + QModelIndex index = this->index(m_list.indexOf(policy)); + emit dataChanged(index, index, {RoleUsername}); + }); + connect(policy, &MqttPolicy::passwordChanged, this, [this, policy]() { + QModelIndex index = this->index(m_list.indexOf(policy)); + emit dataChanged(index, index, {RolePassword}); + }); + connect(policy, &MqttPolicy::allowedPublishTopicFiltersChanged, this, [this, policy]() { + QModelIndex index = this->index(m_list.indexOf(policy)); + emit dataChanged(index, index, {RoleAllowedPublishTopicFilters}); + }); + connect(policy, &MqttPolicy::allowedSubscribeTopicFiltersChanged, this, [this, policy]() { + QModelIndex index = this->index(m_list.indexOf(policy)); + emit dataChanged(index, index, {RoleAllowedSubscribeTopicFilters}); + }); + + endInsertRows(); + emit countChanged(); +} + +void MqttPolicies::removePolicy(MqttPolicy *policy) +{ + int idx = m_list.indexOf(policy); + if (idx < 0) { + return; + } + beginRemoveRows(QModelIndex(), idx, idx); + m_list.takeAt(idx)->deleteLater(); + endRemoveRows(); +} + +MqttPolicy *MqttPolicies::getPolicy(const QString &clientId) const +{ + foreach (MqttPolicy* policy, m_list) { + if (policy->clientId() == clientId) { + return policy; + } + } + return nullptr; +} + +MqttPolicy *MqttPolicies::get(int index) const +{ + if (index < 0 || index >= m_list.count()){ + return nullptr; + } + return m_list.at(index); +} + +void MqttPolicies::clear() +{ + beginResetModel(); + qDeleteAll(m_list); + m_list.clear(); + endResetModel(); +} diff --git a/libnymea-app-core/configuration/mqttpolicies.h b/libnymea-app-core/configuration/mqttpolicies.h new file mode 100644 index 00000000..ae7a5d7c --- /dev/null +++ b/libnymea-app-core/configuration/mqttpolicies.h @@ -0,0 +1,42 @@ +#ifndef MQTTPOLICIES_H +#define MQTTPOLICIES_H + +#include +#include + +class MqttPolicy; + +class MqttPolicies : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) +public: + enum Roles { + RoleClientId, + RoleUsername, + RolePassword, + RoleAllowedPublishTopicFilters, + RoleAllowedSubscribeTopicFilters + }; + + explicit MqttPolicies(QObject *parent = nullptr); + + int rowCount(const QModelIndex &index = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const override; + + void addPolicy(MqttPolicy *policy); + void removePolicy(MqttPolicy *policy); + + Q_INVOKABLE MqttPolicy* getPolicy(const QString &clientId) const; + Q_INVOKABLE MqttPolicy* get(int index) const; + + void clear(); +signals: + void countChanged(); + +private: + QList m_list; +}; + +#endif // MQTTPOLICIES_H diff --git a/libnymea-app-core/configuration/mqttpolicy.cpp b/libnymea-app-core/configuration/mqttpolicy.cpp new file mode 100644 index 00000000..ee476a44 --- /dev/null +++ b/libnymea-app-core/configuration/mqttpolicy.cpp @@ -0,0 +1,82 @@ +#include "mqttpolicy.h" + +MqttPolicy::MqttPolicy(const QString &clientId, const QString &username, const QString &password, const QStringList &allowedPublishTopicFilters, const QStringList &allowedSubscribeTopicFilters, QObject *parent): + QObject(parent), + m_clientId(clientId), + m_username(username), + m_password(password), + m_allowedPublishTopicFilters(allowedPublishTopicFilters), + m_allowedSubscribeTopicFilters(allowedSubscribeTopicFilters) +{ + +} + +QString MqttPolicy::clientId() const +{ + return m_clientId; +} + +void MqttPolicy::setClientId(const QString &clientId) +{ + if (m_clientId != clientId) { + m_clientId = clientId; + emit clientIdChanged(); + } +} + +QString MqttPolicy::username() const +{ + return m_username; +} + +void MqttPolicy::setUsername(const QString &username) +{ + if (m_username != username) { + m_username = username; + emit usernameChanged(); + } +} + +QString MqttPolicy::password() const +{ + return m_password; +} + +void MqttPolicy::setPassword(const QString &password) +{ + if (m_password != password) { + m_password = password; + emit passwordChanged(); + } +} + +QStringList MqttPolicy::allowedPublishTopicFilters() const +{ + return m_allowedPublishTopicFilters; +} + +void MqttPolicy::setAllowedPublishTopicFilters(const QStringList &allowedPublishTopicFilters) +{ + if (m_allowedPublishTopicFilters != allowedPublishTopicFilters) { + m_allowedPublishTopicFilters = allowedPublishTopicFilters; + emit allowedPublishTopicFiltersChanged(); + } +} + +QStringList MqttPolicy::allowedSubscribeTopicFilters() const +{ + return m_allowedSubscribeTopicFilters; +} + +void MqttPolicy::setAllowedSubscribeTopicFilters(const QStringList &allowedSubscribeTopicFilters) +{ + if (m_allowedSubscribeTopicFilters != allowedSubscribeTopicFilters) { + m_allowedSubscribeTopicFilters = allowedSubscribeTopicFilters; + emit allowedSubscribeTopicFiltersChanged(); + } +} + +MqttPolicy *MqttPolicy::clone() +{ + return new MqttPolicy(m_clientId, m_username, m_password, m_allowedPublishTopicFilters, m_allowedSubscribeTopicFilters, this); +} diff --git a/libnymea-app-core/configuration/mqttpolicy.h b/libnymea-app-core/configuration/mqttpolicy.h new file mode 100644 index 00000000..8f809967 --- /dev/null +++ b/libnymea-app-core/configuration/mqttpolicy.h @@ -0,0 +1,54 @@ +#ifndef MQTTPOLICY_H +#define MQTTPOLICY_H + +#include + +class MqttPolicy : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged) + Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) + Q_PROPERTY(QStringList allowedPublishTopicFilters READ allowedPublishTopicFilters WRITE setAllowedPublishTopicFilters NOTIFY allowedPublishTopicFiltersChanged) + Q_PROPERTY(QStringList allowedSubscribeTopicFilters READ allowedSubscribeTopicFilters WRITE setAllowedSubscribeTopicFilters NOTIFY allowedSubscribeTopicFiltersChanged) + +public: + explicit MqttPolicy(const QString &clientId = QString(), + const QString &username = QString(), + const QString &password = QString(), + const QStringList &allowedPublishTopicFilters = QStringList(), + const QStringList &allowedSubscribeTopicFilters = QStringList(), + QObject *parent = nullptr); + + QString clientId() const; + void setClientId(const QString &clientId); + + QString username() const; + void setUsername(const QString &username); + + QString password() const; + void setPassword(const QString &password); + + QStringList allowedPublishTopicFilters() const; + void setAllowedPublishTopicFilters(const QStringList &allowedPublishTopicFilters); + + QStringList allowedSubscribeTopicFilters() const; + void setAllowedSubscribeTopicFilters(const QStringList &allowedSubscribeTopicFilters); + + Q_INVOKABLE MqttPolicy* clone(); +signals: + void clientIdChanged(); + void usernameChanged(); + void passwordChanged(); + void allowedPublishTopicFiltersChanged(); + void allowedSubscribeTopicFiltersChanged(); + +private: + QString m_clientId; + QString m_username; + QString m_password; + QStringList m_allowedPublishTopicFilters; + QStringList m_allowedSubscribeTopicFilters; +}; + +#endif // MQTTPOLICY_H diff --git a/libnymea-app-core/configuration/nymeaconfiguration.cpp b/libnymea-app-core/configuration/nymeaconfiguration.cpp new file mode 100644 index 00000000..79eed28e --- /dev/null +++ b/libnymea-app-core/configuration/nymeaconfiguration.cpp @@ -0,0 +1,457 @@ +#include "nymeaconfiguration.h" + +#include "serverconfiguration.h" +#include "serverconfigurations.h" +#include "mqttpolicy.h" +#include "mqttpolicies.h" + +#include "jsonrpc/jsonrpcclient.h" + +#include + +NymeaConfiguration::NymeaConfiguration(JsonRpcClient *client, QObject *parent): + JsonHandler(parent), + m_client(client), + m_tcpServerConfigurations(new ServerConfigurations(this)), + m_webSocketServerConfigurations(new ServerConfigurations(this)), + m_mqttServerConfigurations(new ServerConfigurations(this)), + m_mqttPolicies(new MqttPolicies(this)) +{ + client->registerNotificationHandler(this, "notificationReceived"); +} + +QString NymeaConfiguration::nameSpace() const +{ + return "Configuration"; +} + +void NymeaConfiguration::init() +{ + m_tcpServerConfigurations->clear(); + m_webSocketServerConfigurations->clear(); + m_mqttServerConfigurations->clear(); + m_client->sendCommand("Configuration.GetConfigurations", this, "getConfigurationsResponse"); + m_client->sendCommand("Configuration.GetAvailableLanguages", this, "getAvailableLanguagesResponse"); + m_client->sendCommand("Configuration.GetTimeZones", this, "getTimezonesResponse"); + m_client->sendCommand("Configuration.GetMqttServerConfigurations", this, "getMqttServerConfigsReply"); + m_client->sendCommand("Configuration.GetMqttPolicies", this, "getMqttPoliciesReply"); +} + +QString NymeaConfiguration::serverName() const +{ + return m_serverName; +} + +void NymeaConfiguration::setServerName(const QString &serverName) +{ + QVariantMap params; + params.insert("serverName", serverName); + m_client->sendCommand("Configuration.SetServerName", params, this, "setServerNameResponse"); +} + +QString NymeaConfiguration::language() const +{ + return m_language; +} + +void NymeaConfiguration::setLanguage(const QString &language) +{ + QVariantMap params; + params.insert("language", language); + m_client->sendCommand("Configuration.SetLanguage", params); +} + +QStringList NymeaConfiguration::availableLanguages() const +{ + return m_availableLanguages; +} + +QString NymeaConfiguration::timezone() const +{ + return m_timezone; +} + +void NymeaConfiguration::setTimezone(const QString &timezone) +{ + QVariantMap params; + params.insert("timeZone", timezone); + m_client->sendCommand("Configuration.SetTimeZone", params, this, "setTimezoneResponse"); +} + +QStringList NymeaConfiguration::timezones() const +{ + return m_timezones; +} + +bool NymeaConfiguration::debugServerEnabled() const +{ + return m_debugServerEnabled; +} + +void NymeaConfiguration::setDebugServerEnabled(bool debugServerEnabled) +{ + QVariantMap params; + params.insert("enabled", debugServerEnabled); + m_client->sendCommand("Configuration.SetDebugServerEnabled", params, this, "setDebugServerEnabledResponse"); +} + +bool NymeaConfiguration::cloudEnabled() const +{ + return m_cloudEnabled; +} + +void NymeaConfiguration::setCloudEnabled(bool cloudEnabled) +{ + QVariantMap params; + params.insert("enabled", cloudEnabled); + m_client->sendCommand("Configuration.SetCloudEnabled", params, this, "setCloudEnabledResponse"); +} + +ServerConfigurations *NymeaConfiguration::tcpServerConfigurations() const +{ + return m_tcpServerConfigurations; +} + +ServerConfigurations *NymeaConfiguration::webSocketServerConfigurations() const +{ + return m_webSocketServerConfigurations; +} + +ServerConfigurations *NymeaConfiguration::mqttServerConfigurations() const +{ + return m_mqttServerConfigurations; +} + +MqttPolicies *NymeaConfiguration::mqttPolicies() const +{ + return m_mqttPolicies; +} + +ServerConfiguration *NymeaConfiguration::createServerConfiguration(const QString &address, int port, bool authEnabled, bool sslEnabled) +{ + return new ServerConfiguration(QUuid::createUuid().toString(), QHostAddress(address), port, authEnabled, sslEnabled); +} + +MqttPolicy *NymeaConfiguration::createMqttPolicy() const +{ + return new MqttPolicy(QString(), QString(), QString(), {"#"}, {"#"}); +} + +void NymeaConfiguration::setTcpServerConfiguration(ServerConfiguration *configuration) +{ + QVariantMap params; + QVariantMap configurationMap; + configurationMap.insert("id", configuration->id()); + configurationMap.insert("address", configuration->address()); + configurationMap.insert("port", configuration->port()); + configurationMap.insert("authenticationEnabled", configuration->authenticationEnabled()); + configurationMap.insert("sslEnabled", configuration->sslEnabled()); + params.insert("configuration", configurationMap); + m_client->sendCommand("Configuration.SetTcpServerConfiguration", params, this, "setTcpConfigReply"); +} + +void NymeaConfiguration::setWebSocketServerConfiguration(ServerConfiguration *configuration) +{ + QVariantMap params; + QVariantMap configurationMap; + configurationMap.insert("id", configuration->id()); + configurationMap.insert("address", configuration->address()); + configurationMap.insert("port", configuration->port()); + configurationMap.insert("authenticationEnabled", configuration->authenticationEnabled()); + configurationMap.insert("sslEnabled", configuration->sslEnabled()); + params.insert("configuration", configurationMap); + m_client->sendCommand("Configuration.SetWebSocketServerConfiguration", params, this, "setWebSocketConfigReply"); +} + +void NymeaConfiguration::setMqttServerConfiguration(ServerConfiguration *configuration) +{ + QVariantMap params; + QVariantMap configurationMap; + configurationMap.insert("id", configuration->id()); + configurationMap.insert("address", configuration->address()); + configurationMap.insert("port", configuration->port()); + configurationMap.insert("authenticationEnabled", configuration->authenticationEnabled()); + configurationMap.insert("sslEnabled", configuration->sslEnabled()); + params.insert("configuration", configurationMap); + m_client->sendCommand("Configuration.SetMqttServerConfiguration", params, this, "setMqttConfigReply"); +} + +void NymeaConfiguration::deleteTcpServerConfiguration(const QString &id) +{ + QVariantMap params; + params.insert("id", id); + m_client->sendCommand("Configuration.DeleteTcpServerConfiguration", params, this, "deleteTcpConfigReply"); +} + +void NymeaConfiguration::deleteWebSocketServerConfiguration(const QString &id) +{ + QVariantMap params; + params.insert("id", id); + m_client->sendCommand("Configuration.DeleteWebSocketServerConfiguration", params, this, "deleteWebSocketConfigReply"); +} + +void NymeaConfiguration::deleteMqttServerConfiguration(const QString &id) +{ + QVariantMap params; + params.insert("id", id); + m_client->sendCommand("Configuration.DeleteMqttServerConfiguration", params, this, "deleteMqttConfigReply"); +} + +void NymeaConfiguration::updateMqttPolicy(MqttPolicy *policy) +{ + QVariantMap params; + QVariantMap policyMap; + policyMap.insert("clientId", policy->clientId()); + policyMap.insert("username", policy->username()); + policyMap.insert("password", policy->password()); + policyMap.insert("allowedPublishTopicFilters", policy->allowedPublishTopicFilters()); + policyMap.insert("allowedSubscribeTopicFilters", policy->allowedSubscribeTopicFilters()); + params.insert("policy", policyMap); + m_client->sendCommand("Configuration.SetMqttPolicy", params, this, "setMqttPolicyReply"); +} + +void NymeaConfiguration::deleteMqttPolicy(const QString &clientId) +{ + QVariantMap params; + params.insert("clientId", clientId); + m_client->sendCommand("Configuration.DeleteMqttPolicy", params, this, "deleteMqttPolicyReply"); +} + +void NymeaConfiguration::getConfigurationsResponse(const QVariantMap ¶ms) +{ + qDebug() << "have config reply" << params; + QVariantMap basicConfig = params.value("params").toMap().value("basicConfiguration").toMap(); + m_debugServerEnabled = basicConfig.value("debugServerEnabled").toBool(); + emit debugServerEnabledChanged(); + m_serverName = basicConfig.value("serverName").toString(); + emit serverNameChanged(); + m_language = basicConfig.value("language").toString(); + emit languageChanged(); + m_timezone = basicConfig.value("timeZone").toString(); + emit timezoneChanged(); + QVariantMap cloudConfig = params.value("params").toMap().value("cloud").toMap(); + m_cloudEnabled = cloudConfig.value("enabled").toBool(); + emit cloudEnabledChanged(); + + tcpServerConfigurations()->clear(); + foreach (const QVariant &tcpServerVariant, params.value("params").toMap().value("tcpServerConfigurations").toList()) { + qDebug() << "tcp server config:" << tcpServerVariant; + QVariantMap tcpConfigMap = tcpServerVariant.toMap(); + ServerConfiguration *config = new ServerConfiguration(tcpConfigMap.value("id").toString(), QHostAddress(tcpConfigMap.value("address").toString()), tcpConfigMap.value("port").toInt(), tcpConfigMap.value("authenticationEnabled").toBool(), tcpConfigMap.value("sslEnabled").toBool()); + m_tcpServerConfigurations->addConfiguration(config); + } + webSocketServerConfigurations()->clear(); + foreach (const QVariant &websocketServerVariant, params.value("params").toMap().value("webSocketServerConfigurations").toList()) { + QVariantMap websocketConfigMap = websocketServerVariant.toMap(); + ServerConfiguration *config = new ServerConfiguration(websocketConfigMap.value("id").toString(), QHostAddress(websocketConfigMap.value("address").toString()), websocketConfigMap.value("port").toInt(), websocketConfigMap.value("authenticationEnabled").toBool(), websocketConfigMap.value("sslEnabled").toBool()); + m_webSocketServerConfigurations->addConfiguration(config); + } +} + +void NymeaConfiguration::getAvailableLanguagesResponse(const QVariantMap ¶ms) +{ + qDebug() << "available languages" << params; +} + +void NymeaConfiguration::getTimezonesResponse(const QVariantMap ¶ms) +{ +// qDebug() << "Get timezones response" << params; + m_timezones = params.value("params").toMap().value("timeZones").toStringList(); + emit timezonesChanged(); +} + +void NymeaConfiguration::setTimezoneResponse(const QVariantMap ¶ms) +{ + qDebug() << "Set timezones response" << params; +} + +void NymeaConfiguration::setServerNameResponse(const QVariantMap ¶ms) +{ + qDebug() << "Server name set:" << params; +} + +void NymeaConfiguration::getCloudConfigurationResponse(const QVariantMap ¶ms) +{ + qDebug() << "Cloud config reply" << params; +} + +void NymeaConfiguration::setCloudEnabledResponse(const QVariantMap ¶ms) +{ + qDebug() << "Set cloud enabled:" << params; +} + +void NymeaConfiguration::setDebugServerEnabledResponse(const QVariantMap ¶ms) +{ + qDebug() << "Debug server set:" << params; +} + +void NymeaConfiguration::setTcpConfigReply(const QVariantMap ¶ms) +{ + +} + +void NymeaConfiguration::deleteTcpConfigReply(const QVariantMap ¶ms) +{ + if (params.value("params").toMap().value("configurationError").toString() == "ConfigurationErrorNoError") { + } +} + +void NymeaConfiguration::setWebSocketConfigReply(const QVariantMap ¶ms) +{ + qDebug() << "set weboscket config reply" << params; +} + +void NymeaConfiguration::deleteWebSocketConfigReply(const QVariantMap ¶ms) +{ + +} + +void NymeaConfiguration::getMqttServerConfigsReply(const QVariantMap ¶ms) +{ + m_mqttServerConfigurations->clear(); + foreach (const QVariant &mqttServerVariant, params.value("params").toMap().value("mqttServerConfigurations").toList()) { + QVariantMap mqttConfigMap = mqttServerVariant.toMap(); + ServerConfiguration *config = new ServerConfiguration(mqttConfigMap.value("id").toString(), QHostAddress(mqttConfigMap.value("address").toString()), mqttConfigMap.value("port").toInt(), mqttConfigMap.value("authenticationEnabled").toBool(), mqttConfigMap.value("sslEnabled").toBool()); + m_mqttServerConfigurations->addConfiguration(config); + } +} + +void NymeaConfiguration::setMqttConfigReply(const QVariantMap ¶ms) +{ + qDebug() << "Set mqtt config reply" << params; +} + +void NymeaConfiguration::deleteMqttConfigReply(const QVariantMap ¶ms) +{ + qDebug() << "Delete Mqtt Broker config reply:" << params; +} + +void NymeaConfiguration::getMqttPoliciesReply(const QVariantMap ¶ms) +{ + qDebug() << "Mqtt polices:" << params; + m_mqttPolicies->clear(); + foreach (const QVariant &policyVariant, params.value("params").toMap().value("mqttPolicies").toList()) { + QVariantMap policyMap = policyVariant.toMap(); + MqttPolicy *policy = new MqttPolicy( + policyMap.value("clientId").toString(), + policyMap.value("username").toString(), + policyMap.value("password").toString(), + policyMap.value("allowedPublishTopicFilters").toStringList(), + policyMap.value("allowedSubscribeTopicFilters").toStringList()); + m_mqttPolicies->addPolicy(policy); + } +} + +void NymeaConfiguration::setMqttPolicyReply(const QVariantMap ¶ms) +{ + qDebug() << "Set MQTT policy reply" << params; +} + +void NymeaConfiguration::deleteMqttPolicyReply(const QVariantMap ¶ms) +{ + qDebug() << "Delete MQTT policy reply" << params; +} + +void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification) +{ + QString notif = notification.value("notification").toString(); + if (notif == "Configuration.BasicConfigurationChanged") { + QVariantMap params = notification.value("params").toMap().value("basicConfiguration").toMap(); + qDebug() << "notif" << params; + m_debugServerEnabled = params.value("debugServerEnabled").toBool(); + emit debugServerEnabledChanged(); + m_serverName = params.value("serverName").toString(); + emit serverNameChanged(); + m_language = params.value("language").toString(); + emit languageChanged(); + m_timezone = params.value("timeZone").toString(); + emit timezoneChanged(); + return; + } + if (notif == "Configuration.CloudConfigurationChanged") { + QVariantMap params = notification.value("params").toMap().value("cloudConfiguration").toMap(); + qDebug() << "notif" << params; + m_cloudEnabled = params.value("enabled").toBool(); + emit cloudEnabledChanged(); + return; + } + if (notif.endsWith("ServerConfigurationChanged")) { + ServerConfigurations *configModel = nullptr; + QVariantMap params; + if (notif == "Configuration.TcpServerConfigurationChanged") { + configModel = m_tcpServerConfigurations; + params = notification.value("params").toMap().value("tcpServerConfiguration").toMap(); + } + if (notif == "Configuration.WebSocketServerConfigurationChanged") { + configModel = m_webSocketServerConfigurations; + params = notification.value("params").toMap().value("webSocketServerConfiguration").toMap(); + } + if (notif == "Configuration.MqttServerConfigurationChanged") { + configModel = m_mqttServerConfigurations; + params = notification.value("params").toMap().value("mqttServerConfiguration").toMap(); + } + if (!configModel) { + return; + } + + ServerConfiguration *serverConfig = nullptr; + for (int i = 0; i < configModel->rowCount(); i++) { + ServerConfiguration* config = configModel->get(i); + if (config->id() == params.value("id").toString()) { + serverConfig = config; + } + } + + if (!serverConfig) { + serverConfig = new ServerConfiguration(params.value("id").toString()); + configModel->addConfiguration(serverConfig); + } + serverConfig->setAddress(params.value("address").toString()); + serverConfig->setPort(params.value("port").toInt()); + serverConfig->setAuthenticationEnabled(params.value("authenticationEnabled").toBool()); + serverConfig->setSslEnabled(params.value("sslEnabled").toBool()); + return; + } + if (notif == "Configuration.TcpServerConfigurationRemoved") { + m_tcpServerConfigurations->removeConfiguration(notification.value("params").toMap().value("id").toString()); + return; + } + if (notif == "Configuration.WebSocketServerConfigurationRemoved") { + m_webSocketServerConfigurations->removeConfiguration(notification.value("params").toMap().value("id").toString()); + return; + } + if (notif == "Configuration.MqttServerConfigurationRemoved") { + m_mqttServerConfigurations->removeConfiguration(notification.value("params").toMap().value("id").toString()); + return; + } + if (notif == "Configuration.MqttPolicyChanged") { + MqttPolicy *policy = nullptr; + QVariantMap policyMap = notification.value("params").toMap().value("policy").toMap(); + for (int i = 0; i < m_mqttPolicies->rowCount(); i++) { + if (m_mqttPolicies->get(i)->clientId() == policyMap.value("clientId").toString()) { + policy = m_mqttPolicies->get(i); + break; + } + } + if (!policy) { + policy = new MqttPolicy(policyMap.value("clientId").toString()); + m_mqttPolicies->addPolicy(policy); + } + policy->setUsername(policyMap.value("username").toString()); + policy->setPassword(policyMap.value("password").toString()); + policy->setAllowedPublishTopicFilters(policyMap.value("allowedPublishTopicFilters").toStringList()); + policy->setAllowedSubscribeTopicFilters(policyMap.value("allowedSubscribeTopicFilters").toStringList()); + qDebug() << "MQTT policy added" << policy->clientId() << policy->username() << policy->password(); + return; + } + if (notif == "Configuration.MqttPolicyRemoved") { + MqttPolicy* policy = m_mqttPolicies->getPolicy(notification.value("params").toMap().value("clientId").toString()); + if (!policy) { + qWarning() << "Reveived a policy removed notification for apolicy we don't know"; + return; + } + m_mqttPolicies->removePolicy(policy); + } + + qDebug() << "Unhandled Configuration notification" << notif << notification; +} diff --git a/libnymea-app-core/configuration/basicconfiguration.h b/libnymea-app-core/configuration/nymeaconfiguration.h similarity index 57% rename from libnymea-app-core/configuration/basicconfiguration.h rename to libnymea-app-core/configuration/nymeaconfiguration.h index 6ff63a4d..83e8f9dd 100644 --- a/libnymea-app-core/configuration/basicconfiguration.h +++ b/libnymea-app-core/configuration/nymeaconfiguration.h @@ -1,61 +1,77 @@ -#ifndef BASICCONFIGURATION_H -#define BASICCONFIGURATION_H +#ifndef NYMEACONFIGURATION_H +#define NYMEACONFIGURATION_H #include + #include "jsonrpc/jsonhandler.h" -#include "serverconfigurations.h" class JsonRpcClient; +class ServerConfiguration; +class ServerConfigurations; +class MqttPolicy; +class MqttPolicies; -class BasicConfiguration : public JsonHandler +class NymeaConfiguration : public JsonHandler { Q_OBJECT - Q_PROPERTY(bool debugServerEnabled READ debugServerEnabled WRITE setDebugServerEnabled NOTIFY debugServerEnabledChanged) + Q_PROPERTY(QString serverName READ serverName WRITE setServerName NOTIFY serverNameChanged) + Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged) - Q_PROPERTY(QString timezone READ timezone WRITE setTimezone NOTIFY timezoneChanged) - - Q_PROPERTY(bool cloudEnabled READ cloudEnabled WRITE setCloudEnabled NOTIFY cloudEnabledChanged) - Q_PROPERTY(QStringList availableLanguages READ availableLanguages NOTIFY availableLanguagesChanged) + + Q_PROPERTY(QString timezone READ timezone WRITE setTimezone NOTIFY timezoneChanged) Q_PROPERTY(QStringList timezones READ timezones NOTIFY timezonesChanged) + Q_PROPERTY(bool cloudEnabled READ cloudEnabled WRITE setCloudEnabled NOTIFY cloudEnabledChanged) + Q_PROPERTY(bool debugServerEnabled READ debugServerEnabled WRITE setDebugServerEnabled NOTIFY debugServerEnabledChanged) + Q_PROPERTY(ServerConfigurations* tcpServerConfigurations READ tcpServerConfigurations CONSTANT) - Q_PROPERTY(ServerConfigurations* websocketServerConfigurations READ websocketServerConfigurations CONSTANT) + Q_PROPERTY(ServerConfigurations* webSocketServerConfigurations READ webSocketServerConfigurations CONSTANT) + Q_PROPERTY(ServerConfigurations* mqttServerConfigurations READ mqttServerConfigurations CONSTANT) + + Q_PROPERTY(MqttPolicies* mqttPolicies READ mqttPolicies CONSTANT) public: - explicit BasicConfiguration(JsonRpcClient* client, QObject *parent = nullptr); + explicit NymeaConfiguration(JsonRpcClient* client, QObject *parent = nullptr); QString nameSpace() const override; - bool debugServerEnabled() const; - void setDebugServerEnabled(bool debugServerEnabled); - QString serverName() const; void setServerName(const QString &serverName); - bool cloudEnabled() const; - void setCloudEnabled(bool cloudEnabled); - QString language() const; void setLanguage(const QString &language); - QStringList availableLanguages() const; QString timezone() const; void setTimezone(const QString &timezone); - QStringList timezones() const; - ServerConfigurations *tcpServerConfigurations() const; - ServerConfigurations *websocketServerConfigurations() const; + bool debugServerEnabled() const; + void setDebugServerEnabled(bool debugServerEnabled); - void setTcpServerConfiguration(ServerConfiguration *configuration) const; - void setWebsocketServerConfiguration(ServerConfiguration *configuration) const; + bool cloudEnabled() const; + void setCloudEnabled(bool cloudEnabled); + + ServerConfigurations *tcpServerConfigurations() const; + ServerConfigurations *webSocketServerConfigurations() const; + ServerConfigurations *mqttServerConfigurations() const; + MqttPolicies *mqttPolicies() const; + + Q_INVOKABLE ServerConfiguration* createServerConfiguration(const QString &address = "0.0.0.0", int port = 0, bool authEnabled = false, bool sslEnabled = false); + Q_INVOKABLE MqttPolicy* createMqttPolicy() const; + + Q_INVOKABLE void setTcpServerConfiguration(ServerConfiguration *configuration); + Q_INVOKABLE void setWebSocketServerConfiguration(ServerConfiguration *configuration); + Q_INVOKABLE void setMqttServerConfiguration(ServerConfiguration *configuration); Q_INVOKABLE void deleteTcpServerConfiguration(const QString &id); - Q_INVOKABLE void deleteWebsocketServerConfiguration(const QString &id); + Q_INVOKABLE void deleteWebSocketServerConfiguration(const QString &id); + Q_INVOKABLE void deleteMqttServerConfiguration(const QString &id); + Q_INVOKABLE void updateMqttPolicy(MqttPolicy* policy); + Q_INVOKABLE void deleteMqttPolicy(const QString &clientId); void init(); private: @@ -67,8 +83,16 @@ private: Q_INVOKABLE void getAvailableLanguagesResponse(const QVariantMap ¶ms); Q_INVOKABLE void getTimezonesResponse(const QVariantMap ¶ms); Q_INVOKABLE void setTimezoneResponse(const QVariantMap ¶ms); + Q_INVOKABLE void setTcpConfigReply(const QVariantMap ¶ms); Q_INVOKABLE void deleteTcpConfigReply(const QVariantMap ¶ms); + Q_INVOKABLE void setWebSocketConfigReply(const QVariantMap ¶ms); Q_INVOKABLE void deleteWebSocketConfigReply(const QVariantMap ¶ms); + Q_INVOKABLE void getMqttServerConfigsReply(const QVariantMap ¶ms); + Q_INVOKABLE void setMqttConfigReply(const QVariantMap ¶ms); + Q_INVOKABLE void deleteMqttConfigReply(const QVariantMap ¶ms); + Q_INVOKABLE void getMqttPoliciesReply(const QVariantMap ¶ms); + Q_INVOKABLE void setMqttPolicyReply(const QVariantMap ¶ms); + Q_INVOKABLE void deleteMqttPolicyReply(const QVariantMap ¶ms); Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification); @@ -83,6 +107,7 @@ signals: private: JsonRpcClient* m_client = nullptr; + bool m_debugServerEnabled = false; QString m_serverName; QString m_language; @@ -92,7 +117,10 @@ private: bool m_cloudEnabled = false; ServerConfigurations *m_tcpServerConfigurations = nullptr; - ServerConfigurations *m_websocketServerConfigurations = nullptr; + ServerConfigurations *m_webSocketServerConfigurations = nullptr; + ServerConfigurations *m_mqttServerConfigurations = nullptr; + MqttPolicies *m_mqttPolicies = nullptr; + }; -#endif // BASICCONFIGURATION_H +#endif // NYMEACONFIGURATION_H diff --git a/libnymea-app-core/configuration/serverconfiguration.cpp b/libnymea-app-core/configuration/serverconfiguration.cpp index 1c30454c..030731b0 100644 --- a/libnymea-app-core/configuration/serverconfiguration.cpp +++ b/libnymea-app-core/configuration/serverconfiguration.cpp @@ -21,17 +21,55 @@ QString ServerConfiguration::address() const return m_hostAddress.toString(); } +void ServerConfiguration::setAddress(const QString &address) +{ + if (m_hostAddress != QHostAddress(address)) { + m_hostAddress = QHostAddress(address); + emit addressChanged(); + } +} + int ServerConfiguration::port() const { return m_port; } +void ServerConfiguration::setPort(int port) +{ + if (m_port != port) { + m_port = port; + emit portChanged(); + } +} + bool ServerConfiguration::authenticationEnabled() const { return m_authEnabled; } +void ServerConfiguration::setAuthenticationEnabled(bool authenticationEnabled) +{ + if (m_authEnabled != authenticationEnabled) { + m_authEnabled = authenticationEnabled; + emit authenticationEnabledChanged(); + } +} + bool ServerConfiguration::sslEnabled() const { return m_sslEnabled; } + +void ServerConfiguration::setSslEnabled(bool sslEnabled) +{ + if (m_sslEnabled != sslEnabled) { + m_sslEnabled = sslEnabled; + emit sslEnabledChanged(); + } +} + +ServerConfiguration *ServerConfiguration::clone() const +{ + ServerConfiguration *ret = new ServerConfiguration(m_id, m_hostAddress, m_port, m_authEnabled, m_sslEnabled); + return ret; +} diff --git a/libnymea-app-core/configuration/serverconfiguration.h b/libnymea-app-core/configuration/serverconfiguration.h index 2f0c1d0f..2d067928 100644 --- a/libnymea-app-core/configuration/serverconfiguration.h +++ b/libnymea-app-core/configuration/serverconfiguration.h @@ -9,18 +9,35 @@ class ServerConfiguration : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id CONSTANT) - Q_PROPERTY(QString address READ address CONSTANT) - Q_PROPERTY(int port READ port CONSTANT) - Q_PROPERTY(bool authenticationEnabled READ authenticationEnabled CONSTANT) - Q_PROPERTY(bool sslEnabled READ sslEnabled CONSTANT) + Q_PROPERTY(QString address READ address WRITE setAddress NOTIFY addressChanged) + Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged) + Q_PROPERTY(bool authenticationEnabled READ authenticationEnabled WRITE setAuthenticationEnabled NOTIFY authenticationEnabledChanged) + Q_PROPERTY(bool sslEnabled READ sslEnabled WRITE setSslEnabled NOTIFY sslEnabledChanged) + public: - explicit ServerConfiguration(const QString &id, const QHostAddress &address, int port, bool authEnabled, bool sslEnabled, QObject *parent = nullptr); + explicit ServerConfiguration(const QString &id, const QHostAddress &address = QHostAddress(), int port = 0, bool authEnabled = false, bool sslEnabled = false, QObject *parent = nullptr); QString id() const; + QString address() const; + void setAddress(const QString &address); + int port() const; + void setPort(int port); + bool authenticationEnabled() const; + void setAuthenticationEnabled(bool authenticationEnabled); + bool sslEnabled() const; + void setSslEnabled(bool sslEnabled); + + Q_INVOKABLE ServerConfiguration* clone() const; + +signals: + void addressChanged(); + void portChanged(); + void authenticationEnabledChanged(); + void sslEnabledChanged(); private: QString m_id; diff --git a/libnymea-app-core/configuration/serverconfigurations.cpp b/libnymea-app-core/configuration/serverconfigurations.cpp index 40bf13d7..e2a2459d 100644 --- a/libnymea-app-core/configuration/serverconfigurations.cpp +++ b/libnymea-app-core/configuration/serverconfigurations.cpp @@ -45,10 +45,40 @@ void ServerConfigurations::addConfiguration(ServerConfiguration *configuration) configuration->setParent(this); beginInsertRows(QModelIndex(), m_list.count(), m_list.count()); m_list.append(configuration); + + connect(configuration, &ServerConfiguration::addressChanged, this, [this, configuration]() { + QModelIndex idx = index(m_list.indexOf(configuration), 0); + emit dataChanged(idx, idx, {RoleAddress}); + }); + connect(configuration, &ServerConfiguration::portChanged, this, [this, configuration]() { + QModelIndex idx = index(m_list.indexOf(configuration), 0); + emit dataChanged(idx, idx, {RolePort}); + }); + connect(configuration, &ServerConfiguration::authenticationEnabledChanged, this, [this, configuration]() { + QModelIndex idx = index(m_list.indexOf(configuration), 0); + emit dataChanged(idx, idx, {RoleAuthenticationEnabled}); + }); + connect(configuration, &ServerConfiguration::sslEnabledChanged, this, [this, configuration]() { + QModelIndex idx = index(m_list.indexOf(configuration), 0); + emit dataChanged(idx, idx, {RoleSslEnabled}); + }); + endInsertRows(); emit countChanged(); } +void ServerConfigurations::removeConfiguration(const QString &id) +{ + for (int i = 0; i < m_list.count(); i++) { + if (m_list.at(i)->id() == id) { + beginRemoveRows(QModelIndex(), i, i); + m_list.takeAt(i)->deleteLater(); + endRemoveRows(); + return; + } + } +} + void ServerConfigurations::clear() { beginResetModel(); @@ -57,3 +87,11 @@ void ServerConfigurations::clear() endResetModel(); emit countChanged(); } + +ServerConfiguration *ServerConfigurations::get(int index) const +{ + if (index < 0 || index > m_list.count() - 1) { + return nullptr; + } + return m_list.at(index); +} diff --git a/libnymea-app-core/configuration/serverconfigurations.h b/libnymea-app-core/configuration/serverconfigurations.h index 8695d52b..c099ebd1 100644 --- a/libnymea-app-core/configuration/serverconfigurations.h +++ b/libnymea-app-core/configuration/serverconfigurations.h @@ -28,9 +28,12 @@ public: QHash roleNames() const override; void addConfiguration(ServerConfiguration *configuration); + void removeConfiguration(const QString &id); void clear(); + Q_INVOKABLE ServerConfiguration* get(int index) const; + signals: void countChanged(); diff --git a/libnymea-app-core/engine.cpp b/libnymea-app-core/engine.cpp index 44e19233..3f511097 100644 --- a/libnymea-app-core/engine.cpp +++ b/libnymea-app-core/engine.cpp @@ -23,7 +23,7 @@ #include "rulemanager.h" #include "logmanager.h" #include "tagsmanager.h" -#include "configuration/basicconfiguration.h" +#include "configuration/nymeaconfiguration.h" #include "connection/awsclient.h" #include "connection/tcpsockettransport.h" @@ -39,7 +39,7 @@ Engine::Engine(QObject *parent) : m_ruleManager(new RuleManager(m_jsonRpcClient, this)), m_logManager(new LogManager(m_jsonRpcClient, this)), m_tagsManager(new TagsManager(m_jsonRpcClient, this)), - m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)) + m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this)) { m_connection->registerTransport(new TcpSocketTransportFactory()); m_connection->registerTransport(new WebsocketTransportFactory()); @@ -92,16 +92,11 @@ LogManager *Engine::logManager() const return m_logManager; } -BasicConfiguration *Engine::basicConfiguration() const +NymeaConfiguration *Engine::nymeaConfiguration() const { - return m_basicConfiguration; + return m_nymeaConfiguration; } -//AWSClient *Engine::awsClient() const -//{ -// return m_aws; -//} - void Engine::deployCertificate() { if (!m_jsonRpcClient->connected()) { @@ -133,7 +128,7 @@ void Engine::onConnectedChanged() if (!m_jsonRpcClient->initialSetupRequired() && !m_jsonRpcClient->authenticationRequired()) { m_deviceManager->init(); m_ruleManager->init(); - m_basicConfiguration->init(); + m_nymeaConfiguration->init(); } } } diff --git a/libnymea-app-core/engine.h b/libnymea-app-core/engine.h index 9f90a4a8..28cc4759 100644 --- a/libnymea-app-core/engine.h +++ b/libnymea-app-core/engine.h @@ -31,8 +31,7 @@ class RuleManager; class LogManager; class TagsManager; -class BasicConfiguration; -class AWSClient; +class NymeaConfiguration; class Engine : public QObject { @@ -42,8 +41,7 @@ class Engine : public QObject Q_PROPERTY(RuleManager* ruleManager READ ruleManager CONSTANT) Q_PROPERTY(TagsManager* tagsManager READ tagsManager CONSTANT) Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT) - Q_PROPERTY(BasicConfiguration* basicConfiguration READ basicConfiguration CONSTANT) -// Q_PROPERTY(AWSClient* awsClient READ awsClient CONSTANT) + Q_PROPERTY(NymeaConfiguration* nymeaConfiguration READ nymeaConfiguration CONSTANT) public: explicit Engine(QObject *parent = nullptr); @@ -57,8 +55,7 @@ public: TagsManager *tagsManager() const; JsonRpcClient *jsonRpcClient() const; LogManager *logManager() const; - BasicConfiguration *basicConfiguration() const; -// AWSClient* awsClient() const; + NymeaConfiguration *nymeaConfiguration() const; Q_INVOKABLE void deployCertificate(); @@ -69,8 +66,7 @@ private: RuleManager *m_ruleManager; LogManager *m_logManager; TagsManager *m_tagsManager; - BasicConfiguration *m_basicConfiguration; -// AWSClient *m_aws; + NymeaConfiguration *m_nymeaConfiguration; private slots: void onConnectedChanged(); diff --git a/libnymea-app-core/jsonrpc/jsonrpcclient.cpp b/libnymea-app-core/jsonrpc/jsonrpcclient.cpp index 69656d5d..7478668e 100644 --- a/libnymea-app-core/jsonrpc/jsonrpcclient.cpp +++ b/libnymea-app-core/jsonrpc/jsonrpcclient.cpp @@ -356,7 +356,6 @@ void JsonRpcClient::dataReceived(const QByteArray &data) m_pushButtonAuthAvailable = dataMap.value("pushButtonAuthAvailable").toBool(); emit pushButtonAuthAvailableChanged(); - qDebug() << "Handshake received" << "initRequired:" << m_initialSetupRequired << "authRequired:" << m_authenticationRequired << "pushButtonAvailable:" << m_pushButtonAuthAvailable;; m_serverUuid = dataMap.value("uuid").toString(); m_serverVersion = dataMap.value("version").toString(); @@ -366,6 +365,8 @@ void JsonRpcClient::dataReceived(const QByteArray &data) } m_jsonRpcVersion = QVersionNumber::fromString(protoVersionString); + qDebug() << "Handshake reply:" << "Protocol version:" << protoVersionString << "InitRequired:" << m_initialSetupRequired << "AuthRequired:" << m_authenticationRequired << "PushButtonAvailable:" << m_pushButtonAuthAvailable;; + QVersionNumber minimumRequiredVersion = QVersionNumber(1, 0); if (m_jsonRpcVersion < minimumRequiredVersion) { qWarning() << "Nymea box doesn't support minimum required version. Required:" << minimumRequiredVersion << "Found:" << m_jsonRpcVersion; diff --git a/libnymea-app-core/libnymea-app-core.h b/libnymea-app-core/libnymea-app-core.h index 650259ce..af67aa52 100644 --- a/libnymea-app-core/libnymea-app-core.h +++ b/libnymea-app-core/libnymea-app-core.h @@ -35,7 +35,11 @@ #include "models/logsmodelng.h" #include "models/valuelogsproxymodel.h" #include "models/interfacesproxy.h" -#include "configuration/basicconfiguration.h" +#include "configuration/nymeaconfiguration.h" +#include "configuration/serverconfiguration.h" +#include "configuration/serverconfigurations.h" +#include "configuration/mqttpolicy.h" +#include "configuration/mqttpolicies.h" #include "wifisetup/networkmanagercontroller.h" #include "wifisetup/wirelessaccesspoint.h" #include "wifisetup/wirelessaccesspoints.h" @@ -143,14 +147,17 @@ void registerQmlTypes() { qmlRegisterUncreatableType(uri, 1, 0, "Plugins", "Can't create this in QML. Get it from the DeviceManager."); qmlRegisterType(uri, 1, 0, "PluginsProxy"); - qmlRegisterUncreatableType(uri, 1, 0, "BasicConfiguration", "Uncreatable"); + qmlRegisterUncreatableType(uri, 1, 0, "NymeaConfiguration", "Get it from Engine"); + qmlRegisterUncreatableType(uri, 1, 0, "ServerConfiguration", "Get it from NymeaConfiguration"); + qmlRegisterUncreatableType(uri, 1, 0, "ServerConfigurations", "Get it from NymeaConfiguration"); + qmlRegisterUncreatableType(uri, 1, 0, "MqttPolicy", "Get it from NymeaConfiguration"); + qmlRegisterUncreatableType(uri, 1, 0, "MqttPolicies", "Get it from NymeaConfiguration"); qmlRegisterType(uri, 1, 0, "NymeaDiscovery"); qmlRegisterUncreatableType(uri, 1, 0, "DiscoveryModel", "Get it from NymeaDiscovery"); qmlRegisterUncreatableType(uri, 1, 0, "DiscoveryDevice", "Get it from DiscoveryModel"); qmlRegisterUncreatableType(uri, 1, 0, "Connection", "Get it from DiscoveryDevice"); - qmlRegisterType(uri, 1, 0, "LogsModel"); qmlRegisterType(uri, 1, 0, "LogsModelNg"); qmlRegisterType(uri, 1, 0, "ValueLogsProxyModel"); diff --git a/libnymea-app-core/libnymea-app-core.pro b/libnymea-app-core/libnymea-app-core.pro index 514e2cce..7ba2784e 100644 --- a/libnymea-app-core/libnymea-app-core.pro +++ b/libnymea-app-core/libnymea-app-core.pro @@ -55,7 +55,6 @@ SOURCES += \ models/valuelogsproxymodel.cpp \ discovery/nymeadiscovery.cpp \ logmanager.cpp \ - configuration/basicconfiguration.cpp \ wifisetup/bluetoothdevice.cpp \ wifisetup/bluetoothdeviceinfo.cpp \ wifisetup/bluetoothdeviceinfos.cpp \ @@ -80,7 +79,11 @@ SOURCES += \ connection/sigv4utils.cpp \ ruletemplates/ruleactionparamtemplate.cpp \ configuration/serverconfiguration.cpp \ - configuration/serverconfigurations.cpp + configuration/serverconfigurations.cpp \ + configuration/nymeaconfiguration.cpp \ + models/mqttpolicies.cpp \ + configuration/mqttpolicy.cpp \ + configuration/mqttpolicies.cpp HEADERS += \ engine.h \ @@ -115,7 +118,6 @@ HEADERS += \ models/valuelogsproxymodel.h \ discovery/nymeadiscovery.h \ logmanager.h \ - configuration/basicconfiguration.h \ wifisetup/bluetoothdevice.h \ wifisetup/bluetoothdeviceinfo.h \ wifisetup/bluetoothdeviceinfos.h \ @@ -140,7 +142,10 @@ HEADERS += \ connection/cloudtransport.h \ ruletemplates/ruleactionparamtemplate.h \ configuration/serverconfiguration.h \ - configuration/serverconfigurations.h + configuration/serverconfigurations.h \ + configuration/nymeaconfiguration.h \ + configuration/mqttpolicy.h \ + configuration/mqttpolicies.h unix { target.path = /usr/lib diff --git a/libnymea-app-core/models/mqttpolicies.cpp b/libnymea-app-core/models/mqttpolicies.cpp new file mode 100644 index 00000000..dad5c524 --- /dev/null +++ b/libnymea-app-core/models/mqttpolicies.cpp @@ -0,0 +1,6 @@ +#include "mqttpolicies.h" + +MqttPolicies::MqttPolicies(QObject *parent) : QObject(parent) +{ + +} diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index 7c5b0ec3..92a8f823 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -157,5 +157,7 @@ ui/images/lighting/concentrate.svg ui/images/lighting/reading.svg ui/images/lighting/relax.svg + ui/images/messaging-app-symbolic.svg + ui/images/mqtt.svg diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index e3ef4ff8..1b02648e 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -136,5 +136,8 @@ ui/customviews/GenericTypeGraph.qml ui/devicepages/SmartMeterDevicePage.qml ui/devicelistpages/SmartMeterDeviceListPage.qml + ui/system/MqttBrokerSettingsPage.qml + ui/system/ServerConfigurationDialog.qml + ui/system/MqttPolicyPage.qml diff --git a/nymea-app/ui/NewDeviceWizard.qml b/nymea-app/ui/NewDeviceWizard.qml index 121683c7..6f860f84 100644 --- a/nymea-app/ui/NewDeviceWizard.qml +++ b/nymea-app/ui/NewDeviceWizard.qml @@ -322,7 +322,7 @@ Page { id: paramRepeater model: d.deviceDescriptorId == null ? d.deviceClass.paramTypes : null delegate: ParamDelegate { - Layout.preferredHeight: 60 +// Layout.preferredHeight: 60 Layout.fillWidth: true paramType: d.deviceClass.paramTypes.get(index) } diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 2ef02af4..5a948bfe 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -158,7 +158,7 @@ ApplicationWindow { case "gateway": return Qt.resolvedUrl("images/network-wired-symbolic.svg") case "notifications": - return Qt.resolvedUrl("images/notification.svg") + return Qt.resolvedUrl("images/messaging-app-symbolic.svg") case "inputtrigger": return Qt.resolvedUrl("images/attention.svg") case "outputtrigger": diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index 0ef4a56f..f7b8b3f0 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -276,7 +276,7 @@ Item { delegate: TabButton { id: hostTabButton property var engine: mainRepeater.itemAt(index)._engine - property string serverName: engine.basicConfiguration.serverName + property string serverName: engine.nymeaConfiguration.serverName Material.elevation: index contentItem: RowLayout { diff --git a/nymea-app/ui/SettingsPage.qml b/nymea-app/ui/SettingsPage.qml index 1df641fa..df24ac24 100644 --- a/nymea-app/ui/SettingsPage.qml +++ b/nymea-app/ui/SettingsPage.qml @@ -62,12 +62,12 @@ Page { TextField { id: nameTextField Layout.fillWidth: true - text: engine.basicConfiguration.serverName + text: engine.nymeaConfiguration.serverName } Button { text: qsTr("OK") - visible: nameTextField.displayText !== engine.basicConfiguration.serverName - onClicked: engine.basicConfiguration.serverName = nameTextField.displayText + visible: nameTextField.displayText !== engine.nymeaConfiguration.serverName + onClicked: engine.nymeaConfiguration.serverName = nameTextField.displayText } } @@ -81,10 +81,10 @@ Page { text: qsTr("Language") } ComboBox { - model: engine.basicConfiguration.availableLanguages - currentIndex: model.indexOf(engine.basicConfiguration.language) + model: engine.nymeaConfiguration.availableLanguages + currentIndex: model.indexOf(engine.nymeaConfiguration.language) onActivated: { - engine.basicConfiguration.language = currentText; + engine.nymeaConfiguration.language = currentText; } } } @@ -100,10 +100,10 @@ Page { } ComboBox { Layout.minimumWidth: 200 - model: engine.basicConfiguration.timezones - currentIndex: model.indexOf(engine.basicConfiguration.timezone) + model: engine.nymeaConfiguration.timezones + currentIndex: model.indexOf(engine.nymeaConfiguration.timezone) onActivated: { - engine.basicConfiguration.timezone = currentText; + engine.nymeaConfiguration.timezone = currentText; } } } @@ -122,8 +122,8 @@ Page { } Switch { id: debugServerEnabledSwitch - checked: engine.basicConfiguration.debugServerEnabled - onClicked: engine.basicConfiguration.debugServerEnabled = checked + checked: engine.nymeaConfiguration.debugServerEnabled + onClicked: engine.nymeaConfiguration.debugServerEnabled = checked } } @@ -138,6 +138,12 @@ Page { } + MeaListItemDelegate { + Layout.fillWidth: true + iconName: "../images/logs.svg" + text: qsTr("Log viewer") + onClicked: pageStack.push(Qt.resolvedUrl("system/LogViewerPage.qml")) + } MeaListItemDelegate { Layout.fillWidth: true iconName: "../images/cloud.svg" @@ -145,24 +151,25 @@ Page { visible: engine.jsonRpcClient.ensureServerVersion("1.9") onClicked: pageStack.push(Qt.resolvedUrl("system/CloudSettingsPage.qml")) } - MeaListItemDelegate { - Layout.fillWidth: true - iconName: "../images/plugin.svg" - text: qsTr("Plugins") - onClicked:pageStack.push(Qt.resolvedUrl("system/PluginsPage.qml")) - } - MeaListItemDelegate { - Layout.fillWidth: true - iconName: "../images/logs.svg" - text: qsTr("Log viewer") - onClicked: pageStack.push(Qt.resolvedUrl("system/LogViewerPage.qml")) - } MeaListItemDelegate { Layout.fillWidth: true iconName: "../images/network-vpn.svg" text: qsTr("Server interfaces") onClicked: pageStack.push(Qt.resolvedUrl("system/ConnectionInterfacesPage.qml")) } + MeaListItemDelegate { + Layout.fillWidth: true + iconName: "../images/mqtt.svg" + text: qsTr("MQTT broker") + visible: engine.jsonRpcClient.ensureServerVersion("1.11") + onClicked: pageStack.push(Qt.resolvedUrl("system/MqttBrokerSettingsPage.qml")) + } + MeaListItemDelegate { + Layout.fillWidth: true + iconName: "../images/plugin.svg" + text: qsTr("Plugins") + onClicked:pageStack.push(Qt.resolvedUrl("system/PluginsPage.qml")) + } MeaListItemDelegate { Layout.fillWidth: true iconName: "../images/info.svg" diff --git a/nymea-app/ui/components/MeaListItemDelegate.qml b/nymea-app/ui/components/MeaListItemDelegate.qml index a243ab2d..e93839a1 100644 --- a/nymea-app/ui/components/MeaListItemDelegate.qml +++ b/nymea-app/ui/components/MeaListItemDelegate.qml @@ -5,6 +5,7 @@ import QtQuick.Controls.Material 2.2 SwipeDelegate { id: root + implicitWidth: 200 property string subText property bool progressive: true diff --git a/nymea-app/ui/delegates/ActionDelegate.qml b/nymea-app/ui/delegates/ActionDelegate.qml index 7da1f75f..fd239d59 100644 --- a/nymea-app/ui/delegates/ActionDelegate.qml +++ b/nymea-app/ui/delegates/ActionDelegate.qml @@ -8,11 +8,13 @@ import "../components" ItemDelegate { id: root - property var actionType: null + property ActionType actionType: null property var actionState: null signal executeAction(var params) + readonly property bool multiParam: actionType.paramTypes.count > 1 + contentItem: ColumnLayout { RowLayout { Label { @@ -25,7 +27,7 @@ ItemDelegate { id: loader Layout.fillWidth: sourceComponent == textFieldComponent || sourceComponent == buttonComponent sourceComponent: { - if (root.actionType.paramTypes.count !== 1) { + if (root.multiParam || root.actionType.paramTypes.count === 0) { return buttonComponent } @@ -68,16 +70,21 @@ ItemDelegate { } Repeater { id: paramRepeater + model: root.actionType.paramTypes delegate: Loader { id: bottomLoader property var paramType: root.actionType.paramTypes.get(index) + Layout.fillWidth: true sourceComponent: { switch (paramType.type.toLowerCase()) { case "int": case "double": if (paramType.minValue !== undefined && paramType.maxValue !== undefined) { + if (root.multiParam) { + return labelledSpinnerComponent; + } return sliderComponent } return textFieldComponent; @@ -87,7 +94,7 @@ ItemDelegate { return paramType.allowedValues.length === 0 ? textFieldComponent : root.actionType.paramTypes.count === 1 ? null : comboBoxComponent case "bool": - if (root.actionType.paramTypes.count > 1) { + if (root.multiParam) { return labeledBoolComponent; } return null @@ -104,9 +111,9 @@ ItemDelegate { } Binding { target: bottomLoader.item - when: bottomLoader.item && root.actionState + when: bottomLoader.item property: "value" - value: root.actionState + value: (root.actionState && index == 0) ? root.actionState : root.actionType.paramTypes.get(index).defaultValue } } } @@ -147,8 +154,10 @@ ItemDelegate { id: switchRow property var paramType: null property var value + Label { text: paramType.displayName + Layout.fillWidth: true } Switch { checked: paramType.defaultValue @@ -196,7 +205,28 @@ ItemDelegate { text: sliderRow.paramType.maxValue } } + } + Component { + id: labelledSpinnerComponent + RowLayout { + id: sliderRow + property var paramType: null + property var value: null + Label { + text: sliderRow.paramType.displayName + Layout.fillWidth: true + } + SpinBox { + from: sliderRow.paramType.minValue + to: sliderRow.paramType.maxValue + value: sliderRow.value + editable: true + onValueModified: { + sliderRow.value = value + } + } + } } Component { diff --git a/nymea-app/ui/delegates/ParamDelegate.qml b/nymea-app/ui/delegates/ParamDelegate.qml index ae4a51e5..3c0d7f7e 100644 --- a/nymea-app/ui/delegates/ParamDelegate.qml +++ b/nymea-app/ui/delegates/ParamDelegate.qml @@ -8,7 +8,7 @@ import "../components" ItemDelegate { id: root - property var paramType: null + property ParamType paramType: null property alias value: d.value property var param: Param { id: d @@ -17,7 +17,10 @@ ItemDelegate { } property bool writable: true + topPadding: 0 + bottomPadding: 0 contentItem: ColumnLayout { + id: contentItemColumn RowLayout { Label { Layout.fillWidth: true @@ -36,10 +39,7 @@ ItemDelegate { case "bool": return boolComponent; case "int": - if (root.paramType.minimumValue !== undefined && root.paramType.maximumValue !== undefined) { - return sliderComponent; - } - return textFieldComponent; + return spinnerComponent; case "string": case "qstring": if (root.paramType.allowedValues.length > 0) { @@ -58,12 +58,12 @@ ItemDelegate { Layout.fillWidth: true sourceComponent: { switch (root.paramType.type.toLowerCase()) { - case "int": - case "double": - if (root.paramType.minValue !== undefined && root.paramType.maxValue !== undefined) { - return sliderComponent - } - break; +// case "int": +// case "double": +// if (root.paramType.minValue !== undefined && root.paramType.maxValue !== undefined) { +// return sliderComponent +// } +// break; case "color": return colorPickerComponent } @@ -140,6 +140,20 @@ ItemDelegate { } + Component { + id: spinnerComponent + SpinBox { + value: root.param.value + from: root.paramType.minValue + to: root.paramType.maxValue + editable: true + onValueModified: root.param.value = value + textFromValue: function(value) { + return value + } + } + } + Component { id: textFieldComponent TextField { @@ -167,7 +181,7 @@ ItemDelegate { ColorPicker { id: colorPicker implicitHeight: 200 -// color: root.param.value + // color: root.param.value Binding { target: colorPicker diff --git a/nymea-app/ui/devicelistpages/DeviceListPageBase.qml b/nymea-app/ui/devicelistpages/DeviceListPageBase.qml index 652bebbf..76a600e6 100644 --- a/nymea-app/ui/devicelistpages/DeviceListPageBase.qml +++ b/nymea-app/ui/devicelistpages/DeviceListPageBase.qml @@ -22,7 +22,8 @@ Page { function enterPage(index, replace) { var device = devicesProxy.get(index); var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); - var page = app.interfaceListToDevicePage(deviceClass.interfaces); + var page = app.interfaceListToDevicePage(root.shownInterfaces); +// var page = "GenericDevicePage.qml"; if (replace) { pageStack.replace(Qt.resolvedUrl("../devicepages/" + page), {device: devicesProxy.get(index)}) } else { diff --git a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml index 5061321f..469e7e92 100644 --- a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml @@ -101,7 +101,7 @@ DeviceListPageBase { Label { Layout.fillWidth: true text: sensorValueDelegate.stateValue - ? "%1 %2".arg(sensorValueDelegate.stateValue.value).arg(sensorValueDelegate.stateType.unitString) + ? "%1 %2".arg(Math.round(sensorValueDelegate.stateValue.value * 100) / 100).arg(sensorValueDelegate.stateType.unitString) : "" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter diff --git a/nymea-app/ui/devicepages/InputTriggerDevicePage.qml b/nymea-app/ui/devicepages/InputTriggerDevicePage.qml index ebd7ff77..eb8eaeca 100644 --- a/nymea-app/ui/devicepages/InputTriggerDevicePage.qml +++ b/nymea-app/ui/devicepages/InputTriggerDevicePage.qml @@ -9,6 +9,7 @@ DevicePageBase { id: root GenericTypeLogView { + id: logView anchors.fill: parent logsModel: engine.jsonRpcClient.ensureServerVersion("1.10") ? logsModelNg : logsModel @@ -28,6 +29,22 @@ DevicePageBase { typeIds: [root.deviceClass.eventTypes.findByName("triggered").id]; } +// delegate: MeaListItemDelegate { +// width: parent.width +// iconName: app.interfaceToIcon("inputtrigger") +// text: model.value.trim() +// subText: Qt.formatDateTime(model.timestamp) +// progressive: false + +// onClicked: { +// print("a", model.value.trim()) +// var parts = model.value.trim().split(', ') +// print("b", parts) +// var popup = detailsPopup.createObject(root, {timestamp: model.timestamp, notificationTitle: parts[1], notificationBody: parts[0]}); +// popup.open(); +// } +// } + onAddRuleClicked: { var value = logView.logsModel.get(index).value var typeId = logView.logsModel.get(index).typeId diff --git a/nymea-app/ui/devicepages/NotificationsDevicePage.qml b/nymea-app/ui/devicepages/NotificationsDevicePage.qml index 4f7cd7e8..4ee79901 100644 --- a/nymea-app/ui/devicepages/NotificationsDevicePage.qml +++ b/nymea-app/ui/devicepages/NotificationsDevicePage.qml @@ -88,6 +88,7 @@ DevicePageBase { id: logsModelNg deviceId: root.device.id engine: _engine + live: true typeIds: [root.deviceClass.actionTypes.findByName("notify").id]; } @@ -102,7 +103,7 @@ DevicePageBase { delegate: MeaListItemDelegate { width: parent.width - iconName: "../images/notification.svg" + iconName: app.interfaceToIcon("notifications") text: model.value.trim() subText: Qt.formatDateTime(model.timestamp) progressive: false diff --git a/nymea-app/ui/images/messaging-app-symbolic.svg b/nymea-app/ui/images/messaging-app-symbolic.svg new file mode 100644 index 00000000..7f6d4219 --- /dev/null +++ b/nymea-app/ui/images/messaging-app-symbolic.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/nymea-app/ui/images/mqtt.svg b/nymea-app/ui/images/mqtt.svg new file mode 100644 index 00000000..29419932 --- /dev/null +++ b/nymea-app/ui/images/mqtt.svg @@ -0,0 +1,72 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + MQTT + + + + + diff --git a/nymea-app/ui/mainviews/DevicesPageDelegate.qml b/nymea-app/ui/mainviews/DevicesPageDelegate.qml index 95f635c1..fa8845b6 100644 --- a/nymea-app/ui/mainviews/DevicesPageDelegate.qml +++ b/nymea-app/ui/mainviews/DevicesPageDelegate.qml @@ -389,7 +389,7 @@ MainPageTile { // switch (model.name) { // case "sensor": // } - return parent.state.value + "°C"; + return (Math.round(parent.state.value * 100) / 100) + " °C"; } } } diff --git a/nymea-app/ui/system/CloudSettingsPage.qml b/nymea-app/ui/system/CloudSettingsPage.qml index cb5c74d3..04ee5808 100644 --- a/nymea-app/ui/system/CloudSettingsPage.qml +++ b/nymea-app/ui/system/CloudSettingsPage.qml @@ -49,9 +49,9 @@ Page { SwitchDelegate { Layout.fillWidth: true text: qsTr("Cloud connection enabled") - checked: engine.basicConfiguration.cloudEnabled + checked: engine.nymeaConfiguration.cloudEnabled onToggled: { - engine.basicConfiguration.cloudEnabled = checked; + engine.nymeaConfiguration.cloudEnabled = checked; } } diff --git a/nymea-app/ui/system/ConnectionInterfaceDelegate.qml b/nymea-app/ui/system/ConnectionInterfaceDelegate.qml index f28102f7..8f970feb 100644 --- a/nymea-app/ui/system/ConnectionInterfaceDelegate.qml +++ b/nymea-app/ui/system/ConnectionInterfaceDelegate.qml @@ -5,9 +5,9 @@ import Nymea 1.0 import "../components" MeaListItemDelegate { - text: model.address - subText: model.port - iconName: "../images/network-wifi-symbolic.svg" + text: qsTr("Interface: %1").arg(model.address === "0.0.0.0" ? qsTr("Any") : model.address === "127.0.0.1" ? qsTr("localhost") : model.address) + subText: qsTr("Port: %1").arg(model.port) + iconName: "../images/network-vpn.svg" progressive: false iconColor: { if ((engine.connection.hostAddress === model.address || model.address === "0.0.0.0") diff --git a/nymea-app/ui/system/ConnectionInterfacesPage.qml b/nymea-app/ui/system/ConnectionInterfacesPage.qml index 2fed94b1..ee7e0232 100644 --- a/nymea-app/ui/system/ConnectionInterfacesPage.qml +++ b/nymea-app/ui/system/ConnectionInterfacesPage.qml @@ -27,18 +27,52 @@ Page { Layout.topMargin: app.margins text: qsTr("TCP Server Interfaces") wrapMode: Text.WordWrap + color: app.accentColor } Repeater { - model: engine.basicConfiguration.tcpServerConfigurations + model: engine.nymeaConfiguration.tcpServerConfigurations delegate: ConnectionInterfaceDelegate { Layout.fillWidth: true + canDelete: true + onClicked: { + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.tcpServerConfigurations.get(index).clone() }); + popup.accepted.connect(function() { + engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } onDeleteClicked: { print("should delete") - engine.basicConfiguration.deleteTcpServerConfiguration(model.id) + engine.nymeaConfiguration.deleteTcpServerConfiguration(model.id) } } } + Button { + Layout.fillWidth: true + Layout.margins: app.margins + text: qsTr("Add") + onClicked: { + var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 2222 + engine.nymeaConfiguration.tcpServerConfigurations.count, false, false); + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: config }); + popup.accepted.connect(function() { + engine.nymeaConfiguration.setTcpServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } + } + + ThinDivider {} Label { Layout.fillWidth: true Layout.leftMargin: app.margins @@ -46,18 +80,51 @@ Page { Layout.topMargin: app.margins text: qsTr("WebSocket Server Interfaces") wrapMode: Text.WordWrap + color: app.accentColor } Repeater { - model: engine.basicConfiguration.websocketServerConfigurations + model: engine.nymeaConfiguration.webSocketServerConfigurations delegate: ConnectionInterfaceDelegate { Layout.fillWidth: true + canDelete: true + onClicked: { + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.webSocketServerConfigurations.get(index).clone() }); + popup.accepted.connect(function() { + print("configuring:", popup.serverConfiguration.port) + engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } onDeleteClicked: { print("should delete", model.id) - engine.basicConfiguration.deleteWebsocketServerConfiguration(model.id) + engine.nymeaConfiguration.deleteWebSocketServerConfiguration(model.id) } } } + Button { + Layout.fillWidth: true + Layout.margins: app.margins + text: qsTr("Add") + onClicked: { + var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 4444 + engine.nymeaConfiguration.webSocketServerConfigurations.count, false, false); + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: config }); + popup.accepted.connect(function() { + engine.nymeaConfiguration.setWebSocketServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } + } } } } diff --git a/nymea-app/ui/system/MqttBrokerSettingsPage.qml b/nymea-app/ui/system/MqttBrokerSettingsPage.qml new file mode 100644 index 00000000..0d7e0781 --- /dev/null +++ b/nymea-app/ui/system/MqttBrokerSettingsPage.qml @@ -0,0 +1,143 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Nymea 1.0 +import "../components" + +Page { + id: root + header: GuhHeader { + text: qsTr("MQTT broker") + onBackPressed: pageStack.pop(); + } + +// Flickable { +// anchors.fill: parent +// contentHeight: connectionsColumn.implicitHeight +// interactive: contentHeight > height + + ColumnLayout { + id: connectionsColumn +// anchors { left: parent.left; top: parent.top; right: parent.right } + anchors.fill: parent +// layoutDirection: Qt. + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + Layout.topMargin: app.margins + text: qsTr("MQTT Server Interfaces") + wrapMode: Text.WordWrap + color: app.accentColor + } + + ListView { + Layout.fillWidth: true + Layout.minimumHeight: 0 + Layout.preferredHeight: Math.min(contentHeight, 120) + model: engine.nymeaConfiguration.mqttServerConfigurations + clip: true + ScrollBar.vertical: ScrollBar {} + + delegate: ConnectionInterfaceDelegate { + width: parent.width + canDelete: true + onClicked: { + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: engine.nymeaConfiguration.mqttServerConfigurations.get(index).clone() }); + popup.accepted.connect(function() { + engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } + + onDeleteClicked: { + engine.nymeaConfiguration.deleteMqttServerConfiguration(model.id) + } + } + } + Button { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + text: qsTr("Add") + onClicked: { + var config = engine.nymeaConfiguration.createServerConfiguration("0.0.0.0", 1883 + engine.nymeaConfiguration.mqttServerConfigurations.count, false, false); + var component = Qt.createComponent(Qt.resolvedUrl("ServerConfigurationDialog.qml")); + var popup = component.createObject(root, { serverConfiguration: config }); + popup.accepted.connect(function() { + engine.nymeaConfiguration.setMqttServerConfiguration(popup.serverConfiguration) + popup.serverConfiguration.destroy(); + }) + popup.rejected.connect(function() { + popup.serverConfiguration.destroy(); + }) + popup.open() + } + } + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.topMargin: app.margins; Layout.rightMargin: app.margins + text: qsTr("MQTT permissions") + wrapMode: Text.WordWrap + color: app.accentColor + } + + ListView { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, parent.height * .4) + model: engine.nymeaConfiguration.mqttPolicies + clip: true + ScrollBar.vertical: ScrollBar {} + delegate: MeaListItemDelegate { + width: parent.width + iconName: "../images/account.svg" + text: qsTr("Client ID: %1").arg(model.clientId) + subText: qsTr("Username: %1").arg(model.username) + progressive: false + canDelete: true + onClicked: { + var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.mqttPolicies.get(index).clone() }); + page.accepted.connect(function() { + if (page.policy.clientId !== model.clientId) { + engine.nymeaConfiguration.deleteMqttPolicy(model.clientId); + } + engine.nymeaConfiguration.updateMqttPolicy(page.policy) + page.policy.destroy(); + }) + page.rejected.connect(function() { + page.policy.destroy(); + }) + } + onDeleteClicked: { + engine.nymeaConfiguration.deleteMqttPolicy(model.clientId) + } + } + } + + Button { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + text: qsTr("Add") + onClicked: { + var page = pageStack.push(Qt.resolvedUrl("MqttPolicyPage.qml"), { policy: engine.nymeaConfiguration.createMqttPolicy() }); + page.accepted.connect(function() { + engine.nymeaConfiguration.updateMqttPolicy(page.policy) + page.policy.destroy(); + }) + page.rejected.connect(function() { + page.policy.destroy(); + }) + } + } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 0 + } + } +// } +} diff --git a/nymea-app/ui/system/MqttPolicyPage.qml b/nymea-app/ui/system/MqttPolicyPage.qml new file mode 100644 index 00000000..ba958481 --- /dev/null +++ b/nymea-app/ui/system/MqttPolicyPage.qml @@ -0,0 +1,201 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 +import "../components" + +Page { + id: root + header: GuhHeader { + text: qsTr("Mqtt permission") + onBackPressed: { + root.rejected(); + pageStack.pop(); + } + HeaderButton { + imageSource: "../images/tick.svg" + enabled: clientIdTextField.isValid + onClicked: { + root.accepted(); + pageStack.pop(); + } + } + } + property MqttPolicy policy: null + + signal accepted(); + signal rejected() + + ColumnLayout { + anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom } + RowLayout { + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins + spacing: app.margins + Label { + text: qsTr("Client ID:") + Layout.fillWidth: true + } + TextField { + id: clientIdTextField + Layout.fillWidth: true + text: root.policy ? root.policy.clientId : "" + onEditingFinished: root.policy.clientId = text + placeholderText: qsTr("E.g. Sensor_1") + property bool isEmpty: displayText.length === 0 + property bool isDuplicate: clientIdTextField.displayText != root.policy.clientId && engine.nymeaConfiguration.mqttPolicies.getPolicy(clientIdTextField.displayText) !== null + property bool isValid: !isEmpty && !isDuplicate + } + } + Label { + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + text: clientIdTextField.isDuplicate ? qsTr("%1 is already used").arg(clientIdTextField.displayText) : qsTr("Can't be blank") + font.pixelSize: app.smallFont + Layout.alignment: Qt.AlignRight + color: "red" + visible: !clientIdTextField.isValid + } + + RowLayout { + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + Label { + text: qsTr("Username:") + Layout.fillWidth: true + } + TextField { + id: usernameTextField + Layout.fillWidth: true + text: root.policy ? root.policy.username : "" + onEditingFinished: root.policy.username = text + placeholderText: qsTr("Optional") + } + } + + RowLayout { + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + Label { + text: qsTr("Password:") + Layout.fillWidth: true + } + RowLayout { + TextField { + id: passwordTextField + Layout.fillWidth: true + text: root.policy ? root.policy.password : "" + onEditingFinished: root.policy.password = text + placeholderText: qsTr("Optional") + echoMode: hiddenPassword ? TextInput.Password : TextInput.Normal + property bool hiddenPassword: true + } + ColorIcon { + Layout.preferredHeight: app.iconSize + Layout.preferredWidth: height + name: "../images/eye.svg" + color: passwordTextField.hiddenPassword ? keyColor : app.accentColor + MouseArea { + anchors.fill: parent + onClicked: passwordTextField.hiddenPassword = !passwordTextField.hiddenPassword + } + } + } + } + + ThinDivider {} + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins + text: qsTr("Allowed publish topics") + } + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + model: root.policy.allowedPublishTopicFilters + ScrollBar.vertical: ScrollBar {} + clip: true + delegate: MeaListItemDelegate { + width: parent.width + text: modelData + canDelete: true + progressive: false + onDeleteClicked: { + root.policy.allowedPublishTopicFilters.splice(index, 1) + } + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + property bool add: false + TextField { + id: pubField + Layout.fillWidth: parent.add + Layout.preferredWidth: parent.add ? undefined : 0 + Behavior on width { + NumberAnimation {} + } + } + Button { + Layout.fillWidth: !parent.add + text: parent.add ? qsTr("OK") : qsTr("Add") + enabled: !parent.add || pubField.displayText.length > 0 + onClicked: { + if (parent.add) { + root.policy.allowedPublishTopicFilters.push(pubField.displayText) + pubField.clear(); + } + parent.add = !parent.add; + } + } + } + + ThinDivider {} + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins + text: qsTr("Allowed subscribe filters") + } + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + model: root.policy.allowedSubscribeTopicFilters + ScrollBar.vertical: ScrollBar {} + clip: true + delegate: MeaListItemDelegate { + width: parent.width + text: modelData + canDelete: true + progressive: false + onDeleteClicked: { + root.policy.allowedSubscribeTopicFilters.splice(index, 1) + } + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins + property bool add: false + TextField { + id: subField + Layout.fillWidth: parent.add + Layout.preferredWidth: parent.add ? undefined : 0 + } + Button { + Layout.fillWidth: !parent.add; + Behavior on width { NumberAnimation {} } + text: parent.add ? qsTr("OK") : qsTr("Add") + enabled: !parent.add || subField.displayText.length > 0 + onClicked: { + if (parent.add) { + root.policy.allowedSubscribeTopicFilters.push(subField.displayText) + subField.clear(); + } + parent.add = !parent.add; + } + } + } + } +} diff --git a/nymea-app/ui/system/ServerConfigurationDialog.qml b/nymea-app/ui/system/ServerConfigurationDialog.qml new file mode 100644 index 00000000..b3837944 --- /dev/null +++ b/nymea-app/ui/system/ServerConfigurationDialog.qml @@ -0,0 +1,95 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 + +Dialog { + id: root + title: qsTr("Server configuration") + width: parent.width * .8 + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + property ServerConfiguration serverConfiguration: null + standardButtons: Dialog.Ok | Dialog.Cancel + + ColumnLayout { + anchors { left: parent.left; top: parent.top; right: parent.right } + RowLayout { + Label { + text: qsTr("Interface") + Layout.fillWidth: true + } + ComboBox { + id: interfaceCombobox + model: [qsTr("Any"), qsTr("Localhost"), qsTr("Custom")] + Layout.fillWidth: true + currentIndex: !root.serverConfiguration + ? 0 : root.serverConfiguration.address === "0.0.0.0" + ? 0 + : root.serverConfiguration.address === "127.0.0.1" + ? 1 : 2 + onActivated: { + switch (index) { + case 0: + root.serverConfiguration.address = "0.0.0.0"; + break; + case 1: + root.serverConfiguration.address = "127.0.0.1"; + break; + } + } + } + } + RowLayout { + visible: interfaceCombobox.currentIndex === 2 + Label { + text: qsTr("Address:") + Layout.fillWidth: true + } + TextField { + id: addressTextField + Layout.fillWidth: true + inputMethodHints: Qt.ImhPreferNumbers + inputMask: "000.000.000.000" + text: root.serverConfiguration ? root.serverConfiguration.address : "" + onEditingFinished: root.serverConfiguration.address = text + } + } + + RowLayout { + Label { + text: qsTr("Port:") + Layout.fillWidth: true + } + TextField { + inputMethodHints: Qt.ImhDigitsOnly + text: root.serverConfiguration ? root.serverConfiguration.port : 0 + validator: IntValidator { bottom: 0; top: 65535 } + onEditingFinished: root.serverConfiguration.port = text + } + } + + RowLayout { + Label { + Layout.fillWidth: true + text: qsTr("SSL enabled") + } + CheckBox { + checkState: root.serverConfiguration && root.serverConfiguration.sslEnabled ? Qt.Checked : Qt.Unchecked + onClicked: root.serverConfiguration.sslEnabled = checked + } + } + RowLayout { + Label { + Layout.fillWidth: true + text: qsTr("Login required") + } + CheckBox { + checkState: root.serverConfiguration && root.serverConfiguration.authenticationEnabled ? Qt.Checked : Qt.Unchecked + onClicked: root.serverConfiguration.authenticationEnabled = checked + } + } + } +}