From 94b46bdd540a46858ce1e542363f78381df0ff66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 1 Dec 2023 11:16:10 +0100 Subject: [PATCH] SunSpec: Filter out invalid energy produced values --- sunspec/integrationpluginsunspec.cpp | 60 ++++++++++++++++++++++++---- sunspec/integrationpluginsunspec.h | 3 ++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index b16c4ec..1663951 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -558,8 +558,8 @@ void IntegrationPluginSunSpec::processDiscoveryResult(Thing *thing, SunSpecConne qCDebug(dcSunSpec()) << "Found" << modelThing << "for" << model; if (modelThing->thingClassId() == sunspecSinglePhaseInverterThingClassId - || modelThing->thingClassId() == sunspecSplitPhaseInverterThingClassId - || modelThing->thingClassId() == sunspecThreePhaseInverterThingClassId) { + || modelThing->thingClassId() == sunspecSplitPhaseInverterThingClassId + || modelThing->thingClassId() == sunspecThreePhaseInverterThingClassId) { if (!m_sunSpecInverters.contains(modelThing)) { m_sunSpecInverters.insert(modelThing, model); @@ -1214,7 +1214,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() // Note: solar edge needs some calculations for the current pv power double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecSinglePhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecSinglePhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecSinglePhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecSinglePhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecSinglePhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1235,7 +1235,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() // Note: solar edge needs some calculations for the current pv power double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecSinglePhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecSinglePhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecSinglePhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecSinglePhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecSinglePhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1255,7 +1255,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecSplitPhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecSplitPhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecSplitPhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecSplitPhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecSplitPhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1278,7 +1278,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecSplitPhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecSplitPhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecSplitPhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecSplitPhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecSplitPhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1301,7 +1301,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecThreePhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecThreePhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecThreePhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecThreePhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecThreePhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1326,7 +1326,7 @@ void IntegrationPluginSunSpec::onInverterBlockUpdated() double currentPower = calculateSolarEdgePvProduction(thing, -inverter->watts(), -inverter->dcWatts()); thing->setStateValue(sunspecThreePhaseInverterCurrentPowerStateTypeId, currentPower); - thing->setStateValue(sunspecThreePhaseInverterTotalEnergyProducedStateTypeId, inverter->wattHours() / 1000.0); + evaluateEnergyProducedValue(thing, inverter->wattHours() / 1000.0); thing->setStateValue(sunspecThreePhaseInverterTotalCurrentStateTypeId, inverter->amps()); thing->setStateValue(sunspecThreePhaseInverterFrequencyStateTypeId, inverter->hz()); thing->setStateValue(sunspecThreePhaseInverterCabinetTemperatureStateTypeId, inverter->cabinetTemperature()); @@ -1652,3 +1652,47 @@ void IntegrationPluginSunSpec::onSolarEdgeBatteryBlockUpdated() thing->setStateValue(solarEdgeBatteryStateOfHealthStateTypeId, battery->batteryData().stateOfHealth); thing->setStateValue(solarEdgeBatteryVersionStateTypeId, battery->batteryData().firmwareVersion); } + +void IntegrationPluginSunSpec::evaluateEnergyProducedValue(Thing *inverterThing, float energyProduced) +{ + /* Note: on some systems the inverter sends for a longer period an absurdly + * high and wrong value for the produced energy (seen so far with SolarEdge inverters). + * + * In order to catch such situations, we need to verify if the state changed makes sense, + * or if the difference is to big for a regular produced energy value. + * + * Following scenarios need to be considered: + * - This is the first data value, we have no history to verify if this values makes sense + * - The system might be switched off for some time, the energy produced could be much more than the last known value + * - More than one value in a row could occure, not only single garbage data value + */ + + StateTypeId energyProducedStateTypeId; + if (inverterThing->thingClassId() == sunspecSinglePhaseInverterThingClassId) { + energyProducedStateTypeId = sunspecSinglePhaseInverterTotalEnergyProducedStateTypeId; + } else if (inverterThing->thingClassId() == sunspecSplitPhaseInverterThingClassId) { + energyProducedStateTypeId = sunspecSplitPhaseInverterTotalEnergyProducedStateTypeId; + } else if (inverterThing->thingClassId() == sunspecThreePhaseInverterThingClassId) { + energyProducedStateTypeId = sunspecThreePhaseInverterTotalEnergyProducedStateTypeId; + } else { + qCWarning(dcSunSpec()) << "Could not evaluate energy produced value for ThingClassId" << inverterThing->thingClassId() << "The value will not be updated."; + return; + } + + double currentEnergyValue = inverterThing->stateValue(energyProducedStateTypeId).toDouble(); + if (currentEnergyValue <= 0) { + // Probably the initial value, no fancy data handling here + inverterThing->setStateValue(energyProducedStateTypeId, energyProduced); + } else { + double producedDiff = energyProduced - currentEnergyValue; + if (producedDiff > 10000 /*kWh*/) { + // The new energy value is way to high in order to be a reglar energy produced change... + qCWarning(dcSunSpec()) << "The energy produced value for" << inverterThing << "is way to high compared to the previouse value:" + << currentEnergyValue << "kWh. Ignoring the value:" << energyProduced << "kWh."; + return; + } else { + // Not a huge jump, just set the value + inverterThing->setStateValue(energyProducedStateTypeId, energyProduced); + } + } +} diff --git a/sunspec/integrationpluginsunspec.h b/sunspec/integrationpluginsunspec.h index f15b53c..c7a8b82 100644 --- a/sunspec/integrationpluginsunspec.h +++ b/sunspec/integrationpluginsunspec.h @@ -121,6 +121,9 @@ private slots: void onStorageBlockUpdated(); void onSolarEdgeBatteryBlockUpdated(); + void evaluateEnergyProducedValue(Thing *inverterThing, float energyProduced); + + }; #endif // INTEGRATIONPLUGINSUNSPEC_H