From 9bac68614837c61ccc0b0577460f6b2bd149c6d0 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 19 Oct 2021 00:14:08 +0200 Subject: [PATCH] Some cleanup --- garadget/Garadget.pro | 14 - garadget/garadget.pro | 12 + garadget/integrationpluginGaradget.cpp | 406 ------------------ garadget/integrationplugingaradget.cpp | 208 +++++++++ ...Garadget.h => integrationplugingaradget.h} | 40 +- ...et.json => integrationplugingaradget.json} | 4 +- nymea-plugins.pro | 1 + 7 files changed, 226 insertions(+), 459 deletions(-) delete mode 100644 garadget/Garadget.pro create mode 100644 garadget/garadget.pro delete mode 100644 garadget/integrationpluginGaradget.cpp create mode 100644 garadget/integrationplugingaradget.cpp rename garadget/{integrationpluginGaradget.h => integrationplugingaradget.h} (56%) rename garadget/{integrationpluginGaradget.json => integrationplugingaradget.json} (98%) diff --git a/garadget/Garadget.pro b/garadget/Garadget.pro deleted file mode 100644 index b38efde7..00000000 --- a/garadget/Garadget.pro +++ /dev/null @@ -1,14 +0,0 @@ -include($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri) - -QT += network - -PKGCONFIG += nymea-mqtt - -TARGET = $$qtLibraryTarget(nymea_integrationpluginGaradget) - -SOURCES += \ - integrationpluginGaradget.cpp - -HEADERS += \ - integrationpluginGaradget.h - diff --git a/garadget/garadget.pro b/garadget/garadget.pro new file mode 100644 index 00000000..d2f5044e --- /dev/null +++ b/garadget/garadget.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +QT += network + +PKGCONFIG += nymea-mqtt + +SOURCES += \ + integrationplugingaradget.cpp + +HEADERS += \ + integrationplugingaradget.h + diff --git a/garadget/integrationpluginGaradget.cpp b/garadget/integrationpluginGaradget.cpp deleted file mode 100644 index 77ddd049..00000000 --- a/garadget/integrationpluginGaradget.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* 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 "integrationpluginGaradget.h" -#include "plugininfo.h" - -#include -#include -#include -#include - -#include "hardwaremanager.h" -#include "network/networkaccessmanager.h" -#include "network/mqtt/mqttprovider.h" -#include "network/mqtt/mqttchannel.h" - -static QHash sonoff_basicPowerStateTypeIds = { - {"POWER1", sonoff_basicPowerStateTypeId}, -}; -static QHash> stateMaps = { - {sonoff_basicThingClassId, sonoff_basicPowerStateTypeIds}, -}; - -// bee add static id - -static QHash sonoff_basicstateStateTypeIds = { - {"closed", sonoff_basicStateStateTypeId}, -}; - -static QHash> stateStateMAPS = { - {sonoff_basicThingClassId, sonoff_basicstateStateTypeIds}, -}; - -static QHash sonoff_basicClosingOutputStateTypeIds = { - {"CLOSE1", sonoff_basicClosingOutputStateTypeId}, -}; - -static QHash> stateClosingOutputMaps = { - {sonoff_basicThingClassId, sonoff_basicClosingOutputStateTypeIds}, -}; - -static QHash sonoff_basicOpeningOutputStateTypeIds = { - {"OPEN1", sonoff_basicOpeningOutputStateTypeId}, -}; - -static QHash> stateOpeningMaps = { - {sonoff_basicThingClassId, sonoff_basicOpeningOutputStateTypeIds}, -}; - -static QHash sonoff_basicMovingStateTypeIds = { - {"STOP1", sonoff_basicMovingStateTypeId}, -}; - -static QHash> stateMovingMaps = { - {sonoff_basicThingClassId, sonoff_basicMovingStateTypeIds}, -}; - - -IntegrationPluginGaradget::IntegrationPluginGaradget() -{ - // Helper maps for parent devices (aka sonoff_*) - m_ipAddressParamTypeMap[sonoff_basicThingClassId] = sonoff_basicThingIpAddressParamTypeId; - - - // Helper maps for all devices - m_connectedStateTypeMap[sonoff_basicThingClassId] = sonoff_basicConnectedStateTypeId; - - m_signalStrengthStateTypeMap[sonoff_basicThingClassId] = sonoff_basicSignalStrengthStateTypeId; - - // bee adders for extendedgaragestate - - m_stateStateTypeMap[sonoff_basicThingClassId] = sonoff_basicStateStateTypeId; - - m_movingStateTypeMap[sonoff_basicThingClassId] = sonoff_basicMovingStateTypeId; - - m_percentageStateTypeMap[sonoff_basicThingClassId] = sonoff_basicPercentageStateTypeId; - - m_closingOutputStateTypeMap[sonoff_basicThingClassId] = sonoff_basicClosingOutputStateTypeId; - - m_openingOutputStateTypeMap[sonoff_basicThingClassId] = sonoff_basicOpeningOutputStateTypeId; - -} - -IntegrationPluginGaradget::~IntegrationPluginGaradget() -{ -} - -void IntegrationPluginGaradget::init() -{ -} - -void IntegrationPluginGaradget::setupThing(ThingSetupInfo *info) -{ - Thing *thing = info->thing(); - - if (m_ipAddressParamTypeMap.contains(thing->thingClassId())) { -// ParamTypeId ipAddressParamTypeId = m_ipAddressParamTypeMap.value(thing->thingClassId()); - -// QHostAddress deviceAddress = QHostAddress(thing->paramValue(ipAddressParamTypeId).toString()); - // ipaddress is defined here but not sure how to get rid of and still have the mqtt channel. Garadget only works with the broker. - QHostAddress deviceAddress = QHostAddress("192.168.1.174"); - if (deviceAddress.isNull()) { - qCWarning(dcGaradget) << "Not a valid IP address given for IP address parameter"; - //: Error setting up thing - return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The given IP address is not valid.")); - } -// MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel(thing->id().toString().remove(QRegExp("[{}-]")), deviceAddress ); - MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel("garage", deviceAddress ); - if (!channel) { - qCWarning(dcGaradget) << "Failed to create MQTT channel."; - //: Error setting up thing - return info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings.")); - } - - qCDebug(dcGaradget) << "initial setup instructions:" << deviceAddress.toString() << channel; -/* - QUrl url(QString("http://%1/cm").arg(deviceAddress.toString())); - QUrlQuery query; - QMap configItems; - configItems.insert("MqttHost", channel->serverAddress().toString()); - configItems.insert("MqttPort", QString::number(channel->serverPort())); - configItems.insert("MqttClient", channel->clientId()); - configItems.insert("MqttUser", channel->username()); - configItems.insert("MqttPassword", channel->password()); - configItems.insert("Topic", "sonoff"); - configItems.insert("FullTopic", channel->topicPrefixList().first() + "/%topic%/"); - - QStringList configList; - foreach (const QString &key, configItems.keys()) { - configList << key + ' ' + configItems.value(key); - } - QString fullCommand = "Backlog " + configList.join(';'); - query.addQueryItem("cmnd", fullCommand.toUtf8().toPercentEncoding()); - - url.setQuery(query); - - QNetworkRequest request(url); - QNetworkReply *reply = hardwareManager()->networkManager()->get(request); - connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(reply, &QNetworkReply::finished, info, [this, info, channel, reply](){ - if (reply->error() != QNetworkReply::NoError) { - qCDebug(dcTasmota) << "Sonoff thing setup call failed:" << reply->error() << reply->errorString() << reply->readAll(); - hardwareManager()->mqttProvider()->releaseChannel(channel); - //: Error setting up thing - info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("Could not connect to Tasmota device.")); - return; - } -*/ -//BEE mod - - m_mqttChannels.insert(info->thing(), channel); - connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGaradget::onClientConnected); - connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGaradget::onClientDisconnected); - connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGaradget::onPublishReceived); - qCDebug(dcGaradget) << "Garadget setup complete"; - info->finish(Thing::ThingErrorNoError); - foreach (Thing *child, myThings()) { - if (child->parentId() == info->thing()->id()) { - // Already have child devices... We're done here - return; - } - } -// }); - -// insert to reprogram client Garadget to new device/topic name - QJsonObject garadgetobj; - garadgetobj.insert("nme", QString(channel->topicPrefixList().first())); - QJsonDocument garadgetdoc(garadgetobj); - QByteArray garadgetdata = garadgetdoc.toJson(QJsonDocument::Compact); - QString jsonString(garadgetdata); - qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() << garadgetdata; - channel->publish("garadget/" + channel->topicPrefixList().first() + "/set-config", garadgetdata); - return; - } - - if (m_connectedStateTypeMap.contains(thing->thingClassId())) { - qCDebug(dcGaradget) << "entered connectedStateTypeMap function"; - Thing* parentDevice = myThings().findById(thing->parentId()); - StateTypeId connectedStateTypeId = m_connectedStateTypeMap.value(thing->thingClassId()); - thing->setStateValue(m_connectedStateTypeMap.value(thing->thingClassId()), parentDevice->stateValue(connectedStateTypeId)); - return info->finish(Thing::ThingErrorNoError); - } - - qCWarning(dcGaradget) << "Unhandled ThingClass in setupDevice" << thing->thingClassId(); -} - -void IntegrationPluginGaradget::thingRemoved(Thing *thing) -{ - qCDebug(dcGaradget) << "Device removed" << thing->name(); - if (m_mqttChannels.contains(thing)) { - qCDebug(dcGaradget) << "Releasing MQTT channel"; - MqttChannel* channel = m_mqttChannels.take(thing); - - // insert to reprogram client Garadget to new device/topic name - QJsonObject garadgetobj; - garadgetobj.insert("nme","garage" ); - QJsonDocument garadgetdoc(garadgetobj); - QByteArray garadgetdata = garadgetdoc.toJson(QJsonDocument::Compact); - QString jsonString(garadgetdata); - qCDebug(dcGaradget) << "garadget/" + QString(channel->topicPrefixList().first()) + "/set-config" << " topic = " << garadgetdata; - channel->publish("garadget/" + QString(channel->topicPrefixList().first()) + "/set-config", garadgetdata); - - hardwareManager()->mqttProvider()->releaseChannel(channel); - } -} - -void IntegrationPluginGaradget::executeAction(ThingActionInfo *info) -{ - Thing *thing = info->thing(); - Action action = info->action(); - - qCDebug(dcGaradget) << "executeAction function" << (thing->thingClassId()); - - if (m_closableStopActionTypeMap.contains(thing->thingClassId())) { - qCDebug(dcGaradget) << "Publishing: m_closableStopActionTypeMap" << "how did I get here?"; - Thing *parentDev = myThings().findById(thing->parentId()); - MqttChannel *channel = m_mqttChannels.value(parentDev); - if (!channel) { - qCWarning(dcGaradget()) << "No mqtt channel for this thing."; - return info->finish(Thing::ThingErrorHardwareNotAvailable); - } - ParamTypeId openingChannelParamTypeId = m_openingChannelParamTypeMap.value(thing->thingClassId()); - ParamTypeId closingChannelParamTypeId = m_closingChannelParamTypeMap.value(thing->thingClassId()); - if (action.actionTypeId() == m_closableOpenActionTypeMap.value(thing->thingClassId())) { - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF"); - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "ON"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "ON"); - } else if (action.actionTypeId() == m_closableCloseActionTypeMap.value(thing->thingClassId())) { - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF"); - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "ON"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "ON"); - } else { // Stop - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString() << "OFF"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(openingChannelParamTypeId).toString().toLower(), "OFF"); - qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString() << "OFF"; - channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + thing->paramValue(closingChannelParamTypeId).toString().toLower(), "OFF"); - } - return info->finish(Thing::ThingErrorNoError); - } - - if (thing->thingClassId() == sonoff_basicThingClassId) { - MqttChannel *channel = m_mqttChannels.value(thing); - if (!channel) { - qCWarning(dcGaradget()) << "No MQTT channel for this thing."; - info->finish(Thing::ThingErrorHardwareNotAvailable); - return; - } - qCDebug(dcGaradget) << "entered sonoff_basicThingClassId function" << (thing->thingClassId()); -// QString channelName = stateMaps.value(thing->thingClassId()).key(action.actionTypeId()); -// QByteArray payload = action.paramValue(action.actionTypeId()).toBool() ? "ON" : "OFF"; -// qCDebug(dcGaradget) << "Publishing:" << channel->topicPrefixList().first() + "/sonoff/cmnd/" + channelName << payload; -// channel->publish(channel->topicPrefixList().first() + "/sonoff/cmnd/" + channelName, payload); -// qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << payload; -// channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", payload); -// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); -// info->finish(Thing::ThingErrorNoError); -// return; - - if (action.actionTypeId() == sonoff_basicOpenActionTypeId) { - qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "open"; - channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "open"); -// qCDebug(dcGaradget) << "OpenAction stuff:" << action.actionTypeId() << action.paramValue(action.actionTypeId()); -// qCDebug(dcGaradget) << "OpenMapAction stuff:" << action.paramValue(action.actionTypeId()) << stateOpeningMaps.value(thing->thingClassId()).key(action.actionTypeId()); -// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); - info->finish(Thing::ThingErrorNoError); - return; - } - if (action.actionTypeId() == sonoff_basicCloseActionTypeId) { - qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "close"; - channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "close"); -// qCDebug(dcGaradget) << "CloseAction stuff:" << thing << thing->thingClassId() << thing->thingClassId(); -// qCDebug(dcGaradget) << "CloseAction newparameter" << thing->paramValue(stateStateMAPS) ; -// thing->setStateValue(stateStateMAPS.value(thing->thingClassId()).key(action.actionTypeId()),"closed"); -// qCDebug(dcGaradget) << "CloseMapAction stuff:" << stateStateMAPS.value(thing->thingClassId()).key(action.actionTypeId()); -// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); - info->finish(Thing::ThingErrorNoError); - return; - } - if (action.actionTypeId() == sonoff_basicStopActionTypeId) { - qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "stop"; - channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "stop"); -// qCDebug(dcGaradget) << "StopAction stuff:" << action.actionTypeId() << action.paramValue(action.actionTypeId()); -// qCDebug(dcGaradget) << "StopMapAction stuff:" << action.paramValue(action.actionTypeId()) << stateMovingMaps.value(thing->thingClassId()).key(action.actionTypeId()); -// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); - info->finish(Thing::ThingErrorNoError); - return; - } - - } - qCWarning(dcGaradget) << "Unhandled execute action call for device" << thing; -} - -void IntegrationPluginGaradget::onClientConnected(MqttChannel *channel) -{ - qCDebug(dcGaradget) << "Sonoff thing connected!"; - Thing *dev = m_mqttChannels.key(channel); - dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), true); - - foreach (Thing *child, myThings()) { - if (child->parentId() == dev->id()) { - child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), true); - } - } -} - -void IntegrationPluginGaradget::onClientDisconnected(MqttChannel *channel) -{ - qCDebug(dcGaradget) << "Sonoff thing disconnected!"; - Thing *dev = m_mqttChannels.key(channel); - dev->setStateValue(m_connectedStateTypeMap.value(dev->thingClassId()), false); - - foreach (Thing *child, myThings()) { - if (child->parentId() == dev->id()) { - child->setStateValue(m_connectedStateTypeMap.value(child->thingClassId()), false); - } - } -} - -void IntegrationPluginGaradget::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload) -{ - qCDebug(dcGaradget) << "Publish received from Sonoff thing:" << topic << qUtf8Printable(payload); - Thing *thing = m_mqttChannels.key(channel); - if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/POWER")) { - QString channelName = topic.split("/").last(); - - thing->setStateValue(stateMaps.value(thing->thingClassId()).value(channelName), payload == "ON"); - - // Legacy (deprecated) connected things via params - foreach (Thing *child, myThings().filterByParentId(thing->id())) { - if (child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString() != channelName) { - continue; - } - if (m_powerStateTypeMap.contains(child->thingClassId())) { - child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), payload == "ON"); - } - } - } - if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/STATE")) { - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcGaradget) << "Cannot parse JSON from Garadget device" << error.errorString(); - return; - } - QVariantMap dataMap = jsonDoc.toVariant().toMap(); - thing->setStateValue(m_signalStrengthStateTypeMap.value(thing->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt()); - - if (m_brightnessStateTypeMap.contains(thing->thingClassId())) { - thing->setStateValue(m_brightnessStateTypeMap.value(thing->thingClassId()), dataMap.value("Dimmer").toInt()); - } - - // Legacy (deprecated) connected things by params - foreach (Thing *child, myThings().filterByParentId(thing->id())) { - if (m_powerStateTypeMap.contains(child->thingClassId())) { - QString childChannel = child->paramValue(m_channelParamTypeMap.value(child->thingClassId())).toString(); - QString valueString = jsonDoc.toVariant().toMap().value(childChannel).toString(); - child->setStateValue(m_powerStateTypeMap.value(child->thingClassId()), valueString == "ON"); - } - child->setStateValue(m_signalStrengthStateTypeMap.value(child->thingClassId()), dataMap.value("Wifi").toMap().value("RSSI").toInt()); - } - } - if (topic.startsWith("garadget/" + channel->topicPrefixList().first() + "/status")) { - qCDebug(dcGaradget) << "Garadget status received " << payload; - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcGaradget) << "Cannot parse JSON from Garadget device" << error.errorString(); - return; - } - QVariantMap dataMap = jsonDoc.toVariant().toMap(); -// thing->setStateValue(m_signalStrengthStateTypeMap.value(thing->thingClassId()), dataMap.value("Gstatus").toMap().value("status")); - thing->setStateValue(m_stateStateTypeMap.value(thing->thingClassId()), dataMap.value("status").toMap().value("state")); - - qCDebug(dcGaradget) << "Datamap " << m_signalStrengthStateTypeMap.value(thing->thingClassId()); - qCDebug(dcGaradget) << "Garadget status decode " << jsonDoc; - } -} diff --git a/garadget/integrationplugingaradget.cpp b/garadget/integrationplugingaradget.cpp new file mode 100644 index 00000000..b5e66a9f --- /dev/null +++ b/garadget/integrationplugingaradget.cpp @@ -0,0 +1,208 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* 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 "integrationplugingaradget.h" +#include "plugininfo.h" + +#include +#include +#include +#include + +#include "hardwaremanager.h" +#include "network/networkaccessmanager.h" +#include "network/mqtt/mqttprovider.h" +#include "network/mqtt/mqttchannel.h" + +void IntegrationPluginGaradget::setupThing(ThingSetupInfo *info) +{ + Thing *thing = info->thing(); + +// QHostAddress deviceAddress = QHostAddress(thing->paramValue(garadgetThingIpAddressParamTypeId).toString()); + // ipaddress is defined here but not sure how to get rid of and still have the mqtt channel. Garadget only works with the broker. + + // Note from Michael: So, how does one configure the garadget device to connect to the MQTT broker? Is there a webinterface + // where you go with the browser and configure nymea's address there? If so, we could automate that so that the user + // doesn't have to do that. + QHostAddress deviceAddress = QHostAddress("192.168.1.174"); + if (deviceAddress.isNull()) { + qCWarning(dcGaradget) << "Not a valid IP address given for IP address parameter"; + //: Error setting up thing + return info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The given IP address is not valid.")); + } + + // Note from Michael: Does the clientId really need to be "garage"? Note that client ids must be unique on a single + // broker. So setting this to "garage" would prevent multiple garadgets from connecting to the same nymea instance. + // Better use something unique like the thingId if the garadget device supports it. Or, maybe, the user could configure + // it during setup using a paramType (like the ip address param stuff above). + MqttChannel *channel = hardwareManager()->mqttProvider()->createChannel("garage", deviceAddress ); + if (!channel) { + qCWarning(dcGaradget) << "Failed to create MQTT channel."; + //: Error setting up thing + return info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error creating MQTT channel. Please check MQTT server settings.")); + } + + qCDebug(dcGaradget) << "initial setup instructions:" << deviceAddress.toString() << channel; + + // Note from Michael: With other devices that use MQTT, normally we do a REST call to their webinterface just to configure + // nymea's IP on them with this code below. Could that work with the garadget too? +/* + QUrl url(QString("http://%1/cm").arg(deviceAddress.toString())); + QUrlQuery query; + QMap configItems; + configItems.insert("MqttHost", channel->serverAddress().toString()); + configItems.insert("MqttPort", QString::number(channel->serverPort())); + configItems.insert("MqttClient", channel->clientId()); + configItems.insert("MqttUser", channel->username()); + configItems.insert("MqttPassword", channel->password()); + configItems.insert("Topic", "sonoff"); + configItems.insert("FullTopic", channel->topicPrefixList().first() + "/%topic%/"); + + QStringList configList; + foreach (const QString &key, configItems.keys()) { + configList << key + ' ' + configItems.value(key); + } + QString fullCommand = "Backlog " + configList.join(';'); + query.addQueryItem("cmnd", fullCommand.toUtf8().toPercentEncoding()); + + url.setQuery(query); + + QNetworkRequest request(url); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, info, [this, info, channel, reply](){ + if (reply->error() != QNetworkReply::NoError) { + qCDebug(dcTasmota) << "Sonoff thing setup call failed:" << reply->error() << reply->errorString() << reply->readAll(); + hardwareManager()->mqttProvider()->releaseChannel(channel); + //: Error setting up thing + info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("Could not connect to Tasmota device.")); + return; + } +*/ +//BEE mod + + m_mqttChannels.insert(thing, channel); + connect(channel, &MqttChannel::clientConnected, this, &IntegrationPluginGaradget::onClientConnected); + connect(channel, &MqttChannel::clientDisconnected, this, &IntegrationPluginGaradget::onClientDisconnected); + connect(channel, &MqttChannel::publishReceived, this, &IntegrationPluginGaradget::onPublishReceived); + qCDebug(dcGaradget) << "Garadget setup complete"; + +// insert to reprogram client Garadget to new device/topic name + QJsonObject garadgetobj; + garadgetobj.insert("nme", QString(channel->topicPrefixList().first())); + QJsonDocument garadgetdoc(garadgetobj); + QByteArray garadgetdata = garadgetdoc.toJson(QJsonDocument::Compact); + QString jsonString(garadgetdata); + qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() << garadgetdata; + channel->publish("garadget/" + channel->topicPrefixList().first() + "/set-config", garadgetdata); + + + info->finish(Thing::ThingErrorNoError); +} + +void IntegrationPluginGaradget::thingRemoved(Thing *thing) +{ + qCDebug(dcGaradget) << "Device removed" << thing->name(); + qCDebug(dcGaradget) << "Releasing MQTT channel"; + MqttChannel* channel = m_mqttChannels.take(thing); + hardwareManager()->mqttProvider()->releaseChannel(channel); +} + +void IntegrationPluginGaradget::executeAction(ThingActionInfo *info) +{ + Thing *thing = info->thing(); + Action action = info->action(); + + qCDebug(dcGaradget) << "executeAction function" << (thing->thingClassId()); + + MqttChannel *channel = m_mqttChannels.value(info->thing()); + if (!channel) { + qCWarning(dcGaradget()) << "No mqtt channel for this thing."; + return info->finish(Thing::ThingErrorHardwareNotAvailable); + } + + + if (action.actionTypeId() == garadgetOpenActionTypeId) { + qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "open"; + channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "open"); +// qCDebug(dcGaradget) << "OpenAction stuff:" << action.actionTypeId() << action.paramValue(action.actionTypeId()); +// qCDebug(dcGaradget) << "OpenMapAction stuff:" << action.paramValue(action.actionTypeId()) << stateOpeningMaps.value(thing->thingClassId()).key(action.actionTypeId()); +// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); + info->finish(Thing::ThingErrorNoError); + return; + } + if (action.actionTypeId() == garadgetCloseActionTypeId) { + qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "close"; + channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "close"); +// qCDebug(dcGaradget) << "CloseAction stuff:" << thing << thing->thingClassId() << thing->thingClassId(); +// qCDebug(dcGaradget) << "CloseAction newparameter" << thing->paramValue(stateStateMAPS) ; +// thing->setStateValue(stateStateMAPS.value(thing->thingClassId()).key(action.actionTypeId()),"closed"); +// qCDebug(dcGaradget) << "CloseMapAction stuff:" << stateStateMAPS.value(thing->thingClassId()).key(action.actionTypeId()); +// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); + info->finish(Thing::ThingErrorNoError); + return; + } + if (action.actionTypeId() == garadgetStopActionTypeId) { + qCDebug(dcGaradget) << "Publishing:" << "garadget/" + channel->topicPrefixList().first() + "/command" << "stop"; + channel->publish("garadget/" + channel->topicPrefixList().first() + "/command", "stop"); +// qCDebug(dcGaradget) << "StopAction stuff:" << action.actionTypeId() << action.paramValue(action.actionTypeId()); +// qCDebug(dcGaradget) << "StopMapAction stuff:" << action.paramValue(action.actionTypeId()) << stateMovingMaps.value(thing->thingClassId()).key(action.actionTypeId()); +// thing->setStateValue(action.actionTypeId(), action.paramValue(action.actionTypeId())); + info->finish(Thing::ThingErrorNoError); + return; + } + + qCWarning(dcGaradget) << "Unhandled execute action call for garadget thing" << thing; +} + +void IntegrationPluginGaradget::onClientConnected(MqttChannel *channel) +{ + qCDebug(dcGaradget) << "Garadget thing connected!"; + Thing *dev = m_mqttChannels.key(channel); + dev->setStateValue(garadgetConnectedStateTypeId, true); +} + +void IntegrationPluginGaradget::onClientDisconnected(MqttChannel *channel) +{ + qCDebug(dcGaradget) << "Garadget thing disconnected!"; + Thing *dev = m_mqttChannels.key(channel); + dev->setStateValue(garadgetConnectedStateTypeId, false); +} + +void IntegrationPluginGaradget::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload) +{ + qCDebug(dcGaradget) << "Publish received from garadget thing:" << topic << qUtf8Printable(payload); + Thing *thing = m_mqttChannels.key(channel); + + // TODO: Fill in the "state" state with "open", "closed", "opening", "closing", "intermediate", dending on what we get from the garadget + if (topic.startsWith(channel->topicPrefixList().first() + "/sonoff/STATE")) { + thing->setStateValue(garadgetStateStateTypeId, "closed"); + } +} diff --git a/garadget/integrationpluginGaradget.h b/garadget/integrationplugingaradget.h similarity index 56% rename from garadget/integrationpluginGaradget.h rename to garadget/integrationplugingaradget.h index f6ff6123..68302c76 100644 --- a/garadget/integrationpluginGaradget.h +++ b/garadget/integrationplugingaradget.h @@ -39,15 +39,14 @@ class IntegrationPluginGaradget: public IntegrationPlugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginGaradget.json") + Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugingaradget.json") Q_INTERFACES(IntegrationPlugin) public: - explicit IntegrationPluginGaradget(); - ~IntegrationPluginGaradget(); + explicit IntegrationPluginGaradget() = default; + ~IntegrationPluginGaradget() = default; - void init() override; void setupThing(ThingSetupInfo *info) override; void thingRemoved(Thing *thing) override; void executeAction(ThingActionInfo *info) override; @@ -59,39 +58,6 @@ private slots: private: QHash m_mqttChannels; - - // Helpers for parent devices (the ones starting with sonoff) - QHash m_ipAddressParamTypeMap; - QHash > m_attachedDeviceParamTypeIdMap; - - // helpers from bee - QHash m_stateParamTypeMap; - QHash m_movingParamTypeMap; - QHash m_percentageParamTypeMap; - QHash m_closingOutputParamTypeMap; - QHash m_openingOutputParamTypeMap; - - QHash m_stateStateTypeMap; - QHash m_movingStateTypeMap; - QHash m_percentageStateTypeMap; - QHash m_closingOutputStateTypeMap; - QHash m_openingOutputStateTypeMap; - - // Helpers for child devices (virtual ones, starting with Garadget) - QHash m_channelParamTypeMap; - QHash m_openingChannelParamTypeMap; - QHash m_closingChannelParamTypeMap; - QHash m_powerStateTypeMap; - - QHash m_closableOpenActionTypeMap; - QHash m_closableCloseActionTypeMap; - QHash m_closableStopActionTypeMap; - - // Helpers for both devices - QHash m_connectedStateTypeMap; - QHash m_signalStrengthStateTypeMap; - - QHash m_brightnessStateTypeMap; }; #endif // INTEGRATIONPLUGINGARADGET_H diff --git a/garadget/integrationpluginGaradget.json b/garadget/integrationplugingaradget.json similarity index 98% rename from garadget/integrationpluginGaradget.json rename to garadget/integrationplugingaradget.json index 60b780c7..4d3aa93d 100644 --- a/garadget/integrationpluginGaradget.json +++ b/garadget/integrationplugingaradget.json @@ -1,6 +1,6 @@ { "name": "Garadget", - "displayName": "Sonoff-Garadget", + "displayName": "Garadget", "id": "476781af-ad41-423b-b9bb-f308f2aff339", "vendors": [ { @@ -9,7 +9,7 @@ "id": "22d59bee-79cc-4dea-b742-30ac6bdcf2c9", "thingClasses": [ { - "name": "sonoff_basic", + "name": "garadget", "displayName": "Garadget", "id": "e808b8ae-7608-41ce-8444-892f0648a4d3", "setupMethod": "JustAdd", diff --git a/nymea-plugins.pro b/nymea-plugins.pro index a8f3990e..40fd2fd6 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -22,6 +22,7 @@ PLUGIN_DIRS = \ fastcom \ flowercare \ fronius \ + garadget \ genericelements \ genericthings \ goecharger \