full solar passthrough

This commit is contained in:
MalteSchm 2023-04-23 11:26:02 +02:00 committed by helgeerbe
parent a6f5e8a3a2
commit e0a8da84d7
14 changed files with 222 additions and 26 deletions

View File

@ -113,7 +113,8 @@ Other settings are:
* Target power consumption and hysteresis set the power range that can be consumed from the grid.
* Power limits control the min / max limits of the inverter
* Inverter is behind power meter. Select this if your inverter power is measured by the power meter. This is typically the case.
* Battery start and stop threshold can be configured using voltage and / or state of charge values. Stage of charge values require a Pylontech battery at this point.
* Battery start and stop threshold can be configured using voltage and / or state of charge values. Stage of charge values requires a Pylontech battery at this point.
* A Battery full solar passthrough threshold can be configured using voltage or state of charge value. Stage of charge values requires a Pylontech battery at this point. The option can be used if the battery is full and will steer the inverter according to solar power reported by the Victron MPPT Solar Charger.
![image](https://user-images.githubusercontent.com/59169507/222155765-9fff47a4-8ffa-42cf-8671-6359288e0cab.png)

View File

@ -153,6 +153,7 @@ cmd topics are used to set values. Status topics are updated from values set in
| battery/charging/chargeImmediately | R | Charge immediately flag | 0 / 1 |
### Huawei AC charger topics
| Topic | R / W | Description | Value / Unit |
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
| huawei/cmd/limit_online_voltage | W | Online voltage (i.e. CAN bus connected) | Volt (V) |
@ -168,4 +169,11 @@ cmd topics are used to set values. Status topics are updated from values set in
| huawei/output_power | R | Output power | Watt (W) |
| huawei/input_temp | R | Input air temperature | °C |
| huawei/output_temp | R | Output air temperature | °C |
| huawei/efficiency | R | Efficiency | Percentage |
| huawei/efficiency | R | Efficiency | Percentage |
### Power Limiter topics
| Topic | R / W | Description | Value / Unit |
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
| powerlimiter/cmd/mode | W | Power Limiter operation mode | 0 - Normal operation, 1 - Fully disable, 2 - Solar Passthrough only |
| powerlimiter/status/mode | R | Get Power Limiter operation mode | see above |

View File

@ -151,6 +151,9 @@ struct CONFIG_T {
float PowerLimiter_VoltageStopThreshold;
float PowerLimiter_VoltageLoadCorrectionFactor;
int8_t PowerLimiter_RestartHour;
uint32_t PowerLimiter_FullSolarPassThroughSoc;
float PowerLimiter_FullSolarPassThroughStartVoltage;
float PowerLimiter_FullSolarPassThroughStopVoltage;
bool Battery_Enabled;
bool Huawei_Enabled;

View File

@ -12,6 +12,11 @@
#define PL_UI_STATE_USE_SOLAR_ONLY 2
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
#define PL_MODE_ENABLE_NORMAL_OP 0
#define PL_MODE_FULL_DISABLE 1
#define PL_MODE_SOLAR_PT_ONLY 2
typedef enum {
SHUTDOWN = 0,
ACTIVE
@ -29,8 +34,8 @@ public:
void loop();
uint8_t getPowerLimiterState();
int32_t getLastRequestedPowewrLimit();
void setDisable(bool disable);
bool getDisable();
void setMode(uint8_t mode);
bool getMode();
void calcNextInverterRestart();
private:
@ -38,10 +43,11 @@ private:
int32_t _lastRequestedPowerLimit = 0;
uint32_t _lastLimitSetTime = 0;
plStates _plState;
bool _disabled = false;
uint8_t _mode = PL_MODE_ENABLE_NORMAL_OP;
bool _batteryDischargeEnabled = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
bool _fullSolarPassThroughEnabled = false;
float _powerMeter1Power;
float _powerMeter2Power;
@ -54,6 +60,7 @@ private:
float getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter);
bool isStartThresholdReached(std::shared_ptr<InverterAbstract> inverter);
bool isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter);
bool useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
};
extern PowerLimiterClass PowerLimiter;

View File

@ -123,6 +123,9 @@
#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
#define POWERLIMITER_RESTART_HOUR -1
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC 100
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100
#define BATTERY_ENABLED false

View File

@ -164,6 +164,9 @@ bool ConfigurationClass::write()
powerlimiter["voltage_stop_threshold"] = config.PowerLimiter_VoltageStopThreshold;
powerlimiter["voltage_load_correction_factor"] = config.PowerLimiter_VoltageLoadCorrectionFactor;
powerlimiter["inverter_restart_hour"] = config.PowerLimiter_RestartHour;
powerlimiter["full_solar_passthrough_soc"] = config.PowerLimiter_FullSolarPassThroughSoc;
powerlimiter["full_solar_passthrough_start_voltage"] = config.PowerLimiter_FullSolarPassThroughStartVoltage;
powerlimiter["full_solar_passthrough_stop_voltage"] = config.PowerLimiter_FullSolarPassThroughStopVoltage;
JsonObject battery = doc.createNestedObject("battery");
battery["enabled"] = config.Battery_Enabled;
@ -362,6 +365,9 @@ bool ConfigurationClass::read()
config.PowerLimiter_VoltageStopThreshold = powerlimiter["voltage_stop_threshold"] | POWERLIMITER_VOLTAGE_STOP_THRESHOLD;
config.PowerLimiter_VoltageLoadCorrectionFactor = powerlimiter["voltage_load_correction_factor"] | POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR;
config.PowerLimiter_RestartHour = powerlimiter["inverter_restart_hour"] | POWERLIMITER_RESTART_HOUR;
config.PowerLimiter_FullSolarPassThroughSoc = powerlimiter["full_solar_passthrough_soc"] | POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC;
config.PowerLimiter_FullSolarPassThroughStartVoltage = powerlimiter["full_solar_passthrough_start_voltage"] | POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE;
config.PowerLimiter_FullSolarPassThroughStopVoltage = powerlimiter["full_solar_passthrough_stop_voltage"] | POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE;
JsonObject battery = doc["battery"];
config.Battery_Enabled = battery["enabled"] | BATTERY_ENABLED;

View File

@ -8,7 +8,7 @@
#include "PowerLimiter.h"
#include <ctime>
#define TOPIC_SUB_POWER_LIMITER "disable"
#define TOPIC_SUB_POWER_LIMITER "mode"
MqttHandlePowerLimiterClass MqttHandlePowerLimiter;
@ -38,7 +38,7 @@ void MqttHandlePowerLimiterClass::loop()
const CONFIG_T& config = Configuration.get();
if ((millis() - _lastPublish) > (config.Mqtt_PublishInterval * 1000) ) {
MqttSettings.publish("powerlimiter/status/disabled", String(PowerLimiter.getDisable()));
MqttSettings.publish("powerlimiter/status/mode", String(PowerLimiter.getMode()));
yield();
_lastPublish = millis();
@ -77,14 +77,19 @@ void MqttHandlePowerLimiterClass::onMqttMessage(const espMqttClientTypes::Messag
delete[] str;
if (!strcmp(setting, TOPIC_SUB_POWER_LIMITER)) {
if(payload_val == 2) {
MessageOutput.println("Power limiter full solar PT");
PowerLimiter.setMode(PL_MODE_SOLAR_PT_ONLY);
return;
}
if(payload_val == 1) {
MessageOutput.println("Power limiter disabled");
PowerLimiter.setDisable(true);
PowerLimiter.setMode(PL_MODE_FULL_DISABLE);
return;
}
if(payload_val == 0) {
MessageOutput.println("Power limiter enabled");
PowerLimiter.setDisable(false);
PowerLimiter.setMode(PL_MODE_ENABLE_NORMAL_OP);
return;
}
MessageOutput.println("Power limiter enable / disable - unknown command received. Please use 0 or 1");

View File

@ -15,7 +15,7 @@
PowerLimiterClass PowerLimiter;
// #define POWER_LIMITER_DEBUG
#define POWER_LIMITER_DEBUG
void PowerLimiterClass::init()
{
@ -75,7 +75,7 @@ void PowerLimiterClass::loop()
}
// Make sure inverter is turned off if PL is disabled by user/MQTT
if (((!config.PowerLimiter_Enabled || _disabled) && _plState != SHUTDOWN)) {
if (((!config.PowerLimiter_Enabled || _mode == PL_MODE_FULL_DISABLE) && _plState != SHUTDOWN)) {
if (inverter->isProducing()) {
MessageOutput.printf("PL initiated inverter shutdown.\r\n");
inverter->sendActivePowerControlRequest(static_cast<float>(config.PowerLimiter_LowerPowerLimit), PowerLimitControlType::AbsolutNonPersistent);
@ -91,7 +91,7 @@ void PowerLimiterClass::loop()
}
// Return if power limiter is disabled
if (!config.PowerLimiter_Enabled || _disabled) {
if (!config.PowerLimiter_Enabled || _mode == PL_MODE_FULL_DISABLE) {
#ifdef POWER_LIMITER_DEBUG
MessageOutput.printf("[PowerLimiterClass::loop] ******************* PL disabled\r\n");
#endif
@ -206,12 +206,12 @@ int32_t PowerLimiterClass::getLastRequestedPowewrLimit() {
return _lastRequestedPowerLimit;
}
bool PowerLimiterClass::getDisable() {
return _disabled;
bool PowerLimiterClass::getMode() {
return _mode;
}
void PowerLimiterClass::setDisable(bool disable) {
_disabled = disable;
void PowerLimiterClass::setMode(uint8_t mode) {
_mode = mode;
}
bool PowerLimiterClass::canUseDirectSolarPower()
@ -232,7 +232,13 @@ bool PowerLimiterClass::canUseDirectSolarPower()
}
// Logic table
// | Case # | batteryDischargeEnabled | solarPowerEnabled | useFullSolarPassthrough | Result |
// | 1 | false | false | doesn't matter | PL = 0 |
// | 2 | false | true | doesn't matter | PL = Victron Power |
// | 3 | true | doesn't matter | false | PL = PowerMeter value (Battery can supply unlimited energy) |
// | 4 | true | false | true | PL = PowerMeter value |
// | 5 | true | true | true | PL = max(PowerMeter value, Victron Power) |
int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool solarPowerEnabled, bool batteryDischargeEnabled)
{
@ -241,7 +247,7 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
int32_t newPowerLimit = round(PowerMeter.getPowerTotal());
if (!solarPowerEnabled && !batteryDischargeEnabled) {
// No energy sources available
// Case 1 - No energy sources available
return 0;
}
@ -256,6 +262,7 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
// We're not trying to hit 0 exactly but take an offset into account
// This means we never fully compensate the used power with the inverter
// Case 3
newPowerLimit -= config.PowerLimiter_TargetPowerConsumption;
// Check if the new value is within the limits of the hysteresis and
@ -268,17 +275,27 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
return _lastRequestedPowerLimit;
}
// We should use Victron solar power only (corrected by efficiency factor)
if (solarPowerEnabled && !batteryDischargeEnabled) {
float efficiency = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_EFF);
int32_t victronChargePower = this->getDirectSolarPower();
int32_t adjustedVictronChargePower = victronChargePower * (efficiency > 0.0 ? (efficiency / 100.0) : 1.0); // if inverter is off, use 1.0
// At this point we've calculated the required energy to compensate for household consumption.
// If the battery is enabled this can always be supplied since we assume that the battery can supply unlimited power
// The next step is to determine if the Solar power as provided by the Victron charger
// actually constrains or dictates another inverter power value
float efficiency = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_EFF) * 0.95 /*Victron efficiency*/;
int32_t victronChargePower = this->getDirectSolarPower();
int32_t adjustedVictronChargePower = victronChargePower * (efficiency > 0.0 ? (efficiency / 100.0) : 1.0); // if inverter is off, use 1.0
// Battery can be discharged and we should output max (Victron solar power || power meter value)
if(batteryDischargeEnabled && useFullSolarPassthrough(inverter)) {
// Case 5
newPowerLimit = newPowerLimit > adjustedVictronChargePower ? newPowerLimit : adjustedVictronChargePower;
}
// We should use Victron solar power only (corrected by efficiency factor)
if ((solarPowerEnabled && !batteryDischargeEnabled) || (_mode == PL_MODE_SOLAR_PT_ONLY)) {
// Case 2 - Limit power to solar power only
MessageOutput.printf("[PowerLimiterClass::loop] Consuming Solar Power Only -> victronChargePower: %d, efficiency: %.2f, powerConsumption: %d \r\n",
victronChargePower, efficiency, newPowerLimit);
// Limit power to solar power only
if (adjustedVictronChargePower < newPowerLimit)
if ((adjustedVictronChargePower < newPowerLimit) || (_mode == PL_MODE_SOLAR_PT_ONLY))
newPowerLimit = adjustedVictronChargePower;
}
@ -445,4 +462,48 @@ void PowerLimiterClass::calcNextInverterRestart()
_nextInverterRestart = 0;
}
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart @ %d millis\r\n", _nextInverterRestart);
}
bool PowerLimiterClass::useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter)
{
CONFIG_T& config = Configuration.get();
// We only do full solar PT if general solar PT is enabled
if(!config.PowerLimiter_SolarPassThroughEnabled) {
return false;
}
// Check if the Battery interface is enabled and the SOC stop threshold is reached
if (config.Battery_Enabled
&& config.PowerLimiter_FullSolarPassThroughSoc > 0.0
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000
&& Battery.stateOfCharge >= config.PowerLimiter_FullSolarPassThroughSoc) {
return true;
}
// Otherwise we use the voltage threshold
if (config.PowerLimiter_FullSolarPassThroughStartVoltage <= 0.0 || config.PowerLimiter_FullSolarPassThroughStopVoltage <= 0.0) {
return false;
}
float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC);
#ifdef POWER_LIMITER_DEBUG
MessageOutput.printf("[PowerLimiterClass::loop] useFullSolarPassthrough: FullSolarPT Start %f, FullSolarPT Stop: %f, dcVoltage: %f\r\n",
config.PowerLimiter_FullSolarPassThroughStartVoltage, config.PowerLimiter_FullSolarPassThroughStopVoltage, dcVoltage);
#endif
if (dcVoltage <= 0.0) {
return false;
}
if (dcVoltage >= config.PowerLimiter_FullSolarPassThroughStartVoltage) {
_fullSolarPassThroughEnabled = true;
}
if (dcVoltage <= config.PowerLimiter_FullSolarPassThroughStopVoltage) {
_fullSolarPassThroughEnabled = false;
}
return _fullSolarPassThroughEnabled;
}

View File

@ -53,6 +53,9 @@ void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request)
root[F("voltage_stop_threshold")] = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100 +0.5) / 100.0;;
root[F("voltage_load_correction_factor")] = config.PowerLimiter_VoltageLoadCorrectionFactor;
root[F("inverter_restart_hour")] = config.PowerLimiter_RestartHour;
root[F("full_solar_passthrough_soc")] = config.PowerLimiter_FullSolarPassThroughSoc;
root[F("full_solar_passthrough_start_voltage")] = static_cast<int>(config.PowerLimiter_FullSolarPassThroughStartVoltage * 100 + 0.5) / 100.0;
root[F("full_solar_passthrough_stop_voltage")] = static_cast<int>(config.PowerLimiter_FullSolarPassThroughStopVoltage * 100 + 0.5) / 100.0;
response->setLength();
request->send(response);
@ -120,7 +123,7 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
CONFIG_T& config = Configuration.get();
config.PowerLimiter_Enabled = root[F("enabled")].as<bool>();
PowerLimiter.setDisable(false); // User input clears the PL internal disable flag
PowerLimiter.setMode(PL_MODE_ENABLE_NORMAL_OP); // User input sets PL to normal operation
config.PowerLimiter_SolarPassThroughEnabled = root[F("solar_passtrough_enabled")].as<bool>();
config.PowerLimiter_BatteryDrainStategy= root[F("battery_drain_strategy")].as<uint8_t>();
config.PowerLimiter_IsInverterBehindPowerMeter = root[F("is_inverter_behind_powermeter")].as<bool>();
@ -138,6 +141,12 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
config.PowerLimiter_VoltageStopThreshold = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100) / 100.0;
config.PowerLimiter_VoltageLoadCorrectionFactor = root[F("voltage_load_correction_factor")].as<float>();
config.PowerLimiter_RestartHour = root[F("inverter_restart_hour")].as<int8_t>();
config.PowerLimiter_FullSolarPassThroughSoc = root[F("full_solar_passthrough_soc")].as<uint32_t>();
config.PowerLimiter_FullSolarPassThroughStartVoltage = static_cast<int>(root[F("full_solar_passthrough_start_voltage")].as<float>() * 100) / 100.0;
config.PowerLimiter_FullSolarPassThroughStopVoltage = static_cast<int>(root[F("full_solar_passthrough_stop_voltage")].as<float>() * 100) / 100.0;
Configuration.write();
PowerLimiter.calcNextInverterRestart();

View File

@ -542,8 +542,13 @@
"PowerMeters": "Leistungsmesser",
"BatterySocStartThreshold": "Akku SOC - Start",
"BatterySocStopThreshold": "Akku SOC - Stop",
"BatterySocSolarPassthroughStartThreshold": "Akku SOC - Start solar passthrough",
"BatterySocSolarPassthroughStartThresholdHint": "Wenn der Batterie SOC über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist.",
"VoltageStartThreshold": "DC Spannung - Start",
"VoltageStopThreshold": "DC Spannung - Stop",
"VoltageSolarPassthroughStartThreshold": "DC Spannung - Start solar passthrough",
"VoltageSolarPassthroughStopThreshold": "DC Spannung - Stop solar passthrough",
"VoltageSolarPassthroughStartThresholdHint": "Wenn der Batteriespannung über diesem Limit ist wird die Inverter Leistung entsprechend der Victron MPPT Leistung gesetzt (abzüglich Effizienzkorrekturfaktor). Kann verwendet werden um überschüssige Solarleistung an das Netz zu liefern wenn die Batterie voll ist. Dieser Mode wird aktiv wenn das Start Spannungslimit überschritten wird und inaktiv wenn das Stop Spannungslimit unterschritten wird.",
"VoltageLoadCorrectionFactor": "DC Spannung - Lastkorrekturfaktor",
"BatterySocInfo": "<b>Hinweis:</b> Der Battery SOC (State of charge) -Wert kann nur benutzt werden wenn das Battery CAN Bus Interface aktiviert ist. Wenn die Batterie innerhalb der letzten Minute keine Werte geschickt hat, werden als Fallback-Option die Spannungseinstellungen verwendet.",
"InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser",

View File

@ -546,8 +546,13 @@
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)",
"BatterySocStartThreshold": "Battery SOC - Start threshold",
"BatterySocStopThreshold": "Battery SOC - Stop threshold",
"BatterySocSolarPassthroughStartThreshold": "Battery SOC - Start threshold for full solar passthrough",
"BatterySocSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) if battery SOC is over this limit. Use this if you like to supply excess power to the grid when battery is full",
"VoltageStartThreshold": "DC Voltage - Start threshold",
"VoltageStopThreshold": "DC Voltage - Stop threshold",
"VoltageSolarPassthroughStartThreshold": "DC Voltage - Start threshold for full solar passthrough",
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
"BatterySocInfo": "<b>Hint:</b> The battery SOC (State of charge) values can only be used when the Battery CAN Bus interface is enabled. If the battery has not reported any updates of SOC in the last minute, the voltage thresholds will be used as fallback.",
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",

View File

@ -541,6 +541,46 @@
"ResetConfirm": "Remise à zéro !",
"Cancel": "@:maintenancereboot.Cancel"
},
"powerlimiteradmin": {
"PowerLimiterSettings": "Power Limiter Settings",
"PowerLimiterConfiguration": "Power Limiter Configuration",
"General": "General",
"Enable": "Enable",
"EnableSolarPasstrough": "Enable Solar-Passtrough",
"BatteryDrainStrategy": "Battery drain strategy",
"BatteryDrainWhenFull": "Empty when full",
"BatteryDrainAtNight": "Empty at night",
"SolarpasstroughInfo": "When the sun is shining, this setting enables the sychronization of the inverter limit with the current solar power of the Victron MPPT charger. This optimizes battery degradation and loses.",
"InverterId": "Inverter ID",
"InverterIdHint": "Select proper inverter ID where battery is connected to.",
"InverterChannelId": "Channel ID",
"InverterChannelIdHint": "Select proper channel where battery is connected to.",
"TargetPowerConsumption": "Target power consumption from grid",
"TargetPowerConsumptionHint": "Set the grid power consumption the limiter tries to achieve.",
"TargetPowerConsumptionHysteresis": "Hysteresis for power consumption from grid",
"TargetPowerConsumptionHysteresisHint": "Value around which the target grid power consumption fluctuates without readjustment.",
"LowerPowerLimit": "Lower power limit",
"UpperPowerLimit": "Upper power limit",
"PowerMeters": "Power meter",
"MqttTopicPowerMeter1": "MQTT topic - Power meter #1",
"MqttTopicPowerMeter2": "MQTT topic - Power meter #2 (optional)",
"MqttTopicPowerMeter3": "MQTT topic - Power meter #3 (optional)",
"BatterySocStartThreshold": "Battery SOC - Start threshold",
"BatterySocStopThreshold": "Battery SOC - Stop threshold",
"BatterySocSolarPassthroughStartThreshold": "Battery SOC - Start threshold for full solar passthrough",
"BatterySocSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) if battery SOC is over this limit. Use this if you like to supply excess power to the grid when battery is full",
"VoltageStartThreshold": "DC Voltage - Start threshold",
"VoltageStopThreshold": "DC Voltage - Stop threshold",
"VoltageSolarPassthroughStartThreshold": "DC Voltage - Start threshold for full solar passthrough",
"VoltageSolarPassthroughStopThreshold": "DC Voltage - Stop threshold for full solar passthrough",
"VoltageSolarPassthroughStartThresholdHint": "Inverter power is set according to Victron MPPT power (minus efficiency factors) when full solar passthrough is active. Use this if you like to supply excess power to the grid when battery is full. This is started when battery voltage goes over this limit and stopped if voltage drops below stop limit.",
"VoltageLoadCorrectionFactor": "DC Voltage - Load correction factor",
"BatterySocInfo": "<b>Hint:</b> The battery SOC (State of charge) values can only be used when the Battery CAN Bus interface is enabled. If the battery has not reported any updates of SOC in the last minute, the voltage thresholds will be used as fallback.",
"InverterIsBehindPowerMeter": "Inverter is behind Power meter",
"Battery": "DC / Battery",
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor).",
"Save": "@:dtuadmin.Save"
},
"login": {
"Login": "Connexion",
"SystemLogin": "Connexion au système",

View File

@ -15,4 +15,7 @@ export interface PowerLimiterConfig {
voltage_stop_threshold: number;
voltage_load_correction_factor: number;
inverter_restart_hour: number;
full_solar_passthrough_soc: number;
full_solar_passthrough_start_voltage: number;
full_solar_passthrough_stop_voltage: number;
}

View File

@ -152,6 +152,20 @@
</div>
</div>
<div class="row mb-3" v-show="powerLimiterConfigList.solar_passtrough_enabled">
<label for="batterySocSolarPassthroughStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.BatterySocSolarPassthroughStartThreshold') }}
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.BatterySocSolarPassthroughStartThresholdHint')" />
</label>
<div class="col-sm-10">
<div class="input-group">
<input type="number" class="form-control" id="batterySocSolarPassthroughStartThreshold"
placeholder="20" v-model="powerLimiterConfigList.full_solar_passthrough_soc"
aria-describedby="batterySocSolarPassthroughStartThresholdDescription" min="0" max="100" required/>
<span class="input-group-text" id="batterySocSolarPassthroughStartThresholdDescription">%</span>
</div>
</div>
</div>
<div class="alert alert-secondary" role="alert" v-html="$t('powerlimiteradmin.BatterySocInfo')"></div>
<div class="row mb-3">
@ -178,6 +192,32 @@
</div>
</div>
<div class="row mb-3" v-show="powerLimiterConfigList.solar_passtrough_enabled">
<label for="inputVoltageSolarPassthroughStartThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.VoltageSolarPassthroughStartThreshold') }}:
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.VoltageSolarPassthroughStartThresholdHint')" />
</label>
<div class="col-sm-10">
<div class="input-group">
<input type="number" step="0.01" class="form-control" id="inputVoltageSolarPassthroughStartThreshold"
placeholder="49" v-model="powerLimiterConfigList.full_solar_passthrough_start_voltage"
aria-describedby="voltageSolarPassthroughStartThresholdDescription" required/>
<span class="input-group-text" id="voltageSolarPassthroughStartThresholdDescription">V</span>
</div>
</div>
</div>
<div class="row mb-3" v-show="powerLimiterConfigList.solar_passtrough_enabled">
<label for="inputVoltageSolarPassthroughStopThreshold" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.VoltageSolarPassthroughStopThreshold') }}:</label>
<div class="col-sm-10">
<div class="input-group">
<input type="number" step="0.01" class="form-control" id="inputVoltageSolarPassthroughStopThreshold"
placeholder="49" v-model="powerLimiterConfigList.full_solar_passthrough_stop_voltage"
aria-describedby="voltageSolarPassthroughStopThresholdDescription" required/>
<span class="input-group-text" id="voltageSolarPassthroughStopThresholdDescription">V</span>
</div>
</div>
</div>
<div class="row mb-3">
<label for="inputVoltageLoadCorrectionFactor" class="col-sm-2 col-form-label">{{ $t('powerlimiteradmin.VoltageLoadCorrectionFactor') }}:</label>
<div class="col-sm-10">