From ce7af2adc5aadab68049036fb9f0b4a22e439dfb Mon Sep 17 00:00:00 2001 From: hhoebel Date: Thu, 20 Feb 2025 12:13:59 +0100 Subject: [PATCH 1/8] Added new feature: use battery-charge-current-limit --- include/Configuration.h | 6 ++ include/battery/Controller.h | 1 + include/defaults.h | 8 ++ include/solarcharger/Controller.h | 1 + include/solarcharger/Provider.h | 1 + include/solarcharger/mqtt/Provider.h | 1 + include/solarcharger/victron/Provider.h | 3 + lib/VeDirectFrameHandler/VeDirectData.h | 2 + .../VeDirectMpptController.cpp | 29 ++++++- .../VeDirectMpptController.h | 19 ++-- src/Configuration.cpp | 12 +++ src/battery/Controller.cpp | 69 +++++++++++++++ src/solarcharger/Controller.cpp | 7 ++ src/solarcharger/victron/Provider.cpp | 35 ++++++++ src/solarcharger/victron/Stats.cpp | 6 +- webapp/src/locales/de.json | 14 ++- webapp/src/locales/en.json | 14 ++- webapp/src/types/BatteryConfig.ts | 6 ++ webapp/src/views/BatteryAdminView.vue | 87 +++++++++++++++++++ 19 files changed, 311 insertions(+), 10 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index fe9c7d7ef..4e6df2316 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -241,6 +241,12 @@ struct BATTERY_CONFIG_T { float DischargeCurrentLimitBelowSoc; float DischargeCurrentLimitBelowVoltage; bool UseBatteryReportedDischargeCurrentLimit; + bool EnableChargeCurrentLimit; + float MaxChargeCurrentLimit; + float MinChargeCurrentLimit; + float ChargeCurrentLimitBelowSoc; + float ChargeCurrentLimitBelowVoltage; + bool UseBatteryReportedChargeCurrentLimit; }; using BatteryConfig = struct BATTERY_CONFIG_T; diff --git a/include/battery/Controller.h b/include/battery/Controller.h index c80b30312..74b3d88e9 100644 --- a/include/battery/Controller.h +++ b/include/battery/Controller.h @@ -15,6 +15,7 @@ class Controller { void updateSettings(); float getDischargeCurrentLimit(); + float getChargeCurrentLimit(); std::shared_ptr getStats() const; diff --git a/include/defaults.h b/include/defaults.h index 85bf5c30d..32e87e61f 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -158,6 +158,14 @@ #define BATTERY_DISCHARGE_CURRENT_LIMIT_BELOW_SOC 100.0 #define BATTERY_DISCHARGE_CURRENT_LIMIT_BELOW_VOLTAGE 60.0 #define BATTERY_USE_BATTERY_REPORTED_DISCHARGE_CURRENT_LIMIT false + +#define BATTERY_ENABLE_CHARGE_CURRENT_LIMIT false +#define BATTERY_CHARGE_CURRENT_LIMIT_MIN 0.0 +#define BATTERY_CHARGE_CURRENT_LIMIT_MAX 0.0 +#define BATTERY_CHARGE_CURRENT_LIMIT_BELOW_SOC 100.0 +#define BATTERY_CHARGE_CURRENT_LIMIT_BELOW_VOLTAGE 60.0 +#define BATTERY_USE_BATTERY_REPORTED_CHARGE_CURRENT_LIMIT false + #define BATTERY_ZENDURE_POLLING_INTERVAL 15 #define BATTERY_ZENDURE_DEVICE 0 #define BATTERY_ZENDURE_MIN_SOC 7 diff --git a/include/solarcharger/Controller.h b/include/solarcharger/Controller.h index dc3f37e9a..3216eed23 100644 --- a/include/solarcharger/Controller.h +++ b/include/solarcharger/Controller.h @@ -23,6 +23,7 @@ class Controller { mutable std::mutex _mutex; std::unique_ptr _upProvider = nullptr; bool _forcePublishSensors = false; + float _actChargeLimit = 0.0f; }; } // namespace SolarChargers diff --git a/include/solarcharger/Provider.h b/include/solarcharger/Provider.h index 8c8af735b..8c6d58b15 100644 --- a/include/solarcharger/Provider.h +++ b/include/solarcharger/Provider.h @@ -12,6 +12,7 @@ class Provider { virtual bool init() = 0; virtual void deinit() = 0; virtual void loop() = 0; + virtual void setChargeLimit(float limit, float act_charge_current) = 0; virtual std::shared_ptr getStats() const = 0; }; diff --git a/include/solarcharger/mqtt/Provider.h b/include/solarcharger/mqtt/Provider.h index 760256ae9..4e243c752 100644 --- a/include/solarcharger/mqtt/Provider.h +++ b/include/solarcharger/mqtt/Provider.h @@ -19,6 +19,7 @@ class Provider : public ::SolarChargers::Provider { bool init() final; void deinit() final; void loop() final { return; } // this class is event-driven + void setChargeLimit(float limit, float act_charge_current) final { return; } std::shared_ptr<::SolarChargers::Stats> getStats() const final { return _stats; } private: diff --git a/include/solarcharger/victron/Provider.h b/include/solarcharger/victron/Provider.h index b1a71ba1c..417da5228 100644 --- a/include/solarcharger/victron/Provider.h +++ b/include/solarcharger/victron/Provider.h @@ -18,6 +18,7 @@ class Provider : public ::SolarChargers::Provider { bool init() final; void deinit() final; void loop() final; + void setChargeLimit( float limit, float act_charge_current) final; std::shared_ptr<::SolarChargers::Stats> getStats() const final { return _stats; } private: @@ -31,6 +32,8 @@ class Provider : public ::SolarChargers::Provider { std::vector _controllers; std::vector _serialPortOwners; std::shared_ptr _stats = std::make_shared(); + float _chargeLimit { 0.0f }; + float _chargeCurrent { 0.0f }; bool initController(gpio_num_t rx, gpio_num_t tx, uint8_t instance); }; diff --git a/lib/VeDirectFrameHandler/VeDirectData.h b/lib/VeDirectFrameHandler/VeDirectData.h index 4cda91274..5ce4c9ab1 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.h +++ b/lib/VeDirectFrameHandler/VeDirectData.h @@ -56,6 +56,7 @@ struct veMpptStruct : veStruct { std::pair NetworkTotalDcInputPowerMilliWatts; std::pair BatteryAbsorptionMilliVolt; std::pair BatteryFloatMilliVolt; + std::pair ChargeCurrentLimit; std::pair NetworkInfo; std::pair NetworkMode; std::pair NetworkStatus; @@ -146,6 +147,7 @@ enum class VeDirectHexRegister : uint16_t { BatteryAbsorptionVoltage = 0xEDF7, BatteryFloatVoltage = 0xEDF6, TotalChargeCurrent = 0x2013, + ChargeCurrentLimit = 0x2015, ChargeStateElapsedTime= 0x2007, BatteryVoltageSense = 0x2002, LoadCurrent = 0xEDAD, diff --git a/lib/VeDirectFrameHandler/VeDirectMpptController.cpp b/lib/VeDirectFrameHandler/VeDirectMpptController.cpp index 17465e340..1859d8e4b 100644 --- a/lib/VeDirectFrameHandler/VeDirectMpptController.cpp +++ b/lib/VeDirectFrameHandler/VeDirectMpptController.cpp @@ -117,6 +117,15 @@ void VeDirectMpptController::frameValidEvent() { } } +void VeDirectMpptController::setChargeLimit( float limit ) +{ + // Victron MPPT needs limit with a resolution of 0.1A + if (limit == __FLT_MAX__) { + _chargeLimit = 0xFFFF; + } else { + _chargeLimit = static_cast( limit * 10.0f ); + } +} void VeDirectMpptController::loop() { @@ -147,6 +156,7 @@ void VeDirectMpptController::loop() resetTimestamp(_tmpFrame.NetworkTotalDcInputPowerMilliWatts); resetTimestamp(_tmpFrame.BatteryFloatMilliVolt); resetTimestamp(_tmpFrame.BatteryAbsorptionMilliVolt); + resetTimestamp(_tmpFrame.ChargeCurrentLimit); #ifdef PROCESS_NETWORK_STATE resetTimestamp(_tmpFrame.NetworkInfo); @@ -234,6 +244,16 @@ bool VeDirectMpptController::hexDataHandler(VeDirectHexData const &data) { return true; break; + case VeDirectHexRegister::ChargeCurrentLimit: + _tmpFrame.ChargeCurrentLimit = + { millis(), static_cast(data.value) }; + + ESP_LOGD(TAG, "%s Hex Data: Charge Current Limit (0x%04X): %.1fA", + _logId, regLog, + static_cast(_tmpFrame.ChargeCurrentLimit.second) / 10.0); + return true; + break; + #ifdef PROCESS_NETWORK_STATE case VeDirectHexRegister::NetworkInfo: _tmpFrame.NetworkInfo = @@ -316,7 +336,14 @@ void VeDirectMpptController::sendNextHexCommandFromQueue(void) { (!prio && (_hexQueue[idx]._readPeriod != HIGH_PRIO_COMMAND))) && (millisTime - _hexQueue[idx]._lastSendTime) > (_hexQueue[idx]._readPeriod * 1000)) { - sendHexCommand(VeDirectHexCommand::GET, _hexQueue[idx]._hexRegister); + if (_hexQueue[idx]._setCommand) + { + sendHexCommand(VeDirectHexCommand::SET, _hexQueue[idx]._hexRegister, _hexQueue[idx]._data, _hexQueue[idx]._dataLength); + } + else + { + sendHexCommand(VeDirectHexCommand::GET, _hexQueue[idx]._hexRegister); + } _hexQueue[idx]._lastSendTime = millisTime; // we need this information to check if we get an answer, see hexDataHandler() diff --git a/lib/VeDirectFrameHandler/VeDirectMpptController.h b/lib/VeDirectFrameHandler/VeDirectMpptController.h index 6c3013563..2af41cddd 100644 --- a/lib/VeDirectFrameHandler/VeDirectMpptController.h +++ b/lib/VeDirectFrameHandler/VeDirectMpptController.h @@ -38,8 +38,11 @@ class MovingAverage { struct VeDirectHexQueue { VeDirectHexRegister _hexRegister; // hex register + bool _setCommand; // true if the command is a SET-command, false if GET uint8_t _readPeriod; // time period in sec until we send the command again uint32_t _lastSendTime; // time stamp in milli sec of last send + uint32_t& _data; // data to send (only at SET-command) + uint8_t _dataLength; // length of data (only at SET-command -> 8/16/32) }; class VeDirectMpptController : public VeDirectFrameHandler { @@ -49,7 +52,7 @@ class VeDirectMpptController : public VeDirectFrameHandler { void init(gpio_num_t rx, gpio_num_t tx, uint8_t hwSerialPort); using data_t = veMpptStruct; - + void setChargeLimit( float value ); void loop() final; private: @@ -62,12 +65,16 @@ class VeDirectMpptController : public VeDirectFrameHandler { uint32_t _sendTimeout = 0; // timeout until we send the next command from the queue size_t _sendQueueNr = 0; // actual queue position; + uint32_t _chargeLimit = 0xFFFF; // limit MPPT to this limit (in 0.1A), default: 0xFFFF (=full current) + uint32_t _no_data = 0; // dummy-value // for slow changing values we use a send time period of 4 sec #define HIGH_PRIO_COMMAND 1 - std::array _hexQueue { VeDirectHexRegister::NetworkTotalDcInputPower, HIGH_PRIO_COMMAND, 0, - VeDirectHexRegister::ChargeControllerTemperature, 4, 0, - VeDirectHexRegister::SmartBatterySenseTemperature, 4, 0, - VeDirectHexRegister::BatteryFloatVoltage, 4, 0, - VeDirectHexRegister::BatteryAbsorptionVoltage, 4, 0 }; + std::array _hexQueue { VeDirectHexRegister::NetworkTotalDcInputPower, false, HIGH_PRIO_COMMAND, 0, _no_data, 0, + VeDirectHexRegister::ChargeControllerTemperature, false, 4, 0, _no_data, 0, + VeDirectHexRegister::SmartBatterySenseTemperature, false, 4, 0, _no_data, 0, + VeDirectHexRegister::BatteryFloatVoltage, false, 4, 0, _no_data, 0, + VeDirectHexRegister::BatteryAbsorptionVoltage, false, 4, 0, _no_data, 0, + VeDirectHexRegister::ChargeCurrentLimit, false, 4, 0, _no_data, 0, + VeDirectHexRegister::ChargeCurrentLimit, true, 10, 0, _chargeLimit, 16}; }; diff --git a/src/Configuration.cpp b/src/Configuration.cpp index d1fb639fe..7aef27e8f 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -135,6 +135,12 @@ void ConfigurationClass::serializeBatteryConfig(BatteryConfig const& source, Jso target["discharge_current_limit_below_soc"] = config.Battery.DischargeCurrentLimitBelowSoc; target["discharge_current_limit_below_voltage"] = config.Battery.DischargeCurrentLimitBelowVoltage; target["use_battery_reported_discharge_current_limit"] = config.Battery.UseBatteryReportedDischargeCurrentLimit; + target["enable_charge_current_limit"] = config.Battery.EnableChargeCurrentLimit; + target["max_charge_current_limit"] = config.Battery.MaxChargeCurrentLimit; + target["min_charge_current_limit"] = config.Battery.MinChargeCurrentLimit; + target["charge_current_limit_below_soc"] = config.Battery.ChargeCurrentLimitBelowSoc; + target["charge_current_limit_below_voltage"] = config.Battery.ChargeCurrentLimitBelowVoltage; + target["use_battery_reported_charge_current_limit"] = config.Battery.UseBatteryReportedChargeCurrentLimit; } void ConfigurationClass::serializeBatteryZendureConfig(BatteryZendureConfig const& source, JsonObject& target) @@ -559,6 +565,12 @@ void ConfigurationClass::deserializeBatteryConfig(JsonObject const& source, Batt target.DischargeCurrentLimitBelowSoc = source["discharge_current_limit_below_soc"] | BATTERY_DISCHARGE_CURRENT_LIMIT_BELOW_SOC; target.DischargeCurrentLimitBelowVoltage = source["discharge_current_limit_below_voltage"] | BATTERY_DISCHARGE_CURRENT_LIMIT_BELOW_VOLTAGE; target.UseBatteryReportedDischargeCurrentLimit = source["use_battery_reported_discharge_current_limit"] | BATTERY_USE_BATTERY_REPORTED_DISCHARGE_CURRENT_LIMIT; + target.EnableChargeCurrentLimit = source["enable_charge_current_limit"] | BATTERY_ENABLE_CHARGE_CURRENT_LIMIT; + target.MaxChargeCurrentLimit = source["max_charge_current_limit"] | BATTERY_CHARGE_CURRENT_LIMIT_MAX; + target.MinChargeCurrentLimit = source["min_charge_current_limit"] | BATTERY_CHARGE_CURRENT_LIMIT_MIN; + target.ChargeCurrentLimitBelowSoc = source["charge_current_limit_below_soc"] | BATTERY_CHARGE_CURRENT_LIMIT_BELOW_SOC; + target.ChargeCurrentLimitBelowVoltage = source["charge_current_limit_below_voltage"] | BATTERY_CHARGE_CURRENT_LIMIT_BELOW_VOLTAGE; + target.UseBatteryReportedChargeCurrentLimit = source["use_battery_reported_charge_current_limit"] | BATTERY_USE_BATTERY_REPORTED_CHARGE_CURRENT_LIMIT; } void ConfigurationClass::deserializeBatteryZendureConfig(JsonObject const& source, BatteryZendureConfig& target) diff --git a/src/battery/Controller.cpp b/src/battery/Controller.cpp index 879de52eb..9bc7078b7 100644 --- a/src/battery/Controller.cpp +++ b/src/battery/Controller.cpp @@ -152,4 +152,73 @@ float Controller::getDischargeCurrentLimit() return std::min(getConfiguredLimit(), getBatteryLimit()); } +float Controller::getChargeCurrentLimit() +{ + auto const& config = Configuration.get(); + + if (!config.Battery.EnableChargeCurrentLimit) { return FLT_MAX; } + + auto maxChargeCurrentLimit = config.Battery.MaxChargeCurrentLimit; + auto maxChargeCurrentLimitValid = maxChargeCurrentLimit > 0.0f; + auto minChargeCurrentLimit = config.Battery.MinChargeCurrentLimit; + auto minChargeCurrentLimitValid = minChargeCurrentLimit > 0.0f; + auto chargeCurrentLimitBelowSoc = config.Battery.ChargeCurrentLimitBelowSoc; + auto chargeCurrentLimitBelowVoltage = config.Battery.ChargeCurrentLimitBelowVoltage; + auto statsSoCValid = getStats()->getSoCAgeSeconds() <= 60 && !config.PowerLimiter.IgnoreSoc; + auto statsSoC = statsSoCValid ? getStats()->getSoC() : 100.0; // fail open so we use voltage instead + auto statsVoltageValid = getStats()->getVoltageAgeSeconds() <= 60; + auto statsVoltage = statsVoltageValid ? getStats()->getVoltage() : 0.0; // fail closed + auto statsCurrentLimit = getStats()->getChargeCurrentLimit(); + auto statsLimitValid = config.Battery.UseBatteryReportedChargeCurrentLimit + && statsCurrentLimit >= 0.0f + && getStats()->getChargeCurrentLimitAgeSeconds() <= 60; + + if (statsSoCValid) { + if (statsSoC > chargeCurrentLimitBelowSoc) { + // Above SoC and Voltage thresholds, ignore custom limit. + // Battery-provided limit will still be applied. + maxChargeCurrentLimitValid = false; + } + } else { + if (statsVoltage > chargeCurrentLimitBelowVoltage) { + // Above SoC and Voltage thresholds, ignore custom limit. + // Battery-provided limit will still be applied. + maxChargeCurrentLimitValid = false; + } + } + + if (statsLimitValid && maxChargeCurrentLimitValid) { + // take the lowest limit + return min(statsCurrentLimit, maxChargeCurrentLimit); + } + + if (statsSoCValid) { + if (statsSoC <= chargeCurrentLimitBelowSoc) { + // below SoC and Voltage thresholds, ignore custom limit. + // Battery-provided limit will still be applied. + minChargeCurrentLimitValid = false; + } + } else { + if (statsVoltage <= chargeCurrentLimitBelowVoltage) { + // below SoC and Voltage thresholds, ignore custom limit. + // Battery-provided limit will still be applied. + minChargeCurrentLimitValid = false; + } + } + + if (statsLimitValid && minChargeCurrentLimitValid) { + // take the highest limit + return max(statsCurrentLimit, minChargeCurrentLimit); + } + + if (statsLimitValid) { + return statsCurrentLimit; + } + + if (maxChargeCurrentLimitValid) { + return maxChargeCurrentLimit; + } + + return FLT_MAX; +} } // namespace Batteries diff --git a/src/solarcharger/Controller.cpp b/src/solarcharger/Controller.cpp index 096ed63da..7429b7848 100644 --- a/src/solarcharger/Controller.cpp +++ b/src/solarcharger/Controller.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include #include #include #include @@ -72,6 +73,12 @@ void Controller::loop() if (!_upProvider) { return; } + //get charge current limitation from battery + auto stats = Battery.getStats(); + + float chargeCurrentLimit = Battery.getChargeCurrentLimit(); + float actChargeCurrent = stats->getChargeCurrent(); + _upProvider->setChargeLimit(chargeCurrentLimit, actChargeCurrent); _upProvider->loop(); // TODO(schlimmchen): this cannot make sure that transient diff --git a/src/solarcharger/victron/Provider.cpp b/src/solarcharger/victron/Provider.cpp index 0446969a8..f70c8b046 100644 --- a/src/solarcharger/victron/Provider.cpp +++ b/src/solarcharger/victron/Provider.cpp @@ -64,11 +64,46 @@ bool Provider::initController(gpio_num_t rx, gpio_num_t tx, uint8_t instance) return true; } +void Provider::setChargeLimit( float limit, float act_charge_current ) +{ + _chargeLimit = limit; + _chargeCurrent = act_charge_current; +} + + void Provider::loop() { std::lock_guard lock(_mutex); + float overallChargeCurrent { 0.0f }; + float overallLimit { _chargeLimit }; + float reservedChargeCurrent { 0.5f }; // minimum current for a controller whenever the given limit is higher + + uint8_t numControllers { 0 }; + // calculate the actual charge current of all MPPTs for (auto const& upController : _controllers) { + overallChargeCurrent += static_cast( upController->getData().batteryCurrent_I_mA ) / 1000.0f; + numControllers++; + } + // increase the charge limit with the current drawn by the inverter(s) + float inverterCurrent { overallChargeCurrent - _chargeCurrent }; + overallLimit += inverterCurrent; + + // reserve a minimum charge-current for every controller, to ensure that we get a good distribution over all MPPTs + const float overallReservedChargeCurrent { reservedChargeCurrent * static_cast(numControllers) }; + if (overallLimit > overallReservedChargeCurrent) { + overallLimit -= overallReservedChargeCurrent; + } else { + // the limit is lower than the needed reserve --> distribute the allowed limit in a simple way over all MPPTs + reservedChargeCurrent = overallLimit / static_cast(numControllers); + overallLimit = 0.0f; + } + for (auto const& upController : _controllers) { + const float batCurrent = static_cast( upController->getData().batteryCurrent_I_mA ) / 1000.0f; + const float factor = batCurrent / overallChargeCurrent; + const float controllerLimit { factor * overallLimit + reservedChargeCurrent}; + + upController->setChargeLimit(controllerLimit); upController->loop(); if (upController->isDataValid()) { diff --git a/src/solarcharger/victron/Stats.cpp b/src/solarcharger/victron/Stats.cpp index 3ad95f431..5afa232ca 100644 --- a/src/solarcharger/victron/Stats.cpp +++ b/src/solarcharger/victron/Stats.cpp @@ -251,7 +251,11 @@ void Stats::populateJsonWithInstanceStats(const JsonObject &root, const VeDirect device["MpptTemperature"]["u"] = "°C"; device["MpptTemperature"]["d"] = "1"; } - + if (mpptData.ChargeCurrentLimit.first > 0) { + device["ChargeCurrentLimit"]["v"] = mpptData.ChargeCurrentLimit.second / 10.0; + device["ChargeCurrentLimit"]["u"] = "A"; + device["ChargeCurrentLimit"]["d"] = 1; + } const JsonObject output = values["output"].to(); output["P"]["v"] = mpptData.batteryOutputPower_W; output["P"]["u"] = "W"; diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index b6680e011..6adc5d092 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -200,7 +200,8 @@ "RELAY": "Status Fehlerrelais", "ERR": "Fehlerbeschreibung", "HSDS": "Anzahl der Tage (0..364)", - "MpptTemperature": "Ladereglertemperatur" + "MpptTemperature": "Ladereglertemperatur", + "ChargeCurrentLimit": "Ladestromlimit" }, "section_output": "Ausgang (Batterie)", "output": { @@ -791,6 +792,17 @@ "BatteryReportedDischargeCurrentLimitInfo": "Hinweis: Das niedrigste Limit wird angewendet, wobei das von der Batterie übermittelte Entladestromlimit nur verwendet wird, wenn in der letzten Minute ein Update eingegangen ist. Andernfalls dient das zuvor festgelegte Limit als Fallback.", "MqttDischargeCurrentLimitTopic": "Topic für Entladestromlimit", "MqttAmperageUnit": "@:base.Unit", + "ChargeCurrentLimitConfiguration": "Einstellungen Ladestromlimit", + "LimitChargeCurrent": "Ladestrom limitieren", + "MinChargeCurrentLimit": "min. Ladestrom", + "MinChargeCurrentLimitInfo": "Das höchste Limit wird angewendet, wobei das von der Batterie übermittelte Ladestromlimit nur verwendet wird, wenn in der letzten Minute ein Update eingegangen ist; andernfalls dient das zuvor festgelegte Limit als Fallback.", + "MaxChargeCurrentLimit": "max. Ladestrom", + "ChargeCurrentLimitBelowSoc": "Limitieren unter SoC", + "ChargeCurrentLimitBelowSocInfo": "Das max Ladestromlimit wird nur unter dieser SoC-Schwelle angewendet (wird nicht verwendet, falls 'Batterie SoC ignorieren' in den DPL-Einstellungen aktiviert ist).", + "ChargeCurrentLimitBelowVoltage": "Limitieren unter Spannung", + "ChargeCurrentLimitBelowVoltageInfo": "Das max Ladestromlimit wird nur unter dieser Spannungs-Schwelle angewendet (wenn 'Batterie SoC ignorieren' in den DPL-Einstellungen aktiviert ist oder SoC nicht verfügbar ist).", + "UseBatteryReportedChargeCurrentLimit": "Von der Batterie übermitteltes Limit verwenden", + "BatteryReportedChargeCurrentLimitInfo": "Hinweis: Das niedrigste Limit wird angewendet, wobei das von der Batterie übermittelte Ladestromlimit nur verwendet wird, wenn in der letzten Minute ein Update eingegangen ist; andernfalls dient das zuvor festgelegte Limit als Fallback.", "ZendureConfiguration": "Einstellungen", "ZendureDeviceType": "Produkt Typ", "ZendureDeviceId": "Produkt Identifikation", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 44de8cc57..2a97d78ae 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -200,7 +200,8 @@ "RELAY": "Error relay state", "ERR": "Error code", "HSDS": "Day sequence number (0..364)", - "MpptTemperature": "Charge controller temperature" + "MpptTemperature": "Charge controller temperature", + "ChargeCurrentLimit": "Charge current limit" }, "section_output": "Output (Battery)", "output": { @@ -792,6 +793,17 @@ "BatteryReportedDischargeCurrentLimitInfo": "Hint: The lowest limit will be applied, with the battery-reported discharge current limit used only if an update was received in the last minute; otherwise, the previously specified limit will act as a fallback.", "MqttDischargeCurrentLimitTopic": "Discharge Current Limit Value Topic", "MqttAmperageUnit": "@:base.Unit", + "ChargeCurrentLimitConfiguration": "Charge Current Limit Settings", + "LimitChargeCurrent": "Limit Charge Current", + "MinChargeCurrentLimit": "min. Charge Current", + "MinChargeCurrentLimitInfo": "The highest limit will be applied, with the battery-reported discharge current limit used only if an update was received in the last minute; otherwise, the previously specified limit will act as a fallback.", + "MaxChargeCurrentLimit": "max. Charge Current", + "ChargeCurrentLimitBelowSoc": "Apply max limit below SoC", + "ChargeCurrentLimitBelowSocInfo": "The charge max current limit is only applied below this SoC (not used if 'Ignore Battery SoC' is enabled in the DPL settings).", + "ChargeCurrentLimitBelowVoltage": "Apply max limit below voltage", + "ChargeCurrentLimitBelowVoltageInfo": "The max charge current limit is only applied below this voltage (used if 'Ignore Battery SoC' is enabled in the DPL settings or when SoC is unavailable).", + "UseBatteryReportedChargeCurrentLimit": "Use Battery-Reported limit", + "BatteryReportedChargeCurrentLimitInfo": "Hint: The lowest limit will be applied, with the battery-reported discharge current limit used only if an update was received in the last minute; otherwise, the previously specified limit will act as a fallback.", "ZendureConfiguration": "Configuration", "ZendureDeviceType": "Product Type", "ZendureDeviceId": "Product ID", diff --git a/webapp/src/types/BatteryConfig.ts b/webapp/src/types/BatteryConfig.ts index 44fba6498..7443f1caa 100644 --- a/webapp/src/types/BatteryConfig.ts +++ b/webapp/src/types/BatteryConfig.ts @@ -50,4 +50,10 @@ export interface BatteryConfig { discharge_current_limit_below_soc: number; discharge_current_limit_below_voltage: number; use_battery_reported_discharge_current_limit: boolean; + enable_charge_current_limit: boolean; + min_charge_current_limit: number; + max_charge_current_limit: number; + charge_current_limit_below_soc: number; + charge_current_limit_below_voltage: number; + use_battery_reported_charge_current_limit: boolean; } diff --git a/webapp/src/views/BatteryAdminView.vue b/webapp/src/views/BatteryAdminView.vue index d1cdae2c5..e4aa169e7 100644 --- a/webapp/src/views/BatteryAdminView.vue +++ b/webapp/src/views/BatteryAdminView.vue @@ -276,6 +276,93 @@ + + + + + +