mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-17 00:36:35 +02:00
Add sunspec model
This commit is contained in:
parent
c08b38e4cf
commit
f647b809c9
@ -1,6 +1,8 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
QT += network
|
||||
PKGCONFIG += nymea-sunspec
|
||||
|
||||
QT += network serialbus
|
||||
|
||||
SOURCES += \
|
||||
froniusnetworkreply.cpp \
|
||||
|
||||
@ -202,6 +202,106 @@ void IntegrationPluginFronius::setupThing(ThingSetupInfo *info)
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
||||
QHostAddress address(thing->paramValue(sunspecStorageThingAddressParamTypeId).toString());
|
||||
uint port(thing->paramValue(sunspecStorageThingPortParamTypeId).toUInt());
|
||||
|
||||
SunSpecConnection * connection = new SunSpecConnection(address, port, 1, SunSpecDataPoint::ByteOrderBigEndian, nullptr);
|
||||
|
||||
connect(info, &ThingSetupInfo::aborted, connection, &SunSpecConnection::deleteLater);
|
||||
|
||||
// Update all child things connected states for this connection
|
||||
connect(connection, &SunSpecConnection::connectedChanged, thing, [this, connection, thing] (bool connected) {
|
||||
if (connected) {
|
||||
qCDebug(dcSunSpec()) << connection << "connected";
|
||||
} else {
|
||||
qCWarning(dcSunSpec()) << connection << "disconnected";
|
||||
}
|
||||
|
||||
thing->setStateValue("connected", connected);
|
||||
});
|
||||
|
||||
// Only during setup
|
||||
connect(connection, &SunSpecConnection::connectedChanged, info, [this, connection, info] (bool connected) {
|
||||
//qCDebug(dcSunSpec()) << "SunSpec connected changed during setup:" << (connected ? "connected" : "disconnected");
|
||||
if (connected) {
|
||||
connect(connection, &SunSpecConnection::discoveryFinished, info, [this, connection, info] (bool success) {
|
||||
if (success) {
|
||||
qCDebug(dcSunSpec()) << "Discovery finished successfully during setup of" << connection << ". Found SunSpec data on base register" << connection->baseRegister();
|
||||
m_sunspecConnections.insert(connection, info->thing());
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
Thing *thing = info->thing();
|
||||
|
||||
// Get the battery model
|
||||
foreach (SunSpecModel *model, connection->models()) {
|
||||
if (model->modelId() == SunSpecModelFactory::ModelIdStorage) {
|
||||
SunSpecStorageModel *storage = qobject_cast<SunSpecStorageModel *>(model);
|
||||
m_sunSpecStorages.insert(thing, storage);
|
||||
|
||||
connect(model, &SunSpecModel::blockUpdated, this, [storage, thing, model](){
|
||||
qCDebug(dcSunSpec()) << thing->name() << "block data updated" << storage;
|
||||
|
||||
thing->setStateValue(sunspecStorageConnectedStateTypeId, true);
|
||||
thing->setStateValue(sunspecStorageVersionStateTypeId, model->commonModelInfo().versionString);
|
||||
|
||||
thing->setStateValue(sunspecStorageBatteryCriticalStateTypeId, storage->chaState() < 5);
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, qRound(storage->chaState()));
|
||||
thing->setStateValue(sunspecStorageGridChargingStateTypeId, storage->chaGriSet() == SunSpecStorageModel::ChagrisetGrid);
|
||||
thing->setStateValue(sunspecStorageEnableChargingStateTypeId, storage->storCtlMod().testFlag(SunSpecStorageModel::Storctl_modCharge));
|
||||
thing->setStateValue(sunspecStorageChargingRateStateTypeId, storage->wChaGra());
|
||||
thing->setStateValue(sunspecStorageDischargingRateStateTypeId, storage->wDisChaGra());
|
||||
|
||||
switch (storage->chaSt()) {
|
||||
case SunSpecStorageModel::ChastOff:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Off");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "idle");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastEmpty:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Empty");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "idle");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastDischarging:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Discharging");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "discharging");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastCharging:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Charging");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "charging");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastFull:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Full");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "idle");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastHolding:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Holding");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "idle");
|
||||
break;
|
||||
case SunSpecStorageModel::ChastTesting:
|
||||
thing->setStateValue(sunspecStorageBatteryLevelStateTypeId, "Testing");
|
||||
thing->setStateValue(sunspecStorageChargingStateStateTypeId, "idle");
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
qCWarning(dcSunSpec()) << "Discovery finished with errors during setup of" << connection;
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("The SunSpec discovery finished with errors. Please make sure this is a SunSpec device."));
|
||||
}
|
||||
});
|
||||
// Perform initial discovery, finish if a valid base register has been found
|
||||
connection->startDiscovery();
|
||||
} else {
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
}
|
||||
});
|
||||
|
||||
connection->connectDevice();
|
||||
|
||||
} else {
|
||||
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||
}
|
||||
@ -220,6 +320,11 @@ void IntegrationPluginFronius::postSetupThing(Thing *thing)
|
||||
foreach (FroniusSolarConnection *connection, m_froniusConnections.keys()) {
|
||||
refreshConnection(connection);
|
||||
}
|
||||
|
||||
foreach (SunSpecStorageModel *model, m_sunSpecStorages.values()) {
|
||||
model->readBlockData();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
m_connectionRefreshTimer->start();
|
||||
@ -250,7 +355,122 @@ void IntegrationPluginFronius::thingRemoved(Thing *thing)
|
||||
|
||||
void IntegrationPluginFronius::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
Q_UNUSED(info)
|
||||
Thing *thing = info->thing();
|
||||
Action action = info->action();
|
||||
|
||||
if (thing->thingClassId() == sunspecStorageThingClassId) {
|
||||
SunSpecStorageModel *storage = qobject_cast<SunSpecStorageModel *>(m_sunSpecStorages.value(thing));
|
||||
if (!storage) {
|
||||
qWarning(dcSunSpec()) << "Could not find sunspec model instance for thing" << thing;
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!thing->stateValue(sunspecStorageConnectedStateTypeId).toBool()) {
|
||||
qWarning(dcSunSpec()) << "Could not execute action for" << thing << "because the SunSpec connection is not connected.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The SunSpec connection is not connected."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sunspecStorageGridChargingActionTypeId) {
|
||||
bool gridCharging = action.param(sunspecStorageGridChargingActionGridChargingParamTypeId).value().toBool();
|
||||
qCDebug(dcFronius()) << "Set grid charging" << (gridCharging ? SunSpecStorageModel::ChagrisetGrid : SunSpecStorageModel::ChagrisetPv);
|
||||
QModbusReply *reply = storage->setChaGriSet(gridCharging ? SunSpecStorageModel::ChagrisetGrid : SunSpecStorageModel::ChagrisetPv);
|
||||
if (!reply) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, info, [info, reply]{
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
qCDebug(dcFronius()) << "Set grid charging failed";
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
qCDebug(dcFronius()) << "Set grid charging finished successfully";
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == sunspecStorageEnableChargingActionTypeId || action.actionTypeId() == sunspecStorageEnableDischargingActionTypeId) {
|
||||
SunSpecStorageModel::Storctl_modFlags controlModeFlags;
|
||||
if (action.actionTypeId() == sunspecStorageEnableChargingActionTypeId) {
|
||||
if (action.param(sunspecStorageEnableChargingActionEnableChargingParamTypeId).value().toBool()) {
|
||||
controlModeFlags.setFlag(SunSpecStorageModel::Storctl_modCharge);
|
||||
}
|
||||
|
||||
if (thing->stateValue(sunspecStorageEnableDischargingStateTypeId).toBool())
|
||||
controlModeFlags.setFlag(SunSpecStorageModel::Storctl_modDiScharge);
|
||||
|
||||
}
|
||||
|
||||
if (action.actionTypeId() == sunspecStorageEnableDischargingActionTypeId) {
|
||||
if (action.param(sunspecStorageEnableDischargingActionEnableDischargingParamTypeId).value().toBool()) {
|
||||
controlModeFlags.setFlag(SunSpecStorageModel::Storctl_modDiScharge);
|
||||
}
|
||||
|
||||
if (thing->stateValue(sunspecStorageEnableChargingStateTypeId).toBool())
|
||||
controlModeFlags.setFlag(SunSpecStorageModel::Storctl_modCharge);
|
||||
|
||||
}
|
||||
|
||||
qCDebug(dcFronius()) << "Set charging control flag" << controlModeFlags;
|
||||
|
||||
QModbusReply *reply = storage->setStorCtlMod(controlModeFlags);
|
||||
if (!reply) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, info, [info, reply]{
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
qCDebug(dcFronius()) << "Set charging control flag finished with error";
|
||||
return;
|
||||
}
|
||||
if (info->action().actionTypeId() == sunspecStorageEnableChargingActionTypeId) {
|
||||
info->thing()->setStateValue(sunspecStorageEnableChargingStateTypeId, info->action().param(sunspecStorageEnableChargingActionEnableChargingParamTypeId).value());
|
||||
}
|
||||
|
||||
if (info->action().actionTypeId() == sunspecStorageEnableDischargingActionTypeId) {
|
||||
info->thing()->setStateValue(sunspecStorageEnableDischargingStateTypeId, info->action().param(sunspecStorageEnableDischargingActionEnableDischargingParamTypeId).value());
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
qCDebug(dcFronius()) << "Set charging control flag finished successfully";
|
||||
});
|
||||
} else if (action.actionTypeId() == sunspecStorageChargingRateActionTypeId) {
|
||||
QModbusReply *reply = storage->setInWRte(action.param(sunspecStorageChargingRateActionChargingRateParamTypeId).value().toInt());
|
||||
if (!reply) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, info, [info, reply]{
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == sunspecStorageDischargingRateActionTypeId) {
|
||||
QModbusReply *reply = storage->setOutWRte(action.param(sunspecStorageDischargingRateActionDischargingRateParamTypeId).value().toInt());
|
||||
if (!reply) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||
connect(reply, &QModbusReply::finished, info, [info, reply]{
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else {
|
||||
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginFronius::refreshConnection(FroniusSolarConnection *connection)
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "froniussolarconnection.h"
|
||||
|
||||
#include <nymea-sunspec/sunspecconnection.h>
|
||||
#include <nymea-sunspec/home/jenkins/nymea-plugins-modbus/libnymea-sunspec/models/sunspecstoragemodel.h>
|
||||
#include <nymea-sunspec/home/jenkins/nymea-plugins-modbus/libnymea-sunspec/models/sunspecmodelfactory.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
@ -60,6 +64,8 @@ private:
|
||||
PluginTimer *m_connectionRefreshTimer = nullptr;
|
||||
|
||||
QHash<FroniusSolarConnection *, Thing *> m_froniusConnections;
|
||||
QHash<SunSpecConnection *, Thing *> m_sunspecConnections;
|
||||
QHash<Thing *, SunSpecStorageModel *> m_sunSpecStorages;
|
||||
|
||||
void refreshConnection(FroniusSolarConnection *connection);
|
||||
|
||||
|
||||
@ -302,6 +302,7 @@
|
||||
"createMethods": ["auto"],
|
||||
"interfaces": [ "energystorage", "connectable"],
|
||||
"paramTypes": [
|
||||
|
||||
{
|
||||
"id": "49087f31-abf5-4bb8-946b-a3626ee80566",
|
||||
"name": "id",
|
||||
@ -387,6 +388,190 @@
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sunspecStorage",
|
||||
"displayName": "Fronius SunSpec Storage",
|
||||
"id": "5cdf938a-5680-4fdd-96cf-69a06b3b68ae",
|
||||
"createMethods": [ "user" ],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "115f1b9c-10f7-40e3-8dd9-7bd886e7d19c",
|
||||
"name": "address",
|
||||
"displayName": "Host address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": "84.20.187.116"
|
||||
},
|
||||
{
|
||||
"id": "cefe2a6d-1312-4f3f-b1fa-839c1dc22418",
|
||||
"name":"port",
|
||||
"displayName": "Port",
|
||||
"type": "int",
|
||||
"defaultValue": 21502
|
||||
},
|
||||
{
|
||||
"id": "a63cb6d6-29c7-4961-a341-f5b7289b1145",
|
||||
"name":"modelId",
|
||||
"displayName": "Model",
|
||||
"type": "int",
|
||||
"readOnly": true,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "3f107844-00c5-4f39-86e5-485b3d1f5c1a",
|
||||
"name":"modbusAddress",
|
||||
"displayName": "Modbus address",
|
||||
"type": "uint",
|
||||
"readOnly": true,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "1335ae67-7597-45d3-856f-2a56449b8a5f",
|
||||
"name":"manufacturer",
|
||||
"displayName": "Manufacturer",
|
||||
"type": "QString",
|
||||
"defaultValue": "Unkown",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "c860d44c-af8c-4e6c-b67e-481c73a1ad68",
|
||||
"name":"deviceModel",
|
||||
"displayName": "Device model",
|
||||
"type": "QString",
|
||||
"defaultValue": "Unkown",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "fb366e44-86f2-46ad-97d0-5b20d44eb6cb",
|
||||
"name":"serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"defaultValue": "Unkown",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes":[
|
||||
{
|
||||
"id": "47755a1c-b4a7-4d50-b718-dd43ab6ee5cd",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f57c9418-fd16-4d9d-b7c5-54c734754dd2",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "Battery critical",
|
||||
"displayNameEvent": "Battery critical changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "56930aec-93c7-4609-a0d1-0e24fc45e3da",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "Battery level",
|
||||
"displayNameEvent": "Battery level changed",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "1dd36a84-eacd-4bd7-adbd-3ab1ccfaa9ac",
|
||||
"name": "storageStatus",
|
||||
"displayName": "Status",
|
||||
"displayNameEvent": "Status changed",
|
||||
"type": "QString",
|
||||
"possibleValues": [
|
||||
"Off",
|
||||
"Empty",
|
||||
"Discharging",
|
||||
"Charging",
|
||||
"Full",
|
||||
"Holding",
|
||||
"Testing"
|
||||
],
|
||||
"defaultValue": "Off"
|
||||
},
|
||||
{
|
||||
"id": "c8f9cd0a-423a-40d0-94cc-8775cdf075fe",
|
||||
"name": "chargingState",
|
||||
"displayName": "Charging state",
|
||||
"displayNameEvent": "Charging state changed",
|
||||
"type": "QString",
|
||||
"possibleValues": ["idle", "charging", "discharging"],
|
||||
"defaultValue": "idle"
|
||||
},
|
||||
{
|
||||
"id": "12435c63-0a73-4b49-95e1-1d8c8f7e4578",
|
||||
"name": "gridCharging",
|
||||
"displayName": "Grid charging",
|
||||
"displayNameEvent": "Grid charging changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true,
|
||||
"displayNameAction": "Set grid charging"
|
||||
},
|
||||
{
|
||||
"id": "b80df52f-47aa-44cf-8199-54dc96eba2ad",
|
||||
"name": "enableCharging",
|
||||
"displayName": "Charging",
|
||||
"displayNameEvent": "Charging changed",
|
||||
"displayNameAction": "Enable charging",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "80924163-d2a7-409a-a5c2-e30dc2cd30a3",
|
||||
"name": "enableDischarging",
|
||||
"displayName": "Discharging",
|
||||
"displayNameEvent": "Discharging changed",
|
||||
"displayNameAction": "Enable discharging",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "a8954186-4264-4edf-873e-1507d45e40e4",
|
||||
"name": "chargingRate",
|
||||
"displayName": "Charging rate",
|
||||
"displayNameEvent": "Charging rate changed",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"unit": "Percentage",
|
||||
"defaultValue": false,
|
||||
"writable": true,
|
||||
"displayNameAction": "Set charging rate"
|
||||
},
|
||||
{
|
||||
"id": "67bc1367-453f-4f24-8942-978d9ffd43f5",
|
||||
"name": "dischargingRate",
|
||||
"displayName": "Discharging rate",
|
||||
"displayNameEvent": "Discharging rate changed",
|
||||
"type": "int",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"unit": "Percentage",
|
||||
"defaultValue": false,
|
||||
"writable": true,
|
||||
"displayNameAction": "Set discharging rate"
|
||||
},
|
||||
{
|
||||
"id": "6364e277-b168-4acf-b0f6-fe32ced05f0e",
|
||||
"name": "version",
|
||||
"displayName": "Version",
|
||||
"displayNameEvent": "Version changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user