Add vibration sensor interface

This commit is contained in:
Michael Zanetti 2022-12-07 00:14:55 +01:00
parent 81fc45df1b
commit 18154afcaa
13 changed files with 365 additions and 49 deletions

View File

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

View File

@ -298,5 +298,6 @@
<file>ui/images/arrow-up.svg</file>
<file>ui/images/plus.svg</file>
<file>ui/images/minus.svg</file>
<file>ui/images/sensors/vibration.svg</file>
</qresource>
</RCC>

View File

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

View File

@ -102,6 +102,7 @@ Item {
"gassensor": orange,
"daylightsensor": yellow,
"presencesensor": darkBlue,
"vibrationsensor": orange,
"closablesensor": green,
"smartmeterconsumer": orange,
"smartmeterproducer": lime,

View File

@ -71,8 +71,6 @@ ItemDelegate {
signal deleteClicked()
signal secondaryIconClicked()
onPressAndHold: swipe.open(SwipeDelegate.Right)
QtObject {
id: d
property var deleteContextOption: [{

View File

@ -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 {

View File

@ -53,12 +53,6 @@ Item {
SwipeDelegateGroup {}
onContentYChanged: {
if (!logsModel.busy && contentY - originY < 5 * height) {
logsModel.fetchEarlier(24)
}
}
delegate: NymeaSwipeDelegate {
id: logEntryDelegate
width: parent.width

View File

@ -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" }

View File

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

View File

@ -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
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
viewBox="0 0 96 96.000001"
sodipodi:docname="vibration.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6199993"
inkscape:cx="55.782925"
inkscape:cy="50.889686"
inkscape:document-units="px"
inkscape:current-layer="g4780"
showgrid="false"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:document-rotation="0"
inkscape:window-width="1466"
inkscape:window-height="933"
inkscape:window-x="70"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780"
style="display:inline">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
id="rect4782"
width="96"
height="96.037987"
x="345.36221"
y="341.96448"
transform="matrix(0,1,1,0,0,0)" />
<g
id="g1141"
transform="translate(0,-4)">
<g
id="g1007"
transform="matrix(0,-0.9996045,1.0003957,0,-3.5344014,783.19142)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,429.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path4182"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,393.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path843"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
</g>
<g
id="g1099"
transform="matrix(0,-0.9996045,1.0003957,0,-3.5344014,791.19142)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,429.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1095"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,393.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1097"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
</g>
</g>
<g
id="g1149"
transform="translate(0,4)">
<g
id="g1013"
transform="matrix(0,-0.9996045,1.0003957,0,-3.5344014,821.19142)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,429.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1009"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,393.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1011"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
</g>
<g
id="g1105"
transform="matrix(0,-0.9996045,1.0003957,0,-3.5344014,813.19142)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,429.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1101"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.8;marker:none;enable-background:accumulate"
d="m 410.99179,393.36224 h -4.0016 c 0,0 4.20227,-5.47167 4.00158,-9.91267 -0.20069,-4.44101 -7.79075,-11.58811 -8.00316,-16.04945 -0.21242,-4.46134 4.00158,-10.0379 4.00158,-10.0379 h 4.0016 c 0,0 -4.2233,5.64195 -4.00158,10.1058 0.22172,4.46386 7.80091,11.5671 8.00316,15.99708 0.20225,4.42997 -4.00158,9.89714 -4.00158,9.89714 z"
id="path1103"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cczzcczzc" />
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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) {