diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index f855504b..ee60f6c4 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -61,8 +61,12 @@
ui/devicepages/ButtonDevicePage.qml
ui/devicepages/GenericDeviceStateDetailsPage.qml
ui/devicepages/GenericDevicePage.qml
+ ui/devicepages/WeatherDevicePagePre110.qml
+ ui/devicepages/WeatherDevicePagePost110.qml
ui/devicepages/WeatherDevicePage.qml
ui/devicepages/SensorDevicePage.qml
+ ui/devicepages/SensorDevicePagePre110.qml
+ ui/devicepages/SensorDevicePagePost110.qml
ui/devicepages/DevicePageBase.qml
ui/devicepages/ConfigureThingPage.qml
ui/devicepages/InputTriggerDevicePage.qml
@@ -129,5 +133,6 @@
../LICENSE
ui/customviews/GenericTypeGraphPre110.qml
ui/customviews/GenericTypeGraph.qml
+ ui/customviews/SensorChart.qml
diff --git a/nymea-app/ui/NewDeviceWizard.qml b/nymea-app/ui/NewDeviceWizard.qml
index c4e90538..121683c7 100644
--- a/nymea-app/ui/NewDeviceWizard.qml
+++ b/nymea-app/ui/NewDeviceWizard.qml
@@ -36,6 +36,7 @@ Page {
Connections {
target: engine.deviceManager
onPairDeviceReply: {
+ busyOverlay.shown = false
switch (params["setupMethod"]) {
case "SetupMethodPushButton":
d.pairingTransactionId = params["pairingTransactionId"];
@@ -52,9 +53,11 @@ Page {
}
}
onConfirmPairingReply: {
+ busyOverlay.shown = false
internalPageStack.push(resultsPage, {success: params["deviceError"] === "DeviceErrorNoError", deviceId: params["deviceId"]})
}
onAddDeviceReply: {
+ busyOverlay.shown = false;
internalPageStack.push(resultsPage, {success: params["deviceError"] === "DeviceErrorNoError", deviceId: params["deviceId"]})
}
}
@@ -358,6 +361,8 @@ Page {
break;
}
+ busyOverlay.shown = true;
+
}
}
}
@@ -448,4 +453,8 @@ Page {
}
}
}
+
+ BusyOverlay {
+ id: busyOverlay
+ }
}
diff --git a/nymea-app/ui/components/HeaderButton.qml b/nymea-app/ui/components/HeaderButton.qml
index 74d46cdf..d38d58d3 100644
--- a/nymea-app/ui/components/HeaderButton.qml
+++ b/nymea-app/ui/components/HeaderButton.qml
@@ -13,6 +13,7 @@ ToolButton {
id: image
anchors.fill: parent
anchors.margins: app.margins / 2
+ opacity: enabled ? 1 : .5
}
}
}
diff --git a/nymea-app/ui/customviews/GenericTypeGraph.qml b/nymea-app/ui/customviews/GenericTypeGraph.qml
index c5eeebdc..f138505c 100644
--- a/nymea-app/ui/customviews/GenericTypeGraph.qml
+++ b/nymea-app/ui/customviews/GenericTypeGraph.qml
@@ -12,10 +12,14 @@ Item {
property var device: null
property var stateType: null
+ property var valueState: device.states.getState(stateType.id)
readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
readonly property bool hasConnectable: deviceClass.interfaces.indexOf("connectable") >= 0
readonly property var connectedStateType: hasConnectable ? deviceClass.stateTypes.findByName("connected") : null
+ property color color: app.accentColor
+ property string iconSource: ""
+
LogsModelNg {
id: logsModelNg
engine: _engine
@@ -40,54 +44,34 @@ Item {
anchors.fill: parent
spacing: 0
RowLayout {
- Layout.alignment: Qt.AlignHCenter
- HeaderButton {
- imageSource: "../images/zoom-out.svg"
- onClicked: {
- var diff = xAxis.max.getTime() - xAxis.min.getTime()
- var newTime = new Date(xAxis.min.getTime() - (diff / 4))
- xAxis.min = newTime;
- }
+ Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ name: root.iconSource
+ visible: root.iconSource.length > 0
+ color: root.color
}
Label {
- Layout.preferredWidth: 100
- horizontalAlignment: Text.AlignHCenter
- text: {
- var timeDiff = (xAxis.max.getTime() - xAxis.min.getTime()) / 1000;
- if (timeDiff < 60) {
- return qsTr("%1 seconds").arg(Math.round(timeDiff));
- }
- timeDiff = timeDiff / 60
- if (timeDiff < 60) {
- return qsTr("%1 minutes").arg(Math.round(timeDiff));
- }
- timeDiff = timeDiff / 60
- if (timeDiff < 48) {
- return qsTr("%1 hours").arg(Math.round(timeDiff));
- }
- timeDiff = timeDiff / 24;
- if (timeDiff < 14) {
- return qsTr("%1 days").arg(Math.round(timeDiff));
- }
- timeDiff = timeDiff / 7
- if (timeDiff < 5) {
- return qsTr("%1 weeks").arg(Math.round(timeDiff));
- }
- timeDiff * timeDiff * 7 / 30
- if (timeDiff < 24) {
- return qsTr("%1 months").arg(Math.round(timeDiff));
- }
- timeDiff = timeDiff * 30 / 356
- return qsTr("%1 years").arg(Math.round(timeDiff))
+ Layout.fillWidth: true
+ text: root.valueState.value + " " + root.stateType.unitString
+ font.pixelSize: app.largeFont
+ }
+
+ HeaderButton {
+ imageSource: "../images/zoom-out.svg"
+ onClicked: {
+ var newTime = new Date(xAxis.min.getTime() - (xAxis.timeDiff / 4))
+ xAxis.min = newTime;
}
}
HeaderButton {
imageSource: "../images/zoom-in.svg"
+ enabled: xAxis.timeDiff > (1000 * 60 * 30)
onClicked: {
- var diff = xAxis.max.getTime() - xAxis.min.getTime()
- var newTime = new Date(xAxis.min.getTime() + (diff / 4))
+ var newTime = new Date(Math.min(xAxis.min.getTime() + (xAxis.timeDiff / 4), xAxis.max.getTime() - (1000 * 60 * 30)))
xAxis.min = newTime;
}
}
@@ -102,6 +86,7 @@ Item {
margins.left: 0
margins.right: 0
backgroundColor: Material.background
+ legend.visible: false
legend.labelColor: app.foregroundColor
animationDuration: 300
@@ -109,8 +94,8 @@ Item {
ValueAxis {
id: yAxis
- min: logsModelNg.minValue
- max: logsModelNg.maxValue
+ min: logsModelNg.minValue - logsModelNg.minValue * .01
+ max: logsModelNg.maxValue + logsModelNg.maxValue * .01
labelsFont.pixelSize: app.smallFont
labelsColor: app.foregroundColor
tickCount: chartView.height / 40
@@ -132,30 +117,60 @@ Item {
tickCount: chartView.width / 70
labelsFont.pixelSize: app.smallFont
labelsColor: app.foregroundColor
+ property int timeDiff: xAxis.max.getTime() - xAxis.min.getTime()
+
+ function getTimeSpanString() {
+ var td = timeDiff / 1000
+ if (td < 60) {
+ return qsTr("%1 seconds").arg(Math.round(td));
+ }
+ td = td / 60
+ if (td < 60) {
+ return qsTr("%1 minutes").arg(Math.round(td));
+ }
+ td = td / 60
+ if (td < 48) {
+ return qsTr("%1 hours").arg(Math.round(td));
+ }
+ td = td / 24;
+ if (td < 14) {
+ return qsTr("%1 days").arg(Math.round(td));
+ }
+ td = td / 7
+ if (td < 9) {
+ return qsTr("%1 weeks").arg(Math.round(td));
+ }
+ td = td * 7 / 30
+ if (td < 24) {
+ return qsTr("%1 months").arg(Math.round(td));
+ }
+ td = td * 30 / 356
+ return qsTr("%1 years").arg(Math.round(td))
+ }
+
titleText: {
if (xAxis.min.getYear() === xAxis.max.getYear()
&& xAxis.min.getMonth() === xAxis.max.getMonth()
&& xAxis.min.getDate() === xAxis.max.getDate()) {
- return Qt.formatDate(xAxis.min)
+ return Qt.formatDate(xAxis.min) + " (" + getTimeSpanString() + ")"
}
- return Qt.formatDate(xAxis.min) + " - " + Qt.formatDate(xAxis.max)
+ return Qt.formatDate(xAxis.min) + " - " + Qt.formatDate(xAxis.max) + " (" + getTimeSpanString() + ")"
}
titleBrush: app.foregroundColor
format: {
- var timeDiff = (xAxis.max.getTime() - xAxis.min.getTime()) / 1000
- if (timeDiff < 60) { // one minute
+ if (timeDiff < 1000 * 60) { // one minute
return "mm:ss"
}
- if (timeDiff < 60 * 60) { // one hour
+ if (timeDiff < 1000 * 60 * 60) { // one hour
return "hh:mm"
}
- if (timeDiff < 60 * 60 * 24 * 2) { // two day
+ if (timeDiff < 1000 * 60 * 60 * 24 * 2) { // two day
return "hh:mm"
}
- if (timeDiff < 60 * 60 * 24 * 7) { // one week
+ if (timeDiff < 1000 * 60 * 60 * 24 * 7) { // one week
return "ddd hh:mm"
}
- if (timeDiff < 60 * 60 * 24 * 7 * 30) { // one month
+ if (timeDiff < 1000 * 60 * 60 * 24 * 7 * 30) { // one month
return "dd.MM."
}
return "MMM yy"
@@ -186,17 +201,62 @@ Item {
}
AreaSeries {
+ id: mainSeries
axisX: xAxis
axisY: yAxis
name: root.stateType.displayName
- borderColor: app.accentColor
+ borderColor: root.color
borderWidth: 4
upperSeries: LineSeries {
id: lineSeries1
}
- color: Qt.rgba(app.accentColor.r, app.accentColor.g, app.accentColor.b, .3)
+ color: Qt.rgba(root.color.r, root.color.g, root.color.b, .3)
+ onHovered: {
+ markClosestPoint(point)
+ }
+
+ function markClosestPoint(point) {
+ var found = false;
+ if (lineSeries1.count == 1) {
+ selectedHighlights.removePoints(0, selectedHighlights.count)
+ selectedHighlights.append(lineSeries1.at(0).x, lineSeries1.at(1).y)
+ return;
+ }
+
+ var searchIndex = Math.floor(lineSeries1.count / 2)
+ var previousIndex = 0;
+ var nextIndex = lineSeries1.count - 1;
+
+ while (previousIndex + 1 != nextIndex) {
+ if (point.x < lineSeries1.at(searchIndex).x) {
+ previousIndex = searchIndex;
+ } else if (point.x > lineSeries1.at(searchIndex).x) {
+ nextIndex = searchIndex;
+ }
+ searchIndex = previousIndex + Math.floor((nextIndex - previousIndex) / 2);
+ }
+ var diffToPrevious = Math.abs(point.x - lineSeries1.at(previousIndex).x)
+ var diffToNext = Math.abs(point.x - lineSeries1.at(nextIndex).x)
+ var closestPoint = diffToPrevious < diffToNext ? lineSeries1.at(previousIndex) : lineSeries1.at(nextIndex);
+
+ selectedHighlights.removePoints(0, selectedHighlights.count)
+ selectedHighlights.append(closestPoint.x, closestPoint.y)
+ }
}
+ ScatterSeries {
+ id: selectedHighlights
+ color: root.color
+ markerSize: 10
+ borderWidth: 2
+ borderColor: root.color
+ axisX: xAxis
+ axisY: yAxis
+ pointLabelsVisible: true
+ pointLabelsColor: app.foregroundColor
+ pointLabelsFont.pixelSize: app.smallFont
+ pointLabelsFormat: "@yPoint"
+ }
MouseArea {
anchors.fill: parent
@@ -225,19 +285,20 @@ Item {
chartView.animationOptions = ChartView.NoAnimation
var oldMax = xAxis.max;
chartView.scrollRight(dy);
- var timeDiff = xAxis.max.getTime() - oldMax.getTime()
- xAxis.min = new Date(xAxis.min.getTime() - timeDiff * 2)
+ xAxis.min = new Date(xAxis.min.getTime() - xAxis.timeDiff * 2)
chartView.animationOptions = ChartView.SeriesAnimations
}
onPressed: {
lastX = mouse.x
lastY = mouse.y
+ var pt = chartView.mapToValue(Qt.point(mouse.x, mouse.y), mainSeries)
+ mainSeries.markClosestPoint(pt)
}
onWheel: {
scrollRightLimited(-wheel.pixelDelta.x)
-// zoomInLimited(wheel.pixelDelta.y)
+// zoomInLimited(wheel.pixelDelta.y)
}
onPositionChanged: {
diff --git a/nymea-app/ui/customviews/SensorChart.qml b/nymea-app/ui/customviews/SensorChart.qml
new file mode 100644
index 00000000..d2cadaf8
--- /dev/null
+++ b/nymea-app/ui/customviews/SensorChart.qml
@@ -0,0 +1,28 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Controls.Material 2.1
+import QtQuick.Layouts 1.3
+import "../components"
+import Nymea 1.0
+
+CustomViewBase {
+ id: root
+ implicitHeight: width * .6
+ property string interfaceName
+
+ readonly property string stateTypeName: {
+ switch (interfaceName) {
+ case "lightsensor":
+ return "lightIntensity";
+ default:
+ return interfaceName.replace("sensor", "");
+ }
+ }
+ GenericTypeGraph {
+ anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom }
+ device: root.device
+ stateType: root.deviceClass.stateTypes.findByName(root.stateTypeName)
+ color: app.interfaceToColor(root.interfaceName)
+ iconSource: app.interfaceToIcon(root.interfaceName)
+ }
+}
diff --git a/nymea-app/ui/devicepages/SensorDevicePage.qml b/nymea-app/ui/devicepages/SensorDevicePage.qml
index 65998a15..e50610dc 100644
--- a/nymea-app/ui/devicepages/SensorDevicePage.qml
+++ b/nymea-app/ui/devicepages/SensorDevicePage.qml
@@ -8,24 +8,16 @@ import "../customviews"
DevicePageBase {
id: root
- ListView {
- anchors { fill: parent }
- model: ListModel {
- Component.onCompleted: {
- var supportedInterfaces = ["temperaturesensor", "humiditysensor", "pressuresensor", "moisturesensor", "lightsensor", "conductivitysensor"]
- for (var i = 0; i < supportedInterfaces.length; i++) {
- print("checking", root.deviceClass.name, root.deviceClass.interfaces)
- if (root.deviceClass.interfaces.indexOf(supportedInterfaces[i]) >= 0) {
- append({name: supportedInterfaces[i]});
- }
- }
+ Loader {
+ anchors.fill: parent
+ Component.onCompleted: {
+ var src
+ if (engine.jsonRpcClient.ensureServerVersion("1.10")) {
+ src = "SensorDevicePagePost110.qml"
+ } else {
+ src = "SensorDevicePagePre110.qml"
}
- }
- delegate: SensorView {
- width: parent.width
- interfaceName: modelData
- device: root.device
- deviceClass: root.deviceClass
+ setSource(Qt.resolvedUrl(src), {device: root.device, deviceClass: root.deviceClass})
}
}
}
diff --git a/nymea-app/ui/devicepages/SensorDevicePagePost110.qml b/nymea-app/ui/devicepages/SensorDevicePagePost110.qml
new file mode 100644
index 00000000..135bef73
--- /dev/null
+++ b/nymea-app/ui/devicepages/SensorDevicePagePost110.qml
@@ -0,0 +1,27 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+import "../components"
+import "../customviews"
+
+ListView {
+ anchors { fill: parent }
+ model: ListModel {
+ Component.onCompleted: {
+ var supportedInterfaces = ["temperaturesensor", "humiditysensor", "pressuresensor", "moisturesensor", "lightsensor", "conductivitysensor"]
+ for (var i = 0; i < supportedInterfaces.length; i++) {
+ print("checking", root.deviceClass.name, root.deviceClass.interfaces)
+ if (root.deviceClass.interfaces.indexOf(supportedInterfaces[i]) >= 0) {
+ append({name: supportedInterfaces[i]});
+ }
+ }
+ }
+ }
+ delegate: SensorChart {
+ width: parent.width
+ interfaceName: modelData
+ device: root.device
+ deviceClass: root.deviceClass
+ }
+}
diff --git a/nymea-app/ui/devicepages/SensorDevicePagePre110.qml b/nymea-app/ui/devicepages/SensorDevicePagePre110.qml
new file mode 100644
index 00000000..3b15028f
--- /dev/null
+++ b/nymea-app/ui/devicepages/SensorDevicePagePre110.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+import "../components"
+import "../customviews"
+
+ListView {
+ anchors { fill: parent }
+
+ property var device
+ property var deviceClass
+
+ model: ListModel {
+ Component.onCompleted: {
+ var supportedInterfaces = ["temperaturesensor", "humiditysensor", "pressuresensor", "moisturesensor", "lightsensor", "conductivitysensor"]
+ for (var i = 0; i < supportedInterfaces.length; i++) {
+ print("checking", root.deviceClass.name, root.deviceClass.interfaces)
+ if (root.deviceClass.interfaces.indexOf(supportedInterfaces[i]) >= 0) {
+ append({name: supportedInterfaces[i]});
+ }
+ }
+ }
+ }
+ delegate: SensorView {
+ width: parent.width
+ interfaceName: modelData
+ device: root.device
+ deviceClass: root.deviceClass
+ }
+}
diff --git a/nymea-app/ui/devicepages/WeatherDevicePage.qml b/nymea-app/ui/devicepages/WeatherDevicePage.qml
index 6a7c7543..363faf3b 100644
--- a/nymea-app/ui/devicepages/WeatherDevicePage.qml
+++ b/nymea-app/ui/devicepages/WeatherDevicePage.qml
@@ -8,36 +8,16 @@ import "../customviews"
DevicePageBase {
id: root
- Flickable {
+ Loader {
anchors.fill: parent
- clip: true
- contentHeight: content.implicitHeight
- ColumnLayout {
- id: content
- width: parent.width
- WeatherView {
- Layout.fillWidth: true
- device: root.device
- deviceClass: root.deviceClass
- }
- SensorView {
- Layout.fillWidth: true
- device: root.device
- deviceClass: root.deviceClass
- interfaceName: "temperaturesensor"
- }
- SensorView {
- Layout.fillWidth: true
- device: root.device
- deviceClass: root.deviceClass
- interfaceName: "humiditysensor"
- }
- SensorView {
- Layout.fillWidth: true
- device: root.device
- deviceClass: root.deviceClass
- interfaceName: "pressuresensor"
+ Component.onCompleted: {
+ var src
+ if (engine.jsonRpcClient.ensureServerVersion("1.10")) {
+ src = "WeatherDevicePagePost110.qml"
+ } else {
+ src = "WeatherDevicePagePre110.qml"
}
+ setSource(Qt.resolvedUrl(src), {device: root.device, deviceClass: root.deviceClass})
}
}
}
diff --git a/nymea-app/ui/devicepages/WeatherDevicePagePost110.qml b/nymea-app/ui/devicepages/WeatherDevicePagePost110.qml
new file mode 100644
index 00000000..04f08ca3
--- /dev/null
+++ b/nymea-app/ui/devicepages/WeatherDevicePagePost110.qml
@@ -0,0 +1,43 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+import "../components"
+import "../customviews"
+
+Flickable {
+ anchors.fill: parent
+ clip: true
+ contentHeight: content.implicitHeight
+
+ property var device
+ property var deviceClass
+
+ ColumnLayout {
+ id: content
+ width: parent.width
+ WeatherView {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ }
+ SensorChart {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "temperaturesensor"
+ }
+ SensorChart {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "humiditysensor"
+ }
+ SensorChart {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "pressuresensor"
+ }
+ }
+}
diff --git a/nymea-app/ui/devicepages/WeatherDevicePagePre110.qml b/nymea-app/ui/devicepages/WeatherDevicePagePre110.qml
new file mode 100644
index 00000000..3feaa8e5
--- /dev/null
+++ b/nymea-app/ui/devicepages/WeatherDevicePagePre110.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+import "../components"
+import "../customviews"
+
+
+Flickable {
+ anchors.fill: parent
+ clip: true
+ contentHeight: content.implicitHeight
+
+ property var device
+ property var deviceClass
+
+ ColumnLayout {
+ id: content
+ width: parent.width
+ WeatherView {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ }
+ SensorView {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "temperaturesensor"
+ }
+ SensorView {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "humiditysensor"
+ }
+ SensorView {
+ Layout.fillWidth: true
+ device: root.device
+ deviceClass: root.deviceClass
+ interfaceName: "pressuresensor"
+ }
+ }
+}