Add basics for inverter communication
This commit is contained in:
parent
a30d338de9
commit
8b9c38b04d
@ -118,7 +118,7 @@ isEmpty(WITH_PLUGINS) {
|
||||
}
|
||||
PLUGINS-=$${WITHOUT_PLUGINS}
|
||||
|
||||
#FIXME: PLUGINS=sma
|
||||
# FIXME: PLUGINS=sma
|
||||
message("Building plugins:")
|
||||
for(plugin, PLUGINS) {
|
||||
exists($${plugin}) {
|
||||
|
||||
@ -131,6 +131,48 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
speedwireDiscovery->startDiscovery();
|
||||
} else if (info->thingClassId() == speedwireInverterThingClassId) {
|
||||
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
|
||||
if (!speedwireDiscovery->initialize()) {
|
||||
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){
|
||||
qCDebug(dcSma()) << "Speed wire discovery finished.";
|
||||
speedwireDiscovery->deleteLater();
|
||||
|
||||
ThingDescriptors descriptors;
|
||||
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
|
||||
if (result.deviceType == SpeedwireInterface::DeviceTypeInverter) {
|
||||
if (result.serialNumber == 0)
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(speedwireInverterThingClassId, "SMA Inverter", "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
// We found an energy meter, let's check if we already added this one
|
||||
foreach (Thing *existingThing, myThings()) {
|
||||
if (existingThing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) {
|
||||
descriptor.setThingId(existingThing->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ParamList params;
|
||||
params << Param(speedwireInverterThingHostParamTypeId, result.address.toString());
|
||||
params << Param(speedwireInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
params << Param(speedwireInverterThingSerialNumberParamTypeId, result.serialNumber);
|
||||
params << Param(speedwireInverterThingModelIdParamTypeId, result.modelId);
|
||||
descriptor.setParams(params);
|
||||
descriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
info->addThingDescriptors(descriptors);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
speedwireDiscovery->startDiscovery();
|
||||
}
|
||||
}
|
||||
@ -188,6 +230,7 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
||||
|
||||
SpeedwireMeter *meter = new SpeedwireMeter(address, modelId, serialNumber, this);
|
||||
if (!meter->initialize()) {
|
||||
qCWarning(dcSma()) << "Setup failed. Could not initialize meter interface.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
@ -219,7 +262,28 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
||||
|
||||
m_speedwireMeters.insert(thing, meter);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}else {
|
||||
} else if (thing->thingClassId() == speedwireInverterThingClassId) {
|
||||
QHostAddress address = QHostAddress(thing->paramValue(speedwireInverterThingHostParamTypeId).toString());
|
||||
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt());
|
||||
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireInverterThingModelIdParamTypeId).toUInt());
|
||||
|
||||
if (m_speedwireInverters.contains(thing)) {
|
||||
m_speedwireInverters.take(thing)->deleteLater();
|
||||
}
|
||||
|
||||
SpeedwireInverter *inverter = new SpeedwireInverter(address, modelId, serialNumber, this);
|
||||
if (!inverter->initialize()) {
|
||||
qCWarning(dcSma()) << "Setup failed. Could not initialize inverter interface.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: reachable state
|
||||
m_speedwireInverters.insert(thing, inverter);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
inverter->sendLoginRequest();
|
||||
} else {
|
||||
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,11 +31,12 @@
|
||||
#ifndef INTEGRATIONPLUGINSMA_H
|
||||
#define INTEGRATIONPLUGINSMA_H
|
||||
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "plugintimer.h"
|
||||
#include "sunnywebbox.h"
|
||||
#include <integrations/integrationplugin.h>
|
||||
#include <plugintimer.h>
|
||||
|
||||
#include "sunnywebbox.h"
|
||||
#include "speedwiremeter.h"
|
||||
#include "speedwireinverter.h"
|
||||
|
||||
class IntegrationPluginSma: public IntegrationPlugin {
|
||||
Q_OBJECT
|
||||
@ -61,6 +62,7 @@ private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
|
||||
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
|
||||
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSMA_H
|
||||
|
||||
@ -306,6 +306,155 @@
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "b63a0669-f2ac-4769-abea-e14cafb2309a",
|
||||
"name": "speedwireInverter",
|
||||
"displayName": "SMA Inverter",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": [ "solarinverter" ],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "c8098d53-69eb-4d0b-9f07-e43c4a0ea9a9",
|
||||
"name": "host",
|
||||
"displayName": "Host address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": "192.168.0.168"
|
||||
},
|
||||
{
|
||||
"id": "7df0ab60-0f11-4495-8e0d-508ba2b6d858",
|
||||
"name": "macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "e42242b4-2811-47f9-b42b-b150ed233217",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "d9892f74-5b93-4c98-8da2-72aca033273a",
|
||||
"name": "modelId",
|
||||
"displayName": "Model ID",
|
||||
"type": "uint",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "aaff72c3-c70a-4a2f-bed1-89f38cebe442",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "6ef4eb16-a3d6-4bc9-972d-5e7cb81173a5",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "d9a5768b-1bf5-4933-810d-84dd7a688f71",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "fc168dc6-eecf-40b4-b214-3e28da0dbb12",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "2a6c59ca-853a-47d6-96fb-0c85edf32f52",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"displayNameEvent": "Current phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "4db96fec-737c-4c4b-bf07-5ef2fd62508a",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"displayNameEvent": "Current phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "0f23fb0e-a440-4ac2-9aff-896bc65feb2c",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"displayNameEvent": "Current phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "d7ceb482-5df8-4c0c-82bd-62ce7ba22c43",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "b366f680-6134-488b-8362-b1b824a8daca",
|
||||
"name": "currentPowerMpp1",
|
||||
"displayName": "Current power MPP1",
|
||||
"displayNameEvent": "Current power MPP1 changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "87d9b654-5558-47a3-9db9-ffd7c23b4774",
|
||||
"name": "currentPowerMpp2",
|
||||
"displayName": "Current power MPP2",
|
||||
"displayNameEvent": "Current power MPP2 changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "51cadd66-2cf1-485a-a2a9-191d11abfbd1",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"displayNameEvent": "Total energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "6d76cc7b-9e00-4561-be7b-4e2a6b8f7b66",
|
||||
"name": "firmwareVersion",
|
||||
"displayName": "Firmware version",
|
||||
"displayNameEvent": "Firmware version changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
#include "obisdata.h"
|
||||
|
||||
ObisData::ObisData()
|
||||
{
|
||||
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
#ifndef OBISDATA_H
|
||||
#define OBISDATA_H
|
||||
|
||||
|
||||
class ObisData
|
||||
{
|
||||
public:
|
||||
ObisData();
|
||||
};
|
||||
|
||||
#endif // OBISDATA_H
|
||||
@ -4,16 +4,16 @@ QT += network
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsma.cpp \
|
||||
obisdata.cpp \
|
||||
speedwirediscovery.cpp \
|
||||
speedwireinterface.cpp \
|
||||
speedwireinverter.cpp \
|
||||
speedwiremeter.cpp \
|
||||
sunnywebbox.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsma.h \
|
||||
obisdata.h \
|
||||
speedwirediscovery.h \
|
||||
speedwireinterface.h \
|
||||
speedwireinverter.h \
|
||||
speedwiremeter.h \
|
||||
sunnywebbox.h
|
||||
|
||||
@ -6,44 +6,12 @@ SpeedwireInterface::SpeedwireInterface(const QHostAddress &address, bool multica
|
||||
m_address(address),
|
||||
m_multicast(multicast)
|
||||
{
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Create interface for" << address.toString() << (multicast ? "multicast" : "unicast");
|
||||
m_socket = new QUdpSocket(this);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams);
|
||||
connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged);
|
||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
|
||||
|
||||
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00020058 001e8200 ff208200 00000000 => query device type
|
||||
// Response 534d4100000402a000000001009e0010 606527a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01020058 01000000 03000000 011e8210 6f89e95f 534e3a20 33303130 35333831 31360000 00000000 00000000 00000000 00000000
|
||||
// 011f8208 6f89e95f 411f0001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 => 1f41 solar inverter
|
||||
// 01208208 6f89e95f 96240000 80240000 81240001 82240000 feffff00 00000000 00000000 00000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00028053 001e2500 ff1e2500 00000000 => query spot dc power
|
||||
// Response 534d4100000402a000000001005e0010 606517a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01028053 00000000 01000000 011e2540 61a7e95f 57000000 57000000 57000000 57000000 01000000
|
||||
// 021e2540 61a7e95f 5e000000 5e000000 5e000000 5e000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000580 00028053 001f4500 ff214500 00000000 => query spot dc voltage/current
|
||||
// Response 534d4100000402a00000000100960010 606525a0 7d0042be283a00a1 7a01842a71b30001 000000000580 01028053 02000000 05000000 011f4540 61a7e95f 05610000 05610000 05610000 05610000 01000000
|
||||
// 021f4540 61a7e95f 505b0000 505b0000 505b0000 505b0000 01000000
|
||||
// 01214540 61a7e95f 60010000 60010000 60010000 60010000 01000000
|
||||
// 02214540 61a7e95f 95010000 95010000 95010000 95010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000680 00020051 00404600 ff424600 00000000 => query spot ac power
|
||||
// Response 534d4100000402a000000001007a0010 60651ea0 7d0042be283a00a1 7a01842a71b30001 000000000680 01020051 09000000 0b000000 01404640 61a7e95f 38000000 38000000 38000000 38000000 01000000
|
||||
// 01414640 61a7e95f 37000000 37000000 37000000 37000000 01000000
|
||||
// 01424640 61a7e95f 39000000 39000000 39000000 39000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000780 00020051 00484600 ff554600 00000000 => query spot ac voltage/current
|
||||
// Response 534d4100000402a000000001013e0010 60654fa0 7d0042be283a00a1 7a01842a71b30001 000000000780 01020051 0c000000 15000000 01484600 61a7e95f 5a590000 5a590000 5a590000 5a590000 01000000
|
||||
// 01494600 61a7e95f cf590000 cf590000 cf590000 cf590000 01000000
|
||||
// 014a4600 61a7e95f 7a590000 7a590000 7a590000 7a590000 01000000
|
||||
// 014b4600 61a7e95f f19a0000 f19a0000 f19a0000 f19a0000 01000000
|
||||
// 014c4600 61a7e95f 3c9b0000 3c9b0000 3c9b0000 3c9b0000 01000000
|
||||
// 014d4600 61a7e95f 189b0000 189b0000 189b0000 189b0000 01000000
|
||||
// 014e4600 51a7e95f 1d000000 1d000000 1d000000 1d000000 01000000
|
||||
// 01534640 61a7e95f 24010000 24010000 24010000 24010000 01000000
|
||||
// 01544640 61a7e95f 1e010000 1e010000 1e010000 1e010000 01000000
|
||||
// 01554640 61a7e95f 23010000 23010000 23010000 23010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000980 00028051 00482100 ff482100 00000000 => query device status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000980 01028051 00000000 00000000 01482108 59c5e95f 33010001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000a80 00028051 00644100 ff644100 00000000 => query grid relay status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000a80 01028051 07000000 07000000 01644108 59c5e95f 33000001 37010000 fdffff00 feffff00 00000000 00000000 00000000 00000000 00000000
|
||||
}
|
||||
|
||||
SpeedwireInterface::~SpeedwireInterface()
|
||||
@ -59,7 +27,7 @@ bool SpeedwireInterface::initialize()
|
||||
}
|
||||
|
||||
if (m_multicast && !m_socket->joinMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_socket->errorString();
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_socket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -73,7 +41,7 @@ void SpeedwireInterface::deinitialize()
|
||||
if (m_initialized) {
|
||||
if (m_multicast) {
|
||||
if (!m_socket->leaveMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString();
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << m_multicastAddress.toString();
|
||||
}
|
||||
|
||||
m_socket->close();
|
||||
@ -111,6 +79,7 @@ SpeedwireInterface::SpeedwireHeader SpeedwireInterface::parseHeader(QDataStream
|
||||
|
||||
void SpeedwireInterface::sendData(const QByteArray &data)
|
||||
{
|
||||
//qCDebug(dcSma()) << "Send data:" << data.toHex();
|
||||
m_socket->writeDatagram(data, m_address, m_port);
|
||||
}
|
||||
|
||||
@ -129,7 +98,7 @@ void SpeedwireInterface::readPendingDatagrams()
|
||||
continue;
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
|
||||
//qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
|
||||
emit dataReceived(datagram);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,9 +5,6 @@
|
||||
#include <QUdpSocket>
|
||||
#include <QDataStream>
|
||||
|
||||
|
||||
|
||||
|
||||
class SpeedwireInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -44,10 +41,8 @@ public:
|
||||
inline bool isValid() const {
|
||||
return smaSignature == 0x534d4100 && protocolId != ProtocolIdUnknown;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
explicit SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent = nullptr);
|
||||
~SpeedwireInterface();
|
||||
|
||||
|
||||
221
sma/speedwireinverter.cpp
Normal file
221
sma/speedwireinverter.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include "speedwireinverter.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_address(address),
|
||||
m_modelId(modelId),
|
||||
m_serialNumber(serialNumber)
|
||||
{
|
||||
|
||||
m_interface = new SpeedwireInterface(m_address, true, this);
|
||||
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
|
||||
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00028053 001e2500 ff1e2500 00000000 => query spot dc power
|
||||
// Response 534d4100000402a000000001005e0010 606517a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01028053 00000000 01000000 011e2540 61a7e95f 57000000 57000000 57000000 57000000 01000000
|
||||
// 021e2540 61a7e95f 5e000000 5e000000 5e000000 5e000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000580 00028053 001f4500 ff214500 00000000 => query spot dc voltage/current
|
||||
// Response 534d4100000402a00000000100960010 606525a0 7d0042be283a00a1 7a01842a71b30001 000000000580 01028053 02000000 05000000 011f4540 61a7e95f 05610000 05610000 05610000 05610000 01000000
|
||||
// 021f4540 61a7e95f 505b0000 505b0000 505b0000 505b0000 01000000
|
||||
// 01214540 61a7e95f 60010000 60010000 60010000 60010000 01000000
|
||||
// 02214540 61a7e95f 95010000 95010000 95010000 95010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000680 00020051 00404600 ff424600 00000000 => query spot ac power
|
||||
// Response 534d4100000402a000000001007a0010 60651ea0 7d0042be283a00a1 7a01842a71b30001 000000000680 01020051 09000000 0b000000 01404640 61a7e95f 38000000 38000000 38000000 38000000 01000000
|
||||
// 01414640 61a7e95f 37000000 37000000 37000000 37000000 01000000
|
||||
// 01424640 61a7e95f 39000000 39000000 39000000 39000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000780 00020051 00484600 ff554600 00000000 => query spot ac voltage/current
|
||||
// Response 534d4100000402a000000001013e0010 60654fa0 7d0042be283a00a1 7a01842a71b30001 000000000780 01020051 0c000000 15000000 01484600 61a7e95f 5a590000 5a590000 5a590000 5a590000 01000000
|
||||
// 01494600 61a7e95f cf590000 cf590000 cf590000 cf590000 01000000
|
||||
// 014a4600 61a7e95f 7a590000 7a590000 7a590000 7a590000 01000000
|
||||
// 014b4600 61a7e95f f19a0000 f19a0000 f19a0000 f19a0000 01000000
|
||||
// 014c4600 61a7e95f 3c9b0000 3c9b0000 3c9b0000 3c9b0000 01000000
|
||||
// 014d4600 61a7e95f 189b0000 189b0000 189b0000 189b0000 01000000
|
||||
// 014e4600 51a7e95f 1d000000 1d000000 1d000000 1d000000 01000000
|
||||
// 01534640 61a7e95f 24010000 24010000 24010000 24010000 01000000
|
||||
// 01544640 61a7e95f 1e010000 1e010000 1e010000 1e010000 01000000
|
||||
// 01554640 61a7e95f 23010000 23010000 23010000 23010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000980 00028051 00482100 ff482100 00000000 => query device status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000980 01028051 00000000 00000000 01482108 59c5e95f 33010001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000a80 00028051 00644100 ff644100 00000000 => query grid relay status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000a80 01028051 07000000 07000000 01644108 59c5e95f 33000001 37010000 fdffff00 feffff00 00000000 00000000 00000000 00000000 00000000
|
||||
}
|
||||
|
||||
bool SpeedwireInverter::initialize()
|
||||
{
|
||||
return m_interface->initialize();
|
||||
}
|
||||
|
||||
bool SpeedwireInverter::initialized() const
|
||||
{
|
||||
return m_interface->initialized();
|
||||
}
|
||||
|
||||
void SpeedwireInverter::sendLoginRequest(const QString &password, bool loginAsUser)
|
||||
{
|
||||
// Request 534d4100000402a000000001003a0010 60650ea0 7a01842a71b30001 7d0042be283a0001 000000000280 0c04fdff 07000000 84030000 00d8e85f 00000000 c1c1c1c18888888888888888 00000000 => login command = 0xfffd040c, first = 0x00000007 (user 7, installer a), last = 0x00000384 (hier timeout), time = 0x5fdf9ae8, 0x00000000, pw 12 bytes
|
||||
|
||||
// Response 534d4100000402a000000001002e0010 60650be0 7d0042be283a0001 7a01842a71b30001 000000000280 0d04fdff 07000000 84030000 00d8e85f 00000000 00000000 => login OK
|
||||
// Response 534d4100000402a000000001002e0010 60650be0 7d0042be283a0001 7a01842a71b30001 000100000280 0d04fdff 07000000 84030000 fddbe85f 00000000 00000000 => login INVALID PASSWORD
|
||||
// command 0xfffd040c => 0x400 set? 0x00c bytecount=12?
|
||||
|
||||
// Build the header
|
||||
QByteArray header = QByteArray::fromHex("534d4100000402a000000001003a001060650ea0");
|
||||
|
||||
// The payload is little endian encoded
|
||||
QByteArray payload;
|
||||
QDataStream payloadStream(&payload, QIODevice::WriteOnly);
|
||||
payloadStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// Destination
|
||||
payloadStream << m_modelId << m_serialNumber << static_cast<quint16>(0x0100);
|
||||
|
||||
// Source
|
||||
payloadStream << m_interface->sourceModelId() << m_interface->sourceSerialNumber() << static_cast<quint16>(0x0100);
|
||||
|
||||
// Packet information
|
||||
quint16 errorCode = 0;
|
||||
quint16 fragmentId = 0;
|
||||
quint16 packetId = m_packetId++ | 0x8000;
|
||||
payloadStream << errorCode << fragmentId << packetId;
|
||||
|
||||
// Command
|
||||
payloadStream << static_cast<quint32>(CommandQueryLogin);
|
||||
|
||||
// User type: 7 = user, a = installer
|
||||
payloadStream << (loginAsUser ? static_cast<quint32>(0x00000007) : static_cast<quint32>(0x0000000a));
|
||||
|
||||
// Timeout
|
||||
payloadStream << static_cast<quint32>(900); // 1s
|
||||
|
||||
// Current time
|
||||
payloadStream << static_cast<quint32>(QDateTime::currentMSecsSinceEpoch() / 1000.0);
|
||||
|
||||
// Zeros
|
||||
payloadStream << static_cast<quint32>(0);
|
||||
|
||||
// Password
|
||||
QByteArray passwordData = password.toUtf8();
|
||||
QByteArray encodedPassword(12, loginAsUser ? 0x88 : 0xBB);
|
||||
for (int i = 0; i < password.count(); i++) {
|
||||
encodedPassword[i] = passwordData.at(i) + (loginAsUser ? 0x88 : 0xBB);
|
||||
}
|
||||
|
||||
for (int i = 0; i < encodedPassword.count(); i++) {
|
||||
payloadStream << static_cast<quint8>(encodedPassword.at(i));
|
||||
}
|
||||
|
||||
// End of data
|
||||
payloadStream << static_cast<quint32>(0);
|
||||
|
||||
QByteArray data = header + payload;
|
||||
qCDebug(dcSma()) << "Inverter: Send login request" << data.toHex();
|
||||
m_interface->sendData(data);
|
||||
|
||||
// 534d4100000402a000000001003a001060650ea0 7a01 842a71b3 0001 7d00 42be283a 0001 000000000280 0c04fdff 07000000 84030000 00d8e85f 00000000 c1c1c1c18888888888888888 00000000 => login command = 0xfffd040c, first = 0x00000007 (user 7, installer a), last = 0x00000384 (hier timeout), time = 0x5fdf9ae8, 0x00000000, pw 12 bytes
|
||||
// 534d4100000402a000000001003a001060650ea0 9013 be52007d 0001 7d00 42be283a 0001 000000000280 0c04fdff 07000000 84030000 cc96b061 00000000 c1c1c1c18888888888888888 00000000
|
||||
// 534d4100000402a000000001003a001060650ea0 9013 be52007d 0001 7d00 42be283a 0001 000000000180 0c04fdff 07000000 84030000 ae9db061 00000000 c1c1c1c18888888888888888 00000000
|
||||
|
||||
}
|
||||
|
||||
void SpeedwireInverter::querySoftwareVersion()
|
||||
{
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01 842a71b30001 7d00 42be283a0001 000000000380 00020058 00348200 ff348200 00000000 => query software version
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d00 42be283a00a1 7a01 842a71b30001 000000000380 01020058 0a000000 0a000000 01348200 2ae5e65f 00000000 00000000 feffffff feffffff 040a1003 040a1003 00000000 00000000 00000000 code = 0x00823401 3 (BCD).10 (BCD).10 (BIN) Typ R (Enum)
|
||||
|
||||
// ========= header
|
||||
|
||||
// == 534d4100000402a00000000100260010
|
||||
|
||||
// 534d4100 : SMA\0 signature
|
||||
// 0004 : header length
|
||||
// 02a0 : Tag0 type
|
||||
// 0000 : Tag version
|
||||
// 0001 : Group
|
||||
// 0026 : payload length
|
||||
// 0010 : SMA Net 2 version
|
||||
|
||||
// == 606509a0
|
||||
|
||||
// 6065 : inverter protocol id
|
||||
// 09 : length of long words = payload length / 4
|
||||
// a0 : control ?
|
||||
|
||||
|
||||
|
||||
// ========= payload (big endian)
|
||||
|
||||
// == 7a01842a71b30001
|
||||
|
||||
// 7a01 : destination model id
|
||||
// 842a71b3 : destination serial number
|
||||
// 0001 : destination control field
|
||||
|
||||
// == 7d0042be283a0001
|
||||
|
||||
// 7d00 : source model id
|
||||
// 42be283a: source serial number
|
||||
// 0001 : source control field
|
||||
|
||||
// == 0000 0000 0380 00020058
|
||||
|
||||
// 0000 : error code
|
||||
// 0000 : fragment id
|
||||
// 0380 : packet id
|
||||
|
||||
// 00020058 : command id = CommandQueryDevice
|
||||
// 00348200 : first register
|
||||
// ff348200 : last register
|
||||
|
||||
// 00000000 : end of data
|
||||
|
||||
qCDebug(dcSma()) << "Inverter: Query software version...";
|
||||
|
||||
// The payload is little endian encoded
|
||||
QByteArray payload;
|
||||
QDataStream payloadStream(&payload, QIODevice::WriteOnly);
|
||||
payloadStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
// Destination
|
||||
payloadStream << m_modelId << m_serialNumber << static_cast<quint16>(0x1000);
|
||||
|
||||
// Source
|
||||
payloadStream << m_interface->sourceModelId() << m_interface->sourceSerialNumber() << static_cast<quint16>(0x1000);
|
||||
|
||||
// Packet information
|
||||
quint16 errorCode = 0;
|
||||
quint16 fragmentId = 0;
|
||||
m_packetId += 1;
|
||||
quint16 packetId = m_packetId | 0x8000;
|
||||
payloadStream << errorCode << fragmentId << packetId;
|
||||
|
||||
// Request information
|
||||
payloadStream << static_cast<quint32>(CommandQueryDevice);
|
||||
payloadStream << static_cast<quint32>(0x00823400); // Software version first
|
||||
payloadStream << static_cast<quint32>(0x008234ff); // Software version last
|
||||
|
||||
// End of data
|
||||
payloadStream << static_cast<quint32>(0);
|
||||
|
||||
// Build the header
|
||||
QByteArray header = QByteArray::fromHex("534d4100000402a00000000100260010606509a0");
|
||||
|
||||
QByteArray data = header + payload;
|
||||
m_interface->sendData(data);
|
||||
}
|
||||
|
||||
void SpeedwireInverter::queryDeviceType()
|
||||
{
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00020058 001e8200 ff208200 00000000 => query device type
|
||||
// Response 534d4100000402a000000001009e0010 606527a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01020058 01000000 03000000 011e8210 6f89e95f 534e3a20 33303130 35333831 31360000 00000000 00000000 00000000 00000000
|
||||
// 011f8208 6f89e95f 411f0001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 => 1f41 solar inverter
|
||||
// 01208208 6f89e95f 96240000 80240000 81240001 82240000 feffff00 00000000 00000000 00000000 00000000
|
||||
}
|
||||
|
||||
|
||||
void SpeedwireInverter::processData(const QByteArray &data)
|
||||
{
|
||||
qCDebug(dcSma()) << "Inverter: data received" << data.toHex();
|
||||
|
||||
}
|
||||
75
sma/speedwireinverter.h
Normal file
75
sma/speedwireinverter.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef SPEEDWIREINVERTER_H
|
||||
#define SPEEDWIREINVERTER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "speedwireinterface.h"
|
||||
|
||||
class SpeedwireInverter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Command {
|
||||
CommandQueryAc = 0x51000200,
|
||||
CommandQueryStatus = 0x51800200,
|
||||
CommandQueryDevice = 0x58000200,
|
||||
CommandQueryDc = 0x53800200,
|
||||
CommandQueryLogin = 0xfffd040c
|
||||
};
|
||||
|
||||
|
||||
Q_ENUM(Command)
|
||||
|
||||
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
Request();
|
||||
|
||||
SpeedwireInverter::Command command() const;
|
||||
|
||||
quint16 requestId() const;
|
||||
|
||||
private:
|
||||
SpeedwireInverter::Command m_command;
|
||||
quint16 m_requestId = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
explicit SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
|
||||
|
||||
bool initialize();
|
||||
bool initialized() const;
|
||||
|
||||
double currentPower() const;
|
||||
double totalEnergyProduced() const;
|
||||
|
||||
// Query methods
|
||||
void sendLoginRequest(const QString &password = "0000", bool loginAsUser = true);
|
||||
void querySoftwareVersion();
|
||||
void queryDeviceType();
|
||||
|
||||
signals:
|
||||
void valuesUpdated();
|
||||
|
||||
private:
|
||||
SpeedwireInterface *m_interface = nullptr;
|
||||
QHostAddress m_address;
|
||||
bool m_initialized = false;
|
||||
quint16 m_modelId = 0;
|
||||
quint32 m_serialNumber = 0;
|
||||
quint16 m_packetId = 0;
|
||||
|
||||
// Properties
|
||||
double m_currentPower = 0;
|
||||
double m_totalEnergyProduced = 0;
|
||||
|
||||
QString m_softwareVersion;
|
||||
|
||||
private slots:
|
||||
void processData(const QByteArray &data);
|
||||
|
||||
};
|
||||
|
||||
#endif // SPEEDWIREINVERTER_H
|
||||
@ -218,28 +218,28 @@ void SpeedwireMeter::processData(const QByteArray &data)
|
||||
|
||||
if (measurementIndex == 1 && measurement != 0) {
|
||||
m_totalEnergyConsumed = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Total energy consumed" << m_totalEnergyConsumed << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Total energy consumed" << m_totalEnergyConsumed << "kWh";
|
||||
} else if (measurementIndex == 2 && measurement != 0) {
|
||||
m_totalEnergyProduced = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Total energy produced" << m_totalEnergyProduced << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Total energy produced" << m_totalEnergyProduced << "kWh";
|
||||
} else if (measurementIndex == 21 && measurement != 0) {
|
||||
m_energyConsumedPhaseA = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase A" << m_energyConsumedPhaseA << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy consumed phase A" << m_energyConsumedPhaseA << "kWh";
|
||||
} else if (measurementIndex == 41 && measurement != 0) {
|
||||
m_energyConsumedPhaseB = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase B" << m_energyConsumedPhaseB << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy consumed phase B" << m_energyConsumedPhaseB << "kWh";
|
||||
} else if (measurementIndex == 61 && measurement != 0) {
|
||||
m_energyConsumedPhaseC = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase C" << m_energyConsumedPhaseC << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy consumed phase C" << m_energyConsumedPhaseC << "kWh";
|
||||
} else if (measurementIndex == 22 && measurement != 0) {
|
||||
m_energyProducedPhaseA = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase A" << m_energyProducedPhaseA << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy produced phase A" << m_energyProducedPhaseA << "kWh";
|
||||
} else if (measurementIndex == 42 && measurement != 0) {
|
||||
m_energyProducedPhaseB = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase B" << m_energyProducedPhaseB << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy produced phase B" << m_energyProducedPhaseB << "kWh";
|
||||
} else if (measurementIndex == 62 && measurement != 0) {
|
||||
m_energyProducedPhaseC = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase C" << m_energyProducedPhaseC << "kWh";
|
||||
qCDebug(dcSma()) << "Meter: Energy produced phase C" << m_energyProducedPhaseC << "kWh";
|
||||
} else {
|
||||
// qCDebug(dcSma()) << "Meter: --> Channel:" << measurementChannel << "Index:" << measurementIndex << "Type:" << measurmentType << "Rate:" << measurmentTariff;
|
||||
// qCDebug(dcSma()) << "Meter: Value:" << measurement;
|
||||
|
||||
@ -14,7 +14,6 @@ public:
|
||||
bool initialize();
|
||||
bool initialized() const;
|
||||
|
||||
|
||||
double currentPower() const;
|
||||
double totalEnergyProduced() const;
|
||||
double totalEnergyConsumed() const;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user