add Association Management Commands (#14)

* Add AddAssociation and RemoveAssociation Commands - This completes the Association Functionality
This commit is contained in:
Justin Hammond 2020-01-09 11:36:48 +08:00 committed by GitHub
parent 78544d6fc2
commit 47f4b61364
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 292 additions and 4 deletions

View file

@ -1177,3 +1177,56 @@ This allows a MQTT Client to set a value on a Device. As OZW supports many diffe
[testNetwork](#testNetwork)
## addAssociation
**Params**:
"node" - uint8: The NodeID to test
"group" - uint8: The Association Group you wish to add a member to
"target" - string: The Target Node you wish to send notifications to for this group.
**Returns**:
"addAssociation" - if OZW accepted the command
**Notification**:
topic "nodeGroupChanged" Indicates the Memberships was successfully refreshed from the device
**Notes**:
Note - The Target string can be specified as a single NodeID (eg, "1" or "1.0") or a NodeID and Instance as "<nodeid>.<instance>". This allows you to configure a device to send a notification to a different instance on Multi Channel Devices. You should ensure that the target device has a valid instance and supports the commands it will recieve.
If you specify a instance (other than 0) for a target node, but the group does not support sending MultiChannel messages, the instance will be removed (or set to 0, which indicates the root node).
**See Also**:
[removeAssociation](#removeAssociation)
## removeAssociation
**Params**:
"node" - uint8: The NodeID to test
"group" - uint8: The Association Group you wish to add a member to
"target" - string: The Target Node you wish remove from the Group Membership List.
**Returns**:
"removeAssociation" - if OZW accepted the command
**Notification**:
topic "nodeGroupChanged" Indicates the Memberships was successfully refreshed from the device
**Notes**:
Note - The Target string can be specified as a single NodeID (eg, "1" or "1.0") or a NodeID and Instance as "<nodeid>.<instance>". You should first check the membership list via the Association Topics for the device and only attempt to remove existing members. Attempting to remove a member that does exist will result in a error.
**See Also**:
[addAssociation](#addAssociation)

View file

@ -63,6 +63,10 @@ public:
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
#if 0
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
#endif
protected:
QVariant getassocationData(quint8 _node, quint8 _groupIDX, QTOZW_Associations::associationColumns _column);

View file

@ -141,6 +141,8 @@ public:
bool refreshValue(quint64);
bool AddAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target);
bool RemoveAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target);
/* Property Methods */
QDir OZWDatabasePath() { return this->m_ozwdatabasepath; }

View file

@ -149,6 +149,11 @@ class QTOZWManager {
SLOT(QString getControllerPath())
SLOT(bool refreshValue(quint64))
SLOT(bool AddAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target))
SLOT(bool RemoveAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target))
SLOT(qint32 getPollInterval())
SLOT(void setPollInterval(qint32 interval, bool intervalBetweenPolls))

View file

@ -135,6 +135,10 @@ public Q_SLOTS:
bool refreshValue(quint64);
bool AddAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target);
bool RemoveAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target);
/* these slots are called from our OZWNotification Class. Applications should not call them */
void pvt_valueAdded(quint64 vidKey);
void pvt_valueRemoved(quint64 vidKey);

View file

@ -99,7 +99,41 @@ QVariant QTOZW_Associations::headerData(int section, Qt::Orientation orientation
}
return QVariant();
}
#if 0
bool QTOZW_Associations::setData(const QModelIndex &index, const QVariant &value, int role) {
if (role != Qt::EditRole) {
return false;
}
switch (static_cast<associationColumns>(index.column())) {
case Members:
// if (this->m_valueData.at(index.row())[static_cast<ValueIdColumns>(index.column())] != value) {
// qCDebug(valueModel) << "setData Called for Row" << index.row() << " With Value" << value;
// this->m_valueData[index.row()][static_cast<ValueIdColumns>(index.column())] = value;
// QVector<int> roles;
// roles << Qt::DisplayRole << QTOZW_UserRoles::ModelDataChanged;
// this->dataChanged(index, index, roles);
// }
break;
default:
qCWarning(valueModel) << "got a setData for a Column other than Value. Ignoring" << index.column();
return false;
}
return true;
}
Qt::ItemFlags QTOZW_Associations::flags(const QModelIndex &index) const {
if (!index.isValid())
return Qt::NoItemFlags;
switch (static_cast<associationColumns>(index.column())) {
case Members: {
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}
break;
default:
return QAbstractTableModel::flags(index);
}
}
#endif
QVariant QTOZW_Associations::getassocationData(quint8 _node, quint8 _groupIDX, associationColumns _column) {
int32_t row = this->getassocationRow(_node, _groupIDX);
if (row >= 0)

View file

@ -453,6 +453,13 @@ void QTOZWManager::syncroniseNodeNeighbors(quint8 node) {
bool QTOZWManager::refreshValue(quint64 vidKey) {
CALL_DPTR_RTN(refreshValue(vidKey), bool);
}
bool QTOZWManager::AddAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target) {
CALL_DPTR_RTN(AddAssociation(_nodeId, _groupIdx, target), bool);
}
bool QTOZWManager::RemoveAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target) {
CALL_DPTR_RTN(RemoveAssociation(_nodeId, _groupIdx, target), bool);
}
void QTOZWManager::setOZWDatabasePath(QDir path) {
if (path.exists())

View file

@ -841,6 +841,105 @@ bool QTOZWManager_Internal::refreshValue(quint64 vidKey) {
return false;
}
bool QTOZWManager_Internal::AddAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target) {
if (!this->checkHomeId())
return false;
if (!this->checkNodeId(_nodeId))
return false;
quint8 targetnode = 0;
quint8 targetinstance = 0;
QRegExp rx("^(\\d+)\\.(\\d+)$");
if (target.contains(rx)) {
targetnode = rx.cap(1).toInt();
targetinstance = rx.cap(2).toInt();
qCDebug(manager) << "Got a AddAssociation with a instance value" << target << " node: " << targetnode << " instance: " << targetinstance;
}
rx.setPattern("^(\\d+)$");
if (target.contains(rx)) {
targetnode = rx.cap(1).toInt();
targetinstance = 0;
qCDebug(manager) << "Got a AddAssociation with no instance value" << target << " node: " << targetnode;
}
if (targetnode == 0) {
qCWarning(manager) << "Couldn't find a valid Target in AddAssociation: " << target;
return false;
}
if (targetinstance > 0) {
try {
if (!this->m_manager->IsMultiInstance(this->homeId(), _nodeId, _groupIdx)) {
qCWarning(manager) << "AddAssociation: Association Group " << _groupIdx << "for node " << _nodeId << "does not support instances but a instance was specified:" << target << ". Removing";
targetinstance = 0;
}
} catch (OpenZWave::OZWException &e) {
emit this->error(QTOZWManagerErrorCodes::OZWException);
this->setErrorString(e.GetMsg().c_str());
return false;
}
}
if (this->m_associationsModel->findAssociation(_nodeId, _groupIdx, targetnode, targetinstance)) {
qCWarning(manager) << "AddAssociation: Association Group " << _groupIdx << "for node " << _nodeId << "already has a Assocation to " << target;
return false;
}
try {
this->m_manager->AddAssociation(this->homeId(), _nodeId, _groupIdx, targetnode, targetinstance);
return true;
} catch (OpenZWave::OZWException &e) {
emit this->error(QTOZWManagerErrorCodes::OZWException);
this->setErrorString(e.GetMsg().c_str());
}
return false;
}
bool QTOZWManager_Internal::RemoveAssociation (quint8 const _nodeId, quint8 const _groupIdx, QString const target) {
if (!this->checkHomeId())
return false;
if (!this->checkNodeId(_nodeId))
return false;
quint8 targetnode = 0;
quint8 targetinstance = 0;
QRegExp rx("^(\\d+)\\.(\\d+)$");
if (target.contains(rx)) {
targetnode = rx.cap(1).toInt();
targetinstance = rx.cap(2).toInt();
}
rx.setPattern("^(\\d+)$");
if (target.contains(rx)) {
targetnode = rx.cap(1).toInt();
targetinstance = 0;
}
if (targetnode == 0) {
qCWarning(manager) << "Couldn't find a valid Target in RemoveAssocation: " << target;
return false;
}
if (targetinstance > 0) {
try {
if (!this->m_manager->IsMultiInstance(this->homeId(), _nodeId, _groupIdx)) {
qCWarning(manager) << "RemoveAssoication: Association Group " << _groupIdx << "for node " << _nodeId << "does not support instances but a instance was specified:" << target << ". Removing";
targetinstance = 0;
}
} catch (OpenZWave::OZWException &e) {
emit this->error(QTOZWManagerErrorCodes::OZWException);
this->setErrorString(e.GetMsg().c_str());
return false;
}
}
if (!this->m_associationsModel->findAssociation(_nodeId, _groupIdx, targetnode, targetinstance)) {
qCWarning(manager) << "RemoveAssociation: Association Group " << _groupIdx << "for node " << _nodeId << "does not have a Assocation to " << target;
return false;
}
try {
this->m_manager->RemoveAssociation(this->homeId(), _nodeId, _groupIdx, targetnode, targetinstance);
return true;
} catch (OpenZWave::OZWException &e) {
emit this->error(QTOZWManagerErrorCodes::OZWException);
this->setErrorString(e.GetMsg().c_str());
}
return false;
}
QTOZW_Nodes *QTOZWManager_Internal::getNodeModel() {

View file

@ -1,4 +1,3 @@
#include "qt-openzwave/qtozwmanager.h"
#include "mqttAssociations.h"
#include "qtrj.h"

View file

@ -0,0 +1,19 @@
#include "mqttcommands/addAssociation.h"
MqttCommand_AddAssociation::MqttCommand_AddAssociation(QObject *parent) :
MqttCommand(parent)
{
this->m_requiredIntFields << "node" << "group";
this->m_requiredStringFields << "target";
}
MqttCommand* MqttCommand_AddAssociation::Create(QObject *parent) {
return new MqttCommand_AddAssociation(parent);
}
bool MqttCommand_AddAssociation::processMessage(rapidjson::Document &msg) {
if (!this->checkNode(msg, "node")) {
return this->sendSimpleStatus(false, "Invalid Node Number");
}
QTOZWManager *mgr = getOZWManager();
return this->sendSimpleStatus(mgr->AddAssociation(msg["node"].GetUint(), msg["group"].GetUint(), msg["target"].GetString()));
}

View file

@ -0,0 +1,17 @@
#ifndef ADDASSOCIATION_H
#define ADDASSOCIATION_H
#include "mqttcommands/mqttcommands.h"
class MqttCommand_AddAssociation : public MqttCommand {
Q_OBJECT
public:
static MqttCommand *Create(QObject *parent = nullptr);
static QString StaticGetCommand() { return "AddAssociation";};
QString GetCommand() override { return StaticGetCommand(); };
bool processMessage(rapidjson::Document &) override;
private:
MqttCommand_AddAssociation(QObject *parent = nullptr);
};
#endif // PING_H

View file

@ -38,6 +38,8 @@
#include "mqttcommands/syncroniseNodeNeighbors.h"
#include "mqttcommands/enablePoll.h"
#include "mqttcommands/refreshValue.h"
#include "mqttcommands/addAssociation.h"
#include "mqttcommands/removeAssociation.h"
Q_LOGGING_CATEGORY(ozwmc, "ozw.mqtt.commands");
@ -251,6 +253,8 @@ void MqttCommands::setupCommands() {
this->Register(MqttCommand_SyncroniseNodeNeighbors::StaticGetCommand(), &MqttCommand_SyncroniseNodeNeighbors::Create);
// this->Register(MqttCommand_EnablePoll::StaticGetCommand(), &MqttCommand_EnablePoll::Create);
this->Register(MqttCommand_RefreshValue::StaticGetCommand(), &MqttCommand_RefreshValue::Create);
this->Register(MqttCommand_AddAssociation::StaticGetCommand(), &MqttCommand_AddAssociation::Create);
this->Register(MqttCommand_RemoveAssociation::StaticGetCommand(), &MqttCommand_RemoveAssociation::Create);
}
void MqttCommands::setupSubscriptions(QMqttClient *mqttclient, QString topTopic) {

View file

@ -0,0 +1,19 @@
#include "mqttcommands/removeAssociation.h"
MqttCommand_RemoveAssociation::MqttCommand_RemoveAssociation(QObject *parent) :
MqttCommand(parent)
{
this->m_requiredIntFields << "node" << "group";
this->m_requiredStringFields << "target";
}
MqttCommand* MqttCommand_RemoveAssociation::Create(QObject *parent) {
return new MqttCommand_RemoveAssociation(parent);
}
bool MqttCommand_RemoveAssociation::processMessage(rapidjson::Document &msg) {
if (!this->checkNode(msg, "node")) {
return this->sendSimpleStatus(false, "Invalid Node Number");
}
QTOZWManager *mgr = getOZWManager();
return this->sendSimpleStatus(mgr->RemoveAssociation(msg["node"].GetUint(), msg["group"].GetUint(), msg["target"].GetString()));
}

View file

@ -0,0 +1,17 @@
#ifndef REMOVEASSOCIATION_H
#define REMOVEASSOCIATION_H
#include "mqttcommands/mqttcommands.h"
class MqttCommand_RemoveAssociation : public MqttCommand {
Q_OBJECT
public:
static MqttCommand *Create(QObject *parent = nullptr);
static QString StaticGetCommand() { return "RemoveAssociation";};
QString GetCommand() override { return StaticGetCommand(); };
bool processMessage(rapidjson::Document &) override;
private:
MqttCommand_RemoveAssociation(QObject *parent = nullptr);
};
#endif // PING_H

View file

@ -380,7 +380,7 @@ void mqttpublisher::sendCommandUpdate(QString command, rapidjson::Document &js)
void mqttpublisher::sendAssociationUpdate(quint8 node, quint8 group, rapidjson::Document &js) {
QT2JS::SetUInt64(js, "TimeStamp", QDateTime::currentSecsSinceEpoch());
this->m_client->publish(QMqttTopicName(getAssociationTopic(node, group)), QT2JS::getJSON(js), 0, false);
this->m_client->publish(QMqttTopicName(getAssociationTopic(node, group)), QT2JS::getJSON(js), 0, true);
return;
}

View file

@ -59,7 +59,9 @@ qtHaveModule(mqtt) {
mqttcommands/setPollInterval.cpp \
mqttcommands/getPollINterval.cpp \
mqttcommands/syncroniseNodeNeighbors.cpp \
mqttcommands/refreshValue.cpp
mqttcommands/refreshValue.cpp \
mqttcommands/addAssociation.cpp \
mqttcommands/removeAssociation.cpp
HEADERS += mqttpublisher.h \
qtrj.h \
@ -101,7 +103,10 @@ qtHaveModule(mqtt) {
mqttcommands/setPollInterval.h \
mqttcommands/getPollInterval.h\
mqttcommands/syncroniseNodeNeighbors.h \
mqttcommands/refreshValue.h
mqttcommands/refreshValue.h \
mqttcommands/addAssociation.h \
mqttcommands/removeAssociation.h
} else {
warning("MQTT Qt Module Not Found. Not Building MQTT Client Capabilities")