diff --git a/include/Configuration.h b/include/Configuration.h index 13319cadf..6ef29b8a5 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -138,9 +138,10 @@ struct POWERLIMITER_INVERTER_CONFIG_T { bool IsGoverned; bool IsBehindPowerMeter; bool IsSolarPowered; - bool UseOverscalingToCompensateShading; + bool UseOverscaling; uint16_t LowerPowerLimit; uint16_t UpperPowerLimit; + uint8_t ScalingThreshold; }; using PowerLimiterInverterConfig = struct POWERLIMITER_INVERTER_CONFIG_T; diff --git a/include/PowerLimiterSolarInverter.h b/include/PowerLimiterSolarInverter.h index 72023211c..392615b36 100644 --- a/include/PowerLimiterSolarInverter.h +++ b/include/PowerLimiterSolarInverter.h @@ -17,4 +17,5 @@ class PowerLimiterSolarInverter : public PowerLimiterInverter { private: uint16_t scaleLimit(uint16_t expectedOutputWatts); void setAcOutput(uint16_t expectedOutputWatts) final; + static char mpptName(MpptNum_t mppt); }; diff --git a/include/defaults.h b/include/defaults.h index e67638572..3e2c4b0a5 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -132,13 +132,14 @@ #define POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT false #define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true #define POWERLIMITER_IS_INVERTER_SOLAR_POWERED false -#define POWERLIMITER_USE_OVERSCALING_TO_COMPENSATE_SHADING false +#define POWERLIMITER_USE_OVERSCALING false #define POWERLIMITER_INVERTER_CHANNEL_ID 0 #define POWERLIMITER_TARGET_POWER_CONSUMPTION 0 #define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0 #define POWERLIMITER_LOWER_POWER_LIMIT 10 #define POWERLIMITER_BASE_LOAD_LIMIT 100 #define POWERLIMITER_UPPER_POWER_LIMIT 800 +#define POWERLIMITER_SCALING_THRESHOLD 98 #define POWERLIMITER_IGNORE_SOC true #define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80 #define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20 diff --git a/lib/Hoymiles/src/inverters/HMS_4CH.cpp b/lib/Hoymiles/src/inverters/HMS_4CH.cpp index 1616d013e..0d8d16702 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_4CH.cpp @@ -90,3 +90,10 @@ uint8_t HMS_4CH::getChannelMetaDataSize() const { return sizeof(channelMetaData) / sizeof(channelMetaData[0]); } + +bool HMS_4CH::supportsPowerDistributionLogic() +{ + // This feature was added in inverter firmware version 01.01.12 and + // will limit the AC output instead of limiting the DC inputs. + return DevInfo()->getFwBuildVersion() >= 10112U; +}; diff --git a/lib/Hoymiles/src/inverters/HMS_4CH.h b/lib/Hoymiles/src/inverters/HMS_4CH.h index cb7573390..fb2d1662f 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.h +++ b/lib/Hoymiles/src/inverters/HMS_4CH.h @@ -12,4 +12,5 @@ class HMS_4CH : public HMS_Abstract { uint8_t getByteAssignmentSize() const; const channelMetaData_t* getChannelMetaData() const; uint8_t getChannelMetaDataSize() const; + bool supportsPowerDistributionLogic() final; }; diff --git a/lib/Hoymiles/src/inverters/HM_Abstract.h b/lib/Hoymiles/src/inverters/HM_Abstract.h index 491149dc2..1088403ba 100644 --- a/lib/Hoymiles/src/inverters/HM_Abstract.h +++ b/lib/Hoymiles/src/inverters/HM_Abstract.h @@ -16,6 +16,7 @@ class HM_Abstract : public InverterAbstract { bool sendRestartControlRequest(); bool resendPowerControlRequest(); bool sendGridOnProFileParaRequest(); + bool supportsPowerDistributionLogic() override { return false; }; private: uint8_t _lastAlarmLogCnt = 0; @@ -23,4 +24,4 @@ class HM_Abstract : public InverterAbstract { PowerLimitControlType _activePowerControlType = PowerLimitControlType::AbsolutNonPersistent; uint8_t _powerState = 1; -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index db2ed2556..53822b498 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -120,6 +120,9 @@ class InverterAbstract { virtual bool sendChangeChannelRequest(); virtual bool sendGridOnProFileParaRequest() = 0; + // This feature will limit the AC output instead of limiting the DC inputs. + virtual bool supportsPowerDistributionLogic() = 0; + HoymilesRadio* getRadio(); AlarmLogParser* EventLog(); diff --git a/src/Configuration.cpp b/src/Configuration.cpp index e5bb77b9f..fd7b81eea 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -159,9 +159,10 @@ void ConfigurationClass::serializePowerLimiterConfig(PowerLimiterConfig const& s t["is_governed"] = s.IsGoverned; t["is_behind_power_meter"] = s.IsBehindPowerMeter; t["is_solar_powered"] = s.IsSolarPowered; - t["use_overscaling_to_compensate_shading"] = s.UseOverscalingToCompensateShading; + t["use_overscaling_to_compensate_shading"] = s.UseOverscaling; t["lower_power_limit"] = s.LowerPowerLimit; t["upper_power_limit"] = s.UpperPowerLimit; + t["scaling_threshold"] = s.ScalingThreshold; } } @@ -484,9 +485,10 @@ void ConfigurationClass::deserializePowerLimiterConfig(JsonObject const& source, inv.IsGoverned = s["is_governed"] | false; inv.IsBehindPowerMeter = s["is_behind_power_meter"] | POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER; inv.IsSolarPowered = s["is_solar_powered"] | POWERLIMITER_IS_INVERTER_SOLAR_POWERED; - inv.UseOverscalingToCompensateShading = s["use_overscaling_to_compensate_shading"] | POWERLIMITER_USE_OVERSCALING_TO_COMPENSATE_SHADING; + inv.UseOverscaling = s["use_overscaling_to_compensate_shading"] | POWERLIMITER_USE_OVERSCALING; inv.LowerPowerLimit = s["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT; inv.UpperPowerLimit = s["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT; + inv.ScalingThreshold = s["scaling_threshold"] | POWERLIMITER_SCALING_THRESHOLD; } } @@ -916,7 +918,7 @@ void ConfigurationClass::migrateOnBattery() inv.IsGoverned = true; inv.IsBehindPowerMeter = powerlimiter["is_inverter_behind_powermeter"] | POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER; inv.IsSolarPowered = powerlimiter["is_inverter_solar_powered"] | POWERLIMITER_IS_INVERTER_SOLAR_POWERED; - inv.UseOverscalingToCompensateShading = powerlimiter["use_overscaling_to_compensate_shading"] | POWERLIMITER_USE_OVERSCALING_TO_COMPENSATE_SHADING; + inv.UseOverscaling = powerlimiter["use_overscaling_to_compensate_shading"] | POWERLIMITER_USE_OVERSCALING; inv.LowerPowerLimit = powerlimiter["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT; inv.UpperPowerLimit = powerlimiter["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT; diff --git a/src/PowerLimiterSolarInverter.cpp b/src/PowerLimiterSolarInverter.cpp index 1acaa62e7..32584e995 100644 --- a/src/PowerLimiterSolarInverter.cpp +++ b/src/PowerLimiterSolarInverter.cpp @@ -117,6 +117,14 @@ uint16_t PowerLimiterSolarInverter::standby() uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) { + // overscalling allows us to compensate for shaded panels by increasing the + // total power limit, if the inverter is solar powered. + // this feature should not be used when homyiles 'Power Distribution Logic' is available + // as the inverter will take care of the power distribution across the MPPTs itself. + // (added in inverter firmware 01.01.12 on supported models (HMS-1600/1800/2000)) + // When disabled we return the expected output. + if (!_config.UseOverscaling || _spInverter->supportsPowerDistributionLogic()) { return expectedOutputWatts; } + // prevent scaling if inverter is not producing, as input channels are not // producing energy and hence are detected as not-producing, causing // unreasonable scaling. @@ -136,108 +144,81 @@ uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) // producing very little due to the very low limit. if (getCurrentLimitWatts() < dcTotalChnls * 10) { return expectedOutputWatts; } - // overscalling allows us to compensate for shaded panels by increasing the - // total power limit, if the inverter is solar powered. - if (_config.UseOverscalingToCompensateShading) { - auto inverterOutputAC = pStats->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC); + auto inverterOutputAC = pStats->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC); - float inverterEfficiencyFactor = pStats->getChannelFieldValue(TYPE_INV, CH0, FLD_EFF); + float inverterEfficiencyFactor = pStats->getChannelFieldValue(TYPE_INV, CH0, FLD_EFF); - // fall back to hoymiles peak efficiency as per datasheet if inverter - // is currently not producing (efficiency is zero in that case) - inverterEfficiencyFactor = (inverterEfficiencyFactor > 0) ? inverterEfficiencyFactor/100 : 0.967; + // fall back to hoymiles peak efficiency as per datasheet if inverter + // is currently not producing (efficiency is zero in that case) + inverterEfficiencyFactor = (inverterEfficiencyFactor > 0) ? inverterEfficiencyFactor/100 : 0.967; - // 98% of the expected power is good enough - auto expectedAcPowerPerMppt = (getCurrentLimitWatts() / dcTotalMppts) * 0.98; + auto scalingThreshold = static_cast(_config.ScalingThreshold) / 100.0; + auto expectedAcPowerPerMppt = (getCurrentLimitWatts() / dcTotalMppts) * scalingThreshold; - if (_verboseLogging) { - MessageOutput.printf("%s expected AC power per MPPT %.0f W\r\n", - _logPrefix, expectedAcPowerPerMppt); - } - - size_t dcShadedMppts = 0; - auto shadedChannelACPowerSum = 0.0; - - for (auto& m : dcMppts) { - float mpptPowerAC = 0.0; - std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); + if (_verboseLogging) { + MessageOutput.printf("%s expected AC power per MPPT %.0f W\r\n", + _logPrefix, expectedAcPowerPerMppt); + } - for (auto& c : mpptChnls) { - mpptPowerAC += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) * inverterEfficiencyFactor; - } + size_t dcShadedMppts = 0; + auto shadedChannelACPowerSum = 0.0; - if (mpptPowerAC < expectedAcPowerPerMppt) { - dcShadedMppts++; - shadedChannelACPowerSum += mpptPowerAC; - } + for (auto& m : dcMppts) { + float mpptPowerAC = 0.0; + std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); - if (_verboseLogging) { - MessageOutput.printf("%s MPPT-%c AC power %.0f W\r\n", - _logPrefix, m + 'a', mpptPowerAC); - } + for (auto& c : mpptChnls) { + mpptPowerAC += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) * inverterEfficiencyFactor; } - // no shading or the shaded channels provide more power than what - // we currently need. - if (dcShadedMppts == 0 || shadedChannelACPowerSum >= expectedOutputWatts) { - return expectedOutputWatts; + if (mpptPowerAC < expectedAcPowerPerMppt) { + dcShadedMppts++; + shadedChannelACPowerSum += mpptPowerAC; } - if (dcShadedMppts == dcTotalMppts) { - // keep the currentLimit when: - // - all channels are shaded - // - currentLimit >= expectedOutputWatts - // - we get the expected AC power or less and - if (getCurrentLimitWatts() >= expectedOutputWatts && - inverterOutputAC <= expectedOutputWatts) { - if (_verboseLogging) { - MessageOutput.printf("%s all mppts are shaded, " - "keeping the current limit of %d W\r\n", - _logPrefix, getCurrentLimitWatts()); - } - - return getCurrentLimitWatts(); - - } else { - return expectedOutputWatts; - } - } - - size_t dcNonShadedMppts = dcTotalMppts - dcShadedMppts; - uint16_t overScaledLimit = (expectedOutputWatts - shadedChannelACPowerSum) / dcNonShadedMppts * dcTotalMppts; - - if (overScaledLimit <= expectedOutputWatts) { return expectedOutputWatts; } - if (_verboseLogging) { - MessageOutput.printf("%s %d/%d mppts are shaded, scaling %d W\r\n", - _logPrefix, dcShadedMppts, dcTotalMppts, overScaledLimit); + MessageOutput.printf("%s MPPT-%c AC power %.0f W\r\n", + _logPrefix, mpptName(m), mpptPowerAC); } + } - return overScaledLimit; + // no shading or the shaded channels provide more power than what + // we currently need. + if (dcShadedMppts == 0 || shadedChannelACPowerSum >= expectedOutputWatts) { + return expectedOutputWatts; } - size_t dcProdMppts = 0; - for (auto& m : dcMppts) { - float dcPowerMppt = 0.0; - std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); + if (dcShadedMppts == dcTotalMppts) { + // keep the currentLimit when: + // - all channels are shaded + // - currentLimit >= expectedOutputWatts + // - we get the expected AC power or less and + if (getCurrentLimitWatts() >= expectedOutputWatts && + inverterOutputAC <= expectedOutputWatts) { + if (_verboseLogging) { + MessageOutput.printf("%s all mppts are shaded, " + "keeping the current limit of %d W\r\n", + _logPrefix, getCurrentLimitWatts()); + } - for (auto& c : mpptChnls) { - dcPowerMppt += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC); - } + return getCurrentLimitWatts(); - if (dcPowerMppt > 2.0 * mpptChnls.size()) { - dcProdMppts++; + } else { + return expectedOutputWatts; } } - if (dcProdMppts == 0 || dcProdMppts == dcTotalMppts) { return expectedOutputWatts; } + size_t dcNonShadedMppts = dcTotalMppts - dcShadedMppts; + uint16_t overScaledLimit = (expectedOutputWatts - shadedChannelACPowerSum) / dcNonShadedMppts * dcTotalMppts; - uint16_t scaled = expectedOutputWatts / dcProdMppts * dcTotalMppts; - MessageOutput.printf("%s %d/%d mppts are producing, scaling from %d to " - "%d W\r\n", _logPrefix, dcProdMppts, dcTotalMppts, - expectedOutputWatts, scaled); + if (overScaledLimit <= expectedOutputWatts) { return expectedOutputWatts; } - return scaled; + if (_verboseLogging) { + MessageOutput.printf("%s %d/%d mppts are not-producing/shaded, scaling %d W\r\n", + _logPrefix, dcShadedMppts, dcTotalMppts, overScaledLimit); + } + + return overScaledLimit; } void PowerLimiterSolarInverter::setAcOutput(uint16_t expectedOutputWatts) @@ -246,3 +227,23 @@ void PowerLimiterSolarInverter::setAcOutput(uint16_t expectedOutputWatts) setTargetPowerLimitWatts(scaleLimit(expectedOutputWatts)); setTargetPowerState(true); } + +char PowerLimiterSolarInverter::mpptName(MpptNum_t mppt) +{ + switch (mppt) { + case MpptNum_t::MPPT_A: + return 'a'; + + case MpptNum_t::MPPT_B: + return 'b'; + + case MpptNum_t::MPPT_C: + return 'c'; + + case MpptNum_t::MPPT_D: + return 'd'; + + default: + return '?'; + } +} diff --git a/src/WebApi_devinfo.cpp b/src/WebApi_devinfo.cpp index 449cd1772..45078ffef 100644 --- a/src/WebApi_devinfo.cpp +++ b/src/WebApi_devinfo.cpp @@ -35,6 +35,7 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request) root["hw_model_name"] = inv->DevInfo()->getHwModelName(); root["max_power"] = inv->DevInfo()->getMaxPower(); root["fw_build_datetime"] = inv->DevInfo()->getFwBuildDateTimeStr(); + root["pdl_supported"] = inv->supportsPowerDistributionLogic(); } WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); diff --git a/src/WebApi_powerlimiter.cpp b/src/WebApi_powerlimiter.cpp index 8301c738b..196bc5be0 100644 --- a/src/WebApi_powerlimiter.cpp +++ b/src/WebApi_powerlimiter.cpp @@ -70,6 +70,7 @@ void WebApiPowerLimiterClass::onMetaData(AsyncWebServerRequest* request) obj["type"] = inv->typeName(); auto channels = inv->Statistics()->getChannelsByType(TYPE_DC); obj["channels"] = channels.size(); + obj["pdl_supported"] = inv->supportsPowerDistributionLogic(); } WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); diff --git a/webapp/src/components/DevInfo.vue b/webapp/src/components/DevInfo.vue index 7a998e1c0..a56f8ce64 100644 --- a/webapp/src/components/DevInfo.vue +++ b/webapp/src/components/DevInfo.vue @@ -46,6 +46,11 @@ {{ $t('devinfo.HardwareVersion') }} {{ devInfoList.hw_version }} + + {{ $t('devinfo.SupportsPowerDistributionLogic') }} + {{ $t('devinfo.yes') }} + {{ $t('devinfo.no') }} + diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 95b8f667d..eeedb183d 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -233,7 +233,10 @@ "FirmwareVersion": "Firmware-Version", "FirmwareBuildDate": "Firmware-Erstellungsdatum", "HardwarePartNumber": "Hardware-Teilenummer", - "HardwareVersion": "Hardware-Version" + "HardwareVersion": "Hardware-Version", + "SupportsPowerDistributionLogic": "'Power Distribution Logic' unterstützt", + "yes": "@:base.Yes", + "no": "@:base.No" }, "gridprofile": { "NoInfo": "@:devinfo.NoInfo", @@ -708,10 +711,12 @@ "VoltageLoadCorrectionFactor": "Lastkorrekturfaktor", "BatterySocInfo": "Hinweis: Die Batterie State of Charge (SoC) Schwellwerte werden bevorzugt herangezogen. Sie werden allerdings nur benutzt, wenn die Batterie-Kommunikationsschnittstelle innerhalb der letzten Minute gültige Werte verarbeitet hat. Andernfalls werden ersatzweise die Spannungs-Schwellwerte verwendet.", "InverterIsBehindPowerMeter": "Stromzählermessung beinhaltet Wechselrichter", + "ScalingPowerThreshold": "Schwellenwert für Überskalierung", + "ScalingPowerThresholdHint": "Minimale Eingangsleistungsschwelle (%). Eingänge unterhalb dieses Prozentsatzes werden als verschattet/ungenutzt bewertet.", "InverterIsBehindPowerMeterHint": "Aktivieren falls sich der Stromzähler-Messwert um die Ausgangsleistung des Wechselrichters verringert, wenn dieser Strom produziert. Normalerweise ist das zutreffend.", "InverterIsSolarPowered": "Wechselrichter wird von Solarmodulen gespeist", - "UseOverscalingToCompensateShading": "Verschattung durch Überskalierung ausgleichen", - "UseOverscalingToCompensateShadingHint": "Erlaubt das Überskalieren des Wechselrichter-Limits, um Verschattung eines oder mehrerer Eingänge auszugleichen.", + "UseOverscaling": "Verschattetet/Ungenutzte Eingänge ausgleichen", + "UseOverscalingHint": "Erlaubt das Überskalieren des Wechselrichter-Limits, um ungenutzte Eingänge oder Verschattung eines oder mehrerer Eingänge auszugleichen.", "VoltageThresholds": "Batterie Spannungs-Schwellwerte ", "VoltageLoadCorrectionInfo": "Hinweis: Wenn Leistung von der Batterie abgegeben wird, bricht ihre Spannung etwas ein. Der Spannungseinbruch skaliert mit dem Entladestrom. Damit Wechselrichter nicht vorzeitig ausgeschaltet werden sobald der Stop-Schwellenwert unterschritten wurde, wird der hier angegebene Korrekturfaktor mit einberechnet, um die Spannung zu errechnen, die der Akku in Ruhe hätte. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) * Korrekturfaktor).", "InverterRestartHour": "Uhrzeit für automatischen Wechselrichterneustart", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index f8dc51000..1020aaa0a 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -233,7 +233,10 @@ "FirmwareVersion": "Firmware Version", "FirmwareBuildDate": "Firmware Build Date", "HardwarePartNumber": "Hardware Part Number", - "HardwareVersion": "Hardware Version" + "HardwareVersion": "Hardware Version", + "SupportsPowerDistributionLogic": "'Power Distribution Logic' supported", + "yes": "@:base.Yes", + "no": "@:base.No" }, "gridprofile": { "NoInfo": "@:devinfo.NoInfo", @@ -710,10 +713,12 @@ "VoltageLoadCorrectionFactor": "Load correction factor", "BatterySocInfo": "Hint: The use of battery State of Charge (SoC) thresholds is prioritized. However, SoC thresholds are only used if the battery communication interface has processed valid SoC values in the last minute. Otherwise, the voltage thresholds will be used as fallback.", "InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output", + "ScalingPowerThreshold": "Overscaling input power threshold", + "ScalingPowerThresholdHint": "Set the minimum power input threshold (%). Inputs below this percentage are considered shaded/unused.", "InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.", "InverterIsSolarPowered": "Inverter is powered by solar modules", - "UseOverscalingToCompensateShading": "Compensate for shading", - "UseOverscalingToCompensateShadingHint": "Allow to overscale the inverter limit to compensate for shading of one or multiple inputs.", + "UseOverscaling": "Compensate shaded or unused inputs", + "UseOverscalingHint": "Allow to overscale the inverter limit to compensate for unused inputs or shading of one or multiple inputs.", "VoltageThresholds": "Battery Voltage Thresholds", "VoltageLoadCorrectionInfo": "Hint: When the battery is discharged, its voltage drops. The voltage drop scales with the discharge current. In order to not stop inverters too early (stop threshold), this load correction factor can be specified to calculate the battery voltage if it was idle. Corrected voltage = DC Voltage + (Current power * correction factor).", "InverterRestartHour": "Automatic Inverter Restart Time", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 67b497d6f..8bf5bdae0 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -268,7 +268,10 @@ "FirmwareVersion": "Version du firmware", "FirmwareBuildDate": "Date de création du firmware", "HardwarePartNumber": "Numéro d'article matériel", - "HardwareVersion": "Version du matériel" + "HardwareVersion": "Version du matériel", + "SupportsPowerDistributionLogic": "'Power Distribution Logic' supported", + "yes": "@:base.Yes", + "no": "@:base.No" }, "gridprofile": { "NoInfo": "@:devinfo.NoInfo", @@ -776,10 +779,12 @@ "VoltageLoadCorrectionFactor": "Load correction factor", "BatterySocInfo": "Hint: The use of battery State of Charge (SoC) thresholds is prioritized. However, SoC thresholds are only used if the battery communication interface has processed valid SoC values in the last minute. Otherwise, the voltage thresholds will be used as fallback.", "InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output", + "ScalingPowerThreshold": "Overscaling input power threshold", + "ScalingPowerThresholdHint": "Set the minimum power input threshold (%). Inputs below this percentage are considered shaded/unused.", "InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.", "InverterIsSolarPowered": "Inverter is powered by solar modules", - "UseOverscalingToCompensateShading": "Compensate for shading", - "UseOverscalingToCompensateShadingHint": "Allow to overscale the inverter limit to compensate for shading of one or multiple inputs.", + "UseOverscaling": "Compensate shaded or unused inputs", + "UseOverscalingHint": "Allow to overscale the inverter limit to compensate for unused inputs or shading of one or multiple inputs.", "VoltageThresholds": "Battery Voltage Thresholds", "VoltageLoadCorrectionInfo": "Hint: When the battery is discharged, its voltage drops. The voltage drop scales with the discharge current. In order to not stop inverters too early (stop threshold), this load correction factor can be specified to calculate the battery voltage if it was idle. Corrected voltage = DC Voltage + (Current power * correction factor).", "InverterRestartHour": "Automatic Inverter Restart Time", diff --git a/webapp/src/types/DevInfoStatus.ts b/webapp/src/types/DevInfoStatus.ts index 7c37a5673..452524c9c 100644 --- a/webapp/src/types/DevInfoStatus.ts +++ b/webapp/src/types/DevInfoStatus.ts @@ -8,4 +8,5 @@ export interface DevInfoStatus { hw_version: number; hw_model_name: string; max_power: number; + pdl_supported: boolean; } diff --git a/webapp/src/types/PowerLimiterConfig.ts b/webapp/src/types/PowerLimiterConfig.ts index 99d5d67f9..253b001be 100644 --- a/webapp/src/types/PowerLimiterConfig.ts +++ b/webapp/src/types/PowerLimiterConfig.ts @@ -10,6 +10,7 @@ export interface PowerLimiterInverterInfo { max_power: number; type: string; channels: number; + pdl_supported: boolean; } // meta-data not directly part of the DPL settings, @@ -29,6 +30,7 @@ export interface PowerLimiterInverterConfig { use_overscaling_to_compensate_shading: boolean; lower_power_limit: number; upper_power_limit: number; + scaling_threshold: number; } export interface PowerLimiterConfig { diff --git a/webapp/src/views/PowerLimiterAdminView.vue b/webapp/src/views/PowerLimiterAdminView.vue index 1d404640e..c89a0bf56 100644 --- a/webapp/src/views/PowerLimiterAdminView.vue +++ b/webapp/src/views/PowerLimiterAdminView.vue @@ -129,14 +129,29 @@ /> + +