Add support for selecting the channel on zigbee network creation

This commit is contained in:
Michael Zanetti 2021-10-21 23:14:38 +02:00
parent a0be4365d4
commit 2a16081e99
13 changed files with 1123 additions and 288 deletions

View File

@ -391,6 +391,22 @@ void ThingsProxy::setFilterUpdates(bool filterUpdates)
}
}
QVariantMap ThingsProxy::paramsFilter() const
{
return m_paramsFilter;
}
void ThingsProxy::setParamsFilter(const QVariantMap &paramsFilter)
{
if (m_paramsFilter != paramsFilter) {
m_paramsFilter = paramsFilter;
emit paramsFilterChanged();
invalidateFilter();
emit countChanged();
}
}
bool ThingsProxy::groupByInterface() const
{
return m_groupByInterface;
@ -590,5 +606,14 @@ bool ThingsProxy::filterAcceptsRow(int source_row, const QModelIndex &source_par
}
}
if (!m_paramsFilter.isEmpty()) {
foreach (const QString &paramName, m_paramsFilter.keys()) {
Param *param = thing->paramByName(paramName);
if (!param || param->value() != m_paramsFilter.value(paramName)) {
return false;
}
}
}
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}

View File

@ -75,6 +75,9 @@ class ThingsProxy : public QSortFilterProxyModel
Q_PROPERTY(bool filterUpdates READ filterUpdates WRITE setFilterUpdates NOTIFY filterUpdatesChanged)
// A map of paramName:value pairs, all given need to match
Q_PROPERTY(QVariantMap paramsFilter READ paramsFilter WRITE setParamsFilter NOTIFY paramsFilterChanged)
Q_PROPERTY(bool groupByInterface READ groupByInterface WRITE setGroupByInterface NOTIFY groupByInterfaceChanged)
public:
@ -143,6 +146,9 @@ public:
bool filterUpdates() const;
void setFilterUpdates(bool filterUpdates);
QVariantMap paramsFilter() const;
void setParamsFilter(const QVariantMap &paramsFilter);
bool groupByInterface() const;
void setGroupByInterface(bool groupByInterface);
@ -172,6 +178,7 @@ signals:
void filterDisconnectedChanged();
void filterSetupFailedChanged();
void filterUpdatesChanged();
void paramsFilterChanged();
void groupByInterfaceChanged();
void countChanged();
@ -203,6 +210,8 @@ private:
bool m_filterSetupFailed = false;
bool m_filterUpdates = false;
QVariantMap m_paramsFilter;
bool m_groupByInterface = false;
protected:

View File

@ -93,12 +93,16 @@ ZigbeeNetworks *ZigbeeManager::networks() const
return m_networks;
}
int ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, const QString &backend)
int ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, const QString &backend, ZigbeeChannels channels)
{
QVariantMap params;
params.insert("serialPort", serialPort);
params.insert("baudRate", baudRate);
params.insert("backend", backend);
qWarning() << "************ channel mask!" << channels;
if (m_engine->jsonRpcClient()->ensureServerVersion("5.8")) {
params.insert("channelMask", static_cast<uint>(channels));
}
qCDebug(dcZigbee()) << "Add zigbee network" << params;
return m_engine->jsonRpcClient()->sendCommand("Zigbee.AddNetwork", params, this, "addNetworkResponse");

View File

@ -52,6 +52,29 @@ class ZigbeeManager : public QObject
Q_PROPERTY(ZigbeeNetworks *networks READ networks CONSTANT)
public:
enum ZigbeeChannel {
ZigbeeChannel11 = 0x00000800,
ZigbeeChannel12 = 0x00001000,
ZigbeeChannel13 = 0x00002000,
ZigbeeChannel14 = 0x00004000,
ZigbeeChannel15 = 0x00008000,
ZigbeeChannel16 = 0x00010000,
ZigbeeChannel17 = 0x00020000,
ZigbeeChannel18 = 0x00040000,
ZigbeeChannel19 = 0x00080000,
ZigbeeChannel20 = 0x00100000,
ZigbeeChannel21 = 0x00200000,
ZigbeeChannel22 = 0x00400000,
ZigbeeChannel23 = 0x00800000,
ZigbeeChannel24 = 0x01000000,
ZigbeeChannel25 = 0x02000000,
ZigbeeChannel26 = 0x04000000,
ZigbeeChannelPrimaryLightLink = 0x02108800,
ZigbeeChannelAll = 0x07fff800
};
Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel)
Q_FLAG(ZigbeeChannels)
explicit ZigbeeManager(QObject *parent = nullptr);
~ZigbeeManager();
@ -63,7 +86,7 @@ public:
ZigbeeNetworks *networks() const;
// Network
Q_INVOKABLE int addNetwork(const QString &serialPort, uint baudRate, const QString &backend);
Q_INVOKABLE int addNetwork(const QString &serialPort, uint baudRate, const QString &backend, ZigbeeChannels channels);
Q_INVOKABLE void removeNetwork(const QUuid &networkUuid);
Q_INVOKABLE void setPermitJoin(const QUuid &networkUuid, uint duration = 120);
Q_INVOKABLE void factoryResetNetwork(const QUuid &networkUuid);

View File

@ -37,7 +37,7 @@
ZigbeeNodesProxy::ZigbeeNodesProxy(QObject *parent) :
QSortFilterProxyModel(parent)
{
sort(0);
}
ZigbeeNodes *ZigbeeNodesProxy::zigbeeNodes() const
@ -59,6 +59,12 @@ void ZigbeeNodesProxy::setZigbeeNodes(ZigbeeNodes *zigbeeNodes)
sort(0, Qt::AscendingOrder);
emit countChanged();
});
connect(m_zigbeeNodes, &ZigbeeNodes::rowsInserted, this, [this](const QModelIndex &parent, int first, int last){
Q_UNUSED(parent)
for (int i = first; i <= last; i++) {
m_newNodes.insert(m_zigbeeNodes->get(i), QDateTime::currentDateTime());
}
});
setSourceModel(m_zigbeeNodes);
@ -69,6 +75,36 @@ void ZigbeeNodesProxy::setZigbeeNodes(ZigbeeNodes *zigbeeNodes)
emit countChanged();
}
bool ZigbeeNodesProxy::showCoordinator() const
{
return m_showCoordinator;
}
void ZigbeeNodesProxy::setShowCoordinator(bool showCoordinator)
{
if (m_showCoordinator != showCoordinator) {
m_showCoordinator = showCoordinator;
emit showCoordinatorChanged();
invalidateFilter();
emit countChanged();
}
}
bool ZigbeeNodesProxy::newOnTop() const
{
return m_newOnTop;
}
void ZigbeeNodesProxy::setNewOnTop(bool newOnTop)
{
if (m_newOnTop != newOnTop) {
m_newOnTop = newOnTop;
emit newOnTopChanged();
invalidate();
}
}
ZigbeeNode *ZigbeeNodesProxy::get(int index) const
{
if (index >= 0 && index < m_zigbeeNodes->rowCount()) {
@ -76,3 +112,31 @@ ZigbeeNode *ZigbeeNodesProxy::get(int index) const
}
return nullptr;
}
bool ZigbeeNodesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
ZigbeeNode *node = m_zigbeeNodes->get(source_row);
if (!m_showCoordinator && node->type() == ZigbeeNode::ZigbeeNodeTypeCoordinator) {
return false;
}
return true;
}
bool ZigbeeNodesProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (m_newOnTop) {
ZigbeeNode *left = m_zigbeeNodes->get(source_left.row());
ZigbeeNode *right = m_zigbeeNodes->get(source_right.row());
if (m_newNodes.contains(left) && !m_newNodes.contains(right)) {
return true;
}
if (!m_newNodes.contains(left) && !m_newNodes.contains(right)) {
return false;
}
if (m_newNodes.contains(left) && m_newNodes.contains(right)) {
return m_newNodes.value(left) > m_newNodes.value(right);
}
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}

View File

@ -34,7 +34,7 @@
#include <QObject>
#include <QSortFilterProxyModel>
class ZigbeeNode;
#include "zigbeenode.h"
class ZigbeeNodes;
class ZigbeeNodesProxy : public QSortFilterProxyModel
@ -42,6 +42,9 @@ class ZigbeeNodesProxy : public QSortFilterProxyModel
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
Q_PROPERTY(ZigbeeNodes *zigbeeNodes READ zigbeeNodes WRITE setZigbeeNodes NOTIFY zigbeeNodesChanged)
Q_PROPERTY(bool showCoordinator READ showCoordinator WRITE setShowCoordinator NOTIFY showCoordinatorChanged)
Q_PROPERTY(bool newOnTop READ newOnTop WRITE setNewOnTop NOTIFY newOnTopChanged)
public:
explicit ZigbeeNodesProxy(QObject *parent = nullptr);
@ -49,15 +52,32 @@ public:
ZigbeeNodes *zigbeeNodes() const;
void setZigbeeNodes(ZigbeeNodes *zigbeeNodes);
bool showCoordinator() const;
void setShowCoordinator(bool showCoordinator);
bool newOnTop() const;
void setNewOnTop(bool newOnTop);
Q_INVOKABLE ZigbeeNode *get(int index) const;
signals:
void countChanged();
void zigbeeNodesChanged(ZigbeeNodes *zigbeeNodes);
void showCoordinatorChanged();
void newOnTopChanged();
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
ZigbeeNodes *m_zigbeeNodes = nullptr;
bool m_showCoordinator = true;
bool m_newOnTop = false;
QHash<ZigbeeNode*, QDateTime> m_newNodes;
};
#endif // ZIGBEENODESPROXY_H

View File

@ -120,6 +120,8 @@ SwipeDelegate {
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
width: Style.bigIconSize
height: width
visible: running
running: false
}

View File

@ -38,45 +38,10 @@ import "../components"
SettingsPageBase {
id: root
title: qsTr("Add a new ZigBee network")
busy: d.pendingCommandId != -1
property ZigbeeManager zigbeeManager: null
QtObject {
id: d
property int pendingCommandId: -1
function addNetwork(serialPort, baudRate, backend) {
d.pendingCommandId = root.zigbeeManager.addNetwork(serialPort, baudRate, backend)
}
}
Connections {
target: root.zigbeeManager
onAddNetworkReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
var props = {};
switch (error) {
case "ZigbeeErrorNoError":
pageStack.pop();
return;
case "ZigbeeErrorAdapterNotAvailable":
props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect.");
break;
case "ZigbeeErrorAdapterAlreadyInUse":
props.text = qsTr("The selected adapter is already in use.");
break;
default:
props.errorCode = error;
}
var comp = Qt.createComponent("../components/ErrorDialog.qml")
var popup = comp.createObject(app, props)
popup.open();
}
}
}
signal done();
SettingsPageSectionHeader {
text: qsTr("Hardware not available")
@ -84,10 +49,10 @@ SettingsPageBase {
}
RowLayout {
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
visible: root.zigbeeManager.adapters.count == 0
spacing: app.margins
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
@ -107,7 +72,7 @@ SettingsPageBase {
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
text: qsTr("Please select the ZigBee adapter on which the new network will be created.")
font.pixelSize: app.smallFont
wrapMode: Text.WordWrap
@ -128,7 +93,9 @@ SettingsPageBase {
Layout.fillWidth: true
iconName: "../images/zigbee.svg"
text: model.backend + " - " + model.description + " - " + model.serialPort
onClicked: d.addNetwork(model.serialPort, model.baudRate, model.backend)
onClicked: {
pageStack.push(addSettingsPageComponent, {serialPort: model.serialPort, baudRate: model.baudRate, backend: model.backend, allowSerialPortSettings: false})
}
}
}
@ -138,7 +105,7 @@ SettingsPageBase {
}
Label {
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
text: qsTr("Please verify that the ZigBee adapter is properly connected to a serial port and select the appropriate port.")
@ -160,27 +127,69 @@ SettingsPageBase {
iconName: "../images/stock_usb.svg"
text: model.description + " - " + model.serialPort
onClicked: {
var dialog = serialPortOptionsDialogComponent.createObject(app, {serialPort: model.serialPort, baudRate: model.baudRate, backend: model.backend})
dialog.open()
pageStack.push(addSettingsPageComponent, {serialPort: model.serialPort, baudRate: model.baudRate, backend: model.backend, allowSerialPortSettings: true})
}
}
}
Component {
id: serialPortOptionsDialogComponent
MeaDialog {
id: serialPortOptionsDialog
id: addSettingsPageComponent
SettingsPageBase {
id: addSettingsPage
title: qsTr("Add ZigBee network")
busy: d.pendingCommandId != -1
property bool allowSerialPortSettings: false
property string serialPort
property int baudRate
property string backend
headerIcon: "../images/stock_usb.svg"
title: qsTr("Serial port options")
text: qsTr("Please select the serial port options for using the ZigBee adapter")
standardButtons: Dialog.Ok | Dialog.Cancel
QtObject {
id: d
property int pendingCommandId: -1
}
Connections {
target: root.zigbeeManager
onAddNetworkReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
var props = {};
switch (error) {
case "ZigbeeErrorNoError":
root.done()
return;
case "ZigbeeErrorAdapterNotAvailable":
props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect.");
break;
case "ZigbeeErrorAdapterAlreadyInUse":
props.text = qsTr("The selected adapter is already in use.");
break;
default:
props.errorCode = error;
}
var comp = Qt.createComponent("../components/ErrorDialog.qml")
var popup = comp.createObject(app, props)
popup.open();
}
}
}
SettingsPageSectionHeader {
text: qsTr("Serial port options")
visible: addSettingsPage.allowSerialPortSettings
}
Label {
visible: addSettingsPage.allowSerialPortSettings
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
wrapMode: Text.WordWrap
text: qsTr("Please select the serial port options for using the ZigBee adapter")
}
RowLayout {
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
visible: addSettingsPage.allowSerialPortSettings
Label {
text: qsTr("Adapter")
Layout.fillWidth: true
@ -189,12 +198,14 @@ SettingsPageBase {
id: backendComboBox
model: root.zigbeeManager.availableBackends
Component.onCompleted: {
currentIndex = backendComboBox.find(serialPortOptionsDialog.backend)
currentIndex = backendComboBox.find(addSettingsPage.backend)
}
}
}
RowLayout {
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
visible: addSettingsPage.allowSerialPortSettings
Label {
text: qsTr("Baud rate")
Layout.fillWidth: true
@ -203,13 +214,55 @@ SettingsPageBase {
id: baudRateComboBox
model: ["9600", "14400", "19200", "38400", "57600", "115200", "128000", "230400", "256000"]
Component.onCompleted: {
currentIndex = baudRateComboBox.find(serialPortOptionsDialog.baudRate)
currentIndex = baudRateComboBox.find(addSettingsPage.baudRate)
}
}
}
onAccepted: {
d.addNetwork(serialPortOptionsDialog.serialPort, baudRateComboBox.currentText, backendComboBox.currentText)
SettingsPageSectionHeader {
text: qsTr("Zigbee network settings")
}
RowLayout {
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
Label {
text: qsTr("Channel")
Layout.fillWidth: true
}
ComboBox {
id: channelCombobox
model: ListModel {
id: channelsModel
ListElement { modelData: qsTr("Auto"); channel: ZigbeeManager.ZigbeeChannelAll }
ListElement { modelData: qsTr("Auto light link"); channel: ZigbeeManager.ZigbeeChannelPrimaryLightLink }
ListElement { modelData: "11"; channel: ZigbeeManager.ZigbeeChannel11 }
ListElement { modelData: "12"; channel: ZigbeeManager.ZigbeeChannel12 }
ListElement { modelData: "13"; channel: ZigbeeManager.ZigbeeChannel13 }
ListElement { modelData: "14"; channel: ZigbeeManager.ZigbeeChannel14 }
ListElement { modelData: "15"; channel: ZigbeeManager.ZigbeeChannel15 }
ListElement { modelData: "16"; channel: ZigbeeManager.ZigbeeChannel16 }
ListElement { modelData: "17"; channel: ZigbeeManager.ZigbeeChannel17 }
ListElement { modelData: "18"; channel: ZigbeeManager.ZigbeeChannel18 }
ListElement { modelData: "19"; channel: ZigbeeManager.ZigbeeChannel19 }
ListElement { modelData: "20"; channel: ZigbeeManager.ZigbeeChannel20 }
ListElement { modelData: "21"; channel: ZigbeeManager.ZigbeeChannel21 }
ListElement { modelData: "22"; channel: ZigbeeManager.ZigbeeChannel22 }
ListElement { modelData: "23"; channel: ZigbeeManager.ZigbeeChannel23 }
ListElement { modelData: "24"; channel: ZigbeeManager.ZigbeeChannel24 }
ListElement { modelData: "25"; channel: ZigbeeManager.ZigbeeChannel25 }
ListElement { modelData: "26"; channel: ZigbeeManager.ZigbeeChannel26 }
}
currentIndex: 0
}
}
Button {
Layout.fillWidth: true; Layout.leftMargin: Style.margins; Layout.rightMargin: Style.margins
text: qsTr("OK")
onClicked: {
print("adding ---", channelCombobox.currentIndex, channelsModel.get(channelCombobox.currentIndex).modelData, channelsModel.get(channelCombobox.currentIndex).channel)
d.pendingCommandId = root.zigbeeManager.addNetwork(addSettingsPage.serialPort, baudRateComboBox.currentText, backendComboBox.currentText, channelsModel.get(channelCombobox.currentIndex).channel)
}
}
}
}

View File

@ -48,14 +48,23 @@ SettingsPageBase {
HeaderButton {
imageSource: "/ui/images/help.svg"
text: qsTr("Network settings")
onClicked: pageStack.push(zigbeeHelpPage)
text: qsTr("Help")
onClicked: {
var popup = zigbeeHelpDialog.createObject(app)
popup.open()
}
}
HeaderButton {
imageSource: "/ui/images/configure.svg"
text: qsTr("Network settings")
onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkSettingsPage.qml"), { zigbeeManager: zigbeeManager, network: network })
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("ZigbeeNetworkSettingsPage.qml"), { zigbeeManager: zigbeeManager, network: network })
page.exit.connect(function() {
pageStack.pop(root, StackView.Immediate)
pageStack.pop()
})
}
}
}
@ -99,9 +108,9 @@ SettingsPageBase {
}
ColumnLayout {
spacing: app.margins
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
spacing: Style.margins
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
RowLayout {
Layout.fillWidth: true
@ -229,193 +238,175 @@ SettingsPageBase {
enabled: network.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline
onClicked: zigbeeManager.setPermitJoin(network.networkUuid)
}
}
SettingsPageSectionHeader {
text: qsTr("ZigBee nodes")
text: qsTr("Connected devices")
}
Label {
Layout.fillWidth: true
Layout.margins: Style.margins
horizontalAlignment: Text.AlignHCenter
text: qsTr("There are no ZigBee devices connectd yet. Open the network for new devices to join and start the pairing procedure from the ZigBee device. Please refer to the devices manual for more information on how to start the pairing.")
wrapMode: Text.WordWrap
visible: nodesModel.count === 0
}
Repeater {
id: zigbeeNodeRepeater
model: ZigbeeNodesProxy {
id: zigbeeNodesProxy
id: nodesModel
zigbeeNodes: root.network.nodes
showCoordinator: false
newOnTop: true
}
delegate: NymeaSwipeDelegate {
id: nodeDelegate
readonly property ZigbeeNode node: nodesModel.get(index)
ThingsProxy {
id: nodeThings
engine: _engine
paramsFilter: {"ieeeAddress": nodeDelegate.node.ieeeAddress}
}
readonly property Thing nodeThing: nodeThings.count === 1 ? nodeThings.get(0) : null
property int signalStrength: node ? Math.round(node.lqi * 100.0 / 255.0) : 0
delegate: BigTile {
Layout.fillWidth: true
Layout.leftMargin: Style.smallMargins
Layout.rightMargin: Style.smallMargins
interactive: false
text: nodeThing ? nodeThing.name : node.model
subText: node.manufacturer || node.ieeeAddress
iconName: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/zigbee.svg"
iconColor: busy
? Style.tileOverlayColor
: nodeThing != null ? Style.accentColor : Style.iconColor
progressive: false
readonly property ZigbeeNode node: zigbeeNodesProxy.get(index)
busy: node.state !== ZigbeeNode.ZigbeeNodeStateInitialized
header: RowLayout {
width: parent.width
ColorIcon {
Layout.preferredHeight: Style.smallIconSize
Layout.preferredWidth: Style.smallIconSize
name: !node || node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator
? "/ui/images/zigbee.svg"
: node.type === ZigbeeNode.ZigbeeNodeTypeRouter
? "/ui/images/zigbee-router.svg"
: "/ui/images/zigbee-enddevice.svg"
}
Led {
Layout.preferredHeight: Style.smallIconSize
Layout.preferredWidth: Style.smallIconSize
state: {
if (!node) {
return "off"
}
if (node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator) {
switch (network.networkState) {
case ZigbeeNetwork.ZigbeeNetworkStateOnline:
return "on"
case ZigbeeNetwork.ZigbeeNetworkStateOffline:
return "off"
case ZigbeeNetwork.ZigbeeNetworkStateStarting:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateUpdating:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateError:
return "red"
}
}
if (node.state !== ZigbeeNode.ZigbeeNodeStateInitialized) {
return "orange"
}
if (node.reachable) {
return "on"
} else {
return "red"
}
}
}
Label {
Layout.fillWidth: true
text: node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator
? network.backend + " " + qsTr("network coordinator")
: node ? node.model : ""
elide: Text.ElideRight
}
BusyIndicator {
Layout.preferredHeight: Style.smallIconSize
Layout.preferredWidth: Style.smallIconSize
running: visible
visible: node && node.state !== ZigbeeNode.ZigbeeNodeStateInitialized
}
Label {
text: signalStrengthIcon.signalStrength + "%"
font: Style.smallFont
visible: node && node.type !== ZigbeeNode.ZigbeeNodeTypeCoordinator
}
ColorIcon {
id: signalStrengthIcon
Layout.preferredHeight: Style.smallIconSize
Layout.preferredWidth: Style.smallIconSize
visible: node && node.type !== ZigbeeNode.ZigbeeNodeTypeCoordinator
property int signalStrength: node ? Math.round(node.lqi * 100.0 / 255.0) : 0
name: {
if (!node || !node.reachable)
return "/ui/images/connections/nm-signal-00.svg"
if (signalStrength <= 25)
return "/ui/images/connections/nm-signal-25.svg"
if (signalStrength <= 50)
return "/ui/images/connections/nm-signal-50.svg"
if (signalStrength <= 75)
return "/ui/images/connections/nm-signal-75.svg"
if (signalStrength <= 100)
return "/ui/images/connections/nm-signal-100.svg"
}
}
ColorIcon {
id: sleepyIconLoader
Layout.preferredHeight: Style.smallIconSize
Layout.preferredWidth: Style.smallIconSize
visible: node && !node.rxOnWhenIdle
name: "/ui/images/system-suspend.svg"
}
Led {
id: communicationIndicatorLed
Layout.preferredWidth: Style.smallIconSize
Layout.preferredHeight: Style.smallIconSize
state: "off"
Connections {
target: node
onLastSeenChanged: {
communicationIndicatorLed.state = "on"
communicationIndicatorLedTimer.start()
}
}
Timer {
id: communicationIndicatorLedTimer
interval: 200
repeat: false
onTriggered: communicationIndicatorLed.state = "off"
}
}
canDelete: true
onDeleteClicked: {
var dialog = removeZigbeeNodeDialogComponent.createObject(app, {zigbeeNode: node})
dialog.open()
}
contentItem: ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
visible: node && node.type !== ZigbeeNode.ZigbeeNodeTypeCoordinator
text: node.manufacturer
}
secondaryIconName: node && !node.rxOnWhenIdle ? "/ui/images/system-suspend.svg" : ""
Label {
Layout.fillWidth: true
elide: Text.ElideRight
visible: node && (node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator || node.version !== "")
text: qsTr("Version") + ": " + (node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator ? network.firmwareVersion : node.version)
}
tertiaryIconName: {
if (!node || !node.reachable)
return "/ui/images/connections/nm-signal-00.svg"
Label {
Layout.fillWidth: true
text: qsTr("IEEE address") + ": " + node.ieeeAddress
elide: Text.ElideRight
}
if (signalStrength <= 25)
return "/ui/images/connections/nm-signal-25.svg"
Label {
Layout.fillWidth: true
text: qsTr("Network address") + ": 0x" + (node.networkAddress + Math.pow(16, 4)).toString(16).slice(-4).toUpperCase();
elide: Text.ElideRight
}
if (signalStrength <= 50)
return "/ui/images/connections/nm-signal-50.svg"
if (signalStrength <= 75)
return "/ui/images/connections/nm-signal-75.svg"
if (signalStrength <= 100)
return "/ui/images/connections/nm-signal-100.svg"
}
tertiaryIconColor: node.reachable ? Style.iconColor : "red"
Connections {
target: node
onLastSeenChanged: communicationIndicatorLedTimer.start()
}
Timer {
id: communicationIndicatorLedTimer
interval: 200
}
additionalItem: ColorIcon {
size: Style.smallIconSize
visible: node && !node.rxOnWhenIdle
anchors.verticalCenter: parent.verticalCenter
name: node.type === ZigbeeNode.ZigbeeNodeTypeRouter
? "/ui/images/zigbee-router.svg"
: "/ui/images/zigbee-enddevice.svg"
color: communicationIndicatorLedTimer.running ? Style.accentColor : Style.iconColor
}
onClicked: {
var popup = nodeInfoComponent.createObject(app, {node: node, nodeThing: nodeThing})
popup.open()
}
}
}
Component {
id: nodeInfoComponent
MeaDialog {
id: nodeInfoDialog
property ZigbeeNode node: null
property Thing nodeThing: null
headerIcon: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/zigbee.svg"
title: nodeThing ? nodeThing.name : node.model
standardButtons: Dialog.NoButton
NymeaItemDelegate {
text: qsTr("Model")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.model
}
NymeaItemDelegate {
text: qsTr("Manufacturer")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.manufacturer
}
NymeaItemDelegate {
text: qsTr("IEEE address")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.ieeeAddress
}
NymeaItemDelegate {
text: qsTr("Network address")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.networkAddress
}
NymeaItemDelegate {
text: qsTr("Version")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.version
}
NymeaItemDelegate {
text: qsTr("Signal strength")
Layout.fillWidth: true
progressive: false
subText: (nodeInfoDialog.node.lqi * 100.0 / 255.0) + " %"
}
RowLayout {
Layout.fillWidth: true
Button {
// size: Style.iconSize
// size: Style.iconSize
visible: node && node.type !== ZigbeeNode.ZigbeeNodeTypeCoordinator
// imageSource: "/ui/images/delete.svg"
// imageSource: "/ui/images/delete.svg"
text: qsTr("Remove")
Layout.alignment: Qt.AlignRight
Layout.alignment: Qt.AlignLeft
onClicked: {
var dialog = removeZigbeeNodeDialogComponent.createObject(app, {zigbeeNode: node})
dialog.open()
nodeInfoDialog.close()
}
}
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("OK")
onClicked: nodeInfoDialog.close()
Layout.alignment: Qt.AlignRight
}
}
}
}
@ -446,76 +437,48 @@ SettingsPageBase {
}
Component {
id: zigbeeHelpPage
id: zigbeeHelpDialog
SettingsPageBase {
id: root
MeaDialog {
id: dialog
title: qsTr("ZigBee network help")
header: NymeaHeader {
text: qsTr("ZigBee network help")
backButtonVisible: true
onBackPressed: pageStack.pop()
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-router.svg"
}
Label {
text: qsTr("ZigBee router")
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.topMargin: 2 * app.margins
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee.svg"
}
Label {
text: qsTr("ZigBee network coordinator")
}
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-enddevice.svg"
}
Label {
text: qsTr("ZigBee end device")
}
}
RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-router.svg"
}
Label {
text: qsTr("ZigBee router")
}
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/system-suspend.svg"
}
RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-enddevice.svg"
}
Label {
text: qsTr("ZigBee end device")
}
}
RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/system-suspend.svg"
}
Label {
text: qsTr("Sleepy device")
}
Label {
text: qsTr("Sleepy device")
}
}
}

View File

@ -0,0 +1,486 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, 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 "../components"
import Nymea 1.0
SettingsPageBase {
id: root
property ZigbeeManager zigbeeManager: null
property ZigbeeNetwork network: null
header: NymeaHeader {
text: qsTr("ZigBee 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("ZigbeeNetworkSettingsPage.qml"), { zigbeeManager: zigbeeManager, network: network })
page.exit.connect(function() {
pageStack.pop(root, StackView.Immediate)
pageStack.pop()
})
}
}
}
busy: d.pendingCommandId != -1
QtObject {
id: d
property int pendingCommandId: -1
function removeNode(networkUuid, ieeeAddress) {
d.pendingCommandId = root.zigbeeManager.removeNode(networkUuid, ieeeAddress)
}
}
Connections {
target: root.zigbeeManager
onRemoveNodeReply: {
if (commandId == d.pendingCommandId) {
d.pendingCommandId = -1
var props = {};
switch (error) {
case "ZigbeeErrorNoError":
return;
case "ZigbeeErrorAdapterNotAvailable":
props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect.");
break;
case "ZigbeeErrorAdapterAlreadyInUse":
props.text = qsTr("The selected adapter is already in use.");
break;
default:
props.errorCode = error;
}
var comp = Qt.createComponent("../components/ErrorDialog.qml")
var popup = comp.createObject(app, props)
popup.open();
}
}
}
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 ZigbeeNetwork.ZigbeeNetworkStateOnline:
return qsTr("Online")
case ZigbeeNetwork.ZigbeeNetworkStateOffline:
return qsTr("Offline")
case ZigbeeNetwork.ZigbeeNetworkStateStarting:
return qsTr("Starting")
case ZigbeeNetwork.ZigbeeNetworkStateUpdating:
return qsTr("Updating")
case ZigbeeNetwork.ZigbeeNetworkStateError:
return qsTr("Error")
}
}
}
Led {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
state: {
switch (network.networkState) {
case ZigbeeNetwork.ZigbeeNetworkStateOnline:
return "on"
case ZigbeeNetwork.ZigbeeNetworkStateOffline:
return "off"
case ZigbeeNetwork.ZigbeeNetworkStateStarting:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateUpdating:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateError:
return "red"
}
}
}
}
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: qsTr("Channel")
}
Label {
text: network.channel
}
}
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: qsTr("Permit new devices:")
}
Label {
text: network.permitJoiningEnabled ? qsTr("Open for %0 s").arg(network.permitJoiningRemaining) : qsTr("Closed")
}
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: network.permitJoiningEnabled ? "/ui/images/lock-open.svg" : "/ui/images/lock-closed.svg"
visible: !network.permitJoiningEnabled
}
Canvas {
id: canvas
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
rotation: -90
visible: network.permitJoiningEnabled
property real progress: network.permitJoiningRemaining / network.permitJoiningDuration
onProgressChanged: {
canvas.requestPaint()
}
onPaint: {
var ctx = canvas.getContext("2d");
ctx.save();
ctx.reset();
var data = [1 - progress, progress];
var myTotal = 0;
for(var e = 0; e < data.length; e++) {
myTotal += data[e];
}
ctx.fillStyle = Style.accentColor
ctx.strokeStyle = Style.accentColor
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(canvas.width/2,canvas.height/2);
ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2,0,(Math.PI*2*((1-progress)/myTotal)),false);
ctx.lineTo(canvas.width/2,canvas.height/2);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2 - 1,0,Math.PI*2,false);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
}
}
Button {
Layout.fillWidth: true
text: network.permitJoiningEnabled ? qsTr("Extend open duration") : qsTr("Open for new devices")
enabled: network.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline
onClicked: zigbeeManager.setPermitJoin(network.networkUuid)
}
}
SettingsPageSectionHeader {
text: qsTr("Connected devices")
}
Label {
Layout.fillWidth: true
Layout.margins: Style.margins
horizontalAlignment: Text.AlignHCenter
text: qsTr("There are no ZigBee devices connectd yet. Open the network for new devices to join and start the pairing procedure from the ZigBee 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: ZigbeeNodesProxy {
id: nodesModel
zigbeeNodes: root.network.nodes
showCoordinator: false
newOnTop: true
}
delegate: NymeaSwipeDelegate {
id: nodeDelegate
readonly property ZigbeeNode node: nodesModel.get(index)
ThingsProxy {
id: nodeThings
engine: _engine
paramsFilter: {"ieeeAddress": nodeDelegate.node.ieeeAddress}
}
readonly property Thing nodeThing: nodeThings.count === 1 ? nodeThings.get(0) : null
property int signalStrength: node ? Math.round(node.lqi * 100.0 / 255.0) : 0
Layout.fillWidth: true
text: nodeThing ? nodeThing.name : node.model
subText: node.manufacturer || node.ieeeAddress
iconName: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/zigbee.svg"
iconColor: busy
? Style.tileOverlayColor
: nodeThing != null ? Style.accentColor : Style.iconColor
progressive: false
busy: node.state !== ZigbeeNode.ZigbeeNodeStateInitialized
canDelete: true
onDeleteClicked: {
var dialog = removeZigbeeNodeDialogComponent.createObject(app, {zigbeeNode: node})
dialog.open()
}
secondaryIconName: node && !node.rxOnWhenIdle ? "/ui/images/system-suspend.svg" : ""
tertiaryIconName: {
if (!node || !node.reachable)
return "/ui/images/connections/nm-signal-00.svg"
if (signalStrength <= 25)
return "/ui/images/connections/nm-signal-25.svg"
if (signalStrength <= 50)
return "/ui/images/connections/nm-signal-50.svg"
if (signalStrength <= 75)
return "/ui/images/connections/nm-signal-75.svg"
if (signalStrength <= 100)
return "/ui/images/connections/nm-signal-100.svg"
}
tertiaryIconColor: node.reachable ? Style.iconColor : "red"
Connections {
target: node
onLastSeenChanged: communicationIndicatorLedTimer.start()
}
Timer {
id: communicationIndicatorLedTimer
interval: 200
}
additionalItem: ColorIcon {
size: Style.smallIconSize
visible: node && !node.rxOnWhenIdle
anchors.verticalCenter: parent.verticalCenter
name: node.type === ZigbeeNode.ZigbeeNodeTypeRouter
? "/ui/images/zigbee-router.svg"
: "/ui/images/zigbee-enddevice.svg"
color: communicationIndicatorLedTimer.running ? Style.accentColor : Style.iconColor
}
onClicked: {
var popup = nodeInfoComponent.createObject(app, {node: node, nodeThing: nodeThing})
popup.open()
}
}
}
Component {
id: nodeInfoComponent
MeaDialog {
id: nodeInfoDialog
property ZigbeeNode node: null
property Thing nodeThing: null
headerIcon: nodeThing ? app.interfacesToIcon(nodeThing.thingClass.interfaces) : "/ui/images/zigbee.svg"
title: nodeThing ? nodeThing.name : node.model
standardButtons: Dialog.NoButton
NymeaItemDelegate {
text: qsTr("Model")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.model
}
NymeaItemDelegate {
text: qsTr("Manufacturer")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.manufacturer
}
NymeaItemDelegate {
text: qsTr("IEEE address")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.ieeeAddress
}
NymeaItemDelegate {
text: qsTr("Network address")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.networkAddress
}
NymeaItemDelegate {
text: qsTr("Signal strength")
Layout.fillWidth: true
progressive: false
subText: (nodeInfoDialog.node.lqi * 100.0 / 255.0) + " %"
}
NymeaItemDelegate {
text: qsTr("Version")
Layout.fillWidth: true
progressive: false
subText: nodeInfoDialog.node.version
}
RowLayout {
Layout.fillWidth: true
Button {
// size: Style.iconSize
visible: node && node.type !== ZigbeeNode.ZigbeeNodeTypeCoordinator
// imageSource: "/ui/images/delete.svg"
text: qsTr("Remove")
Layout.alignment: Qt.AlignLeft
onClicked: {
var dialog = removeZigbeeNodeDialogComponent.createObject(app, {zigbeeNode: node})
dialog.open()
nodeInfoDialog.close()
}
}
Item {
Layout.fillWidth: true
}
Button {
text: qsTr("OK")
onClicked: nodeInfoDialog.close()
Layout.alignment: Qt.AlignRight
}
}
}
}
Component {
id: removeZigbeeNodeDialogComponent
MeaDialog {
id: removeZigbeeNodeDialog
property ZigbeeNode zigbeeNode
headerIcon: "/ui/images/zigbee.svg"
title: qsTr("Remove ZigBee node") + " " + (zigbeeNode ? zigbeeNode.model : "")
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: {
d.removeNode(zigbeeNode.networkUuid, zigbeeNode.ieeeAddress)
}
}
}
Component {
id: zigbeeHelpDialog
MeaDialog {
id: dialog
title: qsTr("ZigBee network help")
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-router.svg"
}
Label {
text: qsTr("ZigBee router")
}
}
RowLayout {
spacing: Style.margins
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: "/ui/images/zigbee-enddevice.svg"
}
Label {
text: qsTr("ZigBee 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")
}
}
}
}
}

View File

@ -41,6 +41,8 @@ SettingsPageBase {
property ZigbeeManager zigbeeManager: null
property ZigbeeNetwork network: null
signal exit()
header: NymeaHeader {
text: qsTr("ZigBee network settings")
backButtonVisible: true
@ -144,7 +146,7 @@ SettingsPageBase {
text: qsTr("Remove network")
onClicked: {
root.zigbeeManager.removeNetwork(root.network.networkUuid)
pageStack.pop()
root.exit()
}
}

View File

@ -45,10 +45,17 @@ SettingsPageBase {
HeaderButton {
imageSource: "../images/add.svg"
text: qsTr("Add ZigBee network")
onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager})
onClicked: {
addNetwork()
}
}
}
function addNetwork() {
var addPage = pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager})
addPage.done.connect(function() {pageStack.pop(root)})
}
ZigbeeManager {
id: zigbeeManager
engine: _engine
@ -67,7 +74,7 @@ SettingsPageBase {
imageSource: "/ui/images/zigbee.svg"
buttonText: qsTr("Add network")
onButtonClicked: {
pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager})
addNetwork()
}
}
}

View File

@ -0,0 +1,177 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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.8
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.3
import "../components"
import Nymea 1.0
SettingsPageBase {
id: root
header: NymeaHeader {
text: qsTr("ZigBee")
backButtonVisible: true
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/add.svg"
text: qsTr("Add ZigBee network")
onClicked: {
addNetwork()
}
}
}
function addNetwork() {
var addPage = pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager})
addPage.done.connect(function() {pageStack.pop(root)})
}
ZigbeeManager {
id: zigbeeManager
engine: _engine
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: root.height
visible: zigbeeManager.networks.count == 0
EmptyViewPlaceholder {
width: parent.width - app.margins * 2
anchors.centerIn: parent
title: qsTr("ZigBee")
text: qsTr("There are no ZigBee networks set up yet. In order to use ZigBee, create a ZigBee network.")
imageSource: "/ui/images/zigbee.svg"
buttonText: qsTr("Add network")
onButtonClicked: {
addNetwork()
}
}
}
ColumnLayout {
Layout.margins: app.margins / 2
Repeater {
model: zigbeeManager.networks
delegate: BigTile {
Layout.fillWidth: true
interactive: false
onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { zigbeeManager: zigbeeManager, network: zigbeeManager.networks.get(index) })
header: RowLayout {
ColorIcon {
name: "/ui/images/zigbee/" + model.backend + ".svg"
Layout.preferredWidth: Style.iconSize
Layout.preferredHeight: Style.iconSize
}
Label {
Layout.fillWidth: true
text: model.backend
font.pixelSize: app.largeFont
}
}
contentItem: ColumnLayout {
spacing: app.margins
RowLayout {
Label {
Layout.fillWidth: true
text: qsTr("Network state:")
}
Label {
text: {
switch (model.networkState) {
case ZigbeeNetwork.ZigbeeNetworkStateOnline:
return qsTr("Online")
case ZigbeeNetwork.ZigbeeNetworkStateOffline:
return qsTr("Offline")
case ZigbeeNetwork.ZigbeeNetworkStateStarting:
return qsTr("Starting")
case ZigbeeNetwork.ZigbeeNetworkStateUpdating:
return qsTr("Updating")
case ZigbeeNetwork.ZigbeeNetworkStateError:
return qsTr("Error")
}
}
}
Led {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
state: {
switch (model.networkState) {
case ZigbeeNetwork.ZigbeeNetworkStateOnline:
return "on"
case ZigbeeNetwork.ZigbeeNetworkStateOffline:
return "off"
case ZigbeeNetwork.ZigbeeNetworkStateStarting:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateUpdating:
return "orange"
case ZigbeeNetwork.ZigbeeNetworkStateError:
return "red"
}
}
}
}
RowLayout {
Label {
Layout.fillWidth: true
text: qsTr("MAC address:")
}
Label {
text: model.macAddress
}
}
RowLayout {
Label {
Layout.fillWidth: true
text: qsTr("Firmware version:")
}
Label {
text: model.firmwareVersion
}
}
}
}
}
}
}