From bebe246410580e55927a4ad53e87193e793c2447 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 20 Nov 2018 16:25:43 +0100 Subject: [PATCH] enable configuring mqtt policies --- .../configuration/mqttpolicies.cpp | 21 ++ .../configuration/mqttpolicies.h | 1 + .../configuration/mqttpolicy.cpp | 9 +- libnymea-app-core/configuration/mqttpolicy.h | 1 + .../configuration/nymeaconfiguration.cpp | 20 +- .../configuration/nymeaconfiguration.h | 1 + nymea-app/resources.qrc | 2 +- .../ui/system/MqttBrokerSettingsPage.qml | 106 +++++----- nymea-app/ui/system/MqttPolicyDialog.qml | 57 ------ nymea-app/ui/system/MqttPolicyPage.qml | 186 ++++++++++++++++++ 10 files changed, 295 insertions(+), 109 deletions(-) delete mode 100644 nymea-app/ui/system/MqttPolicyDialog.qml create mode 100644 nymea-app/ui/system/MqttPolicyPage.qml diff --git a/libnymea-app-core/configuration/mqttpolicies.cpp b/libnymea-app-core/configuration/mqttpolicies.cpp index dfe4dba4..c03a5235 100644 --- a/libnymea-app-core/configuration/mqttpolicies.cpp +++ b/libnymea-app-core/configuration/mqttpolicies.cpp @@ -71,6 +71,27 @@ void MqttPolicies::addPolicy(MqttPolicy *policy) 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()){ diff --git a/libnymea-app-core/configuration/mqttpolicies.h b/libnymea-app-core/configuration/mqttpolicies.h index 887676ae..ae7a5d7c 100644 --- a/libnymea-app-core/configuration/mqttpolicies.h +++ b/libnymea-app-core/configuration/mqttpolicies.h @@ -28,6 +28,7 @@ public: 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(); diff --git a/libnymea-app-core/configuration/mqttpolicy.cpp b/libnymea-app-core/configuration/mqttpolicy.cpp index c1bb2f5a..ee476a44 100644 --- a/libnymea-app-core/configuration/mqttpolicy.cpp +++ b/libnymea-app-core/configuration/mqttpolicy.cpp @@ -5,8 +5,8 @@ MqttPolicy::MqttPolicy(const QString &clientId, const QString &username, const Q m_clientId(clientId), m_username(username), m_password(password), - m_allowedSubscribeTopicFilters(allowedSubscribeTopicFilters), - m_allowedPublishTopicFilters(allowedPublishTopicFilters) + m_allowedPublishTopicFilters(allowedPublishTopicFilters), + m_allowedSubscribeTopicFilters(allowedSubscribeTopicFilters) { } @@ -75,3 +75,8 @@ void MqttPolicy::setAllowedSubscribeTopicFilters(const QStringList &allowedSubsc 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 index 09e4d8bb..8f809967 100644 --- a/libnymea-app-core/configuration/mqttpolicy.h +++ b/libnymea-app-core/configuration/mqttpolicy.h @@ -35,6 +35,7 @@ public: QStringList allowedSubscribeTopicFilters() const; void setAllowedSubscribeTopicFilters(const QStringList &allowedSubscribeTopicFilters); + Q_INVOKABLE MqttPolicy* clone(); signals: void clientIdChanged(); void usernameChanged(); diff --git a/libnymea-app-core/configuration/nymeaconfiguration.cpp b/libnymea-app-core/configuration/nymeaconfiguration.cpp index 452e2299..79eed28e 100644 --- a/libnymea-app-core/configuration/nymeaconfiguration.cpp +++ b/libnymea-app-core/configuration/nymeaconfiguration.cpp @@ -134,7 +134,7 @@ ServerConfiguration *NymeaConfiguration::createServerConfiguration(const QString MqttPolicy *NymeaConfiguration::createMqttPolicy() const { - return new MqttPolicy(); + return new MqttPolicy(QString(), QString(), QString(), {"#"}, {"#"}); } void NymeaConfiguration::setTcpServerConfiguration(ServerConfiguration *configuration) @@ -214,7 +214,7 @@ void NymeaConfiguration::deleteMqttPolicy(const QString &clientId) { QVariantMap params; params.insert("clientId", clientId); - m_client->sendCommand("Configuration.RemoveMqttPolicy", params); + m_client->sendCommand("Configuration.DeleteMqttPolicy", params, this, "deleteMqttPolicyReply"); } void NymeaConfiguration::getConfigurationsResponse(const QVariantMap ¶ms) @@ -347,6 +347,11 @@ 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(); @@ -421,7 +426,7 @@ void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification) } if (notif == "Configuration.MqttPolicyChanged") { MqttPolicy *policy = nullptr; - QVariantMap policyMap = notification.value("params").toMap(); + 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); @@ -436,8 +441,17 @@ void NymeaConfiguration::notificationReceived(const QVariantMap ¬ification) 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/nymeaconfiguration.h b/libnymea-app-core/configuration/nymeaconfiguration.h index a7b9ee46..83e8f9dd 100644 --- a/libnymea-app-core/configuration/nymeaconfiguration.h +++ b/libnymea-app-core/configuration/nymeaconfiguration.h @@ -92,6 +92,7 @@ private: 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); diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 088ac41d..1b02648e 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -138,6 +138,6 @@ ui/devicelistpages/SmartMeterDeviceListPage.qml ui/system/MqttBrokerSettingsPage.qml ui/system/ServerConfigurationDialog.qml - ui/system/MqttPolicyDialog.qml + ui/system/MqttPolicyPage.qml diff --git a/nymea-app/ui/system/MqttBrokerSettingsPage.qml b/nymea-app/ui/system/MqttBrokerSettingsPage.qml index 8bc1f99e..0d7e0781 100644 --- a/nymea-app/ui/system/MqttBrokerSettingsPage.qml +++ b/nymea-app/ui/system/MqttBrokerSettingsPage.qml @@ -21,49 +21,6 @@ Page { // anchors { left: parent.left; top: parent.top; right: parent.right } anchors.fill: parent // layoutDirection: Qt. - Label { - Layout.fillWidth: true - Layout.margins: app.margins - text: qsTr("MQTT permissions") - wrapMode: Text.WordWrap - color: app.accentColor - } - - ListView { - Layout.fillWidth: true - Layout.preferredHeight: contentHeight - model: engine.nymeaConfiguration.mqttPolicies - 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 - onDeleteClicked: { - engine.nymeaConfiguration.deleteMqttPolicy(model.clientId) - } - } - } - - Button { - Layout.fillWidth: true - Layout.margins: app.margins - text: qsTr("Add") - onClicked: { - var component = Qt.createComponent(Qt.resolvedUrl("MqttPolicyDialog.qml")); - var popup = component.createObject(root, { policy: engine.nymeaConfiguration.createMqttPolicy() }); - popup.accepted.connect(function() { - engine.nymeaConfiguration.updateMqttPolicy(popup.policy) - popup.policy.destroy(); - }) - popup.rejected.connect(function() { - popup.policy.destroy(); - }) - popup.open() - } - } - Label { Layout.fillWidth: true Layout.leftMargin: app.margins @@ -77,8 +34,11 @@ Page { ListView { Layout.fillWidth: true Layout.minimumHeight: 0 - Layout.preferredHeight: contentHeight + Layout.preferredHeight: Math.min(contentHeight, 120) model: engine.nymeaConfiguration.mqttServerConfigurations + clip: true + ScrollBar.vertical: ScrollBar {} + delegate: ConnectionInterfaceDelegate { width: parent.width canDelete: true @@ -96,14 +56,13 @@ Page { } onDeleteClicked: { - print("should delete") engine.nymeaConfiguration.deleteMqttServerConfiguration(model.id) } } } Button { Layout.fillWidth: true - Layout.margins: app.margins + 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); @@ -119,6 +78,61 @@ Page { 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 diff --git a/nymea-app/ui/system/MqttPolicyDialog.qml b/nymea-app/ui/system/MqttPolicyDialog.qml deleted file mode 100644 index 596912c1..00000000 --- a/nymea-app/ui/system/MqttPolicyDialog.qml +++ /dev/null @@ -1,57 +0,0 @@ -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("Mqtt permission") - width: parent.width * .8 - x: (parent.width - width) / 2 - y: (parent.height - height) / 2 - - property MqttPolicy policy: null - standardButtons: Dialog.Ok | Dialog.Cancel - - ColumnLayout { - anchors { left: parent.left; top: parent.top; right: parent.right } - RowLayout { - 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 - } - } - RowLayout { - Label { - text: qsTr("Username:") - Layout.fillWidth: true - } - TextField { - id: usernameTextField - Layout.fillWidth: true - text: root.policy ? root.policy.username : "" - onEditingFinished: root.policy.username = text - } - } - - RowLayout { - Label { - text: qsTr("Password:") - Layout.fillWidth: true - } - TextField { - Layout.fillWidth: true - text: root.policy ? root.policy.password : "" - onEditingFinished: root.policy.password = text - } - } - - } -} diff --git a/nymea-app/ui/system/MqttPolicyPage.qml b/nymea-app/ui/system/MqttPolicyPage.qml new file mode 100644 index 00000000..7b6274a6 --- /dev/null +++ b/nymea-app/ui/system/MqttPolicyPage.qml @@ -0,0 +1,186 @@ +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 + } + TextField { + Layout.fillWidth: true + text: root.policy ? root.policy.password : "" + onEditingFinished: root.policy.password = text + placeholderText: qsTr("Optional") + } + } + + 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; + } + } + } + } +}