From 877d4fb6c3719eb480447bc06af7a2e216e16b7c Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 10 Jun 2021 14:54:08 +0200 Subject: [PATCH] New connection setup wizard --- libnymea-app/wifisetup/bluetoothdiscovery.cpp | 8 + nymea-app/images.qrc | 2 + nymea-app/resources.qrc | 2 + nymea-app/ui/RootItem.qml | 9 +- nymea-app/ui/StyleBase.qml | 8 +- nymea-app/ui/components/BigThingTile.qml | 2 +- nymea-app/ui/components/BigTile.qml | 7 - nymea-app/ui/components/WizardPageBase.qml | 117 +++ .../ui/connection/NewConnectionWizard.qml | 715 ++++++++++++++++++ .../devicepages/NotificationsDevicePage.qml | 7 + .../ui/devicepages/SmartMeterDevicePage.qml | 7 + .../images/setupwizard/wired-connection.svg | 237 ++++++ .../setupwizard/wireless-connection.svg | 142 ++++ 13 files changed, 1249 insertions(+), 14 deletions(-) create mode 100644 nymea-app/ui/components/WizardPageBase.qml create mode 100644 nymea-app/ui/connection/NewConnectionWizard.qml create mode 100644 nymea-app/ui/images/setupwizard/wired-connection.svg create mode 100644 nymea-app/ui/images/setupwizard/wireless-connection.svg diff --git a/libnymea-app/wifisetup/bluetoothdiscovery.cpp b/libnymea-app/wifisetup/bluetoothdiscovery.cpp index f4f065c6..9efd73a5 100644 --- a/libnymea-app/wifisetup/bluetoothdiscovery.cpp +++ b/libnymea-app/wifisetup/bluetoothdiscovery.cpp @@ -33,6 +33,7 @@ #include #include #include +#include BluetoothDiscovery::BluetoothDiscovery(QObject *parent) : QObject(parent), @@ -180,6 +181,13 @@ void BluetoothDiscovery::deviceDiscovered(const QBluetoothDeviceInfo &deviceInfo return; } + // Only show devices that either list the wifi service uuid or are called BT WLAN setup (for legacy reasons) + static QBluetoothUuid wifiServiceUuid = QBluetoothUuid(QUuid("e081fec0-f757-4449-b9c9-bfa83133f7fc")); + if (!deviceInfo.serviceUuids().contains(wifiServiceUuid) && deviceInfo.name() != "BT WLAN setup") { + qDebug() << "Skipping device" << deviceInfo.name() << deviceInfo.serviceUuids(); + return; + } + foreach (BluetoothDeviceInfo *di, m_deviceInfos->deviceInfos()) { if (di->address() == deviceInfo.address().toString()) { di->setBluetoothDeviceInfo(deviceInfo); diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index 02bf4a33..1a17b457 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -260,5 +260,7 @@ ui/images/cleaning-robot.svg ui/images/chart.svg ui/images/modbus.svg + ui/images/setupwizard/wired-connection.svg + ui/images/setupwizard/wireless-connection.svg diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index dce7ce38..2bab6a46 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -249,5 +249,7 @@ ui/system/ModbusRtuSettingsPage.qml ui/system/ModbusRtuAddMasterPage.qml ui/system/ModbusRtuReconfigureMasterPage.qml + ui/connection/NewConnectionWizard.qml + ui/components/WizardPageBase.qml diff --git a/nymea-app/ui/RootItem.qml b/nymea-app/ui/RootItem.qml index 9deb880e..819d9201 100644 --- a/nymea-app/ui/RootItem.qml +++ b/nymea-app/ui/RootItem.qml @@ -184,13 +184,14 @@ Item { } else if (autoConnectHost.length > 0) { var host = nymeaDiscovery.nymeaHosts.createLanHost(Configuration.systemName, autoConnectHost); engine.jsonRpcClient.connectToHost(host) + return; } else { // Only hide the splash right away if we're not trying to connect to something // If it's not hidden here it will be hidden in 3 seconds or when the connection is up, whichever comes first PlatformHelper.hideSplashScreen(); } - pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"), StackView.Immediate) + pageStack.push(Qt.resolvedUrl("connection/NewConnectionWizard.qml"), StackView.Immediate) } Timer { running: true; repeat: false; interval: 3000; onTriggered: PlatformHelper.hideSplashScreen(); } @@ -200,7 +201,8 @@ Item { pageStack.clear() if (!engine.jsonRpcClient.currentHost) { print("pushing ConnectPage") - pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) + tabSettings.lastConnectedHost = "" + pageStack.push(Qt.resolvedUrl("connection/NewConnectionWizard.qml")) PlatformHelper.hideSplashScreen(); return; } @@ -475,7 +477,6 @@ Item { Layout.fillWidth: true Material.elevation: 2 position: TabBar.Footer - property int tabWidth: Math.max(150, root.width / tabModel.count) Repeater { model: tabModel.count @@ -485,7 +486,7 @@ Item { property var engine: mainRepeater.itemAt(index)._engine property string serverName: engine.nymeaConfiguration.serverName Material.elevation: index - width: tabbar.tabWidth + width: Math.max(150, tabbar.width / tabModel.count) Rectangle { anchors.fill: parent diff --git a/nymea-app/ui/StyleBase.qml b/nymea-app/ui/StyleBase.qml index 5e2870d4..cf165efb 100644 --- a/nymea-app/ui/StyleBase.qml +++ b/nymea-app/ui/StyleBase.qml @@ -51,13 +51,17 @@ Item { family: "Ubuntu", pixelSize: 16 }) - readonly property font largeFont: Qt.font({ + readonly property font bigFont: Qt.font({ family: "Ubuntu", pixelSize: 20 }) + readonly property font largeFont: Qt.font({ + family: "Ubuntu", + pixelSize: 32 + }) readonly property font hugeFont: Qt.font({ family: "Ubuntu", - pixelSize: 40 + pixelSize: 46 }) diff --git a/nymea-app/ui/components/BigThingTile.qml b/nymea-app/ui/components/BigThingTile.qml index 7042540b..b663d174 100644 --- a/nymea-app/ui/components/BigThingTile.qml +++ b/nymea-app/ui/components/BigThingTile.qml @@ -17,7 +17,7 @@ BigTile { var contextMenu = contextMenuComponent.createObject(root, { thing: root.thing }) contextMenu.x = Qt.binding(function() { return (root.width - contextMenu.width) / 2 }) contextMenu.open() - } + } header: RowLayout { id: headerRow diff --git a/nymea-app/ui/components/BigTile.qml b/nymea-app/ui/components/BigTile.qml index d7494260..397646b8 100644 --- a/nymea-app/ui/components/BigTile.qml +++ b/nymea-app/ui/components/BigTile.qml @@ -30,13 +30,6 @@ Item { wobbleAnimation.start(); } - onPressAndHold: { - var contextMenuComponent = Qt.createComponent("../components/ThingContextMenu.qml"); - var contextMenu = contextMenuComponent.createObject(root, { thing: root.thing }) - contextMenu.x = Qt.binding(function() { return (root.width - contextMenu.width) / 2 }) - contextMenu.open() - } - transform: Translate { id: wobbleTransform } SequentialAnimation { diff --git a/nymea-app/ui/components/WizardPageBase.qml b/nymea-app/ui/components/WizardPageBase.qml new file mode 100644 index 00000000..496443ca --- /dev/null +++ b/nymea-app/ui/components/WizardPageBase.qml @@ -0,0 +1,117 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.9 +import QtQuick.Layouts 1.2 +import "../components" +import Nymea 1.0 + +Page { + id: root + + property alias text: textLabel.text + property alias content: contentContainer.children + + property alias showNextButton: nextButton.visible + property alias nextButtonText: nextLabel.text + property alias showBackButton: backButton.visible + property alias backButtonText: backLabel.text + property alias showExtraButton: extraButton.visible + property alias extraButtonText: extraButtonLabel.text + + signal next(); + signal back(); + signal extraButtonPressed(); + + ColumnLayout { + anchors.fill: parent + spacing: Style.margins + + Label { + id: titleLabel + Layout.fillWidth: true + Layout.margins: Style.margins + text: root.title + font: Style.largeFont + horizontalAlignment: Text.AlignHCenter + } + + Label { + id: textLabel + Layout.fillWidth: true + Layout.margins: Style.margins + wrapMode: Text.WordWrap + text: qsTr("This wizard will guide you through the process of setting up a new nymea system.") + horizontalAlignment: Text.AlignHCenter + } + + Item { + id: contentContainer + Layout.fillWidth: true + Layout.fillHeight: true + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: Style.margins + Layout.rightMargin: Style.margins + + MouseArea { + id: backButton + Layout.preferredHeight: Style.delegateHeight + Layout.preferredWidth: childrenRect.width + Layout.alignment: Qt.AlignLeft + RowLayout { + anchors.centerIn: parent + ColorIcon { + Layout.alignment: Qt.AlignRight + size: Style.iconSize + name: "back" + } + Label { + id: backLabel + Layout.fillWidth: true + text: qsTr("Back") + } + } + onClicked: root.back() + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: Style.delegateHeight + MouseArea { + id: extraButton + anchors { left: parent.left; verticalCenter: parent.verticalCenter } + height: Style.delegateHeight + width: childrenRect.width + visible: false + Label { + id: extraButtonLabel + anchors.centerIn: parent + } + onClicked: root.extraButtonPressed() + } + } + + MouseArea { + id: nextButton + Layout.preferredHeight: Style.delegateHeight + Layout.preferredWidth: childrenRect.width + Layout.alignment: Qt.AlignRight + RowLayout { + anchors.centerIn: parent + Label { + id: nextLabel + Layout.fillWidth: true + text: qsTr("Next") + } + ColorIcon { + Layout.alignment: Qt.AlignRight + size: Style.iconSize + name: "next" + } + } + onClicked: root.next() + } + } + } +} diff --git a/nymea-app/ui/connection/NewConnectionWizard.qml b/nymea-app/ui/connection/NewConnectionWizard.qml new file mode 100644 index 00000000..322cde77 --- /dev/null +++ b/nymea-app/ui/connection/NewConnectionWizard.qml @@ -0,0 +1,715 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.9 +import QtQuick.Layouts 1.2 +import "../components" +import Nymea 1.0 + +WizardPageBase { + id: root + title: qsTr("Welcome") + text: qsTr("This wizard will guide you through the process of setting up a new nymea system.") + showBackButton: false + showExtraButton: true + extraButtonText: qsTr("Demo mode") + + onNext: pageStack.push(connectionSelectionComponent) + onExtraButtonPressed: { + var host = nymeaDiscovery.nymeaHosts.createWanHost("Demo server", "nymea://nymea.nymea.io:2222") + engine.jsonRpcClient.connectToHost(host) + } + + content: ColumnLayout { + anchors.fill: parent + anchors.margins: Style.margins + + ColumnLayout { + Layout.fillHeight: false + ColorIcon { + size: Style.hugeIconSize * 3 + name: "nymea-logo" + Layout.alignment: Qt.AlignHCenter + } + + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: "nymea" + font: Style.hugeFont + } + } + + + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + font: Style.smallFont + text: qsTr("In order to use nymea, you will need to install nymea:core on a computer in your network. This can be a Raspberry Pi or any generic Linux computer.") + horizontalAlignment: Text.AlignHCenter + } + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + font: Style.smallFont + text: qsTr("Please follow the installation instructions on %1 to install a nymea system.").arg('nymea.io') + horizontalAlignment: Text.AlignHCenter + onLinkActivated: Qt.openUrlExternally(link) + } + } + + Component { + id: connectionSelectionComponent + WizardPageBase { + title: qsTr("Connectivity") + text: qsTr("How would you like to connect nymea to your network?") + + nextButtonText: qsTr("Skip") + + onNext: pageStack.push(selectInstanceComponent) + onBack: pageStack.pop() + + content: ColumnLayout { + anchors.fill: parent + anchors.margins: Style.margins + + BigTile { + Layout.fillWidth: true + + onClicked: pageStack.push(wiredInstructionsComponent) + + contentItem: RowLayout { + spacing: Style.margins + ColorIcon { + size: Style.hugeIconSize + name: "connections/network-wired" + color: Style.accentColor + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: qsTr("Wired network") + } + Label { + Layout.fillWidth: true + text: qsTr("Connect nymea to your network using a network cable. This is recommended for best performance.") + font: Style.smallFont + wrapMode: Text.WordWrap + } + } + } + } + + BigTile { + Layout.fillWidth: true + + onClicked: pageStack.push(wirelessInstructionsComponent) + + contentItem: RowLayout { + spacing: Style.margins + ColorIcon { + size: Style.hugeIconSize + name: "connections/network-wifi" + color: Style.accentColor + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: qsTr("Wireless network") + } + Label { + Layout.fillWidth: true + text: qsTr("Connect nymea to your WiFi network.") + font: Style.smallFont + wrapMode: Text.WordWrap + } + } + } + } + + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: qsTr("If your nymea system is already connected to the network you can skip this step.") + horizontalAlignment: Qt.AlignHCenter + font: Style.smallFont + } + } + } + } + + Component { + id: selectInstanceComponent + WizardPageBase { + title: qsTr("Connection") + text: qsTr("Connecting to the nymea system.") + showNextButton: false + + onBack: pageStack.pop() + + content: ColumnLayout { + anchors.fill: parent + + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: NymeaHostsFilterModel { + id: hostsProxy + discovery: nymeaDiscovery + showUnreachableBearers: false + jsonRpcClient: engine.jsonRpcClient + showUnreachableHosts: false + } + + ColumnLayout { + anchors.centerIn: parent + width: parent.width + visible: hostsProxy.count == 0 + spacing: Style.margins + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + } + Label { + Layout.fillWidth: true + Layout.margins: Style.margins + text: qsTr("Please wait while your nymea system is being discovered.") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + } + + + delegate: NymeaSwipeDelegate { + id: nymeaHostDelegate + width: parent.width + property var nymeaHost: hostsProxy.get(index) + property string defaultConnectionIndex: { + var bestIndex = -1 + var bestPriority = 0; + for (var i = 0; i < nymeaHost.connections.count; i++) { + var connection = nymeaHost.connections.get(i); + if (bestIndex === -1 || connection.priority > bestPriority) { + bestIndex = i; + bestPriority = connection.priority; + } + } + return bestIndex; + } + iconName: { + switch (nymeaHost.connections.get(defaultConnectionIndex).bearerType) { + case Connection.BearerTypeLan: + case Connection.BearerTypeWan: + if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) { + return "/ui/images/connections/network-wired.svg" + } + return "/ui/images/connections/network-wifi.svg"; + case Connection.BearerTypeBluetooth: + return "/ui/images/connections/bluetooth.svg"; + case Connection.BearerTypeCloud: + return "/ui/images/connections/cloud.svg" + case Connection.BearerTypeLoopback: + return "qrc:/styles/%1/logo.svg".arg(styleController.currentStyle) + } + return "" + } + text: model.name + subText: nymeaHost.connections.get(defaultConnectionIndex).url + wrapTexts: false + prominentSubText: false + progressive: false + property bool isSecure: nymeaHost.connections.get(defaultConnectionIndex).secure + property bool isOnline: nymeaHost.connections.get(defaultConnectionIndex).bearerType !== Connection.BearerTypeWan ? nymeaHost.connections.get(defaultConnectionIndex).online : true + tertiaryIconName: isSecure ? "/ui/images/connections/network-secure.svg" : "" + secondaryIconName: !isOnline ? "/ui/images/connections/cloud-error.svg" : "" + secondaryIconColor: "red" + + onClicked: { + engine.jsonRpcClient.connectToHost(nymeaHostDelegate.nymeaHost) + } + + contextOptions: [ + { + text: qsTr("Info"), + icon: Qt.resolvedUrl("/ui/images/info.svg"), + callback: function() { + var nymeaHost = hostsProxy.get(index); + var popup = connectionInfoDialog.createObject(app,{nymeaHost: nymeaHost}) + popup.open() + } + } + ] + } + } + } + } + } + + Component { + id: wiredInstructionsComponent + WizardPageBase { + title: qsTr("Wired connection") + text: qsTr("Connect the nymea system to your network using an ethernet cable and turn it on.") + + onNext: pageStack.push(selectInstanceComponent) + onBack: pageStack.pop() + + content: ColumnLayout { + anchors.fill: parent + anchors.margins: Style.margins + + + Image { + Layout.fillWidth: true + Layout.leftMargin: Style.bigMargins + Layout.rightMargin: Style.bigMargins + Layout.preferredHeight: width * sourceSize.height / sourceSize.width + source: "/ui/images/setupwizard/wired-connection.svg" + } + } + } + } + + Component { + id: wirelessInstructionsComponent + WizardPageBase { + title: qsTr("Wireless connection") + text: qsTr("Turn the nymea system on by connecting the power cable and wait for it to start up.") + + onNext: pageStack.push(wirelessBluetoothDiscoveryComponent) + onBack: pageStack.pop() + + content: ColumnLayout { + anchors.fill: parent + anchors.margins: Style.margins + + + Image { + Layout.fillWidth: true + Layout.leftMargin: Style.bigMargins + Layout.rightMargin: Style.bigMargins + Layout.preferredHeight: width * sourceSize.height / sourceSize.width + source: "/ui/images/setupwizard/wireless-connection.svg" + } + } + } + } + Component { + id: wirelessBluetoothDiscoveryComponent + WizardPageBase { + id: wirelessBluetoothDiscoveryPage + title: qsTr("Wireless setup") + text: qsTr("Searching for the nymea system...") + showNextButton: false + onBack: pageStack.pop() + + BtWiFiSetup { + id: wifiSetup + + onBluetoothStatusChanged: { + print("status changed", status) + switch (status) { + case BtWiFiSetup.BluetoothStatusDisconnected: + pageStack.pop(wirelessBluetoothDiscoveryPage) + break; + case BtWiFiSetup.BluetoothStatusConnectingToBluetooth: + break; + case BtWiFiSetup.BluetoothStatusConnectedToBluetooth: + break; + case BtWiFiSetup.BluetoothStatusLoaded: + if (!wifiSetup.networkingEnabled) { + wifiSetup.networkingEnabled = true; + } + if (!wifiSetup.wirelessEnabled) { + wifiSetup.wirelessEnabled = true; + } + pageStack.pop(wirelessBluetoothDiscoveryPage, StackView.Immediate) + pageStack.push(wirelessSelectWifiComponent, {wifiSetup: wifiSetup}) + break; + } + } + onBluetoothConnectionError: { + pageStack.pop(wirelessBluetoothDiscoveryPage, StackView.Immediate) + pageStack.push(wirelessBtErrorComponent) + } + + onCurrentConnectionChanged: { + if (wifiSetup.currentConnection) { + print("**** connected!") + pageStack.push(wirelessConnectionCompletedComponent, {wifiSetup: wifiSetup}) + } + } + onWirelessStatusChanged: { + print("Wireless status changed:", wifiSetup.networkStatus) + if (wifiSetup.wirelessStatus === BtWiFiSetup.WirelessStatusFailed) { + pageStack.pop() + } + } + } + + BluetoothDiscovery { + id: bluetoothDiscovery + discoveryEnabled: pageStack.currentItem === wirelessBluetoothDiscoveryPage + } + + content: ListView { + anchors.fill: parent + model: bluetoothDiscovery.deviceInfos + + BusyIndicator { + anchors.centerIn: parent + visible: bluetoothDiscovery.discovering && bluetoothDiscovery.deviceInfos.count == 0 + } + + delegate: NymeaSwipeDelegate { + width: parent.width + iconName: Qt.resolvedUrl("/ui/images/connections/bluetooth.svg") + text: model.name + subText: model.address + + onClicked: { + wifiSetup.connectToDevice(bluetoothDiscovery.deviceInfos.get(index)) + pageStack.push(wirelessBluetoothConnectingComponent) + } + } + } + } + } + + Component { + id: wirelessBluetoothConnectingComponent + WizardPageBase { + title: qsTr("Wireless setup") + text: qsTr("Connecting to the nymea system...") + showNextButton: false + + content: BusyIndicator { + anchors.centerIn: parent + } + } + } + + Component { + id: wirelessSelectWifiComponent + WizardPageBase { + title: qsTr("Wireless setup") + text: qsTr("Select the WiFi you want to use.") + showNextButton: false + + property var wifiSetup: null + + content: ListView { + anchors.fill: parent + model: wifiSetup.accessPoints + clip: true + + delegate: NymeaItemDelegate { + width: parent.width + + text: model.ssid !== "" ? model.ssid : qsTr("Hidden Network") + subText: model.hostAddress + + iconColor: model.selectedNetwork ? Style.accentColor : "#808080" + iconName: { + if (model.protected) { + if (model.signalStrength <= 25) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-25-secure.svg") + + if (model.signalStrength <= 50) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-50-secure.svg") + + if (model.signalStrength <= 75) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-75-secure.svg") + + if (model.signalStrength <= 100) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-100-secure.svg") + + } else { + + if (model.signalStrength <= 25) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-25.svg") + + if (model.signalStrength <= 50) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-50.svg") + + if (model.signalStrength <= 75) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-75.svg") + + if (model.signalStrength <= 100) + return Qt.resolvedUrl("/ui/images/connections/nm-signal-100.svg") + + } + } + + onClicked: { + print("Connect to ", model.ssid, " --> ", model.macAddress) + if (model.selectedNetwork) { + pageStack.push(networkInformationPage, { ssid: model.ssid}) + } else { + pageStack.push(wirelessAuthenticationComponent, { wifiSetup: wifiSetup, ssid: model.ssid }) + } + } + } + } + } + } + + Component { + id: wirelessAuthenticationComponent + WizardPageBase { + title: qsTr("Wireless setup") + text: qsTr("Enter the password for the WiFi network.") + showNextButton: passwordTextField.isValidPassword + + onNext: { + print("connecting to", ssid, passwordTextField.password) + wifiSetup.connectDeviceToWiFi(ssid, passwordTextField.password) + pageStack.push(wirelessConnectingWiFiComponent) + } + + property BtWiFiSetup wifiSetup: null + property string ssid: "" + + content: ColumnLayout { + anchors.centerIn: parent + width: parent.width - Style.margins * 2 + + Label { + Layout.fillWidth: true + text: ssid + } + + PasswordTextField { + id: passwordTextField + Layout.fillWidth: true + signup: false + requireLowerCaseLetter: false + requireUpperCaseLetter: false + requireNumber: false + requireSpecialChar: false + minPasswordLength: 8 + } + } + } + } + + Component { + id: wirelessBtErrorComponent + WizardPageBase { + title: qsTr("Wireless setup") + text: qsTr("An error happened in the Bluetooth connection. Please try again.") + showNextButton: false + onBack: pageStack.pop() + } + } + + Component { + id: wirelessConnectingWiFiComponent + WizardPageBase { + title: qsTr("Wireless setup") + text: qsTr("Please wait while the nymea system is being connected to the WiFi.") + showNextButton: false + onBack: pageStack.pop() + + content: BusyIndicator { + anchors.centerIn: parent + } + } + } + + Component { + id: wirelessConnectionCompletedComponent + WizardPageBase { + id: wirelessConnectionCompletedPage + title: qsTr("Wireless setup") + text: qsTr("The nymea system has been connected successfully.") + + showNextButton: host != null + showBackButton: false + + onNext: engine.jsonRpcClient.connectToHost(host) + + property BtWiFiSetup wifiSetup: null + + property NymeaHost host: null + + Component.onCompleted: updateNextButton() + + Connections { + target: nymeaDiscovery.nymeaHosts + onCountChanged: updateNextButton(); + } + + function updateNextButton() { + if (!wifiSetup.currentConnection) { + wirelessConnectionCompletedPage.host = null; + return; + } + + // FIXME: We should rather look for the UUID here, but nymea-networkmanager doesn't support getting us the nymea uuid (yet) + for (var i = 0; i < nymeaDiscovery.nymeaHosts.count; i++) { + for (var j = 0; j < nymeaDiscovery.nymeaHosts.get(i).connections.count; j++) { + if (nymeaDiscovery.nymeaHosts.get(i).connections.get(j).url.toString().indexOf(wifiSetup.currentConnection.hostAddress) >= 0) { + wirelessConnectionCompletedPage.host = nymeaDiscovery.nymeaHosts.get(i) + return; + } + } + nymeaDiscovery.nymeaHosts.get(i).connections.countChanged.connect(function() { + updateNextButton(); + }) + } + wirelessConnectionCompletedPage.host = null; + } + + content: ColumnLayout { + width: parent.width - Style.margins * 2 + anchors.centerIn: parent + spacing: Style.margins + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: qsTr("You can now go ahead and configure your nymea system.") + visible: wirelessConnectionCompletedPage.host != null + } + BusyIndicator { + Layout.alignment: Qt.AlignHCenter + visible: wirelessConnectionCompletedPage.host == null + } + + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + visible: wirelessConnectionCompletedPage.host == null + text: qsTr("Waiting for your nymea setup to appear in the network.") + horizontalAlignment: Text.AlignHCenter + } + } + } + } + + + Component { + id: connectionInfoDialog + Dialog { + id: dialog + width: Math.min(parent.width, contentGrid.implicitWidth) + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + modal: true + title: qsTr("Box information") + + standardButtons: Dialog.Ok + + property var nymeaHost: null + + header: Item { + implicitHeight: headerRow.height + app.margins * 2 + implicitWidth: parent.width + RowLayout { + id: headerRow + anchors { left: parent.left; right: parent.right; top: parent.top; margins: app.margins } + spacing: app.margins + ColorIcon { + Layout.preferredHeight: Style.iconSize * 2 + Layout.preferredWidth: height + name: "../images/info.svg" + color: Style.accentColor + } + + Label { + id: titleLabel + Layout.fillWidth: true + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + text: dialog.title + color: Style.accentColor + font.pixelSize: app.largeFont + } + } + } + + GridLayout { + id: contentGrid + anchors.fill: parent + rowSpacing: app.margins + columns: 2 + Label { + text: "Name:" + } + Label { + text: dialog.nymeaHost.name + Layout.fillWidth: true + elide: Text.ElideRight + } + Label { + text: "UUID:" + } + Label { + text: dialog.nymeaHost.uuid + Layout.fillWidth: true + elide: Text.ElideRight + } + Label { + text: "Version:" + } + Label { + text: dialog.nymeaHost.version + Layout.fillWidth: true + elide: Text.ElideRight + } + ThinDivider { Layout.columnSpan: 2 } + Label { + Layout.columnSpan: 2 + text: qsTr("Available connections") + } + + Flickable { + Layout.columnSpan: 2 + Layout.fillWidth: true + Layout.preferredHeight: 200 + contentHeight: contentColumn.implicitHeight + clip: true + ColumnLayout { + id: contentColumn + width: parent.width + Repeater { + model: dialog.nymeaHost.connections + delegate: NymeaSwipeDelegate { + Layout.fillWidth: true + wrapTexts: false + progressive: false + text: model.name + subText: model.url + prominentSubText: false + iconName: { + switch (model.bearerType) { + case Connection.BearerTypeLan: + case Connection.BearerTypeWan: + if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) { + return "../images/connections/network-wired.svg" + } + return "../images/connections/network-wifi.svg"; + case Connection.BearerTypeBluetooth: + return "../images/connections/bluetooth.svg"; + case Connection.BearerTypeCloud: + return "../images/connections/cloud.svg" + case Connection.BearerTypeLoopback: + return "../images/connections/network-wired.svg" + } + return "" + } + + tertiaryIconName: model.secure ? "../images/connections/network-secure.svg" : "" + secondaryIconName: !model.online ? "../images/connections/cloud-error.svg" : "" + secondaryIconColor: "red" + + onClicked: { + dialog.close() + engine.jsonRpcClient.connectToHost(dialog.nymeaHost, dialog.nymeaHost.connections.get(index)) + } + } + } + } + } + } + } + } + +} diff --git a/nymea-app/ui/devicepages/NotificationsDevicePage.qml b/nymea-app/ui/devicepages/NotificationsDevicePage.qml index 7d28264b..86337d43 100644 --- a/nymea-app/ui/devicepages/NotificationsDevicePage.qml +++ b/nymea-app/ui/devicepages/NotificationsDevicePage.qml @@ -159,6 +159,13 @@ ThingPageBase { readonly property string title: model.value.trim().replace(/, ?.*/, "") readonly property string text: model.value.trim().replace(/.*, ?/, "") + onPressAndHold: { + var contextMenuComponent = Qt.createComponent("../components/ThingContextMenu.qml"); + var contextMenu = contextMenuComponent.createObject(root, { thing: root.thing }) + contextMenu.x = Qt.binding(function() { return (root.width - contextMenu.width) / 2 }) + contextMenu.open() + } + contentItem: RowLayout { ColumnLayout { Label { diff --git a/nymea-app/ui/devicepages/SmartMeterDevicePage.qml b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml index fa029f99..5eee9825 100644 --- a/nymea-app/ui/devicepages/SmartMeterDevicePage.qml +++ b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml @@ -62,6 +62,13 @@ ThingPageBase { text: qsTr("Total energy consumption") } + onPressAndHold: { + var contextMenuComponent = Qt.createComponent("../components/ThingContextMenu.qml"); + var contextMenu = contextMenuComponent.createObject(root, { thing: root.thing }) + contextMenu.x = Qt.binding(function() { return (root.width - contextMenu.width) / 2 }) + contextMenu.open() + } + contentItem: RowLayout { ColorIcon { Layout.preferredHeight: Style.iconSize diff --git a/nymea-app/ui/images/setupwizard/wired-connection.svg b/nymea-app/ui/images/setupwizard/wired-connection.svg new file mode 100644 index 00000000..8161f0af --- /dev/null +++ b/nymea-app/ui/images/setupwizard/wired-connection.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + + + + diff --git a/nymea-app/ui/images/setupwizard/wireless-connection.svg b/nymea-app/ui/images/setupwizard/wireless-connection.svg new file mode 100644 index 00000000..af134df3 --- /dev/null +++ b/nymea-app/ui/images/setupwizard/wireless-connection.svg @@ -0,0 +1,142 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + +