diff --git a/sma/integrationpluginsma.cpp b/sma/integrationpluginsma.cpp
index e69de29b..efb46a06 100644
--- a/sma/integrationpluginsma.cpp
+++ b/sma/integrationpluginsma.cpp
@@ -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 .
+*
+* 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)
+}
diff --git a/sma/integrationpluginsma.h b/sma/integrationpluginsma.h
index e69de29b..821cec70 100644
--- a/sma/integrationpluginsma.h
+++ b/sma/integrationpluginsma.h
@@ -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 .
+*
+* 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
+#include
+#include
+
+
+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 m_sunnyWebBoxes;
+ QHash m_asyncSetup;
+ QUdpSocket *m_udpSocket = nullptr;
+
+ SunnyWebBox * createSunnyWebBoxConnection(Thing *thing);
+ void setupChild(ThingSetupInfo *info, Thing *parentThing);
+ void getData(Thing *thing);
+};
+
+#endif // INTEGRATIONPLUGINSMA_H
diff --git a/sma/integrationpluginsma.json b/sma/integrationpluginsma.json
index e69de29b..c823b3f0 100644
--- a/sma/integrationpluginsma.json
+++ b/sma/integrationpluginsma.json
@@ -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"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/sma/sma.cpp b/sma/sma.cpp
deleted file mode 100644
index e69de29b..00000000
diff --git a/sma/sma.h b/sma/sma.h
deleted file mode 100644
index e69de29b..00000000
diff --git a/sma/sma.pro b/sma/sma.pro
index e69de29b..5408af6e 100644
--- a/sma/sma.pro
+++ b/sma/sma.pro
@@ -0,0 +1,12 @@
+include(../plugins.pri)
+
+QT += \
+ network \
+
+SOURCES += \
+ integrationpluginsma.cpp \
+ sunnywebbox.cpp \
+
+HEADERS += \
+ integrationpluginsma.h \
+ sunnywebbox.h \
diff --git a/sma/sunnywebbox.cpp b/sma/sunnywebbox.cpp
new file mode 100644
index 00000000..0f131fad
--- /dev/null
+++ b/sma/sunnywebbox.cpp
@@ -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 .
+*
+* 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 ¶ms)
+{
+ 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 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;
+ }
+}
diff --git a/sma/sunnywebbox.h b/sma/sunnywebbox.h
new file mode 100644
index 00000000..5be8c307
--- /dev/null
+++ b/sma/sunnywebbox.h
@@ -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 .
+*
+* 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
+#include
+#include
+
+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 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 ¶ms);
+
+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 devices);
+};
+
+#endif // SUNNYWEBBOX_H