Merge PR #881: Add support for configuring ZigBee bindings
This commit is contained in:
commit
e1f209e1de
@ -329,9 +329,12 @@ void registerQmlTypes() {
|
||||
qmlRegisterUncreatableType<ZigbeeNetwork>(uri, 1, 0, "ZigbeeNetwork", "Get it from the ZigbeeManager");
|
||||
qmlRegisterUncreatableType<ZigbeeNetworks>(uri, 1, 0, "ZigbeeNetworks", "Get it from the ZigbeeManager");
|
||||
qmlRegisterUncreatableType<ZigbeeNode>(uri, 1, 0, "ZigbeeNode", "Get it from the ZigbeeNodes");
|
||||
qmlRegisterUncreatableType<ZigbeeNodes>(uri, 1, 0, "ZigbeeNodes", "Get it from the ZigbeeNetwork");
|
||||
qmlRegisterUncreatableType<ZigbeeNodeNeighbor>(uri, 1, 0, "ZigbeeNodeNeighbor", "Get it from the ZigbeeNode");
|
||||
qmlRegisterUncreatableType<ZigbeeNodeRoute>(uri, 1, 0, "ZigbeeNodeRoute", "Get it from the ZigbeeNode");
|
||||
qmlRegisterUncreatableType<ZigbeeNodes>(uri, 1, 0, "ZigbeeNodes", "Get it from the ZigbeeNetwork");
|
||||
qmlRegisterUncreatableType<ZigbeeNodeBinding>(uri, 1, 0, "ZigbeeNodeBinding", "Get it from the ZigbeeNode");
|
||||
qmlRegisterUncreatableType<ZigbeeNodeEndpoint>(uri, 1, 0, "ZigbeeNodeEndpoint", "Get it from the ZigbeeNode");
|
||||
qmlRegisterUncreatableType<ZigbeeCluster>(uri, 1, 0, "ZigbeeCluster", "Get it from the ZigbeeNode");
|
||||
qmlRegisterType<ZigbeeNodesProxy>(uri, 1, 0, "ZigbeeNodesProxy");
|
||||
|
||||
qmlRegisterType<ZWaveManager>(uri, 1, 0, "ZWaveManager");
|
||||
|
||||
@ -156,6 +156,38 @@ void ZigbeeManager::refreshNeighborTables(const QUuid &networkUuid)
|
||||
m_engine->jsonRpcClient()->sendCommand("Zigbee.RefreshNeighborTables", {{"networkUuid", networkUuid}});
|
||||
}
|
||||
|
||||
int ZigbeeManager::createBinding(const QUuid &networkUuid, const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpointId)
|
||||
{
|
||||
QVariantMap params = {
|
||||
{"networkUuid", networkUuid},
|
||||
{"sourceAddress", sourceAddress},
|
||||
{"sourceEndpointId", sourceEndpointId},
|
||||
{"clusterId", clusterId},
|
||||
{"destinationAddress", destinationAddress},
|
||||
{"destinationEndpointId", destinationEndpointId}
|
||||
};
|
||||
qCDebug(dcZigbee()) << "Creating binding for:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
|
||||
return m_engine->jsonRpcClient()->sendCommand("Zigbee.CreateBinding", params, this, "createBindingResponse");
|
||||
}
|
||||
|
||||
int ZigbeeManager::removeBinding(const QUuid &networkUuid, ZigbeeNodeBinding *binding)
|
||||
{
|
||||
QVariantMap params = {
|
||||
{"networkUuid", networkUuid},
|
||||
{"sourceAddress", binding->sourceAddress()},
|
||||
{"sourceEndpointId", binding->sourceEndpointId()},
|
||||
{"clusterId", binding->clusterId()}
|
||||
};
|
||||
if (!binding->destinationAddress().isEmpty()) {
|
||||
params.insert("destinationAddress", binding->destinationAddress());
|
||||
params.insert("destinationEndpointId", binding->destinationEndpointId());
|
||||
} else {
|
||||
params.insert("destinationGroupAddress", binding->groupAddress());
|
||||
}
|
||||
qCDebug(dcZigbee()) << "Removing binding for:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
|
||||
return m_engine->jsonRpcClient()->sendCommand("Zigbee.RemoveBinding", params, this, "removeBindingResponse");
|
||||
}
|
||||
|
||||
void ZigbeeManager::init()
|
||||
{
|
||||
m_fetchingData = true;
|
||||
@ -216,7 +248,9 @@ void ZigbeeManager::getNetworksResponse(int commandId, const QVariantMap ¶ms
|
||||
void ZigbeeManager::addNetworkResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcZigbee()) << "Zigbee add network response" << commandId << params;
|
||||
emit addNetworkReply(commandId, params.value("zigbeeError").toString(), params.value("networkUuid").toUuid());
|
||||
QMetaEnum errorEnum = QMetaEnum::fromType<ZigbeeError>();
|
||||
ZigbeeError error = static_cast<ZigbeeError>(errorEnum.keyToValue(params.value("zigbeeError").toByteArray()));
|
||||
emit addNetworkReply(commandId, error, params.value("networkUuid").toUuid());
|
||||
}
|
||||
|
||||
void ZigbeeManager::removeNetworkResponse(int commandId, const QVariantMap ¶ms)
|
||||
@ -254,7 +288,25 @@ void ZigbeeManager::getNodesResponse(int commandId, const QVariantMap ¶ms)
|
||||
void ZigbeeManager::removeNodeResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcZigbee()) << "Zigbee remove node response" << commandId << params;
|
||||
emit removeNodeReply(commandId, params.value("zigbeeError").toString());
|
||||
QMetaEnum errorEnum = QMetaEnum::fromType<ZigbeeError>();
|
||||
ZigbeeError error = static_cast<ZigbeeError>(errorEnum.keyToValue(params.value("zigbeeError").toByteArray()));
|
||||
emit removeNodeReply(commandId, error);
|
||||
}
|
||||
|
||||
void ZigbeeManager::createBindingResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcZigbee()) << "Create binding response" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
|
||||
QMetaEnum errorEnum = QMetaEnum::fromType<ZigbeeError>();
|
||||
ZigbeeError error = static_cast<ZigbeeError>(errorEnum.keyToValue(params.value("zigbeeError").toByteArray()));
|
||||
emit createBindingReply(commandId, error);
|
||||
}
|
||||
|
||||
void ZigbeeManager::removeBindingResponse(int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
qCDebug(dcZigbee()) << "Remove binding response" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson());
|
||||
QMetaEnum errorEnum = QMetaEnum::fromType<ZigbeeError>();
|
||||
ZigbeeError error = static_cast<ZigbeeError>(errorEnum.keyToValue(params.value("zigbeeError").toByteArray()));
|
||||
emit removeBindingReply(commandId, error);
|
||||
}
|
||||
|
||||
void ZigbeeManager::notificationReceived(const QVariantMap ¬ification)
|
||||
@ -438,4 +490,52 @@ void ZigbeeManager::updateNodeProperties(ZigbeeNode *node, const QVariantMap &no
|
||||
routes.append(destinationAddress);
|
||||
}
|
||||
node->commitRoutes(routes);
|
||||
|
||||
foreach (const QVariant &binding, nodeMap.value("bindingTableRecords").toList()) {
|
||||
QVariantMap bindingMap = binding.toMap();
|
||||
QString sourceAddress = bindingMap.value("sourceAddress").toString();
|
||||
quint8 sourceEndpointId = bindingMap.value("sourceEndpointId").toUInt();
|
||||
quint16 clusterId = bindingMap.value("clusterId").toUInt();
|
||||
if (bindingMap.contains("groupAddress")) {
|
||||
quint16 groupAddress = bindingMap.value("groupAddress").toUInt();
|
||||
node->addBinding(sourceAddress, sourceEndpointId, clusterId, groupAddress);
|
||||
} else {
|
||||
QString destinationAddress = bindingMap.value("destinationAddress").toString();
|
||||
quint8 destinationEndpointId = bindingMap.value("destinationEndpointId").toUInt();
|
||||
node->addBinding(sourceAddress, sourceEndpointId, clusterId, destinationAddress, destinationEndpointId);
|
||||
}
|
||||
}
|
||||
node->commitBindings();
|
||||
|
||||
foreach (const QVariant &e, nodeMap.value("endpoints").toList()) {
|
||||
QVariantMap endpointMap = e.toMap();
|
||||
quint8 endpointId = endpointMap.value("endpointId").toUInt();
|
||||
ZigbeeNodeEndpoint *endpoint = node->getEndpoint(endpointId);
|
||||
if (!endpoint) {
|
||||
endpoint = new ZigbeeNodeEndpoint(endpointId);
|
||||
node->addEndpoint(endpoint);
|
||||
}
|
||||
foreach (const QVariant &c, endpointMap.value("inputClusters").toList()) {
|
||||
QVariantMap clusterMap = c.toMap();
|
||||
quint16 clusterId = clusterMap.value("clusterId").toUInt();
|
||||
if (endpoint->getInputCluster(clusterId)) {
|
||||
continue;
|
||||
}
|
||||
QMetaEnum clusterDirectionEnum = QMetaEnum::fromType<ZigbeeCluster::ZigbeeClusterDirection>();
|
||||
ZigbeeCluster::ZigbeeClusterDirection direction = static_cast<ZigbeeCluster::ZigbeeClusterDirection>(clusterDirectionEnum.keyToValue(clusterMap.value("direction").toByteArray().data()));
|
||||
ZigbeeCluster *cluster = new ZigbeeCluster(clusterId, direction);
|
||||
endpoint->addInputCluster(cluster);
|
||||
}
|
||||
foreach (const QVariant &c, endpointMap.value("outputClusters").toList()) {
|
||||
QVariantMap clusterMap = c.toMap();
|
||||
quint16 clusterId = clusterMap.value("clusterId").toUInt();
|
||||
if (endpoint->getOutputCluster(clusterId)) {
|
||||
continue;
|
||||
}
|
||||
QMetaEnum clusterDirectionEnum = QMetaEnum::fromType<ZigbeeCluster::ZigbeeClusterDirection>();
|
||||
ZigbeeCluster::ZigbeeClusterDirection direction = static_cast<ZigbeeCluster::ZigbeeClusterDirection>(clusterDirectionEnum.keyToValue(clusterMap.value("direction").toByteArray().data()));
|
||||
ZigbeeCluster *cluster = new ZigbeeCluster(clusterId, direction);
|
||||
endpoint->addOutputCluster(cluster);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ class ZigbeeNetwork;
|
||||
class ZigbeeNetworks;
|
||||
class ZigbeeNode;
|
||||
class ZigbeeNodes;
|
||||
class ZigbeeNodeBinding;
|
||||
|
||||
class ZigbeeManager : public QObject
|
||||
{
|
||||
@ -53,6 +54,22 @@ class ZigbeeManager : public QObject
|
||||
Q_PROPERTY(ZigbeeNetworks *networks READ networks CONSTANT)
|
||||
|
||||
public:
|
||||
enum ZigbeeError {
|
||||
ZigbeeErrorNoError,
|
||||
ZigbeeErrorAdapterNotAvailable,
|
||||
ZigbeeErrorAdapterAlreadyInUse,
|
||||
ZigbeeErrorNetworkUuidNotFound,
|
||||
ZigbeeErrorDurationOutOfRange,
|
||||
ZigbeeErrorNetworkOffline,
|
||||
ZigbeeErrorUnknownBackend,
|
||||
ZigbeeErrorNodeNotFound,
|
||||
ZigbeeErrorForbidden,
|
||||
ZigbeeErrorInvalidChannel,
|
||||
ZigbeeErrorNetworkError,
|
||||
ZigbeeErrorTimeoutError
|
||||
};
|
||||
Q_ENUM(ZigbeeError)
|
||||
|
||||
enum ZigbeeChannel {
|
||||
ZigbeeChannel11 = 0x00000800,
|
||||
ZigbeeChannel12 = 0x00001000,
|
||||
@ -96,13 +113,17 @@ public:
|
||||
Q_INVOKABLE void getNodes(const QUuid &networkUuid);
|
||||
Q_INVOKABLE int removeNode(const QUuid &networkUuid, const QString &ieeeAddress);
|
||||
Q_INVOKABLE void refreshNeighborTables(const QUuid &networkUuid);
|
||||
Q_INVOKABLE int createBinding(const QUuid &networkUuid, const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpointId);
|
||||
Q_INVOKABLE int removeBinding(const QUuid &networkUuid, ZigbeeNodeBinding *binding);
|
||||
|
||||
signals:
|
||||
void engineChanged();
|
||||
void fetchingDataChanged();
|
||||
void availableBackendsChanged();
|
||||
void addNetworkReply(int commandId, const QString &error, const QUuid &networkUuid);
|
||||
void removeNodeReply(int commandId, const QString &error);
|
||||
void addNetworkReply(int commandId, ZigbeeError error, const QUuid &networkUuid);
|
||||
void removeNodeReply(int commandId, ZigbeeError error);
|
||||
void createBindingReply(int commandId, ZigbeeError error);
|
||||
void removeBindingReply(int commandId, ZigbeeError error);
|
||||
|
||||
private:
|
||||
void init();
|
||||
@ -119,6 +140,9 @@ private:
|
||||
Q_INVOKABLE void getNodesResponse(int commandId, const QVariantMap ¶ms);
|
||||
Q_INVOKABLE void removeNodeResponse(int commandId, const QVariantMap ¶ms);
|
||||
|
||||
Q_INVOKABLE void createBindingResponse(int commandId, const QVariantMap ¶ms);
|
||||
Q_INVOKABLE void removeBindingResponse(int commandId, const QVariantMap ¶ms);
|
||||
|
||||
Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification);
|
||||
|
||||
private:
|
||||
|
||||
@ -30,6 +30,8 @@
|
||||
|
||||
#include "zigbeenode.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
ZigbeeNode::ZigbeeNode(const QUuid &networkUuid, const QString &ieeeAddress, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_networkUuid(networkUuid),
|
||||
@ -299,6 +301,81 @@ void ZigbeeNode::commitRoutes(QList<quint16> toBeKept)
|
||||
}
|
||||
}
|
||||
|
||||
QList<ZigbeeNodeBinding *> ZigbeeNode::bindings() const
|
||||
{
|
||||
return m_bindings;
|
||||
}
|
||||
|
||||
void ZigbeeNode::addBinding(const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, quint16 groupAddress)
|
||||
{
|
||||
ZigbeeNodeBinding *newBinding = new ZigbeeNodeBinding(sourceAddress, sourceEndpointId, clusterId, groupAddress, this);
|
||||
foreach (ZigbeeNodeBinding *binding, m_bindings) {
|
||||
if (binding == newBinding) {
|
||||
binding->setProperty("validated", true);
|
||||
delete newBinding;
|
||||
return;
|
||||
}
|
||||
}
|
||||
newBinding->setProperty("validated", true);
|
||||
m_bindings.append(newBinding);
|
||||
m_bindingsDirty = true;
|
||||
}
|
||||
|
||||
void ZigbeeNode::addBinding(const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpointId)
|
||||
{
|
||||
ZigbeeNodeBinding *newBinding = new ZigbeeNodeBinding(sourceAddress, sourceEndpointId, clusterId, destinationAddress, destinationEndpointId, this);
|
||||
foreach (ZigbeeNodeBinding *binding, m_bindings) {
|
||||
if (binding == newBinding) {
|
||||
binding->setProperty("validated", true);
|
||||
delete newBinding;
|
||||
return;
|
||||
}
|
||||
}
|
||||
newBinding->setProperty("validated", true);
|
||||
m_bindings.append(newBinding);
|
||||
m_bindingsDirty = true;
|
||||
}
|
||||
|
||||
void ZigbeeNode::commitBindings()
|
||||
{
|
||||
QMutableListIterator<ZigbeeNodeBinding*> it(m_bindings);
|
||||
while (it.hasNext()) {
|
||||
ZigbeeNodeBinding *binding = it.next();
|
||||
if (!binding->property("validated").toBool()) {
|
||||
it.remove();
|
||||
m_bindingsDirty = true;
|
||||
} else {
|
||||
binding->setProperty("validated", false);
|
||||
}
|
||||
}
|
||||
if (m_bindingsDirty) {
|
||||
m_bindingsDirty = false;
|
||||
emit bindingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QList<ZigbeeNodeEndpoint *> ZigbeeNode::endpoints() const
|
||||
{
|
||||
return m_endpoints;
|
||||
}
|
||||
|
||||
ZigbeeNodeEndpoint *ZigbeeNode::getEndpoint(quint8 endpointId) const
|
||||
{
|
||||
foreach (ZigbeeNodeEndpoint *endpoint, m_endpoints) {
|
||||
if (endpoint->endpointId() == endpointId) {
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ZigbeeNode::addEndpoint(ZigbeeNodeEndpoint *endpoint)
|
||||
{
|
||||
endpoint->setParent(this);
|
||||
m_endpoints.append(endpoint);
|
||||
emit endpointsChanged();
|
||||
}
|
||||
|
||||
ZigbeeNode::ZigbeeNodeState ZigbeeNode::stringToNodeState(const QString &nodeState)
|
||||
{
|
||||
if (nodeState == "ZigbeeNodeStateUninitialized") {
|
||||
@ -450,3 +527,157 @@ void ZigbeeNodeRoute::setManyToOne(bool manyToOne)
|
||||
emit manyToOneChanged();
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeNodeBinding::ZigbeeNodeBinding(const QString &sourceAddress, quint8 sourceEndointId, quint16 clusterId, quint16 groupAddress, QObject *parent):
|
||||
QObject(parent),
|
||||
m_sourceAddress(sourceAddress),
|
||||
m_sourceEndpointId(sourceEndointId),
|
||||
m_clusterId(clusterId),
|
||||
m_type(ZigbeeNode::ZigbeeNodeBindingTypeGroup),
|
||||
m_groupAddress(groupAddress)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ZigbeeNodeBinding::ZigbeeNodeBinding(const QString &sourceAddress, quint8 sourceEndointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpoint, QObject *parent):
|
||||
QObject(parent),
|
||||
m_sourceAddress(sourceAddress),
|
||||
m_sourceEndpointId(sourceEndointId),
|
||||
m_clusterId(clusterId),
|
||||
m_type(ZigbeeNode::ZigbeeNodeBindingTypeDevice),
|
||||
m_destinationAddress(destinationAddress),
|
||||
m_destinationEndpointId(destinationEndpoint)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString ZigbeeNodeBinding::sourceAddress() const
|
||||
{
|
||||
return m_sourceAddress;
|
||||
}
|
||||
|
||||
quint8 ZigbeeNodeBinding::sourceEndpointId() const
|
||||
{
|
||||
return m_sourceEndpointId;
|
||||
}
|
||||
|
||||
quint16 ZigbeeNodeBinding::clusterId() const
|
||||
{
|
||||
return m_clusterId;
|
||||
}
|
||||
|
||||
ZigbeeNode::ZigbeeNodeBindingType ZigbeeNodeBinding::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
quint16 ZigbeeNodeBinding::groupAddress() const
|
||||
{
|
||||
return m_groupAddress;
|
||||
}
|
||||
|
||||
QString ZigbeeNodeBinding::destinationAddress() const
|
||||
{
|
||||
return m_destinationAddress;
|
||||
}
|
||||
|
||||
quint8 ZigbeeNodeBinding::destinationEndpointId() const
|
||||
{
|
||||
return m_destinationEndpointId;
|
||||
}
|
||||
|
||||
ZigbeeCluster::ZigbeeCluster(quint16 clusterId, ZigbeeClusterDirection direction, QObject *parent):
|
||||
QObject(parent),
|
||||
m_clusterId(clusterId),
|
||||
m_direction(direction)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
quint16 ZigbeeCluster::clusterId() const
|
||||
{
|
||||
return m_clusterId;
|
||||
}
|
||||
|
||||
ZigbeeCluster::ZigbeeClusterDirection ZigbeeCluster::direction() const
|
||||
{
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
ZigbeeNodeEndpoint::ZigbeeNodeEndpoint(quint8 endpointId, const QList<ZigbeeCluster*> &inputClusters, const QList<ZigbeeCluster*> &outputClusters, QObject *parent):
|
||||
QObject(parent),
|
||||
m_endpointId(endpointId),
|
||||
m_inputClusters(inputClusters),
|
||||
m_outputClusters(outputClusters)
|
||||
{
|
||||
foreach (ZigbeeCluster *cluster, inputClusters) {
|
||||
cluster->setParent(this);
|
||||
}
|
||||
foreach (ZigbeeCluster *cluster, outputClusters) {
|
||||
cluster->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
quint8 ZigbeeNodeEndpoint::endpointId() const
|
||||
{
|
||||
return m_endpointId;
|
||||
}
|
||||
|
||||
QList<ZigbeeCluster*> ZigbeeNodeEndpoint::inputClusters() const
|
||||
{
|
||||
return m_inputClusters;
|
||||
}
|
||||
|
||||
ZigbeeCluster *ZigbeeNodeEndpoint::getInputCluster(quint16 clusterId) const
|
||||
{
|
||||
foreach (ZigbeeCluster *cluster, m_inputClusters) {
|
||||
if (cluster->clusterId() == clusterId) {
|
||||
return cluster;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ZigbeeNodeEndpoint::addInputCluster(ZigbeeCluster *cluster)
|
||||
{
|
||||
cluster->setParent(this);
|
||||
m_inputClusters.append(cluster);
|
||||
emit inputClustersChanged();
|
||||
}
|
||||
|
||||
QList<ZigbeeCluster*> ZigbeeNodeEndpoint::outputClusters() const
|
||||
{
|
||||
return m_outputClusters;
|
||||
}
|
||||
|
||||
ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(quint16 clusterId) const
|
||||
{
|
||||
foreach (ZigbeeCluster *cluster, m_outputClusters) {
|
||||
if (cluster->clusterId() == clusterId) {
|
||||
return cluster;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ZigbeeNodeEndpoint::addOutputCluster(ZigbeeCluster *cluster)
|
||||
{
|
||||
cluster->setParent(this);
|
||||
m_outputClusters.append(cluster);
|
||||
emit outputClustersChanged();
|
||||
}
|
||||
|
||||
QString ZigbeeCluster::clusterName() const
|
||||
{
|
||||
QMetaEnum clusterEnum = QMetaEnum::fromType<ZigbeeClusterId>();
|
||||
QString name = clusterEnum.valueToKey(m_clusterId);
|
||||
name.remove("ZigbeeClusterId");
|
||||
QRegExp re1 = QRegExp("([A-Z])([a-z]*)");
|
||||
name.replace(re1, ";\\1\\2");
|
||||
QStringList parts = name.split(";");
|
||||
QString clusterName = parts.join(" ").trimmed();
|
||||
if (clusterName.isEmpty()) {
|
||||
clusterName = "0x" + QString::number(m_clusterId, 16);
|
||||
}
|
||||
return clusterName;
|
||||
}
|
||||
|
||||
@ -38,6 +38,8 @@
|
||||
|
||||
class ZigbeeNodeNeighbor;
|
||||
class ZigbeeNodeRoute;
|
||||
class ZigbeeNodeBinding;
|
||||
class ZigbeeNodeEndpoint;
|
||||
|
||||
class ZigbeeNode : public QObject
|
||||
{
|
||||
@ -56,6 +58,8 @@ class ZigbeeNode : public QObject
|
||||
Q_PROPERTY(QDateTime lastSeen READ lastSeen WRITE setLastSeen NOTIFY lastSeenChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeNeighbor*> neighbors READ neighbors NOTIFY neighborsChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeRoute*> routes READ routes NOTIFY routesChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeBinding*> bindings READ bindings NOTIFY bindingsChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeEndpoint*> endpoints READ endpoints NOTIFY endpointsChanged)
|
||||
|
||||
public:
|
||||
enum ZigbeeNodeType {
|
||||
@ -91,6 +95,12 @@ public:
|
||||
};
|
||||
Q_ENUM(ZigbeeNodeRouteStatus)
|
||||
|
||||
enum ZigbeeNodeBindingType {
|
||||
ZigbeeNodeBindingTypeDevice,
|
||||
ZigbeeNodeBindingTypeGroup
|
||||
};
|
||||
Q_ENUM(ZigbeeNodeBindingType)
|
||||
|
||||
explicit ZigbeeNode(const QUuid &networkUuid, const QString &ieeeAddress, QObject *parent = nullptr);
|
||||
|
||||
QUuid networkUuid() const;
|
||||
@ -134,6 +144,15 @@ public:
|
||||
void addOrUpdateRoute(quint16 destinationAddress, quint16 nextHopAddress, ZigbeeNodeRouteStatus status, bool memoryConstrained, bool manyToOne);
|
||||
void commitRoutes(QList<quint16> toBeKept);
|
||||
|
||||
QList<ZigbeeNodeBinding*> bindings() const;
|
||||
void addBinding(const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, quint16 groupAddress);
|
||||
void addBinding(const QString &sourceAddress, quint8 sourceEndpointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpointId);
|
||||
void commitBindings();
|
||||
|
||||
QList<ZigbeeNodeEndpoint*> endpoints() const;
|
||||
Q_INVOKABLE ZigbeeNodeEndpoint *getEndpoint(quint8 endpointId) const;
|
||||
void addEndpoint(ZigbeeNodeEndpoint *endpoint);
|
||||
|
||||
static ZigbeeNodeState stringToNodeState(const QString &nodeState);
|
||||
static ZigbeeNodeType stringToNodeType(const QString &nodeType);
|
||||
|
||||
@ -150,6 +169,8 @@ signals:
|
||||
void lastSeenChanged(const QDateTime &lastSeen);
|
||||
void neighborsChanged();
|
||||
void routesChanged();
|
||||
void bindingsChanged();
|
||||
void endpointsChanged();
|
||||
|
||||
private:
|
||||
QUuid m_networkUuid;
|
||||
@ -168,6 +189,9 @@ private:
|
||||
bool m_neighborsDirty = false;
|
||||
QList<ZigbeeNodeRoute*> m_routes;
|
||||
bool m_routesDirty = false;
|
||||
QList<ZigbeeNodeBinding*> m_bindings;
|
||||
bool m_bindingsDirty = false;
|
||||
QList<ZigbeeNodeEndpoint*> m_endpoints;
|
||||
};
|
||||
|
||||
class ZigbeeNodeNeighbor: public QObject
|
||||
@ -250,4 +274,184 @@ private:
|
||||
bool m_manyToOne = false;
|
||||
};
|
||||
|
||||
class ZigbeeNodeBinding: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString sourceAddress READ sourceAddress CONSTANT)
|
||||
Q_PROPERTY(quint8 sourceEndpointId READ sourceEndpointId CONSTANT)
|
||||
Q_PROPERTY(quint16 clusterId READ clusterId CONSTANT)
|
||||
Q_PROPERTY(ZigbeeNode::ZigbeeNodeBindingType type READ type CONSTANT)
|
||||
Q_PROPERTY(quint16 groupAddress READ groupAddress CONSTANT)
|
||||
Q_PROPERTY(QString destinationAddress READ destinationAddress CONSTANT)
|
||||
Q_PROPERTY(quint8 destinationEndpointId READ destinationEndpointId CONSTANT)
|
||||
|
||||
public:
|
||||
ZigbeeNodeBinding(const QString &sourceAddress, quint8 sourceEndointId, quint16 clusterId, quint16 groupAddress, QObject *parent);
|
||||
ZigbeeNodeBinding(const QString &sourceAddress, quint8 sourceEndointId, quint16 clusterId, const QString &destinationAddress, quint8 destinationEndpoint, QObject *parent);
|
||||
|
||||
QString sourceAddress() const;
|
||||
quint8 sourceEndpointId() const;
|
||||
quint16 clusterId() const;
|
||||
ZigbeeNode::ZigbeeNodeBindingType type() const;
|
||||
quint16 groupAddress() const;
|
||||
QString destinationAddress() const;
|
||||
quint8 destinationEndpointId() const;
|
||||
|
||||
private:
|
||||
QString m_sourceAddress;
|
||||
quint8 m_sourceEndpointId = 0;
|
||||
quint16 m_clusterId = 0;
|
||||
ZigbeeNode::ZigbeeNodeBindingType m_type = ZigbeeNode::ZigbeeNodeBindingTypeDevice;
|
||||
quint16 m_groupAddress = 0;
|
||||
QString m_destinationAddress;
|
||||
quint8 m_destinationEndpointId = 0;
|
||||
};
|
||||
|
||||
class ZigbeeCluster: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint16 clusterId READ clusterId CONSTANT)
|
||||
Q_PROPERTY(ZigbeeClusterDirection direction READ direction CONSTANT)
|
||||
public:
|
||||
enum ZigbeeClusterDirection {
|
||||
ZigbeeClusterDirectionServer,
|
||||
ZigbeeClusterDirectionClient
|
||||
};
|
||||
Q_ENUM(ZigbeeClusterDirection)
|
||||
|
||||
enum ZigbeeClusterId {
|
||||
// Basics
|
||||
ZigbeeClusterIdUnknown = 0xffff,
|
||||
ZigbeeClusterIdBasic = 0x0000,
|
||||
ZigbeeClusterIdPowerConfiguration = 0x0001,
|
||||
ZigbeeClusterIdDeviceTemperature = 0x0002,
|
||||
ZigbeeClusterIdIdentify = 0x0003,
|
||||
ZigbeeClusterIdGroups = 0x0004,
|
||||
ZigbeeClusterIdScenes = 0x0005,
|
||||
ZigbeeClusterIdOnOff = 0x0006,
|
||||
ZigbeeClusterIdOnOffCOnfiguration = 0x0007,
|
||||
ZigbeeClusterIdLevelControl = 0x0008,
|
||||
ZigbeeClusterIdAlarms = 0x0009,
|
||||
ZigbeeClusterIdTime = 0x000A,
|
||||
ZigbeeClusterIdRssiLocation = 0x000B,
|
||||
ZigbeeClusterIdAnalogInput = 0x000C,
|
||||
ZigbeeClusterIdAnalogOutput = 0x000D,
|
||||
ZigbeeClusterIdAnalogValue = 0x000E,
|
||||
ZigbeeClusterIdBinaryInput = 0x000F,
|
||||
ZigbeeClusterIdBinaryOutput = 0x0010,
|
||||
ZigbeeClusterIdBinaryValue = 0x0011,
|
||||
ZigbeeClusterIdMultistateInput = 0x0012,
|
||||
ZigbeeClusterIdMultistateOutput = 0x0013,
|
||||
ZigbeeClusterIdMultistateValue = 0x0014,
|
||||
ZigbeeClusterIdCommissoning = 0x0015,
|
||||
|
||||
// Over the air uppgrade (OTA)
|
||||
ZigbeeClusterIdOtaUpgrade = 0x0019,
|
||||
|
||||
// Poll controll
|
||||
ZigbeeClusterIdPollControl = 0x0020,
|
||||
|
||||
|
||||
// Closures
|
||||
ZigbeeClusterIdShadeConfiguration = 0x0100,
|
||||
|
||||
// Door Lock
|
||||
ZigbeeClusterIdDoorLock = 0x0101,
|
||||
|
||||
// Heating, Ventilation and Air-Conditioning (HVAC)
|
||||
ZigbeeClusterIdPumpConfigurationControl = 0x0200,
|
||||
ZigbeeClusterIdThermostat = 0x0201,
|
||||
ZigbeeClusterIdFanControll = 0x0202,
|
||||
ZigbeeClusterIdDehumiditationControl = 0x0203,
|
||||
ZigbeeClusterIdThermostatUserControl = 0x0204,
|
||||
|
||||
// Lighting
|
||||
ZigbeeClusterIdColorControl = 0x0300,
|
||||
ZigbeeClusterIdBallastConfiguration = 0x0301,
|
||||
|
||||
// Sensing
|
||||
ZigbeeClusterIdIlluminanceMeasurement = 0x0400,
|
||||
ZigbeeClusterIdIlluminanceLevelSensing = 0x0401,
|
||||
ZigbeeClusterIdTemperatureMeasurement = 0x0402,
|
||||
ZigbeeClusterIdPressureMeasurement = 0x0403,
|
||||
ZigbeeClusterIdFlowMeasurement = 0x0404,
|
||||
ZigbeeClusterIdRelativeHumidityMeasurement = 0x0405,
|
||||
ZigbeeClusterIdOccupancySensing = 0x0406,
|
||||
|
||||
// Security and Safty
|
||||
ZigbeeClusterIdIasZone = 0x0500,
|
||||
ZigbeeClusterIdIasAce = 0x0501,
|
||||
ZigbeeClusterIdIasWd = 0x0502,
|
||||
|
||||
// Smart energy
|
||||
ZigbeeClusterIdPrice = 0x0700,
|
||||
ZigbeeClusterIdDemandResponseAndLoadControl = 0x0701,
|
||||
ZigbeeClusterIdMetering = 0x0702,
|
||||
ZigbeeClusterIdMessaging = 0x0703,
|
||||
ZigbeeClusterIdTunneling = 0x0704,
|
||||
ZigbeeClusterIdKeyEstablishment = 0x0800,
|
||||
|
||||
// ZLL
|
||||
ZigbeeClusterIdTouchlinkCommissioning = 0x1000,
|
||||
|
||||
// NXP Appliances
|
||||
ZigbeeClusterIdApplianceControl = 0x001B,
|
||||
ZigbeeClusterIdApplianceIdentification = 0x0B00,
|
||||
ZigbeeClusterIdApplianceEventsAlerts = 0x0B02,
|
||||
ZigbeeClusterIdApplianceStatistics = 0x0B03,
|
||||
|
||||
// Electrical Measurement
|
||||
ZigbeeClusterIdElectricalMeasurement = 0x0B04,
|
||||
ZigbeeClusterIdDiagnostics = 0x0B05,
|
||||
|
||||
// Zigbee green power
|
||||
ZigbeeClusterIdGreenPower = 0x0021,
|
||||
|
||||
// Manufacturer specific
|
||||
ZigbeeClusterIdManufacturerSpecificPhilips = 0xfc00,
|
||||
|
||||
};
|
||||
Q_ENUM(ZigbeeClusterId)
|
||||
|
||||
|
||||
ZigbeeCluster(quint16 clusterId, ZigbeeClusterDirection direction, QObject *parent = nullptr);
|
||||
|
||||
quint16 clusterId() const;
|
||||
ZigbeeClusterDirection direction() const;
|
||||
|
||||
Q_INVOKABLE QString clusterName() const;
|
||||
|
||||
private:
|
||||
quint16 m_clusterId = 0;
|
||||
ZigbeeClusterDirection m_direction;
|
||||
};
|
||||
|
||||
class ZigbeeNodeEndpoint: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint8 endpointId READ endpointId CONSTANT)
|
||||
Q_PROPERTY(QList<ZigbeeCluster*> inputClusters READ inputClusters NOTIFY inputClustersChanged)
|
||||
Q_PROPERTY(QList<ZigbeeCluster*> outputClusters READ outputClusters NOTIFY outputClustersChanged)
|
||||
public:
|
||||
ZigbeeNodeEndpoint(quint8 endpointId, const QList<ZigbeeCluster*> &inputClusters = QList<ZigbeeCluster*>(), const QList<ZigbeeCluster*> &outputClusters = QList<ZigbeeCluster*>(), QObject *parent = nullptr);
|
||||
|
||||
quint8 endpointId() const;
|
||||
QList<ZigbeeCluster*> inputClusters() const;
|
||||
Q_INVOKABLE ZigbeeCluster *getInputCluster(quint16 clusterId) const;
|
||||
void addInputCluster(ZigbeeCluster *cluster);
|
||||
|
||||
QList<ZigbeeCluster*> outputClusters() const;
|
||||
Q_INVOKABLE ZigbeeCluster *getOutputCluster(quint16 clusterId) const;
|
||||
void addOutputCluster(ZigbeeCluster *cluster);
|
||||
|
||||
signals:
|
||||
void inputClustersChanged();
|
||||
void outputClustersChanged();
|
||||
|
||||
private:
|
||||
quint8 m_endpointId = 0;
|
||||
QList<ZigbeeCluster*> m_inputClusters;
|
||||
QList<ZigbeeCluster*> m_outputClusters;
|
||||
};
|
||||
|
||||
#endif // ZIGBEENODE_H
|
||||
|
||||
@ -277,5 +277,6 @@
|
||||
<file>ui/system/zwave/ZWaveNetworkPage.qml</file>
|
||||
<file>ui/system/zwave/ZWaveNetworkSettingsPage.qml</file>
|
||||
<file>ui/components/ActivityIndicator.qml</file>
|
||||
<file>ui/system/zigbee/ZigbeeNodePage.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -157,17 +157,17 @@ SettingsPageBase {
|
||||
d.pendingCommandId = -1
|
||||
var props = {};
|
||||
switch (error) {
|
||||
case "ZigbeeErrorNoError":
|
||||
case ZigbeeManager.ZigbeeErrorNoError:
|
||||
root.done()
|
||||
return;
|
||||
case "ZigbeeErrorAdapterNotAvailable":
|
||||
case ZigbeeManager.ZigbeeErrorAdapterNotAvailable:
|
||||
props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect.");
|
||||
break;
|
||||
case "ZigbeeErrorAdapterAlreadyInUse":
|
||||
case ZigbeeManager.ZigbeeErrorAdapterAlreadyInUse:
|
||||
props.text = qsTr("The selected adapter is already in use.");
|
||||
break;
|
||||
default:
|
||||
props.errorCode = error;
|
||||
props.error = error;
|
||||
}
|
||||
var comp = Qt.createComponent("/ui/components/ErrorDialog.qml")
|
||||
var popup = comp.createObject(app, props)
|
||||
|
||||
@ -85,16 +85,16 @@ SettingsPageBase {
|
||||
d.pendingCommandId = -1
|
||||
var props = {};
|
||||
switch (error) {
|
||||
case "ZigbeeErrorNoError":
|
||||
case ZigbeeManager.ZigbeeErrorNoError:
|
||||
return;
|
||||
case "ZigbeeErrorAdapterNotAvailable":
|
||||
case ZigbeeManager.ZigbeeErrorAdapterNotAvailable:
|
||||
props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect.");
|
||||
break;
|
||||
case "ZigbeeErrorAdapterAlreadyInUse":
|
||||
case ZigbeeManager.ZigbeeErrorAdapterAlreadyInUse:
|
||||
props.text = qsTr("The selected adapter is already in use.");
|
||||
break;
|
||||
default:
|
||||
props.errorCode = error;
|
||||
props.error = error;
|
||||
}
|
||||
var comp = Qt.createComponent("/ui/components/ErrorDialog.qml")
|
||||
var popup = comp.createObject(app, props)
|
||||
@ -355,8 +355,9 @@ SettingsPageBase {
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
var popup = nodeInfoComponent.createObject(app, {node: node, nodeThings: nodeThings})
|
||||
popup.open()
|
||||
pageStack.push("ZigbeeNodePage.qml", {zigbeeManager: zigbeeManager, network: network, node: node})
|
||||
// var popup = nodeInfoComponent.createObject(app, {node: node, nodeThings: nodeThings})
|
||||
// popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
502
nymea-app/ui/system/zigbee/ZigbeeNodePage.qml
Normal file
502
nymea-app/ui/system/zigbee/ZigbeeNodePage.qml
Normal file
@ -0,0 +1,502 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 ZigbeeManager zigbeeManager: null
|
||||
property ZigbeeNetwork network: null
|
||||
property ZigbeeNode node: null
|
||||
|
||||
header: NymeaHeader {
|
||||
text: qsTr("ZigBee node info")
|
||||
backButtonVisible: true
|
||||
onBackPressed: pageStack.pop()
|
||||
|
||||
HeaderButton {
|
||||
imageSource: "/ui/images/delete.svg"
|
||||
text: qsTr("Remove node")
|
||||
onClicked: {
|
||||
var popup = removeZigbeeNodeDialogComponent.createObject(app)
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
busy: d.pendingCommandId != -1
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
property int pendingCommandId: -1
|
||||
function removeNode(networkUuid, ieeeAddress) {
|
||||
d.pendingCommandId = root.zigbeeManager.removeNode(networkUuid, ieeeAddress)
|
||||
}
|
||||
|
||||
onPendingCommandIdChanged: {
|
||||
print("pendingCommandId changed", pendingCommandId, wakeupDialog)
|
||||
if (pendingCommandId == -1 && wakeupDialog != null) {
|
||||
wakeupDialog.close();
|
||||
wakeupDialog.destroy();
|
||||
wakeupDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
property var wakeupDialog: null
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.zigbeeManager
|
||||
onCreateBindingReply: {
|
||||
print("**** create binding reply", error)
|
||||
if (commandId == d.pendingCommandId) {
|
||||
d.pendingCommandId = -1
|
||||
var props = {};
|
||||
switch (error) {
|
||||
case ZigbeeManager.ZigbeeErrorNoError:
|
||||
return;
|
||||
case ZigbeeManager.ZigbeeErrorNetworkError:
|
||||
props.text = qsTr("An error happened in the ZigBee network when creating the binding.");
|
||||
break;
|
||||
case ZigbeeManager.ZigbeeErrorTimeoutError:
|
||||
props.text = qsTr("The ZigBee device did not respond. Please try again.");
|
||||
break;
|
||||
default:
|
||||
props.error = error;
|
||||
}
|
||||
var comp = Qt.createComponent("/ui/components/ErrorDialog.qml")
|
||||
var popup = comp.createObject(app, props)
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
onRemoveBindingReply: {
|
||||
if (commandId == d.pendingCommandId) {
|
||||
d.pendingCommandId = -1
|
||||
|
||||
var props = {};
|
||||
switch (error) {
|
||||
case ZigbeeManager.ZigbeeErrorNoError:
|
||||
return;
|
||||
case ZigbeeManager.ZigbeeErrorNetworkError:
|
||||
props.text = qsTr("An error happened in the ZigBee network when removing the binding.");
|
||||
break;
|
||||
case ZigbeeManager.ZigbeeErrorTimeoutError:
|
||||
props.text = qsTr("The ZigBee device did not respond. Please try again.");
|
||||
break;
|
||||
default:
|
||||
props.error = error;
|
||||
}
|
||||
var comp = Qt.createComponent("/ui/components/ErrorDialog.qml")
|
||||
var popup = comp.createObject(app, props)
|
||||
popup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThingsProxy {
|
||||
id: nodeThings
|
||||
engine: _engine
|
||||
paramsFilter: {"ieeeAddress": root.node.ieeeAddress}
|
||||
}
|
||||
|
||||
property int signalStrength: node ? Math.round(node.lqi * 100.0 / 255.0) : 0
|
||||
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: root.node.model
|
||||
subText: qsTr("Model")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
}
|
||||
NymeaItemDelegate {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
Layout.fillWidth: true
|
||||
text: root.node.manufacturer
|
||||
subText: qsTr("Manufacturer")
|
||||
}
|
||||
|
||||
NymeaItemDelegate {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
Layout.fillWidth: true
|
||||
text: root.node.ieeeAddress
|
||||
subText: qsTr("IEEE address")
|
||||
font: Style.smallFont
|
||||
}
|
||||
NymeaItemDelegate {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
Layout.fillWidth: true
|
||||
text: "0x" + root.node.networkAddress.toString(16)
|
||||
subText: qsTr("Network address")
|
||||
}
|
||||
NymeaItemDelegate {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
Layout.fillWidth: true
|
||||
text: (root.node.lqi * 100 / 255).toFixed(0) + " %"
|
||||
subText: qsTr("Signal strength")
|
||||
}
|
||||
NymeaItemDelegate {
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
Layout.fillWidth: true
|
||||
text: root.node.version.length > 0 ? root.node.version : qsTr("Unknown")
|
||||
subText: qsTr("Version")
|
||||
}
|
||||
|
||||
// NymeaItemDelegate {
|
||||
// Layout.fillWidth: true
|
||||
// text: qsTr("Device endpoints")
|
||||
// subText: qsTr("Show detailed information about the node")
|
||||
// onClicked: pageStack.push(endpointsPageComponent)
|
||||
// }
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Associated things")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: nodeThings
|
||||
delegate: NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
property Thing thing: nodeThings.get(index)
|
||||
iconName: app.interfacesToIcon(thing.thingClass.interfaces)
|
||||
text: thing.name
|
||||
onClicked: pageStack.push("/ui/thingconfiguration/ConfigureThingPage.qml", {thing: thing})
|
||||
}
|
||||
|
||||
// delegate: RowLayout {
|
||||
// id: thingDelegate
|
||||
// Layout.leftMargin: Style.margins
|
||||
// Layout.rightMargin: Style.margins
|
||||
// property Thing thing: 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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
SettingsPageSectionHeader {
|
||||
text: qsTr("Bindings")
|
||||
}
|
||||
|
||||
property ZigbeeNode coordinatorNode: root.network.nodes.getNodeByNetworkAddress(0)
|
||||
Repeater {
|
||||
model: root.node.bindings
|
||||
delegate: NymeaSwipeDelegate {
|
||||
id: bindingDelegate
|
||||
Layout.fillWidth: true
|
||||
property ZigbeeNodeBinding binding: root.node.bindings[index]
|
||||
ThingsProxy {
|
||||
id: destinationThings
|
||||
engine: _engine
|
||||
paramsFilter: {"ieeeAddress": bindingDelegate.binding.destinationAddress}
|
||||
}
|
||||
|
||||
canDelete: true
|
||||
progressive: false
|
||||
text: {
|
||||
if (destinationThings.count > 0) {
|
||||
return destinationThings.get(0).name
|
||||
}
|
||||
if (binding.destinationAddress != "") {
|
||||
if (binding.destinationAddress == coordinatorNode.ieeeAddress) {
|
||||
return Configuration.systemName
|
||||
}
|
||||
return binding.destinationAddress
|
||||
}
|
||||
return qsTr("Group: 0x%1").arg(NymeaUtils.pad(binding.groupAddress.toString(16), 4))
|
||||
}
|
||||
property ZigbeeNodeEndpoint endpoint: root.node.getEndpoint(binding.sourceEndpointId);
|
||||
property ZigbeeCluster inputCluster: endpoint ? endpoint.getInputCluster(binding.clusterId) : null
|
||||
property ZigbeeCluster outputCluster: endpoint ? endpoint.getOutputCluster(binding.clusterId) : null
|
||||
property ZigbeeCluster usedCluster: inputCluster ? inputCluster : outputCluster
|
||||
subText: {
|
||||
var ret = usedCluster.clusterName();
|
||||
if (binding.destinationAddress != "") {
|
||||
ret += " (" + binding.sourceEndpointId + " -> " + binding.destinationEndpointId + ")"
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
onDeleteClicked: {
|
||||
if (!node.rxOnWhenIdle) {
|
||||
d.wakeupDialog = wakeupDialogComponent.createObject(root)
|
||||
d.wakeupDialog.open()
|
||||
}
|
||||
d.pendingCommandId = zigbeeManager.removeBinding(network.networkUuid, binding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.margins
|
||||
Layout.rightMargin: Style.margins
|
||||
text: qsTr("Add binding")
|
||||
onClicked: {
|
||||
var dialog = addBindingComponent.createObject(root)
|
||||
dialog.open()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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: endpointsPageComponent
|
||||
SettingsPageBase {
|
||||
title: qsTr("Device endpoints")
|
||||
|
||||
Repeater {
|
||||
model: root.node.endpoints
|
||||
delegate: ColumnLayout {
|
||||
id: endpointDelegate
|
||||
property ZigbeeNodeEndpoint endpoint: root.node.endpoints[index]
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "- " + qsTr("Endpoint %1").arg(endpointDelegate.endpoint.endpointId)
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: " " + qsTr("Input clusters")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: endpointDelegate.endpoint.inputClusters
|
||||
delegate: Label {
|
||||
Layout.fillWidth: true
|
||||
property ZigbeeCluster cluster: endpointDelegate.endpoint.inputClusters[index]
|
||||
text: " - " + cluster.clusterName() + " (" + (cluster.direction == ZigbeeCluster.ZigbeeClusterDirectionClient ? qsTr("Client") : qsTr("Server")) + ")"
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: " " + qsTr("Output clusters")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: endpointDelegate.endpoint.outputClusters
|
||||
delegate: Label {
|
||||
Layout.fillWidth: true
|
||||
property ZigbeeCluster cluster: endpointDelegate.endpoint.outputClusters[index]
|
||||
text: " - " + cluster.clusterName() + " (" + (cluster.direction == ZigbeeCluster.ZigbeeClusterDirectionClient ? qsTr("Client") : qsTr("Server")) + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: addBindingComponent
|
||||
MeaDialog {
|
||||
title: qsTr("Add binding")
|
||||
|
||||
Label {
|
||||
text: qsTr("Source endpoint")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
Layout.fillWidth: true
|
||||
id: sourceEndpointComboBox
|
||||
model: root.node.endpoints
|
||||
textRole: "endpointId"
|
||||
displayText: currentEndpoint.endpointId
|
||||
property ZigbeeNodeEndpoint currentEndpoint: root.node.endpoints[currentIndex]
|
||||
onCurrentEndpointChanged: print("source endpoint changed", currentEndpoint.endpointId)
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Target node")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: destinationNodeComboBox
|
||||
Layout.fillWidth: true
|
||||
model: network.nodes
|
||||
displayText: currentDestinationNodeThings.count > 0
|
||||
? currentDestinationNodeThings.get(0).name
|
||||
: currentNode == root.coordinatorNode
|
||||
? Configuration.systemName
|
||||
: currentNode.model
|
||||
property ZigbeeNode currentNode: network.nodes.get(currentIndex)
|
||||
ThingsProxy {
|
||||
id: currentDestinationNodeThings
|
||||
engine: _engine
|
||||
paramsFilter: {"ieeeAddress": destinationNodeDelegate.node.ieeeAddress}
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: destinationNodeDelegate
|
||||
property ZigbeeNode node: network.nodes.get(index)
|
||||
width: parent.width
|
||||
text: destinationNodeThings.count > 0
|
||||
? destinationNodeThings.get(0).name
|
||||
: node == root.coordinatorNode
|
||||
? Configuration.systemName
|
||||
: node.model
|
||||
ThingsProxy {
|
||||
id: destinationNodeThings
|
||||
engine: _engine
|
||||
paramsFilter: {"ieeeAddress": destinationNodeDelegate.node.ieeeAddress}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Destination endpoint")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ComboBox {
|
||||
id: destinationEndpointComboBox
|
||||
Layout.fillWidth: true
|
||||
model: destinationNodeComboBox.currentNode.endpoints
|
||||
textRole: "endpointId"
|
||||
displayText: currentEndpoint.endpointId
|
||||
property ZigbeeNodeEndpoint currentEndpoint: destinationNodeComboBox.currentNode.endpoints[currentIndex]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Cluster")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: clusterComboBox
|
||||
Layout.fillWidth: true
|
||||
delegate: ItemDelegate {
|
||||
width: parent.width
|
||||
text: modelData.clusterName()
|
||||
}
|
||||
model: {
|
||||
var ret = []
|
||||
print("updating clusters", sourceEndpointComboBox.currentEndpoint, destinationNodeComboBox.currentNode, destinationEndpointComboBox.currentEndpoint)
|
||||
if (!sourceEndpointComboBox.currentEndpoint || !destinationNodeComboBox.currentNode || !destinationEndpointComboBox.currentEndpoint) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (var i = 0; i < sourceEndpointComboBox.currentEndpoint.outputClusters.length; i++) {
|
||||
var outputCluster = sourceEndpointComboBox.currentEndpoint.outputClusters[i]
|
||||
print("source has cluster", outputCluster.clusterId)
|
||||
for (var j = 0; j < destinationEndpointComboBox.currentEndpoint.inputClusters.length; j++) {
|
||||
var inputCluster = destinationEndpointComboBox.currentEndpoint.inputClusters[j]
|
||||
print("destination has cluster", inputCluster.clusterId)
|
||||
if (inputCluster.clusterId === outputCluster.clusterId && inputCluster.direction !== outputCluster.direction) {
|
||||
ret.push(outputCluster);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < sourceEndpointComboBox.currentEndpoint.inputClusters.length; i++) {
|
||||
var inputCluster = sourceEndpointComboBox.currentEndpoint.inputClusters[i]
|
||||
print("source has cluster", inputCluster.clusterId)
|
||||
for (var j = 0; j < destinationEndpointComboBox.currentEndpoint.outputClusters.length; j++) {
|
||||
var outputCluster = destinationEndpointComboBox.currentEndpoint.outputClusters[j]
|
||||
print("destination has cluster", outputCluster.clusterId)
|
||||
if (inputCluster.clusterId === outputCluster.clusterId && inputCluster.direction !== outputCluster.direction) {
|
||||
ret.push(inputCluster);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
property ZigbeeCluster currentCluster: currentValue
|
||||
displayText: currentValue.clusterName()
|
||||
}
|
||||
|
||||
|
||||
onAccepted: {
|
||||
d.pendingCommandId = root.zigbeeManager.createBinding(
|
||||
root.network.networkUuid,
|
||||
root.node.ieeeAddress,
|
||||
sourceEndpointComboBox.currentEndpoint.endpointId,
|
||||
clusterComboBox.currentCluster.clusterId,
|
||||
destinationNodeComboBox.currentNode.ieeeAddress,
|
||||
destinationEndpointComboBox.currentEndpoint.endpointId)
|
||||
|
||||
if (!root.node.rxOnWhenIdle) {
|
||||
d.wakeupDialog = wakeupDialogComponent.createObject(root)
|
||||
d.wakeupDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: wakeupDialogComponent
|
||||
MeaDialog {
|
||||
id: wakeupDialog
|
||||
title: qsTr("Wake up %1").arg(root.node.model)
|
||||
text: qsTr("The selected node is a sleepy device. Please wake up the device by pressing a button.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,23 +121,26 @@ SettingsPageBase {
|
||||
text: qsTr("Information")
|
||||
}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Vendor:")
|
||||
subText: engine.thingManager.vendors.getVendor(root.thing.thingClass.vendorId).displayName
|
||||
text: engine.thingManager.vendors.getVendor(root.thing.thingClass.vendorId).displayName
|
||||
subText: qsTr("Vendor")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
}
|
||||
NymeaSwipeDelegate {
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Type:")
|
||||
subText: root.thing.thingClass.displayName
|
||||
text: root.thing.thingClass.displayName
|
||||
subText: qsTr("Type")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("ID:")
|
||||
subText: root.thing.id.toString().replace(/[{}]/g, "")
|
||||
text: root.thing.id.toString().replace(/[{}]/g, "")
|
||||
subText: qsTr("ID")
|
||||
prominentSubText: false
|
||||
progressive: false
|
||||
onClicked: {
|
||||
PlatformHelper.toClipBoard(root.thing.id.toString().replace(/[{}]/g, ""));
|
||||
@ -145,7 +148,7 @@ SettingsPageBase {
|
||||
}
|
||||
}
|
||||
|
||||
NymeaSwipeDelegate {
|
||||
NymeaItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Thing class")
|
||||
subText: qsTr("View the type definition for this thing")
|
||||
|
||||
Reference in New Issue
Block a user