diff --git a/nymea-app/dashboard/dashboarditem.cpp b/nymea-app/dashboard/dashboarditem.cpp index 281fc031..bc9777df 100644 --- a/nymea-app/dashboard/dashboarditem.cpp +++ b/nymea-app/dashboard/dashboarditem.cpp @@ -163,3 +163,20 @@ void DashboardWebViewItem::setInteractive(bool interactive) emit changed(); } } + +DashboardStateItem::DashboardStateItem(const QUuid &thingId, const QUuid &stateTypeId, QObject *parent): + DashboardItem("state", parent), + m_thingId(thingId), + m_stateTypeId(stateTypeId) +{ +} + +QUuid DashboardStateItem::thingId() const +{ + return m_thingId; +} + +QUuid DashboardStateItem::stateTypeId() const +{ + return m_stateTypeId; +} diff --git a/nymea-app/dashboard/dashboarditem.h b/nymea-app/dashboard/dashboarditem.h index ebbf330d..4f16036b 100644 --- a/nymea-app/dashboard/dashboarditem.h +++ b/nymea-app/dashboard/dashboarditem.h @@ -110,4 +110,18 @@ private: bool m_interactive = false; }; +class DashboardStateItem: public DashboardItem +{ + Q_OBJECT + Q_PROPERTY(QUuid thingId READ thingId CONSTANT) + Q_PROPERTY(QUuid stateTypeId READ stateTypeId CONSTANT) +public: + explicit DashboardStateItem(const QUuid &thingId, const QUuid &stateTypeId, QObject *parent = nullptr); + QUuid thingId() const; + QUuid stateTypeId() const; +private: + QUuid m_thingId; + QUuid m_stateTypeId; +}; + #endif // DASHBOARDITEM_H diff --git a/nymea-app/dashboard/dashboardmodel.cpp b/nymea-app/dashboard/dashboardmodel.cpp index 831d8454..f72e2bf8 100644 --- a/nymea-app/dashboard/dashboardmodel.cpp +++ b/nymea-app/dashboard/dashboardmodel.cpp @@ -85,6 +85,12 @@ void DashboardModel::addWebViewItem(const QUrl &url, int columnSpan, int rowSpan addItem(item, index); } +void DashboardModel::addStateItem(const QUuid &thingId, const QUuid &stateTypeId, int index) +{ + DashboardStateItem *item = new DashboardStateItem(thingId, stateTypeId, this); + addItem(item, index); +} + void DashboardModel::removeItem(int index) { qWarning() << "removing" << index; @@ -119,6 +125,7 @@ void DashboardModel::loadFromJson(const QByteArray &json) m_list.clear(); QJsonDocument jsonDoc = QJsonDocument::fromJson(json); + qCritical() << "dashboard:" << qUtf8Printable(jsonDoc.toJson()); foreach (const QVariant &itemVariant, jsonDoc.toVariant().toList()) { QVariantMap itemMap = itemVariant.toMap(); QString type = itemMap.value("type").toString(); @@ -136,6 +143,8 @@ void DashboardModel::loadFromJson(const QByteArray &json) item = new DashboardSceneItem(itemMap.value("ruleId").toUuid(), this); } else if (type == "webview") { item = new DashboardWebViewItem(itemMap.value("url").toUrl(), itemMap.value("interactive", false).toBool(), this); + } else if (type == "state") { + item = new DashboardStateItem(itemMap.value("thingId").toUuid(), itemMap.value("stateTypeId").toUuid(), this); } else { qWarning() << "Dashboard item type" << type << "is not implemented. Skipping..."; continue; @@ -179,6 +188,10 @@ QByteArray DashboardModel::toJson() const if (webViewItem->interactive()) { map.insert("interactive", true); } + } else if (item->type() == "state") { + DashboardStateItem *stateItem = dynamic_cast(item); + map.insert("thingId", stateItem->thingId()); + map.insert("stateTypeId", stateItem->stateTypeId()); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Type " + item->type().toUtf8() + " not implemented!"); continue; diff --git a/nymea-app/dashboard/dashboardmodel.h b/nymea-app/dashboard/dashboardmodel.h index dc738873..e89b0115 100644 --- a/nymea-app/dashboard/dashboardmodel.h +++ b/nymea-app/dashboard/dashboardmodel.h @@ -30,6 +30,7 @@ public: Q_INVOKABLE void addGraphItem(const QUuid &thingId, const QUuid &stateTypeId, int index = -1); Q_INVOKABLE void addSceneItem(const QUuid &ruleId, int index = -1); Q_INVOKABLE void addWebViewItem(const QUrl &url, int columnSpan, int rowSpan, bool interactive, int index = -1); + Q_INVOKABLE void addStateItem(const QUuid &thingId, const QUuid &stateTypeId, int index = -1); Q_INVOKABLE void removeItem(int index); Q_INVOKABLE void move(int from, int to); diff --git a/nymea-app/main.cpp b/nymea-app/main.cpp index 2b9590c3..f8a15f14 100644 --- a/nymea-app/main.cpp +++ b/nymea-app/main.cpp @@ -182,6 +182,7 @@ int main(int argc, char *argv[]) qmlRegisterUncreatableType("Nymea", 1, 0, "DashboardGraphItem", ""); qmlRegisterUncreatableType("Nymea", 1, 0, "DashboardSceneItem", ""); qmlRegisterUncreatableType("Nymea", 1, 0, "DashboardWebViewItem", ""); + qmlRegisterUncreatableType("Nymea", 1, 0, "DashboardStateItem", ""); qmlRegisterSingletonType("NymeaApp.Utils", 1, 0, "PrivacyPolicyHelper", PrivacyPolicyHelper::qmlProvider); qmlRegisterType("NymeaApp.Utils", 1, 0, "QHash"); diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 788f6237..f228d7fa 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -314,5 +314,6 @@ ui/devicepages/NotificationsThingPage.qml ui/devicepages/ActionLogPage.qml ui/devicepages/EventLogPage.qml + ui/mainviews/dashboard/DashboardStateDelegate.qml diff --git a/nymea-app/ui/components/MainPageTile.qml b/nymea-app/ui/components/MainPageTile.qml index c08ac0f4..0ef311cf 100644 --- a/nymea-app/ui/components/MainPageTile.qml +++ b/nymea-app/ui/components/MainPageTile.qml @@ -154,7 +154,7 @@ Item { anchors.centerIn: parent width: parent.width text: root.text.toUpperCase() - font.pixelSize: app.smallFont + font.pixelSize: Style.smallFont.pixelSize font.letterSpacing: 1 wrapMode: Text.WrapAtWordBoundaryOrAnywhere horizontalAlignment: Text.AlignHCenter diff --git a/nymea-app/ui/mainviews/dashboard/Dashboard.qml b/nymea-app/ui/mainviews/dashboard/Dashboard.qml index 77cf6d97..c3326762 100644 --- a/nymea-app/ui/mainviews/dashboard/Dashboard.qml +++ b/nymea-app/ui/mainviews/dashboard/Dashboard.qml @@ -63,7 +63,8 @@ MainViewBase { "folder": "DashboardFolderDelegate.qml", "graph": "DashboardGraphDelegate.qml", "scene": "DashboardSceneDelegate.qml", - "webview": "DashboardWebViewDelegate.qml" + "webview": "DashboardWebViewDelegate.qml", + "state": "DashboardStateDelegate.qml" } onEditModeChanged: { diff --git a/nymea-app/ui/mainviews/dashboard/DashboardAddWizard.qml b/nymea-app/ui/mainviews/dashboard/DashboardAddWizard.qml index 678d432e..1fb37165 100644 --- a/nymea-app/ui/mainviews/dashboard/DashboardAddWizard.qml +++ b/nymea-app/ui/mainviews/dashboard/DashboardAddWizard.qml @@ -75,6 +75,14 @@ NymeaDialog { internalPageStack.push(addFolderComponent) } } + NymeaItemDelegate { + Layout.fillWidth: true + text: qsTr("State") + iconName: "state" + onClicked: { + internalPageStack.push(addStateSelectThingComponent) + } + } NymeaItemDelegate { Layout.fillWidth: true text: qsTr("Chart") @@ -234,8 +242,47 @@ NymeaDialog { } } } - } + + Component { + id: addStateSelectThingComponent + ColumnLayout { + RowLayout { + Layout.leftMargin: Style.margins + Layout.rightMargin: Style.margins + ColorIcon { + name: "/ui/images/find.svg" + } + TextField { + id: filterTextField + Layout.fillWidth: true + } + } + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredHeight: Style.delegateHeight * 6 + clip: true + + ScrollBar.vertical: ScrollBar {} + + model: ThingsProxy { + id: thingsProxy + engine: _engine + nameFilter: filterTextField.displayText + } + delegate: NymeaItemDelegate { + text: model.name + width: parent ? parent.width : 0 // silence warning on delegate descruction + iconName: app.interfacesToIcon(thingsProxy.get(index).thingClass.interfaces) + onClicked: { + internalPageStack.push(addStateSelectStateComponent, {thing: thingsProxy.get(index)}) + } + } + } + } + } + Component { id: addGraphSelectStateComponent ListView { @@ -258,6 +305,28 @@ NymeaDialog { } } + Component { + id: addStateSelectStateComponent + ListView { + implicitHeight: Style.delegateHeight * 6 + clip: true + + ScrollBar.vertical: ScrollBar {} + + property Thing thing: null + model: thing.thingClass.stateTypes + width: parent.width + delegate: NymeaItemDelegate { + width: parent.width + text: model.displayName + onClicked: { + root.dashboardModel.addStateItem(thing.id, model.id, root.index) + root.close() + } + } + } + } + Component { id: addSceneComponent ListView { diff --git a/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml b/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml new file mode 100644 index 00000000..3a0213af --- /dev/null +++ b/nymea-app/ui/mainviews/dashboard/DashboardStateDelegate.qml @@ -0,0 +1,125 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import QtQuick 2.8 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.2 +import QtCharts 2.2 +import Nymea 1.0 +import NymeaApp.Utils 1.0 +import "../../components" +import "../../delegates" + +DashboardDelegateBase { + id: root + property DashboardStateItem item: null + + readonly property Thing thing: engine.thingManager.fetchingData ? null : engine.thingManager.things.getThing(root.item.thingId) + readonly property StateType stateType: thing ? thing.thingClass.stateTypes.getStateType(item.stateTypeId) : null + readonly property State state: thing ? thing.state(root.item.stateTypeId) : null + + contentItem: MainPageTile { + id: delegateRoot + height: root.height + width: root.width +// lowerText: root.thing.name + "\n" + root.stateType.displayName +// iconName: NymeaUtils.namedIcon(root.item.icon) + iconColor: Style.accentColor +// onClicked: pageStack.push(Qt.resolvedUrl("DashboardPage.qml"), {item: root.item}) + onPressAndHold: root.longPressed(); + contentItem: Item { + id: bottomLayout + width: parent.width + height: parent.height + + RowLayout { + anchors.fill: parent + anchors.margins: Style.smallMargins + + ColorIcon { + size: Style.iconSize + name: root.thing ? app.interfacesToIcon(root.thing.thingClass.interfaces) : "" + } + + Label { + Layout.fillWidth: true + text: root.thing ? root.thing.name : "" + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + maximumLineCount: 2 + font: Style.smallFont + } + } + } + + ColumnLayout { + width: parent.width + anchors.centerIn: parent + anchors.verticalCenterOffset: -bottomLayout.height / 2 + + Label { + Layout.fillWidth: true + visible: root.stateType && root.stateType.type.toLowerCase() != "bool" + horizontalAlignment: Text.AlignHCenter + font: Style.largeFont + elide: Text.ElideRight + text: root.state ? + 1.0 * Math.round(Types.toUiValue(root.state.value, root.stateType.unit) * Math.pow(10, 1)) / Math.pow(10, 1) + " " + Types.toUiUnit(root.stateType.unit) + : "" + } + + Led { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: Style.bigIconSize + Layout.preferredHeight: Style.bigIconSize + anchors.verticalCenterOffset: -bottomLayout.height / 2 + visible: root.stateType && root.stateType.type.toLowerCase() === "bool" + state: root.state && root.state.value === true ? "on" : "off" + } + + Label { + Layout.fillWidth: true + text: root.stateType ? root.stateType.displayName.toUpperCase() : "" + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: Style.smallFont.pixelSize + font.letterSpacing: 1 + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + maximumLineCount: 2 + + } + + } + } +}