import QtQuick 2.8 import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.1 import QtQuick.Layouts 1.2 import Qt.labs.settings 1.0 import QtQuick.Window 2.3 import Nymea 1.0 ApplicationWindow { id: app visible: true width: 360 height: 580 visibility: ApplicationWindow.AutomaticVisibility font: Qt.application.font // Those variables must be present in the Style title: appName Material.primary: primaryColor Material.accent: accentColor Material.foreground: foregroundColor property int margins: 14 property int bigMargins: 20 property int extraSmallFont: 10 property int smallFont: 13 property int mediumFont: 16 property int largeFont: 20 property int iconSize: 30 property int delegateHeight: 60 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 property string graphStyle: "bars" property string style: "light" property int currentMainViewIndex: 0 property bool showHiddenOptions: false property int cloudEnvironment: 0 } Binding { target: Engine.awsClient property: "config" value: settings.cloudEnvironment } Component.onCompleted: { pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) setupPushNotifications(); } Connections { target: PlatformHelper onHasPermissionsChanged: { setupPushNotifications(false) } } Connections { target: PushNotifications onTokenChanged: { setupPushNotifications(); } } Connections { target: Engine.awsClient onIsLoggedInChanged: { setupPushNotifications() } } Connections { target: Engine.jsonRpcClient onConnectedChanged: { print("json client connected changed", Engine.jsonRpcClient.connected) if (Engine.jsonRpcClient.connected) { settings.lastConnectedHost = Engine.connection.url } init(); } onAuthenticationRequiredChanged: { print("auth required changed") init(); } onInitialSetupRequiredChanged: { print("setup required changed") init(); } onInvalidProtocolVersion: { var popup = invalidVersionComponent.createObject(app.contentItem); popup.actualVersion = actualVersion; popup.minimumVersion = minimumVersion popup.open() settings.lastConnectedHost = "" } } function init() { print("calling init. Auth required:", Engine.jsonRpcClient.authenticationRequired, "initial setup required:", Engine.jsonRpcClient.initialSetupRequired, "jsonrpc connected:", Engine.jsonRpcClient.connected) pageStack.clear() if (!Engine.connection.connected) { pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) return; } if (Engine.jsonRpcClient.authenticationRequired || Engine.jsonRpcClient.initialSetupRequired) { if (Engine.jsonRpcClient.pushButtonAuthAvailable) { print("opening push button auth") var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml")) page.backPressed.connect(function() { settings.lastConnectedHost = ""; Engine.connection.disconnect(); init(); }) } else { var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml")); page.backPressed.connect(function() { settings.lastConnectedHost = ""; Engine.connection.disconnect() init(); }) } } else if (Engine.jsonRpcClient.connected) { pageStack.push(Qt.resolvedUrl("MainPage.qml")) } else { pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml")) } } function setupPushNotifications(askForPermissions) { if (askForPermissions === undefined) { askForPermissions = true; } if (!Engine.awsClient.isLoggedIn) { print("AWS not logged in. Cannot register for push"); return; } if (PushNotifications.token.length === 0) { print("Don't have a token yet. Cannot register for push"); return; } if (!PlatformHelper.hasPermissions) { if (askForPermissions) { PlatformHelper.requestPermissions(); } } else { Engine.awsClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp"); } } // Workaround flickering on pageStack animations when the white background shines through Rectangle { anchors.fill: parent color: Material.background } StackView { id: pageStack objectName: "pageStack" anchors.fill: parent initialItem: Page {} // onDepthChanged: { // print("stackview depth changed", pageStack.depth) // } } onClosing: { if (Qt.platform.os == "android") { // If we're connected, allow going back up to MainPage if ((Engine.jsonRpcClient.connected && pageStack.depth > 1) // if we're not connected, only allow using the back button in wizards || (!Engine.jsonRpcClient.connected && pageStack.depth > 3)) { close.accepted = false; pageStack.pop(); } } } Connections { target: Qt.application enabled: Engine.jsonRpcClient.connected && settings.returnToHome onStateChanged: { print("App active state changed:", state) if (state !== Qt.ApplicationActive) { init(); } } } property var supportedInterfaces: ["light", "weather", "sensor", "media", "garagegate", "extendedawning", "extendedshutter", "extendedblind", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"] function interfaceToString(name) { switch(name) { case "light": return qsTr("Lighting") case "weather": return qsTr("Weather") case "sensor": return qsTr("Sensors") case "media": return qsTr("Media") case "button": return qsTr("Switches") case "gateway": return qsTr("Gateways") case "notifications": return qsTr("Notifications") case "temperaturesensor": return qsTr("Temperature"); case "humiditysensor": return qsTr("Humidity"); case "pressuresensor": return qsTr("Pressure"); case "inputtrigger": return qsTr("Incoming Events"); case "outputtrigger": return qsTr("Events"); case "shutter": case "extendedshutter": return qsTr("Shutters"); case "blind": case "extendedblind": return qsTr("Blinds"); case "awning": case "extendedawning": return qsTr("Awnings"); case "garagegate": return qsTr("Garage gates"); case "uncategorized": return qsTr("Uncategorized") } } function interfacesToIcon(interfaces) { for (var i = 0; i < interfaces.length; i++) { var icon = interfaceToIcon(interfaces[i]); if (icon !== "") { return icon; } } return Qt.resolvedUrl("images/select-none.svg") } function interfaceToIcon(name) { switch (name) { case "light": case "colorlight": case "dimmablelight": return Qt.resolvedUrl("images/light-on.svg") case "sensor": return Qt.resolvedUrl("images/sensors.svg") case "temperaturesensor": return Qt.resolvedUrl("images/sensors/temperature.svg") case "humiditysensor": return Qt.resolvedUrl("images/sensors/humidity.svg") case "moisturesensor": return Qt.resolvedUrl("images/sensors/moisture.svg") case "lightsensor": return Qt.resolvedUrl("images/sensors/light.svg") case "conductivitysensor": return Qt.resolvedUrl("images/sensors/conductivity.svg") case "pressuresensor": return Qt.resolvedUrl("images/sensors/pressure.svg") case "media": case "mediacontroller": return Qt.resolvedUrl("images/mediaplayer-app-symbolic.svg") case "button": case "longpressbutton": case "simplemultibutton": case "longpressmultibutton": return Qt.resolvedUrl("images/system-shutdown.svg") case "weather": return Qt.resolvedUrl("images/weather-app-symbolic.svg") case "temperaturesensor": return Qt.resolvedUrl("images/sensors/temperature.svg") case "humiditysensor": return Qt.resolvedUrl("images/sensors/humidity.svg") case "gateway": return Qt.resolvedUrl("images/network-wired-symbolic.svg") case "notifications": return Qt.resolvedUrl("images/notification.svg") case "connectable": return Qt.resolvedUrl("images/stock_link.svg") case "inputtrigger": return Qt.resolvedUrl("images/attention.svg") case "outputtrigger": return Qt.resolvedUrl("images/send.svg") case "shutter": case "extendedshutter": return Qt.resolvedUrl("images/DeviceIconRollerShutter.svg") case "blind": case "extendedblind": return Qt.resolvedUrl("images/DeviceIconBlind.svg") case "garagegate": return Qt.resolvedUrl("images/shutter/shutter-100.svg") case "extendedawning": return Qt.resolvedUrl("images/awning/awning-100.svg") case "battery": return Qt.resolvedUrl("images/battery/battery-050.svg") case "uncategorized": return Qt.resolvedUrl("images/select-none.svg") case "simpleclosable": return Qt.resolvedUrl("images/sort-listitem.svg") default: console.warn("InterfaceToIcon: Unhandled interface", name) } return ""; } property var fallbackColors: { "temperaturesensor": "red", "humiditysensor": "deepskyblue", "moisturesensor":"blue", "lightsensor": "orange", "conductivitysensor": "green", "pressuresensor": "grey" } function interfaceToColor(name) { // Try to load color map from style if (interfaceColors[name]) { return interfaceColors[name]; } if (fallbackColors[name]) { return fallbackColors[name]; } return "grey"; } function interfaceToDisplayName(name) { switch (name) { case "light": return qsTr("light") case "button": return "button"; case "sensor": return qsTr("sensor") case "battery": return qsTr("battery powered thing") case "connectable": return qsTr("connectable thing") default: console.warn("Unhandled interfaceToDisplayName:", name) } } function interfaceListToDevicePage(interfaceList) { var page; if (interfaceList.indexOf("media") >= 0) { page = "MediaDevicePage.qml"; } else if (interfaceList.indexOf("button") >= 0) { page = "ButtonDevicePage.qml"; } else if (interfaceList.indexOf("weather") >= 0) { page = "WeatherDevicePage.qml"; } else if (interfaceList.indexOf("sensor") >= 0) { page = "SensorDevicePage.qml"; } else if (interfaceList.indexOf("inputtrigger") >= 0) { page = "InputTriggerDevicePage.qml"; } else if (interfaceList.indexOf("garagegate") >= 0 ) { page = "GarageGateDevicePage.qml"; } else if (interfaceList.indexOf("light") >= 0) { page = "ColorLightDevicePage.qml"; } else if (interfaceList.indexOf("extendedshutter") >= 0 ) { page = "ShutterDevicePage.qml"; } else if (interfaceList.indexOf("extendedawning") >= 0) { page = "AwningDevicePage.qml"; } else if (interfaceList.indexOf("notifications") >= 0) { page = "NotificationsDevicePage.qml"; } else { page = "GenericDevicePage.qml"; } print("Selecting page", page, "for interface list:", interfaceList) return page; } function pad(num, size) { var s = "000000000" + num; return s.substr(s.length-size); } Component { id: invalidVersionComponent Popup { id: popup property string actualVersion: "0.0" property string minimumVersion: "1.0" width: app.width * .8 height: col.childrenRect.height + app.margins * 2 x: (app.width - width) / 2 y: (app.height - height) / 2 visible: false ColumnLayout { id: col anchors { left: parent.left; right: parent.right } spacing: app.margins Label { text: qsTr("Connection error") Layout.fillWidth: true font.pixelSize: app.largeFont } Label { text: qsTr("Sorry, the version of the %1 box you are trying to connect to is too old. This app requires at least version %2 but the %1 box only supports %3").arg(app.systemName).arg(popup.minimumVersion).arg(popup.actualVersion) wrapMode: Text.WordWrap Layout.fillWidth: true } Button { Layout.fillWidth: true text: qsTr("OK") onClicked: { Engine.connection.disconnect(); popup.close() } } } } } KeyboardLoader { id: keyboardRect anchors { left: parent.left; bottom: parent.bottom; right: parent.right } } }