Merge PR #464: New tiles
This commit is contained in:
commit
fa8aad122e
@ -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
|
bool DevicesProxy::groupByInterface() const
|
||||||
{
|
{
|
||||||
return m_groupByInterface;
|
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 leftName = sourceModel()->data(left, Devices::RoleName).toString();
|
||||||
QString rightName = sourceModel()->data(right, 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
|
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 (!m_nameFilter.isEmpty()) {
|
||||||
if (!device->name().toLower().contains(m_nameFilter.toLower().trimmed())) {
|
if (!device->name().toLower().contains(m_nameFilter.toLower().trimmed())) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -67,6 +67,8 @@ class DevicesProxy : public QSortFilterProxyModel
|
|||||||
|
|
||||||
Q_PROPERTY(bool filterSetupFailed READ filterSetupFailed WRITE setFilterSetupFailed NOTIFY filterSetupFailedChanged)
|
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)
|
Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -120,12 +122,15 @@ public:
|
|||||||
bool filterSetupFailed() const;
|
bool filterSetupFailed() const;
|
||||||
void setFilterSetupFailed(bool filterSetupFailed);
|
void setFilterSetupFailed(bool filterSetupFailed);
|
||||||
|
|
||||||
bool groupByInterface() const;
|
bool filterUpdates() const;
|
||||||
void setGroupByInterface(bool groupByInterface);
|
void setFilterUpdates(bool filterUpdates);
|
||||||
|
|
||||||
|
bool groupByInterface() const;
|
||||||
|
void setGroupByInterface(bool groupByInterface);
|
||||||
|
|
||||||
Q_INVOKABLE Device *get(int index) const;
|
Q_INVOKABLE Device *get(int index) const;
|
||||||
Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
|
Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
|
||||||
Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
|
Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void engineChanged();
|
void engineChanged();
|
||||||
@ -144,6 +149,7 @@ signals:
|
|||||||
void filterBatteryCriticalChanged();
|
void filterBatteryCriticalChanged();
|
||||||
void filterDisconnectedChanged();
|
void filterDisconnectedChanged();
|
||||||
void filterSetupFailedChanged();
|
void filterSetupFailedChanged();
|
||||||
|
void filterUpdatesChanged();
|
||||||
void groupByInterfaceChanged();
|
void groupByInterfaceChanged();
|
||||||
void countChanged();
|
void countChanged();
|
||||||
|
|
||||||
@ -168,6 +174,7 @@ private:
|
|||||||
bool m_filterBatteryCritical = false;
|
bool m_filterBatteryCritical = false;
|
||||||
bool m_filterDisconnected = false;
|
bool m_filterDisconnected = false;
|
||||||
bool m_filterSetupFailed = false;
|
bool m_filterSetupFailed = false;
|
||||||
|
bool m_filterUpdates = false;
|
||||||
|
|
||||||
bool m_groupByInterface = false;
|
bool m_groupByInterface = false;
|
||||||
|
|
||||||
|
|||||||
@ -124,6 +124,13 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QQmlApplicationEngine *engine = new QQmlApplicationEngine();
|
QQmlApplicationEngine *engine = new QQmlApplicationEngine();
|
||||||
|
|
||||||
|
StyleController styleController;
|
||||||
|
|
||||||
|
QQmlFileSelector *styleSelector = new QQmlFileSelector(engine);
|
||||||
|
styleSelector->setExtraSelectors({styleController.currentStyle()});
|
||||||
|
|
||||||
|
engine->rootContext()->setContextProperty("styleController", &styleController);
|
||||||
|
|
||||||
qmlRegisterSingletonType<PlatformHelper>("Nymea", 1, 0, "PlatformHelper", platformHelperProvider);
|
qmlRegisterSingletonType<PlatformHelper>("Nymea", 1, 0, "PlatformHelper", platformHelperProvider);
|
||||||
qmlRegisterSingletonType<NfcHelper>("Nymea", 1, 0, "NfcHelper", NfcHelper::nfcHelperProvider);
|
qmlRegisterSingletonType<NfcHelper>("Nymea", 1, 0, "NfcHelper", NfcHelper::nfcHelperProvider);
|
||||||
qmlRegisterType<NfcThingActionWriter>("Nymea", 1, 0, "NfcThingActionWriter");
|
qmlRegisterType<NfcThingActionWriter>("Nymea", 1, 0, "NfcThingActionWriter");
|
||||||
@ -141,9 +148,6 @@ int main(int argc, char *argv[])
|
|||||||
engine->rootContext()->setContextProperty("qtBuildVersion", QT_VERSION_STR);
|
engine->rootContext()->setContextProperty("qtBuildVersion", QT_VERSION_STR);
|
||||||
engine->rootContext()->setContextProperty("qtVersion", qVersion());
|
engine->rootContext()->setContextProperty("qtVersion", qVersion());
|
||||||
|
|
||||||
StyleController styleController;
|
|
||||||
engine->rootContext()->setContextProperty("styleController", &styleController);
|
|
||||||
|
|
||||||
engine->rootContext()->setContextProperty("kioskMode", parser.isSet(kioskOption));
|
engine->rootContext()->setContextProperty("kioskMode", parser.isSet(kioskOption));
|
||||||
engine->rootContext()->setContextProperty("autoConnectHost", parser.value(connectOption));
|
engine->rootContext()->setContextProperty("autoConnectHost", parser.value(connectOption));
|
||||||
|
|
||||||
|
|||||||
@ -47,9 +47,7 @@
|
|||||||
<file>ui/components/Led.qml</file>
|
<file>ui/components/Led.qml</file>
|
||||||
<file>ui/components/ProgressButton.qml</file>
|
<file>ui/components/ProgressButton.qml</file>
|
||||||
<file>ui/customviews/GenericTypeLogView.qml</file>
|
<file>ui/customviews/GenericTypeLogView.qml</file>
|
||||||
<file>ui/customviews/CustomViewBase.qml</file>
|
|
||||||
<file>ui/customviews/WeatherView.qml</file>
|
<file>ui/customviews/WeatherView.qml</file>
|
||||||
<file>ui/customviews/NotificationsView.qml</file>
|
|
||||||
<file>ui/devicepages/MediaThingPage.qml</file>
|
<file>ui/devicepages/MediaThingPage.qml</file>
|
||||||
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
<file>ui/devicepages/ButtonDevicePage.qml</file>
|
||||||
<file>ui/devicepages/GenericDevicePage.qml</file>
|
<file>ui/devicepages/GenericDevicePage.qml</file>
|
||||||
@ -223,5 +221,9 @@
|
|||||||
<file>ui/magic/WriteNfcTagPage.qml</file>
|
<file>ui/magic/WriteNfcTagPage.qml</file>
|
||||||
<file>ui/components/MediaPlayer.qml</file>
|
<file>ui/components/MediaPlayer.qml</file>
|
||||||
<file>ui/components/InfoPane.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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -20,6 +20,5 @@
|
|||||||
<file>styles/energize/Button.qml</file>
|
<file>styles/energize/Button.qml</file>
|
||||||
<file>styles/energize/logo.svg</file>
|
<file>styles/energize/logo.svg</file>
|
||||||
<file>styles/energize/Page.qml</file>
|
<file>styles/energize/Page.qml</file>
|
||||||
<file>styles/energize/Pane.qml</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@ -58,7 +58,7 @@ ApplicationWindow {
|
|||||||
property color headerForegroundColor: "#79a79f"
|
property color headerForegroundColor: "#79a79f"
|
||||||
|
|
||||||
// The font color
|
// The font color
|
||||||
property color foregroundColor: "#69938c"
|
property color foregroundColor: "#638B87"
|
||||||
|
|
||||||
// The color of selected/highlighted things
|
// The color of selected/highlighted things
|
||||||
property color accentColor: "#8cc1b6"
|
property color accentColor: "#8cc1b6"
|
||||||
|
|||||||
@ -64,7 +64,7 @@ T.Button {
|
|||||||
font.overline: control.font.overline
|
font.overline: control.font.overline
|
||||||
font.pixelSize: app.smallFont
|
font.pixelSize: app.smallFont
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: app.foregroundColor
|
color: app.backgroundColor
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
elide: Text.ElideRight
|
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)
|
text: engine.systemController.updateRunning ? qsTr("System update in progress...") : qsTr("%n system update(s) available", "", updatesModel.count)
|
||||||
imageSource: "../images/system-update.svg"
|
imageSource: "../images/system-update.svg"
|
||||||
rotatingIcon: engine.systemController.updateRunning
|
rotatingIcon: engine.systemController.updateRunning
|
||||||
onPaneClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
|
onClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
|
||||||
|
|
||||||
PackagesFilterModel {
|
PackagesFilterModel {
|
||||||
id: updatesModel
|
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.Controls.Material 2.2
|
||||||
import QtQuick.Layouts 1.2
|
import QtQuick.Layouts 1.2
|
||||||
|
|
||||||
Item {
|
InfoPaneBase {
|
||||||
id: root
|
id: root
|
||||||
implicitHeight: d.shownHeight
|
|
||||||
visible: d.shownHeight > 0
|
|
||||||
|
|
||||||
property alias text: textLabel.text
|
property alias text: textLabel.text
|
||||||
property alias imageSource: icon.name
|
property alias imageSource: icon.name
|
||||||
property alias buttonText: button.text
|
property alias buttonText: button.text
|
||||||
|
|
||||||
property alias color: background.color
|
property color textColor: "white"
|
||||||
property alias textColor: textLabel.color
|
|
||||||
|
|
||||||
property bool rotatingIcon: false
|
property bool rotatingIcon: false
|
||||||
|
|
||||||
property bool shown: false
|
|
||||||
|
|
||||||
function show() {
|
|
||||||
shown = true;
|
|
||||||
}
|
|
||||||
function hide() {
|
|
||||||
shown = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
signal paneClicked();
|
|
||||||
signal buttonClicked();
|
signal buttonClicked();
|
||||||
|
|
||||||
QtObject {
|
contentItem: RowLayout {
|
||||||
id: d
|
id: contentRow
|
||||||
property int shownHeight: shown ? contentRow.implicitHeight : 0
|
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
Behavior on shownHeight { NumberAnimation { easing.type: Easing.InOutQuad; duration: 150 } }
|
|
||||||
}
|
|
||||||
|
|
||||||
Pane {
|
Label {
|
||||||
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
|
id: textLabel
|
||||||
Material.elevation: 2
|
color: root.textColor
|
||||||
padding: 0
|
font.pixelSize: app.smallFont
|
||||||
height: contentRow.implicitHeight
|
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 {
|
RotationAnimation on rotation {
|
||||||
anchors.fill: parent
|
from: 0
|
||||||
onClicked: root.paneClicked()
|
to: 360
|
||||||
|
duration: 2000
|
||||||
|
loops: Animation.Infinite
|
||||||
|
running: root.rotatingIcon
|
||||||
|
onStopped: icon.rotation = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Button {
|
||||||
id: background
|
id: button
|
||||||
color: app.accentColor
|
Layout.leftMargin: app.margins
|
||||||
anchors.fill: parent
|
visible: text.length > 0
|
||||||
|
onClicked: root.buttonClicked()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ import QtQuick.Controls 2.2
|
|||||||
import QtQuick.Controls.Material 2.2
|
import QtQuick.Controls.Material 2.2
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@ -40,114 +41,151 @@ Item {
|
|||||||
property alias iconName: colorIcon.name
|
property alias iconName: colorIcon.name
|
||||||
property alias fallbackIconName: fallbackIcon.name
|
property alias fallbackIconName: fallbackIcon.name
|
||||||
property alias iconColor: colorIcon.color
|
property alias iconColor: colorIcon.color
|
||||||
property alias backgroundImage: background.source
|
property alias backgroundImage: backgroundImg.source
|
||||||
property string text
|
property string text
|
||||||
property bool disconnected: false
|
property bool disconnected: false
|
||||||
property bool isWireless: false
|
property bool isWireless: false
|
||||||
property int signalStrength: 0
|
property int signalStrength: 0
|
||||||
property int setupStatus: Thing.ThingSetupStatusNone
|
property int setupStatus: Thing.ThingSetupStatusNone
|
||||||
property bool batteryCritical: false
|
property bool batteryCritical: false
|
||||||
|
property bool updateStatus: false
|
||||||
|
|
||||||
property alias contentItem: innerContent.children
|
property alias contentItem: innerContent.children
|
||||||
|
|
||||||
signal clicked();
|
signal clicked();
|
||||||
signal pressAndHold();
|
signal pressAndHold();
|
||||||
|
|
||||||
Pane {
|
Rectangle {
|
||||||
|
id: background
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: app.margins / 2
|
anchors.margins: app.margins / 2
|
||||||
Material.elevation: 1
|
gradient: Gradient {
|
||||||
padding: 0
|
GradientStop {
|
||||||
|
position: 1 - innerContent.height / background.height
|
||||||
Image {
|
color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .1))
|
||||||
id: background
|
}
|
||||||
anchors.fill: parent
|
GradientStop {
|
||||||
anchors.margins: 1
|
position: 1 - innerContent.height / background.height
|
||||||
z: -1
|
color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05))
|
||||||
fillMode: Image.PreserveAspectCrop
|
}
|
||||||
// horizontalAlignment: Image.AlignTop
|
|
||||||
// opacity: .5
|
|
||||||
// Rectangle {
|
|
||||||
// anchors.fill: parent
|
|
||||||
// color: Material.background
|
|
||||||
// opacity: .5
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: ItemDelegate {
|
radius: 6
|
||||||
padding: 0; topPadding: 0; bottomPadding: 0
|
}
|
||||||
onClicked: root.clicked()
|
|
||||||
onPressAndHold: root.pressAndHold()
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
Image {
|
||||||
spacing: 0
|
id: backgroundImg
|
||||||
ColumnLayout {
|
anchors.fill: parent
|
||||||
Layout.fillHeight: true
|
anchors.margins: app.margins / 2
|
||||||
Layout.margins: app.margins
|
visible: false
|
||||||
Item {
|
z: -1
|
||||||
visible: background.status !== Image.Ready
|
fillMode: Image.PreserveAspectCrop
|
||||||
Layout.fillWidth: true
|
}
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
ColorIcon {
|
Rectangle {
|
||||||
id: colorIcon
|
id: backgroundImgClipper
|
||||||
anchors.centerIn: parent
|
radius: background.radius
|
||||||
height: app.hugeIconSize //* 1.5
|
anchors.fill: parent
|
||||||
width: height
|
visible: false
|
||||||
ColorIcon {
|
gradient: Gradient {
|
||||||
id: fallbackIcon
|
GradientStop {
|
||||||
anchors.fill: parent
|
position: 1 - innerContent.height / background.height
|
||||||
color: root.iconColor
|
color: "transparent"
|
||||||
visible: parent.status === Image.Error
|
}
|
||||||
}
|
GradientStop {
|
||||||
}
|
position: 1 - innerContent.height / background.height
|
||||||
}
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
OpacityMask {
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
Layout.fillHeight: true
|
anchors.margins: app.margins / 2
|
||||||
visible: background.status !== Image.Ready
|
source: backgroundImg
|
||||||
|
maskSource: backgroundImgClipper
|
||||||
|
// visible: root.backgroundImage.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
ItemDelegate {
|
||||||
id: label
|
anchors {
|
||||||
anchors.centerIn: parent
|
top: parent.top; left: parent.left; right: parent.right; bottom: innerContent.top
|
||||||
width: parent.width
|
topMargin: app.margins / 2; leftMargin: app.margins / 2; rightMargin: app.margins / 2
|
||||||
text: root.text.toUpperCase()
|
}
|
||||||
font.pixelSize: app.smallFont
|
padding: 0; topPadding: 0; bottomPadding: 0
|
||||||
font.letterSpacing: 1
|
onClicked: root.clicked()
|
||||||
wrapMode: Text.WordWrap
|
onPressAndHold: root.pressAndHold()
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
maximumLineCount: 2
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
contentItem: ColumnLayout {
|
||||||
MouseArea {
|
spacing: 0
|
||||||
id: innerContent
|
ColumnLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
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
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: app.iconSize + app.margins * 2
|
Layout.fillHeight: true
|
||||||
visible: root.contentItem.length > 1
|
|
||||||
|
|
||||||
Rectangle {
|
ColorIcon {
|
||||||
anchors.fill: parent
|
id: colorIcon
|
||||||
color: Material.background
|
anchors.centerIn: parent
|
||||||
|
height: app.hugeIconSize
|
||||||
|
width: height
|
||||||
|
ColorIcon {
|
||||||
|
id: fallbackIcon
|
||||||
|
anchors.fill: parent
|
||||||
|
color: root.iconColor
|
||||||
|
visible: parent.status === Image.Error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
anchors.fill: parent
|
Layout.fillWidth: true
|
||||||
color: Material.foreground
|
Layout.fillHeight: true
|
||||||
opacity: 0.05
|
visible: backgroundImg.status !== Image.Ready
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: label
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
text: root.text.toUpperCase()
|
||||||
|
font.pixelSize: app.smallFont
|
||||||
|
font.letterSpacing: 1
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Item {
|
||||||
|
id: innerContent
|
||||||
|
anchors { left: parent.left; bottom: parent.bottom; right: parent.right; margins: app.margins / 2 }
|
||||||
|
height: app.iconSize + app.margins * 2
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
id: quickAlertPane
|
id: quickAlertPane
|
||||||
anchors { top: parent.top; right: parent.right; margins: app.margins }
|
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 {
|
ColorIcon {
|
||||||
height: app.smallIconSize
|
height: app.smallIconSize
|
||||||
width: height
|
width: height
|
||||||
|
|||||||
@ -36,13 +36,15 @@ import Nymea 1.0
|
|||||||
import "../components"
|
import "../components"
|
||||||
import "../delegates"
|
import "../delegates"
|
||||||
|
|
||||||
MouseArea {
|
Item {
|
||||||
id: root
|
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: ""
|
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 {
|
ColorIcon {
|
||||||
id: secondaryIcon
|
id: secondaryIcon
|
||||||
Layout.preferredHeight: app.iconSize * .5
|
Layout.preferredHeight: app.smallIconSize
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
visible: name.length > 0
|
visible: name.length > 0
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@ -165,7 +165,7 @@ SwipeDelegate {
|
|||||||
|
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: tertiaryIcon
|
id: tertiaryIcon
|
||||||
Layout.preferredHeight: app.iconSize * .5
|
Layout.preferredHeight: app.smallIconSize
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
visible: name.length > 0
|
visible: name.length > 0
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@ -179,7 +179,7 @@ SwipeDelegate {
|
|||||||
|
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: progressionIcon
|
id: progressionIcon
|
||||||
Layout.preferredHeight: app.iconSize * .6
|
Layout.preferredHeight: app.smallIconSize
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
name: "../images/next.svg"
|
name: "../images/next.svg"
|
||||||
visible: root.progressive
|
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"
|
readonly property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
|
||||||
|
|
||||||
name: "../images/system-update.svg"
|
name: "../images/system-update.svg"
|
||||||
|
color: app.accentColor
|
||||||
|
|
||||||
RotationAnimation on rotation {
|
RotationAnimation on rotation {
|
||||||
from: 0; to: 360
|
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
|
id: connectedLineSeries
|
||||||
onPointAdded: {
|
onPointAdded: {
|
||||||
var newPoint = connectedLineSeries.at(index)
|
var newPoint = connectedLineSeries.at(index)
|
||||||
print("pointadded", newPoint.x, newPoint.y)
|
// print("pointadded", newPoint.x, newPoint.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ Item {
|
|||||||
id: lineSeries1
|
id: lineSeries1
|
||||||
onPointAdded: {
|
onPointAdded: {
|
||||||
var newPoint = lineSeries1.at(index)
|
var newPoint = lineSeries1.at(index)
|
||||||
print("pointadded", newPoint.x, newPoint.y)
|
// print("pointadded", newPoint.x, newPoint.y)
|
||||||
|
|
||||||
if (newPoint.x > lineSeries0.at(0).x) {
|
if (newPoint.x > lineSeries0.at(0).x) {
|
||||||
lineSeries0.replace(0, newPoint.x, 0)
|
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 "../components"
|
||||||
import Nymea 1.0
|
import Nymea 1.0
|
||||||
|
|
||||||
CustomViewBase {
|
Item {
|
||||||
id: root
|
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
|
property Thing thing: null
|
||||||
readonly property State weatherConditionState: weatherConditionStateType && device.states ? device.states.getState(weatherConditionStateType.id) : null
|
|
||||||
|
|
||||||
readonly property StateType weatherDescriptionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("weatherDescription") : null
|
readonly property State weatherConditionState: thing.stateByName("weatherCondition")
|
||||||
readonly property State weatherDescriptionState: weatherDescriptionStateType && device.states ? device.states.getState(weatherDescriptionStateType.id) : null
|
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 StateType temperatureStateType: thing.thingClass.stateTypes.findByName("temperature")
|
||||||
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
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: grid
|
id: grid
|
||||||
@ -86,7 +77,6 @@ CustomViewBase {
|
|||||||
Label {
|
Label {
|
||||||
text: (temperatureState ? Math.round(Types.toUiValue(temperatureState.value, temperatureStateType.unit) * 10) / 10 : "N/A") + " °"
|
text: (temperatureState ? Math.round(Types.toUiValue(temperatureState.value, temperatureStateType.unit) * 10) / 10 : "N/A") + " °"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
font.pixelSize: app.largeFont
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
|
|||||||
@ -44,6 +44,7 @@ MainPageTile {
|
|||||||
isWireless: devicesSubProxyConnectables.count > 0 && devicesSubProxyConnectables.get(0).thingClass.interfaces.indexOf("wirelessconnectable") >= 0
|
isWireless: devicesSubProxyConnectables.count > 0 && devicesSubProxyConnectables.get(0).thingClass.interfaces.indexOf("wirelessconnectable") >= 0
|
||||||
batteryCritical: devicesSubProxyBattery.count > 0
|
batteryCritical: devicesSubProxyBattery.count > 0
|
||||||
setupStatus: thingsSubProxySetupFailure.count > 0 ? Thing.ThingSetupStatusFailed : Thing.ThingSetupStatusComplete
|
setupStatus: thingsSubProxySetupFailure.count > 0 ? Thing.ThingSetupStatusFailed : Thing.ThingSetupStatusComplete
|
||||||
|
updateStatus: thingsSubProxyUpdates.count > 0
|
||||||
|
|
||||||
property Interface iface: null
|
property Interface iface: null
|
||||||
property alias filterTagId: devicesProxy.filterTagId
|
property alias filterTagId: devicesProxy.filterTagId
|
||||||
@ -139,6 +140,12 @@ MainPageTile {
|
|||||||
parentProxy: devicesProxy
|
parentProxy: devicesProxy
|
||||||
filterSetupFailed: true
|
filterSetupFailed: true
|
||||||
}
|
}
|
||||||
|
ThingsProxy {
|
||||||
|
id: thingsSubProxyUpdates
|
||||||
|
engine: _engine
|
||||||
|
parentProxy: devicesProxy
|
||||||
|
filterUpdates: true
|
||||||
|
}
|
||||||
|
|
||||||
property int currentDeviceIndex: 0
|
property int currentDeviceIndex: 0
|
||||||
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
|
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
|
||||||
@ -220,7 +227,7 @@ MainPageTile {
|
|||||||
var d = devicesProxy.get(i);
|
var d = devicesProxy.get(i);
|
||||||
var st = d.deviceClass.stateTypes.findByName("playbackStatus")
|
var st = d.deviceClass.stateTypes.findByName("playbackStatus")
|
||||||
var s = d.states.getState(st.id)
|
var s = d.states.getState(st.id)
|
||||||
s.valueChanged.connect(function() {updateTile()})
|
s.valueChanged.connect(function() {inlineMediaControl.updateTile()})
|
||||||
}
|
}
|
||||||
updateTile();
|
updateTile();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ MainPageTile {
|
|||||||
disconnected: connectedState && connectedState.value === false
|
disconnected: connectedState && connectedState.value === false
|
||||||
signalStrength: signalStrengthState ? signalStrengthState.value : -1
|
signalStrength: signalStrengthState ? signalStrengthState.value : -1
|
||||||
setupStatus: device.setupStatus
|
setupStatus: device.setupStatus
|
||||||
|
updateStatus: updateStatusState && updateStatusState.value !== "idle"
|
||||||
|
|
||||||
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
|
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ MainPageTile {
|
|||||||
readonly property State signalStrengthState: device.stateByName("signalStrength")
|
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 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 artworkState: deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("artwork").id) : null
|
||||||
|
readonly property State updateStatusState: device.stateByName("updateStatus")
|
||||||
|
|
||||||
contentItem: Loader {
|
contentItem: Loader {
|
||||||
id: loader
|
id: loader
|
||||||
|
|||||||
@ -83,88 +83,69 @@ DeviceListPageBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
spacing: app.margins
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Pane {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width
|
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)
|
onClicked: root.enterPage(index)
|
||||||
property DeviceClass deviceClass: device.deviceClass
|
|
||||||
|
|
||||||
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
|
property State connectedState: thing.stateByName("connected")
|
||||||
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
|
property State movingState: thing.stateByName("moving")
|
||||||
|
property State percentageState: thing.stateByName("percentage")
|
||||||
|
|
||||||
property StateType percentageStateType: deviceClass.stateTypes.findByName("percentage");
|
contentItem: RowLayout {
|
||||||
property ActionType percentageActionType: deviceClass.actionTypes.findByName("percentage");
|
|
||||||
property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
|
|
||||||
|
|
||||||
property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
|
|
||||||
property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
|
|
||||||
property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
|
|
||||||
|
|
||||||
Material.elevation: 1
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
contentItem: ItemDelegate {
|
|
||||||
id: contentItem
|
|
||||||
implicitHeight: nameRow.implicitHeight
|
|
||||||
|
|
||||||
topPadding: 0
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
RowLayout {
|
|
||||||
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
|
|
||||||
id: nameRow
|
|
||||||
z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
|
|
||||||
spacing: app.margins
|
spacing: app.margins
|
||||||
Item {
|
|
||||||
Layout.preferredHeight: app.iconSize
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: icon
|
Layout.preferredHeight: app.iconSize
|
||||||
anchors.fill: parent
|
Layout.preferredWidth: app.iconSize
|
||||||
color: itemDelegate.movingStateType && itemDelegate.movingState.value === true
|
color: itemDelegate.movingState && itemDelegate.movingState.value === true
|
||||||
? app.accentColor
|
? app.accentColor
|
||||||
: keyColor
|
: keyColor
|
||||||
name: itemDelegate.percentageStateType
|
name: itemDelegate.percentageState
|
||||||
? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
|
? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
|
||||||
: root.iconBasename + "-050.svg"
|
: root.iconBasename + "-050.svg"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: itemDelegate.device.name
|
text: itemDelegate.thing.name
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
ThingStatusIcons {
|
||||||
Layout.preferredWidth: shutterControls.implicitWidth
|
thing: itemDelegate.thing
|
||||||
Layout.preferredHeight: app.iconSize * 2
|
}
|
||||||
ShutterControls {
|
|
||||||
id: shutterControls
|
ShutterControls {
|
||||||
height: parent.height
|
id: shutterControls
|
||||||
device: itemDelegate.device
|
Layout.fillWidth: false
|
||||||
invert: root.invertControls
|
height: parent.height
|
||||||
}
|
device: itemDelegate.thing
|
||||||
|
invert: root.invertControls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
|
||||||
enterPage(index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,242 +44,86 @@ DeviceListPageBase {
|
|||||||
header: NymeaHeader {
|
header: NymeaHeader {
|
||||||
id: header
|
id: header
|
||||||
onBackPressed: pageStack.pop()
|
onBackPressed: pageStack.pop()
|
||||||
text: root.title
|
text: qsTr("Garage doors")
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
spacing: app.margins
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Pane {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width
|
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 State connectedState: thing.stateByName("connected")
|
||||||
property Device thing: device
|
property State movingState: thing.stateByName("moving")
|
||||||
property DeviceClass deviceClass: device.deviceClass
|
property State percentageState: thing.stateByName("percentage")
|
||||||
|
|
||||||
readonly property bool isImpulseBased: device.deviceClass.interfaces.indexOf("impulsegaragedoor") >= 0
|
readonly property bool isImpulseBased: thing.thingClass.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
|
|
||||||
|
|
||||||
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
|
contentItem: RowLayout {
|
||||||
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
|
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");
|
Label {
|
||||||
property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
|
Layout.fillWidth: true
|
||||||
property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
|
text: itemDelegate.thing.name
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
Material.elevation: 1
|
ThingStatusIcons {
|
||||||
topPadding: 0
|
thing: itemDelegate.thing
|
||||||
bottomPadding: 0
|
}
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
contentItem: ItemDelegate {
|
|
||||||
id: contentItem
|
|
||||||
implicitHeight: contentLoader.item.implicitHeight
|
|
||||||
|
|
||||||
topPadding: 0
|
ShutterControls {
|
||||||
|
id: shutterControls
|
||||||
contentItem: Loader {
|
Layout.fillWidth: false
|
||||||
id: contentLoader
|
height: parent.height
|
||||||
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
|
device: itemDelegate.thing
|
||||||
sourceComponent: isImpulseBased ? impulseGaragedoor
|
invert: root.invertControls
|
||||||
: isExtended ? extendedStatefulGaragedoor
|
visible: !itemDelegate.isImpulseBased
|
||||||
: isStateful ? garagedoor
|
}
|
||||||
: simpleGaragedoor
|
ProgressButton {
|
||||||
Binding { target: contentLoader.item; property: "device"; value: itemDelegate.device }
|
Layout.preferredHeight: app.iconSize
|
||||||
}
|
Layout.preferredWidth: height
|
||||||
onClicked: {
|
longpressEnabled: false
|
||||||
enterPage(index)
|
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)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,15 +53,69 @@ DeviceListPageBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: root.devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: ThingDelegate {
|
GridLayout {
|
||||||
width: parent.width
|
id: contentGrid
|
||||||
device: engine.deviceManager.devices.getDevice(model.id);
|
width: parent.width - app.margins
|
||||||
onClicked: {
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
enterPage(index)
|
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"
|
import "../components"
|
||||||
|
|
||||||
DeviceListPageBase {
|
DeviceListPageBase {
|
||||||
|
id: root
|
||||||
|
|
||||||
header: NymeaHeader {
|
header: NymeaHeader {
|
||||||
text: qsTr("Lights")
|
text: qsTr("Lights")
|
||||||
@ -70,125 +71,151 @@ DeviceListPageBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
spacing: app.margins
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Pane {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width
|
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 State connectedState: thing.stateByName("connected")
|
||||||
property DeviceClass deviceClass: device.deviceClass
|
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");
|
onClicked: root.enterPage(index)
|
||||||
property State connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
|
|
||||||
|
|
||||||
property StateType powerStateType: deviceClass.stateTypes.findByName("power");
|
property int pendingCommand: -1
|
||||||
property ActionType powerActionType: deviceClass.actionTypes.findByName("power");
|
property var pendingValue: null
|
||||||
property State powerState: device.states.getState(powerStateType.id)
|
|
||||||
|
|
||||||
property StateType brightnessStateType: deviceClass.stateTypes.findByName("brightness");
|
function adjustBrightness(value) {
|
||||||
property ActionType brightnessActionType: deviceClass.actionTypes.findByName("brightness");
|
if (pendingCommand != -1) {
|
||||||
property State brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
|
// busy, cache value
|
||||||
|
pendingValue = value;
|
||||||
property StateType colorStateType: deviceClass.stateTypes.findByName("color");
|
return;
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
pendingCommand = engine.thingManager.executeAction(itemDelegate.thing.id,
|
||||||
|
itemDelegate.brightnessState.stateTypeId,
|
||||||
|
[{
|
||||||
|
paramTypeId: itemDelegate.brightnessState.stateTypeId,
|
||||||
|
value: value
|
||||||
|
}])
|
||||||
|
pendingValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Connections {
|
||||||
Layout.fillWidth: true
|
target: engine.thingManager
|
||||||
text: model.name
|
onExecuteActionReply: {
|
||||||
elide: Text.ElideRight
|
if (itemDelegate.pendingCommand == commandId) {
|
||||||
verticalAlignment: Text.AlignVCenter
|
itemDelegate.pendingCommand = -1;
|
||||||
}
|
if (itemDelegate.pendingValue != null) {
|
||||||
ThrottledSlider {
|
itemDelegate.adjustBrightness(pendingValue)
|
||||||
id: inlineSlider
|
}
|
||||||
visible: contentItem.enabled && itemDelegate.brightnessStateType && itemDelegate.inline
|
}
|
||||||
from: 0; to: 100
|
}
|
||||||
value: itemDelegate.brightnessState ? itemDelegate.brightnessState.value : 0
|
}
|
||||||
onMoved: {
|
|
||||||
var params = [];
|
contentItem: Rectangle {
|
||||||
var param1 = {};
|
color: itemDelegate.powerState.value === true && itemDelegate.colorState ? itemDelegate.colorState.value : "#00000000"
|
||||||
param1["paramTypeId"] = itemDelegate.brightnessActionType.paramTypes.get(0).id;
|
implicitHeight: contentColumn.implicitHeight
|
||||||
param1["value"] = value;
|
Behavior on implicitHeight { NumberAnimation { duration: 100 } }
|
||||||
params.push(param1)
|
radius: 6
|
||||||
engine.deviceManager.executeAction(itemDelegate.device.id, itemDelegate.brightnessActionType.id, params)
|
|
||||||
}
|
ColumnLayout {
|
||||||
}
|
id: contentColumn
|
||||||
Switch {
|
anchors { left: parent.left; right: parent.right }
|
||||||
checked: itemDelegate.powerState.value === true
|
spacing: 0
|
||||||
onClicked: {
|
|
||||||
var params = [];
|
RowLayout {
|
||||||
var param1 = {};
|
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
|
||||||
param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id;
|
spacing: app.margins
|
||||||
param1["value"] = checked;
|
|
||||||
params.push(param1)
|
ColorIcon {
|
||||||
engine.deviceManager.executeAction(device.id, itemDelegate.powerActionType.id, params)
|
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
|
rowSpacing: 0
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.devicesProxy
|
model: root.thingsProxy
|
||||||
|
|
||||||
delegate: Item {
|
delegate: BigTile {
|
||||||
id: itemDelegate
|
id: itemDelegate
|
||||||
Layout.preferredWidth: contentGrid.width / contentGrid.columns
|
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 StateType playbackStateType: thing.thingClass.stateTypes.findByName("playbackStatus")
|
||||||
readonly property State playbackState: thing.stateByName("playbackStatus")
|
readonly property State playbackState: thing.stateByName("playbackStatus")
|
||||||
@ -74,162 +78,112 @@ DeviceListPageBase {
|
|||||||
readonly property StateType playerTypeStateType: thing.thingClass.stateTypes.findByName("playerType")
|
readonly property StateType playerTypeStateType: thing.thingClass.stateTypes.findByName("playerType")
|
||||||
readonly property State playerTypeState: thing.stateByName("playerType")
|
readonly property State playerTypeState: thing.stateByName("playerType")
|
||||||
|
|
||||||
Pane {
|
onClicked: {
|
||||||
id: contentItem
|
enterPage(index)
|
||||||
width: parent.width - app.margins
|
}
|
||||||
anchors.centerIn: parent
|
|
||||||
Material.elevation: 2
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
contentItem: ItemDelegate {
|
contentItem: RowLayout {
|
||||||
leftPadding: 0
|
ColumnLayout {
|
||||||
rightPadding: 0
|
id: leftColummn
|
||||||
topPadding: 0
|
Layout.margins: app.margins
|
||||||
bottomPadding: 0
|
Label {
|
||||||
contentItem: ColumnLayout {
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
text: itemDelegate.playbackState.value === "Stopped" ?
|
||||||
Rectangle {
|
qsTr("No playback")
|
||||||
Layout.fillWidth: true
|
: itemDelegate.thing.stateByName("title").value
|
||||||
Layout.preferredHeight: app.mediumFont + app.margins
|
horizontalAlignment: Text.AlignHCenter
|
||||||
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
|
elide: Text.ElideRight
|
||||||
RowLayout {
|
}
|
||||||
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
|
Label {
|
||||||
Label {
|
Layout.fillWidth: true
|
||||||
Layout.fillWidth: true
|
text: itemDelegate.thing.stateByName("artist").value
|
||||||
text: model.name
|
font.pixelSize: app.smallFont
|
||||||
elide: Text.ElideRight
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
elide: Text.ElideRight
|
||||||
BatteryStatusIcon {
|
}
|
||||||
Layout.preferredHeight: app.iconSize * .5
|
Label {
|
||||||
Layout.preferredWidth: height
|
Layout.fillWidth: true
|
||||||
thing: itemDelegate.thing
|
text: itemDelegate.thing.stateByName("collection").value
|
||||||
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
font.pixelSize: app.smallFont
|
||||||
ConnectionStatusIcon {
|
elide: Text.ElideRight
|
||||||
Layout.preferredHeight: app.iconSize * .5
|
}
|
||||||
Layout.preferredWidth: height
|
MediaControls {
|
||||||
thing: itemDelegate.thing
|
visible: itemDelegate.thing.thingClass.interfaces.indexOf("mediacontroller") >= 0
|
||||||
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
|
thing: itemDelegate.thing
|
||||||
}
|
}
|
||||||
SetupStatusIcon {
|
}
|
||||||
Layout.preferredHeight: app.iconSize * .5
|
Item {
|
||||||
Layout.preferredWidth: height
|
Layout.preferredHeight: leftColummn.height + app.margins * 2
|
||||||
thing: itemDelegate.thing
|
Layout.preferredWidth: height * .7
|
||||||
visible: setupFailed || setupInProgress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 {
|
ColorIcon {
|
||||||
id: artworkContainer
|
width: Math.min(parent.width, parent.height) - app.margins * 2
|
||||||
anchors.fill: parent
|
height: width
|
||||||
Image {
|
anchors.centerIn: parent
|
||||||
id: artworkImage
|
name: itemDelegate.playerTypeState && itemDelegate.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
|
||||||
width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
|
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
|
||||||
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
|
color: "white"
|
||||||
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;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
anchors.fill: parent
|
||||||
model: devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
spacing: app.margins
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Pane {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width
|
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)
|
delegate: BigTile {
|
||||||
property DeviceClass deviceClass: device.deviceClass
|
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");
|
onClicked: root.enterPage(index)
|
||||||
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
|
|
||||||
|
|
||||||
property var powerStateType: deviceClass.stateTypes.findByName("power");
|
property State connectedState: thing.stateByName("connected")
|
||||||
property var powerActionType: deviceClass.actionTypes.findByName("power");
|
property State powerState: thing.stateByName("power")
|
||||||
property var powerState: device.states.getState(powerStateType.id)
|
|
||||||
|
|
||||||
Material.elevation: 1
|
contentItem: RowLayout {
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
contentItem: ItemDelegate {
|
|
||||||
id: contentItem
|
|
||||||
implicitHeight: nameRow.implicitHeight
|
|
||||||
topPadding: 0
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
RowLayout {
|
|
||||||
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
|
|
||||||
id: nameRow
|
|
||||||
z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
|
|
||||||
spacing: app.margins
|
spacing: app.margins
|
||||||
Item {
|
|
||||||
Layout.preferredHeight: app.iconSize
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
ColorIcon {
|
ColorIcon {
|
||||||
id: icon
|
Layout.preferredHeight: app.iconSize
|
||||||
anchors.fill: parent
|
Layout.preferredWidth: app.iconSize
|
||||||
color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false
|
name: app.interfaceToIcon("powersocket")
|
||||||
? "red"
|
color: itemDelegate.powerState.value === true ? app.accentColor : keyColor
|
||||||
: itemDelegate.powerState.value === true ? app.accentColor : keyColor
|
|
||||||
name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ?
|
|
||||||
"../images/dialog-warning-symbolic.svg"
|
|
||||||
: app.interfaceToIcon("powersocket")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: model.name
|
text: itemDelegate.thing.name
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThingStatusIcons {
|
||||||
|
thing: itemDelegate.thing
|
||||||
|
}
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
checked: itemDelegate.powerState.value === true
|
checked: itemDelegate.powerState.value === true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var params = [];
|
var params = [];
|
||||||
var param1 = {};
|
var param1 = {};
|
||||||
param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id;
|
param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
|
||||||
param1["value"] = checked;
|
param1["value"] = checked;
|
||||||
params.push(param1)
|
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()
|
onBackPressed: pageStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: root.thingsProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
topMargin: app.margins / 2
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Item {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width - app.margins
|
width: parent.width - app.margins
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
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: {
|
onClicked: {
|
||||||
enterPage(index)
|
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()
|
onBackPressed: pageStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: root.devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
topMargin: app.margins / 2
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Item {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width - app.margins
|
width: parent.width - app.margins
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
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: {
|
onClicked: {
|
||||||
enterPage(index)
|
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()
|
onBackPressed: pageStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: root.devicesProxy
|
contentHeight: contentGrid.implicitHeight
|
||||||
topMargin: app.margins / 2
|
topMargin: app.margins / 2
|
||||||
|
|
||||||
delegate: Item {
|
GridLayout {
|
||||||
id: itemDelegate
|
id: contentGrid
|
||||||
width: parent.width - app.margins
|
width: parent.width - app.margins
|
||||||
height: contentItem.height + app.margins
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
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)
|
delegate: BigTile {
|
||||||
property DeviceClass deviceClass: device.deviceClass
|
id: itemDelegate
|
||||||
|
Layout.preferredWidth: contentGrid.width / contentGrid.columns
|
||||||
|
thing: root.thingsProxy.get(model.id)
|
||||||
|
|
||||||
Pane {
|
onClicked: enterPage(index)
|
||||||
id: contentItem
|
|
||||||
width: parent.width - app.margins
|
|
||||||
anchors.centerIn: parent
|
|
||||||
Material.elevation: 2
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
contentItem: ItemDelegate {
|
contentItem: WeatherView {
|
||||||
leftPadding: 0
|
thing: itemDelegate.thing
|
||||||
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)})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -242,86 +242,10 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
ThingInfoPane {
|
||||||
id: infoPane
|
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 }
|
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
property bool setupInProgress: root.thing.setupStatus == Thing.ThingSetupStatusInProgress
|
thing: root.thing
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ DevicePageBase {
|
|||||||
GridLayout {
|
GridLayout {
|
||||||
id: contentGrid
|
id: contentGrid
|
||||||
width: parent.width
|
width: parent.width
|
||||||
columns: width / 300
|
columns: Math.ceil(width / 600)
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
|
|||||||
@ -49,8 +49,7 @@ DevicePageBase {
|
|||||||
|
|
||||||
WeatherView {
|
WeatherView {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
device: root.device
|
thing: root.thing
|
||||||
deviceClass: root.deviceClass
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
|
|||||||
@ -67,203 +67,6 @@ MainViewBase {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
pageStack.push(Qt.resolvedUrl("../grouping/GroupInterfacesPage.qml"), {groupTag: model.tagId})
|
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 {
|
SettingsPageBase {
|
||||||
id: root
|
id: root
|
||||||
property Device device: null
|
property Thing thing: null
|
||||||
|
property alias device: root.thing
|
||||||
readonly property DeviceClass deviceClass: device ? device.deviceClass : null
|
readonly property DeviceClass deviceClass: device ? device.deviceClass : null
|
||||||
|
|
||||||
header: NymeaHeader {
|
header: NymeaHeader {
|
||||||
@ -50,6 +51,12 @@ SettingsPageBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThingInfoPane {
|
||||||
|
id: infoPane
|
||||||
|
anchors { left: parent.left; top: parent.top; right: parent.right }
|
||||||
|
thing: root.thing
|
||||||
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: deviceMenu
|
id: deviceMenu
|
||||||
width: implicitWidth + app.margins
|
width: implicitWidth + app.margins
|
||||||
|
|||||||
@ -64,4 +64,19 @@ Item {
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDark(color) {
|
||||||
|
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