This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/magic/EditRulePage.qml
2025-05-12 09:09:04 +02:00

906 lines
37 KiB
QML

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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.3
import QtQuick.Controls 2.1
import "../components"
import Nymea 1.0
Page {
id: root
property Rule rule: null
property Thing initialDeviceToBeAdded: null
property bool busy: false
readonly property bool isEventBased: rule.eventDescriptors.count > 0 || rule.timeDescriptor.timeEventItems.count > 0
readonly property bool isStateBased: (rule.stateEvaluator !== null || rule.timeDescriptor.calendarItems.count > 0)
readonly property bool actionsVisible: true
readonly property bool exitActionsVisible: isStateBased
readonly property bool hasActions: rule.actions.count > 0
readonly property bool hasExitActions: rule.exitActions.count > 0
readonly property bool isEmpty: !isEventBased && !isStateBased && !hasActions
property string ruleIcon: engine.tagsManager.tags.findRuleTag(rule.id, "icon").value
property string ruleColor: engine.tagsManager.tags.findRuleTag(rule.id, "color").value
signal accept();
signal cancel();
Component.onCompleted: print("+++ created editrulepage")
Component.onDestruction: {
print("--- destroying editrulepage")
d.backupRule.destroy();
}
onRuleChanged: d.createRuleBackup();
QtObject {
id: d
property Rule backupRule: null
function createRuleBackup() {
if (backupRule !== null) {
backupRule.destroy();
}
backupRule = root.rule.clone();
}
}
function addEventDescriptor(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showEvents: true});
page.onBackPressed.connect(function() {
pageStack.pop();
});
page.onThingSelected.connect(function(thing) {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.thingId = thing.id;
selectEventDescriptorData(eventDescriptor);
})
page.onInterfaceSelected.connect(function(interfaceName) {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.interfaceName = interfaceName;
selectEventDescriptorData(eventDescriptor);
})
}
function addInterfaceEventDescriptor() {
addEventDescriptor(true);
}
function selectEventDescriptorData(eventDescriptor, destroyOnCancel) {
if (destroyOnCancel == undefined) {
destroyOnCancel = true
}
var eventPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorPage.qml"), {text: "Select event", eventDescriptor: eventDescriptor});
eventPage.onBackPressed.connect(function() {
eventPage.StackView.onRemoved.connect(function() {
if (destroyOnCancel) {
print("destroying event descriptor")
eventDescriptor.destroy();
}
});
pageStack.pop();
})
eventPage.onDone.connect(function() {
root.rule.eventDescriptors.addEventDescriptor(eventPage.eventDescriptor);
pageStack.pop(root)
})
}
function addTimeEventItem() {
var timeEventItem = root.rule.timeDescriptor.timeEventItems.createNewTimeEventItem();
var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: timeEventItem});
page.onBackPressed.connect(function() {
page.StackView.onRemoved.connect(function() {
timeEventItem.destroy();
});
pageStack.pop()
})
page.onDone.connect(function() {
root.rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEventItem);
pageStack.pop(root);
})
}
function addCalendarItem() {
var calendarItem = root.rule.timeDescriptor.calendarItems.createNewCalendarItem();
var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: calendarItem});
page.onBackPressed.connect(function() {
page.StackView.onRemoved.connect(function() {
calendarItem.destroy();
});
pageStack.pop();
})
page.onDone.connect(function() {
root.rule.timeDescriptor.calendarItems.addCalendarItem(calendarItem);
pageStack.pop(root);
})
}
function createStateEvaluator(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showStates: true});
page.backPressed.connect(function() {
pageStack.pop();
});
page.interfaceSelected.connect(function(interfaceName) {
var stateEvaluator = root.rule.createStateEvaluator();
stateEvaluator.stateDescriptor.interfaceName = interfaceName;
selectStateDescriptorData(stateEvaluator)
});
page.thingSelected.connect(function(thing) {
var stateEvaluator = root.rule.createStateEvaluator();
stateEvaluator.stateDescriptor.thingId = thing.id
selectStateDescriptorData(stateEvaluator)
})
}
function selectStateDescriptorData(stateEvaluator) {
print("Selecting stateDescriptorData for", stateEvaluator.stateDescriptor.thingId, stateEvaluator.stateDescriptor.interfaceName)
var statePage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorPage.qml"), {text: "Select state", stateDescriptor: stateEvaluator.stateDescriptor})
statePage.backPressed.connect(function() {
statePage.StackView.onRemoved.connect(function() {
stateEvaluator.destroy();
})
pageStack.pop()
})
statePage.done.connect(function() {
root.rule.setStateEvaluator(stateEvaluator)
pageStack.pop(root);
})
}
function createInterfaceStateEvaluator() {
createStateEvaluator(true)
}
function editStateEvaluator() {
print("opening page", root.rule.stateEvaluator)
var page = pageStack.push(Qt.resolvedUrl("EditStateEvaluatorPage.qml"), { stateEvaluator: root.rule.stateEvaluator })
}
function addRuleAction(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showActions: true});
page.onBackPressed.connect(function() {
pageStack.pop();
})
page.onThingSelected.connect(function(thing) {
var ruleAction = root.rule.actions.createNewRuleAction();
ruleAction.thingId = thing.id;
selectRuleActionData(root.rule.actions, ruleAction)
})
page.onInterfaceSelected.connect(function(interfaceName) {
var ruleAction = root.rule.actions.createNewRuleAction();
ruleAction.interfaceName = interfaceName;
selectRuleActionData(root.rule.actions, ruleAction)
})
}
function addInterfaceRuleAction() {
addRuleAction(true);
}
function addRuleExitAction(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showActions: true});
page.onBackPressed.connect(function() {
pageStack.pop();
})
page.onThingSelected.connect(function(thing) {
var ruleAction = root.rule.exitActions.createNewRuleAction();
ruleAction.thingId = thing.id;
selectRuleActionData(root.rule.exitActions, ruleAction)
})
page.onInterfaceSelected.connect(function(interfaceName) {
var ruleAction = root.rule.exitActions.createNewRuleAction();
ruleAction.interfaceName = interfaceName;
selectRuleActionData(root.rule.exitActions, ruleAction)
})
}
function addInterfaceRuleExitAction() {
addRuleExitAction(true);
}
function selectRuleActionData(ruleActions, ruleAction) {
var ruleActionPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionPage.qml"), {text: "Select action", ruleAction: ruleAction, rule: rule });
ruleActionPage.onBackPressed.connect(function() {
ruleActionPage.StackView.onRemoved.connect(function() {
ruleAction.destroy();
});
pageStack.pop();
})
ruleActionPage.onDone.connect(function() {
print("Adding rule action", ruleAction)
ruleActions.addRuleAction(ruleAction)
pageStack.pop(root);
})
}
function startAddAction() {
if (root.isEmpty) {
root.rule.executable = true;
}
if (root.initialDeviceToBeAdded !== null) {
var ruleAction = root.rule.actions.createNewRuleAction();
ruleAction.thingId = root.initialDeviceToBeAdded.id;
root.initialDeviceToBeAdded = null;
selectRuleActionData(root.rule.actions, ruleAction)
} else {
var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: false});
}
}
header: NymeaHeader {
text: root.rule.name.length === 0 ? qsTr("Add new magic") : qsTr("Edit %1").arg(root.rule.name)
onBackPressed: {
if (root.rule.compare(d.backupRule)) {
print("Rule has not been changed. Exiting EditRulePage");
root.cancel();
return;
}
print("Rule has changed. Asking for cancellation dialog")
var component = Qt.createComponent(Qt.resolvedUrl("../components/NymeaDialog.qml"));
var popup = component.createObject(root, {headerIcon: "qrc:/icons/question.svg",
title: qsTr("Cancel?"),
text: qsTr("Any changes to the rule will be lost."),
standardButtons: Dialog.Yes | Dialog.No
})
popup.accepted.connect(function() {
root.cancel();
})
popup.open();
}
HeaderButton {
imageSource: "qrc:/icons/tick.svg"
enabled: actionsRepeater.count > 0 && root.rule.name.length > 0
opacity: enabled ? 1 : .3
onClicked: root.accept()
}
}
Flickable {
anchors.fill: parent
contentHeight: contentColumn.implicitHeight + app.margins
clip: true
ColumnLayout {
id: contentColumn
anchors { left: parent.left; top: parent.top; right: parent.right; }
ColumnLayout {
id: ruleSettings
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
Layout.topMargin: app.margins
visible: !root.isEmpty
property bool showDetails: false
RowLayout {
Layout.fillWidth: true
spacing: app.margins
Label {
text: qsTr("Name:")
}
TextField {
Layout.fillWidth: true
text: root.rule.name
onTextChanged: root.rule.name = text;
}
ColorIcon {
name: "qrc:/icons/settings.svg"
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
MouseArea {
anchors.fill: parent
onClicked: ruleSettings.showDetails = !ruleSettings.showDetails
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: ruleSettings.showDetails ? implicitHeight : 0
opacity: ruleSettings.showDetails ? 1 : 0
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
Label {
Layout.fillWidth: true
text: qsTr("This rule is enabled")
}
CheckBox {
checked: root.rule.enabled
onClicked: {
root.rule.enabled = checked
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: ruleSettings.showDetails ? implicitHeight : 0
opacity: ruleSettings.showDetails ? 1 : 0
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
Label {
Layout.fillWidth: true
text: qsTr("This is a scene")
}
CheckBox {
checked: root.rule.executable
onClicked: {
root.rule.executable = checked
}
}
}
GridLayout {
id: colorsGrid
Layout.fillWidth: true
columns: (root.width / 10 < Style.iconSize + app.margins) ? 5 : 10
columnSpacing: app.margins
rowSpacing: app.margins
Layout.preferredHeight: opacity > 0 ? implicitHeight : 0
opacity: ruleSettings.showDetails && root.rule.executable ? 1 : 0
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
Repeater {
model: ["red", "orange", "yellow", "lime", "green", "aqua", "skyblue", "blue", "magenta", "purple"]
delegate: Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.iconSize + app.margins
Rectangle {
height: parent.height
width: height
color: "transparent"
border.width: 2
border.color: modelData === root.ruleColor ? Style.accentColor : "transparent"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
root.ruleColor = modelData
}
}
ColorIcon {
height: Style.iconSize
width: Style.iconSize
color: modelData
name: "qrc:/icons/" + (root.ruleIcon ? root.ruleIcon : "slideshow") + ".svg"
anchors.centerIn: parent
}
}
}
Repeater {
model: ["light-on", "light-off", "alarm-clock", "media-playback-start", "connections/network-secure", "notification", "sensors", "shutter/shutter-050", "attention", "eye"]
delegate: Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.iconSize + app.margins
Rectangle {
height: parent.height
width: height
color: "transparent"
border.width: 2
border.color: modelData === root.ruleIcon ? Style.accentColor : "transparent"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
root.ruleIcon = modelData
}
}
ColorIcon {
height: Style.iconSize
width: Style.iconSize
color: root.ruleColor
name: "qrc:/icons/" + modelData + ".svg"
anchors.centerIn: parent
}
}
}
}
}
ThinDivider { visible: root.isEmpty || root.isEventBased }
Label {
Layout.fillWidth: true
Layout.margins: app.margins
font.pixelSize: app.mediumFont
wrapMode: Text.WordWrap
text: eventsRepeater.count === 0 && timeEventRepeater.count === 0 && actionsRepeater.count === 0 ?
qsTr("Execute actions when something happens.") :
qsTr("When any of these events happen...")
visible: root.isEmpty || root.isEventBased
font.bold: true
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
text: qsTr("Examples:\n• When a button is pressed...\n• When the temperature changes...\n• At 7 am...")
visible: root.isEmpty
}
Repeater {
id: eventsRepeater
model: root.rule.eventDescriptors
delegate: EventDescriptorDelegate {
Layout.fillWidth: true
implicitWidth: parent.width
eventDescriptor: root.rule.eventDescriptors.get(index)
onRemoveEventDescriptor: root.rule.eventDescriptors.removeEventDescriptor(index)
onClicked: {
selectEventDescriptorData(eventDescriptor, false)
}
}
}
Repeater {
id: timeEventRepeater
model: root.rule.timeDescriptor.timeEventItems
delegate: TimeEventDelegate {
Layout.fillWidth: true
timeEventItem: root.rule.timeDescriptor.timeEventItems.get(index)
onRemoveTimeEventItem: {
root.rule.timeDescriptor.timeEventItems.removeTimeEventItem(index);
}
}
}
Button {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
text: eventsRepeater.count == 0 && timeEventRepeater.count === 0 ? qsTr("Configure...") : qsTr("Add another...")
visible: root.isEmpty || root.isEventBased
onClicked: {
if (root.initialDeviceToBeAdded !== null) {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.thingId = root.initialDeviceToBeAdded.id;
root.initialDeviceToBeAdded = null;
selectEventDescriptorData(eventDescriptor);
} else if (root.rule.timeDescriptor.calendarItems.count > 0) {
root.addEventDescriptor()
} else {
pageStack.push(eventQuestionPageComponent)
}
}
}
ThinDivider {}
Label {
text: root.isEmpty ?
qsTr("Do something while a condition is met.") :
root.isEventBased ?
qsTr("...but only if those conditions are met...") :
qsTr("When this condition...")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
font.bold: true
visible: {
if (root.isEventBased) {
if (root.rule.timeDescriptor.calendarItems.count === 0) {
return true;
}
if (root.rule.stateEvaluator === null) {
return false;
}
} else {
if (root.rule.stateEvaluator === null && root.rule.timeDescriptor.calendarItems.count > 0) {
return false;
}
}
return true;
}
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
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
}
StateEvaluatorDelegate {
Layout.fillWidth: true
stateEvaluator: root.rule.stateEvaluator
visible: root.rule.stateEvaluator !== null
onDeleteClicked: {
root.rule.stateEvaluator = null
}
}
Label {
text: root.rule.stateEvaluator === null && !root.isEventBased ?
qsTr("When time is in...") :
qsTr("...during this time...")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
font.bold: true
visible: root.rule.timeDescriptor.timeEventItems.count === 0 && (root.rule.timeDescriptor.calendarItems.count > 0 || root.rule.stateEvaluator !== null)
}
Repeater {
model: root.rule.timeDescriptor.calendarItems
delegate: CalendarItemDelegate {
Layout.fillWidth: true
calendarItem: root.rule.timeDescriptor.calendarItems.get(index)
onRemoveCalendarItem: {
root.rule.timeDescriptor.calendarItems.removeCalendarItem(index)
}
}
}
Button {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
text: root.rule.stateEvaluator === null || root.rule.timeDescriptor.calendarItems.count === 0 ?
qsTr("Configure...") :
qsTr("Add another...")
visible: root.rule.timeDescriptor.timeEventItems.count === 0 || root.rule.stateEvaluator === null
onClicked: {
if (root.initialDeviceToBeAdded !== null) {
var stateEvaluator = root.rule.createStateEvaluator();
stateEvaluator.stateDescriptor.thingId = root.initialDeviceToBeAdded.id
root.initialDeviceToBeAdded = null;
selectStateDescriptorData(stateEvaluator)
} else if (root.rule.timeDescriptor.timeEventItems.count > 0) {
root.rule.setStateEvaluator(root.rule.createStateEvaluator());
} else if (root.rule.stateEvaluator !== null) {
root.addCalendarItem();
} else {
pageStack.push(stateQuestionPageComponent)
}
}
}
ThinDivider { visible: root.actionsVisible }
Label {
text: root.isEmpty ? qsTr("Create a scene.")
: root.isEventBased ? qsTr("...execute those actions:")
: root.isStateBased ? qsTr("...come true, execute those actions:")
: qsTr("Execute those actions:")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
visible: root.actionsVisible
font.bold: true
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
text: qsTr("Just pick some actions which will be executed when the scene is activated. Scenes are like any other magic except they can also be activated manually.")
visible: root.isEmpty
}
Repeater {
id: actionsRepeater
model: root.actionsVisible ? root.rule.actions : null
delegate: RuleActionDelegate {
Layout.fillWidth: true
implicitWidth: parent.width
ruleAction: root.rule.actions.get(index)
onRemoveRuleAction: root.rule.actions.removeRuleAction(index)
onClicked: {
var ruleActionPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionPage.qml"), {text: "Select action", ruleAction: ruleAction, rule: root.rule });
ruleActionPage.onBackPressed.connect(function() {
pageStack.pop();
})
ruleActionPage.onDone.connect(function() {
pageStack.pop(root);
})
}
}
}
Button {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
text: root.isEmpty ? qsTr("Configure...") :
actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
onClicked: {
root.startAddAction();
}
visible: root.actionsVisible
}
ThinDivider { visible: root.exitActionsVisible }
Label {
text: root.isEventBased ? qsTr("If the condition isn't met, execute those actions instead:") : qsTr("When the condition isn't met any more, execute those actions:")
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
visible: root.exitActionsVisible
font.pixelSize: app.mediumFont
font.bold: true
}
Repeater {
id: exitActionsRepeater
model: root.exitActionsVisible ? root.rule.exitActions : null
delegate: RuleActionDelegate {
Layout.fillWidth: true
implicitWidth: parent.width
ruleAction: root.rule.exitActions.get(index)
onRemoveRuleAction: root.rule.exitActions.removeRuleAction(index)
onClicked: {
var ruleActionPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionPage.qml"), {text: "Select action", ruleAction: ruleAction, rule: root.rule });
ruleActionPage.onBackPressed.connect(function() {
pageStack.pop();
})
ruleActionPage.onDone.connect(function() {
pageStack.pop(root);
})
}
}
}
Button {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins
text: exitActionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
onClicked: {
var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: true});
}
visible: root.exitActionsVisible
}
}
}
Rectangle {
id: busyOverlay
anchors.fill: parent
color: "#55000000"
opacity: root.busy ? 1 : 0
Behavior on opacity { NumberAnimation {duration: 200 } }
BusyIndicator {
anchors.centerIn: parent
running: parent.opacity > 0
}
}
Component {
id: eventQuestionPageComponent
Page {
header: NymeaHeader {
text: qsTr("Add event")
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: ListModel {
ListElement {
iconName: "qrc:/icons/event.svg"
text: qsTr("When one of my things triggers an event")
method: "addEventDescriptor"
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "qrc:/icons/event-interface.svg"
text: qsTr("When a thing of a given type triggers an event")
method: "addInterfaceEventDescriptor"
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "qrc:/icons/alarm-clock.svg"
text: qsTr("At a particular time or date")
method: "addTimeEventItem"
minimumJsonRpcVersion: "1.0"
}
}
delegate: NymeaItemDelegate {
Layout.fillWidth: true
Layout.preferredHeight: Style.largeDelegateHeight
iconName: model.iconName
text: model.text
progressive: true
iconSize: Style.iconSize * 2
visible: engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
}
}
}
Component {
id: stateQuestionPageComponent
Page {
header: NymeaHeader {
text: qsTr("Add condition...")
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: ListModel {
ListElement {
iconName: "qrc:/icons/state.svg"
text: qsTr("When one of my things is in a certain state")
method: "createStateEvaluator"
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "qrc:/icons/state-interface.svg"
text: qsTr("When a thing of a given type enters a state")
method: "createInterfaceStateEvaluator"
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "qrc:/icons/clock-app-symbolic.svg"
text: qsTr("During a given time")
method: "addCalendarItem"
minimumJsonRpcVersion: "1.0"
}
}
delegate: NymeaItemDelegate {
Layout.fillWidth: true
Layout.preferredHeight: Style.largeDelegateHeight
iconName: model.iconName
text: model.text
progressive: true
iconSize: Style.iconSize * 2
visible: engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
}
}
}
Component {
id: ruleActionQuestionPageComponent
Page {
id: ruleActionQuestionPage
property bool exitAction: false
header: NymeaHeader {
text: qsTr("Add action...")
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: ListModel {
ListElement {
iconName: "qrc:/icons/action.svg"
text: qsTr("Execute an action on one of my things")
method: "addRuleAction"
isExitAction: false
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "qrc:/icons/action-interface.svg"
text: qsTr("Execute an action on an entire kind of things")
method: "addInterfaceRuleAction"
isExitAction: false
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "qrc:/icons/action.svg"
text: qsTr("Execute an action on one of my things")
method: "addRuleExitAction"
isExitAction: true
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "qrc:/icons/action-interface.svg"
text: qsTr("Execute an action on an entire kind of things")
method: "addInterfaceRuleExitAction"
isExitAction: true
minimumJsonRpcVersion: "1.5"
}
}
delegate: NymeaSwipeDelegate {
Layout.fillWidth: true
Layout.preferredHeight: Style.largeDelegateHeight
iconName: model.iconName
text: model.text
progressive: true
iconSize: Style.iconSize * 2
visible: ruleActionQuestionPage.exitAction === model.isExitAction && engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
}
}
}
}