moved to http based RPC API

This commit is contained in:
Boernsman 2021-02-24 14:13:33 +01:00 committed by Simon Stürz
parent fda9c23728
commit cd122e89b0
6 changed files with 114 additions and 92 deletions

View File

@ -73,12 +73,10 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
if (!m_sunnyWebBoxCommunication) {
m_sunnyWebBoxCommunication = new SunnyWebBoxCommunication(hardwareManager()->networkManager(), this);
}
qCDebug(dcSma()) << "Setup thing" << thing->name();
if (!m_refreshTimer) {
qCDebug(dcSma()) << "Starting refresh timer";
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(30);
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginSma::onRefreshTimer);
}
@ -93,15 +91,23 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
return;
}
}
SunnyWebBox *sunnyWebBox = new SunnyWebBox(m_sunnyWebBoxCommunication, QHostAddress(thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString()), this);
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
connect(sunnyWebBox, &SunnyWebBox::devicesReceived, this, &IntegrationPluginSma::onDevicesReceived);
connect(sunnyWebBox, &SunnyWebBox::processDataReceived, this, &IntegrationPluginSma::onProcessDataReceived);
connect(sunnyWebBox, &SunnyWebBox::parameterChannelsReceived, this, &IntegrationPluginSma::onParameterChannelsReceived);
m_sunnyWebBoxes.insert(thing, sunnyWebBox);
connect(info, &ThingSetupInfo::aborted, this, [thing, this] { m_sunnyWebBoxes.remove(thing);});
if (m_sunnyWebBoxes.contains(thing)) {
qCDebug(dcSma()) << "Setup after reconfiguration, cleaning up...";
m_sunnyWebBoxes.take(thing)->deleteLater();
}
SunnyWebBox *sunnyWebBox = new SunnyWebBox(hardwareManager()->networkManager(), QHostAddress(thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString()), this);
connect(info, &ThingSetupInfo::aborted, sunnyWebBox, &SunnyWebBox::deleteLater);
connect(sunnyWebBox, &SunnyWebBox::destroyed, this, [thing, this] { m_sunnyWebBoxes.remove(thing);});
QString requestId = sunnyWebBox->getPlantOverview();
m_asyncSetup.insert(requestId, info);
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, info, [sunnyWebBox, info, this] {
qCDebug(dcSma()) << "Received plant overview, finishing setup";
info->finish(Thing::ThingErrorNoError);
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
connect(sunnyWebBox, &SunnyWebBox::devicesReceived, this, &IntegrationPluginSma::onDevicesReceived);
connect(sunnyWebBox, &SunnyWebBox::processDataReceived, this, &IntegrationPluginSma::onProcessDataReceived);
connect(sunnyWebBox, &SunnyWebBox::parameterChannelsReceived, this, &IntegrationPluginSma::onParameterChannelsReceived);
m_sunnyWebBoxes.insert(info->thing(), sunnyWebBox);
});
return info->finish(Thing::ThingErrorNoError);
} else if (thing->thingClassId() == inverterThingClassId) {
@ -127,6 +133,7 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
void IntegrationPluginSma::postSetupThing(Thing *thing)
{
qCDebug(dcSma()) << "Post setup thing" << thing->name();
if (thing->thingClassId() == sunnyWebBoxThingClassId) {
SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing);
if (!sunnyWebBox)
@ -167,9 +174,10 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
m_sunnyWebBoxes.take(thing)->deleteLater();
}
if (myThings().filterByThingClassId(sunnyWebBoxThingClassId).isEmpty()) {
m_sunnyWebBoxCommunication->deleteLater();
m_sunnyWebBoxCommunication = nullptr;
if (myThings().isEmpty()) {
qCDebug(dcSma()) << "Stopping timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
m_refreshTimer = nullptr;
}
}
@ -210,7 +218,6 @@ void IntegrationPluginSma::onDevicesReceived(const QString &messageId, QList<Sun
info->finish(Thing::ThingErrorNoError);
}
Thing *thing = m_sunnyWebBoxes.key(static_cast<SunnyWebBox *>(sender()));
if (!thing)
return;
@ -254,16 +261,6 @@ void IntegrationPluginSma::onParameterChannelsReceived(const QString &messageId,
qCDebug(dcSma()) << "Parameter channels received" << deviceKey << parameterChannels;
}
SunnyWebBox * IntegrationPluginSma::createSunnyWebBoxConnection(Thing *thing)
{
SunnyWebBox *sunnyWebBox = new SunnyWebBox(m_sunnyWebBoxCommunication, QHostAddress(thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString()), this);
m_sunnyWebBoxes.insert(thing, sunnyWebBox);
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
connect(sunnyWebBox, &SunnyWebBox::parameterChannelsReceived, this, &IntegrationPluginSma::onParameterChannelsReceived);
//connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
return sunnyWebBox;
}
void IntegrationPluginSma::setupChild(ThingSetupInfo *info, Thing *parentThing)
{
Q_UNUSED(info)

View File

@ -71,7 +71,6 @@ private:
QHash<QString, ThingActionInfo *> m_asyncActions;
SunnyWebBoxCommunication *m_sunnyWebBoxCommunication = nullptr;
SunnyWebBox *createSunnyWebBoxConnection(Thing *thing);
void setupChild(ThingSetupInfo *info, Thing *parentThing);
void getData(Thing *thing);
};

View File

@ -6,13 +6,11 @@ QT += \
SOURCES += \
integrationpluginsma.cpp \
sunnywebbox.cpp \
sunnywebboxcommunication.cpp \
host.cpp \
discovery.cpp
HEADERS += \
integrationpluginsma.h \
sunnywebbox.h \
sunnywebboxcommunication.h \
host.h \
discovery.h

View File

@ -35,37 +35,42 @@
#include "QJsonObject"
#include "QJsonArray"
SunnyWebBox::SunnyWebBox(SunnyWebBoxCommunication *communication, const QHostAddress &hostAddress, QObject *parrent) :
SunnyWebBox::SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent) :
QObject(parrent),
m_hostAddresss(hostAddress),
m_communication(communication)
m_networkManager(networkAccessManager)
{
qCDebug(dcSma()) << "SunnyWebBox: Creating Sunny Web Box connection";
//TODO connect communication with socket state;
connect(m_communication, &SunnyWebBoxCommunication::messageReceived, this, &SunnyWebBox::onMessageReceived);
}
SunnyWebBox::~SunnyWebBox()
{
qCDebug(dcSma()) << "SunnyWebBox: Deleting Sunny Web Box connection";
}
QString SunnyWebBox::getPlantOverview()
{
return m_communication->sendMessage(m_hostAddresss, "GetPlantOverview");
return sendMessage(m_hostAddresss, "GetPlantOverview");
}
QString SunnyWebBox::getDevices()
{
return m_communication->sendMessage(m_hostAddresss, "GetDevices");
return sendMessage(m_hostAddresss, "GetDevices");
}
QString SunnyWebBox::getProcessDataChannels(const QString &deviceId)
{
QJsonObject params;
params["device"] = deviceId;
return m_communication->sendMessage(m_hostAddresss, "GetProcessDataChannels", params);
return sendMessage(m_hostAddresss, "GetProcessDataChannels", params);
}
QString SunnyWebBox::getProcessData(const QStringList &deviceKeys)
{
QJsonObject params;
params["device"] = deviceKeys.first();
return m_communication->sendMessage(m_hostAddresss, "GetProcessData", params);
return sendMessage(m_hostAddresss, "GetProcessData", params);
}
QString SunnyWebBox::getParameterChannels(const QString &deviceKey)
@ -76,7 +81,7 @@ QString SunnyWebBox::getParameterChannels(const QString &deviceKey)
deviceObj["key"] = deviceKey;
devicesArray.append(deviceObj);
paramsObj["devices"] = devicesArray;
return m_communication->sendMessage(m_hostAddresss, "GetParameterChannels", paramsObj);
return sendMessage(m_hostAddresss, "GetParameterChannels", paramsObj);
}
QString SunnyWebBox::getParameters(const QStringList &deviceKeys)
@ -87,7 +92,7 @@ QString SunnyWebBox::getParameters(const QStringList &deviceKeys)
deviceObj["key"] = deviceKeys.first(); //TODO
devicesArray.append(deviceObj);
paramsObj["devices"] = devicesArray;
return m_communication->sendMessage(m_hostAddresss, "GetParameter", paramsObj);
return sendMessage(m_hostAddresss, "GetParameter", paramsObj);
}
QString SunnyWebBox::setParameters(const QString &deviceKey, const QHash<QString, QVariant> &channels)
@ -106,11 +111,12 @@ QString SunnyWebBox::setParameters(const QString &deviceKey, const QHash<QString
deviceObj["channels"] = channelsArray;
devicesArray.append(deviceObj);
paramsObj["devices"] = devicesArray;
return m_communication->sendMessage(m_hostAddresss, "SetParameter", paramsObj);
return sendMessage(m_hostAddresss, "SetParameter", paramsObj);
}
void SunnyWebBox::setHostAddress(const QHostAddress &address)
{
qCDebug(dcSma()) << "SunnyWebBox: Setting host address to" << address.toString();
m_hostAddresss = address;
}
@ -119,12 +125,8 @@ QHostAddress SunnyWebBox::hostAddress()
return m_hostAddresss;
}
void SunnyWebBox::onMessageReceived(const QHostAddress &address, const QString &messageId, const QString &messageType, const QVariantMap &result)
void SunnyWebBox::parseMessage(const QString &messageId, const QString &messageType, const QVariantMap &result)
{
if (address != m_hostAddresss) {
return;
}
if (messageType == "GetPlantOverview") {
Overview overview;
QVariantList overviewList = result.value("overview").toList();
@ -215,3 +217,70 @@ void SunnyWebBox::onMessageReceived(const QHostAddress &address, const QString &
qCWarning(dcSma()) << "Unknown message type" << messageType;
}
}
QString SunnyWebBox::sendMessage(const QHostAddress &address, const QString &procedure)
{
return sendMessage(address, procedure, QJsonObject());
}
QString SunnyWebBox::sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject &params)
{
QString requestId = QUuid::createUuid().toString().remove('{').remove('-').left(14);
QJsonDocument doc;
QJsonObject obj;
obj["format"] = "JSON";
obj["id"] = requestId;
obj["proc"] = procedure;
obj["version"] = "1.0";
if (!params.isEmpty()) {
obj.insert("params", params);
}
doc.setObject(obj);
QUrl url;
url.setHost(address.toString());
url.setPath("/rpc");
url.setPort(80);
url.setScheme("http");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
QByteArray data = doc.toJson(QJsonDocument::JsonFormat::Compact);
data.prepend("RPC=");
QNetworkReply *reply = m_networkManager->post(request, data);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, address, requestId, reply]{
//int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray data = reply->readAll();
qCDebug(dcSma()) << "Received reply" << data;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(dcSma()) << "Could not parse JSON" << error.errorString();
return;
}
if (!doc.isObject()) {
qCWarning(dcSma()) << "JSON is not an Object";
return;
}
QVariantMap map = doc.toVariant().toMap();
if (map["version"] != "1.0") {
qCWarning(dcSma()) << "API version not supported" << map["version"];
return;
}
if (map.contains("proc") && map.contains("result")) {
QString requestType = map["proc"].toString();
QString requestId = map["id"].toString();
QVariantMap result = map.value("result").toMap();
parseMessage(requestId, requestType, result);
} else {
qCWarning(dcSma()) << "Missing proc or result value";
}
});
return requestId;
}

View File

@ -73,7 +73,8 @@ public:
double value;
};
explicit SunnyWebBox(SunnyWebBoxCommunication *communication, const QHostAddress &hostAddress, QObject *parrent = 0);
explicit SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent = 0);
~SunnyWebBox();
QString getPlantOverview(); // Returns an object with the following plant data: PAC, E-TODAY, E-TOTAL, MODE, ERROR
QString getDevices(); //Returns a hierarchical list of all detected plant devices.
@ -89,10 +90,11 @@ public:
private:
QHostAddress m_hostAddresss;
SunnyWebBoxCommunication *m_communication = nullptr;
NetworkAccessManager *m_networkManager = nullptr;
public slots:
void onMessageReceived(const QHostAddress &address, const QString &messageId, const QString &messageType, const QVariantMap &result);
QString sendMessage(const QHostAddress &address, const QString &procedure);
QString sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject &params);
void parseMessage(const QString &messageId, const QString &messageType, const QVariantMap &result);
signals:
void connectedChanged(bool connected);

View File

@ -42,6 +42,7 @@ SunnyWebBoxCommunication::SunnyWebBoxCommunication(NetworkAccessManager *network
QObject(parent),
m_networkManager(networkAccessManager)
{
qCDebug(dcSma()) << "Creating SunnyWebBoxCommunictaion";
m_udpSocket = new QUdpSocket(this);
m_udpSocket->bind(QHostAddress::Any, m_port);
@ -63,51 +64,7 @@ SunnyWebBoxCommunication::SunnyWebBoxCommunication(NetworkAccessManager *network
});
}
QString SunnyWebBoxCommunication::sendMessage(const QHostAddress &address, const QString &procedure)
{
QString requestId = QUuid::createUuid().toString().remove('{').remove('-').left(14);
QJsonDocument doc;
QJsonObject obj;
obj["format"] = "JSON";
obj["id"] = requestId;
obj["proc"] = procedure;
obj["version"] = "1.0";
doc.setObject(obj);
QUrl url;
url.setHost(address.toString());
url.setPath("/rpc");
url.setScheme("http");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
QByteArray data = doc.toJson(QJsonDocument::JsonFormat::Compact);
QNetworkReply *reply = m_networkManager->post(request, data);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, address, requestId, reply]{
//int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray data = reply->readAll();
qCDebug(dcSma()) << "Received reply" << data;
datagramReceived(address, data);
});
/* if(!m_messageResponsePending) {
qCDebug(dcSma()) << "Send message" << data << address << m_port;
m_udpSocket->writeDatagram(data, address, m_port);
m_messageResponsePending = true;
} else {
if (m_messageQueue[address].length() < 40) {
qCDebug(dcSma()) << "Adding message to queue" << data << address << m_port;
m_messageQueue[address].append(data);
} else {
qCWarning(dcSma()) << "Message queue overflow";
return "";
}
}*/
return requestId;
}
QString SunnyWebBoxCommunication::sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject &params)
{