diff --git a/libnymea-app-core/devices.cpp b/libnymea-app-core/devices.cpp index 2b41170a..1b231155 100644 --- a/libnymea-app-core/devices.cpp +++ b/libnymea-app-core/devices.cpp @@ -127,6 +127,9 @@ QVariant Devices::data(const QModelIndex &index, int role) const if (interfaces.contains("evcharger")) { return "evcharger"; } + if (interfaces.contains("powersocket")) { + return "powersocket"; + } return "uncategorized"; } diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index bf1b791c..e83d1940 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -168,5 +168,6 @@ ui/images/stock_music.svg ui/images/stock_video.svg ui/images/sensors/presence.svg + ui/images/powersocket.svg diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 01200294..276d5240 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -145,5 +145,7 @@ ui/system/ServerConfigurationDialog.qml ui/system/MqttPolicyPage.qml ui/devicepages/BoolSensorDevicePage.qml + ui/devicepages/PowersocketDevicePage.qml + ui/devicelistpages/PowerSocketsDeviceListPage.qml diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index 2239f8e1..456764d4 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -52,7 +52,7 @@ ApplicationWindow { rootItem.handleCloseEvent(close) } - property var supportedInterfaces: ["light", "weather", "sensor", "media", "garagegate", "awning", "shutter", "blind", "heating", "smartmeter", "evcharger", "accesscontrol", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"] + property var supportedInterfaces: ["light", "weather", "media", "garagegate", "awning", "shutter", "blind", "heating", "powersocket", "sensor", "smartmeter", "evcharger", "accesscontrol", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"] function interfaceToString(name) { switch(name) { case "light": @@ -106,6 +106,8 @@ ApplicationWindow { return qsTr("Heatings"); case "evcharger": return qsTr("EV-chargers"); + case "powersocket": + return qsTr("Power sockets") case "uncategorized": return qsTr("Uncategorized") default: @@ -155,6 +157,8 @@ ApplicationWindow { case "media": case "mediacontroller": return Qt.resolvedUrl("images/mediaplayer-app-symbolic.svg") + case "powersocket": + return Qt.resolvedUrl("images/powersocket.svg") case "button": case "longpressbutton": case "simplemultibutton": @@ -284,6 +288,8 @@ ApplicationWindow { page = "FingerprintReaderDevicePage.qml"; } else if (interfaceList.indexOf("smartmeter") >= 0) { page = "SmartMeterDevicePage.qml" + } else if (interfaceList.indexOf("powersocket") >= 0) { + page = "PowersocketDevicePage.qml"; } else { page = "GenericDevicePage.qml"; } diff --git a/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml new file mode 100644 index 00000000..6de2cab5 --- /dev/null +++ b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml @@ -0,0 +1,98 @@ +import QtQuick 2.5 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 +import "../components" +import "../delegates" +import QtQuick.Controls.Material 2.1 + +DeviceListPageBase { + id: root + + header: GuhHeader { + text: qsTr("My %1").arg(app.interfaceToString("powersocket")) + + onBackPressed: { + pageStack.pop() + } + } + + ListView { + anchors.fill: parent + model: devicesProxy + spacing: app.margins + + delegate: Pane { + id: itemDelegate + width: parent.width + + property var device: devicesProxy.get(index); + property var 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 var powerStateType: deviceClass.stateTypes.findByName("power"); + property var powerActionType: deviceClass.actionTypes.findByName("power"); + property var powerState: device.states.getState(powerStateType.id) + + 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.connectedState !== null && itemDelegate.connectedState.value === false + ? "red" + : itemDelegate.powerState.value === true ? app.accentColor : keyColor + name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ? + "../images/dialog-warning-symbolic.svg" + : app.interfaceToIcon("powersocket") + } + } + + Label { + Layout.fillWidth: true + text: model.name + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + Switch { + checked: itemDelegate.powerState.value === true + onClicked: { + var params = []; + var param1 = {}; + param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id; + param1["value"] = checked; + params.push(param1) + engine.deviceManager.executeAction(device.id, itemDelegate.powerActionType.id, params) + } + } + } + } + onClicked: { + enterPage(index, false) + } + } + } + } +} diff --git a/nymea-app/ui/devicepages/PowersocketDevicePage.qml b/nymea-app/ui/devicepages/PowersocketDevicePage.qml new file mode 100644 index 00000000..ebb8e9a2 --- /dev/null +++ b/nymea-app/ui/devicepages/PowersocketDevicePage.qml @@ -0,0 +1,62 @@ +import QtQuick 2.5 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.1 +import QtQuick.Controls.Material 2.1 +import Nymea 1.0 +import "../components" + +DevicePageBase { + id: root + + 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 + + Item { + Layout.preferredWidth: Math.max(app.iconSize * 4, 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 + + AbstractButton { + height: Math.min(parent.height, parent.width) + width: height + anchors.centerIn: parent + Rectangle { + anchors.fill: parent + color: "white" + border.color: root.powerState.value === true ? app.accentColor : bulbIcon.keyColor + border.width: 4 + radius: width / 2 + } + + ColorIcon { + id: bulbIcon + anchors.fill: parent + anchors.margins: app.margins * 1.5 + name: "../images/powersocket.svg" + color: root.powerState.value === true ? app.accentColor : keyColor + } + onClicked: { + var params = [] + var param = {} + param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id; + param["value"] = !root.powerState.value; + params.push(param) + engine.deviceManager.executeAction(root.device.id, root.powerStateType.id, params); + } + } + } + } +} diff --git a/nymea-app/ui/images/powersocket.svg b/nymea-app/ui/images/powersocket.svg new file mode 100644 index 00000000..4a4da53a --- /dev/null +++ b/nymea-app/ui/images/powersocket.svg @@ -0,0 +1,120 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/nymea-app/ui/mainviews/DevicesPageDelegate.qml b/nymea-app/ui/mainviews/DevicesPageDelegate.qml index a5f94f49..a92e3a0b 100644 --- a/nymea-app/ui/mainviews/DevicesPageDelegate.qml +++ b/nymea-app/ui/mainviews/DevicesPageDelegate.qml @@ -43,6 +43,9 @@ MainPageTile { case "extendedShutter": page = "ShutterDeviceListPage.qml"; break; + case "powersocket": + page = "PowerSocketsDeviceListPage.qml"; + break; default: page = "GenericDeviceListPage.qml" } @@ -98,6 +101,7 @@ MainPageTile { case "extendedshutter": case "awning": case "extendedawning": + case "powersocket": return buttonComponent default: console.warn("DevicesPageDelegate, inlineControl: Unhandled interface", model.name) @@ -119,6 +123,7 @@ MainPageTile { case "media": return devicesProxy.get(0).name; case "light": + case "powersocket": var count = 0; for (var i = 0; i < devicesProxy.count; i++) { var device = devicesProxy.get(i); @@ -289,6 +294,7 @@ MainPageTile { state.value === "Paused" ? "../images/media-playback-start.svg" : "" case "light": + case "powersocket": return "../images/system-shutdown.svg" case "garagegate": case "blind": @@ -307,6 +313,7 @@ MainPageTile { onClicked: { switch (model.name) { case "light": + case "powersocket": if (devicesProxy.count == 1) { var device = devicesProxy.get(0); var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId);