diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index 500749b6..4a4d41d0 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -78,6 +78,11 @@
ui/devicepages/LightDevicePage.qml
ui/devicepages/FingerprintReaderDevicePage.qml
ui/devicelistpages/GenericDeviceListPage.qml
+ ui/devicelistpages/ClosablesDeviceListPage.qml
+ ui/devicelistpages/GarageDeviceListPage.qml
+ ui/devicelistpages/AwningDeviceListPage.qml
+ ui/devicelistpages/ShutterDeviceListPage.qml
+ ui/devicelistpages/BlindDeviceListPage.qml
ui/devicelistpages/LightsDeviceListPage.qml
ui/devicelistpages/SensorsDeviceListPage.qml
ui/devicelistpages/WeatherDeviceListPage.qml
diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml
index 939f8344..a3db35f3 100644
--- a/nymea-app/ui/Nymea.qml
+++ b/nymea-app/ui/Nymea.qml
@@ -175,6 +175,7 @@ ApplicationWindow {
return Qt.resolvedUrl("images/DeviceIconBlind.svg")
case "garagegate":
return Qt.resolvedUrl("images/shutter/shutter-100.svg")
+ case "awning":
case "extendedawning":
return Qt.resolvedUrl("images/awning/awning-100.svg")
case "battery":
diff --git a/nymea-app/ui/devicelistpages/AwningDeviceListPage.qml b/nymea-app/ui/devicelistpages/AwningDeviceListPage.qml
new file mode 100644
index 00000000..f50d3a03
--- /dev/null
+++ b/nymea-app/ui/devicelistpages/AwningDeviceListPage.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.9
+
+ClosablesDeviceListPage {
+ title: qsTr("Awnings")
+ iconBasename: "../images/awning/awning"
+ invertControls: true
+}
diff --git a/nymea-app/ui/devicelistpages/BlindDeviceListPage.qml b/nymea-app/ui/devicelistpages/BlindDeviceListPage.qml
new file mode 100644
index 00000000..d00487ea
--- /dev/null
+++ b/nymea-app/ui/devicelistpages/BlindDeviceListPage.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.9
+
+ClosablesDeviceListPage {
+ title: qsTr("Blinds")
+ iconBasename: "../images/shutter/shutter"
+}
diff --git a/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml
new file mode 100644
index 00000000..12cb9291
--- /dev/null
+++ b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml
@@ -0,0 +1,119 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.1
+import QtQuick.Controls.Material 2.1
+import QtQuick.Layouts 1.1
+import QtGraphicalEffects 1.0
+import Nymea 1.0
+import "../components"
+
+DeviceListPageBase {
+ id: root
+
+ property string iconBasename
+
+ property bool invertControls: false
+
+ header: GuhHeader {
+ id: header
+ onBackPressed: pageStack.pop()
+ text: root.title
+
+ HeaderButton {
+ imageSource: "../images/system-shutdown.svg"
+ onClicked: {
+ for (var i = 0; i < devicesProxy.count; i++) {
+ var device = devicesProxy.get(i);
+ var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
+ var actionType = deviceClass.actionTypes.findByName("close");
+ engine.deviceManager.executeAction(device.id, actionType.id)
+ }
+ }
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: devicesProxy
+ spacing: app.margins
+
+ delegate: Pane {
+ id: itemDelegate
+ width: parent.width
+
+ property bool inline: width > 500
+
+ property Device device: devicesProxy.get(index);
+ property DeviceClass deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);
+
+ property var connectedStateType: deviceClass.stateTypes.findByName("connected");
+ property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
+
+ property StateType percentageStateType: deviceClass.stateTypes.findByName("percentage");
+ property ActionType percentageActionType: deviceClass.actionTypes.findByName("percentage");
+ property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
+
+ property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
+ property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
+ property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
+
+ Material.elevation: 1
+ topPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ rightPadding: 0
+ contentItem: ItemDelegate {
+ id: contentItem
+ implicitHeight: nameRow.implicitHeight
+
+ topPadding: 0
+
+ contentItem: ColumnLayout {
+ spacing: 0
+ RowLayout {
+ enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
+ id: nameRow
+ z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
+ spacing: app.margins
+ Item {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: height
+ Layout.alignment: Qt.AlignVCenter
+
+ ColorIcon {
+ id: icon
+ anchors.fill: parent
+ color: itemDelegate.movingStateType && itemDelegate.movingState.value === true
+ ? app.accentColor
+ : keyColor
+ name: itemDelegate.percentageStateType
+ ? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
+ : root.iconBasename + "-050.svg"
+ }
+ }
+
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.device.name
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Item {
+ Layout.preferredWidth: shutterControls.implicitWidth
+ Layout.preferredHeight: app.iconSize * 2
+ ShutterControls {
+ id: shutterControls
+ height: parent.height
+ device: itemDelegate.device
+ invert: root.invertControls
+ }
+ }
+ }
+ }
+ onClicked: {
+ enterPage(index, false)
+ }
+ }
+ }
+ }
+}
diff --git a/nymea-app/ui/devicelistpages/GarageDeviceListPage.qml b/nymea-app/ui/devicelistpages/GarageDeviceListPage.qml
new file mode 100644
index 00000000..efc3eefd
--- /dev/null
+++ b/nymea-app/ui/devicelistpages/GarageDeviceListPage.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.9
+
+ClosablesDeviceListPage {
+ title: qsTr("Garage gates")
+ iconBasename: "../images/shutter/shutter"
+}
diff --git a/nymea-app/ui/devicelistpages/ShutterDeviceListPage.qml b/nymea-app/ui/devicelistpages/ShutterDeviceListPage.qml
new file mode 100644
index 00000000..b82517bd
--- /dev/null
+++ b/nymea-app/ui/devicelistpages/ShutterDeviceListPage.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.9
+
+ClosablesDeviceListPage {
+ title: qsTr("Shutters")
+ iconBasename: "../images/shutter/shutter"
+}
diff --git a/nymea-app/ui/devicepages/ShutterDevicePage.qml b/nymea-app/ui/devicepages/ShutterDevicePage.qml
index a8149c1c..8813a5fb 100644
--- a/nymea-app/ui/devicepages/ShutterDevicePage.qml
+++ b/nymea-app/ui/devicepages/ShutterDevicePage.qml
@@ -10,7 +10,7 @@ DevicePageBase {
id: root
readonly property bool landscape: width > height
- readonly property bool isExtended: deviceClass.interfaces.indexOf("extendedshutter") >= 0
+ readonly property bool isExtended: deviceClass.interfaces.indexOf("extendedclosable") >= 0
readonly property var percentageState: isExtended ? device.states.getState(deviceClass.stateTypes.findByName("percentage").id) : 0
readonly property var movingState: isExtended ? device.states.getState(deviceClass.stateTypes.findByName("moving").id) : 0
diff --git a/nymea-app/ui/mainviews/DevicesPageDelegate.qml b/nymea-app/ui/mainviews/DevicesPageDelegate.qml
index fa8845b6..a5f94f49 100644
--- a/nymea-app/ui/mainviews/DevicesPageDelegate.qml
+++ b/nymea-app/ui/mainviews/DevicesPageDelegate.qml
@@ -28,6 +28,21 @@ MainPageTile {
case "smartmeter":
page ="SmartMeterDeviceListPage.qml";
break;
+ case "garagegate":
+ page = "GarageDeviceListPage.qml";
+ break;
+ case "awning":
+ case "extendedAwning":
+ page = "AwningDeviceListPage.qml";
+ break;
+ case "blind":
+ case "extendedBlind":
+ page = "ShutterDeviceListPage.qml";
+ break;
+ case "shutter":
+ case "extendedShutter":
+ page = "ShutterDeviceListPage.qml";
+ break;
default:
page = "GenericDeviceListPage.qml"
}