UI refresh

This commit is contained in:
Michael Zanetti 2020-11-28 13:42:42 +01:00
parent 5b5e85c7ad
commit e6d4af8556
38 changed files with 1030 additions and 1488 deletions

View File

@ -308,6 +308,21 @@ void DevicesProxy::setFilterSetupFailed(bool filterSetupFailed)
}
}
bool DevicesProxy::filterUpdates() const
{
return m_filterUpdates;
}
void DevicesProxy::setFilterUpdates(bool filterUpdates)
{
if (m_filterUpdates != filterUpdates) {
m_filterUpdates = filterUpdates;
emit filterUpdatesChanged();
invalidateFilter();
emit countChanged();
}
}
bool DevicesProxy::groupByInterface() const
{
return m_groupByInterface;
@ -371,7 +386,15 @@ bool DevicesProxy::lessThan(const QModelIndex &left, const QModelIndex &right) c
QString leftName = sourceModel()->data(left, Devices::RoleName).toString();
QString rightName = sourceModel()->data(right, Devices::RoleName).toString();
return QString::localeAwareCompare(leftName, rightName) < 0;
int comparison = QString::localeAwareCompare(leftName, rightName);
if (comparison == 0) {
// If there are 2 identically named things we don't want undefined behavor as it may cause items
// to reorder randomly. Use something static like thingId as fallback
QString leftThingId = sourceModel()->data(left, Devices::RoleId).toString();
QString rightThingId = sourceModel()->data(right, Devices::RoleId).toString();
comparison = QString::localeAwareCompare(leftThingId, rightThingId);
}
return comparison < 0;
}
bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
@ -452,6 +475,15 @@ bool DevicesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_pa
}
}
if (m_filterUpdates) {
if (!deviceClass->interfaces().contains("update")) {
return false;
}
if (device->stateValue(deviceClass->stateTypes()->findByName("updateStatus")->id()).toString() == "idle") {
return false;
}
}
if (!m_nameFilter.isEmpty()) {
if (!device->name().toLower().contains(m_nameFilter.toLower().trimmed())) {
return false;

View File

@ -67,6 +67,8 @@ class DevicesProxy : public QSortFilterProxyModel
Q_PROPERTY(bool filterSetupFailed READ filterSetupFailed WRITE setFilterSetupFailed NOTIFY filterSetupFailedChanged)
Q_PROPERTY(bool filterUpdates READ filterUpdates WRITE setFilterUpdates NOTIFY filterUpdatesChanged)
Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged)
public:
@ -120,12 +122,15 @@ public:
bool filterSetupFailed() const;
void setFilterSetupFailed(bool filterSetupFailed);
bool groupByInterface() const;
void setGroupByInterface(bool groupByInterface);
bool filterUpdates() const;
void setFilterUpdates(bool filterUpdates);
bool groupByInterface() const;
void setGroupByInterface(bool groupByInterface);
Q_INVOKABLE Device *get(int index) const;
Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
Q_INVOKABLE Device *getDevice(const QUuid &deviceId) const;
Q_INVOKABLE Device *getThing(const QUuid &thingId) const;
signals:
void engineChanged();
@ -144,6 +149,7 @@ signals:
void filterBatteryCriticalChanged();
void filterDisconnectedChanged();
void filterSetupFailedChanged();
void filterUpdatesChanged();
void groupByInterfaceChanged();
void countChanged();
@ -168,6 +174,7 @@ private:
bool m_filterBatteryCritical = false;
bool m_filterDisconnected = false;
bool m_filterSetupFailed = false;
bool m_filterUpdates = false;
bool m_groupByInterface = false;

View File

@ -47,9 +47,7 @@
<file>ui/components/Led.qml</file>
<file>ui/components/ProgressButton.qml</file>
<file>ui/customviews/GenericTypeLogView.qml</file>
<file>ui/customviews/CustomViewBase.qml</file>
<file>ui/customviews/WeatherView.qml</file>
<file>ui/customviews/NotificationsView.qml</file>
<file>ui/devicepages/MediaThingPage.qml</file>
<file>ui/devicepages/ButtonDevicePage.qml</file>
<file>ui/devicepages/GenericDevicePage.qml</file>
@ -223,5 +221,9 @@
<file>ui/magic/WriteNfcTagPage.qml</file>
<file>ui/components/MediaPlayer.qml</file>
<file>ui/components/InfoPane.qml</file>
<file>ui/components/BigTile.qml</file>
<file>ui/components/ThingStatusIcons.qml</file>
<file>ui/components/InfoPaneBase.qml</file>
<file>ui/components/ThingInfoPane.qml</file>
</qresource>
</RCC>

View File

@ -20,6 +20,5 @@
<file>styles/energize/Button.qml</file>
<file>styles/energize/logo.svg</file>
<file>styles/energize/Page.qml</file>
<file>styles/energize/Pane.qml</file>
</qresource>
</RCC>

View File

@ -58,7 +58,7 @@ ApplicationWindow {
property color headerForegroundColor: "#79a79f"
// The font color
property color foregroundColor: "#69938c"
property color foregroundColor: "#638B87"
// The color of selected/highlighted things
property color accentColor: "#8cc1b6"

View File

@ -64,7 +64,7 @@ T.Button {
font.overline: control.font.overline
font.pixelSize: app.smallFont
font.weight: Font.Bold
color: app.foregroundColor
color: app.backgroundColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight

View File

@ -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
}

View File

@ -202,7 +202,7 @@ Page {
text: engine.systemController.updateRunning ? qsTr("System update in progress...") : qsTr("%n system update(s) available", "", updatesModel.count)
imageSource: "../images/system-update.svg"
rotatingIcon: engine.systemController.updateRunning
onPaneClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
onClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
PackagesFilterModel {
id: updatesModel

View 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()
}
}
}

View File

@ -3,89 +3,53 @@ import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.2
Item {
InfoPaneBase {
id: root
implicitHeight: d.shownHeight
visible: d.shownHeight > 0
property alias text: textLabel.text
property alias imageSource: icon.name
property alias buttonText: button.text
property alias color: background.color
property alias textColor: textLabel.color
property color textColor: "white"
property bool rotatingIcon: false
property bool shown: false
function show() {
shown = true;
}
function hide() {
shown = false;
}
signal paneClicked();
signal buttonClicked();
QtObject {
id: d
property int shownHeight: shown ? contentRow.implicitHeight : 0
Behavior on shownHeight { NumberAnimation { easing.type: Easing.InOutQuad; duration: 150 } }
}
contentItem: RowLayout {
id: contentRow
anchors { left: parent.left; top: parent.top; right: parent.right }
Pane {
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
Material.elevation: 2
padding: 0
height: contentRow.implicitHeight
Label {
id: textLabel
color: root.textColor
font.pixelSize: app.smallFont
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
}
ColorIcon {
id: icon
Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
color: root.textColor
visible: name.length > 0
MouseArea {
anchors.fill: parent
onClicked: root.paneClicked()
RotationAnimation on rotation {
from: 0
to: 360
duration: 2000
loops: Animation.Infinite
running: root.rotatingIcon
onStopped: icon.rotation = 0;
}
}
Rectangle {
id: background
color: app.accentColor
anchors.fill: parent
RowLayout {
id: contentRow
anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: app.margins; rightMargin: app.margins }
Label {
id: textLabel
color: "white"
font.pixelSize: app.smallFont
Layout.fillWidth: true
Layout.margins: app.margins * .4
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap
}
ColorIcon {
id: icon
height: app.smallIconSize
width: height
color: "white"
visible: name.length > 0
RotationAnimation on rotation {
from: 0
to: 360
duration: 2000
loops: Animation.Infinite
running: root.rotatingIcon
onStopped: icon.rotation = 0;
}
}
Button {
id: button
visible: text.length > 0
onClicked: root.buttonClicked()
}
}
Button {
id: button
Layout.leftMargin: app.margins
visible: text.length > 0
onClicked: root.buttonClicked()
}
}
}

View 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
}
}
}

View File

@ -48,6 +48,7 @@ Item {
property int signalStrength: 0
property int setupStatus: Thing.ThingSetupStatusNone
property bool batteryCritical: false
property bool updateStatus: false
property alias contentItem: innerContent.children
@ -119,7 +120,10 @@ Item {
spacing: 0
ColumnLayout {
Layout.fillHeight: true
Layout.margins: app.margins
Layout.topMargin: app.margins
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
Layout.bottomMargin: app.margins / 2
Item {
visible: backgroundImg.status !== Image.Ready
Layout.fillWidth: true
@ -171,10 +175,17 @@ Item {
}
}
Row {
RowLayout {
id: quickAlertPane
anchors { top: parent.top; right: parent.right; margins: app.margins }
spacing: app.margins / 2
ColorIcon {
height: app.smallIconSize
width: height
name: "../images/system-update.svg"
color: app.accentColor
visible: root.updateStatus
}
ColorIcon {
height: app.smallIconSize
width: height

View File

@ -36,13 +36,15 @@ import Nymea 1.0
import "../components"
import "../delegates"
MouseArea {
Item {
id: root
// Prevent scroll events to swipe left/right in case they fall through the grid
preventStealing: true
onWheel: wheel.accepted = true
property string title: ""
// Prevent scroll events to swipe left/right in case they fall through the grid
MouseArea {
anchors.fill: parent
preventStealing: true
onWheel: wheel.accepted = true
}
}

View File

@ -151,7 +151,7 @@ SwipeDelegate {
ColorIcon {
id: secondaryIcon
Layout.preferredHeight: app.iconSize * .5
Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
visible: name.length > 0
MouseArea {
@ -165,7 +165,7 @@ SwipeDelegate {
ColorIcon {
id: tertiaryIcon
Layout.preferredHeight: app.iconSize * .5
Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
visible: name.length > 0
MouseArea {
@ -179,7 +179,7 @@ SwipeDelegate {
ColorIcon {
id: progressionIcon
Layout.preferredHeight: app.iconSize * .6
Layout.preferredHeight: app.smallIconSize
Layout.preferredWidth: height
name: "../images/next.svg"
visible: root.progressive

View 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
}
}
}

View 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 }
}
}

View File

@ -11,6 +11,7 @@ ColorIcon {
readonly property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
name: "../images/system-update.svg"
color: app.accentColor
RotationAnimation on rotation {
from: 0; to: 360

View File

@ -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
}

View File

@ -259,7 +259,7 @@ Item {
id: connectedLineSeries
onPointAdded: {
var newPoint = connectedLineSeries.at(index)
print("pointadded", newPoint.x, newPoint.y)
// print("pointadded", newPoint.x, newPoint.y)
}
}
@ -284,7 +284,7 @@ Item {
id: lineSeries1
onPointAdded: {
var newPoint = lineSeries1.at(index)
print("pointadded", newPoint.x, newPoint.y)
// print("pointadded", newPoint.x, newPoint.y)
if (newPoint.x > lineSeries0.at(0).x) {
lineSeries0.replace(0, newPoint.x, 0)

View File

@ -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)
}
}
}
}

View File

@ -34,30 +34,21 @@ import QtQuick.Controls 2.1
import "../components"
import Nymea 1.0
CustomViewBase {
Item {
id: root
height: grid.implicitHeight + app.margins * 2
implicitHeight: grid.implicitHeight + app.margins * 2
readonly property StateType weatherConditionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("weatherCondition") : null
readonly property State weatherConditionState: weatherConditionStateType && device.states ? device.states.getState(weatherConditionStateType.id) : null
property Thing thing: null
readonly property StateType weatherDescriptionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("weatherDescription") : null
readonly property State weatherDescriptionState: weatherDescriptionStateType && device.states ? device.states.getState(weatherDescriptionStateType.id) : null
readonly property State weatherConditionState: thing.stateByName("weatherCondition")
readonly property State weatherDescriptionState: thing.stateByName("weatherDescription")
readonly property State temperatureState: thing.stateByName("temperature")
readonly property State humidityState: thing.stateByName("humidity")
readonly property State pressureState: thing.stateByName("pressure")
readonly property State windDirectionState: thing.stateByName("windDirection")
readonly property State windSpeedState: thing.stateByName("windSpeed")
readonly property StateType temperatureStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("temperature") : null
readonly property State temperatureState: temperatureStateType && device.states ? device.states.getState(temperatureStateType.id) : null
readonly property StateType humidityStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("humidity") : null
readonly property State humidityState: humidityStateType && device.states ? device.states.getState(humidityStateType.id) : null
readonly property StateType pressureStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("pressure") : null
readonly property State pressureState: pressureStateType && device.states ? device.states.getState(pressureStateType.id) : null
readonly property StateType windDirectionStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("windDirection") : null
readonly property State windDirectionState: windDirectionStateType && device.states ? device.states.getState(windDirectionStateType.id) : null
readonly property StateType windSpeedStateType: deviceClass.stateTypes ? deviceClass.stateTypes.findByName("windSpeed") : null
readonly property State windSpeedState: windSpeedStateType && device.states ? device.states.getState(windSpeedStateType.id) : null
readonly property StateType temperatureStateType: thing.thingClass.stateTypes.findByName("temperature")
ColumnLayout {
id: grid
@ -86,7 +77,6 @@ CustomViewBase {
Label {
text: (temperatureState ? Math.round(Types.toUiValue(temperatureState.value, temperatureStateType.unit) * 10) / 10 : "N/A") + " °"
Layout.fillWidth: true
font.pixelSize: app.largeFont
horizontalAlignment: Text.AlignHCenter
}
ColorIcon {

View File

@ -44,6 +44,7 @@ MainPageTile {
isWireless: devicesSubProxyConnectables.count > 0 && devicesSubProxyConnectables.get(0).thingClass.interfaces.indexOf("wirelessconnectable") >= 0
batteryCritical: devicesSubProxyBattery.count > 0
setupStatus: thingsSubProxySetupFailure.count > 0 ? Thing.ThingSetupStatusFailed : Thing.ThingSetupStatusComplete
updateStatus: thingsSubProxyUpdates.count > 0
property Interface iface: null
property alias filterTagId: devicesProxy.filterTagId
@ -139,6 +140,12 @@ MainPageTile {
parentProxy: devicesProxy
filterSetupFailed: true
}
ThingsProxy {
id: thingsSubProxyUpdates
engine: _engine
parentProxy: devicesProxy
filterUpdates: true
}
property int currentDeviceIndex: 0
readonly property Device currentDevice: devicesProxy.get(currentDeviceIndex)
@ -220,7 +227,7 @@ MainPageTile {
var d = devicesProxy.get(i);
var st = d.deviceClass.stateTypes.findByName("playbackStatus")
var s = d.states.getState(st.id)
s.valueChanged.connect(function() {updateTile()})
s.valueChanged.connect(function() {inlineMediaControl.updateTile()})
}
updateTile();
}

View File

@ -45,6 +45,7 @@ MainPageTile {
disconnected: connectedState && connectedState.value === false
signalStrength: signalStrengthState ? signalStrengthState.value : -1
setupStatus: device.setupStatus
updateStatus: updateStatusState && updateStatusState.value !== "idle"
backgroundImage: artworkState && artworkState.value.length > 0 ? artworkState.value : ""
@ -54,6 +55,7 @@ MainPageTile {
readonly property State signalStrengthState: device.stateByName("signalStrength")
readonly property State batteryCriticalState: deviceClass.interfaces.indexOf("battery") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("batteryCritical").id) : null
readonly property State artworkState: deviceClass.interfaces.indexOf("mediametadataprovider") >= 0 ? device.states.getState(deviceClass.stateTypes.findByName("artwork").id) : null
readonly property State updateStatusState: device.stateByName("updateStatus")
contentItem: Loader {
id: loader

View File

@ -83,88 +83,69 @@ DeviceListPageBase {
}
}
ListView {
Flickable {
anchors.fill: parent
model: devicesProxy
spacing: app.margins
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Pane {
id: itemDelegate
width: parent.width
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.thingsProxy
property bool inline: width > 500
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
showHeader: false
enabled: connectedState == null || connectedState.value === true
topPadding: 0
bottomPadding: 0
property Device device: devicesProxy.getDevice(model.id)
property DeviceClass deviceClass: device.deviceClass
onClicked: root.enterPage(index)
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
property State connectedState: thing.stateByName("connected")
property State movingState: thing.stateByName("moving")
property State percentageState: thing.stateByName("percentage")
property StateType percentageStateType: deviceClass.stateTypes.findByName("percentage");
property ActionType percentageActionType: deviceClass.actionTypes.findByName("percentage");
property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
Material.elevation: 1
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
contentItem: ItemDelegate {
id: contentItem
implicitHeight: nameRow.implicitHeight
topPadding: 0
contentItem: ColumnLayout {
spacing: 0
RowLayout {
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
id: nameRow
z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
contentItem: RowLayout {
spacing: app.margins
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: itemDelegate.movingStateType && itemDelegate.movingState.value === true
? app.accentColor
: keyColor
name: itemDelegate.percentageStateType
? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
: root.iconBasename + "-050.svg"
}
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
color: itemDelegate.movingState && itemDelegate.movingState.value === true
? app.accentColor
: keyColor
name: itemDelegate.percentageState
? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
: root.iconBasename + "-050.svg"
}
Label {
Layout.fillWidth: true
text: itemDelegate.device.name
text: itemDelegate.thing.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: itemDelegate.device
invert: root.invertControls
}
ThingStatusIcons {
thing: itemDelegate.thing
}
ShutterControls {
id: shutterControls
Layout.fillWidth: false
height: parent.height
device: itemDelegate.thing
invert: root.invertControls
}
}
}
onClicked: {
enterPage(index)
}
}
}
}

View File

@ -44,242 +44,86 @@ DeviceListPageBase {
header: NymeaHeader {
id: header
onBackPressed: pageStack.pop()
text: root.title
text: qsTr("Garage doors")
}
ListView {
Flickable {
anchors.fill: parent
model: devicesProxy
spacing: app.margins
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Pane {
id: itemDelegate
width: parent.width
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.thingsProxy
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
showHeader: false
enabled: connectedState == null || connectedState.value === true
topPadding: 0
bottomPadding: 0
property bool inline: width > 500
onClicked: root.enterPage(index)
property Device device: devicesProxy.getDevice(model.id)
property Device thing: device
property DeviceClass deviceClass: device.deviceClass
property State connectedState: thing.stateByName("connected")
property State movingState: thing.stateByName("moving")
property State percentageState: thing.stateByName("percentage")
readonly property bool isImpulseBased: device.deviceClass.interfaces.indexOf("impulsegaragedoor") >= 0
readonly property bool isStateful: device.deviceClass.interfaces.indexOf("statefulgaragedoor") >= 0
|| device.deviceClass.interfaces.indexOf("garagegate") >= 0 // garagegate did not inherit garagedoor before 0.23
readonly property bool isExtended: device.deviceClass.interfaces.indexOf("extendedstatefulgaragedoor") >= 0
readonly property bool isImpulseBased: thing.thingClass.interfaces.indexOf("impulsegaragedoor") >= 0
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
color: itemDelegate.movingState && itemDelegate.movingState.value === true
? app.accentColor
: keyColor
name: itemDelegate.percentageState
? root.iconBasename + "-" + app.pad(Math.round(itemDelegate.percentageState.value / 10) * 10, 3) + ".svg"
: root.iconBasename + "-050.svg"
}
property StateType movingStateType: deviceClass.stateTypes.findByName("moving");
property ActionType movingActionType: deviceClass.actionTypes.findByName("moving");
property State movingState: movingStateType ? device.states.getState(movingStateType.id) : null
Label {
Layout.fillWidth: true
text: itemDelegate.thing.name
elide: Text.ElideRight
}
Material.elevation: 1
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
contentItem: ItemDelegate {
id: contentItem
implicitHeight: contentLoader.item.implicitHeight
ThingStatusIcons {
thing: itemDelegate.thing
}
topPadding: 0
contentItem: Loader {
id: contentLoader
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
sourceComponent: isImpulseBased ? impulseGaragedoor
: isExtended ? extendedStatefulGaragedoor
: isStateful ? garagedoor
: simpleGaragedoor
Binding { target: contentLoader.item; property: "device"; value: itemDelegate.device }
}
onClicked: {
enterPage(index)
}
}
}
}
Component {
id: impulseGaragedoor
RowLayout {
id: contentItem
property Device device: null
spacing: app.margins
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
ItemDelegate {
Layout.preferredHeight: app.iconSize * 2
Layout.preferredWidth: height
ColorIcon {
anchors.centerIn: parent
height: app.iconSize
width: height
name: "../images/closable-move.svg"
}
onClicked: {
var actionTypeId = device.thingClass.actionTypes.findByName("triggerImpulse").id
print("Triggering impulse", actionTypeId)
engine.thingManager.executeAction(device.id, actionTypeId)
}
}
}
}
Component {
id: simpleGaragedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: keyColor
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
}
}
}
}
Component {
id: garagedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
? app.accentColor
: keyColor
name: root.iconBasename + "-100.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
}
}
}
}
Component {
id: extendedStatefulGaragedoor
RowLayout {
id: contentItem
spacing: app.margins
property Device device: null
property StateType stateStateType: device.deviceClass.stateTypes.findByName("state")
property State stateState: stateStateType ? device.states.getState(stateStateType.id) : null
property StateType percentageStateType: device.deviceClass.stateTypes.findByName("percentage");
property ActionType percentageActionType: device.deviceClass.actionTypes.findByName("percentage");
property State percentageState: percentageStateType ? device.states.getState(percentageStateType.id) : null
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: ["opening", "closing"].indexOf(contentItem.stateState.value) >= 0
? app.accentColor
: keyColor
name: contentItem.percentageStateType
? root.iconBasename + "-" + app.pad(Math.round(contentItem.percentageState.value / 10) * 10, 3) + ".svg"
: root.iconBasename + "-050.svg"
}
}
Label {
Layout.fillWidth: true
text: contentItem.device.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
Item {
Layout.preferredWidth: shutterControls.implicitWidth
Layout.preferredHeight: app.iconSize * 2
ShutterControls {
id: shutterControls
height: parent.height
device: contentItem.device
ShutterControls {
id: shutterControls
Layout.fillWidth: false
height: parent.height
device: itemDelegate.thing
invert: root.invertControls
visible: !itemDelegate.isImpulseBased
}
ProgressButton {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
longpressEnabled: false
imageSource: "../images/closable-move.svg"
visible: itemDelegate.isImpulseBased
onClicked: {
var actionTypeId = itemDelegate.thing.thingClass.actionTypes.findByName("triggerImpulse").id
print("Triggering impulse", actionTypeId)
engine.thingManager.executeAction(itemDelegate.thing.id, actionTypeId)
}
}
}
}
}
}

View File

@ -53,15 +53,69 @@ DeviceListPageBase {
}
}
ListView {
Flickable {
anchors.fill: parent
model: root.devicesProxy
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: ThingDelegate {
width: parent.width
device: engine.deviceManager.devices.getDevice(model.id);
onClicked: {
enterPage(index)
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.thingsProxy
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
showHeader: false
enabled: connectedState == null || connectedState.value === true
topPadding: 0
bottomPadding: 0
onClicked: root.enterPage(index)
property State connectedState: thing.stateByName("connected")
property State powerState: thing.stateByName("power")
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: app.interfacesToIcon(itemDelegate.thing.thingClass.interfaces)
color: itemDelegate.powerState && itemDelegate.powerState.value === true ? app.accentColor : keyColor
}
Label {
Layout.fillWidth: true
text: itemDelegate.thing.name
elide: Text.ElideRight
}
ThingStatusIcons {
thing: itemDelegate.thing
}
Switch {
visible: itemDelegate.powerState !== null
checked: itemDelegate.powerState.value === true
onClicked: {
var params = [];
var param1 = {};
param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
param1["value"] = checked;
params.push(param1)
engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
}
}
}
}
}
}
}

View File

@ -37,6 +37,7 @@ import Nymea 1.0
import "../components"
DeviceListPageBase {
id: root
header: NymeaHeader {
text: qsTr("Lights")
@ -70,125 +71,151 @@ DeviceListPageBase {
}
}
ListView {
Flickable {
anchors.fill: parent
model: devicesProxy
spacing: app.margins
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Pane {
id: itemDelegate
width: parent.width
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.thingsProxy
property bool inline: width > 500
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
showHeader: false
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
enabled: connectedState == null || connectedState.value === true
property Device device: devicesProxy.getDevice(model.id)
property DeviceClass deviceClass: device.deviceClass
property State connectedState: thing.stateByName("connected")
property State powerState: thing.stateByName("power")
property State brightnessState: thing.stateByName("brightness")
property State colorState: thing.stateByName("color")
property StateType connectedStateType: deviceClass.stateTypes.findByName("connected");
property State connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
onClicked: root.enterPage(index)
property StateType powerStateType: deviceClass.stateTypes.findByName("power");
property ActionType powerActionType: deviceClass.actionTypes.findByName("power");
property State powerState: device.states.getState(powerStateType.id)
property int pendingCommand: -1
property var pendingValue: null
property StateType brightnessStateType: deviceClass.stateTypes.findByName("brightness");
property ActionType brightnessActionType: deviceClass.actionTypes.findByName("brightness");
property State brightnessState: brightnessStateType ? device.states.getState(brightnessStateType.id) : null
property StateType colorStateType: deviceClass.stateTypes.findByName("color");
property State colorState: colorStateType ? device.states.getState(colorStateType.id) : null
Material.elevation: 1
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
contentItem: ItemDelegate {
id: contentItem
implicitHeight: nameRow.implicitHeight
// gradient: Gradient {
// GradientStop { position: 0.0; color: "transparent" }
// GradientStop { position: 1.0; color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05) }
// }
topPadding: 0
Rectangle {
anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
width: app.margins / 2
color: itemDelegate.colorStateType ? itemDelegate.colorState.value : "#00000000"
}
contentItem: ColumnLayout {
spacing: 0
RowLayout {
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
id: nameRow
z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
spacing: app.margins
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
// DropShadow {
// anchors.fill: icon
// horizontalOffset: 0
// verticalOffset: 0
// radius: 2.0
// samples: 17
// color: app.foregroundColor
// source: icon
// }
ColorIcon {
id: icon
anchors.fill: parent
color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false
? "red"
: app.accentColor
name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ?
"../images/dialog-warning-symbolic.svg"
: itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
}
function adjustBrightness(value) {
if (pendingCommand != -1) {
// busy, cache value
pendingValue = value;
return;
}
pendingCommand = engine.thingManager.executeAction(itemDelegate.thing.id,
itemDelegate.brightnessState.stateTypeId,
[{
paramTypeId: itemDelegate.brightnessState.stateTypeId,
value: value
}])
pendingValue = null;
}
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
ThrottledSlider {
id: inlineSlider
visible: contentItem.enabled && itemDelegate.brightnessStateType && itemDelegate.inline
from: 0; to: 100
value: itemDelegate.brightnessState ? itemDelegate.brightnessState.value : 0
onMoved: {
var params = [];
var param1 = {};
param1["paramTypeId"] = itemDelegate.brightnessActionType.paramTypes.get(0).id;
param1["value"] = value;
params.push(param1)
engine.deviceManager.executeAction(itemDelegate.device.id, itemDelegate.brightnessActionType.id, params)
}
}
Switch {
checked: itemDelegate.powerState.value === true
onClicked: {
var params = [];
var param1 = {};
param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id;
param1["value"] = checked;
params.push(param1)
engine.deviceManager.executeAction(device.id, itemDelegate.powerActionType.id, params)
Connections {
target: engine.thingManager
onExecuteActionReply: {
if (itemDelegate.pendingCommand == commandId) {
itemDelegate.pendingCommand = -1;
if (itemDelegate.pendingValue != null) {
itemDelegate.adjustBrightness(pendingValue)
}
}
}
}
contentItem: Rectangle {
color: itemDelegate.powerState.value === true && itemDelegate.colorState ? itemDelegate.colorState.value : "#00000000"
implicitHeight: contentColumn.implicitHeight
Behavior on implicitHeight { NumberAnimation { duration: 100 } }
radius: 6
ColumnLayout {
id: contentColumn
anchors { left: parent.left; right: parent.right }
spacing: 0
RowLayout {
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: itemDelegate.powerState.value === true ? "../images/light-on.svg" : "../images/light-off.svg"
color: itemDelegate.powerState.value === true ? app.accentColor : keyColor
}
Label {
id: nameLabel
Layout.fillWidth: true
text: itemDelegate.thing.name
elide: Text.ElideRight
Binding {
target: nameLabel
property: "color"
value: itemDelegate.colorState && NymeaUtils.isDark(app.foregroundColor) === NymeaUtils.isDark(itemDelegate.colorState.value) ?
app.backgroundColor : app.foregroundColor
when: nameLabel.enabled && itemDelegate.colorState !== null && itemDelegate.powerState.value === true
}
}
ThingStatusIcons {
thing: itemDelegate.thing
}
Switch {
checked: itemDelegate.powerState.value === true
onClicked: {
var params = [];
var param1 = {};
param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
param1["value"] = checked;
params.push(param1)
print("executing for thing:", itemDelegate.thing.id)
engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 12
visible: itemDelegate.powerState.value === true && itemDelegate.brightnessState != null
radius: 6
color: Qt.tint(app.backgroundColor, Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .1))
Rectangle {
id: knob
height: 12
width: 12
radius: 8
color: app.accentColor
anchors.verticalCenter: parent.verticalCenter
x: itemDelegate.brightnessState ? itemDelegate.brightnessState.value * (parent.width - width) / 100 : 0
}
MouseArea {
anchors.fill: parent
preventStealing: true
onMouseXChanged: {
itemDelegate.adjustBrightness(Math.max(1, Math.min(100, mouseX / width * 100)))
}
}
}
}
}
}
onClicked: {
enterPage(index)
}
}
}

View File

@ -57,16 +57,20 @@ DeviceListPageBase {
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.devicesProxy
model: root.thingsProxy
delegate: Item {
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
height: contentItem.implicitHeight + app.margins
thing: thingsProxy.getThing(model.id)
property bool inline: width > 500
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
enabled: connectedState == null || connectedState.value === true
property Thing thing: thingsProxy.getThing(model.id)
property State connectedState: thing.stateByName("connected")
readonly property StateType playbackStateType: thing.thingClass.stateTypes.findByName("playbackStatus")
readonly property State playbackState: thing.stateByName("playbackStatus")
@ -74,162 +78,112 @@ DeviceListPageBase {
readonly property StateType playerTypeStateType: thing.thingClass.stateTypes.findByName("playerType")
readonly property State playerTypeState: thing.stateByName("playerType")
Pane {
id: contentItem
width: parent.width - app.margins
anchors.centerIn: parent
Material.elevation: 2
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
onClicked: {
enterPage(index)
}
contentItem: ItemDelegate {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.mediumFont + app.margins
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
RowLayout {
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
}
BatteryStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
}
ConnectionStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
}
SetupStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: setupFailed || setupInProgress
}
}
contentItem: RowLayout {
ColumnLayout {
id: leftColummn
Layout.margins: app.margins
Label {
Layout.fillWidth: true
text: itemDelegate.playbackState.value === "Stopped" ?
qsTr("No playback")
: itemDelegate.thing.stateByName("title").value
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.thing.stateByName("artist").value
font.pixelSize: app.smallFont
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.thing.stateByName("collection").value
horizontalAlignment: Text.AlignHCenter
font.pixelSize: app.smallFont
elide: Text.ElideRight
}
MediaControls {
visible: itemDelegate.thing.thingClass.interfaces.indexOf("mediacontroller") >= 0
thing: itemDelegate.thing
}
}
Item {
Layout.preferredHeight: leftColummn.height + app.margins * 2
Layout.preferredWidth: height * .7
Item {
id: artworkContainer
anchors.fill: parent
Image {
id: artworkImage
width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
readonly property State artworkState: thing.stateByName("artwork")
source: artworkState ? artworkState.value : ""
}
Rectangle {
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
anchors.fill: parent
color: "black"
}
RowLayout {
ColumnLayout {
id: leftColummn
Layout.margins: app.margins
Label {
Layout.fillWidth: true
text: itemDelegate.playbackState.value === "Stopped" ?
qsTr("No playback")
: itemDelegate.thing.stateByName("title").value
horizontalAlignment: Text.AlignHCenter
// font.pixelSize: app.largeFont
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.thing.stateByName("artist").value
font.pixelSize: app.smallFont
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: itemDelegate.thing.stateByName("collection").value
horizontalAlignment: Text.AlignHCenter
font.pixelSize: app.smallFont
elide: Text.ElideRight
}
MediaControls {
visible: itemDelegate.thing.thingClass.interfaces.indexOf("mediacontroller") >= 0
thing: itemDelegate.thing
}
}
Item {
Layout.preferredHeight: leftColummn.height + app.margins * 2
Layout.preferredWidth: height * .7
Item {
id: artworkContainer
anchors.fill: parent
Image {
id: artworkImage
width: artworkImage.sourceSize.width * height / artworkImage.sourceSize.height
anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
readonly property State artworkState: thing.stateByName("artwork")
source: artworkState ? artworkState.value : ""
}
Rectangle {
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
anchors.fill: parent
color: "black"
}
ColorIcon {
width: Math.min(parent.width, parent.height) - app.margins * 2
height: width
anchors.centerIn: parent
name: itemDelegate.playerTypeState && itemDelegate.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
color: "white"
}
}
Rectangle {
id: maskRect
anchors.centerIn: parent
height: parent.width
width: parent.height
gradient: Gradient {
GradientStop { position: 0; color: "#00FF0000" }
GradientStop { position: 0.2; color: "#15FF0000" }
GradientStop { position: 1; color: "#FFFF0000" }
}
}
ShaderEffect {
anchors.fill: parent
property variant source: ShaderEffectSource {
sourceItem: artworkContainer
hideSource: true
}
property variant mask: ShaderEffectSource {
sourceItem: maskRect
hideSource: true
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform sampler2D mask;
void main(void)
{
highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
highp float alpha = texture2D(mask, vec2(qt_TexCoord0.y, qt_TexCoord0.x)).a;
sourceColor *= alpha;
gl_FragColor = sourceColor;
}
"
}
}
ColorIcon {
width: Math.min(parent.width, parent.height) - app.margins * 2
height: width
anchors.centerIn: parent
name: itemDelegate.playerTypeState && itemDelegate.playerTypeState.value === "video" ? "../images/stock_video.svg" : "../images/stock_music.svg"
visible: artworkImage.status !== Image.Ready || artworkImage.source === ""
color: "white"
}
}
onClicked: {
enterPage(index)
Rectangle {
id: maskRect
anchors.centerIn: parent
height: parent.width
width: parent.height
radius: 6
gradient: Gradient {
GradientStop { position: 0; color: "#00FF0000" }
GradientStop { position: 0.2; color: "#15FF0000" }
GradientStop { position: 1; color: "#FFFF0000" }
}
}
ShaderEffect {
anchors.fill: parent
property variant source: ShaderEffectSource {
sourceItem: artworkContainer
hideSource: true
}
property variant mask: ShaderEffectSource {
sourceItem: maskRect
hideSource: true
}
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform sampler2D mask;
void main(void)
{
highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
highp float alpha = texture2D(mask, vec2(qt_TexCoord0.y, qt_TexCoord0.x)).a;
sourceColor *= alpha;
gl_FragColor = sourceColor;
}
"
}
}
}
}
}
}
}

View File

@ -47,81 +47,68 @@ DeviceListPageBase {
}
}
ListView {
Flickable {
anchors.fill: parent
model: devicesProxy
spacing: app.margins
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Pane {
id: itemDelegate
width: parent.width
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
Repeater {
model: root.thingsProxy
property Device device: devicesProxy.getDevice(model.id)
property DeviceClass deviceClass: device.deviceClass
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
showHeader: false
enabled: connectedState == null || connectedState.value === true
topPadding: 0
bottomPadding: 0
property var connectedStateType: deviceClass.stateTypes.findByName("connected");
property var connectedState: connectedStateType ? device.states.getState(connectedStateType.id) : null
onClicked: root.enterPage(index)
property var powerStateType: deviceClass.stateTypes.findByName("power");
property var powerActionType: deviceClass.actionTypes.findByName("power");
property var powerState: device.states.getState(powerStateType.id)
property State connectedState: thing.stateByName("connected")
property State powerState: thing.stateByName("power")
Material.elevation: 1
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
contentItem: ItemDelegate {
id: contentItem
implicitHeight: nameRow.implicitHeight
topPadding: 0
contentItem: ColumnLayout {
spacing: 0
RowLayout {
enabled: itemDelegate.connectedState === null || itemDelegate.connectedState.value === true
id: nameRow
z: 2 // make sure the switch in here is on top of the slider, given we cheated a bit and made them overlap
contentItem: RowLayout {
spacing: app.margins
Item {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
ColorIcon {
id: icon
anchors.fill: parent
color: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false
? "red"
: itemDelegate.powerState.value === true ? app.accentColor : keyColor
name: itemDelegate.connectedState !== null && itemDelegate.connectedState.value === false ?
"../images/dialog-warning-symbolic.svg"
: app.interfaceToIcon("powersocket")
}
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: app.interfaceToIcon("powersocket")
color: itemDelegate.powerState.value === true ? app.accentColor : keyColor
}
Label {
Layout.fillWidth: true
text: model.name
text: itemDelegate.thing.name
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
ThingStatusIcons {
thing: itemDelegate.thing
}
Switch {
checked: itemDelegate.powerState.value === true
onClicked: {
var params = [];
var param1 = {};
param1["paramTypeId"] = itemDelegate.powerActionType.paramTypes.get(0).id;
param1["paramTypeId"] = itemDelegate.powerState.stateTypeId;
param1["value"] = checked;
params.push(param1)
engine.deviceManager.executeAction(device.id, itemDelegate.powerActionType.id, params)
engine.deviceManager.executeAction(itemDelegate.thing.id, itemDelegate.powerState.stateTypeId, params)
}
}
}
}
onClicked: {
enterPage(index)
}
}
}
}

View File

@ -43,154 +43,119 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
ListView {
Flickable {
anchors.fill: parent
model: root.thingsProxy
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Item {
id: itemDelegate
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
height: contentItem.implicitHeight + app.margins
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
property bool inline: width > 500
Repeater {
model: root.thingsProxy
property Thing thing: thingsProxy.getThing(model.id)
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
Pane {
id: contentItem
width: parent.width - app.margins
anchors.centerIn: parent
Material.elevation: 2
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ItemDelegate {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.mediumFont + app.margins
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
RowLayout {
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
}
BatteryStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
}
ConnectionStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: itemDelegate.thing.setupStatus == Thing.ThingSetupStatusComplete && (isWireless || !isConnected)
}
SetupStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: setupFailed || setupInProgress
}
}
}
GridLayout {
id: dataGrid
columns: Math.floor(contentItem.width / 120)
Layout.margins: app.margins
Repeater {
model: ListModel {
ListElement { interfaceName: "temperaturesensor"; stateName: "temperature" }
ListElement { interfaceName: "humiditysensor"; stateName: "humidity" }
ListElement { interfaceName: "moisturesensor"; stateName: "moisture" }
ListElement { interfaceName: "pressuresensor"; stateName: "pressure" }
ListElement { interfaceName: "lightsensor"; stateName: "lightIntensity" }
ListElement { interfaceName: "conductivitysensor"; stateName: "conductivity" }
ListElement { interfaceName: "noisesensor"; stateName: "noise" }
ListElement { interfaceName: "co2sensor"; stateName: "co2" }
ListElement { interfaceName: "daylightsensor"; stateName: "daylight" }
ListElement { interfaceName: "presencesensor"; stateName: "isPresent" }
ListElement { interfaceName: "closablesensor"; stateName: "closed" }
ListElement { interfaceName: "heating"; stateName: "power" }
ListElement { interfaceName: "thermostat"; stateName: "targetTemperature" }
}
delegate: RowLayout {
id: sensorValueDelegate
visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
Layout.preferredWidth: contentItem.width / dataGrid.columns
property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
ColorIcon {
Layout.preferredHeight: app.iconSize * .8
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
color: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? "green" : "red";
default:
return app.interfaceToColor(model.interfaceName)
}
}
name: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? Qt.resolvedUrl("../images/lock-closed.svg") : Qt.resolvedUrl("../images/lock-open.svg");
default:
return app.interfaceToIcon(model.interfaceName)
}
}
}
Label {
Layout.fillWidth: true
property var unit: sensorValueDelegate.stateType ? sensorValueDelegate.stateType.unit : Types.UnitNone
text: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("is closed") : qsTr("is open");
default:
return sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool"
? sensorValueDelegate.stateType.displayName
: sensorValueDelegate.stateValue
? "%1 %2".arg(Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, unit) * 100) / 100).arg(Types.toUiUnit(unit))
: ""
}
}
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.smallFont
}
Led {
id: led
visible: ["presencesensor", "daylightsensor", "heating"].indexOf(model.interfaceName) >= 0
state: visible && sensorValueDelegate.stateValue.value === true ? "on" : "off"
}
Item {
Layout.preferredWidth: led.width
visible: led.visible
}
}
}
}
}
onClicked: {
enterPage(index)
}
contentItem: GridLayout {
id: dataGrid
columns: Math.floor(contentItem.width / 120)
Repeater {
model: ListModel {
ListElement { interfaceName: "temperaturesensor"; stateName: "temperature" }
ListElement { interfaceName: "humiditysensor"; stateName: "humidity" }
ListElement { interfaceName: "moisturesensor"; stateName: "moisture" }
ListElement { interfaceName: "pressuresensor"; stateName: "pressure" }
ListElement { interfaceName: "lightsensor"; stateName: "lightIntensity" }
ListElement { interfaceName: "conductivitysensor"; stateName: "conductivity" }
ListElement { interfaceName: "noisesensor"; stateName: "noise" }
ListElement { interfaceName: "co2sensor"; stateName: "co2" }
ListElement { interfaceName: "daylightsensor"; stateName: "daylight" }
ListElement { interfaceName: "presencesensor"; stateName: "isPresent" }
ListElement { interfaceName: "closablesensor"; stateName: "closed" }
ListElement { interfaceName: "heating"; stateName: "power" }
ListElement { interfaceName: "thermostat"; stateName: "targetTemperature" }
}
delegate: RowLayout {
id: sensorValueDelegate
visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
Layout.preferredWidth: contentItem.width / dataGrid.columns
property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
color: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? "green" : "red";
default:
return app.interfaceToColor(model.interfaceName)
}
}
name: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? Qt.resolvedUrl("../images/lock-closed.svg") : Qt.resolvedUrl("../images/lock-open.svg");
default:
return app.interfaceToIcon(model.interfaceName)
}
}
}
Label {
Layout.fillWidth: true
property var unit: sensorValueDelegate.stateType ? sensorValueDelegate.stateType.unit : Types.UnitNone
text: {
switch (model.interfaceName) {
case "closablesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Closed") : qsTr("Open");
case "preencesensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Presence") : qsTr("Vacant");
case "daylightsensor":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("Daytime") : qsTr("Nighttime");
case "heating":
return sensorValueDelegate.stateValue && sensorValueDelegate.stateValue.value === true ? qsTr("On") : qsTr("Off");
default:
return sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool"
? sensorValueDelegate.stateType.displayName
: sensorValueDelegate.stateValue
? "%1 %2".arg(Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, unit) * 100) / 100).arg(Types.toUiUnit(unit))
: ""
}
}
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.smallFont
}
Led {
id: led
visible: sensorValueDelegate.stateType && sensorValueDelegate.stateType.type.toLowerCase() === "bool" && ["presencesensor", "daylightsensor", "heating", "closablesensor"].indexOf(model.interfaceName) < 0
state: visible && sensorValueDelegate.stateValue.value === true ? "on" : "off"
}
Item {
Layout.preferredWidth: led.width
visible: led.visible
}
}
}
}
}
}
}

View File

@ -43,124 +43,82 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
ListView {
Flickable {
anchors.fill: parent
model: root.devicesProxy
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Item {
id: itemDelegate
GridLayout {
id: contentGrid
width: parent.width - app.margins
anchors.horizontalCenter: parent.horizontalCenter
height: contentItem.height + app.margins
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
property bool inline: width > 500
Repeater {
model: root.thingsProxy
property Thing thing: thingsProxy.getThing(model.id)
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.getThing(model.id)
Pane {
id: contentItem
width: parent.width - app.margins
anchors.centerIn: parent
Material.elevation: 2
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ItemDelegate {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.mediumFont + app.margins
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
RowLayout {
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
}
BatteryStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (isCritical || hasBatteryLevel)
}
ConnectionStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (isWireless || !isConnected)
}
SetupStatusIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
thing: itemDelegate.thing
visible: setupFailed || setupInProgress
}
}
}
GridLayout {
id: dataGrid
columns: Math.floor(contentItem.width / 120)
Layout.margins: app.margins
Repeater {
model: ListModel {
Component.onCompleted: {
if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterproducer") >= 0) {
append( {interfaceName: "smartmeterproducer", stateName: "totalEnergyProduced" })
}
if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
append( {interfaceName: "smartmeterconsumer", stateName: "totalEnergyConsumed" })
}
var added = false;
if (itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0) {
append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
added = true;
}
if (!added && itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) {
append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
}
}
}
delegate: RowLayout {
id: sensorValueDelegate
Layout.preferredWidth: contentItem.width / dataGrid.columns
property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
ColorIcon {
Layout.preferredHeight: app.iconSize * .8
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
color: app.interfaceToColor(model.interfaceName)
name: app.interfaceToIcon(model.interfaceName)
}
Label {
Layout.fillWidth: true
text: sensorValueDelegate.stateValue
? "%1 %2".arg(1.0 * Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, sensorValueDelegate.stateType.unit) * 100000) / 100000).arg(Types.toUiUnit(sensorValueDelegate.stateType.unit))
: ""
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.smallFont
}
}
}
}
}
onClicked: {
enterPage(index)
}
contentItem: GridLayout {
id: dataGrid
columns: Math.floor(contentItem.width / 120)
Repeater {
model: ListModel {
Component.onCompleted: {
if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterproducer") >= 0) {
append( {interfaceName: "smartmeterproducer", stateName: "totalEnergyProduced" })
}
if (itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
append( {interfaceName: "smartmeterconsumer", stateName: "totalEnergyConsumed" })
}
var added = false;
if (itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0) {
append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
added = true;
}
if (!added && itemDelegate.thing.thingClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) {
append({interfaceName: "extendedsmartmeterconsumer", stateName: "currentPower"});
}
}
}
delegate: RowLayout {
id: sensorValueDelegate
visible: itemDelegate.thing.thingClass.interfaces.indexOf(model.interfaceName) >= 0
Layout.preferredWidth: contentItem.width / dataGrid.columns
property StateType stateType: itemDelegate.thing.thingClass.stateTypes.findByName(model.stateName)
property State stateValue: stateType ? itemDelegate.thing.states.getState(stateType.id) : null
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
Layout.alignment: Qt.AlignVCenter
color: app.interfaceToColor(model.interfaceName)
name: app.interfaceToIcon(model.interfaceName)
}
Label {
Layout.fillWidth: true
text: sensorValueDelegate.stateValue
? "%1 %2".arg(1.0 * Math.round(Types.toUiValue(sensorValueDelegate.stateValue.value, sensorValueDelegate.stateType.unit) * 100000) / 100000).arg(Types.toUiUnit(sensorValueDelegate.stateType.unit))
: ""
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
font.pixelSize: app.smallFont
}
}
}
}
}
}
}

View File

@ -44,69 +44,31 @@ DeviceListPageBase {
onBackPressed: pageStack.pop()
}
ListView {
Flickable {
anchors.fill: parent
model: root.devicesProxy
contentHeight: contentGrid.implicitHeight
topMargin: app.margins / 2
delegate: Item {
id: itemDelegate
GridLayout {
id: contentGrid
width: parent.width - app.margins
height: contentItem.height + app.margins
anchors.horizontalCenter: parent.horizontalCenter
columns: Math.ceil(width / 600)
rowSpacing: 0
columnSpacing: 0
property bool inline: width > 500
Repeater {
model: root.thingsProxy
property Device device: devicesProxy.getDevice(model.id)
property DeviceClass deviceClass: device.deviceClass
delegate: BigTile {
id: itemDelegate
Layout.preferredWidth: contentGrid.width / contentGrid.columns
thing: root.thingsProxy.get(model.id)
Pane {
id: contentItem
width: parent.width - app.margins
anchors.centerIn: parent
Material.elevation: 2
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
onClicked: enterPage(index)
contentItem: ItemDelegate {
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
contentItem: ColumnLayout {
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: app.mediumFont + app.margins
color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
RowLayout {
anchors { verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right; margins: app.margins }
Label {
Layout.fillWidth: true
text: model.name
elide: Text.ElideRight
}
ColorIcon {
Layout.preferredHeight: app.iconSize * .5
Layout.preferredWidth: height
name: "../images/dialog-warning-symbolic.svg"
visible: itemDelegate.deviceClass.interfaces.indexOf("connectable") >= 0 && itemDelegate.device.states.getState(itemDelegate.deviceClass.stateTypes.findByName("connected").id).value === false
color: "red"
}
}
}
WeatherView {
Layout.fillWidth: true
device: itemDelegate.device
deviceClass: itemDelegate.deviceClass
}
}
onClicked: {
var device = devicesProxy.get(index);
var deviceClass = engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
pageStack.push(Qt.resolvedUrl("../devicepages/WeatherDevicePage.qml"), {device: devicesProxy.get(index)})
contentItem: WeatherView {
thing: itemDelegate.thing
}
}
}

View File

@ -242,86 +242,10 @@ Page {
}
}
Rectangle {
ThingInfoPane {
id: infoPane
visible: setupInProgress || setupFailure || batteryState !== null || (connectedState !== null && connectedState.value === false) || isWireless || updateAvailable
height: visible ? contentRow.implicitHeight : 0
anchors { left: parent.left; top: parent.top; right: parent.right }
property bool setupInProgress: root.thing.setupStatus == Thing.ThingSetupStatusInProgress
property bool setupFailure: root.thing.setupStatus == Thing.ThingSetupStatusFailed
property State batteryState: root.thing.stateByName("batteryLevel")
property State batteryCriticalState: root.thing.stateByName("batteryCritical")
property State connectedState: root.thing.stateByName("connected")
property State signalStrengthState: root.thing.stateByName("signalStrength")
property State updateStatusState: root.thing.stateByName("updateStatus")
property bool updateAvailable: updateStatusState && updateStatusState.value === "available"
property bool updateRunning: updateStatusState && updateStatusState.value === "updating"
property bool isWireless: root.thingClass.interfaces.indexOf("wirelessconnectable") >= 0
property bool alertState: setupFailure ||
(connectedState !== null && connectedState.value === false) ||
(batteryCriticalState !== null && batteryCriticalState.value === true)
property bool highlightState: updateAvailable || updateRunning
color: alertState ? "red"
: infoPane.highlightState ? app.accentColor : "transparent"
z: 1000
RowLayout {
id: contentRow
anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: app.margins; rightMargin: app.margins }
Item {
Layout.fillWidth: true
height: app.iconSize
}
Label {
text: infoPane.setupInProgress ?
qsTr("Thing is being set up...")
: infoPane.setupFailure ?
(root.device.setupDisplayMessage.length > 0 ? root.device.setupDisplayMessage : qsTr("Thing setup failed!"))
: (infoPane.connectedState !== null && infoPane.connectedState.value === false) ?
qsTr("Thing is not connected!")
: infoPane.updateAvailable ?
qsTr("Update available!")
: infoPane.updateRunning ?
qsTr("Updating...")
: qsTr("Thing runs out of battery!")
visible: infoPane.alertState || infoPane.updateAvailable || infoPane.updateRunning
font.pixelSize: app.smallFont
color: "white"
}
UpdateStatusIcon {
height: app.iconSize / 2
width: height
thing: root.thing
color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
visible: updateAvailable || updateRunning
}
BatteryStatusIcon {
height: app.iconSize / 2
width: height * 1.23
thing: root.thing
color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (hasBatteryLevel || isCritical)
}
ConnectionStatusIcon {
height: app.iconSize / 2
width: height
thing: root.thing
color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
visible: thing.setupStatus == Thing.ThingSetupStatusComplete && (hasSignalStrength || !isConnected)
}
SetupStatusIcon {
height: app.iconSize / 2
width: height
thing: root.thing
color: infoPane.alertState || infoPane.highlightState ? "white" : keyColor
visible: setupFailed || setupInProgress
}
}
thing: root.thing
}
Item {

View File

@ -47,7 +47,7 @@ DevicePageBase {
GridLayout {
id: contentGrid
width: parent.width
columns: width / 300
columns: Math.ceil(width / 600)
Repeater {
model: ListModel {

View File

@ -49,8 +49,7 @@ DevicePageBase {
WeatherView {
Layout.fillWidth: true
device: root.device
deviceClass: root.deviceClass
thing: root.thing
}
GridLayout {

View File

@ -65,203 +65,6 @@ MainViewBase {
onClicked: {
pageStack.push(Qt.resolvedUrl("../grouping/GroupInterfacesPage.qml"), {groupTag: model.tagId})
}
// delegate: Item {
// id: groupDelegate
// width: groupsGridView.cellWidth
// height: groupsGridView.cellHeight
// Pane {
// anchors.fill: parent
// anchors.margins: app.margins / 2
// Material.elevation: 2
// padding: 0
// DevicesProxy {
// id: devicesInGroup
// engine: _engine
// filterTagId: model.tagId
// filterTagValue: model.value
// }
// contentItem: ColumnLayout {
// ColorIcon {
// name: "../images/view-grid-symbolic.svg"
// }
// Label {
// text: model
// }
// }
// InterfacesProxy {
// id: controlsInGroup
// shownInterfaces: ["light", "simpleclosable", "mediacontroller", "powersocket"]
// devicesProxyFilter: devicesInGroup
// showStates: true
// showActions: true
// }
// InterfacesProxy {
// id: sensorsInGroup
// shownInterfaces: ["temperaturesensor", "lightsensor", "presencesensor"]
// devicesProxyFilter: devicesInGroup
// showStates: true
// }
// contentItem: ItemDelegate {
// leftPadding: 0
// topPadding: 0
// rightPadding: 0
// bottomPadding: 0
// onClicked: {
// pageStack.push(Qt.resolvedUrl("../grouping/GroupInterfacesPage.qml"), {groupTag: model.tagId})
// }
// contentItem: ColumnLayout {
// Rectangle {
// Layout.fillWidth: true
// Layout.preferredHeight: 30
// color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, .05)
// Label {
// anchors.fill: parent
// verticalAlignment: Text.AlignVCenter
// anchors { leftMargin: app.margins; rightMargin: app.margins }
// text: model.tagId.substring(6)
// elide: Text.ElideRight
// }
// }
// Item {
// Layout.fillHeight: true
// Layout.fillWidth: true
// ColorIcon {
// anchors.centerIn: parent
// height: app.iconSize * 2
// width: height
// visible: controlsInGroup.count == 0
// color: app.accentColor
// name: "../images/view-grid-symbolic.svg"
// }
// ColumnLayout {
// anchors.fill: parent
// Repeater {
//// model: Math.min(controlsInGroup.count, parent.height / 50)
// model: controlsInGroup.count
// delegate: Loader {
// id: controlLoader
// Layout.fillWidth: true
// Layout.leftMargin: app.margins / 2
// Layout.rightMargin: app.margins / 2
// property string interfaceName: controlsInGroup.get(index).name
// sourceComponent: {
// switch (interfaceName) {
// case "simpleclosable":
// return closableDelegate
// case "light":
// return lightDelegate
// case "mediacontroller":
// return mediaControllerDelegate
// case "powersocket":
// return powerSocketDelegate
// }
// }
// Binding {
// target: controlLoader.item
// property: "devices"
// value: devicesInGroup
// }
// }
// }
// Item {
// Layout.fillHeight: true
// Layout.fillWidth: true
// }
// }
// }
// Rectangle {
// Layout.fillWidth: true
// Layout.preferredHeight: app.iconSize * 1.2
// color: Qt.rgba(app.foregroundColor.r, app.foregroundColor.g, app.foregroundColor.b, 0.05)
// RowLayout {
// anchors.fill: parent
// Repeater {
// model: sensorsInGroup
// delegate: Row {
// height: parent.height
// ColorIcon {
// height: app.iconSize * .8
// width: height
// name: app.interfaceToIcon(model.name)
// color: app.interfaceToColor(model.name)
// }
// DevicesProxy {
// id: innerProxy
// engine: _engine
// parentProxy: devicesInGroup
// shownInterfaces: [model.name]
// }
// Led {
// visible: ["presencesensor"].indexOf(model.name) >= 0
// state: {
// var stateName = null
// switch (model.name) {
// case "presencesensor":
// stateName = "isPresent"
// break;
// }
// if (!stateName) {
// return "off";
// }
// var ret = false;
// for (var i = 0; i < innerProxy.count; i++) {
// ret |= innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
// }
// return ret ? "on" : "off";
// }
// }
// Label {
// height: parent.height
// verticalAlignment: Text.AlignVCenter
// text: {
// var stateName = null;
// switch (model.name) {
// case "temperaturesensor":
// stateName = "temperature";
// break;
// case "lightsensor":
// stateName = "lightIntensity"
// break;
// }
// if (!stateName) {
// return "";
// }
// var ret = 0
// for (var i = 0; i < innerProxy.count; i++) {
// ret += innerProxy.get(i).states.getState(innerProxy.get(i).deviceClass.stateTypes.findByName(stateName).id).value
// }
// return (ret / innerProxy.count).toFixed(1)
// }
// }
// }
// }
// }
// }
// }
// }
// }
}
}

View File

@ -37,7 +37,8 @@ import "../delegates"
SettingsPageBase {
id: root
property Device device: null
property Thing thing: null
property alias device: root.thing
readonly property DeviceClass deviceClass: device ? device.deviceClass : null
header: NymeaHeader {
@ -50,6 +51,12 @@ SettingsPageBase {
}
}
ThingInfoPane {
id: infoPane
anchors { left: parent.left; top: parent.top; right: parent.right }
thing: root.thing
}
Menu {
id: deviceMenu
width: implicitWidth + app.margins

View File

@ -65,6 +65,18 @@ Item {
}
function isDark(color) {
return ((color.r() * 299 + color.g() * 587 + color.b() * 114) / 1000) > 123
var r, g, b;
if (color.constructor.name === "Object") {
r = color.r * 255;
g = color.g * 255;
b = color.b * 255;
} else if (color.constructor.name === "String") {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
r = parseInt(result[1], 16)
g = parseInt(result[2], 16)
b = parseInt(result[3], 16)
}
return ((r * 299 + g * 587 + b * 114) / 1000) < 128
}
}