From 7de087967c9a0b41db70b8b20d2b7c47997b41d6 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 7 Mar 2023 11:49:01 +0100 Subject: [PATCH] Shelly: Reconfigure CoAP on gen 1 shellies if they don't send any data --- shelly/integrationpluginshelly.cpp | 72 ++++++++++++++++++++++++++++-- shelly/integrationpluginshelly.h | 3 ++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/shelly/integrationpluginshelly.cpp b/shelly/integrationpluginshelly.cpp index c05ba9d1..7275b042 100644 --- a/shelly/integrationpluginshelly.cpp +++ b/shelly/integrationpluginshelly.cpp @@ -843,13 +843,16 @@ void IntegrationPluginShelly::onMulticastMessageReceived(const QHostAddress &sou return; } + qCDebug(dcShelly) << "CoIoT multicast message for" << thing->name() << ":" << qUtf8Printable(jsonDoc.toJson()); + QVariantMap map = jsonDoc.toVariant().toMap(); + thing->setStateValue("connected", true); foreach (Thing *child, myThings().filterByParentId(thing->id())) { child->setStateValue("connected", true); } + // Remember when we recieved the last update + thing->setProperty("lastCoIoTMessage", QDateTime::currentDateTime()); - qCDebug(dcShelly) << "CoIoT multicast message for" << thing->name() << ":" << qUtf8Printable(jsonDoc.toJson()); - QVariantMap map = jsonDoc.toVariant().toMap(); // Some states are calculated from multiple values in the list and we'll need to keep them temporarily int red = 0, green = 0, blue = 0, white = 0; @@ -1215,9 +1218,10 @@ void IntegrationPluginShelly::updateStatus() void IntegrationPluginShelly::fetchStatusGen1(Thing *thing) { + QHostAddress address = getIP(thing); QUrl url; url.setScheme("http"); - url.setHost(getIP(thing).toString()); + url.setHost(address.toString()); url.setPath("/status"); url.setUserName(thing->paramValue(usernameParamTypeMap.value(thing->thingClassId())).toString()); url.setPassword(thing->paramValue(passwordParamTypeMap.value(thing->thingClassId())).toString()); @@ -1261,6 +1265,53 @@ void IntegrationPluginShelly::fetchStatusGen1(Thing *thing) thing->setStateValue("currentVersion", updateMap.value("old_version").toString()); thing->setStateValue("availableVersion", updateMap.value("new_version").toString()); thing->setStateValue("updateStatus", updateStatusMap.value(updateMap.value("status").toString())); + + + // Sometimes, some shellies just stop to send CoIoT messages until they are rebooted... + // If communication to the shelly per se works fine, but we didn't receive anything in more than a minute, + // let's reconfigure coap and reboot the shelly + if (thing->property("lastCoIoTMessage").toDateTime().addSecs(10 * 60) < QDateTime::currentDateTime()) { + qCInfo(dcShelly()) << "Shelly" << thing->name() << "didn't send us a CoIoT message in a minute. Reconfiguring CoIoT and rebooting it."; + QUrlQuery query; + QHostAddress address = getIP(thing); + query.addQueryItem("coiot_enable", "true"); + if (thing->paramValue("coapMode").toString() == "unicast") { + QHostAddress ourAddress; + foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { + foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) { + if (address.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) { + ourAddress = addressEntry.ip(); + break; + } + } + } + if (!ourAddress.isNull()) { + query.addQueryItem("coiot_peer", ourAddress.toString() + ":5683"); + } else { + qCWarning(dcShelly) << "Unable to determine a matching interface for CoIoT unicast. Falling back to multicast."; + query.addQueryItem("coiot_peer", "mcast"); + } + } else { + query.addQueryItem("coiot_peer", "mcast"); + } + QNetworkRequest setCoIoTRequest = createHttpRequest(thing, "/settings", query); + QNetworkReply *reply = hardwareManager()->networkManager()->get(setCoIoTRequest); + connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, thing, [this, thing, reply](){ + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcShelly()) << "Failed to reconfigure coap on shelly" << thing->name(); + } + + QNetworkRequest rebootRequest = createHttpRequest(thing, "/reboot"); + QNetworkReply *reply = hardwareManager()->networkManager()->get(rebootRequest); + connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, thing, [reply](){ + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcShelly()) << "Failed to send reboot request to shelly."; + } + }); + }); + } }); } @@ -1792,6 +1843,21 @@ void IntegrationPluginShelly::handleInputEvent(Thing *thing, const QString &butt } } +QNetworkRequest IntegrationPluginShelly::createHttpRequest(Thing *thing, const QString &path, const QUrlQuery &urlQuery) +{ + QUrl url; + url.setScheme("http"); + url.setHost(getIP(thing).toString()); + url.setPort(80); + url.setPath(path); + if (!thing->paramValue("username").toString().isEmpty()) { + url.setUserName(thing->paramValue("username").toString()); + url.setPassword(thing->paramValue("password").toString()); + } + url.setQuery(urlQuery); + return QNetworkRequest(url); +} + QVariantMap IntegrationPluginShelly::createRpcRequest(const QString &method) { QVariantMap map; diff --git a/shelly/integrationpluginshelly.h b/shelly/integrationpluginshelly.h index e926591d..f633c698 100644 --- a/shelly/integrationpluginshelly.h +++ b/shelly/integrationpluginshelly.h @@ -37,6 +37,8 @@ #include #include +#include +#include class ZeroConfServiceBrowser; class PluginTimer; @@ -79,6 +81,7 @@ private: void handleInputEvent(Thing *thing, const QString &buttonName, const QString &inputEventString, int inputEventCount); + QNetworkRequest createHttpRequest(Thing *thing, const QString &path, const QUrlQuery &urlQuery = QUrlQuery()); QVariantMap createRpcRequest(const QString &method); private: