diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro
index a7299c0..d68a20c 100644
--- a/libnymea-zigbee/libnymea-zigbee.pro
+++ b/libnymea-zigbee/libnymea-zigbee.pro
@@ -8,9 +8,11 @@ SOURCES += \
backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \
backends/deconz/zigbeebridgecontrollerdeconz.cpp \
backends/deconz/zigbeenetworkdeconz.cpp \
+ zcl/general/zigbeeclusteridentify.cpp \
zcl/general/zigbeeclusteronoff.cpp \
zcl/measurement/zigbeeclusterrelativehumiditymeasurement.cpp \
zcl/measurement/zigbeeclustertemperaturemeasurement.cpp \
+ zcl/security/zigbeeclusteriaszone.cpp \
zcl/zigbeecluster.cpp \
zcl/zigbeeclusterattribute.cpp \
zcl/zigbeeclusterlibrary.cpp \
@@ -52,9 +54,11 @@ HEADERS += \
backends/deconz/interface/zigbeeinterfacedeconzreply.h \
backends/deconz/zigbeebridgecontrollerdeconz.h \
backends/deconz/zigbeenetworkdeconz.h \
+ zcl/general/zigbeeclusteridentify.h \
zcl/general/zigbeeclusteronoff.h \
zcl/measurement/zigbeeclusterrelativehumiditymeasurement.h \
zcl/measurement/zigbeeclustertemperaturemeasurement.h \
+ zcl/security/zigbeeclusteriaszone.h \
zcl/zigbeecluster.h \
zcl/zigbeeclusterattribute.h \
zcl/zigbeeclusterlibrary.h \
diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp
new file mode 100644
index 0000000..a164184
--- /dev/null
+++ b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp
@@ -0,0 +1,217 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "zigbeeclusteridentify.h"
+#include "zigbeenetworkreply.h"
+#include "loggingcategory.h"
+#include "zigbeenetwork.h"
+
+#include
+
+ZigbeeClusterIdentify::ZigbeeClusterIdentify(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
+ ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdIdentify, direction, parent)
+{
+
+}
+
+ZigbeeClusterReply *ZigbeeClusterIdentify::identify(quint16 seconds)
+{
+ ZigbeeNetworkRequest request = createGeneralRequest();
+
+ // Build ZCL frame
+
+ ZigbeeClusterLibrary::FrameControl frameControl;
+ frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
+ frameControl.manufacturerSpecific = false;
+ frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
+ frameControl.disableDefaultResponse = false;
+
+ // ZCL header
+ ZigbeeClusterLibrary::Header header;
+ header.frameControl = frameControl;
+ header.command = ZigbeeClusterIdentify::CommandIdentify;
+ header.transactionSequenceNumber = m_transactionSequenceNumber++;
+
+ // Note: the identify time unit is 0.5 seconds
+ QByteArray payload = ZigbeeDataType(seconds * 2).data();
+
+ // Put them together
+ ZigbeeClusterLibrary::Frame frame;
+ frame.header = header;
+ frame.payload = payload;
+
+ request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
+ request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
+
+ ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
+ ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
+ connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
+ if (!verifyNetworkError(zclReply, networkReply)) {
+ qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
+ << m_node << networkReply->error()
+ << networkReply->zigbeeApsStatus();
+ finishZclReply(zclReply);
+ return;
+ }
+
+ // The request was successfully sent to the device
+ // Now check if the expected indication response received already
+ if (zclReply->isComplete()) {
+ finishZclReply(zclReply);
+ return;
+ }
+ });
+
+ return zclReply;
+}
+
+ZigbeeClusterReply *ZigbeeClusterIdentify::identifyQuery()
+{
+ ZigbeeNetworkRequest request = createGeneralRequest();
+
+ // Build ZCL frame
+
+ ZigbeeClusterLibrary::FrameControl frameControl;
+ frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
+ frameControl.manufacturerSpecific = false;
+ frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
+ frameControl.disableDefaultResponse = false;
+
+ // ZCL header
+ ZigbeeClusterLibrary::Header header;
+ header.frameControl = frameControl;
+ header.command = ZigbeeClusterIdentify::CommandIdentifyQuery;
+ header.transactionSequenceNumber = m_transactionSequenceNumber++;
+
+ // No payload
+
+ // Put them together
+ ZigbeeClusterLibrary::Frame frame;
+ frame.header = header;
+
+ request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
+ request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
+
+ ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
+ ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
+ connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
+ if (!verifyNetworkError(zclReply, networkReply)) {
+ qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
+ << m_node << networkReply->error()
+ << networkReply->zigbeeApsStatus();
+ finishZclReply(zclReply);
+ return;
+ }
+
+ // The request was successfully sent to the device
+ // Now check if the expected indication response received already
+ if (zclReply->isComplete()) {
+ finishZclReply(zclReply);
+ return;
+ }
+ });
+
+ return zclReply;
+}
+
+ZigbeeClusterReply *ZigbeeClusterIdentify::triggerEffect(ZigbeeClusterIdentify::Effect effect, quint8 effectVariant)
+{
+ ZigbeeNetworkRequest request = createGeneralRequest();
+
+ // Build ZCL frame
+
+ ZigbeeClusterLibrary::FrameControl frameControl;
+ frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
+ frameControl.manufacturerSpecific = false;
+ frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
+ frameControl.disableDefaultResponse = false;
+
+ // ZCL header
+ ZigbeeClusterLibrary::Header header;
+ header.frameControl = frameControl;
+ header.command = ZigbeeClusterIdentify::CommandTriggerEffect;
+ header.transactionSequenceNumber = m_transactionSequenceNumber++;
+
+ QByteArray payload;
+ QDataStream stream(&payload, QIODevice::WriteOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ stream << static_cast(effect);
+ stream << static_cast(effectVariant);
+
+ // Put them together
+ ZigbeeClusterLibrary::Frame frame;
+ frame.header = header;
+ frame.payload = payload;
+
+ request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
+ request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
+
+ ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
+ ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
+ connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
+ if (!verifyNetworkError(zclReply, networkReply)) {
+ qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
+ << m_node << networkReply->error()
+ << networkReply->zigbeeApsStatus();
+ finishZclReply(zclReply);
+ return;
+ }
+
+ // The request was successfully sent to the device
+ // Now check if the expected indication response received already
+ if (zclReply->isComplete()) {
+ finishZclReply(zclReply);
+ return;
+ }
+ });
+
+ return zclReply;
+}
+
+void ZigbeeClusterIdentify::setAttribute(const ZigbeeClusterAttribute &attribute)
+{
+ qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType();
+ if (hasAttribute(attribute.id())) {
+ m_attributes[attribute.id()] = attribute;
+ emit attributeChanged(attribute);
+ } else {
+ m_attributes.insert(attribute.id(), attribute);
+ emit attributeChanged(attribute);
+ }
+}
+
+void ZigbeeClusterIdentify::processDataIndication(ZigbeeClusterLibrary::Frame frame)
+{
+ qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
+
+ // TODO: implement identify query response
+
+ // Increase the tsn for continuouse id increasing on both sides
+ m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
+ qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
+}
diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteridentify.h b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.h
new file mode 100644
index 0000000..4d08fa6
--- /dev/null
+++ b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.h
@@ -0,0 +1,86 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEECLUSTERIDENTIFY_H
+#define ZIGBEECLUSTERIDENTIFY_H
+
+#include
+
+#include "zcl/zigbeecluster.h"
+#include "zcl/zigbeeclusterreply.h"
+
+class ZigbeeNode;
+class ZigbeeNetwork;
+class ZigbeeNodeEndpoint;
+class ZigbeeNetworkReply;
+
+class ZigbeeClusterIdentify : public ZigbeeCluster
+{
+ Q_OBJECT
+
+ friend class ZigbeeNode;
+ friend class ZigbeeNetwork;
+
+public:
+
+ enum Attribute {
+ AttributeIdentifyTime = 0x0000
+ };
+ Q_ENUM(Attribute)
+
+ enum Command {
+ CommandIdentify = 0x00,
+ CommandIdentifyQuery = 0x01,
+ CommandTriggerEffect = 0x40
+ };
+ Q_ENUM(Command)
+
+ enum Effect {
+ EffectBlink = 0x00,
+ EffectBreath = 0x01,
+ EffectOkay = 0x02,
+ EffectChannelChange = 0x0b,
+ EffectFinishEffect = 0xfe,
+ EffectStopEffect = 0xff
+ };
+ Q_ENUM(Effect)
+
+ explicit ZigbeeClusterIdentify(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
+
+ ZigbeeClusterReply *identify(quint16 seconds);
+ ZigbeeClusterReply *identifyQuery();
+ ZigbeeClusterReply *triggerEffect(Effect effect, quint8 effectVariant = 0x00);
+
+private:
+ void setAttribute(const ZigbeeClusterAttribute &attribute) override;
+
+protected:
+ void processDataIndication(ZigbeeClusterLibrary::Frame frame) override;
+
+};
+
+#endif // ZIGBEECLUSTERIDENTIFY_H
diff --git a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp
new file mode 100644
index 0000000..7afd29e
--- /dev/null
+++ b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp
@@ -0,0 +1,99 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "zigbeeclusteriaszone.h"
+#include "zigbeenetworkreply.h"
+#include "loggingcategory.h"
+#include "zigbeenetwork.h"
+#include "zigbeeutils.h"
+
+#include
+
+ZigbeeClusterIasZone::ZigbeeClusterIasZone(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
+ ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdIasZone, direction, parent)
+{
+
+}
+
+void ZigbeeClusterIasZone::setAttribute(const ZigbeeClusterAttribute &attribute)
+{
+ qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType();
+ if (hasAttribute(attribute.id())) {
+ m_attributes[attribute.id()] = attribute;
+ emit attributeChanged(attribute);
+ } else {
+ m_attributes.insert(attribute.id(), attribute);
+ emit attributeChanged(attribute);
+ }
+}
+
+void ZigbeeClusterIasZone::processDataIndication(ZigbeeClusterLibrary::Frame frame)
+{
+ qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
+
+ // Increase the tsn for continuouse id increasing on both sides
+ m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
+ switch (m_direction) {
+ case Client:
+ // TODO: handle client frames
+ break;
+ case Server:
+ // If the client cluster sends data to a server cluster (independent which), the command was executed on the device like button pressed
+ if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionServerToClient) {
+ // Read the payload which is
+ ClientCommand command = static_cast(frame.header.command);
+ qCDebug(dcZigbeeCluster()) << "Command received from" << m_node << m_endpoint << this << command;
+ switch (command) {
+ case ClientCommandStatusChangedNotification: {
+ QDataStream stream(frame.payload);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ quint16 zoneStatus = 0; quint8 extendedStatus = 0; quint8 zoneId = 0xff; quint16 delay = 0;
+ stream >> zoneStatus >> extendedStatus >> zoneId >> delay;
+ qCDebug(dcZigbeeCluster()) << "IAS zone status notification from" << m_node << m_endpoint << this
+ << ZoneStatusFlags(zoneStatus) << "Extended status:" << ZigbeeUtils::convertByteToHexString(extendedStatus)
+ << "Zone ID:" << ZigbeeUtils::convertByteToHexString(zoneId) << "Delay:" << delay << "[s/4]";
+ emit zoneStatusChanged(ZoneStatusFlags(zoneStatus), extendedStatus, zoneId, delay);
+ break;
+ }
+ case ClientCommandZoneEnrollRequest: {
+ QDataStream stream(frame.payload);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ quint16 zoneTypeInt = 0; quint16 manufacturerCode = 0;
+ stream >> zoneTypeInt >> manufacturerCode;
+ ZoneType zoneType = static_cast(zoneTypeInt);
+ qCDebug(dcZigbeeCluster()) << "IAS zone enroll request from" << m_node << m_endpoint << this
+ << zoneType << "Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode);
+ emit zoneEnrollRequest(zoneType, manufacturerCode);
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+}
diff --git a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h
new file mode 100644
index 0000000..6650581
--- /dev/null
+++ b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h
@@ -0,0 +1,142 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along with this project.
+* If not, see .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEECLUSTERIASZONE_H
+#define ZIGBEECLUSTERIASZONE_H
+
+#include
+
+#include "zcl/zigbeecluster.h"
+#include "zcl/zigbeeclusterreply.h"
+
+class ZigbeeNode;
+class ZigbeeNetwork;
+class ZigbeeNodeEndpoint;
+class ZigbeeNetworkReply;
+
+class ZigbeeClusterIasZone : public ZigbeeCluster
+{
+ Q_OBJECT
+
+ friend class ZigbeeNode;
+ friend class ZigbeeNetwork;
+
+public:
+
+ enum Attribute {
+ // Zone information attribute set
+ AttributeZoneState = 0x0000,
+ AttributeZoneType = 0x0001,
+ AttributeZoneStatus = 0x0002,
+ // Zone settings attribute set
+ AttributeCieAddress = 0x0010,
+ AttributeZoneId = 0x0011,
+ AttributeNumberOfZoneSensitivityLevelsSupported = 0x0012,
+ AttributeCurrentZoneSensitivityLevel = 0x0013
+ };
+ Q_ENUM(Attribute)
+
+ enum ZoneState {
+ ZoneStateNotEnrolled = 0x00,
+ ZoneStateEnrolled = 0x01
+ };
+ Q_ENUM(ZoneState)
+
+ enum ZoneType {
+ ZoneTypeStandardCIE = 0x0000,
+ ZoneTypeMotionSensor = 0x000d,
+ ZoneTypeContactSwitch = 0x0015,
+ ZoneTypeFireSensor = 0x0028,
+ ZoneTypeWaterSensor = 0x002a,
+ ZoneTypeCarbonMonoxideSensor = 0x002b,
+ ZoneTypePersonalEmergencyDevice = 0x002c,
+ ZoneTypeVibrationMovementSensor = 0x002d,
+ ZoneTypeRemoteControl = 0x010f,
+ ZoneTypeKeyFob = 0x0115,
+ ZoneTypeKeypad = 0x021d,
+ ZoneTypeStandardWarningDevice = 0x0225,
+ ZoneTypeGlassBreakSensor = 0x0226,
+ ZoneTypeSecurityRepater = 0x0229,
+ ZoneTypeInvalidZone = 0xffff
+ };
+ Q_ENUM(ZoneType)
+
+ enum ZoneStatusFlag {
+ ZoneStatusFlagAlarm1 = 0x0001,
+ ZoneStatusFlagAlarm2 = 0x0002,
+ ZoneStatusFlagTamper = 0x0004,
+ ZoneStatusFlagBattery = 0x0008,
+ ZoneStatusFlagSupervisionReports = 0x0010,
+ ZoneStatusFlagRestoreReports = 0x0020,
+ ZoneStatusFlagTrouble = 0x0040,
+ ZoneStatusFlagAcMains = 0x0080,
+ ZoneStatusFlagTest = 0x0100,
+ ZoneStatusFlagBatteryDefect = 0x0200
+ };
+ Q_ENUM(ZoneStatusFlag)
+ Q_DECLARE_FLAGS(ZoneStatusFlags, ZoneStatusFlag)
+
+ enum EnrollResponseCode {
+ EnrollResponseCodeSuccess = 0x00,
+ EnrollResponseCodeNotSupported = 0x01,
+ EnrollResponseCodeNoEnrollPermit = 0x02,
+ EnrollResponseCodeToManyZones = 0x03
+ };
+ Q_ENUM(EnrollResponseCode)
+
+ enum ClientCommand {
+ ClientCommandStatusChangedNotification = 0x00, // M
+ ClientCommandZoneEnrollRequest = 0x01 // M
+ };
+ Q_ENUM(ClientCommand)
+
+ enum ServerCommand {
+ ServerCommandEnrollResponse = 0x00, // M
+ ServerCommandInitNormalOperationMode = 0x01, // O
+ ServerCommandInitTestMode = 0x02 // O
+ };
+ Q_ENUM(ServerCommand)
+
+ explicit ZigbeeClusterIasZone(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
+
+ // TODO: write server commands
+
+private:
+ void setAttribute(const ZigbeeClusterAttribute &attribute) override;
+
+protected:
+ void processDataIndication(ZigbeeClusterLibrary::Frame frame) override;
+
+signals:
+ void zoneStatusChanged(ZoneStatusFlags zoneStatus, quint8 extendedStatus, quint8 zoneId, quint16 delay);
+ void zoneEnrollRequest(ZoneType zoneType, quint16 manufacturerCode);
+
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterIasZone::ZoneStatusFlags)
+
+#endif // ZIGBEECLUSTERIASZONE_H
diff --git a/libnymea-zigbee/zcl/zigbeecluster.cpp b/libnymea-zigbee/zcl/zigbeecluster.cpp
index 162f1f7..f4f9d00 100644
--- a/libnymea-zigbee/zcl/zigbeecluster.cpp
+++ b/libnymea-zigbee/zcl/zigbeecluster.cpp
@@ -218,6 +218,8 @@ void ZigbeeCluster::processDataIndication(ZigbeeClusterLibrary::Frame frame)
{
// Increase the tsn for continuouse id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
+ // Warn about the unhandled cluster indication, you can override this method in cluster implementations
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
}
@@ -246,6 +248,9 @@ void ZigbeeCluster::processApsDataIndication(const QByteArray &asdu, const Zigbe
}
}
+ // Increase the tsn for continuouse id increasing on both sides
+ m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
return;
}
@@ -261,11 +266,14 @@ void ZigbeeCluster::processApsDataIndication(const QByteArray &asdu, const Zigbe
stream >> attributeId >> type;
ZigbeeDataType dataType = ZigbeeClusterLibrary::readDataType(&stream, static_cast(type));
setAttribute(ZigbeeClusterAttribute(attributeId, dataType));
+
+ // Increase the tsn for continuouse id increasing on both sides
+ m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
+
return;
}
}
-
// Not for a reply or not an attribute report, let the cluster process this message internally
processDataIndication(frame);
}
diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp
index 8296345..64c964d 100644
--- a/libnymea-zigbee/zigbeenetwork.cpp
+++ b/libnymea-zigbee/zigbeenetwork.cpp
@@ -352,8 +352,7 @@ void ZigbeeNetwork::loadNetwork()
settings.setArrayIndex(n);
Zigbee::ClusterId clusterId = static_cast(settings.value("clusterId", 0).toUInt());
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Server);
- //qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
- endpoint->m_inputClusters.insert(clusterId, cluster);
+ endpoint->addInputCluster(cluster);
}
settings.endArray(); // inputClusters
@@ -362,12 +361,11 @@ void ZigbeeNetwork::loadNetwork()
settings.setArrayIndex(n);
Zigbee::ClusterId clusterId = static_cast(settings.value("clusterId", 0).toUInt());
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Client);
- //qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
- endpoint->m_outputClusters.insert(clusterId, cluster);
+ endpoint->addOutputCluster(cluster);
}
settings.endArray(); // outputClusters
- node->m_endpoints.append(endpoint);
+ node->m_endpoints.insert(endpoint->endpointId(), endpoint);
}
settings.endArray(); // endpoints
diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp
index b9ef143..4cfc1e4 100644
--- a/libnymea-zigbee/zigbeenode.cpp
+++ b/libnymea-zigbee/zigbeenode.cpp
@@ -68,23 +68,17 @@ ZigbeeAddress ZigbeeNode::extendedAddress() const
QList ZigbeeNode::endpoints() const
{
- return m_endpoints;
+ return m_endpoints.values();
}
bool ZigbeeNode::hasEndpoint(quint8 endpointId) const
{
- return getEndpoint(endpointId) != nullptr;
+ return m_endpoints.keys().contains(endpointId);
}
ZigbeeNodeEndpoint *ZigbeeNode::getEndpoint(quint8 endpointId) const
{
- foreach (ZigbeeNodeEndpoint *endpoint, m_endpoints) {
- if (endpoint->endpointId() == endpointId) {
- return endpoint;
- }
- }
-
- return nullptr;
+ return m_endpoints.value(endpointId);
}
ZigbeeNode::NodeType ZigbeeNode::nodeType() const
@@ -641,7 +635,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
ZigbeeNodeEndpoint *endpoint = nullptr;
if (!hasEndpoint(endpointId)) {
endpoint = new ZigbeeNodeEndpoint(m_network, this, endpointId, this);
- m_endpoints.append(endpoint);
+ m_endpoints.insert(endpoint->endpointId(), endpoint);
} else {
endpoint = getEndpoint(endpointId);
}
@@ -694,7 +688,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
void ZigbeeNode::initBasicCluster()
{
// FIXME: check if we want to read from all endpoints the basic cluster information or only from the first
- ZigbeeClusterBasic *basicCluster = m_endpoints.first()->inputCluster(Zigbee::ClusterIdBasic);
+ ZigbeeClusterBasic *basicCluster = endpoints().first()->inputCluster(Zigbee::ClusterIdBasic);
if (!basicCluster) {
qCWarning(dcZigbeeNode()) << "Could not find basic cluster on" << this << "Set the node to initialized anyways.";
// Set the device initialized any ways since this ist just for convinience
@@ -718,7 +712,7 @@ void ZigbeeNode::readManufacturerName(ZigbeeClusterBasic *basicCluster)
bool valueOk = false;
QString manufacturerName = basicCluster->attribute(attributeId).dataType().toString(&valueOk);
if (valueOk) {
- m_endpoints.first()->m_manufacturerName = manufacturerName;
+ endpoints().first()->m_manufacturerName = manufacturerName;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert manufacturer name attribute data to string" << basicCluster->attribute(attributeId).dataType();
}
@@ -742,7 +736,7 @@ void ZigbeeNode::readManufacturerName(ZigbeeClusterBasic *basicCluster)
bool valueOk = false;
QString manufacturerName = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
- m_endpoints.first()->m_manufacturerName = manufacturerName;
+ endpoints().first()->m_manufacturerName = manufacturerName;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert manufacturer name attribute data to string" << attributeStatusRecord.dataType;
}
@@ -766,7 +760,7 @@ void ZigbeeNode::readModelIdentifier(ZigbeeClusterBasic *basicCluster)
bool valueOk = false;
QString modelIdentifier = basicCluster->attribute(attributeId).dataType().toString(&valueOk);
if (valueOk) {
- m_endpoints.first()->m_modelIdentifier= modelIdentifier;
+ endpoints().first()->m_modelIdentifier= modelIdentifier;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert model identifier attribute data to string" << basicCluster->attribute(attributeId).dataType();
}
@@ -790,7 +784,7 @@ void ZigbeeNode::readModelIdentifier(ZigbeeClusterBasic *basicCluster)
bool valueOk = false;
QString modelIdentifier = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
- m_endpoints.first()->m_modelIdentifier = modelIdentifier;
+ endpoints().first()->m_modelIdentifier = modelIdentifier;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert model identifier attribute data to string" << attributeStatusRecord.dataType;
}
@@ -820,7 +814,7 @@ void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster)
bool valueOk = false;
QString softwareBuildId = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
- m_endpoints.first()->m_softwareBuildId = softwareBuildId;
+ endpoints().first()->m_softwareBuildId = softwareBuildId;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert software build id attribute data to string" << attributeStatusRecord.dataType;
}
@@ -845,7 +839,7 @@ void ZigbeeNode::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataInd
endpoint = new ZigbeeNodeEndpoint(m_network, this, indication.sourceEndpoint, this);
endpoint->setProfile(static_cast(indication.profileId));
// Note: the endpoint is not initializd yet, but keep it anyways
- m_endpoints.append(endpoint);
+ m_endpoints.insert(endpoint->endpointId(), endpoint);
}
endpoint->handleZigbeeClusterLibraryIndication(indication);
diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h
index c2971db..fadf600 100644
--- a/libnymea-zigbee/zigbeenode.h
+++ b/libnymea-zigbee/zigbeenode.h
@@ -199,7 +199,7 @@ private:
ZigbeeNetwork *m_network;
ZigbeeDeviceObject *m_deviceObject = nullptr;
- QList m_endpoints;
+ QHash m_endpoints;
// Node descriptor information
QByteArray m_nodeDescriptorRawData;
@@ -277,6 +277,7 @@ private:
// Init methods
int m_requestRetry = 0;
QList m_uninitializedEndpoints;
+
void initNodeDescriptor();
void initPowerDescriptor();
void initEndpoints();
diff --git a/libnymea-zigbee/zigbeenodeendpoint.cpp b/libnymea-zigbee/zigbeenodeendpoint.cpp
index 2fc32f8..904d926 100644
--- a/libnymea-zigbee/zigbeenodeendpoint.cpp
+++ b/libnymea-zigbee/zigbeenodeendpoint.cpp
@@ -112,6 +112,11 @@ QList ZigbeeNodeEndpoint::outputClusters() const
return m_outputClusters.values();
}
+ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const
+{
+ return m_outputClusters.value(clusterId);
+}
+
bool ZigbeeNodeEndpoint::hasOutputCluster(Zigbee::ClusterId clusterId) const
{
return m_outputClusters.keys().contains(clusterId);
@@ -161,18 +166,27 @@ void ZigbeeNodeEndpoint::setSoftwareBuildId(const QString &softwareBuildId)
ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction)
{
switch (clusterId) {
+ // General
case Zigbee::ClusterIdBasic:
return new ZigbeeClusterBasic(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdOnOff:
return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this);
break;
+ case Zigbee::ClusterIdIdentify:
+ return new ZigbeeClusterIdentify(m_network, m_node, this, direction, this);
+ break;
+ // Measurement
case Zigbee::ClusterIdTemperatureMeasurement:
return new ZigbeeClusterTemperatureMeasurement(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdRelativeHumidityMeasurement:
return new ZigbeeClusterRelativeHumidityMeasurement(m_network, m_node, this, direction, this);
break;
+ // Security
+ case Zigbee::ClusterIdIasZone:
+ return new ZigbeeClusterIasZone(m_network, m_node, this, direction, this);
+ break;
default:
// Return a default cluster since we have no special implementation for this cluster
return new ZigbeeCluster(m_network, m_node, this, clusterId, direction, this);
@@ -182,11 +196,13 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, Zi
void ZigbeeNodeEndpoint::addInputCluster(ZigbeeCluster *cluster)
{
m_inputClusters.insert(cluster->clusterId(), cluster);
+ emit inputClusterAdded(cluster);
}
void ZigbeeNodeEndpoint::addOutputCluster(ZigbeeCluster *cluster)
{
m_outputClusters.insert(cluster->clusterId(), cluster);
+ emit outputClusterAdded(cluster);
}
void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication)
@@ -203,7 +219,7 @@ void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::Apsd
if (!cluster) {
cluster = createCluster(static_cast(indication.clusterId), ZigbeeCluster::Client);
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
- m_outputClusters.insert(cluster->clusterId(), cluster);
+ addOutputCluster(cluster);
}
break;
case ZigbeeClusterLibrary::DirectionServerToClient:
@@ -212,7 +228,7 @@ void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::Apsd
if (!cluster) {
cluster = createCluster(static_cast(indication.clusterId), ZigbeeCluster::Server);
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
- m_inputClusters.insert(cluster->clusterId(), cluster);
+ addInputCluster(cluster);
}
break;
}
@@ -220,11 +236,6 @@ void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::Apsd
cluster->processApsDataIndication(indication.asdu, frame);
}
-ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const
-{
- return m_outputClusters.value(clusterId);
-}
-
QDebug operator<<(QDebug debug, ZigbeeNodeEndpoint *endpoint)
{
debug.nospace().noquote() << "ZigbeeNodeEndpoint(" << ZigbeeUtils::convertByteToHexString(endpoint->endpointId());
diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h
index 7208c31..b77b2f1 100644
--- a/libnymea-zigbee/zigbeenodeendpoint.h
+++ b/libnymea-zigbee/zigbeenodeendpoint.h
@@ -36,11 +36,16 @@
// Import all implemented cluster types
#include "zcl/zigbeecluster.h"
+
#include "zcl/general/zigbeeclusterbasic.h"
#include "zcl/general/zigbeeclusteronoff.h"
+#include "zcl/general/zigbeeclusteridentify.h"
+
#include "zcl/measurement/zigbeeclustertemperaturemeasurement.h"
#include "zcl/measurement/zigbeeclusterrelativehumiditymeasurement.h"
+#include "zcl/security/zigbeeclusteriaszone.h"
+
class ZigbeeNode;
class ZigbeeNetwork;
@@ -82,6 +87,7 @@ public:
ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const;
bool hasOutputCluster(Zigbee::ClusterId clusterId) const;
+ // Convinience cast methods for getting a specific cluster object
template
inline T* inputCluster(Zigbee::ClusterId clusterId)
{
@@ -123,9 +129,6 @@ private:
void setModelIdentifier(const QString &modelIdentifier);
void setSoftwareBuildId(const QString &softwareBuildId);
- // Cluster commands
- //virtual void setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute = ZigbeeClusterAttribute()) = 0;
-
ZigbeeCluster *createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction);
void addInputCluster(ZigbeeCluster *cluster);
@@ -134,12 +137,14 @@ private:
void handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication);
signals:
+ void inputClusterAdded(ZigbeeCluster *cluster);
+ void outputClusterAdded(ZigbeeCluster *cluster);
+
void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
void manufacturerNameChanged(const QString &manufacturerName);
void modelIdentifierChanged(const QString &modelIdentifier);
void softwareBuildIdChanged(const QString &softwareBuildId);
-
};
QDebug operator<<(QDebug debug, ZigbeeNodeEndpoint *endpoint);