mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-01 22:20:55 +02:00
Add senec meter option and add capacity setting
This commit is contained in:
parent
e9a0fe1f08
commit
1f0733db97
@ -126,6 +126,32 @@ void IntegrationPluginSenec::setupThing(ThingSetupInfo *info)
|
||||
thing->setStateValue(senecAccountUserDisplayNameStateTypeId, username);
|
||||
|
||||
} else if (thing->thingClassId() == senecStorageThingClassId) {
|
||||
|
||||
connect(thing, &Thing::settingChanged, this, [this, thing](const ParamTypeId ¶mTypeId, const QVariant &value){
|
||||
if (paramTypeId == senecStorageSettingsCapacityParamTypeId) {
|
||||
thing->setStateValue(senecStorageCapacityStateTypeId, value.toDouble());
|
||||
} else if (paramTypeId == senecStorageSettingsAddMeterParamTypeId) {
|
||||
|
||||
if (value.toBool()) {
|
||||
// Check if we have to add the meter
|
||||
if (myThings().filterByThingClassId(senecMeterThingClassId).filterByParentId(thing->id()).isEmpty()) {
|
||||
qCDebug(dcSenec()) << "Add meter for" << thing->name();
|
||||
emit autoThingsAppeared(ThingDescriptors() << ThingDescriptor(senecMeterThingClassId, "SENEC Meter", QString(), thing->id()));
|
||||
}
|
||||
} else {
|
||||
// Check if we have to remove the meter
|
||||
Things existingMeters = myThings().filterByThingClassId(senecMeterThingClassId).filterByParentId(thing->id());
|
||||
if (!existingMeters.isEmpty()) {
|
||||
qCDebug(dcSenec()) << "Remove meter thing for" << thing->name();
|
||||
emit autoThingDisappeared(existingMeters.takeFirst()->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (thing->thingClassId() == senecMeterThingClassId) {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
@ -201,8 +227,11 @@ void IntegrationPluginSenec::postSetupThing(Thing *thing)
|
||||
});
|
||||
} else if (thing->thingClassId() == senecStorageThingClassId) {
|
||||
|
||||
thing->setStateValue(senecStorageCapacityStateTypeId, thing->setting(senecStorageSettingsCapacityParamTypeId).toDouble());
|
||||
|
||||
SenecAccount *account = m_accounts.value(myThings().findById(thing->parentId()));
|
||||
QString id = thing->paramValue(senecStorageThingIdParamTypeId).toString();
|
||||
|
||||
QNetworkReply *reply = account->getTechnicalData(id);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, account, [reply, thing, this] {
|
||||
@ -269,60 +298,106 @@ void IntegrationPluginSenec::executeAction(ThingActionInfo *info)
|
||||
|
||||
void IntegrationPluginSenec::refresh(Thing *thing)
|
||||
{
|
||||
if (thing) {
|
||||
SenecAccount *account = m_accounts.value(myThings().findById(thing->parentId()));
|
||||
QString id = thing->paramValue(senecStorageThingIdParamTypeId).toString();
|
||||
QNetworkReply *reply = account->getDashboard(id);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, account, [reply, thing] {
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
// Check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcSenec()) << "Dashboard request finished with error. Status:" << status << "Error:" << reply->errorString();
|
||||
thing->setStateValue(senecStorageConnectedStateTypeId, false);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonError);
|
||||
QVariantMap responseMap = jsonDoc.toVariant().toMap();
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcSenec()) << "Dashboard request finished successfully, but the response contains invalid JSON object:" << responseData;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSenec()) << "Dashboard request finished successfully" << qUtf8Printable(jsonDoc.toJson());
|
||||
thing->setStateValue(senecStorageConnectedStateTypeId, true);
|
||||
|
||||
QVariantMap currentDataMap = responseMap.value("currently").toMap();
|
||||
float batteryCharge = qRound(currentDataMap.value("batteryChargeInW").toFloat() * 100) / 100.0;
|
||||
float batteryDischarge = qRound(currentDataMap.value("batteryDischargeInW").toFloat() * 100) / 100.0;
|
||||
int batteryLevel = currentDataMap.value("batteryLevelInPercent").toInt();
|
||||
|
||||
// qCDebug(dcSenec()) << "charge:" << batteryCharge << "W" << "discharge:" << batteryDischarge << "W" << "level" << batteryLevel << "%";
|
||||
|
||||
float currentPower = 0;
|
||||
|
||||
if (batteryCharge != 0) {
|
||||
currentPower = batteryCharge;
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "charging");
|
||||
} else if (batteryDischarge != 0) {
|
||||
currentPower = -batteryDischarge;
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "discharging");
|
||||
} else {
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "idle");
|
||||
}
|
||||
|
||||
thing->setStateValue(senecStorageCurrentPowerStateTypeId, currentPower);
|
||||
thing->setStateValue(senecStorageBatteryLevelStateTypeId, batteryLevel);
|
||||
thing->setStateValue(senecStorageBatteryCriticalStateTypeId, batteryLevel < 10);
|
||||
});
|
||||
} else {
|
||||
// If no thing given, refresh all storages recursive
|
||||
if (!thing) {
|
||||
foreach (Thing *storageThing, myThings().filterByThingClassId(senecStorageThingClassId)) {
|
||||
refresh(storageThing);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Thing *parentThing = myThings().findById(thing->parentId());
|
||||
SenecAccount *account = m_accounts.value(parentThing);
|
||||
QString id = thing->paramValue(senecStorageThingIdParamTypeId).toString();
|
||||
|
||||
QNetworkReply *reply = account->getDashboard(id);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, account, [reply, thing, this] {
|
||||
|
||||
// Check if we have a meter
|
||||
Thing *meterThing = nullptr;
|
||||
Things meterThings = myThings().filterByThingClassId(senecMeterThingClassId).filterByParentId(thing->id());
|
||||
if (!meterThings.isEmpty()) {
|
||||
meterThing = meterThings.first();
|
||||
}
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
// Check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcSenec()) << "Dashboard request finished with error. Status:" << status << "Error:" << reply->errorString();
|
||||
thing->setStateValue(senecStorageConnectedStateTypeId, false);
|
||||
if (meterThing) {
|
||||
meterThing->setStateValue(senecMeterConnectedStateTypeId, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonError);
|
||||
QVariantMap responseMap = jsonDoc.toVariant().toMap();
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcSenec()) << "Dashboard request finished successfully, but the response contains invalid JSON object:" << responseData;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSenec()) << "Dashboard request finished successfully" << qUtf8Printable(jsonDoc.toJson());
|
||||
thing->setStateValue(senecStorageConnectedStateTypeId, true);
|
||||
|
||||
QVariantMap currentDataMap = responseMap.value("currently").toMap();
|
||||
float batteryCharge = qRound(currentDataMap.value("batteryChargeInW").toFloat() * 100) / 100.0;
|
||||
float batteryDischarge = qRound(currentDataMap.value("batteryDischargeInW").toFloat() * 100) / 100.0;
|
||||
int batteryLevel = currentDataMap.value("batteryLevelInPercent").toInt();
|
||||
|
||||
// qCDebug(dcSenec()) << "charge:" << batteryCharge << "W" << "discharge:" << batteryDischarge << "W" << "level" << batteryLevel << "%";
|
||||
|
||||
// Note: there are some situations where the battery is charging and discharging at the same time.
|
||||
// In that case we use the bigger power. Maybe we cloudl als sum them up, that should be tested...
|
||||
|
||||
float currentPower = 0;
|
||||
if (batteryCharge != 0 && batteryCharge != 0) {
|
||||
if (batteryCharge > batteryDischarge) {
|
||||
currentPower = batteryCharge;
|
||||
} else {
|
||||
currentPower = -batteryDischarge;
|
||||
}
|
||||
} else if (batteryCharge != 0) {
|
||||
currentPower = batteryCharge;
|
||||
} else if (batteryDischarge != 0) {
|
||||
currentPower = -batteryDischarge;
|
||||
}
|
||||
|
||||
if (currentPower > 0) {
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "charging");
|
||||
} else if (currentPower < 0) {
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "discharging");
|
||||
} else {
|
||||
thing->setStateValue(senecStorageChargingStateStateTypeId, "idle");
|
||||
}
|
||||
|
||||
thing->setStateValue(senecStorageCurrentPowerStateTypeId, currentPower);
|
||||
thing->setStateValue(senecStorageBatteryLevelStateTypeId, batteryLevel);
|
||||
thing->setStateValue(senecStorageBatteryCriticalStateTypeId, batteryLevel < 10);
|
||||
|
||||
// Check if we have a meter
|
||||
if (meterThing) {
|
||||
float gridConsume = qRound(currentDataMap.value("gridDrawInW").toFloat() * 100) / 100.0;
|
||||
float gridReturn = qRound(currentDataMap.value("gridFeedInInW").toFloat() * 100) / 100.0;
|
||||
|
||||
qCDebug(dcSenec()) << "Grid power: consume" << gridConsume << "W" << "return:" << gridReturn << "W";
|
||||
|
||||
double currentPower = 0;
|
||||
|
||||
if (gridConsume != 0) {
|
||||
currentPower = gridConsume;
|
||||
} else if (gridReturn != 0) {
|
||||
currentPower = -gridReturn;
|
||||
}
|
||||
|
||||
meterThing->setStateValue(senecMeterCurrentPowerStateTypeId, currentPower);
|
||||
meterThing->setStateValue(senecMeterConnectedStateTypeId, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -46,6 +46,25 @@
|
||||
"id": "a983c5ee-1c58-4cad-87fb-1bc612cbe6e4",
|
||||
"createMethods": [ "Auto" ],
|
||||
"interfaces": ["energystorage", "connectable"],
|
||||
"settingsTypes": [
|
||||
{
|
||||
"id": "44836ff6-4d69-42b9-a5a8-b37bcc0b24fe",
|
||||
"name": "capacity",
|
||||
"displayName": "Capacity",
|
||||
"type": "double",
|
||||
"minValue": 7.1,
|
||||
"maxValue": 17.75,
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.0
|
||||
},
|
||||
{
|
||||
"id": "0a16f7c8-2e55-4869-aeb3-574f22b40527",
|
||||
"name":"addMeter",
|
||||
"displayName": "Add meter",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "9a0fa7b1-bc35-44d4-b987-5ecb87fd8b00",
|
||||
@ -56,7 +75,7 @@
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"stateTypes":[
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "320cb3f0-d2c5-4a30-817f-474f0cc87253",
|
||||
"name": "connected",
|
||||
@ -108,6 +127,129 @@
|
||||
"defaultValue": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "senecMeter",
|
||||
"displayName": "SENEC Meter",
|
||||
"id": "303f454d-93b7-4f8f-b295-a4c22e09be6d",
|
||||
"createMethods": [ "auto" ],
|
||||
"interfaces": ["energymeter", "connectable"],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "9aa4dbfb-255b-408b-b148-8fb145ba8c98",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "56e58d03-782d-40bb-8d32-fad396ffefdc",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power usage",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f9920cfa-26a0-4247-ad50-b4fca91596f0",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Energy returned",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "21728bf0-a566-4eb0-a163-5f108f34bb9b",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Energy consumed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "385df15e-4f1d-4842-a10a-597d087bdac0",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "3bab2b05-99d7-4b3a-b903-054d269a3527",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "01a63c94-b635-4e98-8037-d5d69fe0dd7c",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "db469573-bb11-48d2-84ba-2b8ff4b13413",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "176d5a01-220d-44da-b875-9a40aaa9c85a",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e7ab02d3-016a-44ca-8da1-5d7ac9acbc6d",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1923f7b0-5b54-46d8-a7e9-9364b7c9eeb8",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "4908abf8-f655-4e14-a007-3eefeda7088e",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e8415152-0fac-4cd9-98ac-b0e7c006d637",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@ QT+= network
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsenec.cpp \
|
||||
senecaccount.cpp
|
||||
senecaccount.cpp \
|
||||
seneclanstorage.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsenec.h \
|
||||
senecaccount.h
|
||||
senecaccount.h \
|
||||
seneclanstorage.h
|
||||
|
||||
|
||||
5
senec/seneclanstorage.cpp
Normal file
5
senec/seneclanstorage.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include "seneclanstorage.h"
|
||||
|
||||
SenecLanStorage::SenecLanStorage(QObject *parent)
|
||||
: QObject{parent}
|
||||
{}
|
||||
15
senec/seneclanstorage.h
Normal file
15
senec/seneclanstorage.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef SENECLANSTORAGE_H
|
||||
#define SENECLANSTORAGE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class SenecLanStorage : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SenecLanStorage(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
#endif // SENECLANSTORAGE_H
|
||||
Loading…
x
Reference in New Issue
Block a user