// SPDX-License-Identifier: LGPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * nymea-zigbee * Zigbee integration module for nymea * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of nymea-zigbee. * * nymea-zigbee is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * nymea-zigbee 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 nymea-zigbee. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "zigbeeutils.h" #include "loggingcategory.h" #include "zigbeechannelmask.h" #include "zdo/zigbeedeviceprofile.h" #include "zigbeebridgecontrollerdeconz.h" #include ZigbeeBridgeControllerDeconz::ZigbeeBridgeControllerDeconz(QObject *parent) : ZigbeeBridgeController(parent) { m_interface = new ZigbeeInterfaceDeconz(this); connect(m_interface, &ZigbeeInterfaceDeconz::availableChanged, this, &ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged); connect(m_interface, &ZigbeeInterfaceDeconz::packageReceived, this, &ZigbeeBridgeControllerDeconz::onInterfacePackageReceived); m_watchdogTimer = new QTimer(this); m_watchdogTimer->setSingleShot(false); m_watchdogTimer->setInterval(m_watchdogResetTimout * 1000); connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::resetControllerWatchdog); } ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz() { qCDebug(dcZigbeeController()) << "Destroy controller"; } DeconzNetworkConfiguration ZigbeeBridgeControllerDeconz::networkConfiguration() const { return m_networkConfiguration; } Deconz::NetworkState ZigbeeBridgeControllerDeconz::networkState() const { return m_networkState; } void ZigbeeBridgeControllerDeconz::rebootController() { // According to the docs, the watchdog can be used to reboot the device by // setting the watchdog timeout to a small value and let it timeout // Reset the timer to prevent interrupting the reboot m_watchdogTimer->start(); // Write the watchdog to 2 seconds QByteArray parameterData; QDataStream stream(¶meterData, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(2); ZigbeeInterfaceDeconzReply *reply = requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Could initiate reboot controller by writing watchdog parameter." << reply->statusCode(); return; } qCDebug(dcZigbeeController()) << "Reboot device requested successfully. The controler is about to reboot..."; }); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestVersion() { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandVersion); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(9); // Frame length stream << static_cast(0); stream << static_cast(0); return createReply(Deconz::CommandVersion, "Request controller version", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestDeviceState() { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandDeviceState); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(8); // Frame length stream << static_cast(0); // Reserverd stream << static_cast(0); // Reserverd stream << static_cast(0); // Reserverd return createReply(Deconz::CommandDeviceState, "Request controller device state", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadParameter(Deconz::Parameter parameter) { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandReadParameter); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(8); // Frame length 7 + 1 payload stream << static_cast(1); // Payload length stream << static_cast(parameter); return createReply(Deconz::CommandReadParameter, "Request controller read parameter", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data) { quint16 payloadLength = static_cast(1 + data.length()); QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandWriteParameter); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7 + payloadLength); // Frame length 7 + payload length stream << static_cast(payloadLength); // 1 parameter + parameter data length stream << static_cast(parameter); for (int i = 0; i < data.length(); i++) { stream << static_cast(data.at(i)); } return createReply(Deconz::CommandWriteParameter, "Request controller write parameter", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestChangeNetworkState(Deconz::NetworkState networkState) { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandChangeNetworkState); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(6); // Frame length stream << static_cast(networkState); return createReply(Deconz::CommandChangeNetworkState, "Request controller write parameter", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode) { quint16 payloadLength = 0; if (sourceAddressMode != Deconz::SourceAddressModeNone) { payloadLength = 1; } QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandApsDataIndication); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7 + payloadLength); // Frame length + payload length stream << static_cast(payloadLength); // payload length if (payloadLength > 0) stream << static_cast(sourceAddressMode); return createReply(Deconz::CommandApsDataIndication, "Request read received data indication", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestQuerySendDataConfirm() { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandApsDataConfirm); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7); // Frame length stream << static_cast(0); // Payload length return createReply(Deconz::CommandApsDataConfirm, "Request query send data confirm", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(const ZigbeeNetworkRequest &request) { ZigbeeInterfaceDeconzReply *interfaceReply = nullptr; switch (request.destinationAddressMode()) { case Zigbee::DestinationAddressModeGroup: interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(), request.profileId(), request.clusterId(),request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); break; case Zigbee::DestinationAddressModeShortAddress: interfaceReply = requestEnqueueSendDataShortAddress(request.requestId(), request.destinationShortAddress(), request.destinationEndpoint(), request.profileId(), request.clusterId(), request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); break; case Zigbee::DestinationAddressModeIeeeAddress: interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(), request.destinationEndpoint(), request.profileId(), request.clusterId(), request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); break; } return interfaceReply; } void ZigbeeBridgeControllerDeconz::sendNextRequest() { // Check if there is a reply request to send if (m_replyQueue.isEmpty()) return; // Check if there is currently a running reply if (m_currentReply) return; // If the controler request queue is full, wait until it's free again if (!m_apsFreeSlotsAvailable) return; if (!m_available) { return; } // Get the next reply, set the sequence number, send the request data over the interface and start waiting m_currentReply = m_replyQueue.dequeue(); m_currentReply->setSequenceNumber(generateSequenceNumber()); qCDebug(dcZigbeeController()) << "Send request" << m_currentReply; m_interface->sendPackage(m_currentReply->requestData()); m_currentReply->m_timer->start(); } quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber() { return m_sequenceNumber++; } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Command command, const QString &requestName, const QByteArray &requestData, QObject *parent) { // Create the reply ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(command, parent); reply->m_requestName = requestName; reply->m_requestData = requestData; // Make sure we clean up on timeout connect(reply, &ZigbeeInterfaceDeconzReply::timeout, this, [this, reply](){ qCWarning(dcZigbeeController()) << "Reply timeout" << reply; // Make sure we can send the next read confirm reply if (m_readConfirmReply == reply) { m_readConfirmReply = nullptr; } // Make sure we can send the next read indication reply if (m_readIndicationReply == reply) { m_readIndicationReply = nullptr; } // Note: send next reply with the finished signal }); // Auto delete the object on finished connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, [this, reply](){ reply->deleteLater(); if (m_currentReply == reply) { m_currentReply = nullptr; QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection); } }); // Enqueu this reply and send it once the current reply slot is free // If this is a data indication or a confirmation, prepend the reply since responses have higher priority than new requests if (command == Deconz::CommandApsDataConfirm || command == Deconz::CommandApsDataIndication) { m_replyQueue.prepend(reply); qCDebug(dcZigbeeController()) << "Prepend request to queue:" << reply->requestName(); } else { m_replyQueue.enqueue(reply); qCDebug(dcZigbeeController()) << "Enqueue request:" << reply->requestName(); } connect(reply, &ZigbeeInterfaceDeconzReply::timeout, this, [=](){ m_replyQueue.removeAll(reply); }); QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection); return reply; } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { // quint8 sequenceNumber = generateSequenceNumber(); // qCDebug(dcZigbeeController()) << "Request enqueue send data to group" << ZigbeeUtils::convertUint16ToHexString(groupAddress) // << "SQN:" << sequenceNumber // << static_cast(profileId) // << ZigbeeUtils::convertUint16ToHexString(clusterId) // << ZigbeeUtils::convertByteToHexString(sourceEndpoint); Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes"); // Note: 14 protocol bytes + asdu package length quint16 payloadLength = static_cast(14 + asdu.length()); QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandApsDataRequest); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7 + payloadLength); // Frame length stream << payloadLength; stream << requestId; stream << static_cast(0); // Flags stream << static_cast(Zigbee::DestinationAddressModeGroup); stream << groupAddress; stream << profileId; stream << clusterId; stream << sourceEndpoint; stream << static_cast(asdu.length()); for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } stream << static_cast(txOptions); stream << radius; return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to group", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { // quint8 sequenceNumber = generateSequenceNumber(); // qCDebug(dcZigbeeController()) << "Request enqueue send data to short address" << ZigbeeUtils::convertUint16ToHexString(shortAddress) // << "SQN:" << sequenceNumber // << ZigbeeUtils::convertByteToHexString(destinationEndpoint) // << static_cast(profileId) // << ZigbeeUtils::convertUint16ToHexString(clusterId) // << ZigbeeUtils::convertByteToHexString(sourceEndpoint); Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes"); // Note: 15 protocol bytes + asdu package length quint16 payloadLength = static_cast(15 + asdu.length()); QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandApsDataRequest); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7 + payloadLength); // Frame length stream << payloadLength; stream << requestId; stream << static_cast(0); // Flags stream << static_cast(Zigbee::DestinationAddressModeShortAddress); stream << shortAddress << destinationEndpoint; stream << profileId; stream << clusterId; stream << sourceEndpoint; stream << static_cast(asdu.length()); for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } stream << static_cast(txOptions); // TX Options: Use APS ACKs stream << radius; return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to short address", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { // quint8 sequenceNumber = generateSequenceNumber(); // qCDebug(dcZigbeeController()) << "Request enqueue send data to IEEE address" << ieeeAddress.toString() // << "SQN:" << sequenceNumber // << ZigbeeUtils::convertByteToHexString(destinationEndpoint) // << profileId << clusterId // << ZigbeeUtils::convertByteToHexString(sourceEndpoint); Q_ASSERT_X(asdu.length() <= 127, "ZigbeeController", "ASDU package length has to be <= 127 bytes"); // Note: 21 protocol bytes + asdu package length quint16 payloadLength = static_cast(21 + asdu.length()); QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Deconz::CommandApsDataRequest); stream << static_cast(0); // SQN will be generated right before sending stream << static_cast(0); // Reserverd stream << static_cast(7 + payloadLength); // Frame length stream << payloadLength; stream << requestId; stream << static_cast(0); // Flags stream << static_cast(Zigbee::DestinationAddressModeIeeeAddress); stream << ieeeAddress.toUInt64() << destinationEndpoint; stream << profileId; stream << clusterId; stream << sourceEndpoint; stream << static_cast(asdu.length()); for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } stream << static_cast(txOptions); // TX Options: Use APS ACKs stream << radius; return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to IEEE address", message, this); } ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters() { qCDebug(dcZigbeeController()) << "Start reading network parameters"; // This method reads all network configuration parameters sequentially. This method returns a reply which will be finished either // when a read request failes or all requests finished successfully. // If read request failes, this mehtod returns the status code of the failed request. // Create an independent reply for finishing the entire read sequence ZigbeeInterfaceDeconzReply *readNetworkParametersReply = new ZigbeeInterfaceDeconzReply(Deconz::CommandReadParameter, this); connect(readNetworkParametersReply, &ZigbeeInterfaceDeconzReply::finished, readNetworkParametersReply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection); // Read MAC address of the bridge ZigbeeInterfaceDeconzReply *replyMacAddress = requestReadParameter(Deconz::ParameterMacAddress); connect(replyMacAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyMacAddress](){ if (replyMacAddress->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyMacAddress->sequenceNumber() << replyMacAddress->command() << Deconz::ParameterMacAddress << "finished with error" << replyMacAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyMacAddress->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyMacAddress->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint64 macAddress = 0; stream >> payloadLenght >> parameter >> macAddress; m_networkConfiguration.ieeeAddress = ZigbeeAddress(macAddress); qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyMacAddress->sequenceNumber() << replyMacAddress->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "IEEE address:" << m_networkConfiguration.ieeeAddress.toString(); // Read PAN ID ZigbeeInterfaceDeconzReply *replyPanId = requestReadParameter(Deconz::ParameterPanId); connect(replyPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyPanId](){ if (replyPanId->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyPanId->sequenceNumber() << replyPanId->command() << Deconz::ParameterPanId << "finished with error" << replyPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyPanId->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyPanId->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint16 panId = 0; stream >> payloadLenght >> parameter >> panId; m_networkConfiguration.panId = panId; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyPanId->sequenceNumber() << replyPanId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "PAN ID:" << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.panId); // Read short address ZigbeeInterfaceDeconzReply *replyShortAddress = requestReadParameter(Deconz::ParameterNetworkAddress); connect(replyShortAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyShortAddress](){ if (replyShortAddress->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyShortAddress->sequenceNumber() << replyShortAddress->command() << Deconz::ParameterNetworkAddress << "finished with error" << replyShortAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyShortAddress->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyShortAddress->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint16 shortAddress = 0; stream >> payloadLenght >> parameter >> shortAddress; m_networkConfiguration.shortAddress = shortAddress; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyShortAddress->sequenceNumber() << replyShortAddress->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.shortAddress); // Read extended PAN ID ZigbeeInterfaceDeconzReply *replyExtendedPanId = requestReadParameter(Deconz::ParameterNetworkExtendedPanId); connect(replyExtendedPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyExtendedPanId](){ if (replyExtendedPanId->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyExtendedPanId->sequenceNumber() << replyExtendedPanId->command() << Deconz::ParameterNetworkExtendedPanId << "finished with error" << replyExtendedPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyExtendedPanId->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyExtendedPanId->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint64 networkExtendedPanId = 0; stream >> payloadLenght >> parameter >> networkExtendedPanId; m_networkConfiguration.extendedPanId = networkExtendedPanId; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyExtendedPanId->sequenceNumber() << replyExtendedPanId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint64ToHexString(m_networkConfiguration.extendedPanId); // Read device type ZigbeeInterfaceDeconzReply *replyNodeType = requestReadParameter(Deconz::ParameterNodeType); connect(replyNodeType, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyNodeType](){ if (replyNodeType->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyNodeType->sequenceNumber() << replyNodeType->command() << Deconz::ParameterNodeType << "finished with error" << replyNodeType->statusCode(); readNetworkParametersReply->m_statusCode = replyNodeType->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyNodeType->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint8 nodeType = 0; stream >> payloadLenght >> parameter >> nodeType; m_networkConfiguration.nodeType = static_cast(nodeType); qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyNodeType->sequenceNumber() << replyNodeType->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.nodeType; // Read channel mask ZigbeeInterfaceDeconzReply *replyChannelMask = requestReadParameter(Deconz::ParameterChannelMask); connect(replyChannelMask, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyChannelMask](){ if (replyChannelMask->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyChannelMask->sequenceNumber() << replyChannelMask->command() << Deconz::ParameterChannelMask << "finished with error" << replyChannelMask->statusCode(); readNetworkParametersReply->m_statusCode = replyChannelMask->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyChannelMask->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint32 channelMask = 0; stream >> payloadLenght >> parameter >> channelMask; m_networkConfiguration.channelMask = channelMask; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyChannelMask->sequenceNumber() << replyChannelMask->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint32ToHexString(m_networkConfiguration.channelMask); // Read APS extended PAN ID ZigbeeInterfaceDeconzReply *replyApsExtendedPanId = requestReadParameter(Deconz::ParameterApsExtendedPanId); connect(replyApsExtendedPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyApsExtendedPanId](){ if (replyApsExtendedPanId->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyApsExtendedPanId->sequenceNumber() << replyApsExtendedPanId->command() << Deconz::ParameterApsExtendedPanId << "finished with error" << replyApsExtendedPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyApsExtendedPanId->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyApsExtendedPanId->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint64 apsExtendedPanId = 0; stream >> payloadLenght >> parameter >> apsExtendedPanId; m_networkConfiguration.apsExtendedPanId = apsExtendedPanId; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyApsExtendedPanId->sequenceNumber() << replyApsExtendedPanId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint64ToHexString(m_networkConfiguration.apsExtendedPanId); // Read trust center address ZigbeeInterfaceDeconzReply *replyTrustCenterAddress = requestReadParameter(Deconz::ParameterTrustCenterAddress); connect(replyTrustCenterAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyTrustCenterAddress](){ if (replyTrustCenterAddress->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyTrustCenterAddress->sequenceNumber() << replyTrustCenterAddress->command() << Deconz::ParameterTrustCenterAddress << "finished with error" << replyTrustCenterAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyTrustCenterAddress->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyTrustCenterAddress->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint64 trustCenterAddress = 0; stream >> payloadLenght >> parameter >> trustCenterAddress; m_networkConfiguration.trustCenterAddress = ZigbeeAddress(trustCenterAddress); qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyTrustCenterAddress->sequenceNumber() << replyTrustCenterAddress->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.trustCenterAddress; // Read security mode ZigbeeInterfaceDeconzReply *replySecurityMode = requestReadParameter(Deconz::ParameterSecurityMode); connect(replySecurityMode, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replySecurityMode](){ if (replySecurityMode->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replySecurityMode->sequenceNumber() << replySecurityMode->command() << Deconz::ParameterSecurityMode << "finished with error" << replySecurityMode->statusCode(); readNetworkParametersReply->m_statusCode = replySecurityMode->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replySecurityMode->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint8 securityMode = 0; stream >> payloadLenght >> parameter >> securityMode; m_networkConfiguration.securityMode = static_cast(securityMode); qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replySecurityMode->sequenceNumber() << replySecurityMode->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.securityMode; // Read predefined new pan id ZigbeeInterfaceDeconzReply *replyPredefinedNwkPanId = requestReadParameter(Deconz::ParameterPredefinedNwkPanId); connect(replyPredefinedNwkPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyPredefinedNwkPanId](){ if (replyPredefinedNwkPanId->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyPredefinedNwkPanId->sequenceNumber() << replyPredefinedNwkPanId->command() << Deconz::ParameterSecurityMode << "finished with error" << replyPredefinedNwkPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyPredefinedNwkPanId->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyPredefinedNwkPanId->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint8 predefinedNwkPanId = 0; stream >> payloadLenght >> parameter >> predefinedNwkPanId; m_networkConfiguration.predefinedNetworkPanId = static_cast(predefinedNwkPanId); qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyPredefinedNwkPanId->sequenceNumber() << replyPredefinedNwkPanId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.predefinedNetworkPanId; // We don't make use of link key for now // Note: reading the network key returns "InavlidParameter". Might be for security reasons which is good! // Read channel ZigbeeInterfaceDeconzReply *replyChannel = requestReadParameter(Deconz::ParameterCurrentChannel); connect(replyChannel, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyChannel](){ if (replyChannel->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyChannel->sequenceNumber() << replyChannel->command() << Deconz::ParameterCurrentChannel << "finished with error" << replyChannel->statusCode(); readNetworkParametersReply->m_statusCode = replyChannel->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyChannel->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint8 channel = 0; stream >> payloadLenght >> parameter >> channel; m_networkConfiguration.currentChannel = channel; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyChannel->sequenceNumber() << replyChannel->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Current channel:" << m_networkConfiguration.currentChannel; // Read permit join status ZigbeeInterfaceDeconzReply *replyPermitJoin = requestReadParameter(Deconz::ParameterPermitJoin); connect(replyPermitJoin, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyPermitJoin](){ if (replyPermitJoin->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyPermitJoin->sequenceNumber() << replyPermitJoin->command() << Deconz::ParameterPermitJoin << "finished with error" << replyPermitJoin->statusCode(); readNetworkParametersReply->m_statusCode = replyPermitJoin->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyPermitJoin->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; stream >> payloadLenght >> parameter; //m_networkConfiguration.currentChannel = channel; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyPermitJoin->sequenceNumber() << replyPermitJoin->command() << static_cast(parameter) << "finished successfully" << ZigbeeUtils::convertByteArrayToHexString(replyPermitJoin->responseData()); // Read protocol version ZigbeeInterfaceDeconzReply *replyProtocolVersion = requestReadParameter(Deconz::ParameterProtocolVersion); connect(replyProtocolVersion, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyProtocolVersion](){ if (replyProtocolVersion->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyProtocolVersion->sequenceNumber() << replyProtocolVersion->command() << Deconz::ParameterProtocolVersion << "finished with error" << replyProtocolVersion->statusCode(); readNetworkParametersReply->m_statusCode = replyProtocolVersion->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyProtocolVersion->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint16 protocolVersion = 0; stream >> payloadLenght >> parameter >> protocolVersion; m_networkConfiguration.protocolVersion = protocolVersion; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyProtocolVersion->sequenceNumber() << replyProtocolVersion->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Protocol version:" << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.protocolVersion); // Read network updat id ZigbeeInterfaceDeconzReply *replyNetworkUpdateId = requestReadParameter(Deconz::ParameterNetworkUpdateId); connect(replyNetworkUpdateId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyNetworkUpdateId](){ if (replyNetworkUpdateId->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyNetworkUpdateId->sequenceNumber() << replyNetworkUpdateId->command() << Deconz::ParameterNetworkUpdateId << "finished with error" << replyNetworkUpdateId->statusCode(); readNetworkParametersReply->m_statusCode = replyNetworkUpdateId->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyNetworkUpdateId->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint8 networkUpdateId = 0; stream >> payloadLenght >> parameter >> networkUpdateId; m_networkConfiguration.networkUpdateId = networkUpdateId; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyNetworkUpdateId->sequenceNumber() << replyNetworkUpdateId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Network update ID:" << m_networkConfiguration.networkUpdateId; // Make sure the watchdog is available for this version if (m_networkConfiguration.protocolVersion < 0x0108) { qCDebug(dcZigbeeController()) << "The watchdog api is available since protocol version 0x0108. The watchdog is not required for this version"; m_watchdogTimer->stop(); // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished emit networkConfigurationParameterChanged(m_networkConfiguration); readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess; emit readNetworkParametersReply->finished(); return; } // Reset the watchdog in any case resetControllerWatchdog(); // Read watchdog timeout ZigbeeInterfaceDeconzReply *replyWatchdogTimeout = requestReadParameter(Deconz::ParameterWatchdogTtl); connect(replyWatchdogTimeout, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyWatchdogTimeout](){ if (replyWatchdogTimeout->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyWatchdogTimeout->sequenceNumber() << replyWatchdogTimeout->command() << Deconz::ParameterWatchdogTtl << "finished with error" << replyWatchdogTimeout->statusCode(); readNetworkParametersReply->m_statusCode = replyWatchdogTimeout->statusCode(); emit readNetworkParametersReply->finished(); return; } QDataStream stream(replyWatchdogTimeout->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 parameter = 0; quint32 watchdogTimeout = 0; stream >> payloadLenght >> parameter >> watchdogTimeout; m_networkConfiguration.watchdogTimeout = watchdogTimeout; qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyWatchdogTimeout->sequenceNumber() << replyWatchdogTimeout->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Watchdog timeout:" << m_networkConfiguration.watchdogTimeout; // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished emit networkConfigurationParameterChanged(m_networkConfiguration); readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess; emit readNetworkParametersReply->finished(); // We ignore the frame counter for now, since we let the firmware take control }); }); }); }); }); }); }); }); }); }); }); }); }); }); }); return readNetworkParametersReply; } DeconzDeviceState ZigbeeBridgeControllerDeconz::parseDeviceStateFlag(quint8 deviceStateFlag) { DeconzDeviceState state; state.networkState = static_cast(deviceStateFlag & 0x03); state.apsDataConfirm = (deviceStateFlag & 0x04); state.apsDataIndication = (deviceStateFlag & 0x08); state.configurationChanged = (deviceStateFlag & 0x10); state.apsDataRequestFreeSlots = (deviceStateFlag & 0x20); return state; } void ZigbeeBridgeControllerDeconz::reconnectConrtroller() { m_interface->reconnectController(); } void ZigbeeBridgeControllerDeconz::readDataIndication() { if (m_readIndicationReply) { // There is alreay a read indication reply in the queue, let finish that first before creating a new one return; } ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication(); // Set this as the current read indication reply so we don't request more than one at the time m_readIndicationReply = reply; connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ // Allow to send the next read indication reply if required m_readIndicationReply = nullptr; if (reply->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Could not read data indication." << "SQN:" << reply->sequenceNumber() << reply->statusCode(); // FIXME: set an appropriate error return; } // APS data indication received, process the content qCDebug(dcZigbeeController()) << "Reading data indication finished successfully" << "SQN:" << reply->sequenceNumber(); processDataIndication(reply->responseData()); }); } void ZigbeeBridgeControllerDeconz::readDataConfirm() { if (m_readConfirmReply) { // There is alreay a read confirm reply in the queue, let finish that first before creating a new one return; } ZigbeeInterfaceDeconzReply *reply = requestQuerySendDataConfirm(); // Set this as the current read confirm reply so we don't request more than one at the time m_readConfirmReply = reply; connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ // Allow to send the next read confirm reply if required m_readConfirmReply = nullptr; if (reply->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Could not read data confirm." << "SQN:" << reply->sequenceNumber() << reply->statusCode(); // FIXME: set an appropriate error return; } // APS data confirm received, process the content qCDebug(dcZigbeeController()) << "Reading data confirm finished successfully" << "SQN:" << reply->sequenceNumber(); processDataConfirm(reply->responseData()); }); } void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceState) { qCDebug(dcZigbeeController()) << "Process device state notification" << deviceState; if (m_networkState != deviceState.networkState) { qCDebug(dcZigbeeController()) << "Network state changed" << deviceState.networkState; m_networkState = deviceState.networkState; emit networkStateChanged(m_networkState); } if (m_apsFreeSlotsAvailable != deviceState.apsDataRequestFreeSlots) { if (!deviceState.apsDataRequestFreeSlots) { // Warn only if the network is up if (m_networkState == Deconz::NetworkStateConnected) { qCWarning(dcZigbeeController()) << "The APS request table is full on the device. Cannot send requests until the queue gets processed on the controller."; m_apsFreeSlotsAvailable = false; } return; } else { m_apsFreeSlotsAvailable = true; qCDebug(dcZigbeeController()) << "The APS request table is free again. Sending the next request"; sendNextRequest(); } } if (m_networkState != Deconz::NetworkStateConnected) return; // Note: read a data indication before a confirmation since the confirmation arrives after a related indication normally // Check if we have to read a data indication message if (deviceState.apsDataIndication) { readDataIndication(); } // Check if we have a response to read for a request if (deviceState.apsDataConfirm) { readDataConfirm(); } } void ZigbeeBridgeControllerDeconz::processDataIndication(const QByteArray &data) { // APS data indication QDataStream stream(data); stream.setByteOrder(QDataStream::LittleEndian); quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 reserved = 0; quint16 asduLength = 0; Zigbee::ApsdeDataIndication indication; stream >> payloadLenght >> deviceStateFlag; stream >> indication.destinationAddressMode; Zigbee::DestinationAddressMode destinationAddressMode = static_cast(indication.destinationAddressMode); if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress) stream >> indication.destinationShortAddress; if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress) stream >> indication.destinationIeeeAddress; stream >> indication.destinationEndpoint >> indication.sourceAddressMode; Zigbee::SourceAddressMode sourceAddressMode = static_cast(indication.sourceAddressMode); if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress) stream >> indication.sourceShortAddress; if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress) stream >> indication.sourceIeeeAddress; stream >> indication.sourceEndpoint >> indication.profileId >> indication.clusterId >> asduLength; // Fill asdu data for (int i = 0; i < asduLength; i++) { quint8 byte = 0; stream >> byte; indication.asdu.append(static_cast(byte)); } stream >> reserved >> reserved >> indication.lqi >> reserved >> reserved >> reserved >> reserved >> indication.rssi; // Print the information for debugging qCDebug(dcZigbeeController()) << indication; qCDebug(dcZigbeeAps()) << "APSDE-DATA.indication" << indication; emit apsDataIndicationReceived(indication); // Process the device state in order to check if we have to request another indication DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag); qCDebug(dcZigbeeController()) << "Verify device state after data indication response" << deviceState; if (deviceState.apsDataIndication) { readDataIndication(); } } void ZigbeeBridgeControllerDeconz::processDataConfirm(const QByteArray &data) { QDataStream stream(data); stream.setByteOrder(QDataStream::LittleEndian); Zigbee::ApsdeDataConfirm confirm; quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; stream >> payloadLenght >> deviceStateFlag; stream >> confirm.requestId >> confirm.destinationAddressMode; if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeGroup || confirm.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress) stream >> confirm.destinationShortAddress; if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress) stream >> confirm.destinationIeeeAddress; stream >> confirm.destinationEndpoint >> confirm.sourceEndpoint >> confirm.zigbeeStatusCode; // Print the information for debugging qCDebug(dcZigbeeController()) << confirm; qCDebug(dcZigbeeAps()) << "APSDE-DATA.confirm" << confirm; emit apsDataConfirmReceived(confirm); // Process the device state in order to check if we have to request another indication DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag); qCDebug(dcZigbeeController()) << "Verify device state after data confirmation response" << deviceState; if (deviceState.apsDataConfirm) { readDataConfirm(); } } void ZigbeeBridgeControllerDeconz::processMacPoll(const QByteArray &data) { qCDebug(dcZigbeeController()) << "MAC Poll command received" << ZigbeeUtils::convertByteArrayToHexString(data); } void ZigbeeBridgeControllerDeconz::processMacBeacon(const QByteArray &data) { qCDebug(dcZigbeeController()) << "Simplified beacon command received" << ZigbeeUtils::convertByteArrayToHexString(data); } void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available) { qCDebug(dcZigbeeController()) << "Interface available changed" << available; if (!available) { // Clean up any pending replies while (!m_replyQueue.isEmpty()) { ZigbeeInterfaceDeconzReply *reply = m_replyQueue.dequeue(); reply->abort(); } m_sequenceNumber = 0; m_apsFreeSlotsAvailable = true; m_watchdogTimer->stop(); } setAvailable(available); sendNextRequest(); } void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &package) { QDataStream stream(package); stream.setByteOrder(QDataStream::LittleEndian); quint8 commandInt = 0; quint8 sequenceNumber = 0; quint8 statusInt = 0; quint16 frameLength = 0; stream >> commandInt >> sequenceNumber >> statusInt >> frameLength; QByteArray data = package.right(package.length() - 5); Deconz::Command command = static_cast(commandInt); Deconz::StatusCode status = static_cast(statusInt); qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); // Check if this is the response to the current active reply if (m_currentReply && m_currentReply->sequenceNumber() == sequenceNumber && m_currentReply->command() == command) { // If the controller is busy, let's try again once the device state reports free slots if (status == Deconz::StatusCodeBusy) { qCWarning(dcZigbeeController()) << "Controller busy. Rescheduling command."; m_apsFreeSlotsAvailable = false; m_replyQueue.prepend(m_currentReply); m_currentReply = nullptr; return; } m_currentReply->m_responseData = data; m_currentReply->m_statusCode = status; emit m_currentReply->finished(); // Note: the current reply will be cleaned up in the finished slot return; } // We got a notification, lets set the current sequence number to the notification id, // so the next request will be a continuous increase m_sequenceNumber = sequenceNumber + 1; // No request for this data, lets check which notification and process the data switch (command) { case Deconz::CommandDeviceStateChanged: { quint8 deviceStateFlag = 0; stream >> deviceStateFlag; processDeviceState(parseDeviceStateFlag(deviceStateFlag)); break; } case Deconz::CommandMacPoll: { processMacPoll(data); break; } case Deconz::CommandMacBeacon: { processMacBeacon(data); break; } default: qCWarning(dcZigbeeController()) << "Unhandled interface package received" << command << "SQN:" << sequenceNumber << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); break; } } void ZigbeeBridgeControllerDeconz::resetControllerWatchdog() { QByteArray parameterData; QDataStream stream(¶meterData, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << m_watchdogTimeout; ZigbeeInterfaceDeconzReply *reply = requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Could not reset the application watchdog on the deCONZ controller." << reply->statusCode(); return; } qCDebug(dcZigbeeController()) << "Reset application watchdog on the deCONZ controller successfully"; m_watchdogTimer->start(); }); } bool ZigbeeBridgeControllerDeconz::enable(const QString &serialPort, qint32 baudrate) { return m_interface->enable(serialPort, baudrate); } void ZigbeeBridgeControllerDeconz::disable() { m_interface->disable(); } QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState) { QDebugStateSaver saver(debug); debug.nospace() << "DeviceState("; switch (deviceState.networkState) { case Deconz::NetworkStateJoining: debug.nospace() << "Joining, "; break; case Deconz::NetworkStateLeaving: debug.nospace() << "Leaving, "; break; case Deconz::NetworkStateOffline: debug.nospace() << "Offline, "; break; case Deconz::NetworkStateConnected: debug.nospace() << "Connected, "; break; } debug.nospace() << "Confirm=" << static_cast(deviceState.apsDataConfirm) << ", "; debug.nospace() << "Indication=" << static_cast(deviceState.apsDataIndication) << ", "; debug.nospace() << "ConfigChanged=" << static_cast(deviceState.configurationChanged) << ", "; debug.nospace() << "CanSend=" << deviceState.apsDataRequestFreeSlots << ")"; return debug; } QDebug operator<<(QDebug debug, const DeconzNetworkConfiguration &configuration) { QDebugStateSaver saver(debug); debug.nospace() << "Network configuration: " << "\n"; debug.nospace() << " - Node type:" << configuration.nodeType << "\n"; debug.nospace() << " - IEEE address: " << configuration.ieeeAddress.toString() << "\n"; debug.nospace() << " - NWK address: " << ZigbeeUtils::convertUint16ToHexString(configuration.shortAddress) << "\n"; debug.nospace() << " - PAN ID: " << ZigbeeUtils::convertUint16ToHexString(configuration.panId) << " (" << configuration.panId << ")\n"; debug.nospace() << " - Use predefined network PAN ID: " << configuration.predefinedNetworkPanId << "\n"; debug.nospace() << " - Extended PAN ID: " << ZigbeeUtils::convertUint64ToHexString(configuration.extendedPanId) << "\n"; debug.nospace() << " - APS Extended PAN ID: " << ZigbeeUtils::convertUint64ToHexString(configuration.apsExtendedPanId) << "\n"; debug.nospace() << " - Trust center IEEE address: " << configuration.trustCenterAddress.toString() << "\n"; debug.nospace() << " - Channel mask: " << ZigbeeChannelMask(configuration.channelMask) << "\n"; debug.nospace() << " - Channel: " << configuration.currentChannel << "\n"; debug.nospace() << " - Security mode: " << configuration.securityMode << "\n"; debug.nospace() << " - Protocol version: " << ZigbeeUtils::convertUint16ToHexString(configuration.protocolVersion) << "\n"; debug.nospace() << " - Network update ID: " << ZigbeeUtils::convertByteToHexString(configuration.networkUpdateId) << "\n"; debug.nospace() << " - Watchdog TTL: " << configuration.watchdogTimeout << "\n"; return debug; }