diff --git a/libnymea-app/devicesproxy.cpp b/libnymea-app/devicesproxy.cpp
index e18ded4e..4da19364 100644
--- a/libnymea-app/devicesproxy.cpp
+++ b/libnymea-app/devicesproxy.cpp
@@ -308,6 +308,21 @@ void DevicesProxy::setFilterSetupFailed(bool filterSetupFailed)
}
}
+bool DevicesProxy::filterUpdates() const
+{
+ return m_filterUpdates;
+}
+
+void DevicesProxy::setFilterUpdates(bool filterUpdates)
+{
+ if (m_filterUpdates != filterUpdates) {
+ m_filterUpdates = filterUpdates;
+ emit filterUpdatesChanged();
+ invalidateFilter();
+ emit countChanged();
+ }
+}
+
bool DevicesProxy::groupByInterface() const
{
return m_groupByInterface;
@@ -371,7 +386,15 @@ bool DevicesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) c
QString leftName = sourceModel()->data(left, Devices::RoleName).toString();
QString rightName = sourceModel()->data(right, Devices::RoleName).toString();
- return QString::localeAwareCompare(leftName, rightName) < 0;
+ int comparison = QString::localeAwareCompare(leftName, rightName);
+ if (comparison == 0) {
+ // If there are 2 identically named things we don't want undefined behavor as it may cause items
+ // to reorder randomly. Use something static like thingId as fallback
+ QString leftThingId = sourceModel()->data(left, Devices::RoleId).toString();
+ QString rightThingId = sourceModel()->data(right, Devices::RoleId).toString();
+ comparison = QString::localeAwareCompare(leftThingId, rightThingId);
+ }
+ return comparison < 0;
}
bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
@@ -452,6 +475,15 @@ bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_pa
}
}
+ if (m_filterUpdates) {
+ if (!deviceClass->interfaces().contains("update")) {
+ return false;
+ }
+ if (device->stateValue(deviceClass->stateTypes()->findByName("updateStatus")->id()).toString() == "idle") {
+ return false;
+ }
+ }
+
if (!m_nameFilter.isEmpty()) {
if (!device->name().toLower().contains(m_nameFilter.toLower().trimmed())) {
return false;
diff --git a/libnymea-app/devicesproxy.h b/libnymea-app/devicesproxy.h
index 7ade3750..d8a74cf6 100644
--- a/libnymea-app/devicesproxy.h
+++ b/libnymea-app/devicesproxy.h
@@ -67,6 +67,8 @@ class DevicesProxy : public QSortFilterProxyModel
Q_PROPERTY(bool filterSetupFailed READ filterSetupFailed WRITE setFilterSetupFailed NOTIFY filterSetupFailedChanged)
+ Q_PROPERTY(bool filterUpdates READ filterUpdates WRITE setFilterUpdates NOTIFY filterUpdatesChanged)
+
Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged)
public:
@@ -120,12 +122,15 @@ public:
bool filterSetupFailed() const;
void setFilterSetupFailed(bool filterSetupFailed);
- bool groupByInterface() const;
- void setGroupByInterface(bool groupByInterface);
+ bool filterUpdates() const;
+ void setFilterUpdates(bool filterUpdates);
+
+ bool groupByInterface() const;
+ void setGroupByInterface(bool groupByInterface);
Q_INVOKABLE Device *get(int index) const;
- Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
- Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
+ Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
+ Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
signals:
void engineChanged();
@@ -144,6 +149,7 @@ signals:
void filterBatteryCriticalChanged();
void filterDisconnectedChanged();
void filterSetupFailedChanged();
+ void filterUpdatesChanged();
void groupByInterfaceChanged();
void countChanged();
@@ -168,6 +174,7 @@ private:
bool m_filterBatteryCritical = false;
bool m_filterDisconnected = false;
bool m_filterSetupFailed = false;
+ bool m_filterUpdates = false;
bool m_groupByInterface = false;
diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc
index e91ed782..bea8e76d 100644
--- a/nymea-app/resources.qrc
+++ b/nymea-app/resources.qrc
@@ -47,9 +47,7 @@
ui/components/Led.qml
ui/components/ProgressButton.qml
ui/customviews/GenericTypeLogView.qml
- ui/customviews/CustomViewBase.qml
ui/customviews/WeatherView.qml
- ui/customviews/NotificationsView.qml
ui/devicepages/MediaThingPage.qml
ui/devicepages/ButtonDevicePage.qml
ui/devicepages/GenericDevicePage.qml
@@ -223,5 +221,9 @@
ui/magic/WriteNfcTagPage.qml
ui/components/MediaPlayer.qml
ui/components/InfoPane.qml
+ ui/components/BigTile.qml
+ ui/components/ThingStatusIcons.qml
+ ui/components/InfoPaneBase.qml
+ ui/components/ThingInfoPane.qml
diff --git a/nymea-app/styles.qrc b/nymea-app/styles.qrc
index ae4308a9..cab2e807 100644
--- a/nymea-app/styles.qrc
+++ b/nymea-app/styles.qrc
@@ -20,6 +20,5 @@
styles/energize/Button.qml
styles/energize/logo.svg
styles/energize/Page.qml
- styles/energize/Pane.qml
diff --git a/nymea-app/styles/energize/ApplicationWindow.qml b/nymea-app/styles/energize/ApplicationWindow.qml
index fb87969c..30e63822 100644
--- a/nymea-app/styles/energize/ApplicationWindow.qml
+++ b/nymea-app/styles/energize/ApplicationWindow.qml
@@ -58,7 +58,7 @@ ApplicationWindow {
property color headerForegroundColor: "#79a79f"
// The font color
- property color foregroundColor: "#69938c"
+ property color foregroundColor: "#638B87"
// The color of selected/highlighted things
property color accentColor: "#8cc1b6"
diff --git a/nymea-app/styles/energize/Button.qml b/nymea-app/styles/energize/Button.qml
index e65426b7..975ba9ac 100644
--- a/nymea-app/styles/energize/Button.qml
+++ b/nymea-app/styles/energize/Button.qml
@@ -64,7 +64,7 @@ T.Button {
font.overline: control.font.overline
font.pixelSize: app.smallFont
font.weight: Font.Bold
- color: app.foregroundColor
+ color: app.backgroundColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
diff --git a/nymea-app/styles/energize/Pane.qml b/nymea-app/styles/energize/Pane.qml
deleted file mode 100644
index 4a283139..00000000
--- a/nymea-app/styles/energize/Pane.qml
+++ /dev/null
@@ -1,76 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* 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.9
-import QtQuick.Templates 2.2 as T
-import QtQuick.Controls.Material 2.2
-import QtQuick.Controls.Material.impl 2.2
-import QtGraphicalEffects 1.0
-
-T.Pane {
- id: control
-
- implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding)
-
- contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0)
- contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0)
-
- padding: 12
-
- background: Rectangle {
- color: control.Material.backgroundColor
- radius: 5
-
- layer.enabled: control.enabled && control.Material.elevation > 0
- layer.effect: ElevationEffect {
- elevation: control.Material.elevation
- }
- }
-
- layer.effect: Item {
- width: control.width
- height: control.height
- Rectangle {
- id: mask
- anchors.fill: parent
- color: "#FFFFFFFF"
- radius: 5
- }
- OpacityMask {
- width: control.width
- height: control.height
- source: control
- maskSource: mask
- }
- }
- layer.enabled: true
-
-}
diff --git a/nymea-app/ui/MainPage.qml b/nymea-app/ui/MainPage.qml
index 6289f612..c88e870c 100644
--- a/nymea-app/ui/MainPage.qml
+++ b/nymea-app/ui/MainPage.qml
@@ -202,7 +202,7 @@ Page {
text: engine.systemController.updateRunning ? qsTr("System update in progress...") : qsTr("%n system update(s) available", "", updatesModel.count)
imageSource: "../images/system-update.svg"
rotatingIcon: engine.systemController.updateRunning
- onPaneClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
+ onClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
PackagesFilterModel {
id: updatesModel
diff --git a/nymea-app/ui/components/BigTile.qml b/nymea-app/ui/components/BigTile.qml
new file mode 100644
index 00000000..b66c13d2
--- /dev/null
+++ b/nymea-app/ui/components/BigTile.qml
@@ -0,0 +1,69 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.2
+import QtQuick.Controls 2.1
+import Nymea 1.0
+
+Item {
+ id: root
+ implicitHeight: layout.implicitHeight + app.margins
+
+ property Thing thing: null
+
+ property bool showHeader: true
+
+ property alias contentItem: content.contentItem
+
+ property alias leftPadding: content.leftPadding
+ property alias rightPadding: content.rightPadding
+ property alias topPadding: content.topPadding
+ property alias bottomPadding: content.bottomPadding
+
+ signal clicked();
+
+ Rectangle {
+ id: background
+ anchors.fill: parent
+ anchors.margins: app.margins / 2
+ radius: 6
+
+ gradient: Gradient {
+ GradientStop {
+ position: (headerRow.height + app.margins) / background.height
+ color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05))
+ }
+ GradientStop {
+ position: (headerRow.height + app.margins) / background.height
+ color:root.showHeader ?
+ Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .1))
+ : Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05))
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: layout
+ spacing: 0
+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins / 2 }
+
+ RowLayout {
+ id: headerRow
+ visible: root.showHeader
+ Layout.margins: app.margins / 2
+ Label {
+ Layout.fillWidth: true
+ text: root.thing.name
+ elide: Text.ElideRight
+ }
+ ThingStatusIcons {
+ thing: root.thing
+ }
+ }
+
+ ItemDelegate {
+ id: content
+ Layout.fillWidth: true
+ height: contentItem.implicitHeight
+ onClicked: root.clicked()
+ }
+ }
+}
diff --git a/nymea-app/ui/components/InfoPane.qml b/nymea-app/ui/components/InfoPane.qml
index 1acea6a0..882f5184 100644
--- a/nymea-app/ui/components/InfoPane.qml
+++ b/nymea-app/ui/components/InfoPane.qml
@@ -3,89 +3,53 @@ import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.2
-Item {
+InfoPaneBase {
id: root
- implicitHeight: d.shownHeight
- visible: d.shownHeight > 0
property alias text: textLabel.text
property alias imageSource: icon.name
property alias buttonText: button.text
- property alias color: background.color
- property alias textColor: textLabel.color
+ property color textColor: "white"
property bool rotatingIcon: false
- property bool shown: false
-
- function show() {
- shown = true;
- }
- function hide() {
- shown = false;
- }
-
- signal paneClicked();
signal buttonClicked();
- QtObject {
- id: d
- property int shownHeight: shown ? contentRow.implicitHeight : 0
- Behavior on shownHeight { NumberAnimation { easing.type: Easing.InOutQuad; duration: 150 } }
- }
+ contentItem: RowLayout {
+ id: contentRow
+ anchors { left: parent.left; top: parent.top; right: parent.right }
- Pane {
- anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
- Material.elevation: 2
- padding: 0
- height: contentRow.implicitHeight
+ Label {
+ id: textLabel
+ color: root.textColor
+ font.pixelSize: app.smallFont
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignRight
+ wrapMode: Text.WordWrap
+ }
+ ColorIcon {
+ id: icon
+ Layout.preferredHeight: app.smallIconSize
+ Layout.preferredWidth: height
+ color: root.textColor
+ visible: name.length > 0
- MouseArea {
- anchors.fill: parent
- onClicked: root.paneClicked()
+ RotationAnimation on rotation {
+ from: 0
+ to: 360
+ duration: 2000
+ loops: Animation.Infinite
+ running: root.rotatingIcon
+ onStopped: icon.rotation = 0;
+ }
}
- Rectangle {
- id: background
- color: app.accentColor
- anchors.fill: parent
-
- RowLayout {
- id: contentRow
- anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: app.margins; rightMargin: app.margins }
- Label {
- id: textLabel
- color: "white"
- font.pixelSize: app.smallFont
- Layout.fillWidth: true
- Layout.margins: app.margins * .4
- horizontalAlignment: Text.AlignRight
- wrapMode: Text.WordWrap
- }
- ColorIcon {
- id: icon
- height: app.smallIconSize
- width: height
- color: "white"
- visible: name.length > 0
-
- RotationAnimation on rotation {
- from: 0
- to: 360
- duration: 2000
- loops: Animation.Infinite
- running: root.rotatingIcon
- onStopped: icon.rotation = 0;
- }
- }
-
- Button {
- id: button
- visible: text.length > 0
- onClicked: root.buttonClicked()
- }
- }
+ Button {
+ id: button
+ Layout.leftMargin: app.margins
+ visible: text.length > 0
+ onClicked: root.buttonClicked()
}
}
}
diff --git a/nymea-app/ui/components/InfoPaneBase.qml b/nymea-app/ui/components/InfoPaneBase.qml
new file mode 100644
index 00000000..1664484f
--- /dev/null
+++ b/nymea-app/ui/components/InfoPaneBase.qml
@@ -0,0 +1,59 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.2
+import QtQuick.Controls.Material 2.2
+import QtQuick.Layouts 1.2
+
+Item {
+ id: root
+ implicitHeight: d.shownHeight
+ visible: d.shownHeight > 0
+
+ property alias color: background.color
+ property bool shown: false
+
+ property alias contentItem: content.data
+
+ function show() {
+ shown = true;
+ }
+ function hide() {
+ shown = false;
+ }
+
+ signal clicked();
+
+ QtObject {
+ id: d
+ property int shownHeight: shown ? content.implicitHeight : 0
+ Behavior on shownHeight { NumberAnimation { easing.type: Easing.InOutQuad; duration: 150 } }
+ }
+
+ Pane {
+ anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
+ Material.elevation: 2
+ leftPadding: 0
+ rightPadding: 0
+ bottomPadding: 0
+ topPadding: 0
+ height: content.implicitHeight
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: root.clicked()
+ }
+
+ Rectangle {
+ id: background
+ color: app.accentColor
+ anchors.fill: parent
+ }
+
+ Item {
+ id: content
+ anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: app.margins; rightMargin: app.margins; topMargin: app.margins / 2 }
+ implicitHeight: childrenRect.height + app.margins
+ }
+ }
+}
+
+
diff --git a/nymea-app/ui/components/MainPageTile.qml b/nymea-app/ui/components/MainPageTile.qml
index 3305c9b4..8c466eba 100644
--- a/nymea-app/ui/components/MainPageTile.qml
+++ b/nymea-app/ui/components/MainPageTile.qml
@@ -48,6 +48,7 @@ Item {
property int signalStrength: 0
property int setupStatus: Thing.ThingSetupStatusNone
property bool batteryCritical: false
+ property bool updateStatus: false
property alias contentItem: innerContent.children
@@ -119,7 +120,10 @@ Item {
spacing: 0
ColumnLayout {
Layout.fillHeight: true
- Layout.margins: app.margins
+ Layout.topMargin: app.margins
+ Layout.leftMargin: app.margins
+ Layout.rightMargin: app.margins
+ Layout.bottomMargin: app.margins / 2
Item {
visible: backgroundImg.status !== Image.Ready
Layout.fillWidth: true
@@ -171,10 +175,17 @@ Item {
}
}
- Row {
+ RowLayout {
id: quickAlertPane
anchors { top: parent.top; right: parent.right; margins: app.margins }
- spacing: app.margins / 2
+ ColorIcon {
+ height: app.smallIconSize
+ width: height
+ name: "../images/system-update.svg"
+ color: app.accentColor
+ visible: root.updateStatus
+ }
+
ColorIcon {
height: app.smallIconSize
width: height
diff --git a/nymea-app/ui/components/MainViewBase.qml b/nymea-app/ui/components/MainViewBase.qml
index baf58b3a..421655eb 100644
--- a/nymea-app/ui/components/MainViewBase.qml
+++ b/nymea-app/ui/components/MainViewBase.qml
@@ -36,13 +36,15 @@ import Nymea 1.0
import "../components"
import "../delegates"
-MouseArea {
+Item {
id: root
- // Prevent scroll events to swipe left/right in case they fall through the grid
- preventStealing: true
- onWheel: wheel.accepted = true
-
property string title: ""
+ // Prevent scroll events to swipe left/right in case they fall through the grid
+ MouseArea {
+ anchors.fill: parent
+ preventStealing: true
+ onWheel: wheel.accepted = true
+ }
}
diff --git a/nymea-app/ui/components/NymeaListItemDelegate.qml b/nymea-app/ui/components/NymeaListItemDelegate.qml
index 7ddfc47e..577a2eec 100644
--- a/nymea-app/ui/components/NymeaListItemDelegate.qml
+++ b/nymea-app/ui/components/NymeaListItemDelegate.qml
@@ -151,7 +151,7 @@ SwipeDelegate {
ColorIcon {
id: secondaryIcon
- Layout.preferredHeight: app.iconSize * .5
+ Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
visible: name.length > 0
MouseArea {
@@ -165,7 +165,7 @@ SwipeDelegate {
ColorIcon {
id: tertiaryIcon
- Layout.preferredHeight: app.iconSize * .5
+ Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
visible: name.length > 0
MouseArea {
@@ -179,7 +179,7 @@ SwipeDelegate {
ColorIcon {
id: progressionIcon
- Layout.preferredHeight: app.iconSize * .6
+ Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
name: "../images/next.svg"
visible: root.progressive
diff --git a/nymea-app/ui/components/ThingInfoPane.qml b/nymea-app/ui/components/ThingInfoPane.qml
new file mode 100644
index 00000000..66b2945d
--- /dev/null
+++ b/nymea-app/ui/components/ThingInfoPane.qml
@@ -0,0 +1,68 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.2
+import QtQuick.Controls.Material 2.2
+import QtQuick.Layouts 1.2
+import Nymea 1.0
+
+InfoPaneBase {
+ id: root
+
+ property Thing thing: null
+
+ readonly property bool setupInProgress: root.thing.setupStatus == Thing.ThingSetupStatusInProgress
+ readonly property bool setupFailure: root.thing.setupStatus == Thing.ThingSetupStatusFailed
+ readonly property State batteryState: root.thing.stateByName("batteryLevel")
+ readonly property State batteryCriticalState: root.thing.stateByName("batteryCritical")
+ readonly property State connectedState: root.thing.stateByName("connected")
+ readonly property State signalStrengthState: root.thing.stateByName("signalStrength")
+ readonly property State updateStatusState: root.thing.stateByName("updateStatus")
+ readonly property bool updateAvailable: updateStatusState && updateStatusState.value === "available"
+ readonly property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
+ readonly property bool isWireless: root.thing.thingClass.interfaces.indexOf("wirelessconnectable") >= 0
+ readonly property bool alertState: setupFailure ||
+ (connectedState !== null && connectedState.value === false) ||
+ (batteryCriticalState !== null && batteryCriticalState.value === true)
+ readonly property bool batteryCritical: batteryCriticalState && batteryCriticalState.value === true
+ readonly property bool highlightState: updateAvailable || updateRunning
+
+ shown: setupInProgress || setupFailure || batteryState !== null || (connectedState !== null && connectedState.value === false) || signalStrengthState !== null || updateAvailable
+
+ color: alertState ? "red"
+ : highlightState ? app.accentColor : "transparent"
+
+ contentItem: RowLayout {
+ id: contentRow
+ anchors { left: parent.left; top: parent.top; right: parent.right }
+
+ Label {
+ id: textLabel
+ color: root.alertState || root.highlightState ? "white" : app.foregroundColor
+ font.pixelSize: app.smallFont
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignRight
+ wrapMode: Text.WordWrap
+ text: root.setupInProgress ?
+ qsTr("Thing is being set up...")
+ : root.setupFailure ?
+ (root.thing.setupDisplayMessage.length > 0 ? root.thing.setupDisplayMessage : qsTr("Thing setup failed!"))
+ : (root.connectedState !== null && root.connectedState.value === false) ?
+ qsTr("Thing is not connected!")
+ : root.updateAvailable ?
+ qsTr("Update available!")
+ : root.updateRunning ?
+ qsTr("Updating...")
+ : root.batteryCritical ?
+ qsTr("Thing runs out of battery!")
+ : ""
+
+ }
+
+ ThingStatusIcons {
+ thing: root.thing
+ color: root.alertState || root.highlightState ? "white" : keyColor
+
+ }
+ }
+}
+
+
diff --git a/nymea-app/ui/components/ThingStatusIcons.qml b/nymea-app/ui/components/ThingStatusIcons.qml
new file mode 100644
index 00000000..5bd1d225
--- /dev/null
+++ b/nymea-app/ui/components/ThingStatusIcons.qml
@@ -0,0 +1,46 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.1
+import Nymea 1.0
+
+RowLayout {
+ id: root
+ Layout.fillWidth: false
+
+ property Thing thing: null
+
+ property color color: keyColor
+ readonly property color keyColor: updateStatusIcon.keyColor
+
+ UpdateStatusIcon {
+ id: updateStatusIcon
+ Layout.preferredHeight: app.smallIconSize
+ Layout.preferredWidth: height
+ thing: root.thing
+ visible: updateAvailable || updateRunning
+ Binding { target: updateStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor }
+ }
+ BatteryStatusIcon {
+ id: batteryStatusIcon
+ Layout.preferredHeight: app.smallIconSize
+ Layout.preferredWidth: height
+ thing: root.thing
+ visible: root.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
+ Binding { target: batteryStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor }
+ }
+ ConnectionStatusIcon {
+ id: connectionStatusIcon
+ Layout.preferredHeight: app.smallIconSize
+ Layout.preferredWidth: height
+ thing: root.thing
+ visible: root.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
+ Binding { target: connectionStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor }
+ }
+ SetupStatusIcon {
+ id: setupStatusIcon
+ Layout.preferredHeight: app.smallIconSize
+ Layout.preferredWidth: height
+ thing: root.thing
+ visible: setupFailed || setupInProgress
+ Binding { target: setupStatusIcon; property: "color"; value: root.color; when: root.color !== root.keyColor }
+ }
+}
diff --git a/nymea-app/ui/components/UpdateStatusIcon.qml b/nymea-app/ui/components/UpdateStatusIcon.qml
index 24d65b89..f59e90b5 100644
--- a/nymea-app/ui/components/UpdateStatusIcon.qml
+++ b/nymea-app/ui/components/UpdateStatusIcon.qml
@@ -11,6 +11,7 @@ ColorIcon {
readonly property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
name: "../images/system-update.svg"
+ color: app.accentColor
RotationAnimation on rotation {
from: 0; to: 360
diff --git a/nymea-app/ui/customviews/CustomViewBase.qml b/nymea-app/ui/customviews/CustomViewBase.qml
deleted file mode 100644
index 798a9547..00000000
--- a/nymea-app/ui/customviews/CustomViewBase.qml
+++ /dev/null
@@ -1,39 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* 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
-
-Item {
-
- property var device: null
- property var deviceClass: null
- property string interfaceName
-
-}
diff --git a/nymea-app/ui/customviews/GenericTypeGraph.qml b/nymea-app/ui/customviews/GenericTypeGraph.qml
index 0ffacb48..0bfe093e 100644
--- a/nymea-app/ui/customviews/GenericTypeGraph.qml
+++ b/nymea-app/ui/customviews/GenericTypeGraph.qml
@@ -259,7 +259,7 @@ Item {
id: connectedLineSeries
onPointAdded: {
var newPoint = connectedLineSeries.at(index)
- print("pointadded", newPoint.x, newPoint.y)
+// print("pointadded", newPoint.x, newPoint.y)
}
}
@@ -284,7 +284,7 @@ Item {
id: lineSeries1
onPointAdded: {
var newPoint = lineSeries1.at(index)
- print("pointadded", newPoint.x, newPoint.y)
+// print("pointadded", newPoint.x, newPoint.y)
if (newPoint.x > lineSeries0.at(0).x) {
lineSeries0.replace(0, newPoint.x, 0)
diff --git a/nymea-app/ui/customviews/NotificationsView.qml b/nymea-app/ui/customviews/NotificationsView.qml
deleted file mode 100644
index e8362174..00000000
--- a/nymea-app/ui/customviews/NotificationsView.qml
+++ /dev/null
@@ -1,79 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-*
-* 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.Layouts 1.1
-import QtQuick.Controls 2.1
-import "../components"
-import Nymea 1.0
-
-CustomViewBase {
- id: root
- height: grid.implicitHeight + app.margins * 2
-
- ColumnLayout {
- id: grid
- anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins }
- Label {
- Layout.fillWidth: true
- text: qsTr("Send a notification now:")
- }
- TextArea {
- id: titleTextArea
- placeholderText: qsTr("Title")
- Layout.fillWidth: true
- }
- TextArea {
- id: bodyTextArea
- placeholderText: qsTr("Text")
- Layout.fillWidth: true
- }
- Button {
- Layout.fillWidth: true
- text: qsTr("Send")
- onClicked: {
-
- var params = []
- var param1 = {}
- print("bla:", root.deviceClass.actionTypes.findByName("notify").paramTypes)
- var paramTypeId = root.deviceClass.actionTypes.findByName("notify").paramTypes.findByName("title").id
- param1.paramTypeId = paramTypeId
- param1.value = titleTextArea.text
- params.push(param1)
- var param2 = {}
- paramTypeId = root.deviceClass.actionTypes.findByName("notify").paramTypes.findByName("body").id
- param2.paramTypeId = paramTypeId
- param2.value = bodyTextArea.text
- params.push(param2)
- engine.deviceManager.executeAction(root.device.id, root.deviceClass.actionTypes.findByName("notify").id, params)
- }
- }
- }
-}
diff --git a/nymea-app/ui/customviews/WeatherView.qml b/nymea-app/ui/customviews/WeatherView.qml
index ccc4a86c..a35b8ad5 100644
--- a/nymea-app/ui/customviews/WeatherView.qml
+++ b/nymea-app/ui/customviews/WeatherView.qml
@@ -34,30 +34,21 @@ import QtQuick.Controls 2.1
import "../components"
import Nymea 1.0
-CustomViewBase {
+Item {
id: root
- height: grid.implicitHeight + app.margins * 2
+ implicitHeight: grid.implicitHeight + app.margins * 2
- readonly property StateType weatherConditionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("weatherCondition") : null
- readonly property State weatherConditionState: weatherConditionStateType && device.states ? device.states.getState(weatherConditionStateType.id) : null
+ property Thing thing: null
- readonly property StateType weatherDescriptionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("weatherDescription") : null
- readonly property State weatherDescriptionState: weatherDescriptionStateType && device.states ? device.states.getState(weatherDescriptionStateType.id) : null
+ readonly property State weatherConditionState: thing.stateByName("weatherCondition")
+ readonly property State weatherDescriptionState: thing.stateByName("weatherDescription")
+ readonly property State temperatureState: thing.stateByName("temperature")
+ readonly property State humidityState: thing.stateByName("humidity")
+ readonly property State pressureState: thing.stateByName("pressure")
+ readonly property State windDirectionState: thing.stateByName("windDirection")
+ readonly property State windSpeedState: thing.stateByName("windSpeed")
- readonly property StateType temperatureStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("temperature") : null
- readonly property State temperatureState: temperatureStateType && device.states ? device.states.getState(temperatureStateType.id) : null
-
- readonly property StateType humidityStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("humidity") : null
- readonly property State humidityState: humidityStateType && device.states ? device.states.getState(humidityStateType.id) : null
-
- readonly property StateType pressureStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("pressure") : null
- readonly property State pressureState: pressureStateType && device.states ? device.states.getState(pressureStateType.id) : null
-
- readonly property StateType windDirectionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("windDirection") : null
- readonly property State windDirectionState: windDirectionStateType && device.states ? device.states.getState(windDirectionStateType.id) : null
-
- readonly property StateType windSpeedStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("windSpeed") : null
- readonly property State windSpeedState: windSpeedStateType && device.states ? device.states.getState(windSpeedStateType.id) : null
+ readonly property StateType temperatureStateType: thing.thingClass.stateTypes.findByName("temperature")
ColumnLayout {
id: grid
@@ -86,7 +77,6 @@ CustomViewBase {
Label {
text: (temperatureState ? Math.round(Types.toUiValue(temperatureState.value, temperatureStateType.unit) * 10) / 10 : "N/A") + " °"
Layout.fillWidth: true
- font.pixelSize: app.largeFont
horizontalAlignment: Text.AlignHCenter
}
ColorIcon {
diff --git a/nymea-app/ui/delegates/InterfaceTile.qml b/nymea-app/ui/delegates/InterfaceTile.qml
index 6c730d93..bb2ee7f8 100644
--- a/nymea-app/ui/delegates/InterfaceTile.qml
+++ b/nymea-app/ui/delegates/InterfaceTile.qml
@@ -44,6 +44,7 @@ MainPageTile {
isWireless: devicesSubProxyConnectables.count > 0 && devicesSubProxyConnectables.get(0).thingClass.interfaces.indexOf("wirelessconnectable") >= 0
batteryCritical: devicesSubProxyBattery.count > 0
setupStatus: thingsSubProxySetupFailure.count > 0 ? Thing.ThingSetupStatusFailed : Thing.ThingSetupStatusComplete
+ updateStatus: thingsSubProxyUpdates.count > 0
property Interface iface: null
property alias filterTagId: devicesProxy.filterTagId
@@ -139,6 +140,12 @@ MainPageTile {
parentProxy: devicesProxy
filterSetupFailed: true
}
+ ThingsProxy {
+ id: thingsSubProxyUpdates
+ engine: _engine
+ parentProxy: devicesProxy
+ filterUpdates: true
+ }
property int currentDeviceIndex: 0
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
@@ -220,7 +227,7 @@ MainPageTile {
var d = devicesProxy.get(i);
var st = d.deviceClass.stateTypes.findByName("playbackStatus")
var s = d.states.getState(st.id)
- s.valueChanged.connect(function() {updateTile()})
+ s.valueChanged.connect(function() {inlineMediaControl.updateTile()})
}
updateTile();
}
diff --git a/nymea-app/ui/delegates/ThingTile.qml b/nymea-app/ui/delegates/ThingTile.qml
index 930ac767..07f76191 100644
--- a/nymea-app/ui/delegates/ThingTile.qml
+++ b/nymea-app/ui/delegates/ThingTile.qml
@@ -45,6 +45,7 @@ MainPageTile {
disconnected: connectedState && connectedState.value === false
signalStrength: signalStrengthState ? signalStrengthState.value : -1
setupStatus: device.setupStatus
+ updateStatus: updateStatusState && updateStatusState.value !== "idle"
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
@@ -54,6 +55,7 @@ MainPageTile {
readonly property State signalStrengthState: device.stateByName("signalStrength")
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
readonly property State artworkState: deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("artwork").id) : null
+ readonly property State updateStatusState: device.stateByName("updateStatus")
contentItem: Loader {
id: loader
diff --git a/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml
index bb442646..3ca3b42e 100644
--- a/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/ClosablesDeviceListPage.qml
@@ -83,88 +83,69 @@ DeviceListPageBase {
}
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: devicesProxy
- spacing: app.margins
+ contentHeight: contentGrid.implicitHeight
+ topMargin: app.margins / 2
- delegate: Pane {
- id: itemDelegate
- width: parent.width
+ GridLayout {
+ id: contentGrid
+ width: parent.width - app.margins
+ anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: root.thingsProxy
- property bool inline: width > 500
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
+ showHeader: false
+ enabled: connectedState == null || connectedState.value === true
+ topPadding: 0
+ bottomPadding: 0
- property Device device: devicesProxy.getDevice(model.id)
- property DeviceClass deviceClass: device.deviceClass
+ onClicked: root.enterPage(index)
- property var connectedStateType: deviceClass.stateTypes.findByName("connected");
- property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
+ property State connectedState: thing.stateByName("connected")
+ property State movingState: thing.stateByName("moving")
+ property State percentageState: thing.stateByName("percentage")
- 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
+ contentItem: RowLayout {
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"
- }
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ color: itemDelegate.movingState && itemDelegate.movingState.value === true
+ ? app.accentColor
+ : keyColor
+ name: itemDelegate.percentageState
+ ? 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
+ text: itemDelegate.thing.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
- }
+ ThingStatusIcons {
+ thing: itemDelegate.thing
+ }
+
+ ShutterControls {
+ id: shutterControls
+ Layout.fillWidth: false
+ height: parent.height
+ device: itemDelegate.thing
+ invert: root.invertControls
}
}
}
- onClicked: {
- enterPage(index)
- }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/GarageThingListPage.qml b/nymea-app/ui/devicelistpages/GarageThingListPage.qml
index c6eaa856..2d9b730c 100644
--- a/nymea-app/ui/devicelistpages/GarageThingListPage.qml
+++ b/nymea-app/ui/devicelistpages/GarageThingListPage.qml
@@ -44,242 +44,86 @@ DeviceListPageBase {
header: NymeaHeader {
id: header
onBackPressed: pageStack.pop()
- text: root.title
+ text: qsTr("Garage doors")
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: devicesProxy
- spacing: app.margins
+ contentHeight: contentGrid.implicitHeight
+ topMargin: app.margins / 2
- delegate: Pane {
- id: itemDelegate
- width: parent.width
+ GridLayout {
+ id: contentGrid
+ width: parent.width - app.margins
+ anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: root.thingsProxy
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
+ showHeader: false
+ enabled: connectedState == null || connectedState.value === true
+ topPadding: 0
+ bottomPadding: 0
- property bool inline: width > 500
+ onClicked: root.enterPage(index)
- property Device device: devicesProxy.getDevice(model.id)
- property Device thing: device
- property DeviceClass deviceClass: device.deviceClass
+ property State connectedState: thing.stateByName("connected")
+ property State movingState: thing.stateByName("moving")
+ property State percentageState: thing.stateByName("percentage")
- readonly property bool isImpulseBased: device.deviceClass.interfaces.indexOf("impulsegaragedoor") >= 0
- readonly property bool isStateful: device.deviceClass.interfaces.indexOf("statefulgaragedoor") >= 0
- || device.deviceClass.interfaces.indexOf("garagegate") >= 0 // garagegate did not inherit garagedoor before 0.23
- readonly property bool isExtended: device.deviceClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
+ readonly property bool isImpulseBased: thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0
- property var connectedStateType: deviceClass.stateTypes.findByName("connected");
- property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
+ contentItem: RowLayout {
+ spacing: app.margins
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ color: itemDelegate.movingState && itemDelegate.movingState.value === true
+ ? app.accentColor
+ : keyColor
+ name: itemDelegate.percentageState
+ ? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
+ : root.iconBasename + "-050.svg"
+ }
- property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
- property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
- property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.thing.name
+ elide: Text.ElideRight
+ }
- Material.elevation: 1
- topPadding: 0
- bottomPadding: 0
- leftPadding: 0
- rightPadding: 0
- contentItem: ItemDelegate {
- id: contentItem
- implicitHeight: contentLoader.item.implicitHeight
+ ThingStatusIcons {
+ thing: itemDelegate.thing
+ }
- topPadding: 0
-
- contentItem: Loader {
- id: contentLoader
- enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
- sourceComponent: isImpulseBased ? impulseGaragedoor
- : isExtended ? extendedStatefulGaragedoor
- : isStateful ? garagedoor
- : simpleGaragedoor
- Binding { target: contentLoader.item; property: "device"; value: itemDelegate.device }
- }
- onClicked: {
- enterPage(index)
- }
- }
- }
- }
-
- Component {
- id: impulseGaragedoor
-
- RowLayout {
- id: contentItem
- property Device device: null
-
- spacing: app.margins
- Item {
- Layout.preferredHeight: app.iconSize
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
-
- ColorIcon {
- id: icon
- anchors.fill: parent
- name: root.iconBasename + "-100.svg"
- }
- }
-
- Label {
- Layout.fillWidth: true
- text: contentItem.device.name
- elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
- }
-
- ItemDelegate {
- Layout.preferredHeight: app.iconSize * 2
- Layout.preferredWidth: height
- ColorIcon {
- anchors.centerIn: parent
- height: app.iconSize
- width: height
- name: "../images/closable-move.svg"
- }
- onClicked: {
- var actionTypeId = device.thingClass.actionTypes.findByName("triggerImpulse").id
- print("Triggering impulse", actionTypeId)
- engine.thingManager.executeAction(device.id, actionTypeId)
- }
- }
- }
- }
-
- Component {
- id: simpleGaragedoor
-
- RowLayout {
- id: contentItem
- spacing: app.margins
- property Device device: null
-
- Item {
- Layout.preferredHeight: app.iconSize
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
-
- ColorIcon {
- id: icon
- anchors.fill: parent
- color: keyColor
- name: root.iconBasename + "-100.svg"
- }
- }
-
- Label {
- Layout.fillWidth: true
- text: contentItem.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: contentItem.device
- }
- }
- }
- }
-
-
- Component {
- id: garagedoor
-
- RowLayout {
- id: contentItem
- spacing: app.margins
- property Device device: null
-
- property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
- property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
- Item {
- Layout.preferredHeight: app.iconSize
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
-
- ColorIcon {
- id: icon
- anchors.fill: parent
- color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
- ? app.accentColor
- : keyColor
- name: root.iconBasename + "-100.svg"
- }
- }
-
- Label {
- Layout.fillWidth: true
- text: contentItem.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: contentItem.device
- }
- }
- }
- }
-
-
- Component {
- id: extendedStatefulGaragedoor
-
- RowLayout {
- id: contentItem
- spacing: app.margins
- property Device device: null
-
- property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
- property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
-
- property StateType percentageStateType: device.deviceClass.stateTypes.findByName("percentage");
- property ActionType percentageActionType: device.deviceClass.actionTypes.findByName("percentage");
- property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
-
- Item {
- Layout.preferredHeight: app.iconSize
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
-
- ColorIcon {
- id: icon
- anchors.fill: parent
- color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
- ? app.accentColor
- : keyColor
- name: contentItem.percentageStateType
- ? root.iconBasename + "-" + app.pad(Math.round(contentItem.percentageState.value / 10) * 10, 3) + ".svg"
- : root.iconBasename + "-050.svg"
- }
- }
-
- Label {
- Layout.fillWidth: true
- text: contentItem.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: contentItem.device
+ ShutterControls {
+ id: shutterControls
+ Layout.fillWidth: false
+ height: parent.height
+ device: itemDelegate.thing
+ invert: root.invertControls
+ visible: !itemDelegate.isImpulseBased
+ }
+ ProgressButton {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: height
+ longpressEnabled: false
+ imageSource: "../images/closable-move.svg"
+ visible: itemDelegate.isImpulseBased
+ onClicked: {
+ var actionTypeId = itemDelegate.thing.thingClass.actionTypes.findByName("triggerImpulse").id
+ print("Triggering impulse", actionTypeId)
+ engine.thingManager.executeAction(itemDelegate.thing.id, actionTypeId)
+ }
+ }
+ }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml
index 0efbf889..88575567 100644
--- a/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/GenericDeviceListPage.qml
@@ -53,15 +53,69 @@ DeviceListPageBase {
}
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: root.devicesProxy
+ contentHeight: contentGrid.implicitHeight
+ topMargin: app.margins / 2
- delegate: ThingDelegate {
- width: parent.width
- device: engine.deviceManager.devices.getDevice(model.id);
- onClicked: {
- enterPage(index)
+ GridLayout {
+ id: contentGrid
+ width: parent.width - app.margins
+ anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: root.thingsProxy
+
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
+ showHeader: false
+ enabled: connectedState == null || connectedState.value === true
+ topPadding: 0
+ bottomPadding: 0
+
+ onClicked: root.enterPage(index)
+
+ property State connectedState: thing.stateByName("connected")
+ property State powerState: thing.stateByName("power")
+
+ contentItem: RowLayout {
+ spacing: app.margins
+
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ name: app.interfacesToIcon(itemDelegate.thing.thingClass.interfaces)
+ color: itemDelegate.powerState && itemDelegate.powerState.value === true ? app.accentColor : keyColor
+ }
+
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.thing.name
+ elide: Text.ElideRight
+ }
+
+ ThingStatusIcons {
+ thing: itemDelegate.thing
+ }
+
+ Switch {
+ visible: itemDelegate.powerState !== null
+ checked: itemDelegate.powerState.value === true
+ onClicked: {
+ var params = [];
+ var param1 = {};
+ param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
+ param1["value"] = checked;
+ params.push(param1)
+ engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
+ }
+ }
+ }
+ }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml b/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml
index 20cf2b8f..22ca1f05 100644
--- a/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/LightsDeviceListPage.qml
@@ -37,6 +37,7 @@ import Nymea 1.0
import "../components"
DeviceListPageBase {
+ id: root
header: NymeaHeader {
text: qsTr("Lights")
@@ -70,125 +71,151 @@ DeviceListPageBase {
}
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: devicesProxy
- spacing: app.margins
+ contentHeight: contentGrid.implicitHeight
+ topMargin: app.margins / 2
- delegate: Pane {
- id: itemDelegate
- width: parent.width
+ GridLayout {
+ id: contentGrid
+ width: parent.width - app.margins
+ anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: root.thingsProxy
- property bool inline: width > 500
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
+ showHeader: false
+ topPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ rightPadding: 0
+ enabled: connectedState == null || connectedState.value === true
- property Device device: devicesProxy.getDevice(model.id)
- property DeviceClass deviceClass: device.deviceClass
+ property State connectedState: thing.stateByName("connected")
+ property State powerState: thing.stateByName("power")
+ property State brightnessState: thing.stateByName("brightness")
+ property State colorState: thing.stateByName("color")
- property StateType connectedStateType: deviceClass.stateTypes.findByName("connected");
- property State connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
+ onClicked: root.enterPage(index)
- property StateType powerStateType: deviceClass.stateTypes.findByName("power");
- property ActionType powerActionType: deviceClass.actionTypes.findByName("power");
- property State powerState: device.states.getState(powerStateType.id)
+ property int pendingCommand: -1
+ property var pendingValue: null
- property StateType brightnessStateType: deviceClass.stateTypes.findByName("brightness");
- property ActionType brightnessActionType: deviceClass.actionTypes.findByName("brightness");
- property State brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
-
- property StateType colorStateType: deviceClass.stateTypes.findByName("color");
- property State colorState: colorStateType ? device.states.getState(colorStateType.id) : null
-
- Material.elevation: 1
- topPadding: 0
- bottomPadding: 0
- leftPadding: 0
- rightPadding: 0
- contentItem: ItemDelegate {
- id: contentItem
- implicitHeight: nameRow.implicitHeight
- // gradient: Gradient {
- // GradientStop { position: 0.0; color: "transparent" }
- // GradientStop { position: 1.0; color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05) }
- // }
-
-
- topPadding: 0
-
- Rectangle {
- anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
- width: app.margins / 2
- color: itemDelegate.colorStateType ? itemDelegate.colorState.value : "#00000000"
- }
-
- 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
-
-// DropShadow {
-// anchors.fill: icon
-// horizontalOffset: 0
-// verticalOffset: 0
-// radius: 2.0
-// samples: 17
-// color: app.foregroundColor
-// source: icon
-// }
-
- ColorIcon {
- id: icon
- anchors.fill: parent
- color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false
- ? "red"
- : app.accentColor
- name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ?
- "../images/dialog-warning-symbolic.svg"
- : itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
- }
+ function adjustBrightness(value) {
+ if (pendingCommand != -1) {
+ // busy, cache value
+ pendingValue = value;
+ return;
}
+ pendingCommand = engine.thingManager.executeAction(itemDelegate.thing.id,
+ itemDelegate.brightnessState.stateTypeId,
+ [{
+ paramTypeId: itemDelegate.brightnessState.stateTypeId,
+ value: value
+ }])
+ pendingValue = null;
+ }
- Label {
- Layout.fillWidth: true
- text: model.name
- elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
- }
- ThrottledSlider {
- id: inlineSlider
- visible: contentItem.enabled && itemDelegate.brightnessStateType && itemDelegate.inline
- from: 0; to: 100
- value: itemDelegate.brightnessState ? itemDelegate.brightnessState.value : 0
- onMoved: {
- var params = [];
- var param1 = {};
- param1["paramTypeId"] = itemDelegate.brightnessActionType.paramTypes.get(0).id;
- param1["value"] = value;
- params.push(param1)
- engine.deviceManager.executeAction(itemDelegate.device.id, itemDelegate.brightnessActionType.id, params)
- }
- }
- 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)
+ Connections {
+ target: engine.thingManager
+ onExecuteActionReply: {
+ if (itemDelegate.pendingCommand == commandId) {
+ itemDelegate.pendingCommand = -1;
+ if (itemDelegate.pendingValue != null) {
+ itemDelegate.adjustBrightness(pendingValue)
+ }
+ }
+ }
+ }
+
+ contentItem: Rectangle {
+ color: itemDelegate.powerState.value === true && itemDelegate.colorState ? itemDelegate.colorState.value : "#00000000"
+ implicitHeight: contentColumn.implicitHeight
+ Behavior on implicitHeight { NumberAnimation { duration: 100 } }
+ radius: 6
+
+ ColumnLayout {
+ id: contentColumn
+ anchors { left: parent.left; right: parent.right }
+ spacing: 0
+
+ RowLayout {
+ Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
+ spacing: app.margins
+
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ name: itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
+ color: itemDelegate.powerState.value === true ? app.accentColor : keyColor
+ }
+
+ Label {
+ id: nameLabel
+ Layout.fillWidth: true
+ text: itemDelegate.thing.name
+ elide: Text.ElideRight
+
+ Binding {
+ target: nameLabel
+ property: "color"
+ value: itemDelegate.colorState && NymeaUtils.isDark(app.foregroundColor) === NymeaUtils.isDark(itemDelegate.colorState.value) ?
+ app.backgroundColor : app.foregroundColor
+ when: nameLabel.enabled && itemDelegate.colorState !== null && itemDelegate.powerState.value === true
+ }
+ }
+
+ ThingStatusIcons {
+ thing: itemDelegate.thing
+ }
+
+ Switch {
+ checked: itemDelegate.powerState.value === true
+ onClicked: {
+ var params = [];
+ var param1 = {};
+ param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
+ param1["value"] = checked;
+ params.push(param1)
+ print("executing for thing:", itemDelegate.thing.id)
+ engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 12
+ visible: itemDelegate.powerState.value === true && itemDelegate.brightnessState != null
+ radius: 6
+ color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .1))
+
+ Rectangle {
+ id: knob
+ height: 12
+ width: 12
+ radius: 8
+ color: app.accentColor
+ anchors.verticalCenter: parent.verticalCenter
+ x: itemDelegate.brightnessState ? itemDelegate.brightnessState.value * (parent.width - width) / 100 : 0
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ preventStealing: true
+ onMouseXChanged: {
+ itemDelegate.adjustBrightness(Math.max(1, Math.min(100, mouseX / width * 100)))
+ }
+ }
}
}
}
- }
- onClicked: {
- enterPage(index)
}
}
}
diff --git a/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml b/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml
index 72d1f6f5..ac08337d 100644
--- a/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/MediaDeviceListPage.qml
@@ -57,16 +57,20 @@ DeviceListPageBase {
rowSpacing: 0
columnSpacing: 0
Repeater {
- model: root.devicesProxy
+ model: root.thingsProxy
- delegate: Item {
+ delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
- height: contentItem.implicitHeight + app.margins
+ thing: thingsProxy.getThing(model.id)
- property bool inline: width > 500
+ topPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ rightPadding: 0
+ enabled: connectedState == null || connectedState.value === true
- property Thing thing: thingsProxy.getThing(model.id)
+ property State connectedState: thing.stateByName("connected")
readonly property StateType playbackStateType: thing.thingClass.stateTypes.findByName("playbackStatus")
readonly property State playbackState: thing.stateByName("playbackStatus")
@@ -74,162 +78,112 @@ DeviceListPageBase {
readonly property StateType playerTypeStateType: thing.thingClass.stateTypes.findByName("playerType")
readonly property State playerTypeState: thing.stateByName("playerType")
- Pane {
- id: contentItem
- width: parent.width - app.margins
- anchors.centerIn: parent
- Material.elevation: 2
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
+ onClicked: {
+ enterPage(index)
+ }
- contentItem: ItemDelegate {
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
- contentItem: ColumnLayout {
- spacing: 0
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: app.mediumFont + app.margins
- color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
- RowLayout {
- anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
- Label {
- Layout.fillWidth: true
- text: model.name
- elide: Text.ElideRight
- }
- BatteryStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
- }
- ConnectionStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
- }
- SetupStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: setupFailed || setupInProgress
- }
- }
+ contentItem: RowLayout {
+ ColumnLayout {
+ id: leftColummn
+ Layout.margins: app.margins
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.playbackState.value === "Stopped" ?
+ qsTr("No playback")
+ : itemDelegate.thing.stateByName("title").value
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ }
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.thing.stateByName("artist").value
+ font.pixelSize: app.smallFont
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideRight
+ }
+ Label {
+ Layout.fillWidth: true
+ text: itemDelegate.thing.stateByName("collection").value
+ horizontalAlignment: Text.AlignHCenter
+ font.pixelSize: app.smallFont
+ elide: Text.ElideRight
+ }
+ MediaControls {
+ visible: itemDelegate.thing.thingClass.interfaces.indexOf("mediacontroller") >= 0
+ thing: itemDelegate.thing
+ }
+ }
+ Item {
+ Layout.preferredHeight: leftColummn.height + app.margins * 2
+ Layout.preferredWidth: height * .7
+ Item {
+ id: artworkContainer
+ anchors.fill: parent
+ Image {
+ id: artworkImage
+ width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
+ readonly property State artworkState: thing.stateByName("artwork")
+ source: artworkState ? artworkState.value : ""
+ }
+ Rectangle {
+ visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
+ anchors.fill: parent
+ color: "black"
}
- RowLayout {
- ColumnLayout {
- id: leftColummn
- Layout.margins: app.margins
- Label {
- Layout.fillWidth: true
- text: itemDelegate.playbackState.value === "Stopped" ?
- qsTr("No playback")
- : itemDelegate.thing.stateByName("title").value
- horizontalAlignment: Text.AlignHCenter
- // font.pixelSize: app.largeFont
- elide: Text.ElideRight
- }
- Label {
- Layout.fillWidth: true
- text: itemDelegate.thing.stateByName("artist").value
- font.pixelSize: app.smallFont
- horizontalAlignment: Text.AlignHCenter
- elide: Text.ElideRight
- }
- Label {
- Layout.fillWidth: true
- text: itemDelegate.thing.stateByName("collection").value
- horizontalAlignment: Text.AlignHCenter
- font.pixelSize: app.smallFont
- elide: Text.ElideRight
- }
- MediaControls {
- visible: itemDelegate.thing.thingClass.interfaces.indexOf("mediacontroller") >= 0
- thing: itemDelegate.thing
- }
- }
- Item {
- Layout.preferredHeight: leftColummn.height + app.margins * 2
- Layout.preferredWidth: height * .7
- Item {
- id: artworkContainer
- anchors.fill: parent
- Image {
- id: artworkImage
- width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
- anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
- readonly property State artworkState: thing.stateByName("artwork")
- source: artworkState ? artworkState.value : ""
- }
- Rectangle {
- visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
- anchors.fill: parent
- color: "black"
- }
-
- ColorIcon {
- width: Math.min(parent.width, parent.height) - app.margins * 2
- height: width
- anchors.centerIn: parent
- name: itemDelegate.playerTypeState && itemDelegate.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
- visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
- color: "white"
- }
- }
-
- Rectangle {
- id: maskRect
- anchors.centerIn: parent
- height: parent.width
- width: parent.height
- gradient: Gradient {
- GradientStop { position: 0; color: "#00FF0000" }
- GradientStop { position: 0.2; color: "#15FF0000" }
- GradientStop { position: 1; color: "#FFFF0000" }
- }
- }
-
- ShaderEffect {
- anchors.fill: parent
- property variant source: ShaderEffectSource {
- sourceItem: artworkContainer
- hideSource: true
- }
- property variant mask: ShaderEffectSource {
- sourceItem: maskRect
- hideSource: true
- }
-
- fragmentShader: "
- varying highp vec2 qt_TexCoord0;
- uniform sampler2D source;
- uniform sampler2D mask;
- void main(void)
- {
- highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
- highp float alpha = texture2D(mask, vec2(qt_TexCoord0.y, qt_TexCoord0.x)).a;
- sourceColor *= alpha;
- gl_FragColor = sourceColor;
- }
- "
- }
- }
+ ColorIcon {
+ width: Math.min(parent.width, parent.height) - app.margins * 2
+ height: width
+ anchors.centerIn: parent
+ name: itemDelegate.playerTypeState && itemDelegate.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
+ visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
+ color: "white"
}
}
- onClicked: {
- enterPage(index)
+
+ Rectangle {
+ id: maskRect
+ anchors.centerIn: parent
+ height: parent.width
+ width: parent.height
+ radius: 6
+ gradient: Gradient {
+ GradientStop { position: 0; color: "#00FF0000" }
+ GradientStop { position: 0.2; color: "#15FF0000" }
+ GradientStop { position: 1; color: "#FFFF0000" }
+ }
+ }
+
+ ShaderEffect {
+ anchors.fill: parent
+ property variant source: ShaderEffectSource {
+ sourceItem: artworkContainer
+ hideSource: true
+ }
+ property variant mask: ShaderEffectSource {
+ sourceItem: maskRect
+ hideSource: true
+ }
+
+ fragmentShader: "
+ varying highp vec2 qt_TexCoord0;
+ uniform sampler2D source;
+ uniform sampler2D mask;
+ void main(void)
+ {
+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
+ highp float alpha = texture2D(mask, vec2(qt_TexCoord0.y, qt_TexCoord0.x)).a;
+ sourceColor *= alpha;
+ gl_FragColor = sourceColor;
+ }
+ "
}
}
}
}
+
}
}
}
diff --git a/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml
index 5503f786..d96671f3 100644
--- a/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/PowerSocketsDeviceListPage.qml
@@ -47,81 +47,68 @@ DeviceListPageBase {
}
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: devicesProxy
- spacing: app.margins
+ contentHeight: contentGrid.implicitHeight
+ topMargin: app.margins / 2
- delegate: Pane {
- id: itemDelegate
- width: parent.width
+ GridLayout {
+ id: contentGrid
+ width: parent.width - app.margins
+ anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: root.thingsProxy
- property Device device: devicesProxy.getDevice(model.id)
- property DeviceClass deviceClass: device.deviceClass
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
+ showHeader: false
+ enabled: connectedState == null || connectedState.value === true
+ topPadding: 0
+ bottomPadding: 0
- property var connectedStateType: deviceClass.stateTypes.findByName("connected");
- property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
+ onClicked: root.enterPage(index)
- property var powerStateType: deviceClass.stateTypes.findByName("power");
- property var powerActionType: deviceClass.actionTypes.findByName("power");
- property var powerState: device.states.getState(powerStateType.id)
+ property State connectedState: thing.stateByName("connected")
+ property State powerState: thing.stateByName("power")
- 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
+ contentItem: RowLayout {
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")
- }
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: app.iconSize
+ name: app.interfaceToIcon("powersocket")
+ color: itemDelegate.powerState.value === true ? app.accentColor : keyColor
}
Label {
Layout.fillWidth: true
- text: model.name
+ text: itemDelegate.thing.name
elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
}
+
+ ThingStatusIcons {
+ thing: itemDelegate.thing
+ }
+
Switch {
checked: itemDelegate.powerState.value === true
onClicked: {
var params = [];
var param1 = {};
- param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id;
+ param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
param1["value"] = checked;
params.push(param1)
- engine.deviceManager.executeAction(device.id, itemDelegate.powerActionType.id, params)
+ engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
}
}
}
}
- onClicked: {
- enterPage(index)
- }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
index a9654bcb..ebe38b54 100644
--- a/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/SensorsDeviceListPage.qml
@@ -43,154 +43,119 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: root.thingsProxy
+ contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
- delegate: Item {
- id: itemDelegate
+ GridLayout {
+ id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
- height: contentItem.implicitHeight + app.margins
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
- property bool inline: width > 500
+ Repeater {
+ model: root.thingsProxy
- property Thing thing: thingsProxy.getThing(model.id)
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
- Pane {
- id: contentItem
- width: parent.width - app.margins
- anchors.centerIn: parent
- Material.elevation: 2
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
-
- contentItem: ItemDelegate {
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
- contentItem: ColumnLayout {
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: app.mediumFont + app.margins
- color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
- RowLayout {
- anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
- Label {
- Layout.fillWidth: true
- text: model.name
- elide: Text.ElideRight
- }
- BatteryStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
- }
- ConnectionStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (isWireless || !isConnected)
- }
- SetupStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: setupFailed || setupInProgress
- }
- }
- }
- GridLayout {
- id: dataGrid
- columns: Math.floor(contentItem.width / 120)
- Layout.margins: app.margins
- Repeater {
- model: ListModel {
- ListElement { interfaceName: "temperaturesensor"; stateName: "temperature" }
- ListElement { interfaceName: "humiditysensor"; stateName: "humidity" }
- ListElement { interfaceName: "moisturesensor"; stateName: "moisture" }
- ListElement { interfaceName: "pressuresensor"; stateName: "pressure" }
- ListElement { interfaceName: "lightsensor"; stateName: "lightIntensity" }
- ListElement { interfaceName: "conductivitysensor"; stateName: "conductivity" }
- ListElement { interfaceName: "noisesensor"; stateName: "noise" }
- ListElement { interfaceName: "co2sensor"; stateName: "co2" }
- ListElement { interfaceName: "daylightsensor"; stateName: "daylight" }
- ListElement { interfaceName: "presencesensor"; stateName: "isPresent" }
- ListElement { interfaceName: "closablesensor"; stateName: "closed" }
- ListElement { interfaceName: "heating"; stateName: "power" }
- ListElement { interfaceName: "thermostat"; stateName: "targetTemperature" }
- }
-
- delegate: RowLayout {
- id: sensorValueDelegate
- visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
- Layout.preferredWidth: contentItem.width / dataGrid.columns
-
- property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
- property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
-
- ColorIcon {
- Layout.preferredHeight: app.iconSize * .8
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
- color: {
- switch (model.interfaceName) {
- case "closablesensor":
- return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? "green" : "red";
- default:
- return app.interfaceToColor(model.interfaceName)
- }
- }
- name: {
- switch (model.interfaceName) {
- case "closablesensor":
- return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? Qt.resolvedUrl("../images/lock-closed.svg") : Qt.resolvedUrl("../images/lock-open.svg");
- default:
- return app.interfaceToIcon(model.interfaceName)
- }
- }
- }
-
- Label {
- Layout.fillWidth: true
- property var unit: sensorValueDelegate.stateType ? sensorValueDelegate.stateType.unit : Types.UnitNone
- text: {
- switch (model.interfaceName) {
- case "closablesensor":
- return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("is closed") : qsTr("is open");
- default:
- return sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool"
- ? sensorValueDelegate.stateType.displayName
- : sensorValueDelegate.stateValue
- ? "%1 %2".arg(Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, unit) * 100) / 100).arg(Types.toUiUnit(unit))
- : ""
- }
- }
- elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: app.smallFont
- }
- Led {
- id: led
- visible: ["presencesensor", "daylightsensor", "heating"].indexOf(model.interfaceName) >= 0
- state: visible && sensorValueDelegate.stateValue.value === true ? "on" : "off"
- }
- Item {
- Layout.preferredWidth: led.width
- visible: led.visible
- }
- }
- }
- }
- }
onClicked: {
enterPage(index)
}
+
+ contentItem: GridLayout {
+ id: dataGrid
+ columns: Math.floor(contentItem.width / 120)
+
+ Repeater {
+ model: ListModel {
+ ListElement { interfaceName: "temperaturesensor"; stateName: "temperature" }
+ ListElement { interfaceName: "humiditysensor"; stateName: "humidity" }
+ ListElement { interfaceName: "moisturesensor"; stateName: "moisture" }
+ ListElement { interfaceName: "pressuresensor"; stateName: "pressure" }
+ ListElement { interfaceName: "lightsensor"; stateName: "lightIntensity" }
+ ListElement { interfaceName: "conductivitysensor"; stateName: "conductivity" }
+ ListElement { interfaceName: "noisesensor"; stateName: "noise" }
+ ListElement { interfaceName: "co2sensor"; stateName: "co2" }
+ ListElement { interfaceName: "daylightsensor"; stateName: "daylight" }
+ ListElement { interfaceName: "presencesensor"; stateName: "isPresent" }
+ ListElement { interfaceName: "closablesensor"; stateName: "closed" }
+ ListElement { interfaceName: "heating"; stateName: "power" }
+ ListElement { interfaceName: "thermostat"; stateName: "targetTemperature" }
+ }
+
+ delegate: RowLayout {
+ id: sensorValueDelegate
+ visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
+ Layout.preferredWidth: contentItem.width / dataGrid.columns
+
+ property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
+ property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
+
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: height
+ Layout.alignment: Qt.AlignVCenter
+ color: {
+ switch (model.interfaceName) {
+ case "closablesensor":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? "green" : "red";
+ default:
+ return app.interfaceToColor(model.interfaceName)
+ }
+ }
+ name: {
+ switch (model.interfaceName) {
+ case "closablesensor":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? Qt.resolvedUrl("../images/lock-closed.svg") : Qt.resolvedUrl("../images/lock-open.svg");
+ default:
+ return app.interfaceToIcon(model.interfaceName)
+ }
+ }
+ }
+
+ Label {
+ Layout.fillWidth: true
+ property var unit: sensorValueDelegate.stateType ? sensorValueDelegate.stateType.unit : Types.UnitNone
+ text: {
+ switch (model.interfaceName) {
+ case "closablesensor":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Closed") : qsTr("Open");
+ case "preencesensor":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Presence") : qsTr("Vacant");
+ case "daylightsensor":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Daytime") : qsTr("Nighttime");
+ case "heating":
+ return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("On") : qsTr("Off");
+ default:
+ return sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool"
+ ? sensorValueDelegate.stateType.displayName
+ : sensorValueDelegate.stateValue
+ ? "%1 %2".arg(Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, unit) * 100) / 100).arg(Types.toUiUnit(unit))
+ : ""
+ }
+ }
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: app.smallFont
+ }
+ Led {
+ id: led
+ visible: sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool" && ["presencesensor", "daylightsensor", "heating", "closablesensor"].indexOf(model.interfaceName) < 0
+ state: visible && sensorValueDelegate.stateValue.value === true ? "on" : "off"
+ }
+ Item {
+ Layout.preferredWidth: led.width
+ visible: led.visible
+ }
+ }
+ }
+ }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml b/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml
index 4ba056e8..31ff38bb 100644
--- a/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml
@@ -43,124 +43,82 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: root.devicesProxy
+ contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
- delegate: Item {
- id: itemDelegate
+ GridLayout {
+ id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
- height: contentItem.height + app.margins
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
- property bool inline: width > 500
+ Repeater {
+ model: root.thingsProxy
- property Thing thing: thingsProxy.getThing(model.id)
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.getThing(model.id)
- Pane {
- id: contentItem
- width: parent.width - app.margins
- anchors.centerIn: parent
- Material.elevation: 2
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
-
- contentItem: ItemDelegate {
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
- contentItem: ColumnLayout {
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: app.mediumFont + app.margins
- color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
- RowLayout {
- anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
- Label {
- Layout.fillWidth: true
- text: model.name
- elide: Text.ElideRight
- }
- BatteryStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (isCritical || hasBatteryLevel)
- }
- ConnectionStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (isWireless || !isConnected)
- }
- SetupStatusIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- thing: itemDelegate.thing
- visible: setupFailed || setupInProgress
- }
- }
-
- }
- GridLayout {
- id: dataGrid
- columns: Math.floor(contentItem.width / 120)
- Layout.margins: app.margins
- Repeater {
- model: ListModel {
- Component.onCompleted: {
- if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterproducer") >= 0) {
- append( {interfaceName: "smartmeterproducer", stateName: "totalEnergyProduced" })
- }
- if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
- append( {interfaceName: "smartmeterconsumer", stateName: "totalEnergyConsumed" })
- }
- var added = false;
- if (itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0) {
- append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
- added = true;
- }
- if (!added && itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) {
- append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
- }
- }
- }
-
- delegate: RowLayout {
- id: sensorValueDelegate
- Layout.preferredWidth: contentItem.width / dataGrid.columns
-
- property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
- property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
-
- ColorIcon {
- Layout.preferredHeight: app.iconSize * .8
- Layout.preferredWidth: height
- Layout.alignment: Qt.AlignVCenter
- color: app.interfaceToColor(model.interfaceName)
- name: app.interfaceToIcon(model.interfaceName)
- }
-
- Label {
- Layout.fillWidth: true
- text: sensorValueDelegate.stateValue
- ? "%1 %2".arg(1.0 * Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, sensorValueDelegate.stateType.unit) * 100000) / 100000).arg(Types.toUiUnit(sensorValueDelegate.stateType.unit))
- : ""
- elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: app.smallFont
- }
- }
- }
- }
- }
onClicked: {
enterPage(index)
}
+ contentItem: GridLayout {
+ id: dataGrid
+ columns: Math.floor(contentItem.width / 120)
+
+ Repeater {
+ model: ListModel {
+ Component.onCompleted: {
+ if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterproducer") >= 0) {
+ append( {interfaceName: "smartmeterproducer", stateName: "totalEnergyProduced" })
+ }
+ if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
+ append( {interfaceName: "smartmeterconsumer", stateName: "totalEnergyConsumed" })
+ }
+ var added = false;
+ if (itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0) {
+ append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
+ added = true;
+ }
+ if (!added && itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) {
+ append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
+ }
+ }
+ }
+
+ delegate: RowLayout {
+ id: sensorValueDelegate
+ visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
+ Layout.preferredWidth: contentItem.width / dataGrid.columns
+
+ property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
+ property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
+
+ ColorIcon {
+ Layout.preferredHeight: app.iconSize
+ Layout.preferredWidth: height
+ Layout.alignment: Qt.AlignVCenter
+ color: app.interfaceToColor(model.interfaceName)
+ name: app.interfaceToIcon(model.interfaceName)
+ }
+
+ Label {
+ Layout.fillWidth: true
+ text: sensorValueDelegate.stateValue
+ ? "%1 %2".arg(1.0 * Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, sensorValueDelegate.stateType.unit) * 100000) / 100000).arg(Types.toUiUnit(sensorValueDelegate.stateType.unit))
+ : ""
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: app.smallFont
+ }
+ }
+ }
+ }
}
}
}
diff --git a/nymea-app/ui/devicelistpages/WeatherDeviceListPage.qml b/nymea-app/ui/devicelistpages/WeatherDeviceListPage.qml
index 910a4d5f..5c2ee465 100644
--- a/nymea-app/ui/devicelistpages/WeatherDeviceListPage.qml
+++ b/nymea-app/ui/devicelistpages/WeatherDeviceListPage.qml
@@ -44,69 +44,31 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
- ListView {
+ Flickable {
anchors.fill: parent
- model: root.devicesProxy
+ contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
- delegate: Item {
- id: itemDelegate
+ GridLayout {
+ id: contentGrid
width: parent.width - app.margins
- height: contentItem.height + app.margins
anchors.horizontalCenter: parent.horizontalCenter
+ columns: Math.ceil(width / 600)
+ rowSpacing: 0
+ columnSpacing: 0
- property bool inline: width > 500
+ Repeater {
+ model: root.thingsProxy
- property Device device: devicesProxy.getDevice(model.id)
- property DeviceClass deviceClass: device.deviceClass
+ delegate: BigTile {
+ id: itemDelegate
+ Layout.preferredWidth: contentGrid.width / contentGrid.columns
+ thing: root.thingsProxy.get(model.id)
- Pane {
- id: contentItem
- width: parent.width - app.margins
- anchors.centerIn: parent
- Material.elevation: 2
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
+ onClicked: enterPage(index)
- contentItem: ItemDelegate {
- leftPadding: 0
- rightPadding: 0
- topPadding: 0
- bottomPadding: 0
- contentItem: ColumnLayout {
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: app.mediumFont + app.margins
- color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
- RowLayout {
- anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
- Label {
- Layout.fillWidth: true
- text: model.name
- elide: Text.ElideRight
- }
- ColorIcon {
- Layout.preferredHeight: app.iconSize * .5
- Layout.preferredWidth: height
- name: "../images/dialog-warning-symbolic.svg"
- visible: itemDelegate.deviceClass.interfaces.indexOf("connectable") >= 0 && itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("connected").id).value === false
- color: "red"
- }
- }
- }
-
- WeatherView {
- Layout.fillWidth: true
- device: itemDelegate.device
- deviceClass: itemDelegate.deviceClass
- }
- }
- onClicked: {
- var device = devicesProxy.get(index);
- var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
- pageStack.push(Qt.resolvedUrl("../devicepages/WeatherDevicePage.qml"), {device: devicesProxy.get(index)})
+ contentItem: WeatherView {
+ thing: itemDelegate.thing
}
}
}
diff --git a/nymea-app/ui/devicepages/DevicePageBase.qml b/nymea-app/ui/devicepages/DevicePageBase.qml
index 10dfcb3a..0c732ac2 100644
--- a/nymea-app/ui/devicepages/DevicePageBase.qml
+++ b/nymea-app/ui/devicepages/DevicePageBase.qml
@@ -242,86 +242,10 @@ Page {
}
}
- Rectangle {
+ ThingInfoPane {
id: infoPane
- visible: setupInProgress || setupFailure || batteryState !== null || (connectedState !== null && connectedState.value === false) || isWireless || updateAvailable
- height: visible ? contentRow.implicitHeight : 0
anchors { left: parent.left; top: parent.top; right: parent.right }
- property bool setupInProgress: root.thing.setupStatus == Thing.ThingSetupStatusInProgress
- property bool setupFailure: root.thing.setupStatus == Thing.ThingSetupStatusFailed
- property State batteryState: root.thing.stateByName("batteryLevel")
- property State batteryCriticalState: root.thing.stateByName("batteryCritical")
- property State connectedState: root.thing.stateByName("connected")
- property State signalStrengthState: root.thing.stateByName("signalStrength")
- property State updateStatusState: root.thing.stateByName("updateStatus")
- property bool updateAvailable: updateStatusState && updateStatusState.value === "available"
- property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
- property bool isWireless: root.thingClass.interfaces.indexOf("wirelessconnectable") >= 0
- property bool alertState: setupFailure ||
- (connectedState !== null && connectedState.value === false) ||
- (batteryCriticalState !== null && batteryCriticalState.value === true)
- property bool highlightState: updateAvailable || updateRunning
- color: alertState ? "red"
- : infoPane.highlightState ? app.accentColor : "transparent"
- z: 1000
-
- RowLayout {
- id: contentRow
- anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: app.margins; rightMargin: app.margins }
- Item {
- Layout.fillWidth: true
- height: app.iconSize
- }
-
- Label {
- text: infoPane.setupInProgress ?
- qsTr("Thing is being set up...")
- : infoPane.setupFailure ?
- (root.device.setupDisplayMessage.length > 0 ? root.device.setupDisplayMessage : qsTr("Thing setup failed!"))
- : (infoPane.connectedState !== null && infoPane.connectedState.value === false) ?
- qsTr("Thing is not connected!")
- : infoPane.updateAvailable ?
- qsTr("Update available!")
- : infoPane.updateRunning ?
- qsTr("Updating...")
- : qsTr("Thing runs out of battery!")
- visible: infoPane.alertState || infoPane.updateAvailable || infoPane.updateRunning
- font.pixelSize: app.smallFont
- color: "white"
- }
-
- UpdateStatusIcon {
- height: app.iconSize / 2
- width: height
- thing: root.thing
- color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
- visible: updateAvailable || updateRunning
- }
-
- BatteryStatusIcon {
- height: app.iconSize / 2
- width: height * 1.23
- thing: root.thing
- color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
- visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
- }
-
- ConnectionStatusIcon {
- height: app.iconSize / 2
- width: height
- thing: root.thing
- color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
- visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
- }
-
- SetupStatusIcon {
- height: app.iconSize / 2
- width: height
- thing: root.thing
- color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
- visible: setupFailed || setupInProgress
- }
- }
+ thing: root.thing
}
Item {
diff --git a/nymea-app/ui/devicepages/SensorDevicePage.qml b/nymea-app/ui/devicepages/SensorDevicePage.qml
index df75c0c4..6f36c459 100644
--- a/nymea-app/ui/devicepages/SensorDevicePage.qml
+++ b/nymea-app/ui/devicepages/SensorDevicePage.qml
@@ -47,7 +47,7 @@ DevicePageBase {
GridLayout {
id: contentGrid
width: parent.width
- columns: width / 300
+ columns: Math.ceil(width / 600)
Repeater {
model: ListModel {
diff --git a/nymea-app/ui/devicepages/WeatherDevicePage.qml b/nymea-app/ui/devicepages/WeatherDevicePage.qml
index 5120933f..a4232c92 100644
--- a/nymea-app/ui/devicepages/WeatherDevicePage.qml
+++ b/nymea-app/ui/devicepages/WeatherDevicePage.qml
@@ -49,8 +49,7 @@ DevicePageBase {
WeatherView {
Layout.fillWidth: true
- device: root.device
- deviceClass: root.deviceClass
+ thing: root.thing
}
GridLayout {
diff --git a/nymea-app/ui/mainviews/GroupsView.qml b/nymea-app/ui/mainviews/GroupsView.qml
index 52ee0172..89c9467e 100644
--- a/nymea-app/ui/mainviews/GroupsView.qml
+++ b/nymea-app/ui/mainviews/GroupsView.qml
@@ -65,203 +65,6 @@ MainViewBase {
onClicked: {
pageStack.push(Qt.resolvedUrl("../grouping/GroupInterfacesPage.qml"), {groupTag: model.tagId})
}
-
-// delegate: Item {
-// id: groupDelegate
-// width: groupsGridView.cellWidth
-// height: groupsGridView.cellHeight
-
-// Pane {
-// anchors.fill: parent
-// anchors.margins: app.margins / 2
-// Material.elevation: 2
-// padding: 0
-
-// DevicesProxy {
-// id: devicesInGroup
-// engine: _engine
-// filterTagId: model.tagId
-// filterTagValue: model.value
-// }
-
-// contentItem: ColumnLayout {
-
-// ColorIcon {
-// name: "../images/view-grid-symbolic.svg"
-// }
-
-// Label {
-// text: model
-// }
-// }
-
-
-// InterfacesProxy {
-// id: controlsInGroup
-// shownInterfaces: ["light", "simpleclosable", "mediacontroller", "powersocket"]
-// devicesProxyFilter: devicesInGroup
-// showStates: true
-// showActions: true
-// }
-// InterfacesProxy {
-// id: sensorsInGroup
-// shownInterfaces: ["temperaturesensor", "lightsensor", "presencesensor"]
-// devicesProxyFilter: devicesInGroup
-// showStates: true
-// }
-
-
-// contentItem: ItemDelegate {
-// leftPadding: 0
-// topPadding: 0
-// rightPadding: 0
-// bottomPadding: 0
-
-// onClicked: {
-// pageStack.push(Qt.resolvedUrl("../grouping/GroupInterfacesPage.qml"), {groupTag: model.tagId})
-// }
-
-// contentItem: ColumnLayout {
-// Rectangle {
-// Layout.fillWidth: true
-// Layout.preferredHeight: 30
-// color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
-// Label {
-// anchors.fill: parent
-// verticalAlignment: Text.AlignVCenter
-// anchors { leftMargin: app.margins; rightMargin: app.margins }
-// text: model.tagId.substring(6)
-// elide: Text.ElideRight
-// }
-// }
-// Item {
-// Layout.fillHeight: true
-// Layout.fillWidth: true
-
-// ColorIcon {
-// anchors.centerIn: parent
-// height: app.iconSize * 2
-// width: height
-// visible: controlsInGroup.count == 0
-// color: app.accentColor
-// name: "../images/view-grid-symbolic.svg"
-// }
-
-// ColumnLayout {
-// anchors.fill: parent
-
-// Repeater {
-//// model: Math.min(controlsInGroup.count, parent.height / 50)
-// model: controlsInGroup.count
-// delegate: Loader {
-// id: controlLoader
-// Layout.fillWidth: true
-// Layout.leftMargin: app.margins / 2
-// Layout.rightMargin: app.margins / 2
-// property string interfaceName: controlsInGroup.get(index).name
-// sourceComponent: {
-// switch (interfaceName) {
-// case "simpleclosable":
-// return closableDelegate
-// case "light":
-// return lightDelegate
-// case "mediacontroller":
-// return mediaControllerDelegate
-// case "powersocket":
-// return powerSocketDelegate
-// }
-// }
-// Binding {
-// target: controlLoader.item
-// property: "devices"
-// value: devicesInGroup
-// }
-// }
-// }
-// Item {
-// Layout.fillHeight: true
-// Layout.fillWidth: true
-// }
-// }
-
-// }
-// Rectangle {
-// Layout.fillWidth: true
-// Layout.preferredHeight: app.iconSize * 1.2
-// color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05)
-
-// RowLayout {
-// anchors.fill: parent
-
-// Repeater {
-// model: sensorsInGroup
-// delegate: Row {
-// height: parent.height
-
-// ColorIcon {
-// height: app.iconSize * .8
-// width: height
-// name: app.interfaceToIcon(model.name)
-// color: app.interfaceToColor(model.name)
-// }
-// DevicesProxy {
-// id: innerProxy
-// engine: _engine
-// parentProxy: devicesInGroup
-// shownInterfaces: [model.name]
-// }
-
-// Led {
-// visible: ["presencesensor"].indexOf(model.name) >= 0
-// state: {
-// var stateName = null
-// switch (model.name) {
-// case "presencesensor":
-// stateName = "isPresent"
-// break;
-// }
-// if (!stateName) {
-// return "off";
-// }
-// var ret = false;
-// for (var i = 0; i < innerProxy.count; i++) {
-// ret |= innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
-// }
-// return ret ? "on" : "off";
-// }
-// }
-
-// Label {
-// height: parent.height
-// verticalAlignment: Text.AlignVCenter
-// text: {
-// var stateName = null;
-// switch (model.name) {
-// case "temperaturesensor":
-// stateName = "temperature";
-// break;
-// case "lightsensor":
-// stateName = "lightIntensity"
-// break;
-// }
-// if (!stateName) {
-// return "";
-// }
-
-// var ret = 0
-// for (var i = 0; i < innerProxy.count; i++) {
-// ret += innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
-// }
-// return (ret / innerProxy.count).toFixed(1)
-// }
-// }
-// }
-// }
-// }
-// }
-// }
-// }
-// }
}
}
diff --git a/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml b/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml
index 323bbc84..0966dc98 100644
--- a/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml
+++ b/nymea-app/ui/thingconfiguration/ConfigureThingPage.qml
@@ -37,7 +37,8 @@ import "../delegates"
SettingsPageBase {
id: root
- property Device device: null
+ property Thing thing: null
+ property alias device: root.thing
readonly property DeviceClass deviceClass: device ? device.deviceClass : null
header: NymeaHeader {
@@ -50,6 +51,12 @@ SettingsPageBase {
}
}
+ ThingInfoPane {
+ id: infoPane
+ anchors { left: parent.left; top: parent.top; right: parent.right }
+ thing: root.thing
+ }
+
Menu {
id: deviceMenu
width: implicitWidth + app.margins
diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml
index d3a774f6..0d17436e 100644
--- a/nymea-app/ui/utils/NymeaUtils.qml
+++ b/nymea-app/ui/utils/NymeaUtils.qml
@@ -65,6 +65,18 @@ Item {
}
function isDark(color) {
- return ((color.r() * 299 + color.g() * 587 + color.b() * 114) / 1000) > 123
+ var r, g, b;
+ if (color.constructor.name === "Object") {
+ r = color.r * 255;
+ g = color.g * 255;
+ b = color.b * 255;
+ } else if (color.constructor.name === "String") {
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
+ r = parseInt(result[1], 16)
+ g = parseInt(result[2], 16)
+ b = parseInt(result[3], 16)
+ }
+
+ return ((r * 299 + g * 587 + b * 114) / 1000) < 128
}
}