This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/system/zwave/ZWaveNetworkPage.qml
2022-08-27 13:55:35 +02:00

625 lines
23 KiB
QML

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, 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.8
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.3
import "qrc:/ui/components"
import Nymea 1.0
SettingsPageBase {
id: root
property ZWaveManager zwaveManager: null
property ZWaveNetwork network: null
header: NymeaHeader {
text: qsTr("Z-Wave network")
backButtonVisible: true
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "/ui/images/help.svg"
text: qsTr("Help")
onClicked: {
var popup = zigbeeHelpDialog.createObject(app)
popup.open()
}
}
HeaderButton {
imageSource: "/ui/images/configure.svg"
text: qsTr("Network settings")
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("ZWaveNetworkSettingsPage.qml"), { zwaveManager: zwaveManager, network: network })
page.exit.connect(function() {
pageStack.pop(root, StackView.Immediate)
pageStack.pop()
})
}
}
}
busy: d.pendingCommandId != -1
QtObject {
id: d
property var addRemoveNodeDialog: null
property int pendingCommandId: -1
function removeFailedNode(networkUuid, nodeId) {
d.pendingCommandId = root.zwaveManager.removeFailedNode(networkUuid, nodeId)
}
}
Connections {
target: root.zwaveManager
onAddNodeReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
processStatusCode(error)
}
}
onRemoveNodeReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
processStatusCode(error)
}
}
onRemoveFailedNodeReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
processStatusCode(error)
}
}
function processStatusCode(error) {
var props = {};
switch (error) {
case ZWaveManager.ZWaveErrorNoError:
return;
case ZWaveManager.ZWaveErrorBackendError:
props.text = qsTr("Un unexpected error happened in the Z-Wave backend.");
break;
case ZWaveManager.ZWaveErrorInUse:
props.text = qsTr("The operation could not be started because the Z-Wave network is busy. Please try again later.");
break;
default:
props.text = qsTr("An unexpected error happened. Status code: %1").arg(error);
props.errorCode = error;
}
var comp = Qt.createComponent("/ui/components/ErrorDialog.qml")
var popup = comp.createObject(app, props)
popup.open();
}
}
Connections {
target: root.network
onWaitingForNodeAdditionChanged: {
if (root.network.waitingForNodeAddition) {
var props = {
title: qsTr("Include Z-Wave device"),
text: qsTr("The Z-Wave network is now accepting new devices for inclusion. Please start the pairing procedure from the Z-Wave device you want to add to the network. Check the device manual for further details.")
}
d.addRemoveNodeDialog = addRemoveNodeDialogComponent.createObject(app, props)
d.addRemoveNodeDialog.open();
} else {
if (d.addRemoveNodeDialog) {
d.addRemoveNodeDialog.close()
}
}
}
onWaitingForNodeRemovalChanged: {
if (root.network.waitingForNodeRemoval) {
var props = {
title: qsTr("Exclude Z-Wave device"),
text: qsTr("The Z-Wave network is now accepting devices for exclusion. Please start the pairing procedure from the Z-Wave device you want to remove from the network. Check the device manual for further details.")
}
d.addRemoveNodeDialog = addRemoveNodeDialogComponent.createObject(app, props)
d.addRemoveNodeDialog.open();
} else {
if (d.addRemoveNodeDialog) {
d.addRemoveNodeDialog.close()
}
}
}
}
SettingsPageSectionHeader {
text: qsTr("Network")
}
ColumnLayout {
spacing: Style.margins
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: qsTr("Network state:")
}
Label {
text: {
switch (network.networkState) {
case ZWaveNetwork.ZWaveNetworkStateOnline:
return qsTr("Online")
case ZWaveNetwork.ZWaveNetworkStateOffline:
return qsTr("Offline")
case ZWaveNetwork.ZWaveNetworkStateStarting:
return qsTr("Starting")
case ZWaveNetwork.ZWaveNetworkStateError:
return qsTr("Error")
}
}
}
Led {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
state: {
switch (network.networkState) {
case ZWaveNetwork.ZWaveNetworkStateOnline:
return "on"
case ZWaveNetwork.ZWaveNetworkStateOffline:
return "off"
case ZWaveNetwork.ZWaveNetworkStateStarting:
return "orange"
case ZWaveNetwork.ZWaveNetworkStateError:
return "red"
}
}
}
}
}
SettingsPageSectionHeader {
text: qsTr("Device management")
visible: network.isPrimaryController
}
ColumnLayout {
spacing: Style.margins
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
visible: network.isPrimaryController
Button {
Layout.fillWidth: true
text: qsTr("Add a new device")
enabled: network.networkState === ZWaveNetwork.ZWaveNetworkStateOnline
onClicked: {
zwaveManager.cancelPendingOperation(network.networkUuid)
d.pendingCommandId = zwaveManager.addNode(network.networkUuid)
}
}
Button {
Layout.fillWidth: true
text: qsTr("Remove a device")
enabled: network.networkState === ZWaveNetwork.ZWaveNetworkStateOnline
onClicked: {
zwaveManager.cancelPendingOperation(network.networkUuid)
d.pendingCommandId = zwaveManager.removeNode(network.networkUuid)
}
}
}
SettingsPageSectionHeader {
text: qsTr("Connected devices")
}
Label {
Layout.fillWidth: true
Layout.margins: Style.margins
horizontalAlignment: Text.AlignHCenter
text: qsTr("There are no Z-Wave devices connected yet. Open the network for new devices to join and start the pairing procedure from the Z-Wave device. Please refer to the devices manual for more information on how to start the pairing.")
wrapMode: Text.WordWrap
visible: nodesModel.count === 0
}
Repeater {
model: ZWaveNodesProxy {
id: nodesModel
zwaveNodes: root.network.nodes
showController: false
newOnTop: true
}
delegate: NymeaSwipeDelegate {
id: nodeDelegate
readonly property ZWaveNode node: nodesModel.get(index)
ThingsProxy {
id: nodeThings
engine: _engine
paramsFilter: {"networkUuid": nodeDelegate.node.networkUuid, "nodeId": nodeDelegate.node.nodeId}
}
readonly property Thing nodeThing: nodeThings.count >= 1 ? nodeThings.get(0) : null
Layout.fillWidth: true
text: node.productName + " - " + node.manufacturerName// nodeThing ? nodeThing.name : node.model
subText: node.state == ZigbeeNode.ZigbeeNodeStateInitializing ?
qsTr("Initializing...")
: nodeThings.count == 1 ? nodeThing.name :
nodeThings.count > 1 ? qsTr("%1 things").arg(nodeThings.count)
: node.nodeType == ZWaveNode.ZWaveNodeTypeStaticController || node.nodeType == ZWaveNode.ZWaveNodeTypeController
? qsTr("Network controller") : qsTr("Unrecognized device")
iconName: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/z-wave.svg"
iconColor: busy
? Style.tileOverlayColor
: node.nodeType == ZWaveNode.ZWaveNodeTypeController || node.nodeType == ZWaveNode.ZWaveNodeTypeStaticController
? Style.green
: node.failed
? Style.red
: nodeThing != null
? Style.accentColor
: Style.iconColor
progressive: false
busy: !node.initialized
canDelete: node.failed
// canDelete: true
onDeleteClicked: {
var dialog = removeZWaveNodeDialogComponent.createObject(app, {zwaveNode: node})
dialog.open()
}
secondaryIconName: node && node.sleeping ? "/ui/images/system-suspend.svg" : ""
tertiaryIconName: {
var signalStrength = "00";
if (!node || !node.reachable) {
signalStrength = "00";
} else if (node.linkQuality <= 25) {
signalStrength = "25"
} else if (node.linkQuality <= 50) {
signalStrength = "50"
} else if (node.linkQuality <= 75) {
signalStrength = "75"
} else {
signalStrength = "100"
}
return "/ui/images/connections/nm-signal-" + signalStrength + (node.isSecure ? "-secure" : "") +".svg"
}
tertiaryIconColor: node.reachable ? Style.iconColor : Style.red
Timer {
id: communicationIndicatorLedTimer
interval: 200
}
additionalItem: ColorIcon {
size: Style.smallIconSize
anchors.verticalCenter: parent.verticalCenter
name: {
print("node type:", node.nodeType)
switch (node.nodeType) {
case ZWaveNode.ZWaveNodeTypeController:
case ZWaveNode.ZWaveNodeTypeStaticController:
return "/ui/images/z-wave.svg"
case ZWaveNode.ZWaveNodeTypeRoutingSlave:
return "/ui/images/zigbee-router.svg"
case ZWaveNode.ZWaveNodeTypeSlave:
return "/ui/images/zigbee-enddevice.svg"
}
}
color: communicationIndicatorLedTimer.running ? Style.accentColor : Style.iconColor
}
onClicked: {
var popup = nodeInfoComponent.createObject(app, {node: node, nodeThings: nodeThings})
popup.open()
}
}
}
Component {
id: nodeInfoComponent
MeaDialog {
id: nodeInfoDialog
property ZWaveNode node: null
property ThingsProxy nodeThings: null
readonly property Thing nodeThing: nodeThings.count > 0 ? nodeThings.get(0) : null
header: Item {
implicitHeight: headerRow.height
implicitWidth: parent.width
RowLayout {
id: headerRow
anchors { left: parent.left; right: parent.right; top: parent.top; margins: Style.margins }
ColorIcon {
id: headerColorIcon
Layout.preferredHeight: Style.bigIconSize
Layout.preferredWidth: Style.bigIconSize * 1.5
color: Style.accentColor
name: nodeInfoDialog.node.isZWavePlus ? "/ui/images/zwave/z-wave-plus-wide.svg" : "/ui/images/zwave/z-wave-wide.svg"
}
ColumnLayout {
Layout.margins: Style.margins
Label {
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: nodeInfoDialog.node.productName
}
Label {
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: nodeInfoDialog.node.manufacturerName
}
}
}
}
standardButtons: Dialog.NoButton
GridLayout {
columns: 2
Label {
text: qsTr("Node ID:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.nodeId
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Device type:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.deviceTypeString
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Node type:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.nodeTypeString
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Role:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.roleString
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Security mode:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.securityMode
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Z-Wave plus:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.isZWavePlusDevice ? qsTr("Yes") : qsTr("No")
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Security device:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.isSecurityDevice ? qsTr("Yes") : qsTr("No")
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Beaming supported:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.isBeamingDevice ? qsTr("Yes") : qsTr("No")
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Signal strength:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.linkQuality + " %"
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
Label {
text: qsTr("Version:")
font: Style.smallFont
}
Label {
Layout.fillWidth: true
text: nodeInfoDialog.node.version
font: Style.smallFont
horizontalAlignment: Text.AlignRight
}
}
SettingsPageSectionHeader {
text: qsTr("Associated things")
Layout.leftMargin: 0
Layout.rightMargin: 0
}
Repeater {
model: nodeInfoDialog.nodeThings
delegate: RowLayout {
id: thingDelegate
property Thing thing: nodeInfoDialog.nodeThings.get(index)
Layout.fillWidth: true
ColorIcon {
size: Style.iconSize
source: app.interfacesToIcon(thing.thingClass.interfaces)
color: Style.accentColor
}
TextField {
text: thingDelegate.thing.name
Layout.fillWidth: true
onEditingFinished: engine.thingManager.editThing(thingDelegate.thing.id, text)
}
}
}
RowLayout {
Layout.fillWidth: true
Button {
visible: node && node.nodeType !== ZWaveNode.ZWaveNodeTypeController && node.failed
text: qsTr("Remove")
Layout.alignment: Qt.AlignLeft
onClicked: {
var dialog = removeZWaveNodeDialogComponent.createObject(app, {zwaveNode: node})
dialog.open()
nodeInfoDialog.close()
}
}
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("OK")
onClicked: nodeInfoDialog.close()
Layout.alignment: Qt.AlignRight
}
}
}
}
Component {
id: removeZWaveNodeDialogComponent
MeaDialog {
id: removeZWaveNodeDialog
property ZWaveNode zwaveNode
headerIcon: "/ui/images/zigbee.svg"
title: qsTr("Remove Z-Wave node") + " " + (zwaveNode ? zwaveNode.name != "" ? zwaveNode.name : zwaveNode.productName : "")
text: qsTr("Are you sure you want to remove this node from the network?")
standardButtons: Dialog.Ok | Dialog.Cancel
Label {
text: qsTr("Please note that if this node has been assigned to a thing, it will also be removed from the system.")
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
onAccepted: {
zwaveManager.cancelPendingOperation(network.networkUuid)
d.removeFailedNode(zwaveNode.networkUuid, zwaveNode.nodeId)
}
}
}
Component {
id: zigbeeHelpDialog
MeaDialog {
id: dialog
title: qsTr("Z-Wave network help")
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-router.svg"
}
Label {
text: qsTr("Z-Wave router")
}
}
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-enddevice.svg"
}
Label {
text: qsTr("Z-Wave end device")
}
}
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/system-suspend.svg"
}
Label {
text: qsTr("Sleepy device")
}
}
}
}
Component {
id: addRemoveNodeDialogComponent
MeaDialog {
standardButtons: Dialog.Cancel
onRejected: {
zwaveManager.cancelPendingOperation(network.networkUuid)
}
}
}
}