diff --git a/libnymea-app-core/connection/awsclient.cpp b/libnymea-app-core/connection/awsclient.cpp index ef752798..2192ca2e 100644 --- a/libnymea-app-core/connection/awsclient.cpp +++ b/libnymea-app-core/connection/awsclient.cpp @@ -10,6 +10,7 @@ #include "sigv4utils.h" +AWSClient* AWSClient::s_instance = nullptr; // This is Symantec's root CA certificate and most platforms should // have this in their certificate storage already, but as we can't @@ -116,6 +117,14 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent), m_sessionTokenExpiry = settings.value("sessionTokenExpiry").toDateTime(); } +AWSClient *AWSClient::instance() +{ + if (!s_instance) { + s_instance = new AWSClient(); + } + return s_instance; +} + bool AWSClient::isLoggedIn() const { return !m_userId.isEmpty() && !m_username.isEmpty() && !m_password.isEmpty(); diff --git a/libnymea-app-core/connection/awsclient.h b/libnymea-app-core/connection/awsclient.h index d4c7e498..2f9e49b3 100644 --- a/libnymea-app-core/connection/awsclient.h +++ b/libnymea-app-core/connection/awsclient.h @@ -96,7 +96,7 @@ public: }; Q_ENUM(LoginError) - explicit AWSClient(QObject *parent = nullptr); + static AWSClient* instance(); bool isLoggedIn() const; QString username() const; @@ -145,6 +145,9 @@ signals: void configChanged(); private: + explicit AWSClient(QObject *parent = nullptr); + static AWSClient* s_instance; + void refreshAccessToken(); void getCredentialsForIdentity(const QString &identityId); void connectMQTT(); diff --git a/libnymea-app-core/connection/cloudtransport.cpp b/libnymea-app-core/connection/cloudtransport.cpp index 53e48daf..745a03c7 100644 --- a/libnymea-app-core/connection/cloudtransport.cpp +++ b/libnymea-app-core/connection/cloudtransport.cpp @@ -105,15 +105,14 @@ void CloudTransport::ignoreSslErrors(const QList &errors) m_remoteproxyConnection->ignoreSslErrors(errors); } -CloudTransportFactory::CloudTransportFactory(AWSClient *awsClient): - m_awsClient(awsClient) +CloudTransportFactory::CloudTransportFactory() { } NymeaTransportInterface *CloudTransportFactory::createTransport(QObject *parent) const -{ - return new CloudTransport(m_awsClient, parent); +{ + return new CloudTransport(AWSClient::instance(), parent); } QStringList CloudTransportFactory::supportedSchemes() const diff --git a/libnymea-app-core/connection/cloudtransport.h b/libnymea-app-core/connection/cloudtransport.h index 2d247501..90ecb445 100644 --- a/libnymea-app-core/connection/cloudtransport.h +++ b/libnymea-app-core/connection/cloudtransport.h @@ -13,11 +13,9 @@ class RemoteProxyConnection; class CloudTransportFactory: public NymeaTransportInterfaceFactory { public: - CloudTransportFactory(AWSClient *awsClient); + CloudTransportFactory(); NymeaTransportInterface* createTransport(QObject *parent = nullptr) const override; QStringList supportedSchemes() const override; -private: - AWSClient *m_awsClient = nullptr; }; class CloudTransport : public NymeaTransportInterface diff --git a/libnymea-app-core/engine.cpp b/libnymea-app-core/engine.cpp index 73d8d997..8e6a83f3 100644 --- a/libnymea-app-core/engine.cpp +++ b/libnymea-app-core/engine.cpp @@ -39,30 +39,29 @@ 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_aws(new AWSClient(this)) + m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)) { m_connection->registerTransport(new TcpSocketTransportFactory()); m_connection->registerTransport(new WebsocketTransportFactory()); m_connection->registerTransport(new BluetoothTransportFactoy()); - m_connection->registerTransport(new CloudTransportFactory(m_aws)); + m_connection->registerTransport(new CloudTransportFactory()); connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged); connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged); connect(m_deviceManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged); - connect(m_aws, &AWSClient::devicesFetched, this, [this]() { + connect(AWSClient::instance(), &AWSClient::devicesFetched, this, [this]() { if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) { - if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) { - m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId()); + if (AWSClient::instance()->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) { + m_jsonRpcClient->setupRemoteAccess(AWSClient::instance()->idToken(), AWSClient::instance()->userId()); } } }); connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, [this]() { if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) { - if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) { - m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId()); + if (AWSClient::instance()->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) { + m_jsonRpcClient->setupRemoteAccess(AWSClient::instance()->idToken(), AWSClient::instance()->userId()); } } }); @@ -99,10 +98,10 @@ BasicConfiguration *Engine::basicConfiguration() const return m_basicConfiguration; } -AWSClient *Engine::awsClient() const -{ - return m_aws; -} +//AWSClient *Engine::awsClient() const +//{ +// return m_aws; +//} void Engine::deployCertificate() { @@ -110,11 +109,11 @@ void Engine::deployCertificate() qWarning() << "JSONRPC not connected. Cannot deploy certificate"; return; } - if (!m_aws->isLoggedIn()) { + if (!AWSClient::instance()->isLoggedIn()) { qWarning() << "Not logged in at AWS. Cannot deploy certificate"; return; } - m_aws->fetchCertificate(m_jsonRpcClient->serverUuid(), [this](const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint){ + AWSClient::instance()->fetchCertificate(m_jsonRpcClient->serverUuid(), [this](const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint){ qDebug() << "Certificate received" << certificate << publicKey << privateKey; m_jsonRpcClient->deployCertificate(rootCA, certificate, publicKey, privateKey, endpoint); }); diff --git a/libnymea-app-core/engine.h b/libnymea-app-core/engine.h index f270a89a..9f90a4a8 100644 --- a/libnymea-app-core/engine.h +++ b/libnymea-app-core/engine.h @@ -43,7 +43,7 @@ class Engine : public QObject 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(AWSClient* awsClient READ awsClient CONSTANT) public: explicit Engine(QObject *parent = nullptr); @@ -58,7 +58,7 @@ public: JsonRpcClient *jsonRpcClient() const; LogManager *logManager() const; BasicConfiguration *basicConfiguration() const; - AWSClient* awsClient() const; +// AWSClient* awsClient() const; Q_INVOKABLE void deployCertificate(); @@ -70,7 +70,7 @@ private: LogManager *m_logManager; TagsManager *m_tagsManager; BasicConfiguration *m_basicConfiguration; - AWSClient *m_aws; +// AWSClient *m_aws; private slots: void onConnectedChanged(); diff --git a/libnymea-app-core/libnymea-app-core.h b/libnymea-app-core/libnymea-app-core.h index e8611ed2..4038784f 100644 --- a/libnymea-app-core/libnymea-app-core.h +++ b/libnymea-app-core/libnymea-app-core.h @@ -61,6 +61,14 @@ static QObject* interfacesModel_provider(QQmlEngine *engine, QJSEngine *scriptEn return new Interfaces(); } +static QObject* awsClientProvider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return AWSClient::instance(); +} + void registerQmlTypes() { const char uri[] = "Nymea"; @@ -161,7 +169,7 @@ void registerQmlTypes() { qmlRegisterUncreatableType(uri, 1, 0, "WirelessAccessPoints", "Can't create this in QML. Get it from the Engine instance."); qmlRegisterUncreatableType(uri, 1, 0, "WirelessAccessPoints", "Can't create this in QML. Get it from the Engine instance."); - qmlRegisterUncreatableType(uri, 1, 0, "AWSClient", "Can't create this in QML. Get it from Engine"); + qmlRegisterSingletonType(uri, 1, 0, "AWSClient", awsClientProvider); qmlRegisterUncreatableType(uri, 1, 0, "AWSDevice", "Can't create this in QML. Get it from AWSClient"); qmlRegisterType(uri, 1, 0, "RuleTemplates"); diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 9ac099c5..b12048fc 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -32,7 +32,6 @@ ApplicationWindow { readonly property bool landscape: app.width > app.height readonly property var settings: Settings { - property string lastConnectedHost: "" property alias viewMode: app.visibility property bool returnToHome: false property bool darkTheme: false @@ -42,6 +41,7 @@ ApplicationWindow { property bool showHiddenOptions: false property int cloudEnvironment: 0 property bool showConnectionTabs: app.hasOwnProperty("industrialSetup") && app.industrialSetup + property int tabCount: 1 } RootItem { diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index 500d96db..b19a27c8 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -2,6 +2,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.2 import QtQuick.Layouts 1.3 +import Qt.labs.settings 1.0 import Nymea 1.0 import "components" @@ -16,17 +17,26 @@ Item { ListModel { id: tabModel + Component.onCompleted: { - append({}) + for (var i = 0; i < settings.tabCount; i++) { + tabModel.append({}) + } } + function addTab() { - settings.lastConnectedHost = "" tabModel.append({}) + settings.tabCount++; + } + function removeTab(index) { + remove(index); + settings.tabCount--; } } ColumnLayout { anchors.fill: parent + spacing: 0 SwipeView { id: swipeView @@ -45,13 +55,21 @@ Item { objectName: "pageStack" initialItem: Page {} + Settings { + id: tabSettings + category: "tabSettings" + index + property string lastConnectedHost + } + Engine { id: engine } property alias _engine: engine + property int connectionTabIndex: index + onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url Binding { - target: engine.awsClient + target: AWSClient property: "config" value: settings.cloudEnvironment } @@ -74,14 +92,14 @@ Item { print("opening push button auth") var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml")) page.backPressed.connect(function() { - settings.lastConnectedHost = ""; + tabSettings.lastConnectedHost = ""; engine.connection.disconnect(); init(); }) } else { var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml")); page.backPressed.connect(function() { - settings.lastConnectedHost = ""; + tabSettings.lastConnectedHost = ""; engine.connection.disconnect() init(); }) @@ -110,7 +128,7 @@ Item { askForPermissions = true; } - if (!engine.awsClient.isLoggedIn) { + if (!AWSClient.isLoggedIn) { print("AWS not logged in. Cannot register for push"); return; } @@ -125,7 +143,7 @@ Item { PlatformHelper.requestPermissions(); } } else { - engine.awsClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp"); + AWSClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp"); } } @@ -134,7 +152,7 @@ Item { onConnectedChanged: { print("json client connected changed", engine.jsonRpcClient.connected) if (engine.jsonRpcClient.connected) { - settings.lastConnectedHost = engine.connection.url + tabSettings.lastConnectedHost = engine.connection.url } init(); } @@ -153,7 +171,7 @@ Item { popup.actualVersion = actualVersion; popup.minimumVersion = minimumVersion popup.open() - settings.lastConnectedHost = "" + tabSettings.lastConnectedHost = "" } } @@ -183,7 +201,7 @@ Item { } Connections { - target: engine.awsClient + target: AWSClient onIsLoggedInChanged: { setupPushNotifications() } @@ -233,6 +251,7 @@ Item { RowLayout { visible: settings.showConnectionTabs + spacing: 0 TabBar { id: tabbar @@ -246,6 +265,7 @@ Item { id: hostTabButton property var engine: mainRepeater.itemAt(index)._engine property string serverName: engine.basicConfiguration.serverName + Material.elevation: index contentItem: RowLayout { Label { @@ -260,7 +280,7 @@ Item { name: "../images/close.svg" MouseArea { anchors.fill: parent - onClicked: tabModel.remove(index) + onClicked: tabModel.removeTab(index) } } } @@ -272,17 +292,25 @@ Item { } } - TabButton { + Pane { + Layout.preferredHeight: tabbar.height Layout.preferredWidth: height - contentItem: ColorIcon { - height: parent.height - width: parent.width - name: "../images/add.svg" - } - onClicked: { - tabModel.addTab() + Material.elevation: 2 + padding: 0 + + TabButton { + anchors.fill: parent + contentItem: ColorIcon { + height: parent.height + width: parent.width + name: "../images/add.svg" + } + onClicked: { + tabModel.addTab() + } } } + } } } diff --git a/nymea-app/ui/SettingsPage.qml b/nymea-app/ui/SettingsPage.qml index 4a9a3c0d..cb5c8d14 100644 --- a/nymea-app/ui/SettingsPage.qml +++ b/nymea-app/ui/SettingsPage.qml @@ -36,7 +36,7 @@ Page { Button { text: qsTr("Disconnect") onClicked: { - settings.lastConnectedHost = ""; + tabSettings.lastConnectedHost = ""; engine.connection.disconnect(); } } diff --git a/nymea-app/ui/appsettings/CloudLoginPage.qml b/nymea-app/ui/appsettings/CloudLoginPage.qml index e55427fc..da1b2d31 100644 --- a/nymea-app/ui/appsettings/CloudLoginPage.qml +++ b/nymea-app/ui/appsettings/CloudLoginPage.qml @@ -12,17 +12,17 @@ Page { } Component.onCompleted: { - if (engine.awsClient.isLoggedIn) { - engine.awsClient.fetchDevices(); + if (AWSClient.isLoggedIn) { + AWSClient.fetchDevices(); } } Connections { - target: engine.awsClient + target: AWSClient onLoginResult: { busyOverlay.shown = false; if (error === AWSClient.LoginErrorNoError) { - engine.awsClient.fetchDevices(); + AWSClient.fetchDevices(); } } onDeleteAccountResult: { @@ -39,14 +39,14 @@ Page { ColumnLayout { anchors.fill: parent - visible: engine.awsClient.isLoggedIn + visible: AWSClient.isLoggedIn Label { Layout.fillWidth: true Layout.topMargin: app.margins Layout.leftMargin: app.margins Layout.rightMargin: app.margins wrapMode: Text.WordWrap - text: qsTr("Logged in as %1").arg(engine.awsClient.username) + text: qsTr("Logged in as %1").arg(AWSClient.username) } Button { @@ -66,15 +66,15 @@ Page { Layout.leftMargin: app.margins Layout.rightMargin: app.margins wrapMode: Text.WordWrap - text: engine.awsClient.awsDevices.count === 0 ? + text: AWSClient.awsDevices.count === 0 ? qsTr("There are no boxes connected to your cloud yet.") : - qsTr("There are %n boxes connected to your cloud", "", engine.awsClient.awsDevices.count) + qsTr("There are %n boxes connected to your cloud", "", AWSClient.awsDevices.count) } ListView { Layout.fillWidth: true Layout.fillHeight: true clip: true - model: engine.awsClient.awsDevices + model: AWSClient.awsDevices delegate: MeaListItemDelegate { width: parent.width text: model.name @@ -98,13 +98,13 @@ Page { } onDeleteClicked: { - engine.awsClient.unpairDevice(model.id); + AWSClient.unpairDevice(model.id); } } BusyIndicator { anchors.centerIn: parent - visible: engine.awsClient.awsDevices.busy + visible: AWSClient.awsDevices.busy } } } @@ -132,16 +132,16 @@ Page { onAccepted: { if (deleteCheckbox.checked) { busyOverlay.shown = true; - engine.awsClient.deleteAccount() + AWSClient.deleteAccount() } else { - engine.awsClient.logout() + AWSClient.logout() } } } ColumnLayout { anchors { left: parent.left; right: parent.right; top: parent.top } - visible: !engine.awsClient.isLoggedIn + visible: !AWSClient.isLoggedIn Label { Layout.fillWidth: true Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins @@ -197,12 +197,12 @@ Page { enabled: usernameTextField.acceptableInput onClicked: { busyOverlay.shown = true - engine.awsClient.login(usernameTextField.text, passwordTextField.text); + AWSClient.login(usernameTextField.text, passwordTextField.text); } } Connections { - target: engine.awsClient + target: AWSClient onLoginResult: { errorLabel.visible = (error !== AWSClient.LoginErrorNoError) } @@ -310,14 +310,14 @@ Page { enabled: usernameTextField.acceptableInput && passwordTextField.isValidPassword onClicked: { busyOverlay.shown = true; - engine.awsClient.signup(usernameTextField.text, passwordTextField.password) + AWSClient.signup(usernameTextField.text, passwordTextField.password) } } } Connections { - target: engine.awsClient + target: AWSClient onSignupResult: { busyOverlay.shown = false; var text; @@ -371,12 +371,12 @@ Page { text: qsTr("OK") onClicked: { busyOverlay.shown = true; - engine.awsClient.confirmRegistration(confirmationCodeTextField.text) + AWSClient.confirmRegistration(confirmationCodeTextField.text) } } Connections { - target: engine.awsClient + target: AWSClient onConfirmationResult: { busyOverlay.shown = false; var text @@ -418,7 +418,7 @@ Page { } Connections { - target: engine.awsClient + target: AWSClient onForgotPasswordResult: { busyOverlay.shown = false if (error !== AWSClient.LoginErrorNoError) { @@ -459,7 +459,7 @@ Page { Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins text: qsTr("Reset password") onClicked: { - engine.awsClient.forgotPassword(emailTextField.text) + AWSClient.forgotPassword(emailTextField.text) busyOverlay.shown = true } } @@ -478,7 +478,7 @@ Page { id: confirmResetPasswordPage Connections { - target: engine.awsClient + target: AWSClient onConfirmForgotPasswordResult: { busyOverlay.shown = false if (error !== AWSClient.LoginErrorNoError) { @@ -545,7 +545,7 @@ Page { enabled: passwordTextField.isValidPassword && codeTextField.text.length > 0 onClicked: { busyOverlay.shown = true - engine.awsClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password) + AWSClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password) } } BusyOverlay { diff --git a/nymea-app/ui/connection/ConnectPage.qml b/nymea-app/ui/connection/ConnectPage.qml index a96e50f6..63a4aa03 100644 --- a/nymea-app/ui/connection/ConnectPage.qml +++ b/nymea-app/ui/connection/ConnectPage.qml @@ -11,8 +11,8 @@ Page { readonly property bool haveHosts: discovery.discoveryModel.count > 0 Component.onCompleted: { - print("completed connectPage. last connected host:", settings.lastConnectedHost) - if (settings.lastConnectedHost.length > 0 && engine.connection.connect(settings.lastConnectedHost)) { + print("completed connectPage for tab", connectionTabIndex, "last connected host:", tabSettings.lastConnectedHost) + if (tabSettings.lastConnectedHost.length > 0 && engine.connection.connect(tabSettings.lastConnectedHost)) { var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml")) page.cancel.connect(function() { engine.connection.disconnect(); @@ -37,7 +37,7 @@ Page { NymeaDiscovery { id: discovery objectName: "discovery" - awsClient: engine.awsClient + awsClient: AWSClient discovering: pageStack.currentItem.objectName === "discoveryPage" } @@ -285,7 +285,7 @@ Page { Layout.leftMargin: app.margins Layout.rightMargin: app.margins text: qsTr("Cloud login") - visible: !engine.awsClient.isLoggedIn + visible: !AWSClient.isLoggedIn onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/CloudLoginPage.qml")) } diff --git a/nymea-app/ui/system/CloudSettingsPage.qml b/nymea-app/ui/system/CloudSettingsPage.qml index 0ea6a4dc..cb5c74d3 100644 --- a/nymea-app/ui/system/CloudSettingsPage.qml +++ b/nymea-app/ui/system/CloudSettingsPage.qml @@ -21,8 +21,8 @@ Page { print("cloud connection state changed", engine.jsonRpcClient.cloudConnectionState) if (engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateConnected) { d.deploymentStarted = false; - if (engine.awsClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) === null) { - engine.jsonRpcClient.setupRemoteAccess(engine.awsClient.idToken, engine.awsClient.userId) + if (AWSClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) === null) { + engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId) } } } @@ -43,7 +43,7 @@ Page { // Button { // text: "pair" -// onClicked: engine.jsonRpcClient.setupRemoteAccess(engine.awsClient.idToken, engine.awsClient.userId) +// onClicked: engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId) // } SwitchDelegate { @@ -111,9 +111,9 @@ Page { Layout.fillWidth: true Layout.leftMargin: app.margins; Layout.rightMargin: app.margins visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted - text: engine.awsClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud") + text: AWSClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud") onClicked: { - if (engine.awsClient.isLoggedIn) { + if (AWSClient.isLoggedIn) { d.deploymentStarted = true engine.deployCertificate(); } else { @@ -121,16 +121,5 @@ Page { } } } - - -// Label { -// Layout.fillWidth: true -// Layout.leftMargin: app.margins -// Layout.rightMargin: app.margins -// visible: engine.basicConfiguration.cloudEnabled && engine.awsClient.isLoggedIn -// text: engine.awsClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) !== null ? -// qsTr("This box is connected to a nymea:cloud.") : -// qsTr("Connecting to nymea:cloud...") -// } } }