diff --git a/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.cpp b/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.cpp new file mode 100644 index 0000000..bf368ef --- /dev/null +++ b/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.cpp @@ -0,0 +1,188 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "firmwareupdatehandlernxp.h" +#include "loggingcategory.h" + +#include +#include +#include +#include + +/* + * [UpdateProvider] + * updateBinary=/usr/bin/maveo-zigbee-flasher + * updateReleaseFile=/usr/share/maveo-zigbee-flasher/firmware/release.json + */ + +FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(QObject *parent) : + QObject(parent) +{ + +} + +FirmwareUpdateHandlerNxp::FirmwareUpdateHandlerNxp(const QFileInfo &updateProviderConfgigurationFileInfo, QObject *parent) : + QObject(parent), + m_updateProviderConfgigurationFileInfo(updateProviderConfgigurationFileInfo) +{ + qCDebug(dcZigbeeController()) << "Initialize NXP firmware update provider"; + + QSettings configuration(updateProviderConfgigurationFileInfo.absoluteFilePath(), QSettings::IniFormat, this); + configuration.beginGroup("UpdateProvider"); + + // Verify the update provider binary + m_updateBinary = configuration.value("updateBinary").toString(); + if (m_updateBinary.isEmpty()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary is not specified in" << m_updateProviderConfgigurationFileInfo.absoluteFilePath(); + return; + } + + QFileInfo updateBinaryFileInfo(m_updateBinary); + if (!updateBinaryFileInfo.exists()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary does not exist" << updateBinaryFileInfo.absoluteFilePath(); + return; + } + + if (!updateBinaryFileInfo.isExecutable()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the update binary is not executable" << updateBinaryFileInfo.absoluteFilePath(); + return; + } + + // Verify update release file and corresponding update file and version + m_updateReleaseFilePath = configuration.value("updateReleaseFile").toString(); + QFileInfo releaseFileInfo(m_updateReleaseFilePath); + if (! releaseFileInfo.exists()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the release file does not exist" << releaseFileInfo.absoluteFilePath(); + return; + } + + // Read the release file + QFile releaseFile(releaseFileInfo.absoluteFilePath()); + if (!releaseFile.open(QFile::ReadOnly)) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but could not open update release file" << releaseFileInfo.absoluteFilePath() << releaseFile.errorString(); + return; + } + + QByteArray releaseFileData = releaseFile.readAll(); + releaseFile.close(); + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(releaseFileData, &jsonError); + if (jsonError.error != QJsonParseError::NoError) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but could not parse the release file" << releaseFileInfo.absoluteFilePath() << jsonError.errorString(); + return; + } + + QVariantMap releaseMap = jsonDoc.toVariant().toMap(); + if (!releaseMap.contains("version")) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the release file does not contain available version information" << releaseFileInfo.absoluteFilePath(); + return; + } + + m_availableFirmwareVersion = releaseMap.value("version").toString(); + if (m_availableFirmwareVersion.isEmpty()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the version in the release file is empty" << releaseFileInfo.absoluteFilePath(); + return; + } + + if (!releaseMap.contains("firmware")) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the release file does not contain available firmware file name" << releaseFileInfo.absoluteFilePath(); + return; + } + + m_availableFirmwareFileName = releaseMap.value("firmware").toString(); + if (m_availableFirmwareFileName.isEmpty()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the firmware file path in the release file is empty" << releaseFileInfo.absoluteFilePath(); + return; + } + + QFileInfo firmwareFileInfo(releaseFileInfo.canonicalPath() + QDir::separator() + m_availableFirmwareFileName); + if (!firmwareFileInfo.exists()) { + qCWarning(dcZigbeeController()) << "Update provider configuration available but the update firmware file does not exist" << firmwareFileInfo.absoluteFilePath(); + return; + } + + qCDebug(dcZigbeeController()) << "Firmware update prvider available:" << firmwareFileInfo.absoluteFilePath() << "Version:" << m_availableFirmwareVersion; + configuration.endGroup(); + + m_updateProcess = new QProcess(this); + m_updateProcess->setProcessChannelMode(QProcess::MergedChannels); + m_updateProcess->setProgram(m_updateBinary); + + connect(m_updateProcess, QOverload::of(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus) { + qCDebug(dcZigbeeController()) << "Update process finihed" << exitCode << exitStatus; + if (exitCode != 0) { + emit updateFinished(false); + } else { + emit updateFinished(true); + } + }); + + connect(m_updateProcess, &QProcess::readyRead, [=]() { + qCDebug(dcZigbeeController()) << "Update process:" << qUtf8Printable(m_updateProcess->readAll().replace(0x1B, '\r')); + }); + + m_valid = true; +} + +bool FirmwareUpdateHandlerNxp::updateRunning() const +{ + return m_updateProcess->state() != QProcess::NotRunning; +} + +bool FirmwareUpdateHandlerNxp::isValid() const +{ + return m_valid; +} + +QString FirmwareUpdateHandlerNxp::updateBinary() const +{ + return m_updateBinary; +} + +QString FirmwareUpdateHandlerNxp::availableFirmwareFileName() const +{ + return m_availableFirmwareFileName; +} + +QString FirmwareUpdateHandlerNxp::availableFirmwareVersion() const +{ + return m_availableFirmwareVersion; +} + +void FirmwareUpdateHandlerNxp::startUpdate() +{ + if (!m_updateProcess) { + qCWarning(dcZigbeeController()) << "Cannot start update process. The updater is not vaild."; + return; + } + + qCDebug(dcZigbeeController()) << "Starting firmware update for NXP controller"; + qCDebug(dcZigbeeController()) << "Firmware file:" << m_availableFirmwareFileName; + qCDebug(dcZigbeeController()) << "Firmware version:" << m_availableFirmwareVersion; + m_updateProcess->start(); +} diff --git a/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.h b/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.h new file mode 100644 index 0000000..b42e7f0 --- /dev/null +++ b/libnymea-zigbee/backends/nxp/firmwareupdatehandlernxp.h @@ -0,0 +1,67 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 FIRMWAREUPDATEHANDLERNXP_H +#define FIRMWAREUPDATEHANDLERNXP_H + +#include +#include +#include + +class FirmwareUpdateHandlerNxp : public QObject +{ + Q_OBJECT +public: + explicit FirmwareUpdateHandlerNxp(QObject *parent = nullptr); + FirmwareUpdateHandlerNxp(const QFileInfo &updateProviderConfgigurationFileInfo, QObject *parent = nullptr); + + bool updateRunning() const; + bool isValid() const; + + QString updateBinary() const; + QString availableFirmwareFileName() const; + QString availableFirmwareVersion() const; + + void startUpdate(); + +signals: + void updateRunningChanged(bool updateRunning); + void updateFinished(bool success); + +private: + QFileInfo m_updateProviderConfgigurationFileInfo; + bool m_valid = false; + QProcess *m_updateProcess = nullptr; + + QString m_updateBinary; + QString m_updateReleaseFilePath; + QString m_availableFirmwareFileName; + QString m_availableFirmwareVersion; + +}; + +#endif // FIRMWAREUPDATEHANDLERNXP_H diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp index 98eacae..f76232b 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp @@ -298,6 +298,7 @@ void ZigbeeInterfaceNxp::disable() delete m_serialPort; m_serialPort = nullptr; + + qCDebug(dcZigbeeInterface()) << "Interface disabled."; setAvailable(false); - qCDebug(dcZigbeeInterface()) << "Interface disabled"; } diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp index a865511..043d77b 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp @@ -1,3 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "zigbeeinterfacenxpreply.h" ZigbeeNetworkRequest ZigbeeInterfaceNxpReply::networkRequest() const diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h index 42f829a..4864520 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h @@ -1,3 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 ZIGBEEINTERFACENXPREPLY_H #define ZIGBEEINTERFACENXPREPLY_H diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp index af031e5..92861c6 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp @@ -1,4 +1,31 @@ -#include "zigbeebridgecontrollernxp.h" +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "zigbeebridgecontrollernxp.h" #include "loggingcategory.h" #include "zigbeeutils.h" @@ -205,6 +232,40 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSendRequest(const Zig return interfaceReply; } +bool ZigbeeBridgeControllerNxp::updateAvailable(const QString ¤tVersion) +{ + if (!m_firmwareUpdateHandler) + return false; + + if (m_firmwareUpdateHandler->availableFirmwareVersion() != currentVersion) { + return true; + } + + return false; +} + +QString ZigbeeBridgeControllerNxp::updateFirmwareVersion() const +{ + if (!m_firmwareUpdateHandler) + return QString(); + + return m_firmwareUpdateHandler->availableFirmwareVersion(); +} + +void ZigbeeBridgeControllerNxp::startFirmwareUpdate() +{ + if (!m_firmwareUpdateHandler) + return; + + m_updateRunning = true; + emit updateRunningChanged(m_updateRunning); + + qCDebug(dcZigbeeController()) << "Disable UART interface for update..."; + m_interface->disable(); + + m_firmwareUpdateHandler->startUpdate(); +} + ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent) { // Create the reply @@ -346,6 +407,44 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestEnqueueSendDataIeeeAd return createReply(Nxp::CommandSendApsDataRequest, m_sequenceNumber, "Request send ASP data request to IEEE address", message, this); } +void ZigbeeBridgeControllerNxp::initializeUpdateProvider() +{ + QFileInfo updateProviderConfigurationFileInfo = QFileInfo(m_settingsDirectory.canonicalPath() + QDir::separator() + "zigbee-update-provider-nxp.conf"); + if (!updateProviderConfigurationFileInfo.exists()) { + qCDebug(dcZigbeeController()) << "No firmware update provider configured for this controller."; + return; + } + + qCDebug(dcZigbeeController()) << "Found update provider configuration" << updateProviderConfigurationFileInfo.absoluteFilePath(); + m_firmwareUpdateHandler = new FirmwareUpdateHandlerNxp(updateProviderConfigurationFileInfo, this); + if (!m_firmwareUpdateHandler->isValid()) { + qCWarning(dcZigbeeController()) << "The firmware update provider is not valid. The firmware update is not available for this NXP zigbee controller."; + m_firmwareUpdateHandler->deleteLater(); + m_firmwareUpdateHandler = nullptr; + return; + } + + connect(m_firmwareUpdateHandler, &FirmwareUpdateHandlerNxp::updateFinished, this, [this](bool success){ + if (success) { + qCDebug(dcZigbeeController()) << "Update finished successfully. Reenable controller"; + enable(m_serialPort, m_baudrate); + m_updateRunning = false; + emit updateRunningChanged(m_updateRunning); + } else { + qCWarning(dcZigbeeController()) << "Update finished with errors. Can not continue."; + m_updateRunning = false; + emit updateRunningChanged(m_updateRunning); + + // Fixme: check if we should to retry + disable(); + } + }); + + qCDebug(dcZigbeeController()) << "The firmware update provider is valid. The firmware of this NXP controller can be updated."; + m_canUpdate = true; + emit canUpdateChanged(m_canUpdate); +} + void ZigbeeBridgeControllerNxp::onInterfaceAvailableChanged(bool available) { qCDebug(dcZigbeeController()) << "Interface available changed" << available; @@ -501,6 +600,8 @@ void ZigbeeBridgeControllerNxp::sendNextRequest() bool ZigbeeBridgeControllerNxp::enable(const QString &serialPort, qint32 baudrate) { + m_serialPort = serialPort; + m_baudrate = baudrate; return m_interface->enable(serialPort, baudrate); } diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h index e1cd2ed..e39f42f 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h @@ -1,3 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 ZIGBEEBRIDGECONTROLLERNXP_H #define ZIGBEEBRIDGECONTROLLERNXP_H @@ -11,6 +38,7 @@ #include "zigbeenetworkkey.h" #include "zigbeenetworkrequest.h" #include "zigbeebridgecontroller.h" +#include "firmwareupdatehandlernxp.h" #include "interface/zigbeeinterfacenxp.h" #include "interface/zigbeeinterfacenxpreply.h" @@ -55,6 +83,9 @@ public: // APS ZigbeeInterfaceNxpReply *requestSendRequest(const ZigbeeNetworkRequest &request); + bool updateAvailable(const QString ¤tVersion) override; + QString updateFirmwareVersion() const override; + void startFirmwareUpdate() override; signals: void controllerStateChanged(ControllerState controllerState); @@ -62,6 +93,10 @@ signals: private: ZigbeeInterfaceNxp *m_interface = nullptr; + FirmwareUpdateHandlerNxp *m_firmwareUpdateHandler = nullptr; + QString m_serialPort; + qint32 m_baudrate; + ControllerState m_controllerState = ControllerStateNotRunning; quint8 m_sequenceNumber = 0; @@ -75,6 +110,8 @@ private: ZigbeeInterfaceNxpReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0); ZigbeeInterfaceNxpReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0); +protected: + void initializeUpdateProvider() override; private slots: void onInterfaceAvailableChanged(bool available); diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp index 3c0e002..35fcb55 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp @@ -1,3 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "zigbeenetworknxp.h" #include "loggingcategory.h" #include "zigbeeutils.h" @@ -13,6 +40,20 @@ ZigbeeNetworkNxp::ZigbeeNetworkNxp(QObject *parent) : connect(m_controller, &ZigbeeBridgeControllerNxp::controllerStateChanged, this, &ZigbeeNetworkNxp::onControllerStateChanged); connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataConfirmReceived, this, &ZigbeeNetworkNxp::onApsDataConfirmReceived); connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataIndicationReceived, this, &ZigbeeNetworkNxp::onApsDataIndicationReceived); + connect(m_controller, &ZigbeeBridgeControllerNxp::canUpdateChanged, this, [](bool canUpdate){ + if (canUpdate) { + qCDebug(dcZigbeeNetwork()) << "The controller of this network can be updated."; + } else { + qCDebug(dcZigbeeNetwork()) << "The controller of this network can not be updated."; + } + }); + + connect(m_controller, &ZigbeeBridgeControllerNxp::updateRunningChanged, this, [this](bool updateRunning){ + if (updateRunning) { + qCDebug(dcZigbeeNetwork()) << "The controller is performing an update."; + setState(StateUpdating); + } + }); m_permitJoinRefreshTimer = new QTimer(this); m_permitJoinRefreshTimer->setInterval(250 * 1000); @@ -97,6 +138,64 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::setPermitJoin(quint16 shortAddress, quint8 return sendRequest(request); } +bool ZigbeeNetworkNxp::processVersionReply(ZigbeeInterfaceNxpReply *reply) +{ + qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); + + if (reply->timendOut()) { + m_reconnectCounter++; + if (m_reconnectCounter >= 3) { + if (m_controller->canUpdate()) { + qCDebug(dcZigbeeNetwork()) << "Unable to get controller version."; + qCDebug(dcZigbeeNetwork()) << "Firmware update provider available. Try to flash the firmware, maybe that fixes the problem."; + if (!m_controller->updateRunning()) { + clearSettings(); + qCDebug(dcZigbeeNetwork()) << "Starting firmware update..."; + m_controller->startFirmwareUpdate(); + } else { + qCWarning(dcZigbeeNetwork()) << "There is already an update running..."; + } + return false; + } else { + qCDebug(dcZigbeeNetwork()) << "Unable to get controller version. There is no firmware upgrade available. Giving up."; + return false; + } + } + + qCWarning(dcZigbeeNetwork()) << "Failed to read firmware version. Retry" << m_reconnectCounter << "/ 3"; + ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ + processVersionReply(reply); + }); + + return false; + } + + QByteArray payload = reply->responseData(); + QDataStream stream(&payload, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; + stream >> major >> minor >> patch >> sdkVersion; + + QString version = QString("%1.%2.%3").arg(major).arg(minor).arg(patch); + QString versionString = QString("%1 - %2").arg(version).arg(sdkVersion); + qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; + m_controller->setFirmwareVersion(versionString); + + if (m_controller->canUpdate()) { + if (m_controller->updateAvailable(version)) { + qCDebug(dcZigbeeNetwork()) << "There is an update available for the this zigbee controller:" << version << "-->" << m_controller->updateFirmwareVersion(); + qCDebug(dcZigbeeNetwork()) << "Starting firmware update..."; + m_controller->startFirmwareUpdate(); + return false; + } else { + qCDebug(dcZigbeeNetwork()) << "The current firmware is up to date."; + } + } + + return true; +} + void ZigbeeNetworkNxp::handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication) { // Check if this is a device announcement @@ -150,12 +249,15 @@ void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available) { qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more"); if (available) { + m_reconnectCounter = 0; + ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ + // Retry or firmware upgrade if available + if (!processVersionReply(reply)) + return; - m_controller->refreshControllerState(); - // Get network state, depending on the controller state - - //reset(); - //factoryResetNetwork(); + m_controller->refreshControllerState(); + }); } else { setState(StateOffline); } @@ -167,19 +269,14 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr switch (controllerState) { case ZigbeeBridgeControllerNxp::ControllerStateRunning: { setState(StateStarting); + m_reconnectCounter = 0; + qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); - QByteArray payload = reply->responseData(); - QDataStream stream(&payload, QIODevice::ReadOnly); - stream.setByteOrder(QDataStream::LittleEndian); - quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; - stream >> major >> minor >> patch >> sdkVersion; - - QString versionString = QString ("%1.%2.%3 - %4").arg(major).arg(minor).arg(patch).arg(sdkVersion); - qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; - m_controller->setFirmwareVersion(versionString); + // Retry or firmware upgrade if available + if (!processVersionReply(reply)) + return; qCDebug(dcZigbeeNetwork()) << "Get the current network state"; ZigbeeInterfaceNxpReply *reply = m_controller->requestNetworkState(); @@ -201,7 +298,6 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr setPanId(panId); setChannel(channel); - // Initialize the coordinator node if not already done. if (m_coordinatorNode) { @@ -242,8 +338,9 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); - //FIXME: error handling + // Retry or firmware upgrade if available + if (!processVersionReply(reply)) + return; QByteArray payload = reply->responseData(); QDataStream stream(&payload, QIODevice::ReadOnly); diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h index b46e55b..205eb0d 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h @@ -1,3 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 ZIGBEENETWORKNXP_H #define ZIGBEENETWORKNXP_H @@ -25,9 +52,12 @@ private: ZigbeeBridgeControllerNxp *m_controller = nullptr; bool m_networkRunning = false; QHash m_pendingReplies; + int m_reconnectCounter = 0; QTimer *m_permitJoinRefreshTimer = nullptr; + bool processVersionReply(ZigbeeInterfaceNxpReply *reply); + // ZDO void handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication); diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro index 403df96..602d43d 100644 --- a/libnymea-zigbee/libnymea-zigbee.pro +++ b/libnymea-zigbee/libnymea-zigbee.pro @@ -8,6 +8,7 @@ SOURCES += \ backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \ backends/deconz/zigbeebridgecontrollerdeconz.cpp \ backends/deconz/zigbeenetworkdeconz.cpp \ + backends/nxp/firmwareupdatehandlernxp.cpp \ backends/nxp/interface/zigbeeinterfacenxp.cpp \ backends/nxp/interface/zigbeeinterfacenxpreply.cpp \ backends/nxp/zigbeebridgecontrollernxp.cpp \ @@ -63,6 +64,7 @@ HEADERS += \ backends/deconz/interface/zigbeeinterfacedeconzreply.h \ backends/deconz/zigbeebridgecontrollerdeconz.h \ backends/deconz/zigbeenetworkdeconz.h \ + backends/nxp/firmwareupdatehandlernxp.h \ backends/nxp/interface/nxp.h \ backends/nxp/interface/zigbeeinterfacenxp.h \ backends/nxp/interface/zigbeeinterfacenxpreply.h \ diff --git a/libnymea-zigbee/zigbeebridgecontroller.cpp b/libnymea-zigbee/zigbeebridgecontroller.cpp index 004ad82..271e8ee 100644 --- a/libnymea-zigbee/zigbeebridgecontroller.cpp +++ b/libnymea-zigbee/zigbeebridgecontroller.cpp @@ -43,6 +43,43 @@ bool ZigbeeBridgeController::available() const return m_available; } +bool ZigbeeBridgeController::canUpdate() const +{ + return m_canUpdate; +} + +bool ZigbeeBridgeController::updateRunning() const +{ + return m_updateRunning; +} + +bool ZigbeeBridgeController::updateAvailable(const QString ¤tVersion) +{ + Q_UNUSED(currentVersion) + return false; +} + +QString ZigbeeBridgeController::updateFirmwareVersion() const +{ + return QString(); +} + +void ZigbeeBridgeController::startFirmwareUpdate() +{ + qCWarning(dcZigbeeController()) << "Cannot start firmware update. The feature is not implemented for this controller."; +} + +void ZigbeeBridgeController::setSettingsDirectory(const QDir &settingsDirectory) +{ + qCDebug(dcZigbeeController()) << "Using settings directory" << settingsDirectory.canonicalPath(); + m_settingsDirectory = settingsDirectory; + + // Check if we have an update provider for the controller, if so, + // we can perform an automatic updates + + initializeUpdateProvider(); +} + void ZigbeeBridgeController::setAvailable(bool available) { if (m_available == available) @@ -62,3 +99,8 @@ void ZigbeeBridgeController::setFirmwareVersion(const QString &firmwareVersion) m_firmwareVersion = firmwareVersion; emit firmwareVersionChanged(m_firmwareVersion); } + +void ZigbeeBridgeController::initializeUpdateProvider() +{ + qCDebug(dcZigbeeController()) << "No firmware update provider configured for this controller."; +} diff --git a/libnymea-zigbee/zigbeebridgecontroller.h b/libnymea-zigbee/zigbeebridgecontroller.h index ac54e34..102c700 100644 --- a/libnymea-zigbee/zigbeebridgecontroller.h +++ b/libnymea-zigbee/zigbeebridgecontroller.h @@ -28,8 +28,10 @@ #ifndef ZIGBEEBRIDGECONTROLLER_H #define ZIGBEEBRIDGECONTROLLER_H +#include #include #include + #include "zigbee.h" Q_DECLARE_METATYPE(QSerialPort::SerialPortError); @@ -37,6 +39,8 @@ Q_DECLARE_METATYPE(QSerialPort::SerialPortError); class ZigbeeBridgeController : public QObject { Q_OBJECT + friend class ZigbeeNetwork; + public: explicit ZigbeeBridgeController(QObject *parent = nullptr); ~ZigbeeBridgeController() = default; @@ -44,17 +48,35 @@ public: QString firmwareVersion() const; bool available() const; + bool canUpdate() const; + bool updateRunning() const; + + virtual bool updateAvailable(const QString ¤tVersion); + virtual QString updateFirmwareVersion() const; + virtual void startFirmwareUpdate(); + protected: QString m_firmwareVersion; bool m_available = false; + bool m_canUpdate = false; + bool m_updateRunning = false; + QDir m_settingsDirectory; void setAvailable(bool available); void setFirmwareVersion(const QString &firmwareVersion); + virtual void initializeUpdateProvider(); + +private: + void setSettingsDirectory(const QDir &settingsDirectory); + signals: void availableChanged(bool available); void firmwareVersionChanged(const QString &firmwareVersion); + void canUpdateChanged(bool canUpdate); + void updateRunningChanged(bool updateRunning); + // APS notifications void apsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm); void apsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication); diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 1d98193..b157c94 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -56,11 +56,11 @@ QString ZigbeeNetwork::settingsFilenName() const void ZigbeeNetwork::setSettingsFileName(const QString &settingsFileName) { - if (m_settingsFileName == settingsFileName) - return; - m_settingsFileName = settingsFileName; emit settingsFileNameChanged(m_settingsFileName); + + m_settingsDirectory = QFileInfo(m_settingsFileName).canonicalPath(); + bridgeController()->setSettingsDirectory(m_settingsDirectory); } QString ZigbeeNetwork::serialPortName() const diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index 6cfb801..6ff911f 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -28,6 +28,7 @@ #ifndef ZIGBEENETWORK_H #define ZIGBEENETWORK_H +#include #include #include @@ -49,6 +50,7 @@ public: StateUninitialized, StateOffline, StateStarting, + StateUpdating, StateRunning, StateStopping }; @@ -134,6 +136,7 @@ private: // Network storage QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; + QDir m_settingsDirectory; QList m_nodes; QList m_uninitializedNodes;