diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc
index 91925329..9ee9ca9c 100644
--- a/nymea-app/images.qrc
+++ b/nymea-app/images.qrc
@@ -251,5 +251,7 @@
ui/images/discord.svg
ui/images/media/equalizer.svg
ui/images/media/ambeo.svg
+ ui/images/thermostat/cooling.svg
+ ui/images/thermostat/heating.svg
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 5e21b685..740daa7d 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -230,5 +230,7 @@
ui/components/ColorTemperaturePicker.qml
ui/components/ThingContextMenu.qml
ui/StyleBase.qml
+ ui/customviews/ThermostatController.qml
+ ui/devicepages/ThermostatDevicePage.qml
diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml
index 2d500aad..11982c8e 100644
--- a/nymea-app/ui/Nymea.qml
+++ b/nymea-app/ui/Nymea.qml
@@ -69,6 +69,7 @@ ApplicationWindow {
property int smallFont: 13
property int mediumFont: 16
property int largeFont: 20
+ property int hugeFont: 40
property int smallIconSize: 16
property int iconSize: 24
@@ -145,6 +146,7 @@ ApplicationWindow {
"blind",
"garagedoor",
"powersocket",
+ "thermostat",
"heating",
"smartlock",
"doorbell",
@@ -218,6 +220,8 @@ ApplicationWindow {
return qsTr("Smart meters");
case "heating":
return qsTr("Heating");
+ case "thermostat":
+ return qsTr("Thermostats");
case "evcharger":
return qsTr("EV-chargers");
case "powersocket":
@@ -341,8 +345,9 @@ ApplicationWindow {
case "extendedsmartmeterproducer":
return Qt.resolvedUrl("images/smartmeter.svg")
case "heating":
- case "extendedheating":
- return Qt.resolvedUrl("images/radiator.svg")
+ return Qt.resolvedUrl("images/thermostat/heating.svg")
+ case "cooling":
+ return Qt.resolvedUrl("images/thermostat/cooling.svg")
case "thermostat":
return Qt.resolvedUrl("images/dial.svg")
case "evcharger":
diff --git a/nymea-app/ui/components/Dial.qml b/nymea-app/ui/components/Dial.qml
index ea29109a..3d51079f 100644
--- a/nymea-app/ui/components/Dial.qml
+++ b/nymea-app/ui/components/Dial.qml
@@ -37,7 +37,8 @@ import QtQuick.Controls.Material 2.2
ColumnLayout {
id: dial
- property Device device: null
+ property Thing thing: null
+ property alias device: dial.thing
property StateType stateType: null
property bool showValueLabel: true
@@ -53,14 +54,14 @@ ColumnLayout {
return (to - from) * angle / maxAngle + from
}
- readonly property State deviceState: device && stateType ? device.states.getState(stateType.id) : null
+ readonly property State deviceState: thing && stateType ? thing.states.getState(stateType.id) : null
readonly property double from: dial.stateType ? dial.stateType.minValue : 0
readonly property double to: dial.stateType ? dial.stateType.maxValue : 100
readonly property double anglePerStep: maxAngle / dial.steps
readonly property double startAngle: -(dial.steps * dial.anglePerStep) / 2
- readonly property StateType powerStateType: dial.device.deviceClass.stateTypes.findByName("power")
- readonly property State powerState: powerStateType ? dial.device.states.getState(powerStateType.id) : null
+ readonly property StateType powerStateType: dial.thing.thingClass.stateTypes.findByName("power")
+ readonly property State powerState: powerStateType ? dial.thing.states.getState(powerStateType.id) : null
QtObject {
id: d
@@ -93,11 +94,11 @@ ColumnLayout {
param["paramName"] = dial.stateType.name
param["value"] = value
params.push(param)
- d.pendingActionId = dial.device.executeAction(dial.stateType.name, params)
+ d.pendingActionId = dial.thing.executeAction(dial.stateType.name, params)
}
}
Connections {
- target: engine.deviceManager
+ target: engine.thingManager
onExecuteActionReply: {
if (d.pendingActionId == commandId) {
d.pendingActionId = -1
@@ -109,7 +110,7 @@ ColumnLayout {
}
}
Connections {
- target: dial.device
+ target: dial.thing
onActionExecutionFinished: {
if (id == d.pendingActionId) {
d.pendingActionId = -1;
@@ -121,12 +122,12 @@ ColumnLayout {
}
}
- Component.onCompleted: rotationButton.rotation = dial.valueToAngle(dial.deviceState.value)
+ Component.onCompleted: rotationButton.rotation = dial.valueToAngle(dial.thingState.value)
Connections {
- target: dial.deviceState
+ target: dial.thingState
onValueChanged: {
if (!d.busy) {
- rotationButton.rotation = dial.valueToAngle(dial.deviceState.value)
+ rotationButton.rotation = dial.valueToAngle(dial.thingState.value)
}
}
}
@@ -285,7 +286,7 @@ ColumnLayout {
param["paramName"] = "power"
param["value"] = !dial.powerState.value
params.push(param)
- dial.device.executeAction("power", params)
+ dial.thing.executeAction("power", params)
}
dragging = false;
}
diff --git a/nymea-app/ui/customviews/ThermostatController.qml b/nymea-app/ui/customviews/ThermostatController.qml
new file mode 100644
index 00000000..73a990ae
--- /dev/null
+++ b/nymea-app/ui/customviews/ThermostatController.qml
@@ -0,0 +1,237 @@
+import QtQuick 2.9
+import Nymea 1.0
+import "../utils"
+import "../components"
+
+Item {
+ id: root
+
+ property Thing thing: null
+
+ property double precision: 0.5
+
+ readonly property StateType targetTemperatureStateType: thing.thingClass.stateTypes.findByName("targetTemperature")
+ readonly property State targetTemperatureState: thing.stateByName("targetTemperature")
+ readonly property StateType temperatureStateType: thing.thingClass.stateTypes.findByName("temperature")
+ readonly property State temperatureState: thing.stateByName("temperature")
+ readonly property State heatingOnState: thing.stateByName("heatingOn")
+ readonly property State coolingOnState: thing.stateByName("coolingOn")
+
+ Connections {
+ target: targetTemperatureState
+ onValueChanged: canvas.requestPaint()
+ }
+ Connections {
+ target: temperatureState
+ onValueChanged: canvas.requestPaint()
+ }
+ ActionQueue {
+ id: actionQueue
+ thing: root.thing
+ stateType: targetTemperatureStateType
+ onPendingValueChanged: canvas.requestPaint();
+ }
+
+ Canvas {
+ id: canvas
+ width: Math.min(parent.width, parent.height)
+ height: width
+ anchors.centerIn: parent
+
+ property int startAngle: 135
+ property int maxAngle: 270
+ property int steps: roundToPrecision(root.targetTemperatureStateType.maxValue - root.targetTemperatureStateType.minValue) * (1/root.precision)
+ property double stepSize: (root.targetTemperatureStateType.maxValue - root.targetTemperatureStateType.minValue) / steps
+ property double anglePerStep: maxAngle / steps
+
+
+ function angleToValue(angle) {
+ var from = root.targetTemperatureStateType.minValue
+ var to = root.targetTemperatureStateType.maxValue
+ return (to - from) * angle / maxAngle + from
+ }
+
+ onPaint: {
+ var ctx = canvas.getContext('2d');
+ ctx.save();
+ ctx.reset()
+
+
+ var center = { x: canvas.width / 2, y: canvas.height / 2 };
+ var rotation = 135;
+
+ // Background arc
+ ctx.beginPath()
+// ctx.strokeStyle = Style.tileBackgroundColor;
+ ctx.lineWidth = 0;
+ ctx.fillStyle = Style.tileBackgroundColor
+ var innerRadius = canvas.width * 0.4
+ var outerRadius = canvas.width * 0.5
+ var endAngle = (maxAngle + startAngle) % 360
+ var radStart = startAngle * Math.PI/180;
+ var radEnd = endAngle * Math.PI/180;
+ ctx.arc(center.x, center.y, outerRadius, radStart, radEnd)
+ ctx.arc(center.x, center.y, innerRadius, radEnd, radStart, true)
+ ctx.fill();
+ ctx.closePath();
+
+ // Step lines
+ var currentValue = actionQueue.pendingValue || root.targetTemperatureState.value
+ var targetTempStep = roundToPrecision(currentValue - root.targetTemperatureStateType.minValue) * (1/root.precision)
+ var currentTempStep;
+ if (root.temperatureState) {
+ currentTempStep = roundToPrecision(root.temperatureState.value - root.targetTemperatureStateType.minValue) * (1/root.precision)
+ }
+
+ for(var step = 0; step < steps; step += root.precision) {
+ var angle = step * anglePerStep + startAngle;
+ var innerRadius = canvas.width * 0.4
+ var outerRadius = canvas.width * 0.5
+
+ if (targetTempStep === step) {
+ if (currentTempStep && currentTempStep < targetTempStep) {
+ ctx.strokeStyle = "red";
+ } else if (currentTempStep && currentTempStep > targetTempStep) {
+ ctx.strokeStyle = "dodgerblue";
+ } else {
+ ctx.strokeStyle = Style.accentColor;
+ }
+ innerRadius = canvas.width * 0.38
+ ctx.lineWidth = 4;
+ } else if (currentTempStep && currentTempStep === step) {
+ if (currentTempStep < targetTempStep) {
+ ctx.strokeStyle = "red";
+ } else {
+ ctx.strokeStyle = "dodgerblue";
+ }
+ ctx.lineWidth = 3;
+ } else if (currentTempStep && currentTempStep < step && step < targetTempStep) {
+ ctx.strokeStyle = "red";
+ ctx.lineWidth = 2;
+ } else if (currentTempStep && currentTempStep > step && step > targetTempStep) {
+ ctx.strokeStyle = "dodgerblue";
+ ctx.lineWidth = 2;
+ } else {
+ ctx.strokeStyle = Style.tileOverlayColor;
+ ctx.lineWidth = 1;
+ }
+
+ ctx.beginPath();
+ // rotate
+ //convert to radians
+ var rad = angle * Math.PI/180;
+ var c = Math.cos(rad);
+ var s = Math.sin(rad);
+ var innerPointX = center.x + (innerRadius * c);
+ var innerPointY = center.y + (innerRadius * s);
+ var outerPointX = center.x + (outerRadius * c);
+ var outerPointY = center.x + (outerRadius * s);
+
+ context.moveTo(innerPointX, innerPointY);
+ context.lineTo(outerPointX, outerPointY);
+ ctx.stroke();
+ ctx.closePath();
+ }
+
+ ctx.beginPath();
+ ctx.font = "" + app.hugeFont + "px " + Style.fontFamily;
+ ctx.fillStyle = Style.foregroundColor;
+ var roundedTargetTemp = Types.toUiValue(root.targetTemperatureState.value, root.targetTemperatureStateType.unit)
+ roundedTargetTemp = roundToPrecision(roundedTargetTemp).toFixed(1) + "°"
+ var size = ctx.measureText(roundedTargetTemp)
+ ctx.text(roundedTargetTemp, center.x - size.width / 2, center.y + app.hugeFont / 2);
+ ctx.fill();
+ ctx.closePath();
+
+ if (root.temperatureState) {
+ ctx.beginPath();
+ ctx.font = "" + app.largeFont + "px " + Style.fontFamily;
+ var roundedTemp = Types.toUiValue(root.temperatureState.value, root.temperatureStateType.unit)
+ roundedTemp = roundToPrecision(roundedTemp) + "°"
+ size = ctx.measureText(roundedTemp)
+ ctx.text(roundedTemp, center.x - size.width / 2, center.y + app.hugeFont + app.margins);
+ ctx.fill();
+ ctx.closePath();
+ }
+
+ ctx.restore();
+ }
+
+ function roundToPrecision(value) {
+ var tmp = Math.round(value / root.precision) * root.precision;
+ return tmp;
+ }
+ }
+
+ ColorIcon {
+ width: app.largeIconSize
+ height: width
+ anchors { bottom: canvas.bottom; horizontalCenter: canvas.horizontalCenter }
+ name: root.heatingOnState && root.heatingOnState.value === true
+ ? "../images/thermostat/heating.svg"
+ : root.coolingOnState && root.coolingOnState.value === true
+ ? "../images/thermostat/cooling.svg"
+ : ""
+ color: root.heatingOnState && root.heatingOnState.value === true
+ ? "red"
+ : root.coolingOnState && root.coolingOnState.value === true
+ ? "dodgerblue"
+ : Style.iconColor
+ }
+
+ MouseArea {
+ anchors.fill: canvas
+
+ property bool dragging: false
+ property double lastAngle
+ property double angleDiff
+
+ onPressed: {
+ lastAngle = calculateAngle(mouseX, mouseY)
+ }
+
+ onPositionChanged: {
+ var angle = calculateAngle(mouseX, mouseY)
+ var tmpDiff = angle - lastAngle
+ if (tmpDiff > 300) {
+ tmpDiff -= 360
+ }
+ if (tmpDiff < -300) {
+ tmpDiff += 360
+ }
+
+ lastAngle = angle;
+
+ angleDiff += tmpDiff
+
+ var valueDiff = angleDiff / canvas.anglePerStep * canvas.stepSize
+ valueDiff = canvas.roundToPrecision(valueDiff)
+ if (Math.abs(valueDiff) > 0) {
+ var currentValue = actionQueue.pendingValue || root.targetTemperatureState.value
+ var newValue = currentValue + valueDiff
+ newValue = Math.min(root.targetTemperatureStateType.maxValue, Math.max(root.targetTemperatureStateType.minValue, newValue))
+ if (currentValue !== newValue) {
+ actionQueue.sendValue(newValue)
+ }
+ var steps = Math.floor(valueDiff / canvas.stepSize)
+ angleDiff -= steps * canvas.anglePerStep
+ }
+ }
+
+ function calculateAngle(mouseX, mouseY) {
+ // transform coords to center of dial
+ mouseX -= canvas.width / 2
+ mouseY -= canvas.height / 2
+
+ var rad = Math.atan(mouseY / mouseX);
+ var angle = rad * 180 / Math.PI
+
+ angle += 90;
+
+ if (mouseX < 0 && mouseY >= 0) angle = 180 + angle;
+ if (mouseX < 0 && mouseY < 0) angle = 180 + angle;
+
+ return angle;
+ }
+ }
+}
diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml
index 7b2c2e6d..ee04f920 100644
--- a/nymea-app/ui/delegates/InterfaceTile.qml
+++ b/nymea-app/ui/delegates/InterfaceTile.qml
@@ -167,6 +167,7 @@ MainPageTile {
case "extendedsmartmeterconsumer":
case "extendedsmartmeterproducer":
case "heating":
+ case "thermostat":
return sensorComponent;
// return labelComponent;
diff --git a/nymea-app/ui/devicepages/HeatingDevicePage.qml b/nymea-app/ui/devicepages/HeatingDevicePage.qml
index 6bd0d36d..8fdca878 100644
--- a/nymea-app/ui/devicepages/HeatingDevicePage.qml
+++ b/nymea-app/ui/devicepages/HeatingDevicePage.qml
@@ -30,84 +30,61 @@
import QtQuick 2.5
import QtQuick.Controls 2.1
-import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.1
+import QtQuick.Controls.Material 2.1
import Nymea 1.0
import "../components"
-import "../customviews"
DevicePageBase {
id: root
- readonly property bool landscape: width > height
-
- readonly property StateType targetTemperatureStateType: device.deviceClass.stateTypes.findByName("targetTemperature")
- readonly property State targetTemperatureState: targetTemperatureStateType ? device.states.getState(targetTemperatureStateType.id) : null
- readonly property StateType powerStateType: deviceClass.stateTypes.findByName("power")
- readonly property State powerState: powerStateType ? device.states.getState(powerStateType.id) : null
- readonly property StateType temperatureStateType: device.deviceClass.stateTypes.findByName("temperature")
- readonly property State temperatureState: temperatureStateType ? device.states.getState(temperatureStateType.id) : null
- readonly property StateType percentageStateType: device.deviceClass.stateTypes.findByName("percentage")
- readonly property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
- // TODO: should this be an interface? e.g. extendedthermostat
- readonly property StateType boostStateType: device.deviceClass.stateTypes.findByName("boost")
- readonly property State boostState: boostStateType ? device.states.getState(boostStateType.id) : null
-
- Component.onCompleted: {
- print("d:", root.device, root.targetTemperatureStateType, root.percentageStateType)
- }
+ readonly property var powerStateType: deviceClass.stateTypes.findByName("power")
+ readonly property var powerState: device.states.getState(powerStateType.id)
+ readonly property var powerActionType: deviceClass.actionTypes.findByName("power");
GridLayout {
anchors.fill: parent
anchors.margins: app.margins
columns: app.landscape ? 2 : 1
+ rowSpacing: app.margins
+ columnSpacing: app.margins
+ Layout.alignment: Qt.AlignCenter
- Dial {
- id: dial
- Layout.fillWidth: true
+ Item {
+ Layout.preferredWidth: Math.max(app.iconSize * 6, parent.width / 5)
+ Layout.preferredHeight: width
+ Layout.topMargin: app.margins
+ Layout.bottomMargin: app.landscape ? app.margins : 0
+ Layout.alignment: Qt.AlignCenter
+ Layout.rowSpan: app.landscape ? 4 : 1
Layout.fillHeight: true
-// visible: root.targetTemperatureStateType || root.percentageStateType
- device: root.device
- stateType: root.targetTemperatureStateType ? root.targetTemperatureStateType : root.percentageStateType
-
- }
-
- Rectangle {
- Layout.preferredWidth: app.landscape ? parent.width / 2 : parent.width
- Layout.preferredHeight: 50
- visible: root.boostStateType
- border.color: boostMouseArea.pressed || root.boostStateType && root.boostState.value === true ? Style.accentColor : Style.foregroundColor
- border.width: 1
- radius: height / 2
- color: root.boostStateType && root.boostState.value === true ? Style.accentColor : "transparent"
-
- Row {
+ AbstractButton {
+ height: Math.min(parent.height, parent.width)
+ width: height
anchors.centerIn: parent
- spacing: app.margins / 2
- ColorIcon {
- height: app.iconSize
- width: app.iconSize
- name: "../images/sensors/temperature.svg"
- color: root.boostStateType && root.boostState.value === true ? "red" : Style.iconColor
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ border.color: root.powerState.value === true ? Style.accentColor : Style.iconColor
+ border.width: 4
+ radius: width / 2
}
- Label {
- text: qsTr("Boost")
- anchors.verticalCenter: parent.verticalCenter
+ ColorIcon {
+ id: bulbIcon
+ anchors.fill: parent
+ anchors.margins: app.margins * 1.5
+ name: "../images/thermostat/heating.svg"
+ color: root.powerState.value === true ? Style.accentColor : Style.iconColor
}
- }
- MouseArea {
- id: boostMouseArea
- anchors.fill: parent
- onPressedChanged: PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
onClicked: {
var params = []
var param = {}
- param["paramTypeId"] = root.boostStateType.id
- param["value"] = !root.boostState.value
+ param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id;
+ param["value"] = !root.powerState.value;
params.push(param)
- engine.deviceManager.executeAction(root.device.id, root.boostStateType.id, params);
+ engine.deviceManager.executeAction(root.device.id, root.powerStateType.id, params);
}
}
}
diff --git a/nymea-app/ui/devicepages/ThermostatDevicePage.qml b/nymea-app/ui/devicepages/ThermostatDevicePage.qml
new file mode 100644
index 00000000..5ad700f1
--- /dev/null
+++ b/nymea-app/ui/devicepages/ThermostatDevicePage.qml
@@ -0,0 +1,106 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 .
+*
+* 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.5
+import QtQuick.Controls 2.1
+import QtQuick.Controls.Material 2.2
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+import "../components"
+import "../customviews"
+
+DevicePageBase {
+ id: root
+
+ readonly property bool landscape: width > height
+
+ readonly property StateType targetTemperatureStateType: thing.thingClass.stateTypes.findByName("targetTemperature")
+ readonly property State targetTemperatureState: targetTemperatureStateType ? thing.states.getState(targetTemperatureStateType.id) : null
+ readonly property StateType powerStateType: thingClass.stateTypes.findByName("power")
+ readonly property State powerState: powerStateType ? thing.states.getState(powerStateType.id) : null
+ readonly property StateType temperatureStateType: thing.thingClass.stateTypes.findByName("temperature")
+ readonly property State temperatureState: temperatureStateType ? thing.states.getState(temperatureStateType.id) : null
+ readonly property StateType percentageStateType: thing.thingClass.stateTypes.findByName("percentage")
+ readonly property State percentageState: percentageStateType ? thing.states.getState(percentageStateType.id) : null
+ // TODO: should this be an interface? e.g. extendedthermostat
+ readonly property StateType boostStateType: thing.thingClass.stateTypes.findByName("boost")
+ readonly property State boostState: boostStateType ? thing.states.getState(boostStateType.id) : null
+
+ GridLayout {
+ anchors.fill: parent
+ anchors.margins: app.margins
+ columns: app.landscape ? 2 : 1
+
+ ThermostatController {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ thing: root.thing
+ }
+
+ Rectangle {
+ Layout.preferredWidth: app.landscape ? parent.width / 2 : parent.width
+ Layout.preferredHeight: 50
+ visible: root.boostStateType
+ border.color: boostMouseArea.pressed || root.boostStateType && root.boostState.value === true ? Style.accentColor : Style.foregroundColor
+ border.width: 1
+ radius: height / 2
+ color: root.boostStateType && root.boostState.value === true ? Style.accentColor : "transparent"
+
+ Row {
+ anchors.centerIn: parent
+ spacing: app.margins / 2
+ ColorIcon {
+ height: app.iconSize
+ width: app.iconSize
+ name: "../images/sensors/temperature.svg"
+ color: root.boostStateType && root.boostState.value === true ? "red" : Style.iconColor
+ }
+
+ Label {
+ text: qsTr("Boost")
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ MouseArea {
+ id: boostMouseArea
+ anchors.fill: parent
+ onPressedChanged: PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
+ onClicked: {
+ var params = []
+ var param = {}
+ param["paramTypeId"] = root.boostStateType.id
+ param["value"] = !root.boostState.value
+ params.push(param)
+ engine.thingManager.executeAction(root.thing.id, root.boostStateType.id, params);
+ }
+ }
+ }
+ }
+}
diff --git a/nymea-app/ui/images/thermostat/cooling.svg b/nymea-app/ui/images/thermostat/cooling.svg
new file mode 100644
index 00000000..b189f755
--- /dev/null
+++ b/nymea-app/ui/images/thermostat/cooling.svg
@@ -0,0 +1,231 @@
+
+
+
+
diff --git a/nymea-app/ui/images/thermostat/heating.svg b/nymea-app/ui/images/thermostat/heating.svg
new file mode 100644
index 00000000..bff79b13
--- /dev/null
+++ b/nymea-app/ui/images/thermostat/heating.svg
@@ -0,0 +1,193 @@
+
+
diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml
index 0d17436e..c08bccf7 100644
--- a/nymea-app/ui/utils/NymeaUtils.qml
+++ b/nymea-app/ui/utils/NymeaUtils.qml
@@ -27,8 +27,10 @@ Item {
page = "ButtonDevicePage.qml";
} else if (interfaceList.indexOf("weather") >= 0) {
page = "WeatherDevicePage.qml";
- } else if (interfaceList.indexOf("heating") >= 0 || interfaceList.indexOf("thermostat") >= 0) {
+ } else if (interfaceList.indexOf("heating") >= 0) {
page = "HeatingDevicePage.qml";
+ } else if (interfaceList.indexOf("thermostat") >= 0) {
+ page = "ThermostatDevicePage.qml";
} else if (interfaceList.indexOf("sensor") >= 0) {
page = "SensorDevicePage.qml";
} else if (interfaceList.indexOf("inputtrigger") >= 0) {