added sunny webbox

This commit is contained in:
bernhard.trinnes 2020-07-28 16:03:07 +02:00 committed by Simon Stürz
parent 2dc3d62623
commit f8df49845b
8 changed files with 626 additions and 0 deletions

View File

@ -0,0 +1,114 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "integrationpluginsma.h"
#include "plugininfo.h"
IntegrationPluginSma::IntegrationPluginSma()
{
}
void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
if (!m_udpSocket) {
m_udpSocket = new QUdpSocket(this);
}
if (thing->thingClassId() == sunnyWebBoxThingClassId) {
//check if a Sunny WebBox is already added with this IPv4Address
foreach(SunnyWebBox *sunnyWebBox, m_sunnyWebBoxes.values()) {
if(sunnyWebBox->hostAddress().toString() == thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString()){
//this logger at this IPv4 address is already added
qCWarning(dcSma()) << "thing at " << thing->paramValue(sunnyWebBoxThingHostParamTypeId).toString() << " already added!";
info->finish(Thing::ThingErrorThingInUse);
return;
}
}
} else if (thing->thingClassId() == inverterThingClassId) {
Thing *parentThing = myThings().findById(thing->parentId());
if (!parentThing) {
qCWarning(dcSma()) << "Could not find parentThing for thing " << thing->name();
return info->finish(Thing::ThingErrorHardwareNotAvailable, "Please try again");
}
if (!parentThing->setupComplete()) {
//wait for the parent to finish the setup process
connect(parentThing, &Thing::setupStatusChanged, info, [this, info, parentThing] {
if (parentThing->setupComplete())
setupChild(info, parentThing);
});
return;
}
setupChild(info, parentThing);
} else {
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginSma::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == sunnyWebBoxThingClassId) {
SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing);
if (!sunnyWebBox)
return;
sunnyWebBox->getDevices();
} else if (thing->thingClassId() == inverterThingClassId) {
}
}
void IntegrationPluginSma::thingRemoved(Thing *thing)
{
if (thing->thingClassId() == sunnyWebBoxThingClassId) {
m_sunnyWebBoxes.take(thing)->deleteLater();
}
if (myThings().filterByThingClassId(sunnyWebBoxThingClassId).isEmpty()) {
m_udpSocket->deleteLater();
m_udpSocket = nullptr;
}
}
SunnyWebBox * IntegrationPluginSma::createSunnyWebBoxConnection(Thing *thing)
{
SunnyWebBox *sunnyWebBox = new SunnyWebBox(m_udpSocket, this);
m_sunnyWebBoxes.insert(thing, sunnyWebBox);
//connect();
return sunnyWebBox;
}
void IntegrationPluginSma::setupChild(ThingSetupInfo *info, Thing *parentThing)
{
Q_UNUSED(info)
Q_UNUSED(parentThing)
}

View File

@ -0,0 +1,69 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINSMA_H
#define INTEGRATIONPLUGINSMA_H
#include "integrations/integrationplugin.h"
#include "plugintimer.h"
#include "sunnywebbox.h"
#include <QDebug>
#include <QHostAddress>
#include <QUdpSocket>
class IntegrationPluginSma: public IntegrationPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsma.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginSma();
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;
private slots:
void onRefreshTimer();
private:
PluginTimer *m_refreshTimer = nullptr;
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
QHash<Thing *, ThingSetupInfo *> m_asyncSetup;
QUdpSocket *m_udpSocket = nullptr;
SunnyWebBox * createSunnyWebBoxConnection(Thing *thing);
void setupChild(ThingSetupInfo *info, Thing *parentThing);
void getData(Thing *thing);
};
#endif // INTEGRATIONPLUGINSMA_H

View File

@ -0,0 +1,164 @@
{
"id": "b8442bbf-9d3f-4aa2-9443-b3a31ae09bac",
"name": "sma",
"displayName": "SMA",
"vendors": [
{
"id": "16d5a4a3-36d5-46c0-b7dd-df166ddf5981",
"name": "Sma",
"displayName": "SMA",
"thingClasses": [
{
"id": "49304127-ce9b-45dd-8511-05030a4ac003",
"name": "sunnyWebBox",
"displayName": "Sunny WebBox",
"createMethods": ["user", "discovery"],
"interfaces": ["gateway"],
"paramTypes": [
{
"id": "864d4162-e3ce-48b8-b8ac-c1b971b52d42",
"name": "host",
"displayName": "Host address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.0.168"
},
{
"id": "a6df3d28-1b02-42dd-8301-81754c47e55f",
"name": "id",
"displayName": "Device ID",
"type": "QString",
"defaultValue": "-",
"readOnly": true
}
],
"stateTypes": [
{
"id": "c05e6a1a-252c-4f2b-8b31-09cf113d01c1",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "ff4ff872-2f0f-4ca4-9fe2-220eeaf16cc2",
"name": "currentPower",
"displayName": "Current power",
"displayNameEvent": "Current power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0
},
{
"id": "16f34c5c-8dbb-4dcc-9faa-4b782d57226c",
"name": "dayEnergy",
"displayName": "Day energy",
"displayNameEvent": "Day energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "0bb4e227-7e38-49ca-9b32-ce4621c9305b",
"name": "totalEnergy",
"displayName": "Total energy",
"displayNameEvent": "Total energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "1974550b-6059-4b0e-83f4-70177e20dac3",
"name": "mode",
"displayName": "Mode",
"displayNameEvent": "Mode changed",
"type": "QString",
"defaultValue": "MPP"
},
{
"id": "4e64f9ca-7e5a-4897-8035-6f2ae88fde89",
"name": "error",
"displayName": "Error",
"displayNameEvent": "Error changed",
"type": "QString",
"defaultValue": "None"
}
],
"actionTypes": [
{
"id": "15fc3dac-1868-4490-ba41-7c2f545926ad",
"name": "searchDevices",
"displayName": "Search new devices"
}
]
},
{
"id": "9cb72321-042e-4912-b23e-18516b6bbe96",
"name": "inverter",
"displayName": "Solar Inverter",
"createMethods": ["auto"],
"interfaces" : ["extendedsmartmeterproducer", "connectable"],
"paramTypes": [
{
"id": "f43e8159-7337-4bd0-ba74-b6630e554e43",
"name": "id",
"displayName": "Device ID",
"type": "QString",
"inputType": "TextLine",
"readOnly": true
}
],
"stateTypes": [
{
"id": "eda29c50-73ac-40e0-9c92-26fee352e688",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "a804eabf-d5b8-4c83-84e4-8ec994875950",
"name": "currentPower",
"displayName": "Current power",
"displayNameEvent": "Current Power changed",
"type": "double",
"unit": "Watt",
"defaultValue": "0"
},
{
"id": "cd3a0abc-37cc-4f9c-8ff2-c7ccc2513880",
"name": "eday",
"displayName": "Energy of current day",
"displayNameEvent": "Energy of day changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": "0"
},
{
"id": "341596ec-690b-4bf6-b6ed-0f92fa1c7d6c",
"name": "eyear",
"displayName": "Energy of current year",
"displayNameEvent": "Energy of year changed",
"type": "int",
"unit": "KiloWattHour",
"defaultValue": "0"
},
{
"id": "6b6c1ddb-692f-400f-8b8d-38e0e0ae34ba",
"name": "totalEnergyProduced",
"displayName": "Energy total",
"displayNameEvent": "Energy total changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": "0"
}
]
}
]
}
]
}

View File

View File

View File

@ -0,0 +1,12 @@
include(../plugins.pri)
QT += \
network \
SOURCES += \
integrationpluginsma.cpp \
sunnywebbox.cpp \
HEADERS += \
integrationpluginsma.h \
sunnywebbox.h \

177
sma/sunnywebbox.cpp Normal file
View File

@ -0,0 +1,177 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "sunnywebbox.h"
#include "extern-plugininfo.h"
#include "QJsonDocument"
#include "QJsonObject"
SunnyWebBox::SunnyWebBox(QUdpSocket *udpSocket, QObject *parrent) :
QObject(parrent),
m_udpSocket(udpSocket)
{
connect(m_udpSocket, &QUdpSocket::stateChanged, this, [this](QAbstractSocket::SocketState state) {
emit connectedChanged((state == QAbstractSocket::SocketState::ConnectedState));
});
connect(m_udpSocket, &QUdpSocket::readyRead, this, [this] {
//m_udpSocket->readDatagram(QByteArray())
qCDebug(dcSma()) << "Received datagram" << m_udpSocket->readAll();
});
}
int SunnyWebBox::getPlantOverview()
{
return sendMessage("GetPlantOverview");
}
int SunnyWebBox::getDevices()
{
return sendMessage("GetDevices");
}
int SunnyWebBox::getProcessDataChannels(const QString &deviceId)
{
QJsonObject params;
params["device"] = deviceId;
return sendMessage("GetProcessDataChannels", params);
}
int SunnyWebBox::sendMessage(const QString &procedure)
{
int requestId = qrand();
QJsonDocument doc;
QJsonObject obj;
obj["version"] = "1.0";
obj["proc"] = procedure;
obj["id"] = requestId;
obj["format"] = "JSON";
m_udpSocket->writeDatagram(doc.toJson(), m_hostAddresss, m_port);
return requestId;
}
int SunnyWebBox::sendMessage(const QString &procedure, const QJsonObject &params)
{
int requestId = qrand();
QJsonDocument doc;
QJsonObject obj;
obj["version"] = "1.0";
obj["proc"] = procedure;
obj["id"] = requestId;
obj["format"] = "JSON";
if (!params.isEmpty()) {
obj.insert("params", params);
}
m_udpSocket->writeDatagram(doc.toJson(), m_hostAddresss, m_port);
return requestId;
}
void SunnyWebBox::onDatagramReceived(const QByteArray &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();
int requestId = map["id"].toInt();
QVariantMap result = map.value("result").toMap();
emit messageResponseReceived(requestId, requestType, result);
} else {
qCWarning(dcSma()) << "Missing proc or result value";
}
}
void SunnyWebBox::parseMessageReponse(int messageId, const QString &messageType, const QVariantMap &result)
{
if (messageType == "GetPlantOverview") {
Overview overview;
QVariantList overviewList = result.value("overview").toList();
Q_FOREACH(QVariant value, overviewList) {
QVariantMap map = value.toMap();
if (map["meta"].toString() == "GriPwr") {
overview.power = map["value"].toInt();
} else if (map["meta"].toString() == "GriEgyTdy") {
overview.dailyYield = map["value"].toInt();
} else if (map["meta"].toString() == "GriEgyTot") {
overview.totalYield = map["value"].toInt();
} else if (map["meta"].toString() == "OpStt") {
overview.status = map["value"].toString();
} else if (map["meta"].toString() == "Msg") {
overview.error = map["value"].toString();
}
}
emit plantOverviewReceived(messageId, overview);
} else if (messageType == "GetDevices") {
QList<Device> devices;
QVariantList deviceList = result.value("devices").toList();
Q_FOREACH(QVariant value, deviceList) {
Device device;
QVariantMap map = value.toMap();
device.name = map["name"].toString();
device.key = map["key"].toString();
QVariantList childrenList = map["children"].toList();
Q_FOREACH(QVariant childValue, childrenList) {
Device child;
QVariantMap childMap = childValue.toMap();
device.name = childMap["name"].toString();
device.key = childMap["key"].toString();
device.childrens.append(child);
}
devices.append(device);
}
if (!devices.isEmpty())
emit devicesReceived(messageId, devices);
} else if (messageType == "GetProcessDataChannels") {
} else if (messageType == "GetProcessData") {
} else if (messageType == "GetParameterChannels") {
} else if (messageType == "GetParameter") {
} else if (messageType == "SetParameter") {
} else {
qCWarning(dcSma()) << "Unknown message type" << messageType;
}
}

90
sma/sunnywebbox.h Normal file
View File

@ -0,0 +1,90 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SUNNYWEBBOX_H
#define SUNNYWEBBOX_H
#include "integrations/thing.h"
#include <QObject>
#include <QHostAddress>
#include <QUdpSocket>
class SunnyWebBox : public QObject
{
Q_OBJECT
public:
struct Overview {
int power;
double dailyYield;
int totalYield;
QString status;
QString error;
};
struct Device {
QString key;
QString name;
QList<Device> childrens;
};
explicit SunnyWebBox(QUdpSocket *udpSocket, QObject *parrent = 0);
int getPlantOverview();
int getDevices();
int getProcessDataChannels(const QString &deviceKey);
int getProcessData(const QStringList &deviceKeys);
int getParameterChannels(const QString &deviceKey);
int getParameters(const QStringList &deviceKeys);
void setHostAddress();
QHostAddress hostAddress();
private:
int m_port = 34268;
QHostAddress m_hostAddresss;
QUdpSocket *m_udpSocket = nullptr;
int sendMessage(const QString &procedure);
int sendMessage(const QString &procedure, const QJsonObject &params);
public slots:
void onDatagramReceived(const QByteArray &data);
void parseMessageReponse(int messageId, const QString &messageType, const QVariantMap &result);
signals:
void connectedChanged(bool connected);
void messageResponseReceived(int messageId, const QString &messageType, const QVariantMap &result);
void plantOverviewReceived(int messageId, Overview overview);
void devicesReceived(int messageId, QList<Device> devices);
};
#endif // SUNNYWEBBOX_H