UI refresh
This commit is contained in:
parent
5b5e85c7ad
commit
e6d4af8556
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -47,9 +47,7 @@
|
||||
<file>ui/components/Led.qml</file>
|
||||
<file>ui/components/ProgressButton.qml</file>
|
||||
<file>ui/customviews/GenericTypeLogView.qml</file>
|
||||
<file>ui/customviews/CustomViewBase.qml</file>
|
||||
<file>ui/customviews/WeatherView.qml</file>
|
||||
<file>ui/customviews/NotificationsView.qml</file>
|
||||
<file>ui/devicepages/MediaThingPage.qml</file>
|
||||
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
||||
<file>ui/devicepages/GenericDevicePage.qml</file>
|
||||
@ -223,5 +221,9 @@
|
||||
<file>ui/magic/WriteNfcTagPage.qml</file>
|
||||
<file>ui/components/MediaPlayer.qml</file>
|
||||
<file>ui/components/InfoPane.qml</file>
|
||||
<file>ui/components/BigTile.qml</file>
|
||||
<file>ui/components/ThingStatusIcons.qml</file>
|
||||
<file>ui/components/InfoPaneBase.qml</file>
|
||||
<file>ui/components/ThingInfoPane.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -20,6 +20,5 @@
|
||||
<file>styles/energize/Button.qml</file>
|
||||
<file>styles/energize/logo.svg</file>
|
||||
<file>styles/energize/Page.qml</file>
|
||||
<file>styles/energize/Pane.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
69
nymea-app/ui/components/BigTile.qml
Normal file
69
nymea-app/ui/components/BigTile.qml
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
nymea-app/ui/components/InfoPaneBase.qml
Normal file
59
nymea-app/ui/components/InfoPaneBase.qml
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
68
nymea-app/ui/components/ThingInfoPane.qml
Normal file
68
nymea-app/ui/components/ThingInfoPane.qml
Normal file
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
46
nymea-app/ui/components/ThingStatusIcons.qml
Normal file
46
nymea-app/ui/components/ThingStatusIcons.qml
Normal file
@ -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 }
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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
|
||||
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -47,7 +47,7 @@ DevicePageBase {
|
||||
GridLayout {
|
||||
id: contentGrid
|
||||
width: parent.width
|
||||
columns: width / 300
|
||||
columns: Math.ceil(width / 600)
|
||||
|
||||
Repeater {
|
||||
model: ListModel {
|
||||
|
||||
@ -49,8 +49,7 @@ DevicePageBase {
|
||||
|
||||
WeatherView {
|
||||
Layout.fillWidth: true
|
||||
device: root.device
|
||||
deviceClass: root.deviceClass
|
||||
thing: root.thing
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
|
||||
@ -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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user