diff --git a/libnymea-app/types/interfaces.cpp b/libnymea-app/types/interfaces.cpp
index a9efdec5..c4686643 100644
--- a/libnymea-app/types/interfaces.cpp
+++ b/libnymea-app/types/interfaces.cpp
@@ -278,6 +278,9 @@ Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
addInterface("presencesensor", tr("Presence sensors"), {"sensor"});
addStateType("presencesensor", "isPresent", QVariant::Bool, false, tr("Is present"), tr("Presence changed"));
+ addInterface("vibrationsensor", tr("Vibration sensors"), {"sensor"});
+ addEventType("vibrationsensor", "vibrationDetected", tr("Vibration detected"), new ParamTypes());
+
addInterface("pressuresensor", tr("Pressure sensors"), {"sensor"});
addStateType("pressuresensor", "pressure", QVariant::Double, false, tr("Pressure"), tr("Pressure changed"));
diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc
index b6fe22ac..bd532586 100644
--- a/nymea-app/images.qrc
+++ b/nymea-app/images.qrc
@@ -298,5 +298,6 @@
ui/images/arrow-up.svg
ui/images/plus.svg
ui/images/minus.svg
+ ui/images/sensors/vibration.svg
diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml
index 26308795..7d55201a 100644
--- a/nymea-app/ui/Nymea.qml
+++ b/nymea-app/ui/Nymea.qml
@@ -323,6 +323,8 @@ ApplicationWindow {
return Qt.resolvedUrl("images/sensors/windspeed.svg")
case "watersensor":
return Qt.resolvedUrl("images/sensors/water.svg")
+ case "vibrationsensor":
+ return Qt.resolvedUrl("images/sensors/vibration.svg")
case "waterlevelsensor":
return Qt.resolvedUrl("images/sensors/water.svg")
case "firesensor":
@@ -509,6 +511,9 @@ ApplicationWindow {
case "presencesensor":
//: Select ...
return qsTr("presence sensor")
+ case "vibrationsensor":
+ //: Select ...
+ return qsTr("vibration sensor");
case "doorbell":
//: Select ...
return qsTr("doorbell")
diff --git a/nymea-app/ui/StyleBase.qml b/nymea-app/ui/StyleBase.qml
index d73e83dc..9d44bb75 100644
--- a/nymea-app/ui/StyleBase.qml
+++ b/nymea-app/ui/StyleBase.qml
@@ -102,6 +102,7 @@ Item {
"gassensor": orange,
"daylightsensor": yellow,
"presencesensor": darkBlue,
+ "vibrationsensor": orange,
"closablesensor": green,
"smartmeterconsumer": orange,
"smartmeterproducer": lime,
diff --git a/nymea-app/ui/components/NymeaItemDelegate.qml b/nymea-app/ui/components/NymeaItemDelegate.qml
index 7503693b..0a63f523 100644
--- a/nymea-app/ui/components/NymeaItemDelegate.qml
+++ b/nymea-app/ui/components/NymeaItemDelegate.qml
@@ -71,8 +71,6 @@ ItemDelegate {
signal deleteClicked()
signal secondaryIconClicked()
- onPressAndHold: swipe.open(SwipeDelegate.Right)
-
QtObject {
id: d
property var deleteContextOption: [{
diff --git a/nymea-app/ui/components/NymeaSwipeDelegate.qml b/nymea-app/ui/components/NymeaSwipeDelegate.qml
index 1e80e73f..3043815d 100644
--- a/nymea-app/ui/components/NymeaSwipeDelegate.qml
+++ b/nymea-app/ui/components/NymeaSwipeDelegate.qml
@@ -202,7 +202,7 @@ SwipeDelegate {
swipe.enabled: {
for (var i = 0; i < d.finalContextOptions.length; i++) {
- if (d.finalContextOptions[i].visible) {
+ if (!d.finalContextOptions[i].hasOwnProperty("visible") || d.finalContextOptions[i].visible) {
return true
}
}
@@ -215,7 +215,16 @@ SwipeDelegate {
id: swipeComponent
Item {
height: parent.height
- width: height * d.finalContextOptions.length
+ width: {
+ var count = 0
+ for (var i = 0; i < d.finalContextOptions.length; i++) {
+ if (!d.finalContextOptions[i].hasOwnProperty("visible") || d.finalContextOptions[i].visible) {
+ count++;
+ }
+ }
+ return height * count
+ }
+
anchors.right: parent.right
Item {
anchors {
diff --git a/nymea-app/ui/customviews/GenericTypeLogView.qml b/nymea-app/ui/customviews/GenericTypeLogView.qml
index 092bb318..a98a953b 100644
--- a/nymea-app/ui/customviews/GenericTypeLogView.qml
+++ b/nymea-app/ui/customviews/GenericTypeLogView.qml
@@ -53,12 +53,6 @@ Item {
SwipeDelegateGroup {}
- onContentYChanged: {
- if (!logsModel.busy && contentY - originY < 5 * height) {
- logsModel.fetchEarlier(24)
- }
- }
-
delegate: NymeaSwipeDelegate {
id: logEntryDelegate
width: parent.width
diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml
index 8a3b5e23..c6b34d54 100644
--- a/nymea-app/ui/delegates/InterfaceTile.qml
+++ b/nymea-app/ui/delegates/InterfaceTile.qml
@@ -314,6 +314,7 @@ MainPageTile {
ListElement { ifaceName: "pressuresensor"; stateName: "pressure" }
ListElement { ifaceName: "daylightsensor"; stateName: "daylight" }
ListElement { ifaceName: "presencesensor"; stateName: "isPresent" }
+ ListElement { ifaceName: "vibfrationsensor"; stateName: "" }
ListElement { ifaceName: "closablesensor"; stateName: "closed" }
ListElement { ifaceName: "lightsensor"; stateName: "lightIntensity" }
ListElement { ifaceName: "watersensor"; stateName: "waterDetected" }
diff --git a/nymea-app/ui/delegates/ThingTile.qml b/nymea-app/ui/delegates/ThingTile.qml
index ec0ca55e..24cbe5a1 100644
--- a/nymea-app/ui/delegates/ThingTile.qml
+++ b/nymea-app/ui/delegates/ThingTile.qml
@@ -256,6 +256,9 @@ MainPageTile {
if (thing.thingClass.interfaces.indexOf("presencesensor") >= 0) {
tmp.push({iface: "presencesensor", state: "isPresent"});
}
+ if (thing.thingClass.interfaces.indexOf("vibrationsensor") >= 0) {
+ tmp.push({iface: "vibrationsensor", state: "vibrationDetected"});
+ }
if (thing.thingClass.interfaces.indexOf("phsensor") >= 0) {
tmp.push({iface: "phsensor", state: "ph"})
}
diff --git a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
index b15c74df..0dc4070b 100644
--- a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
@@ -65,7 +65,15 @@ ThingsListPageBase {
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
- onClicked: enterPage(index)
+ onClicked: {
+ // we show all "sensors" in shownInterfaces in here so the "sensors" view would be the best match, but for sensors
+ // that should show the input trigger view instead, we need to override that
+ if (thing.thingClass.interfaces.indexOf("vibrationsensor") >= 0) {
+ enterPage(index, "inputtrigger")
+ } else {
+ enterPage(index)
+ }
+ }
contentItem: GridLayout {
id: dataGrid
@@ -85,6 +93,7 @@ ThingsListPageBase {
ListElement { interfaceName: "gassensor"; stateName: "gasLevel" }
ListElement { interfaceName: "daylightsensor"; stateName: "daylight" }
ListElement { interfaceName: "presencesensor"; stateName: "isPresent" }
+ ListElement { interfaceName: "vibrationsensor"; stateName: ""; eventName: "vibrationDetected" }
ListElement { interfaceName: "closablesensor"; stateName: "closed" }
ListElement { interfaceName: "heating"; stateName: "power" }
ListElement { interfaceName: "thermostat"; stateName: "targetTemperature" }
@@ -109,6 +118,16 @@ ThingsListPageBase {
property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
+ property EventType eventType: itemDelegate.thing.thingClass.eventTypes.findByName(model.eventName)
+ LogsModel {
+ id: eventLogsModel
+ engine: sensorValueDelegate.eventType != null ? _engine : null
+ thingId: itemDelegate.thing.id
+ typeIds: sensorValueDelegate.eventType != null ? [sensorValueDelegate.eventType.id] : []
+ live: true
+ fetchBlockSize: 1
+ }
+
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: height
@@ -150,6 +169,13 @@ ThingsListPageBase {
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Fire") : qsTr("No fire");
case "heating":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("On") : qsTr("Off");
+ case "vibrationsensor": {
+ if (eventLogsModel.count > 0) {
+ return qsTr("Last vibration: %1").arg(eventLogsModel.get(0).timestamp.toLocaleString(Qt.locale(), Locale.ShortFormat))
+ } else {
+ return qsTr("Not moved yet")
+ }
+ }
default:
return sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool"
? sensorValueDelegate.stateType.displayName
@@ -171,6 +197,7 @@ ThingsListPageBase {
Layout.preferredWidth: led.width
visible: led.visible
}
+
}
}
}
diff --git a/nymea-app/ui/devicepages/InputTriggerDevicePage.qml b/nymea-app/ui/devicepages/InputTriggerDevicePage.qml
index ec591239..fedc60a0 100644
--- a/nymea-app/ui/devicepages/InputTriggerDevicePage.qml
+++ b/nymea-app/ui/devicepages/InputTriggerDevicePage.qml
@@ -38,52 +38,93 @@ import "../customviews"
ThingPageBase {
id: root
- GenericTypeLogView {
- id: logView
- anchors.fill: parent
+ property var interfaceEventMap: {
+ "inputtrigger": "triggered",
+ "vibrationsensor": "vibrationDetected"
+ }
- logsModel: logsModelNg
- LogsModelNg {
- id: logsModelNg
- engine: _engine
- thingId: root.thing.id
- live: true
- typeIds: [root.thing.thingClass.eventTypes.findByName("triggered").id];
+ readonly property string interfaceName: {
+ var supportedInterfaces = Object.keys(interfaceEventMap)
+ for (var i = 0; i < supportedInterfaces.length; i++) {
+ if (root.thing.thingClass.interfaces.indexOf(supportedInterfaces[i]) >= 0) {
+ return supportedInterfaces[i]
+ }
+ }
+ return ""
+ }
+
+ readonly property string eventName: interfaceEventMap[interfaceName]
+
+ GridLayout {
+ anchors.fill: parent
+ columns: app.landscape ? 2 : 1
+ rowSpacing: 0
+ columnSpacing: 0
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: width
+ Layout.margins: Style.hugeMargins
+
+ CircleBackground {
+ id: iconView
+ width: Math.min(parent.width, parent.height)
+ height: width
+ anchors.centerIn: parent
+ iconSource: app.interfaceToIcon(root.interfaceName)
+ onColor: app.interfaceToColor(root.interfaceName)
+ on: false
+ Timer {
+ running: parent.on
+ repeat: false
+ interval: 1000
+ onTriggered: {
+ parent.on = false
+ }
+ }
+ }
}
-// delegate: NymeaSwipeDelegate {
-// width: parent.width
-// iconName: app.interfaceToIcon("inputtrigger")
-// text: model.value.trim()
-// subText: Qt.formatDateTime(model.timestamp)
-// progressive: false
-// onClicked: {
-// print("a", model.value.trim())
-// var parts = model.value.trim().split(', ')
-// print("b", parts)
-// var popup = detailsPopup.createObject(root, {timestamp: model.timestamp, notificationTitle: parts[1], notificationBody: parts[0]});
-// popup.open();
-// }
-// }
+ GenericTypeLogView {
+ id: logView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
- onAddRuleClicked: {
- var value = logView.logsModel.get(index).value
- var typeId = logView.logsModel.get(index).typeId
- var rule = engine.ruleManager.createNewRule();
- var eventDescriptor = rule.eventDescriptors.createNewEventDescriptor();
- eventDescriptor.thingId = thing.id;
- var eventType = root.thing.thingClass.eventTypes.getEventType(typeId);
- eventDescriptor.eventTypeId = eventType.id;
- rule.name = root.thing.name + " - " + eventType.displayName;
- if (eventType.paramTypes.count === 1) {
- var paramType = eventType.paramTypes.get(0);
- eventDescriptor.paramDescriptors.setParamDescriptor(paramType.id, value, ParamDescriptor.ValueOperatorEquals);
+ logsModel: logsModel
+ LogsModel {
+ id: logsModel
+ engine: _engine
+ thingId: root.thing.id
+ live: true
+ typeIds: [root.thing.thingClass.eventTypes.findByName(root.eventName).id];
+ onCountChanged: {
+ if (!logsModel.busy) {
+ iconView.on = true
+ }
+ }
+ }
+
+ onAddRuleClicked: {
+ var value = logView.logsModel.get(index).value
+ var typeId = logView.logsModel.get(index).typeId
+ var rule = engine.ruleManager.createNewRule();
+ var eventDescriptor = rule.eventDescriptors.createNewEventDescriptor();
+ eventDescriptor.thingId = thing.id;
+ var eventType = root.thing.thingClass.eventTypes.getEventType(typeId);
+ eventDescriptor.eventTypeId = eventType.id;
+ rule.name = root.thing.name + " - " + eventType.displayName;
+ if (eventType.paramTypes.count === 1) {
+ var paramType = eventType.paramTypes.get(0);
+ eventDescriptor.paramDescriptors.setParamDescriptor(paramType.id, value, ParamDescriptor.ValueOperatorEquals);
+ }
rule.eventDescriptors.addEventDescriptor(eventDescriptor);
rule.name = rule.name + " - " + value
+ var rulePage = pageStack.push(Qt.resolvedUrl("../magic/ThingRulesPage.qml"), {thing: root.thing});
+ rulePage.addRule(rule);
}
- var rulePage = pageStack.push(Qt.resolvedUrl("../magic/ThingRulesPage.qml"), {thing: root.thing});
- rulePage.addRule(rule);
}
}
+
+
}
diff --git a/nymea-app/ui/images/sensors/vibration.svg b/nymea-app/ui/images/sensors/vibration.svg
new file mode 100644
index 00000000..ce89df59
--- /dev/null
+++ b/nymea-app/ui/images/sensors/vibration.svg
@@ -0,0 +1,231 @@
+
+
diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml
index 84a147c0..5f0e45f9 100644
--- a/nymea-app/ui/utils/NymeaUtils.qml
+++ b/nymea-app/ui/utils/NymeaUtils.qml
@@ -39,6 +39,8 @@ Item {
page = "CoolingThingPage.qml";
} else if (interfaceList.indexOf("thermostat") >= 0) {
page = "ThermostatDevicePage.qml";
+ } else if (interfaceList.indexOf("vibrationsensor") >= 0) {
+ page = "InputTriggerDevicePage.qml";
} else if (interfaceList.indexOf("sensor") >= 0) {
page = "SensorDevicePage.qml";
} else if (interfaceList.indexOf("inputtrigger") >= 0) {