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")