609 lines
30 KiB
C++
609 lines
30 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
* 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 "integrationpluginfronius.h"
|
|
#include "plugintimer.h"
|
|
#include "network/networkaccessmanager.h"
|
|
|
|
#include <QUrl>
|
|
#include <QUrlQuery>
|
|
#include <QJsonDocument>
|
|
#include <QPointer>
|
|
#include "plugininfo.h"
|
|
#include <QDebug>
|
|
|
|
// Notes: Test IPs: 93.82.221.82 | 88.117.152.99
|
|
|
|
IntegrationPluginFronius::IntegrationPluginFronius(QObject *parent): IntegrationPlugin(parent){
|
|
|
|
}
|
|
|
|
void IntegrationPluginFronius::setupThing(ThingSetupInfo *info)
|
|
{
|
|
qCDebug(dcFronius()) << "Setting up a new thing:" << info->thing()->name();
|
|
|
|
Thing *thing = info->thing();
|
|
|
|
if (thing->thingClassId() == dataloggerThingClassId) {
|
|
//check if a data logger is already added with this IPv4Address
|
|
foreach(FroniusLogger *logger, m_froniusLoggers.keys()){
|
|
if(logger->hostAddress() == thing->paramValue(dataloggerThingLoggerHostParamTypeId).toString()){
|
|
//this logger at this IPv4 address is already added
|
|
qCWarning(dcFronius()) << "thing at " << thing->paramValue(dataloggerThingLoggerHostParamTypeId).toString() << " already added!";
|
|
info->finish(Thing::ThingErrorThingInUse);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Perform a HTTP request on the given IPv4Address to find things
|
|
QUrl requestUrl;
|
|
requestUrl.setScheme("http");
|
|
requestUrl.setHost(thing->paramValue(dataloggerThingLoggerHostParamTypeId).toString());
|
|
requestUrl.setPath("/solar_api/GetAPIVersion.cgi");
|
|
|
|
qCDebug(dcFronius()) << "Search at address" << requestUrl.toString();
|
|
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(requestUrl));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, info, thing, reply]() {
|
|
QByteArray data = reply->readAll();
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Network request error:" << reply->error() << reply->errorString();
|
|
info->finish(Thing::ThingErrorHardwareNotAvailable, tr("Device not reachable"));
|
|
return;
|
|
}
|
|
|
|
// Convert the rawdata to a JSON document
|
|
QJsonParseError error;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
|
if (error.error != QJsonParseError::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Failed to parse JSON data" << data << ":" << error.errorString() << data;
|
|
info->finish(Thing::ThingErrorHardwareFailure, tr("Please try again"));
|
|
return;
|
|
}
|
|
|
|
FroniusLogger *newLogger = new FroniusLogger(thing, this);
|
|
newLogger->setBaseUrl(jsonDoc.toVariant().toMap().value("BaseURL").toString());
|
|
newLogger->setHostAddress(thing->paramValue(dataloggerThingLoggerHostParamTypeId).toString());
|
|
m_froniusLoggers.insert(newLogger, thing);
|
|
|
|
info->finish(Thing::ThingErrorNoError);
|
|
});
|
|
//Async Setup
|
|
} else if (thing->thingClassId() == inverterThingClassId) {
|
|
|
|
FroniusInverter *newInverter = new FroniusInverter(thing,this);
|
|
newInverter->setDeviceId(thing->paramValue(inverterThingIdParamTypeId).toString());
|
|
newInverter->setName(thing->paramValue(inverterThingNameParamTypeId).toString());
|
|
newInverter->setBaseUrl(thing->paramValue(inverterThingBaseParamTypeId).toString());
|
|
newInverter->setHostAddress(thing->paramValue(inverterThingHostParamTypeId).toString());
|
|
|
|
m_froniusInverters.insert(newInverter,thing);
|
|
|
|
thing->setParentId(ThingId(newInverter->hostId()));
|
|
thing->setName(newInverter->name());
|
|
|
|
// get inverter unique ID
|
|
QUrl requestUrl;
|
|
requestUrl.setScheme("http");
|
|
requestUrl.setHost(newInverter->hostAddress());
|
|
requestUrl.setPath(newInverter->baseUrl() + "GetInverterInfo.cgi");
|
|
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(requestUrl));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, info, newInverter, reply]() {
|
|
QByteArray data = reply->readAll();
|
|
reply->deleteLater();
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Network request error:" << reply->error() << reply->errorString();
|
|
info->finish(Thing::ThingErrorHardwareNotAvailable, "Device not reachable");
|
|
return;
|
|
}
|
|
|
|
// Convert the rawdata to a json document
|
|
QJsonParseError error;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
|
if (error.error != QJsonParseError::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Failed to parse JSON data" << data << ":" << error.errorString();
|
|
info->finish(Thing::ThingErrorHardwareNotAvailable, "Please try again");
|
|
return;
|
|
}
|
|
|
|
// Check reply information
|
|
QVariantMap dataMap = jsonDoc.toVariant().toMap().value("Body").toMap().value("Data").toMap();
|
|
// check for thing id in reply
|
|
if (dataMap.contains(newInverter->deviceId())) {
|
|
qCDebug(dcFronius()) << "Found Thing with unique:" << dataMap.value(newInverter->deviceId()).toMap().value("UniqueID").toString();
|
|
newInverter->setUniqueId(dataMap.value(newInverter->deviceId()).toMap().value("UniqueID").toString());
|
|
newInverter->pluginThing()->setParamValue(inverterThingUniqueIdParamTypeId,newInverter->uniqueId());
|
|
qCDebug(dcFronius()) << "Stored unique ID:" << newInverter->uniqueId();
|
|
}
|
|
info->finish(Thing::ThingErrorNoError);
|
|
});
|
|
// Async Setup
|
|
} else if (thing->thingClassId() == storageThingClassId) {
|
|
|
|
FroniusStorage *newStorage = new FroniusStorage(thing, this);
|
|
newStorage->setDeviceId(thing->paramValue(storageThingIdParamTypeId).toString());
|
|
newStorage->setName(thing->paramValue(storageThingNameParamTypeId).toString());
|
|
newStorage->setBaseUrl(thing->paramValue(storageThingBaseParamTypeId).toString());
|
|
newStorage->setHostAddress(thing->paramValue(storageThingHostParamTypeId).toString());
|
|
|
|
m_froniusStorages.insert(newStorage,thing);
|
|
|
|
thing->setParentId(ThingId(newStorage->hostId()));
|
|
thing->setName(newStorage->name());
|
|
|
|
// Get storage manufacturer and maximum capacity
|
|
QUrlQuery query;
|
|
QUrl requestUrl;
|
|
requestUrl.setScheme("http");
|
|
requestUrl.setHost(newStorage->hostAddress());
|
|
requestUrl.setPath(newStorage->baseUrl() + "GetStorageRealtimeData.cgi");
|
|
query.addQueryItem("Scope","Device");
|
|
query.addQueryItem("DeviceId", newStorage->deviceId());
|
|
requestUrl.setQuery(query);
|
|
qCDebug(dcFronius()) << "Get Storage Data at address" << requestUrl.toString();
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(requestUrl));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, info, newStorage, reply]() {
|
|
QByteArray data = reply->readAll();
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Network request error:" << reply->error() << reply->errorString();
|
|
info->finish(Thing::ThingErrorNoError);
|
|
return;
|
|
}
|
|
|
|
// Convert the rawdata to a json document
|
|
QJsonParseError error;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
|
if (error.error != QJsonParseError::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Failed to parse JSON data" << data << ":" << error.errorString();
|
|
info->finish(Thing::ThingErrorHardwareFailure, tr("Please try again"));
|
|
return;
|
|
}
|
|
|
|
// Create StorageInfo list map
|
|
QVariantMap storageInfoMap = jsonDoc.toVariant().toMap().value("Body").toMap().value("Data").toMap().value("Controller").toMap();
|
|
|
|
newStorage->pluginThing()->setParamValue(storageThingManufacturerParamTypeId, storageInfoMap.value("Details").toMap().value("Manufacturer").toString());
|
|
newStorage->pluginThing()->setParamValue(storageThingCapacityParamTypeId, storageInfoMap.value("Capacity_Maximum").toInt());
|
|
info->finish(Thing::ThingErrorNoError);
|
|
});
|
|
//Async Setup
|
|
} else if (thing->thingClassId() == meterThingClassId) {
|
|
|
|
FroniusMeter *newMeter = new FroniusMeter(thing, this);;
|
|
newMeter->setDeviceId(thing->paramValue(meterThingIdParamTypeId).toString());
|
|
newMeter->setName(thing->paramValue(meterThingNameParamTypeId).toString());
|
|
newMeter->setBaseUrl(thing->paramValue(meterThingBaseParamTypeId).toString());
|
|
newMeter->setHostAddress(thing->paramValue(meterThingHostParamTypeId).toString());
|
|
|
|
m_froniusMeters.insert(newMeter,thing);
|
|
|
|
thing->setParentId(ThingId(newMeter->hostId()));
|
|
thing->setName(newMeter->name());
|
|
|
|
info->finish(Thing::ThingErrorNoError);
|
|
//Async setup
|
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
|
SunspecThing *sunspecThings = new SunspecThing(thing, this);
|
|
m_sunspecThings.insert(sunspecThings, thing);
|
|
info->finish(Thing::ThingErrorNoError);
|
|
} else {
|
|
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginFronius::postSetupThing(Thing *thing)
|
|
{
|
|
qCDebug(dcFronius()) << "Post setup" << thing->name();
|
|
|
|
if (!m_pluginTimer) {
|
|
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(30);
|
|
connect(m_pluginTimer, &PluginTimer::timeout, this, [this]() {
|
|
foreach (Thing *logger, m_froniusLoggers)
|
|
updateThingStates(logger);
|
|
|
|
foreach (Thing *inverter, m_froniusInverters)
|
|
updateThingStates(inverter);
|
|
|
|
foreach (Thing *meter, m_froniusMeters)
|
|
updateThingStates(meter);
|
|
|
|
foreach (Thing *storage, m_froniusStorages)
|
|
updateThingStates(storage);
|
|
|
|
foreach (SunspecThing *sunspecThing, m_sunspecThings.keys())
|
|
sunspecThing->update();
|
|
});
|
|
}
|
|
|
|
if (thing->thingClassId() == dataloggerThingClassId) {
|
|
searchNewThings(m_froniusLoggers.key(thing));
|
|
updateThingStates(thing);
|
|
} else if (thing->thingClassId() == inverterThingClassId) {
|
|
updateThingStates(thing);
|
|
} else if (thing->thingClassId() == meterThingClassId) {
|
|
updateThingStates(thing);
|
|
} else if (thing->thingClassId() == storageThingClassId) {
|
|
updateThingStates(thing);
|
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
|
SunspecThing *sunspecThing = m_sunspecThings.key(thing);
|
|
sunspecThing->update();
|
|
} else {
|
|
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginFronius::thingRemoved(Thing *thing)
|
|
{
|
|
if (thing->thingClassId() == dataloggerThingClassId) {
|
|
FroniusLogger *logger = m_froniusLoggers.key(thing);
|
|
m_froniusLoggers.remove(logger);
|
|
logger->deleteLater();
|
|
return;
|
|
} else if (thing->thingClassId() == inverterThingClassId) {
|
|
FroniusInverter *inverter = m_froniusInverters.key(thing);
|
|
m_froniusInverters.remove(inverter);
|
|
inverter->deleteLater();
|
|
return;
|
|
} else if (thing->thingClassId() == meterThingClassId) {
|
|
FroniusMeter *meter = m_froniusMeters.key(thing);
|
|
m_froniusMeters.remove(meter);
|
|
meter->deleteLater();
|
|
return;
|
|
} else if (thing->thingClassId() == storageThingClassId) {
|
|
FroniusStorage *storage = m_froniusStorages.key(thing);
|
|
m_froniusStorages.remove(storage);
|
|
storage->deleteLater();
|
|
return;
|
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
|
qCDebug(dcFronius()) << "Remove sunspec thing";
|
|
SunspecThing *sunspecThing = m_sunspecThings.key(thing);
|
|
m_sunspecThings.remove(sunspecThing);
|
|
delete sunspecThing;
|
|
qCDebug(dcFronius()) << "Sunspec thing deleted";
|
|
return;
|
|
} else {
|
|
Q_ASSERT_X(false, "thingRemoved", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
|
}
|
|
|
|
if (myThings().isEmpty()) {
|
|
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
|
|
m_pluginTimer = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
void IntegrationPluginFronius::executeAction(ThingActionInfo *info)
|
|
{
|
|
Action action = info->action();
|
|
Thing *thing = info->thing();
|
|
qCDebug(dcFronius()) << "Execute action" << thing->name() << action.actionTypeId().toString();
|
|
|
|
if (thing->thingClassId() == dataloggerThingClassId) {
|
|
|
|
if (action.actionTypeId() == dataloggerSearchDevicesActionTypeId) {
|
|
searchNewThings(m_froniusLoggers.key(thing));
|
|
info->finish(Thing::ThingErrorNoError);
|
|
} else {
|
|
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
|
|
}
|
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
|
SunspecThing *sunspecThing = m_sunspecThings.key(thing);
|
|
if (!sunspecThing) {
|
|
qWarning(dcFronius()) << "Could not find sunspec instance for thing";
|
|
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
|
return;
|
|
}
|
|
|
|
if (action.actionTypeId() == sunspecStorageGridChargingActionTypeId) {
|
|
QUuid requestId = sunspecThing->setGridCharging(action.param(sunspecStorageGridChargingActionGridChargingParamTypeId).value().toBool());
|
|
if (requestId.isNull()) {
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
m_asyncActions.insert(requestId, info);
|
|
}
|
|
} else if (action.actionTypeId() == sunspecStorageEnableChargingLimitActionTypeId) {
|
|
int value = (action.param(sunspecStorageEnableChargingLimitActionEnableChargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableDischargingLimitStateTypeId).toBool();
|
|
QUuid requestId = sunspecThing->setStorageControlMode(value);
|
|
if (requestId.isNull()) {
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
m_asyncActions.insert(requestId, info);
|
|
}
|
|
} else if (action.actionTypeId() == sunspecStorageChargingRateActionTypeId) {
|
|
QUuid requestId = sunspecThing->setChargingRate(action.param(sunspecStorageChargingRateActionChargingRateParamTypeId).value().toInt());
|
|
if (requestId.isNull()) {
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
m_asyncActions.insert(requestId, info);
|
|
}
|
|
} else if (action.actionTypeId() == sunspecStorageEnableDischargingLimitActionTypeId) {
|
|
int value = (action.param(sunspecStorageEnableDischargingLimitActionEnableDischargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableChargingLimitStateTypeId).toBool();
|
|
QUuid requestId = sunspecThing->setStorageControlMode(value);
|
|
if (requestId.isNull()) {
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
m_asyncActions.insert(requestId, info);
|
|
}
|
|
} else if (action.actionTypeId() == sunspecStorageDischargingRateActionTypeId) {
|
|
QUuid requestId = sunspecThing->setDischargingRate(action.param(sunspecStorageDischargingRateActionDischargingRateParamTypeId).value().toInt());
|
|
if (requestId.isNull()) {
|
|
info->finish(Thing::ThingErrorHardwareFailure);
|
|
} else {
|
|
m_asyncActions.insert(requestId, info);
|
|
}
|
|
} else {
|
|
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
|
|
}
|
|
} else {
|
|
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginFronius::updateThingStates(Thing *thing)
|
|
{
|
|
qCDebug(dcFronius()) << "Update thing values for" << thing->name();
|
|
|
|
if(thing->thingClassId() == inverterThingClassId) {
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusInverters.key(thing)->updateUrl()));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, thing, reply]() {
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = reply->readAll();
|
|
if(m_froniusInverters.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusInverters.key(thing)->updateThingInfo(data);
|
|
}
|
|
});
|
|
QNetworkReply *next_reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusInverters.key(thing)->activityUrl()));
|
|
connect(next_reply, &QNetworkReply::finished, next_reply, &QNetworkReply::deleteLater);
|
|
connect(next_reply, &QNetworkReply::finished, [this, thing, next_reply]() {
|
|
if (next_reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << next_reply->error() << next_reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = next_reply->readAll();
|
|
if(m_froniusInverters.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusInverters.key(thing)->updateActivityInfo(data);
|
|
}
|
|
});
|
|
} else if (thing->thingClassId() == dataloggerThingClassId) {
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusLoggers.key(thing)->updateUrl()));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, thing, reply]() {
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = reply->readAll();
|
|
if(m_froniusLoggers.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusLoggers.key(thing)->updateThingInfo(data);
|
|
}
|
|
});
|
|
|
|
reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusLoggers.key(thing)->updateRelayStateUrl()));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, thing, reply]() {
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = reply->readAll();
|
|
if(m_froniusLoggers.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusLoggers.key(thing)->updatePowerRelayState(data);
|
|
}
|
|
});
|
|
|
|
} else if (thing->thingClassId() == meterThingClassId) {
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusMeters.key(thing)->updateUrl()));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, thing, reply]() {
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = reply->readAll();
|
|
if(m_froniusMeters.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusMeters.key(thing)->updateThingInfo(data);
|
|
}
|
|
});
|
|
QNetworkReply *next_reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusMeters.key(thing)->activityUrl()));
|
|
connect(next_reply, &QNetworkReply::finished, next_reply, &QNetworkReply::deleteLater);
|
|
connect(next_reply, &QNetworkReply::finished, [this, thing, next_reply]() {
|
|
|
|
if (next_reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << next_reply->error() << next_reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = next_reply->readAll();
|
|
if(m_froniusMeters.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusMeters.key(thing)->updateActivityInfo(data);
|
|
}
|
|
});
|
|
} else if (thing->thingClassId() == storageThingClassId) {
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusStorages.key(thing)->updateUrl()));
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, thing, reply]() {
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = reply->readAll();
|
|
if(m_froniusStorages.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusStorages.key(thing)->updateThingInfo(data);
|
|
}
|
|
});
|
|
QNetworkReply *next_reply = hardwareManager()->networkManager()->get(QNetworkRequest(m_froniusStorages.key(thing)->activityUrl()));
|
|
connect(next_reply, &QNetworkReply::finished, next_reply, &QNetworkReply::deleteLater);
|
|
connect(next_reply, &QNetworkReply::finished, [this, thing, next_reply]() {
|
|
next_reply->deleteLater();
|
|
if (next_reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << next_reply->error() << next_reply->errorString();
|
|
return;
|
|
}
|
|
QByteArray data = next_reply->readAll();
|
|
if(m_froniusStorages.values().contains(thing)){ // check if thing was not removed before reply was received
|
|
m_froniusStorages.key(thing)->updateActivityInfo(data);
|
|
}
|
|
});
|
|
} else {
|
|
Q_ASSERT_X(false, "updateThingState", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
|
|
|
}
|
|
}
|
|
|
|
void IntegrationPluginFronius::searchNewThings(FroniusLogger *logger)
|
|
{
|
|
QUrl url; QUrlQuery query;
|
|
query.addQueryItem("DeviceClass", "System");
|
|
url.setScheme("http");
|
|
url.setHost(logger->hostAddress());
|
|
url.setPath(logger->baseUrl() + "GetActiveDeviceInfo.cgi");
|
|
url.setQuery(query);
|
|
|
|
qCDebug(dcFronius()) << "Search Things at address" << url.toString();
|
|
QNetworkRequest request = QNetworkRequest(url);
|
|
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
|
|
|
|
QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
|
|
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
|
connect(reply, &QNetworkReply::finished, [this, logger, reply]() {
|
|
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
qCWarning(dcFronius()) << "Network request error:" << reply->error() << reply->errorString();
|
|
return;
|
|
}
|
|
|
|
Thing *loggerThing = m_froniusLoggers.value(logger);
|
|
if (!loggerThing)
|
|
return;
|
|
|
|
QByteArray data = reply->readAll();
|
|
|
|
// Convert the rawdata to a json document
|
|
QJsonParseError error;
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
|
if (error.error != QJsonParseError::NoError) {
|
|
qCWarning(dcFronius()) << "Fronius: Failed to parse JSON data" << data << ":" << error.errorString();
|
|
return;
|
|
}
|
|
|
|
// Parse the data for thing information
|
|
QList<ThingDescriptor> thingDescriptors;
|
|
|
|
// Check reply information
|
|
QVariantMap bodyMap = jsonDoc.toVariant().toMap().value("Body").toMap();
|
|
|
|
// Parse reply for inverters at the host address
|
|
QVariantMap inverterMap = bodyMap.value("Data").toMap().value("Inverter").toMap();
|
|
foreach (QString inverterId, inverterMap.keys()) {
|
|
//check if thing already connected to logger
|
|
if(!existingThing(inverterThingIdParamTypeId,inverterId)) {
|
|
QString thingName = loggerThing->name() + " Inverter " + inverterId;
|
|
ThingDescriptor descriptor(inverterThingClassId, thingName, "Fronius Solar Inverter", loggerThing->id());
|
|
ParamList params;
|
|
params.append(Param(inverterThingNameParamTypeId, thingName));
|
|
params.append(Param(inverterThingHostParamTypeId, m_froniusLoggers.key(loggerThing)->hostAddress()));
|
|
params.append(Param(inverterThingBaseParamTypeId, m_froniusLoggers.key(loggerThing)->baseUrl()));
|
|
params.append(Param(inverterThingIdParamTypeId, inverterId));
|
|
params.append(Param(inverterThingUniqueIdParamTypeId, ""));
|
|
descriptor.setParams(params);
|
|
thingDescriptors.append(descriptor);
|
|
}
|
|
}
|
|
|
|
// parse reply for meter things at the host address
|
|
QVariantMap meterMap = bodyMap.value("Data").toMap().value("Meter").toMap();
|
|
foreach (QString meterId, meterMap.keys()) {
|
|
//check if thing already connected to logger
|
|
if(!existingThing(meterThingIdParamTypeId, meterId)) {
|
|
QString thingName = loggerThing->name() + " Meter " + meterId;
|
|
ThingDescriptor descriptor(meterThingClassId, thingName, "Fronius Solar Meter", loggerThing->id());
|
|
ParamList params;
|
|
params.append(Param(meterThingNameParamTypeId, thingName));
|
|
params.append(Param(meterThingHostParamTypeId, m_froniusLoggers.key(loggerThing)->hostAddress()));
|
|
params.append(Param(meterThingBaseParamTypeId, m_froniusLoggers.key(loggerThing)->baseUrl()));
|
|
params.append(Param(meterThingIdParamTypeId, meterId));
|
|
params.append(Param(meterThingUniqueIdParamTypeId, meterMap.value(meterId).toMap().value("Serial").toString()));
|
|
descriptor.setParams(params);
|
|
thingDescriptors.append(descriptor);
|
|
}
|
|
}
|
|
|
|
// parse reply for storage things at the host address
|
|
QVariantMap storageMap = bodyMap.value("Data").toMap().value("Storage").toMap();
|
|
foreach (QString storageId, storageMap.keys()) {
|
|
//check if thing already connected to logger
|
|
if(!existingThing(storageThingIdParamTypeId,storageId)) {
|
|
QString thingName = loggerThing->name() + " Storage " + storageId;
|
|
ThingDescriptor descriptor(storageThingClassId, thingName, "Fronius Solar Storage", loggerThing->id());
|
|
ParamList params;
|
|
params.append(Param(storageThingNameParamTypeId, thingName));
|
|
params.append(Param(storageThingManufacturerParamTypeId, ""));
|
|
params.append(Param(storageThingCapacityParamTypeId, ""));
|
|
params.append(Param(storageThingHostParamTypeId, m_froniusLoggers.key(loggerThing)->hostAddress()));
|
|
params.append(Param(storageThingBaseParamTypeId, m_froniusLoggers.key(loggerThing)->baseUrl()));
|
|
params.append(Param(storageThingIdParamTypeId, storageId));
|
|
params.append(Param(storageThingUniqueIdParamTypeId, storageMap.value(storageId).toMap().value("Serial").toString()));
|
|
descriptor.setParams(params);
|
|
thingDescriptors.append(descriptor);
|
|
}
|
|
}
|
|
|
|
if (!thingDescriptors.empty()) {
|
|
emit autoThingsAppeared(thingDescriptors);
|
|
thingDescriptors.clear();
|
|
}
|
|
});
|
|
}
|
|
|
|
bool IntegrationPluginFronius::existingThing(ParamTypeId thingParamId, QString thingId)
|
|
{
|
|
foreach(Thing *thing, myThings()) {
|
|
if(thing->paramValue(thingParamId).toString() == thingId) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|