Update rule editor to not use generated events for states any more

This commit is contained in:
Michael Zanetti 2022-02-11 00:56:19 +01:00
parent 43b447d0af
commit efe51b33e4
14 changed files with 750 additions and 251 deletions

View File

@ -116,7 +116,10 @@ QList<int> ThingDiscovery::discoverThingsByInterface(const QString &interfaceNam
for (int i = 0; i < m_engine->thingManager()->thingClasses()->rowCount(); i++) {
ThingClass *thingClass = m_engine->thingManager()->thingClasses()->get(i);
if (!thingClass->interfaces().contains(interfaceName)) {
if (!thingClass->interfaces().contains(interfaceName) && !thingClass->providedInterfaces().contains(interfaceName)) {
continue;
}
if (!thingClass->createMethods().contains("CreateMethodDiscovery")) {
continue;
}
pendingCommands.append(discoverThingsInternal(thingClass->id()));

View File

@ -274,5 +274,6 @@
<file>ui/components/SettingsTile.qml</file>
<file>ui/components/NymeaTextField.qml</file>
<file>ui/system/TunnelProxyServerConfigurationDialog.qml</file>
<file>ui/delegates/StateDelegate.qml</file>
</qresource>
</RCC>

View File

@ -106,48 +106,6 @@
}
]
},
{
"description": "Automatic night mode",
"ruleNameTemplate": "Automatic night mode on %0",
"timeDescriptorTemplate": {
"calendarItemTemplates": [
{
"startTime": "22:00",
"duration": 600,
"repeatingOption": {
"repeatingMode": "RepeatingModeDaily"
},
"editable": true
}
]
},
"ruleActionTemplates": [
{
"interfaceName": "mediacontroller",
"interfaceAction": "nightMode",
"selectionId": 0,
"params": [
{
"name": "nightMode",
"value": true
}
]
}
],
"ruleExitActionTemplates": [
{
"interfaceName": "mediacontroller",
"interfaceAction": "nightMode",
"selectionId": 0,
"params": [
{
"name": "nightMode",
"value": false
}
]
}
]
},
{
"description": "Play/pause music by button press",
"ruleNameTemplate": "%1 toggles play/pause on %0",

View File

@ -39,6 +39,7 @@ SwipeDelegate {
implicitWidth: 200
implicitHeight: Style.smallDelegateHeight
property string subText
property bool progressive: true
property bool canDelete: false
@ -199,6 +200,15 @@ SwipeDelegate {
}
}
swipe.enabled: {
for (var i = 0; i < d.finalContextOptions.length; i++) {
if (d.finalContextOptions[i].visible) {
return true
}
}
return false
}
swipe.right: swipeComponent
Component {

View File

@ -37,14 +37,20 @@ import "../components"
ItemDelegate {
id: root
property var paramType: null
property ParamType paramType: null
property StateType stateType: null
property var value: null
property int operatorType: ParamDescriptors.ValueOperatorEquals
readonly property string type: paramType ? paramType.type.toLowerCase() : stateType ? stateType.type.toLowerCase() : ""
readonly property var minValue: paramType ? paramType.minValue : stateType ? stateType.minValue : undefined
readonly property var maxValue: paramType ? paramType.maxValue : stateType ? stateType.maxValue : undefined
readonly property var allowedValues: paramType ? paramType.allowedValues : stateType ? stateType.allowedValues : undefined
readonly property int unit: paramType ? root.paramType.unit : root.stateType.unit
contentItem: ColumnLayout {
Label {
Layout.fillWidth: true
text: paramType.displayName
text: root.paramType ? root.paramType.displayName : root.stateType.displayName
}
RowLayout {
Layout.fillWidth: true
@ -64,7 +70,7 @@ ItemDelegate {
}
property bool isNumeric: {
switch (paramType.type.toLowerCase()) {
switch (root.type) {
case "bool":
case "string":
case "qstring":
@ -75,7 +81,7 @@ ItemDelegate {
case "double":
return true;
}
console.warn("ParamDescriptorDelegate: Unhandled data type:", paramType.type.toLowerCase());
console.warn("ParamDescriptorDelegate: Unhandled data type:", root.type);
return false;
}
@ -114,44 +120,45 @@ ItemDelegate {
Layout.fillWidth: true
sourceComponent: {
print("Datatye is:", paramType.name, paramType.type, paramType.minValue, paramType.maxValue, paramType.allowedValues)
switch (paramType.type.toLowerCase()) {
print("Datatye is:", root.type, root.minValue, root.maxValue, root.allowedValues)
switch (root.type) {
case "bool":
return boolComponent;
case "uint":
case "int":
case "double":
if (paramType.minValue !== undefined && paramType.maxValue !== undefined) {
if (root.minValue !== undefined && root.maxValue !== undefined) {
return labelComponent;
}
return spinboxComponent;
case "string":
case "qstring":
case "color":
if (paramType.allowedValues.length > 0) {
if (root.allowedValues.length > 0) {
return comboBoxComponent
}
return textFieldComponent;
}
console.warn("ParamDescriptorDelegate: Type delegate not implemented", paramType.type)
console.warn("ParamDescriptorDelegate: Type delegate not implemented", root.type)
return null;
}
}
Label {
text: Types.toUiUnit(paramType.unit)
visible: paramType.unit !== Types.UnitNone
text: Types.toUiUnit(root.unit)
visible: root.unit !== Types.UnitNone
}
}
Loader {
Layout.fillWidth: true
sourceComponent: {
print("***********+ loading", paramType.type)
switch (paramType.type.toLowerCase()) {
print("***********+ loading", root.type)
switch (root.type) {
case "uint":
case "int":
case "double":
if (paramType.minValue !== undefined && paramType.maxValue !== undefined) {
if (root.minValue !== undefined && root.maxValue !== undefined) {
return sliderComponent
}
@ -166,11 +173,11 @@ ItemDelegate {
id: labelComponent
Label {
text: {
switch (root.paramType.type.toLowerCase()) {
switch (root.type.toLowerCase()) {
case "double":
return Math.round(Types.toUiValue(root.value, root.paramType.unit) * 10) / 10
return Math.round(Types.toUiValue(root.value, root.unit) * 10) / 10
}
return Types.toUiValue(root.value, root.paramType.unit)
return Types.toUiValue(root.value, root.unit)
}
}
}
@ -189,13 +196,13 @@ ItemDelegate {
id: sliderComponent
RowLayout {
spacing: app.margins
Label { text: Types.toUiValue(root.paramType.minValue, root.paramType.unit) }
Label { text: Types.toUiValue(root.minValue, root.unit) }
Slider {
from: paramType.minValue
to: paramType.maxValue
from: root.minValue
to: root.maxValue
value: root.value
stepSize: {
switch (root.paramType.type.toLowerCase()) {
switch (root.type.toLowerCase()) {
case "double":
return 0.1
}
@ -206,7 +213,7 @@ ItemDelegate {
root.value = value;
}
}
Label { text: Types.toUiValue(root.paramType.maxValue, root.paramType.unit) }
Label { text: Types.toUiValue(root.maxValue, root.unit) }
}
}
@ -214,11 +221,11 @@ ItemDelegate {
Component {
id: spinboxComponent
NymeaSpinBox {
from: paramType.minValue
to: paramType.maxValue
from: root.minValue
to: root.maxValue
value: root.value != undefined ? root.value : 0
onValueModified: root.value = value
floatingPoint: root.paramType.type.toLowerCase() === "double"
floatingPoint: root.type === "double"
}
}
@ -238,9 +245,9 @@ ItemDelegate {
Component {
id: comboBoxComponent
ComboBox {
model: paramType.allowedValues
model: root.allowedValues
onCurrentIndexChanged: {
root.value = paramType.allowedValues[currentIndex]
root.value = root.allowedValues[currentIndex]
}
}
}

View File

@ -0,0 +1,362 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.8
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
ItemDelegate {
id: root
property StateType stateType: null
property alias value: d.value
property Param param: Param {
id: d
paramTypeId: stateType.id
value: stateType.defaultValue
}
property bool writable: true
property alias nameVisible: nameLabel.visible
property string placeholderText: ""
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
id: contentItemColumn
RowLayout {
spacing: app.margins
property bool labelFillsWidth: loader.sourceComponent !== textFieldComponent
&& loader.sourceComponent !== stringComponent
Label {
id: nameLabel
Layout.fillWidth: parent.labelFillsWidth
// Layout.minimumWidth: parent.width / 2
text: root.stateType.displayName
elide: Text.ElideRight
}
Loader {
id: loader
Layout.fillWidth: !parent.labelFillsWidth
sourceComponent: {
print("Loading ParamDelegate");
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;
}
switch (root.stateType.type.toLowerCase()) {
case "bool":
return boolComponent;
case "uint":
case "int":
if (root.stateType.name == "colorTemperature") {
return null;
}
case "double":
if (root.stateType.allowedValues.length > 0) {
return comboBoxComponent;
} else if (root.stateType.minValue !== undefined && root.stateType.maxValue !== undefined
&& (root.stateType.maxValue - root.stateType.minValue <= 100)) {
return sliderComponent;
} else {
return spinnerComponent;
}
case "string":
case "qstring":
if (root.stateType.allowedValues.length > 0) {
return comboBoxComponent;
}
return textFieldComponent;
case "color":
return colorPreviewComponent;
}
console.warn("Param Delegate: Fallback to stringComponent", root.stateType.name, root.stateType.type)
return stringComponent;
}
}
}
Loader {
Layout.fillWidth: true
sourceComponent: {
if (root.stateType.name == "colorTemperature") {
return colorTemperaturePickerComponent;
}
switch (root.stateType.type.toLowerCase()) {
case "color":
return colorPickerComponent
}
return null;
}
}
}
Component {
id: stringComponent
Label {
text: {
switch (root.stateType.type.toLowerCase()) {
case "int":
return Math.round(root.param.value);
}
return root.param.value;
}
horizontalAlignment: Text.AlignRight
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)
checked: root.param.value === true
Component.onCompleted: {
if (root.param.value === undefined) {
root.param.value = checked;
}
}
onClicked: {
root.param.value = checked;
}
}
}
}
Component {
id: sliderComponent
RowLayout {
spacing: app.margins
Slider {
id: slider
Layout.fillWidth: true
from: root.stateType.minValue
to: root.stateType.maxValue
value: root.param.value
Component.onCompleted: {
if (root.param.value === undefined) {
if (root.stateType.defaultValue !== undefined) {
root.param.value = root.stateType.defaultValue
} else {
root.param.value = root.stateType.minValue
}
}
}
stepSize: {
var ret = 1
for (var i = 0; i < decimals; i++) {
ret /= 10;
}
return ret;
}
property int decimals: root.stateType.type.toLocaleLowerCase() === "double" ? 1 : 0
onMoved: {
var newValue
switch (root.stateType.type.toLowerCase()) {
case "int":
newValue = Math.round(value)
break;
default:
newValue = Math.round(value * 10) / 10
}
root.param.value = newValue;
}
}
Label {
text: Types.toUiValue(root.param.value, root.stateType.unit).toFixed(slider.decimals) + Types.toUiUnit(root.stateType.unit)
}
}
}
Component {
id: spinnerComponent
RowLayout {
spacing: app.margins
NymeaSpinBox {
id: spinbox
value: root.param.value ? root.param.value : 0
from: root.stateType.minValue !== undefined
? root.stateType.minValue
: root.stateType.type.toLowerCase() === "uint"
? 0
: -2000000000
to: root.stateType.maxValue !== undefined
? root.stateType.maxValue
: 2000000000
editable: true
width: 150
onValueModified: root.param.value = value
floatingPoint: root.stateType.type.toLowerCase() == "double"
Component.onCompleted: {
print("from:", from, "min", root.stateType.minValue)
if (root.value === undefined) {
root.value = value
}
}
}
Label {
text: Types.toUiUnit(root.stateType.unit)
visible: text.length > 0
}
}
}
Component {
id: textFieldComponent
TextField {
text: root.param.value !== undefined
? root.param.value
: root.stateType.defaultValue
? root.stateType.defaultValue
: ""
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.stateType.allowedValues
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) : "")
highlighted: control.highlightedIndex === index
}
onActivated: {
root.param.value = root.stateType.allowedValues[index]
}
Component.onCompleted: {
if (root.value === undefined) {
root.value = model[0]
}
}
}
}
Component {
id: colorPickerComponent
ColorPickerPre510 {
id: colorPicker
implicitHeight: 200
// color: root.param.value
Binding {
target: colorPicker
property: "color"
value: root.param.value
when: !colorPicker.pressed
}
onColorChanged: {
root.param.value = color;
}
touchDelegate: Rectangle {
height: 15
width: height
radius: height / 2
color: Material.accent
Rectangle {
color: colorPicker.hovered || colorPicker.pressed ? "#11000000" : "transparent"
anchors.centerIn: parent
height: 30
width: height
radius: width / 2
Behavior on color { ColorAnimation { duration: 200 } }
}
}
}
}
Component {
id: colorTemperaturePickerComponent
ColorPickerCt {
id: colorPickerCt
implicitHeight: 50
minCt: root.stateType.minValue
maxCt: root.stateType.maxValue
ct: root.param.value !== undefined
? root.param.value
: root.stateType.defaultValue
? root.stateType.defaultValue
: root.stateType.minValue
onCtChanged: {
root.param.value = ct
}
touchDelegate: Rectangle {
height: colorPickerCt.height
width: 5
color: Style.accentColor
}
}
}
Component {
id: colorPreviewComponent
Rectangle {
implicitHeight: app.mediumFont
implicitWidth: implicitHeight
color: root.param.value
radius: width / 4
}
}
}

View File

@ -298,6 +298,7 @@ Page {
Flickable {
anchors.fill: parent
contentHeight: contentColumn.implicitHeight + app.margins
clip: true
ColumnLayout {
id: contentColumn
@ -554,7 +555,7 @@ Page {
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
text: qsTr("Examples:\n• While I'm at home...\n• When the temperature is below 0...\n• Between 9 am and 6 pm...")
text: qsTr("Examples:\n• While I'm at home...\n• While the TV is on...\n• Between 9 am and 6 pm...")
visible: root.isEmpty
}
@ -750,7 +751,7 @@ Page {
minimumJsonRpcVersion: "1.0"
}
}
delegate: NymeaSwipeDelegate {
delegate: NymeaItemDelegate {
Layout.fillWidth: true
Layout.preferredHeight: Style.largeDelegateHeight
iconName: model.iconName
@ -801,7 +802,7 @@ Page {
minimumJsonRpcVersion: "1.0"
}
}
delegate: NymeaSwipeDelegate {
delegate: NymeaItemDelegate {
Layout.fillWidth: true
Layout.preferredHeight: Style.largeDelegateHeight
iconName: model.iconName

View File

@ -46,13 +46,16 @@ NymeaSwipeDelegate {
readonly property Interface iface: eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
readonly property EventType eventType: thingClass ? thingClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
readonly property StateType stateType: thingClass ? thingClass.stateTypes.getStateType(eventDescriptor.eventTypeId)
: iface ? iface.stateTypes.findByName(eventDescriptor.interfaceEvent) : null
readonly property var actualEventType: eventType || stateType
signal removeEventDescriptor()
onDeleteClicked: root.removeEventDescriptor()
iconName: root.thing ? "../images/event.svg" : "../images/event-interface.svg"
text: qsTr("%1 - %2").arg(root.thing ? root.thing.name : root.iface.displayName).arg(root.eventType.displayName)
text: "%1 - %2".arg(root.thing ? root.thing.name : root.iface.displayName).arg(root.actualEventType.displayName)
subText: {
var ret = qsTr("anytime");
for (var i = 0; i < root.eventDescriptor.paramDescriptors.count; i++) {
@ -81,9 +84,10 @@ NymeaSwipeDelegate {
operatorString = " ? ";
}
var paramType = paramDescriptor.paramName
? root.eventType.paramTypes.findByName(paramDescriptor.paramName)
: root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId)
print("**", root.eventType, root.stateType, paramDescriptor.paramName, paramDescriptor.paramTypeId)
var paramType = root.eventType ?
(paramDescriptor.paramName ? root.eventType.paramTypes.findByName(paramDescriptor.paramName) : root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId) )
: root.stateType
if (i === 0) {
// TRANSLATORS: example: "only if temperature > 5"

View File

@ -378,13 +378,29 @@ Page {
function createEventDescriptor(rule, ruleTemplate, thing, eventDescriptorTemplate) {
var eventDescriptor = rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.thingId = thing.id;
eventDescriptor.eventTypeId = thing.thingClass.eventTypes.findByName(eventDescriptorTemplate.eventName).id
var eventType = thing.thingClass.eventTypes.findByName(eventDescriptorTemplate.eventName)
var stateType = thing.thingClass.stateTypes.findByName(eventDescriptorTemplate.eventName)
var needsParams = false;
print("Creating event descriptor from template:", eventDescriptorTemplate.interfaceName, eventDescriptorTemplate.eventName, thing.name, eventType, stateType)
if (eventType) {
eventDescriptor.eventTypeId = eventType.id
var eventType = thing.thingClass.eventTypes.getEventType(eventDescriptor.eventTypeId);
for (var j = 0; j < eventType.paramTypes.count; j++) {
var paramType = eventType.paramTypes.get(j);
var paramDescriptorTemplate = eventDescriptorTemplate.paramDescriptors.getParamDescriptorByName(paramType.name)
for (var j = 0; j < eventType.paramTypes.count; j++) {
var paramType = eventType.paramTypes.get(j);
var paramDescriptorTemplate = eventDescriptorTemplate.paramDescriptors.getParamDescriptorByName(paramType.name)
// has the template a value for this? If so, set it, otherwise flag as needsParams
print("template:", paramType.id, eventDescriptorTemplate.paramDescriptors.count)
if (paramDescriptorTemplate && paramDescriptorTemplate.value !== undefined) {
print("filling in param descriptor:", paramDescriptorTemplate.value)
eventDescriptor.paramDescriptors.setParamDescriptorByName(paramDescriptorTemplate.paramName, paramDescriptorTemplate.value, paramDescriptorTemplate.operatorType);
} else {
needsParams = true;
}
}
} else if (stateType) {
eventDescriptor.eventTypeId = stateType.id
var paramType = stateType.id
var paramDescriptorTemplate = eventDescriptorTemplate.paramDescriptors.getParamDescriptorByName(stateType.name)
// has the template a value for this? If so, set it, otherwise flag as needsParams
print("template:", paramType.id, eventDescriptorTemplate.paramDescriptors.count)
if (paramDescriptorTemplate && paramDescriptorTemplate.value !== undefined) {

View File

@ -1,157 +1,187 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.4
import QtQuick.Controls 2.1
import "../components"
import Nymea 1.0
Page {
id: root
property alias text: header.text
// an eventDescriptor object needs to be set and prefilled with either thingId or interfaceName
property var eventDescriptor: null
readonly property Thing thing: eventDescriptor && eventDescriptor.thingId ? engine.thingManager.things.getThing(eventDescriptor.thingId) : null
signal backPressed();
signal done();
onEventDescriptorChanged: buildInterface()
Component.onCompleted: buildInterface()
header: NymeaHeader {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: root.eventDescriptor.interfaceName !== ""
onInterfacesModeChanged: root.buildInterface()
HeaderButton {
imageSource: header.interfacesMode ? "../images/view-expand.svg" : "../images/view-collapse.svg"
visible: root.eventDescriptor.interfaceName === ""
onClicked: header.interfacesMode = !header.interfacesMode
}
}
ListModel {
id: generatedModel
ListElement { displayName: ""; eventTypeId: "" }
}
function buildInterface() {
if (header.interfacesMode) {
if (root.thing) {
generatedModel.clear();
for (var i = 0; i < Interfaces.count; i++) {
var iface = Interfaces.get(i);
if (root.thing.thingClass.interfaces.indexOf(iface.name) >= 0) {
for (var j = 0; j < iface.eventTypes.count; j++) {
var ifaceEt = iface.eventTypes.get(j);
var dcEt = root.thing.thingClass.eventTypes.findByName(ifaceEt.name)
generatedModel.append({displayName: ifaceEt.displayName, eventTypeId: dcEt.id})
}
}
}
listView.model = generatedModel
} else if (root.eventDescriptor.interfaceName !== "") {
listView.model = Interfaces.findByName(root.eventDescriptor.interfaceName).eventTypes
} else {
console.warn("You need to set thing or interfaceName");
}
} else {
if (root.thing) {
listView.model = root.thing.thingClass.eventTypes;
}
}
}
ListView {
id: listView
anchors.fill: parent
delegate: ItemDelegate {
width: parent.width
text: model.displayName
onClicked: {
if (header.interfacesMode) {
if (root.thing) {
root.eventDescriptor.eventTypeId = model.eventTypeId;
var eventType = root.thing.thingClass.eventTypes.getEventType(model.eventTypeId)
if (eventType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
} else if (root.eventDescriptor.interfaceName !== "") {
root.eventDescriptor.interfaceEvent = model.name;
if (listView.model.get(index).paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
} else {
console.warn("Neither thingId not interfaceName set. Cannot continue...");
}
} else {
if (root.thing) {
var eventType = root.thing.thingClass.eventTypes.getEventType(model.id);
root.eventDescriptor.eventTypeId = model.id;
if (eventType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
print("have type", eventType.id)
} else {
console.warn("FIXME: not implemented yet");
}
}
}
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.4
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import "../components"
import Nymea 1.0
Page {
id: root
property alias text: header.text
// an eventDescriptor object needs to be set and prefilled with either thingId or interfaceName
property var eventDescriptor: null
readonly property Thing thing: eventDescriptor && eventDescriptor.thingId ? engine.thingManager.things.getThing(eventDescriptor.thingId) : null
signal backPressed();
signal done();
onEventDescriptorChanged: buildInterface()
Component.onCompleted: buildInterface()
header: NymeaHeader {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: root.eventDescriptor.interfaceName !== ""
onInterfacesModeChanged: root.buildInterface()
// HeaderButton {
// imageSource: header.interfacesMode ? "../images/view-expand.svg" : "../images/view-collapse.svg"
// visible: root.eventDescriptor.interfaceName === ""
// onClicked: header.interfacesMode = !header.interfacesMode
// }
}
ListModel {
id: generatedModel
ListElement { displayName: ""; eventTypeId: "" }
}
function buildInterface() {
if (header.interfacesMode) {
if (root.thing) {
generatedModel.clear();
for (var i = 0; i < Interfaces.count; i++) {
var iface = Interfaces.get(i);
if (root.thing.thingClass.interfaces.indexOf(iface.name) >= 0) {
for (var j = 0; j < iface.eventTypes.count; j++) {
var ifaceEt = iface.eventTypes.get(j);
var dcEt = root.thing.thingClass.eventTypes.findByName(ifaceEt.name)
if (!dcEt) {
dcEt = root.thing.thingClass.stateTypes.findByName(ifaceEt.name)
}
generatedModel.append({displayName: ifaceEt.displayName, eventTypeId: dcEt.id})
}
}
}
listView.model = generatedModel
} else if (root.eventDescriptor.interfaceName !== "") {
listView.model = Interfaces.findByName(root.eventDescriptor.interfaceName).eventTypes
} else {
console.warn("You need to set thing or interfaceName");
}
} else {
if (root.thing) {
generatedModel.clear();
for (var i = 0; i < root.thing.thingClass.stateTypes.count; i++) {
var stateType = root.thing.thingClass.stateTypes.get(i)
generatedModel.append({displayName: stateType.displayName, id: stateType.id.toString(), type: "state"})
}
for (var i = 0; i < root.thing.thingClass.eventTypes.count; i++) {
var eventType = root.thing.thingClass.eventTypes.get(i)
generatedModel.append({displayName: eventType.displayName, id: eventType.id.toString(), type: "event"})
}
listView.model = generatedModel;
}
}
}
ListView {
id: listView
anchors.fill: parent
clip: true
section.property: "type"
section.delegate: ListSectionHeader {
text: section === "state" ? qsTr("State change") : qsTr("Event")
}
delegate: ItemDelegate {
width: parent.width
text: model.displayName
onClicked: {
if (header.interfacesMode) {
if (root.thing) {
root.eventDescriptor.eventTypeId = model.eventTypeId;
var eventType = root.thing.thingClass.eventTypes.getEventType(model.eventTypeId)
if (eventType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
} else if (root.eventDescriptor.interfaceName !== "") {
root.eventDescriptor.interfaceEvent = model.name;
if (listView.model.get(index).paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
} else {
console.warn("Neither thingId not interfaceName set. Cannot continue...");
}
} else {
if (root.thing) {
root.eventDescriptor.eventTypeId = model.id;
if (model.type === "state") {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else if (model.type === "event") {
var eventType = root.thing.thingClass.eventTypes.getEventType(model.id);
if (eventType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
}
}
} else {
console.warn("FIXME: not implemented yet");
}
}
}
}
}
}

View File

@ -42,7 +42,9 @@ Page {
readonly property Thing thing: eventDescriptor && eventDescriptor.thingId ? engine.thingManager.things.getThing(eventDescriptor.thingId) : null
readonly property var iface: eventDescriptor && eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
readonly property var eventType: thing ? thing.thingClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
readonly property EventType eventType: thing ? thing.thingClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
signal backPressed();
@ -57,16 +59,17 @@ Page {
anchors.fill: parent
Repeater {
id: delegateRepeater
model: root.eventType.paramTypes
model: root.eventType ? root.eventType.paramTypes : 1
delegate: ColumnLayout {
Layout.fillWidth: true
property alias paramType: paramDescriptorDelegate.paramType
property alias stateType: paramDescriptorDelegate.stateType
property alias value: paramDescriptorDelegate.value
property alias considerParam: paramCheckBox.checked
property alias operatorType: paramDescriptorDelegate.operatorType
CheckBox {
id: paramCheckBox
text: qsTr("Only consider event if")
text: root.eventType ? qsTr("Only consider event if") : qsTr("Only consider state change if")
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
@ -76,8 +79,9 @@ Page {
id: paramDescriptorDelegate
enabled: paramCheckBox.checked
Layout.fillWidth: true
paramType: root.eventType.paramTypes.get(index)
value: paramType.defaultValue
paramType: root.eventType ? root.eventType.paramTypes.get(index) : null
stateType: root.stateType
value: paramType ? paramType.defaultValue : stateType.defaultValue
}
}
}
@ -95,10 +99,13 @@ Page {
var paramDelegate = delegateRepeater.itemAt(i);
if (paramDelegate.considerParam) {
if (root.thing) {
root.eventDescriptor.paramDescriptors.setParamDescriptor(paramDelegate.paramType.id, paramDelegate.value, paramDelegate.operatorType)
var paramTypeId = paramDelegate.paramType ? paramDelegate.paramType.id : paramDelegate.stateType.id
print("setting param descriptor by id:", paramTypeId, paramDelegate.value)
root.eventDescriptor.paramDescriptors.setParamDescriptor(paramTypeId, paramDelegate.value, paramDelegate.operatorType)
} else if (root.iface) {
print("setting param descriptors by name", root.eventType.paramTypes.get(i), root.eventType.paramTypes.get(i).name)
root.eventDescriptor.paramDescriptors.setParamDescriptorByName(root.eventType.paramTypes.get(i).name, paramDelegate.value, paramDelegate.operatorType)
var name = root.eventType ? root.eventType.paramTypes.get(i).name : root.stateType.name
print("setting param descriptors by name", name, paramDelegate.value)
root.eventDescriptor.paramDescriptors.setParamDescriptorByName(name, paramDelegate.value, paramDelegate.operatorType)
}
}
}

View File

@ -117,15 +117,14 @@ Page {
ThinDivider { Layout.columnSpan: parent.columns }
ParamDelegate {
StateDelegate {
id: staticValueParamDelegate
Layout.fillWidth: true
hoverEnabled: false
padding: 0
paramType: root.thing.thingClass.eventTypes.getEventType(root.stateType.id).paramTypes.getParamType(root.stateType.id)
stateType: root.stateType
enabled: staticValueRadioButton.checked
nameVisible: false
value: root.stateType.defaultValue
visible: staticValueRadioButton.checked
placeholderText: qsTr("Insert value here")
}

View File

@ -0,0 +1,101 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, GNU version 3. This project is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import "../components"
import "../delegates"
import Nymea 1.0
Page {
id: root
// Needs to be set and filled in with thingId and eventTypeId
property var eventDescriptor: null
readonly property Thing thing: eventDescriptor && eventDescriptor.thingId ? engine.thingManager.things.getThing(eventDescriptor.thingId) : null
readonly property var iface: eventDescriptor && eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
readonly property var stateType: thing ? thing.thingClass.stateTypes.getStateType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
signal backPressed();
signal completed();
header: NymeaHeader {
text: "Options"
onBackPressed: root.backPressed();
}
ColumnLayout {
anchors.fill: parent
ColumnLayout {
Layout.fillWidth: true
property alias paramType: paramDescriptorDelegate.paramType
property alias value: paramDescriptorDelegate.value
property alias considerParam: paramCheckBox.checked
property alias operatorType: paramDescriptorDelegate.operatorType
CheckBox {
id: paramCheckBox
text: qsTr("Only consider state change if")
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
}
ParamDescriptorDelegate {
id: paramDescriptorDelegate
enabled: paramCheckBox.checked
Layout.fillWidth: true
stateType: root.stateType
value: stateType.defaultValue
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
Button {
text: "OK"
Layout.fillWidth: true
Layout.margins: app.margins
onClicked: {
root.eventDescriptor.paramDescriptors.clear();
if (paramDelegate.considerParam) {
if (root.thing) {
root.eventDescriptor.paramDescriptors.setParamDescriptor(root.stateType.id, paramDescriptorDelegate.value, paramDescriptorDelegate.operatorType)
} else if (root.iface) {
root.eventDescriptor.paramDescriptors.setParamDescriptorByName(root.stateType.name, paramDescriptorDelegate.value, paramDescriptorDelegate.operatorType)
}
}
root.completed()
}
}
}
}

View File

@ -119,7 +119,7 @@ Page {
print("new checked state;", newCache[thingId])
}
delegate: NymeaSwipeDelegate {
delegate: NymeaItemDelegate {
width: parent.width
text: root.selectInterface ? model.displayName : model.name
iconName: root.selectInterface ? app.interfaceToIcon(model.name) : app.interfacesToIcon(model.interfaces)