From aaa50ccdffd3651f7dabbebdb15bc9630c10e638 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 5 Dec 2020 02:03:45 +0100 Subject: [PATCH] More improvements in the thing lists --- nymea-app/resources.qrc | 1 + nymea-app/ui/components/AutoSizeMenu.qml | 1 + nymea-app/ui/components/BigTile.qml | 13 +- nymea-app/ui/components/ThingContextMenu.qml | 164 +++++++++++++++++ nymea-app/ui/components/ThingStatusIcons.qml | 89 ++++++++- .../ClosablesDeviceListPage.qml | 12 +- .../devicelistpages/GarageThingListPage.qml | 13 +- .../devicelistpages/GenericDeviceListPage.qml | 13 +- .../devicelistpages/LightsDeviceListPage.qml | 12 +- .../devicelistpages/MediaDeviceListPage.qml | 11 +- .../PowerSocketsDeviceListPage.qml | 11 +- .../devicelistpages/SensorsDeviceListPage.qml | 4 +- nymea-app/ui/devicepages/DeviceLogPage.qml | 30 +-- nymea-app/ui/devicepages/DevicePageBase.qml | 172 ++---------------- .../ui/devicepages/GenericDevicePage.qml | 2 +- .../ui/devicepages/ShutterDevicePage.qml | 10 +- nymea-app/ui/magic/DeviceRulesPage.qml | 3 +- 17 files changed, 352 insertions(+), 209 deletions(-) create mode 100644 nymea-app/ui/components/ThingContextMenu.qml diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 2c6da110..ca760f85 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -235,5 +235,6 @@ ui/components/ColorPicker.qml ui/utils/ActionQueue.qml ui/components/ColorTemperaturePicker.qml + ui/components/ThingContextMenu.qml diff --git a/nymea-app/ui/components/AutoSizeMenu.qml b/nymea-app/ui/components/AutoSizeMenu.qml index 0ae7814a..415ddd3f 100644 --- a/nymea-app/ui/components/AutoSizeMenu.qml +++ b/nymea-app/ui/components/AutoSizeMenu.qml @@ -32,6 +32,7 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 Menu { + modal: true function calculateWidth() { var result = 0; var i = 0; diff --git a/nymea-app/ui/components/BigTile.qml b/nymea-app/ui/components/BigTile.qml index 4dd81bac..6266cc8a 100644 --- a/nymea-app/ui/components/BigTile.qml +++ b/nymea-app/ui/components/BigTile.qml @@ -3,7 +3,7 @@ import QtQuick.Layouts 1.2 import QtQuick.Controls 2.1 import Nymea 1.0 -Item { + Item { id: root implicitHeight: layout.implicitHeight + app.margins @@ -18,6 +18,10 @@ Item { property alias topPadding: content.topPadding property alias bottomPadding: content.bottomPadding + readonly property State connectedState: thing.stateByName("connected") + readonly property bool isConnected: connectedState === null || connectedState.value === true + readonly property bool isEnabled: thing.setupStatus == Thing.ThingSetupStatusComplete && isConnected + signal clicked(); signal pressAndHold(); @@ -25,6 +29,13 @@ 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/ThingContextMenu.qml b/nymea-app/ui/components/ThingContextMenu.qml new file mode 100644 index 00000000..3be7b7c5 --- /dev/null +++ b/nymea-app/ui/components/ThingContextMenu.qml @@ -0,0 +1,164 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 + +AutoSizeMenu { + id: root + + property Thing thing: null + + property bool showDetails: true + property bool showLogs: true + + Component.onCompleted: { + root.addItem(menuEntryComponent.createObject(root, {text: qsTr("Magic"), iconSource: "../images/magic.svg", functionName: "openThingMagicPage"})) + + if (root.showDetails) { + root.addItem(menuEntryComponent.createObject(root, {text: qsTr("Details"), iconSource: "../images/info.svg", functionName: "openGenericThingPage"})) + } +// root.addItem(menuEntryComponent.createObject(root, {text: qsTr("Settings"), iconSource: "../images/configure.svg", functionName: "openThingSettingsPage"})) + if (root.showLogs) { + root.addItem(menuEntryComponent.createObject(root, {text: qsTr("Logs"), iconSource: "../images/logs.svg", functionName: "openThingLogPage"})) + } + + if (engine.jsonRpcClient.ensureServerVersion("1.6")) { + root.addItem(menuEntryComponent.createObject(root, + { + text: Qt.binding(function() { return favoritesProxy.count === 0 ? qsTr("Mark as favorite") : qsTr("Remove from favorites")}), + iconSource: Qt.binding(function() { return favoritesProxy.count === 0 ? "../images/starred.svg" : "../images/non-starred.svg"}), + functionName: "toggleFavorite" + })) + + root.addItem(menuEntryComponent.createObject(root, + { + text: qsTr("Grouping"), + iconSource: "../images/view-grid-symbolic.svg", + functionName: "addToGroup" + })) + } + + print("*** creating menu") + print("NFC", NfcHelper.isAvailable) + if (NfcHelper.isAvailable) { + root.addItem(menuEntryComponent.createObject(root, + { + text: qsTr("Write NFC tag"), + iconSource: "../images/nfc.svg", + functionName: "writeNfcTag" + + })); + } + } + + function openThingMagicPage() { + pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {thing: root.thing}) + } + function openGenericThingPage() { + pageStack.push(Qt.resolvedUrl("../devicepages/GenericDevicePage.qml"), {thing: root.thing}) + } + function toggleFavorite() { + if (favoritesProxy.count === 0) { + engine.tagsManager.tagDevice(root.thing.id, "favorites", 100000) + } else { + engine.tagsManager.untagDevice(root.thing.id, "favorites") + } + } + function addToGroup() { + var dialog = addToGroupDialog.createObject(root.parent) + dialog.open(); + } + + function openThingSettingsPage() { + pageStack.push(Qt.resolvedUrl("../thingconfiguration/ConfigureThingPage.qml"), {thing: root.thing}) + } + + function openThingLogPage() { + pageStack.push(Qt.resolvedUrl("../devicepages/DeviceLogPage.qml"), {thing: root.thing }); + } + + function writeNfcTag() { + pageStack.push(Qt.resolvedUrl("../magic/WriteNfcTagPage.qml"), {thing: root.thing}) + } + + TagsProxyModel { + id: favoritesProxy + tags: engine.tagsManager.tags + filterDeviceId: root.thing.id + filterTagId: "favorites" + } + + Component { + id: menuEntryComponent + IconMenuItem { + width: parent.width + property string functionName: "" + onTriggered: root[functionName]() + } + } + + Component { + id: addToGroupDialog + MeaDialog { + title: qsTr("Groups for %1").arg(root.thing.name) + headerIcon: "../images/view-grid-symbolic.svg" + // NOTE: If CloseOnPressOutside is active (default) it will break the QtVirtualKeyboard + // https://bugreports.qt.io/browse/QTBUG-56918 + closePolicy: Popup.CloseOnEscape + + RowLayout { + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + spacing: app.margins + TextField { + id: newGroupdTextField + Layout.fillWidth: true + placeholderText: qsTr("New group") + } + Button { + text: qsTr("OK") + enabled: newGroupdTextField.displayText.length > 0 && !groupTags.containsId("group-" + newGroupdTextField.displayText) + onClicked: { + engine.tagsManager.tagDevice(root.thing.id, "group-" + newGroupdTextField.text, 1000) + newGroupdTextField.text = "" + } + } + } + + + ListView { + Layout.fillWidth: true + height: 200 + clip: true + ScrollIndicator.vertical: ScrollIndicator {} + + model: TagListModel { + id: groupTags + tagsProxy: TagsProxyModel { + tags: engine.tagsManager.tags + filterTagId: "group-.*" + } + } + + delegate: CheckDelegate { + width: parent.width + text: model.tagId.substring(6) + checked: innerProxy.count > 0 + onClicked: { + if (innerProxy.count == 0) { + engine.tagsManager.tagDevice(root.thing.id, model.tagId, 1000) + } else { + engine.tagsManager.untagDevice(root.thing.id, model.tagId, model.value) + } + } + ThingsProxy { + id: innerProxy + engine: _engine + filterTagId: model.tagId + filterDeviceId: root.thing.id + } + } + } + } + } +} diff --git a/nymea-app/ui/components/ThingStatusIcons.qml b/nymea-app/ui/components/ThingStatusIcons.qml index 5bd1d225..f2bfaadb 100644 --- a/nymea-app/ui/components/ThingStatusIcons.qml +++ b/nymea-app/ui/components/ThingStatusIcons.qml @@ -1,23 +1,60 @@ import QtQuick 2.9 +import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 import Nymea 1.0 RowLayout { id: root Layout.fillWidth: false + spacing: app.margins / 2 property Thing thing: null property color color: keyColor readonly property color keyColor: updateStatusIcon.keyColor + signal updateIconClicked(); + signal batteryIconClicked(); + signal connectionIconClicked(); + signal setupIconClicked(); + UpdateStatusIcon { id: updateStatusIcon Layout.preferredHeight: app.smallIconSize Layout.preferredWidth: height thing: root.thing - visible: updateAvailable || updateRunning + visible: setupStatusIcon.setupStatus == Thing.ThingSetupStatusComplete && connectionStatusIcon.isConnected && (updateAvailable || updateRunning) Binding { target: updateStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor } + MouseArea { + anchors.fill: parent + anchors.margins: -app.margins / 4 + onClicked: { + var dialogComponent = Qt.createComponent("MeaDialog.qml") + var currentVersionState = root.thing.stateByName("currentVersion") + var availableVersionState = root.thing.stateByName("availableVersion") + var text = qsTr("An update for %1 is available. Do you want to start the update now?").arg(root.thing.name) + if (currentVersionState) { + text += "\n\n" + qsTr("Current version: %1").arg(currentVersionState.value) + } + if (availableVersionState) { + text += "\n\n" + qsTr("Available version: %1").arg(availableVersionState.value) + } + + var dialog = dialogComponent.createObject(app, + { + headerIcon: "../images/system-update.svg", + title: qsTr("Update"), + text: text, + standardButtons: Dialog.Ok | Dialog.Cancel + }) + dialog.accepted.connect(function() { + print("starting update") + engine.thingManager.executeAction(root.thing.id, root.thing.thingClass.actionTypes.findByName("performUpdate").id) + }) + dialog.open(); + root.updateIconClicked() + } + } } BatteryStatusIcon { id: batteryStatusIcon @@ -26,6 +63,27 @@ RowLayout { thing: root.thing visible: root.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical) Binding { target: batteryStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor } + MouseArea { + anchors.fill: parent + anchors.margins: -app.margins / 4 + onClicked: { + root.batteryIconClicked() + var levelStateType = root.thing.thingClass.stateTypes.findByName("batteryLevel"); + var criticalStateType = root.thing.thingClass.stateTypes.findByName("batteryCritical"); + var stateTypes = [] + if (levelStateType) { + stateTypes.push(levelStateType.id) + } + if (criticalStateType) { + stateTypes.push(criticalStateType.id) + } + pageStack.push("../devicepages/DeviceLogPage.qml", + { + thing: root.thing, + filterTypeIds: stateTypes + }); + } + } } ConnectionStatusIcon { id: connectionStatusIcon @@ -34,6 +92,27 @@ RowLayout { thing: root.thing visible: root.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected) Binding { target: connectionStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor } + MouseArea { + anchors.fill: parent + anchors.margins: -app.margins / 4 + onClicked: { + root.connectionIconClicked() + var signalStateType = root.thing.thingClass.stateTypes.findByName("signalStrength") + var connectedStateType = root.thing.thingClass.stateTypes.findByName("connected") + var stateTypes = [] + if (signalStateType) { + stateTypes.push(signalStateType.id) + } + if (connectedStateType) { + stateTypes.push(connectedStateType.id) + } + pageStack.push("../devicepages/DeviceLogPage.qml", + { + thing: root.thing, + filterTypeIds: stateTypes + }); + } + } } SetupStatusIcon { id: setupStatusIcon @@ -42,5 +121,13 @@ RowLayout { thing: root.thing visible: setupFailed || setupInProgress Binding { target: setupStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor } + MouseArea { + anchors.fill: parent + anchors.margins: -app.margins / 4 + onClicked: { + root.setupIconClicked() + pageStack.push("../thingconfiguration/ConfigureThingPage.qml", { thing: root.thing }); + } + } } } diff --git a/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml index 3ca3b42e..9f773d8a 100644 --- a/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml @@ -103,13 +103,17 @@ DeviceListPageBase { Layout.preferredWidth: contentGrid.width / contentGrid.columns thing: root.thingsProxy.getThing(model.id) showHeader: false - enabled: connectedState == null || connectedState.value === true topPadding: 0 bottomPadding: 0 - onClicked: root.enterPage(index) + onClicked: { + if (isEnabled) { + root.enterPage(index) + } else { + itemDelegate.wobble() + } + } - property State connectedState: thing.stateByName("connected") property State movingState: thing.stateByName("moving") property State percentageState: thing.stateByName("percentage") @@ -131,6 +135,7 @@ DeviceListPageBase { Layout.fillWidth: true text: itemDelegate.thing.name elide: Text.ElideRight + enabled: itemDelegate.isEnabled } ThingStatusIcons { @@ -143,6 +148,7 @@ DeviceListPageBase { height: parent.height device: itemDelegate.thing invert: root.invertControls + enabled: itemDelegate.isEnabled } } } diff --git a/nymea-app/ui/devicelistpages/GarageThingListPage.qml b/nymea-app/ui/devicelistpages/GarageThingListPage.qml index 2d9b730c..15e22786 100644 --- a/nymea-app/ui/devicelistpages/GarageThingListPage.qml +++ b/nymea-app/ui/devicelistpages/GarageThingListPage.qml @@ -67,13 +67,17 @@ DeviceListPageBase { Layout.preferredWidth: contentGrid.width / contentGrid.columns thing: root.thingsProxy.getThing(model.id) showHeader: false - enabled: connectedState == null || connectedState.value === true topPadding: 0 bottomPadding: 0 - onClicked: root.enterPage(index) + onClicked: { + if (isEnabled) { + root.enterPage(index) + } else { + itemDelegate.wobble() + } + } - property State connectedState: thing.stateByName("connected") property State movingState: thing.stateByName("moving") property State percentageState: thing.stateByName("percentage") @@ -97,6 +101,7 @@ DeviceListPageBase { Layout.fillWidth: true text: itemDelegate.thing.name elide: Text.ElideRight + enabled: itemDelegate.isEnabled } ThingStatusIcons { @@ -110,6 +115,7 @@ DeviceListPageBase { device: itemDelegate.thing invert: root.invertControls visible: !itemDelegate.isImpulseBased + enabled: itemDelegate.isEnabled } ProgressButton { Layout.preferredHeight: app.iconSize @@ -117,6 +123,7 @@ DeviceListPageBase { longpressEnabled: false imageSource: "../images/closable-move.svg" visible: itemDelegate.isImpulseBased + enabled: itemDelegate.isEnabled onClicked: { var actionTypeId = itemDelegate.thing.thingClass.actionTypes.findByName("triggerImpulse").id print("Triggering impulse", actionTypeId) diff --git a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml index 88575567..af130a1a 100644 --- a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml @@ -73,11 +73,16 @@ DeviceListPageBase { Layout.preferredWidth: contentGrid.width / contentGrid.columns thing: root.thingsProxy.getThing(model.id) showHeader: false - enabled: connectedState == null || connectedState.value === true topPadding: 0 bottomPadding: 0 - onClicked: root.enterPage(index) + onClicked: { + if (isEnabled) { + root.enterPage(index) + } else { + itemDelegate.wobble() + } + } property State connectedState: thing.stateByName("connected") property State powerState: thing.stateByName("power") @@ -96,6 +101,7 @@ DeviceListPageBase { Layout.fillWidth: true text: itemDelegate.thing.name elide: Text.ElideRight + enabled: itemDelegate.isEnabled } ThingStatusIcons { @@ -104,7 +110,8 @@ DeviceListPageBase { Switch { visible: itemDelegate.powerState !== null - checked: itemDelegate.powerState.value === true + checked: itemDelegate.powerState && itemDelegate.powerState.value === true + enabled: itemDelegate.isEnabled onClicked: { var params = []; var param1 = {}; diff --git a/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml b/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml index 75130df1..491b43c7 100644 --- a/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml @@ -97,18 +97,16 @@ DeviceListPageBase { leftPadding: 0 rightPadding: 0 - property State connectedState: thing.stateByName("connected") property State powerState: thing.stateByName("power") property State brightnessState: thing.stateByName("brightness") property State colorState: thing.stateByName("color") + property State colorTemperatureState: thing.stateByName("colorTemperature") - property bool tileColored: isConnected && colorState && powerState.value === true + property bool tileColored: isEnabled && colorState && powerState.value === true property bool colorInverted: tileColored && NymeaUtils.isDark(app.foregroundColor) === NymeaUtils.isDark(colorState.value) - property bool isConnected: connectedState === null || connectedState.value === true - onClicked: { - if (isConnected) { + if (isEnabled && (colorState || colorTemperatureState)) { root.enterPage(index) } else { itemDelegate.wobble() @@ -126,7 +124,6 @@ DeviceListPageBase { implicitHeight: contentColumn.implicitHeight Behavior on implicitHeight { NumberAnimation { duration: 100 } } radius: 6 - enabled: itemDelegate.isConnected ColumnLayout { id: contentColumn @@ -156,6 +153,7 @@ DeviceListPageBase { Layout.fillWidth: true text: itemDelegate.thing.name elide: Text.ElideRight + enabled: itemDelegate.isEnabled Binding { target: nameLabel @@ -172,6 +170,7 @@ DeviceListPageBase { Switch { id: lightSwitch checked: itemDelegate.powerState.value === true + enabled: itemDelegate.isEnabled onClicked: { var params = []; var param1 = {}; @@ -256,6 +255,7 @@ DeviceListPageBase { visible: itemDelegate.powerState.value === true && itemDelegate.brightnessState != null radius: 6 color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .1)) + enabled: itemDelegate.isEnabled Rectangle { height: knob.x + knob.width / 2 diff --git a/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml b/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml index ac08337d..9c44229f 100644 --- a/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml @@ -68,9 +68,6 @@ DeviceListPageBase { bottomPadding: 0 leftPadding: 0 rightPadding: 0 - enabled: connectedState == null || connectedState.value === true - - property State connectedState: thing.stateByName("connected") readonly property StateType playbackStateType: thing.thingClass.stateTypes.findByName("playbackStatus") readonly property State playbackState: thing.stateByName("playbackStatus") @@ -79,10 +76,15 @@ DeviceListPageBase { readonly property State playerTypeState: thing.stateByName("playerType") onClicked: { - enterPage(index) + if (isEnabled) { + enterPage(index) + } else { + itemDelegate.wobble(); + } } contentItem: RowLayout { + enabled: itemDelegate.isEnabled ColumnLayout { id: leftColummn Layout.margins: app.margins @@ -183,7 +185,6 @@ DeviceListPageBase { } } } - } } } diff --git a/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml index d96671f3..ee9b6117 100644 --- a/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml @@ -67,11 +67,16 @@ DeviceListPageBase { Layout.preferredWidth: contentGrid.width / contentGrid.columns thing: root.thingsProxy.getThing(model.id) showHeader: false - enabled: connectedState == null || connectedState.value === true topPadding: 0 bottomPadding: 0 - onClicked: root.enterPage(index) + onClicked: { + if (isEnabled) { + root.enterPage(index) + } else { + itemDelegate.wobble(); + } + } property State connectedState: thing.stateByName("connected") property State powerState: thing.stateByName("power") @@ -90,6 +95,7 @@ DeviceListPageBase { Layout.fillWidth: true text: itemDelegate.thing.name elide: Text.ElideRight + enabled: itemDelegate.isEnabled } ThingStatusIcons { @@ -98,6 +104,7 @@ DeviceListPageBase { Switch { checked: itemDelegate.powerState.value === true + enabled: itemDelegate.isEnabled onClicked: { var params = []; var param1 = {}; diff --git a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml index ebe38b54..bdd50a49 100644 --- a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml @@ -64,9 +64,7 @@ DeviceListPageBase { Layout.preferredWidth: contentGrid.width / contentGrid.columns thing: root.thingsProxy.getThing(model.id) - onClicked: { - enterPage(index) - } + onClicked: enterPage(index) contentItem: GridLayout { id: dataGrid diff --git a/nymea-app/ui/devicepages/DeviceLogPage.qml b/nymea-app/ui/devicepages/DeviceLogPage.qml index 54346688..cbd952a8 100644 --- a/nymea-app/ui/devicepages/DeviceLogPage.qml +++ b/nymea-app/ui/devicepages/DeviceLogPage.qml @@ -39,11 +39,12 @@ import "../customviews" Page { id: root - property Device device: null + property Thing thing: null + property alias device: root.thing property var filterTypeIds: [] header: NymeaHeader { - text: qsTr("History for %1").arg(root.device.name) + text: qsTr("History for %1").arg(root.thing.name) onBackPressed: pageStack.pop() HeaderButton { @@ -57,7 +58,7 @@ Page { LogsModelNg { id: logsModelNg engine: _engine - deviceId: root.device.id + deviceId: root.thing.id typeIds: root.filterTypeIds.length > 0 ? root.filterTypeIds : filterEnabled @@ -70,7 +71,7 @@ Page { DeviceModel { id: filterDeviceModel - device: root.device + device: root.thing } Pane { @@ -111,7 +112,7 @@ Page { right: parent.right } - readonly property StateType stateType: root.device.deviceClass.stateTypes.getStateType(root.filterTypeIds[0]) + readonly property StateType stateType: root.thing.thingClass.stateTypes.getStateType(root.filterTypeIds[0]) readonly property bool canShowGraph: { if (stateType === null) { @@ -122,14 +123,15 @@ Page { return false; } - switch (stateType.type) { - case "Int": - case "Double": + switch (stateType.type.toLowerCase()) { + case "uint": + case "int": + case "double": return true; - case "Bool": + case "bool": return engine.jsonRpcClient.ensureServerVersion("1.10") } - print("not showing graph for", root.stateType.type) + print("not showing graph for", stateType.type) return false; } @@ -142,7 +144,7 @@ Page { } var source = Qt.resolvedUrl("../customviews/GenericTypeGraph.qml"); - setSource(source, {device: root.device, stateType: stateType}) + setSource(source, {device: root.thing, stateType: stateType}) } } @@ -162,9 +164,9 @@ Page { id: entryDelegate width: parent.width - property StateType stateType: model.source === LogEntry.LoggingSourceStates ? root.device.deviceClass.stateTypes.getStateType(model.typeId) : null - property EventType eventType: model.source === LogEntry.LoggingSourceEvents || model.source === LogEntry.LoggingSourceStates ? root.device.deviceClass.eventTypes.getEventType(model.typeId) : null - property ActionType actionType: model.source === LogEntry.LoggingSourceActions ? root.device.deviceClass.actionTypes.getActionType(model.typeId) : null + property StateType stateType: model.source === LogEntry.LoggingSourceStates ? root.thing.thingClass.stateTypes.getStateType(model.typeId) : null + property EventType eventType: model.source === LogEntry.LoggingSourceEvents || model.source === LogEntry.LoggingSourceStates ? root.thing.thingClass.eventTypes.getEventType(model.typeId) : null + property ActionType actionType: model.source === LogEntry.LoggingSourceActions ? root.thing.thingClass.actionTypes.getActionType(model.typeId) : null contentItem: RowLayout { ColorIcon { diff --git a/nymea-app/ui/devicepages/DevicePageBase.qml b/nymea-app/ui/devicepages/DevicePageBase.qml index b256a7fd..116408ad 100644 --- a/nymea-app/ui/devicepages/DevicePageBase.qml +++ b/nymea-app/ui/devicepages/DevicePageBase.qml @@ -74,171 +74,21 @@ Page { } } - TagsProxyModel { - id: favoritesProxy - tags: engine.tagsManager.tags - filterDeviceId: root.device.id - filterTagId: "favorites" - } - - AutoSizeMenu { + ThingContextMenu { id: thingMenu x: parent.width - width + thing: root.thing + showDetails: root.showDetailsButton + showLogs: root.showLogsButton + } - Component.onCompleted: { - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Magic"), iconSource: "../images/magic.svg", functionName: "openDeviceMagicPage"})) - - if (root.showDetailsButton) { - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Details"), iconSource: "../images/info.svg", functionName: "openGenericDevicePage"})) + Connections { + target: engine.deviceManager.devices + onThingRemoved:{ + if (device == root.device) { + print("Device destroyed") + pageStack.pop() } -// thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Settings"), iconSource: "../images/configure.svg", functionName: "openThingSettingsPage"})) - if (root.showLogsButton) { - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, {text: qsTr("Logs"), iconSource: "../images/logs.svg", functionName: "openDeviceLogPage"})) - } - - if (engine.jsonRpcClient.ensureServerVersion("1.6")) { - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, - { - text: Qt.binding(function() { return favoritesProxy.count === 0 ? qsTr("Mark as favorite") : qsTr("Remove from favorites")}), - iconSource: Qt.binding(function() { return favoritesProxy.count === 0 ? "../images/starred.svg" : "../images/non-starred.svg"}), - functionName: "toggleFavorite" - })) - - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, - { - text: qsTr("Grouping"), - iconSource: "../images/view-grid-symbolic.svg", - functionName: "addToGroup" - })) - } - - print("*** creating menu") - print("NFC", NfcHelper.isAvailable) - if (NfcHelper.isAvailable) { - thingMenu.addItem(menuEntryComponent.createObject(thingMenu, - { - text: qsTr("Write NFC tag"), - iconSource: "../images/nfc.svg", - functionName: "writeNfcTag" - - })); - } - } - - function openDeviceMagicPage() { - pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device}) - } - function openGenericDevicePage() { - pageStack.push(Qt.resolvedUrl("GenericDevicePage.qml"), {device: root.device}) - } - function toggleFavorite() { - if (favoritesProxy.count === 0) { - engine.tagsManager.tagDevice(root.device.id, "favorites", 100000) - } else { - engine.tagsManager.untagDevice(root.device.id, "favorites") - } - } - function addToGroup() { - var dialog = addToGroupDialog.createObject(root) - dialog.open(); - } - - function openThingSettingsPage() { - pageStack.push(Qt.resolvedUrl("../thingconfiguration/ConfigureThingPage.qml"), {device: root.device}) - } - - function openDeviceLogPage() { - pageStack.push(Qt.resolvedUrl("DeviceLogPage.qml"), {device: root.device }); - } - - function writeNfcTag() { - pageStack.push(Qt.resolvedUrl("../magic/WriteNfcTagPage.qml"), {thing: root.thing}) - } - - Component { - id: menuEntryComponent - IconMenuItem { - width: parent.width - property string functionName: "" - onTriggered: thingMenu[functionName]() - } - } - - Connections { - target: engine.deviceManager.devices - onThingRemoved:{ - if (device == root.device) { - print("Device destroyed") - pageStack.pop() - } - } - } - - Component { - id: addToGroupDialog - MeaDialog { - title: qsTr("Groups for %1").arg(root.device.name) - headerIcon: "../images/view-grid-symbolic.svg" - // NOTE: If CloseOnPressOutside is active (default) it will break the QtVirtualKeyboard - // https://bugreports.qt.io/browse/QTBUG-56918 - closePolicy: Popup.CloseOnEscape - - RowLayout { - Layout.leftMargin: app.margins - Layout.rightMargin: app.margins - spacing: app.margins - TextField { - id: newGroupdTextField - Layout.fillWidth: true - placeholderText: qsTr("New group") - } - Button { - text: qsTr("OK") - enabled: newGroupdTextField.displayText.length > 0 && !groupTags.containsId("group-" + newGroupdTextField.displayText) - onClicked: { - engine.tagsManager.tagDevice(root.device.id, "group-" + newGroupdTextField.text, 1000) - newGroupdTextField.text = "" - } - } - } - - - ListView { - Layout.fillWidth: true - height: 200 - clip: true - ScrollIndicator.vertical: ScrollIndicator {} - - model: TagListModel { - id: groupTags - tagsProxy: TagsProxyModel { - tags: engine.tagsManager.tags - filterTagId: "group-.*" - } - } - - delegate: CheckDelegate { - width: parent.width - text: model.tagId.substring(6) - checked: innerProxy.count > 0 - onClicked: { - if (innerProxy.count == 0) { - engine.tagsManager.tagDevice(root.device.id, model.tagId, 1000) - } else { - engine.tagsManager.untagDevice(root.device.id, model.tagId, model.value) - } - } - - DevicesProxy { - id: innerProxy - engine: _engine - filterTagId: model.tagId - filterDeviceId: root.device.id - } - } - } - } - } } diff --git a/nymea-app/ui/devicepages/GenericDevicePage.qml b/nymea-app/ui/devicepages/GenericDevicePage.qml index 90f1a31a..487c0df7 100644 --- a/nymea-app/ui/devicepages/GenericDevicePage.qml +++ b/nymea-app/ui/devicepages/GenericDevicePage.qml @@ -42,7 +42,7 @@ DevicePageBase { function executeAction(actionTypeId, params) { print("executing", actionTypeId) - return engine.deviceManager.executeAction(root.device.id, actionTypeId, params) + return engine.thingManager.executeAction(root.thing.id, actionTypeId, params) } ListView { diff --git a/nymea-app/ui/devicepages/ShutterDevicePage.qml b/nymea-app/ui/devicepages/ShutterDevicePage.qml index e49cbf17..1758e5f2 100644 --- a/nymea-app/ui/devicepages/ShutterDevicePage.qml +++ b/nymea-app/ui/devicepages/ShutterDevicePage.qml @@ -47,9 +47,9 @@ DevicePageBase { readonly property StateType movingStateType: isExtended ? deviceClass.stateTypes.findByName("moving") : null readonly property StateType angleStateType: isVenetian ? deviceClass.stateTypes.findByName("angle") : null - readonly property State movingState: isExtended ? device.states.getState(movingStateType.id) : 0 - readonly property State percentageState: isExtended ? device.states.getState(deviceClass.stateTypes.findByName("percentage").id) : 0 - readonly property State angleState: isVenetian ? device.states.getState(angleStateType.id) : 0 + readonly property State movingState: isExtended ? device.states.getState(movingStateType.id) : null + readonly property State percentageState: isExtended ? device.states.getState(deviceClass.stateTypes.findByName("percentage").id) : null + readonly property State angleState: isVenetian ? device.states.getState(angleStateType.id) : null readonly property bool moving: movingState ? movingState.value === true : false @@ -212,8 +212,8 @@ DevicePageBase { id: angleMouseArea anchors.fill: parent // angle : totalAngle = mouseY : height - property int totalAngle: root.angleStateType.maxValue - root.angleStateType.minValue - property int angle: totalAngle * mouseY / height + root.angleStateType.minValue + property int totalAngle: root.angleState ? root.angleStateType.maxValue - root.angleStateType.minValue : 0 + property int angle: root.angleState ? totalAngle * mouseY / height + root.angleStateType.minValue : 0 hoverEnabled: true property int startY: 0 diff --git a/nymea-app/ui/magic/DeviceRulesPage.qml b/nymea-app/ui/magic/DeviceRulesPage.qml index c8e2013b..0804e311 100644 --- a/nymea-app/ui/magic/DeviceRulesPage.qml +++ b/nymea-app/ui/magic/DeviceRulesPage.qml @@ -37,7 +37,8 @@ import Nymea 1.0 Page { id: root - property var device: null + property Thing thing: null + property alias device: root.thing Component.onCompleted: print("+++ created devicerulespage") Component.onDestruction: print("--- destroying devicerulespage")