diff --git a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp index 45c4789..d71f8b1 100644 --- a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp +++ b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp @@ -39,10 +39,36 @@ ZigbeeClusterIasZone::ZigbeeClusterIasZone(ZigbeeNetwork *network, ZigbeeNode *n } +ZigbeeClusterIasZone::ZoneState ZigbeeClusterIasZone::zoneState() const +{ + return m_zoneState; +} + +ZigbeeClusterIasZone::ZoneType ZigbeeClusterIasZone::zoneType() const +{ + return m_zoneType; +} + +ZigbeeClusterIasZone::ZoneStatus ZigbeeClusterIasZone::zoneStatus() const +{ + return m_zoneStatus; +} + void ZigbeeClusterIasZone::setAttribute(const ZigbeeClusterAttribute &attribute) { qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType(); updateOrAddAttribute(attribute); + + if (attribute.id() == AttributeZoneState) { + quint8 zoneStateInt = attribute.dataType().toUInt8(); + m_zoneState = static_cast(zoneStateInt); + } else if (attribute.id() == AttributeZoneType) { + quint16 zoneTypeInt = attribute.dataType().toUInt16(); + m_zoneType = static_cast(zoneTypeInt); + } else if (attribute.id() == AttributeZoneStatus) { + quint16 zoneStatusInt = attribute.dataType().toUInt16(); + m_zoneStatus = ZoneStatus(zoneStatusInt); + } } void ZigbeeClusterIasZone::processDataIndication(ZigbeeClusterLibrary::Frame frame) @@ -75,6 +101,16 @@ void ZigbeeClusterIasZone::processDataIndication(ZigbeeClusterLibrary::Frame fra // Update the ZoneState attribute setAttribute(ZigbeeClusterAttribute(AttributeZoneState, ZigbeeDataType(Zigbee::BitMap16, frame.payload.left(2)))); emit zoneStatusChanged(ZoneStatusFlags(zoneStatus), extendedStatus, zoneId, delay); + + // Respond with default response if enabled + if (!frame.header.frameControl.disableDefaultResponse) { + // Send the default response with success back to the cluster + ZigbeeClusterReply *reply = sendDefaultResponse(frame.header.transactionSequenceNumber, command, ZigbeeClusterLibrary::StatusSuccess); + connect(reply, &ZigbeeClusterReply::finished, this, [](){ + qCDebug(dcZigbeeCluster()) << "Default response sent successfully to the IAS zone status notification."; + }); + } + break; } case ServerCommandZoneEnrollRequest: { @@ -87,8 +123,16 @@ void ZigbeeClusterIasZone::processDataIndication(ZigbeeClusterLibrary::Frame fra << zoneType << "Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode); // Update the ZoneState attribute setAttribute(ZigbeeClusterAttribute(AttributeZoneType, ZigbeeDataType(Zigbee::Enum16, frame.payload.left(2)))); - emit zoneEnrollRequest(zoneType, manufacturerCode); + + // Respond with default response if enabled + if (!frame.header.frameControl.disableDefaultResponse) { + // Send the default response with success back to the cluster + ZigbeeClusterReply *reply = sendDefaultResponse(frame.header.transactionSequenceNumber, command, ZigbeeClusterLibrary::StatusSuccess); + connect(reply, &ZigbeeClusterReply::finished, this, [](){ + qCDebug(dcZigbeeCluster()) << "Default response sent successfully to the IAS zone status notification."; + }); + } break; } } diff --git a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h index c43c717..2d0d554 100644 --- a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h +++ b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.h @@ -123,9 +123,15 @@ public: explicit ZigbeeClusterIasZone(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); - // TODO: write server commands + ZoneState zoneState() const; + ZoneType zoneType() const; + ZoneStatus zoneStatus() const; private: + ZoneState m_zoneState = ZoneStateNotEnrolled; + ZoneType m_zoneType = ZoneTypeInvalidZone; + ZoneStatus m_zoneStatus; + void setAttribute(const ZigbeeClusterAttribute &attribute) override; protected: diff --git a/libnymea-zigbee/zcl/zigbeecluster.cpp b/libnymea-zigbee/zcl/zigbeecluster.cpp index 8336c35..7b171ae 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.cpp +++ b/libnymea-zigbee/zcl/zigbeecluster.cpp @@ -282,6 +282,52 @@ ZigbeeClusterReply *ZigbeeCluster::sendClusterServerResponse(quint8 command, qui return zclReply; } +ZigbeeClusterReply *ZigbeeCluster::sendDefaultResponse(quint8 transactionSequenceNumber, quint8 command, quint8 status) +{ + ZigbeeNetworkRequest request = createGeneralRequest(); + + // Build ZCL frame control + ZigbeeClusterLibrary::FrameControl frameControl; + frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; + frameControl.manufacturerSpecific = false; + frameControl.direction = ZigbeeClusterLibrary::DirectionServerToClient; + frameControl.disableDefaultResponse = true; + + // Build ZCL header + ZigbeeClusterLibrary::Header header; + header.frameControl = frameControl; + header.command = ZigbeeClusterLibrary::CommandDefaultResponse; + header.transactionSequenceNumber = transactionSequenceNumber; + + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << command << status; + + // Build ZCL frame + 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); + qCDebug(dcZigbeeCluster()) << "Send default response" << "TSN:" << ZigbeeUtils::convertByteToHexString(transactionSequenceNumber) << ZigbeeUtils::convertByteArrayToHexString(payload); + ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); + connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ + if (!verifyNetworkError(zclReply, networkReply)) { + finishZclReply(zclReply); + return; + } + + // Note: since this is a response to a request, we don't expect any additional indications and the reply is finished + finishZclReply(zclReply); + }); + + return zclReply; +} + ZigbeeNetworkRequest ZigbeeCluster::createGeneralRequest() { // Build the request diff --git a/libnymea-zigbee/zcl/zigbeecluster.h b/libnymea-zigbee/zcl/zigbeecluster.h index 4c7e692..97e6d75 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.h +++ b/libnymea-zigbee/zcl/zigbeecluster.h @@ -119,6 +119,7 @@ protected: ZigbeeClusterReply *executeClusterCommand(quint8 command, const QByteArray &payload = QByteArray()); ZigbeeClusterReply *sendClusterServerResponse(quint8 command, quint8 transactionSequenceNumber, const QByteArray &payload = QByteArray()); + ZigbeeClusterReply *sendDefaultResponse(quint8 transactionSequenceNumber, quint8 command, quint8 status); bool verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply); void finishZclReply(ZigbeeClusterReply *zclReply);