diff --git a/libnymea-app/types/thingclass.cpp b/libnymea-app/types/thingclass.cpp index a9358286..6a1d69af 100644 --- a/libnymea-app/types/thingclass.cpp +++ b/libnymea-app/types/thingclass.cpp @@ -3,7 +3,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH -* Copyright (C) 2024 - 2025, chargebyte austria GmbH +* Copyright (C) 2024 - 2026, chargebyte austria GmbH * * This file is part of libnymea-app. * @@ -26,10 +26,9 @@ #include -ThingClass::ThingClass(QObject *parent) : - QObject(parent) -{ -} +ThingClass::ThingClass(QObject *parent) + : QObject(parent) +{} QUuid ThingClass::id() const { @@ -330,3 +329,8 @@ bool ThingClass::hasActionType(const QUuid &actionTypeId) } return false; } + +bool ThingClass::isAutoCreated() const +{ + return m_createMethods.contains("CreateMethodAuto"); +} diff --git a/libnymea-app/types/thingclass.h b/libnymea-app/types/thingclass.h index ae4c905d..df1e8630 100644 --- a/libnymea-app/types/thingclass.h +++ b/libnymea-app/types/thingclass.h @@ -3,7 +3,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH -* Copyright (C) 2024 - 2025, chargebyte austria GmbH +* Copyright (C) 2024 - 2026, chargebyte austria GmbH * * This file is part of libnymea-app. * @@ -25,15 +25,15 @@ #ifndef THINGCLASS_H #define THINGCLASS_H -#include -#include #include +#include #include +#include +#include "actiontypes.h" +#include "eventtypes.h" #include "paramtypes.h" #include "statetypes.h" -#include "eventtypes.h" -#include "actiontypes.h" class ThingClass : public QObject { @@ -135,6 +135,8 @@ public: Q_INVOKABLE bool hasActionType(const QUuid &actionTypeId); + Q_INVOKABLE bool isAutoCreated() const; + signals: void paramTypesChanged(); void settingsTypesChanged(); diff --git a/nymea-app/ui/customviews/GenericTypeGraph.qml b/nymea-app/ui/customviews/GenericTypeGraph.qml index 78e42f0e..c4c3c5b9 100644 --- a/nymea-app/ui/customviews/GenericTypeGraph.qml +++ b/nymea-app/ui/customviews/GenericTypeGraph.qml @@ -126,7 +126,7 @@ Item { ValueAxis { id: yAxis max: { - if (root.stateType && root.stateType.type.toLowerCase() == "bool") { + if (root.stateType && root.stateType.type.toLowerCase() === "bool") { return 1; } else { Math.ceil(logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05)) @@ -137,7 +137,7 @@ Item { // onMaxChanged: applyNiceNumbers(); labelsFont: Style.extraSmallFont labelFormat: { - if (root.stateType && root.stateType.type.toLowerCase() == "bool") { + if (root.stateType && root.stateType.type.toLowerCase() === "bool") { return "x"; } else { return "%d"; diff --git a/nymea-app/ui/customviews/MultiStateChart.qml b/nymea-app/ui/customviews/MultiStateChart.qml index d21a6a99..28607082 100644 --- a/nymea-app/ui/customviews/MultiStateChart.qml +++ b/nymea-app/ui/customviews/MultiStateChart.qml @@ -147,7 +147,7 @@ Item { // y: chartView.plotArea.y // height: chartView.plotArea.height // width: chartView.plotArea.x - x - // visible: root.stateType.type.toLowerCase() != "bool" && logsModel.minValue != logsModel.maxValue + // visible: root.stateType.type.toLowerCase() !== "bool" && logsModel.minValue != logsModel.maxValue // property double range: Math.abs(valueAxis.max - valueAxis.min) // property double stepSize: range / (valueAxis.tickCount - 1) // property int precision: valueAxis.max - valueAxis.min < 5 ? 2 : 0 @@ -256,7 +256,7 @@ Item { print("entry", entry.timestamp, entry.source, JSON.stringify(entry.values)) d.ensureValue(zeroSeries, entry.timestamp) - if (stateType.type.toLowerCase() == "bool") { + if (stateType.type.toLowerCase() === "bool") { var value = entry.values[stateType.name] if (value == null) { value = false; @@ -290,7 +290,7 @@ Item { } } - if (stateType.type.toLowerCase() == "bool") { + if (stateType.type.toLowerCase() === "bool") { var last = series.at(series.count-1); if (last.x < d.endTime) { @@ -304,7 +304,7 @@ Item { } onEntriesRemoved: (index, count) => { print("removing:", index, count, series.count) - if (stateType.type.toLowerCase() == "bool") { + if (stateType.type.toLowerCase() === "bool") { series.removePoints(index * 2, count * 2) if (series.count == 1) { series.removePoints(0, 1); diff --git a/nymea-app/ui/customviews/SensorView.qml b/nymea-app/ui/customviews/SensorView.qml index 1c995cf7..c4ae4563 100644 --- a/nymea-app/ui/customviews/SensorView.qml +++ b/nymea-app/ui/customviews/SensorView.qml @@ -57,7 +57,7 @@ Item { if (root.interfaceName == "closablesensor") { return true } - return sensorStateType && sensorStateType.type.toLowerCase() == "bool" && sensorState.value === true + return sensorStateType && sensorStateType.type.toLowerCase() === "bool" && sensorState.value === true } iconSource: { if (root.interfaceName == "closablesensor") { @@ -100,7 +100,7 @@ Item { } sourceComponent: { - if (stateType.type.toLowerCase() == "bool") { + if (stateType.type.toLowerCase() === "bool") { return boolComponent; } diff --git a/nymea-app/ui/customviews/StateChart.qml b/nymea-app/ui/customviews/StateChart.qml index e5707291..8caf6e8f 100644 --- a/nymea-app/ui/customviews/StateChart.qml +++ b/nymea-app/ui/customviews/StateChart.qml @@ -56,7 +56,7 @@ Item { property date now: new Date() readonly property int range: selectionTabs.currentValue.range - readonly property int sampleRate: root.stateType == null || root.stateType.type.toLowerCase() == "bool" ? NewLogsModel.SampleRateAny : selectionTabs.currentValue.sampleRate + readonly property int sampleRate: root.stateType == null || root.stateType.type.toLowerCase() === "bool" ? NewLogsModel.SampleRateAny : selectionTabs.currentValue.sampleRate readonly property int visibleValues: range / sampleRate @@ -114,7 +114,7 @@ Item { // print("entry", entry.timestamp, entry.source, JSON.stringify(entry.values)) zeroSeries.ensureValue(entry.timestamp) - if (root.stateType.type.toLowerCase() == "bool") { + if (root.stateType.type.toLowerCase() === "bool") { var value = entry.values[root.stateType.name] if (value == null) { value = false; @@ -148,7 +148,7 @@ Item { } } - if (root.stateType.type.toLowerCase() == "bool") { + if (root.stateType.type.toLowerCase() === "bool") { var last = valueSeries.at(valueSeries.count-1); if (last.x < d.endTime) { valueSeries.append(d.endTime, last.y) @@ -160,7 +160,7 @@ Item { } onEntriesRemoved: (index, count) => { print("removing:", index, count, valueSeries.count) - if (root.stateType.type.toLowerCase() == "bool") { + if (root.stateType.type.toLowerCase() === "bool") { valueSeries.removePoints(index * 2, count * 2) if (valueSeries.count == 1) { valueSeries.removePoints(0, 1); @@ -419,7 +419,7 @@ Item { y: chartView.y + chartView.plotArea.y height: chartView.plotArea.height width: Math.max(0, chartContainer.yAxisLabelAreaWidth - Style.smallMargins) - visible: root.stateType && root.stateType.type.toLowerCase() != "bool" && logsModel.minValue != logsModel.maxValue + visible: root.stateType && root.stateType.type.toLowerCase() !== "bool" && logsModel.minValue != logsModel.maxValue property double range: Math.abs(valueAxis.max - valueAxis.min) property double stepSize: range / (valueAxis.tickCount - 1) property int precision: valueAxis.max - valueAxis.min < 5 ? 2 : 0 @@ -589,7 +589,7 @@ Item { elide: Text.ElideRight text: toolTip.value === null ? qsTr("No data") - : root.stateType.type.toLowerCase() == "bool" + : root.stateType.type.toLowerCase() === "bool" ? root.stateType.displayName + ": " + (toolTip.value ? qsTr("Yes") : qsTr("No")) : Types.toUiValue(toolTip.value, root.stateType.unit).toFixed(root.roundTo) + Types.toUiUnit(root.stateType.unit) font: Style.extraSmallFont diff --git a/nymea-app/ui/delegates/ParamDelegate.qml b/nymea-app/ui/delegates/ParamDelegate.qml index 6819d759..a85c0caf 100644 --- a/nymea-app/ui/delegates/ParamDelegate.qml +++ b/nymea-app/ui/delegates/ParamDelegate.qml @@ -37,6 +37,7 @@ ItemDelegate { property alias value: d.value property Param param: Param { id: d + paramTypeId: paramType.id value: paramType.defaultValue } @@ -48,9 +49,11 @@ ItemDelegate { bottomPadding: 0 contentItem: ColumnLayout { id: contentItemColumn + anchors.fill: parent anchors.leftMargin: Style.margins anchors.rightMargin: Style.margins + RowLayout { Layout.fillWidth: true spacing: Style.margins @@ -70,8 +73,6 @@ ItemDelegate { Layout.fillWidth: true//!parent.labelFillsWidth Layout.maximumWidth: root.nameVisible ? contentItemColumn.width / 2 : contentItemColumn.width sourceComponent: { - print("Loading ParamDelegate"); - print("Writable:", root.writable, "type:", root.paramType.type, "min:", root.paramType.minValue, "max:", root.paramType.maxValue, "value:", root.param.value) if (!root.writable) { return stringComponent; } @@ -123,12 +124,11 @@ ItemDelegate { return null; } } - } - Component { id: stringComponent + Label { text: { switch (root.paramType.type.toLowerCase()) { @@ -141,15 +141,17 @@ ItemDelegate { elide: Text.ElideRight } } + Component { id: boolComponent + Item { implicitHeight: theSwitch.implicitHeight implicitWidth: theSwitch.implicitWidth Switch { id: theSwitch anchors { top: parent.top; right: parent.right; bottom: parent.bottom } - width: Math.min(parent.width, implicitiWidth) + width: Math.min(parent.width, implicitWidth) checked: root.param.value === true Component.onCompleted: { if (root.param.value === undefined) { @@ -162,10 +164,11 @@ ItemDelegate { } } } - } + Component { id: sliderComponent + RowLayout { spacing: Style.margins @@ -206,6 +209,7 @@ ItemDelegate { root.param.value = newValue; } } + Label { text: Types.toUiValue(root.param.value, root.paramType.unit).toFixed(slider.decimals) + Types.toUiUnit(root.paramType.unit) } @@ -231,18 +235,19 @@ ItemDelegate { : 2000000000 editable: true width: 150 - onValueModified: root.param.value = value + floatingPoint: root.paramType.type.toLowerCase() === "double" - floatingPoint: root.paramType.type.toLowerCase() == "double" + onValueModified: (value) => { root.param.value = value } Component.onCompleted: { print("from:", from, "min", root.paramType.minValue) print("to:", to, "max", root.paramType.maxValue) - if (root.value === undefined) { - root.value = value + if (root.param.value === undefined) { + root.param.value = value } } } + Label { text: Types.toUiUnit(root.paramType.unit) visible: text.length > 0 @@ -255,40 +260,48 @@ ItemDelegate { TextField { text: root.param.value !== undefined ? root.param.value - : root.paramType.defaultValue + : root.paramType.defaultValue !== undefined ? root.paramType.defaultValue : "" + + placeholderText: root.placeholderText onEditingFinished: { root.param.value = text } + Component.onCompleted: { if (root.param.value === undefined) { root.param.value = text; } } - placeholderText: root.placeholderText + } } Component { id: comboBoxComponent + ComboBox { id: control Layout.fillWidth: true model: root.paramType.allowedValues - displayText: currentText + ( root.paramType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "") + displayText: { + if (currentIndex < 0 || currentIndex >= root.paramType.allowedValues.length) { + return ""; + } + return Types.toUiValue(root.paramType.allowedValues[currentIndex], root.paramType.unit) + + (root.paramType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : ""); + } currentIndex: root.paramType.allowedValues.indexOf(root.param.value !== undefined ? root.param.value : root.paramType.defaultValue) delegate: ItemDelegate { width: control.width - text: Types.toUiValue(modelData, root.paramType.unit) + ( root.paramType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "") + text: Types.toUiValue(modelData, root.paramType.unit) + ( root.paramType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.paramType.unit) : "") highlighted: control.highlightedIndex === index } - onActivated: (index) => { - root.param.value = root.paramType.allowedValues[index] - } + onActivated: (index) => { root.param.value = root.paramType.allowedValues[index] } Component.onCompleted: { - if (root.value === undefined) { - root.value = model[0] + if (root.param.value === undefined) { + root.param.value = model[0] } } } @@ -296,6 +309,7 @@ ItemDelegate { Component { id: colorPickerComponent + ColorPickerPre510 { id: colorPicker implicitHeight: 200 @@ -333,6 +347,7 @@ ItemDelegate { Component { id: colorTemperaturePickerComponent + ColorPickerCt { id: colorPickerCt implicitHeight: 50 @@ -340,7 +355,7 @@ ItemDelegate { maxCt: root.paramType.maxValue ct: root.param.value !== undefined ? root.param.value - : root.paramType.defaultValue + : root.paramType.defaultValue !== undefined ? root.paramType.defaultValue : root.paramType.minValue @@ -359,6 +374,7 @@ ItemDelegate { Component { id: colorPreviewComponent + Rectangle { implicitHeight: app.mediumFont implicitWidth: implicitHeight diff --git a/nymea-app/ui/delegates/StateDelegate.qml b/nymea-app/ui/delegates/StateDelegate.qml index 97bf68e5..bd0fb3d4 100644 --- a/nymea-app/ui/delegates/StateDelegate.qml +++ b/nymea-app/ui/delegates/StateDelegate.qml @@ -63,7 +63,7 @@ ItemDelegate { id: loader Layout.fillWidth: !parent.labelFillsWidth sourceComponent: { - print("Loading ParamDelegate"); + print("Loading StateDelegate"); print("Writable:", root.writable, "type:", root.stateType.type, "min:", root.stateType.minValue, "max:", root.stateType.maxValue, "value:", root.param.value) if (!root.writable) { return stringComponent; @@ -222,7 +222,7 @@ ItemDelegate { width: 150 onValueModified: root.param.value = value - floatingPoint: root.stateType.type.toLowerCase() == "double" + floatingPoint: root.stateType.type.toLowerCase() === "double" Component.onCompleted: { print("from:", from, "min", root.stateType.minValue) @@ -264,11 +264,11 @@ ItemDelegate { id: control Layout.fillWidth: true model: root.stateType.allowedValues - displayText: currentText + ( root.stateType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "") + displayText: currentText + ( root.stateType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "") currentIndex: root.stateType.allowedValues.indexOf(root.param.value !== undefined ? root.param.value : root.stateType.defaultValue) delegate: ItemDelegate { width: control.width - text: Types.toUiValue(modelData, root.stateType.unit) + ( root.stateType.unit != Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "") + text: Types.toUiValue(modelData, root.stateType.unit) + ( root.stateType.unit !== Types.UnitNone ? " " + Types.toUiUnit(root.stateType.unit) : "") highlighted: control.highlightedIndex === index } onActivated: (index) => { diff --git a/nymea-app/ui/delegates/statedelegates/SpinBoxDelegate.qml b/nymea-app/ui/delegates/statedelegates/SpinBoxDelegate.qml index bee05013..75c76573 100644 --- a/nymea-app/ui/delegates/statedelegates/SpinBoxDelegate.qml +++ b/nymea-app/ui/delegates/statedelegates/SpinBoxDelegate.qml @@ -36,7 +36,7 @@ SpinBox { stepSize: Math.min(10, (to - from) / 10) property var unit: Types.UnitNone editable: true - onValueModified: { + onValueModified: (value) => { changed(value) } textFromValue: function(value) { diff --git a/nymea-app/ui/devicepages/DeviceDetailsPage.qml b/nymea-app/ui/devicepages/DeviceDetailsPage.qml index ea09c028..f9fb1fbd 100644 --- a/nymea-app/ui/devicepages/DeviceDetailsPage.qml +++ b/nymea-app/ui/devicepages/DeviceDetailsPage.qml @@ -172,17 +172,9 @@ Page { var maxValue = stateDelegate.stateType.maxValue !== undefined ? stateDelegate.stateType.maxValue : 2000000000; - print("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames) - stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp, - { - value: root.thing.states.getState(stateType.id).value, - possibleValues: stateDelegate.stateType.possibleValues, - possibleValuesDisplayNames: stateDelegate.stateType.possibleValuesDisplayNames, - from: minValue, - to: maxValue, - unit: stateDelegate.stateType.unit, - writable: false, - stateType: stateDelegate.stateType + console.log("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames) + stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp, { + value: root.thing.states.getState(stateType.id).value }) } @@ -231,6 +223,16 @@ Page { property: "unit" value: stateDelegate.stateType.unit } + Binding { + target: stateDelegateLoader.item.hasOwnProperty("writable") ? stateDelegateLoader.item : null + property: "writable" + value: false + } + Binding { + target: stateDelegateLoader.item.hasOwnProperty("stateType") ? stateDelegateLoader.item : null + property: "stateType" + value: stateDelegate.stateType + } } } diff --git a/nymea-app/ui/magic/EventDescriptorDelegate.qml b/nymea-app/ui/magic/EventDescriptorDelegate.qml index 3a378fcd..8d6a6085 100644 --- a/nymea-app/ui/magic/EventDescriptorDelegate.qml +++ b/nymea-app/ui/magic/EventDescriptorDelegate.qml @@ -61,7 +61,7 @@ NymeaSwipeDelegate { operatorString = " = "; break; case ParamDescriptor.ValueOperatorNotEquals: - operatorString = " != "; + operatorString = " !== "; break; case ParamDescriptor.ValueOperatorGreater: operatorString = " > "; diff --git a/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml b/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml index 5bcc84dc..1d8fa1bf 100644 --- a/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml +++ b/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml @@ -84,7 +84,7 @@ DashboardDelegateBase { Label { Layout.fillWidth: true - visible: root.stateType && root.stateType.type.toLowerCase() != "bool" + visible: root.stateType && root.stateType.type.toLowerCase() !== "bool" horizontalAlignment: Text.AlignHCenter font: Style.largeFont elide: Text.ElideRight diff --git a/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml b/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml index 768c0606..b353e493 100644 --- a/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml +++ b/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml @@ -59,20 +59,27 @@ SettingsPageBase { width: implicitWidth + app.margins x: parent.width - width - Component.onCompleted: { - deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Rename"), iconSource: "qrc:/icons/edit.svg", functionName: "renameThing"})) - // FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated - // This check might be wrong for thingClasses with multiple create methods... - if (!root.thing.isChild || root.thing.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) { - deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Delete"), iconSource: "qrc:/icons/delete.svg", functionName: "deleteThing"})) - } - // FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated - // This check might be wrong for thingClasses with multiple create methods... - if (!root.thing.isChild || root.thingClass.createMethods.indexOf("CreateMethodAuto") < 0) { - deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Reconfigure"), iconSource: "qrc:/icons/configure.svg", functionName: "reconfigureThing"})) + function addMenuEntry(properties) { + var item = menuEntryComponent.createObject(null, properties) + if (!item) { + console.warn("Failed to create menu entry", properties && properties.text) + return } - deviceMenu.addItem(menuEntryComponent.createObject(deviceMenu, {text: qsTr("Details"), iconSource: "qrc:/icons/info.svg", functionName: "thingDetails"})) + deviceMenu.addItem(item) + } + + Component.onCompleted: { + addMenuEntry({text: qsTr("Rename"), iconSource: "qrc:/icons/edit.svg", functionName: "renameThing"}) + + // FIXME: This isn't entirely correct... we should have a way to know if a particular thing is in fact autocreated + // This check might be wrong for thingClasses with multiple create methods... + if (!root.thing.isChild && !root.thing.thingClass.isAutoCreated()) { + addMenuEntry({text: qsTr("Delete"), iconSource: "qrc:/icons/delete.svg", functionName: "deleteThing"}) + addMenuEntry({text: qsTr("Reconfigure"), iconSource: "qrc:/icons/configure.svg", functionName: "reconfigureThing"}) + } + + addMenuEntry({text: qsTr("Details"), iconSource: "qrc:/icons/info.svg", functionName: "thingDetails"}) } function renameThing() { @@ -106,8 +113,8 @@ SettingsPageBase { Connections { target: engine.thingManager - onRemoveThingReply: { - if (d.pendingCommand != commandId) { + function onRemoveThingReply(commandId, thingError, ruleIds) { + if (d.pendingCommand !== commandId) { return; } @@ -140,6 +147,7 @@ SettingsPageBase { prominentSubText: false progressive: false } + NymeaItemDelegate { Layout.fillWidth: true text: root.thing.thingClass.displayName @@ -191,6 +199,7 @@ SettingsPageBase { StateTypesProxy { id: ioModel + stateTypes: root.thing.thingClass.stateTypes digitalInputs: true digitalOutputs: true @@ -200,13 +209,14 @@ SettingsPageBase { Repeater { model: ioModel + delegate: NymeaSwipeDelegate { Layout.fillWidth: true iconName: "qrc:/icons/io-connections.svg" text: model.displayName subText: { - if (ioStateType.ioType == Types.IOTypeDigitalInput || ioStateType.ioType == Types.IOTypeAnalogInput) { + if (ioStateType.ioType === Types.IOTypeDigitalInput || ioStateType.ioType === Types.IOTypeAnalogInput) { if (inputConnectionWatcher.ioConnection) { return "%1: %2".arg(inputConnectionWatcher.outputThing.name).arg(inputConnectionWatcher.outputStateType.displayName) } @@ -223,14 +233,17 @@ SettingsPageBase { IOInputConnectionWatcher { id: inputConnectionWatcher + ioConnections: engine.thingManager.ioConnections inputThingId: root.thing.id inputStateTypeId: ioStateType.id property Thing outputThing: ioConnection ? engine.thingManager.things.getThing(ioConnection.outputThingId) : null property StateType outputStateType: ioConnection ? outputThing.thingClass.stateTypes.getStateType(ioConnection.outputStateTypeId) : null } + IOOutputConnectionWatcher { id: outputConnectionWatcher + ioConnections: engine.thingManager.ioConnections outputThingId: root.thing.id outputStateTypeId: ioStateType.id @@ -253,6 +266,7 @@ SettingsPageBase { Repeater { id: settingsRepeater + model: root.thing.settings delegate: ParamDelegate { Layout.fillWidth: true @@ -360,7 +374,7 @@ SettingsPageBase { property IOInputConnectionWatcher inputWatcher: null property IOOutputConnectionWatcher outputWatcher: null - readonly property bool isInput: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput + readonly property bool isInput: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput Label { Layout.fillWidth: true @@ -377,16 +391,16 @@ SettingsPageBase { model: ThingsProxy { id: connectableIODevices engine: _engine - showDigitalInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalOutput - showDigitalOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput - showAnalogInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogOutput - showAnalogOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput + showDigitalInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalOutput + showDigitalOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput + showAnalogInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogOutput + showAnalogOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput } textRole: "name" Layout.fillWidth: true Component.onCompleted: { for (var i = 0; i < connectableIODevices.count; i++) { - if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) { + if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) { if (connectableIODevices.get(i).id === ioConnectionDialog.inputWatcher.ioConnection.outputThingId) { ioThingComboBox.currentIndex = i; break; @@ -407,10 +421,10 @@ SettingsPageBase { model: StateTypesProxy { id: connectableStateTypes stateTypes: connectableIODevices.get(ioThingComboBox.currentIndex).thingClass.stateTypes - digitalInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalOutput - digitalOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput - analogInputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogOutput - analogOutputs: ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput + digitalInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalOutput + digitalOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput + analogInputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogOutput + analogOutputs: ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput } textRole: "displayName" Layout.fillWidth: true @@ -418,7 +432,7 @@ SettingsPageBase { // print("loading for:", ioConnectionDialog.inputWatcher.ioConnection.outputStateTypeId) for (var i = 0; i < connectableStateTypes.count; i++) { print("checking:", connectableStateTypes.get(i).id) - if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) { + if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) { if (connectableStateTypes.get(i).id === ioConnectionDialog.inputWatcher.ioConnection.outputStateTypeId) { ioStateComboBox.currentIndex = i; break; @@ -442,6 +456,7 @@ SettingsPageBase { CheckBox { id: invertCheckBox + checked: ioConnectionDialog.isInput ? ioConnectionDialog.inputWatcher.ioConnection.inverted : ioConnectionDialog.outputWatcher.ioConnection.inverted } } @@ -453,7 +468,7 @@ SettingsPageBase { columns: width > (cancelButton.implicitWidth + disconnectButton.implicitWidth + connectButton.implicitWidth) ? 4 : 1 - layoutDirection: columns == 1 ? Qt.RightToLeft : Qt.LeftToRight + layoutDirection: columns === 1 ? Qt.RightToLeft : Qt.LeftToRight Item { Layout.fillWidth: true @@ -473,8 +488,8 @@ SettingsPageBase { Layout.fillWidth: buttonGrid.columns === 1 onClicked: { - if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput - || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) { + if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput + || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) { engine.thingManager.disconnectIO(ioConnectionDialog.inputWatcher.ioConnection.id); } else { engine.thingManager.disconnectIO(ioConnectionDialog.outputWatcher.ioConnection.id); @@ -495,8 +510,8 @@ SettingsPageBase { var inputStateTypeId; var outputThingId; var outputStateTypeId; - if (ioConnectionDialog.ioStateType.ioType == Types.IOTypeDigitalInput - || ioConnectionDialog.ioStateType.ioType == Types.IOTypeAnalogInput) { + if (ioConnectionDialog.ioStateType.ioType === Types.IOTypeDigitalInput + || ioConnectionDialog.ioStateType.ioType === Types.IOTypeAnalogInput) { inputThingId = root.thing.id; inputStateTypeId = ioConnectionDialog.ioStateType.id; outputThingId = connectableIODevices.get(ioThingComboBox.currentIndex).id; diff --git a/nymea-app/ui/thingconfiguration/SetupWizard.qml b/nymea-app/ui/thingconfiguration/SetupWizard.qml index e636333e..de4f472b 100644 --- a/nymea-app/ui/thingconfiguration/SetupWizard.qml +++ b/nymea-app/ui/thingconfiguration/SetupWizard.qml @@ -156,13 +156,12 @@ Page { Connections { target: engine.thingManager - onPairThingReply: { + function onPairThingReply(commandId, thingError, pairingTransactionId, setupMethod, displayMessage, oAuthUrl) { busyOverlay.shown = false if (thingError !== Thing.ThingErrorNoError) { busyOverlay.shown = false; internalPageStack.push(resultsPage, {thingError: thingError, message: displayMessage}); return; - } d.pairingTransactionId = pairingTransactionId; @@ -179,17 +178,21 @@ Page { break; default: print("Setup method reply not handled:", setupMethod); + break; } } - onConfirmPairingReply: { + + function onConfirmPairingReply(commandId, thingError, thingId, displayMessage) { busyOverlay.shown = false internalPageStack.push(resultsPage, {thingError: thingError, thingId: thingId, message: displayMessage}) } - onAddThingReply: { + + function onAddThingReply(commandId, thingError, thingId, displayMessage) { busyOverlay.shown = false; internalPageStack.push(resultsPage, {thingError: thingError, thingId: thingId, message: displayMessage}) } - onReconfigureThingReply: { + + function onReconfigureThingReply(commandId, thingError, displayMessage) { busyOverlay.shown = false; internalPageStack.push(resultsPage, {thingError: thingError, thingId: root.thing.id, message: displayMessage}) } @@ -372,7 +375,7 @@ Page { Component.onCompleted: { if (root.thingClass.id.toString().match(/\{?f0dd4c03-0aca-42cc-8f34-9902457b05de\}?/)) { console.warn("checking Notification permission!") - if (PlatformPermissions.notificationsPermission != PlatformPermissions.PermissionStatusGranted) { + if (PlatformPermissions.notificationsPermission !== PlatformPermissions.PermissionStatusGranted) { console.warn("Notification permission missing!") PlatformPermissions.requestPermission(PlatformPermissions.PermissionNotifications) } @@ -460,6 +463,11 @@ Page { wrapMode: Text.WordWrap } + Item { + Layout.fillWidth: true + Layout.preferredHeight: Style.margins + } + TextField { id: usernameTextField Layout.fillWidth: true @@ -468,6 +476,11 @@ Page { visible: pairingPage.setupMethod === "SetupMethodUserAndPassword" } + Item { + Layout.fillWidth: true + Layout.preferredHeight: Style.margins + } + PasswordTextField { id: pinTextField Layout.fillWidth: true @@ -476,7 +489,6 @@ Page { signup: false } - Button { Layout.fillWidth: true Layout.margins: app.margins @@ -549,8 +561,9 @@ Page { WebView { id: oAuthWebView - anchors.fill: parent + url: oAuthPage.oAuthUrl + anchors.fill: parent function finishProcess(url) { print("Confirm pairing") @@ -559,7 +572,7 @@ Page { oAuthWebView.visible = false } - onUrlChanged: { + onUrlChanged: (url) => { print("OAUTH URL changed", url) if (url.toString().indexOf("https://127.0.0.1") == 0) { print("Redirect URL detected!")