diff --git a/include/Configuration.h b/include/Configuration.h index a49cc7500..79d3e61c9 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -134,6 +134,7 @@ using PowerMeterHttpSmlConfig = struct POWERMETER_HTTP_SML_CONFIG_T; struct POWERLIMITER_INVERTER_CONFIG_T { uint64_t Serial; + bool IsGoverned; bool IsBehindPowerMeter; bool IsSolarPowered; bool UseOverscalingToCompensateShading; diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 6cc939fcb..a948a2c60 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -116,7 +116,7 @@ void ConfigurationClass::serializePowerLimiterConfig(PowerLimiterConfig const& s }; // we want a representation of our floating-point value in the JSON that - // uses the least amount of decimal digits possble to convey the value that + // uses the least amount of decimal digits possible to convey the value that // is actually represented by the float. this is no easy task. ArduinoJson // does this for us, however, it does it as expected only for variables of // type double. this is probably because it assumes all floating-point @@ -156,6 +156,7 @@ void ConfigurationClass::serializePowerLimiterConfig(PowerLimiterConfig const& s JsonObject t = inverters.add(); t["serial"] = serialStr(s.Serial); + 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; @@ -470,6 +471,7 @@ void ConfigurationClass::deserializePowerLimiterConfig(JsonObject const& source, JsonObject s = inverters[i]; inv.Serial = serialBin(s["serial"] | String("0")); // 0 marks inverter slot as unused + 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; @@ -741,6 +743,7 @@ bool ConfigurationClass::read() } inv.Serial = previousInverterSerial; config.PowerLimiter.InverterSerialForDcVoltage = previousInverterSerial; + 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; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 88003ca54..59a156358 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -140,6 +140,8 @@ void PowerLimiterClass::loop() if (invConfig.Serial == 0ULL) { break; } + if (!invConfig.IsGoverned) { continue; } + auto iter = _inverters.begin(); while (iter != _inverters.end()) { if ((*iter)->getSerial() == invConfig.Serial) { break; } @@ -160,7 +162,7 @@ void PowerLimiterClass::loop() for (size_t i = 0; i < INV_MAX_COUNT; ++i) { auto const& inv = config.PowerLimiter.Inverters[i]; if (inv.Serial == 0ULL) { break; } - found = inv.Serial == (*iter)->getSerial(); + found = inv.Serial == (*iter)->getSerial() && inv.IsGoverned; if (found) { break; } } diff --git a/src/WebApi_powerlimiter.cpp b/src/WebApi_powerlimiter.cpp index 35b62f81f..0d8a5be8e 100644 --- a/src/WebApi_powerlimiter.cpp +++ b/src/WebApi_powerlimiter.cpp @@ -52,21 +52,20 @@ void WebApiPowerLimiterClass::onMetaData(AsyncWebServerRequest* request) root["battery_enabled"] = config.Battery.Enabled; root["charge_controller_enabled"] = config.Vedirect.Enabled; - JsonObject inverters = root["inverters"].to(); + JsonArray inverters = root["inverters"].to(); for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial); if (!inv) { continue; } - JsonObject obj = inverters[inv->serialString()].to(); + JsonObject obj = inverters.add(); + obj["serial"] = inv->serialString(); obj["pos"] = i; obj["name"] = String(config.Inverter[i].Name); obj["poll_enable"] = config.Inverter[i].Poll_Enable; obj["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night; obj["command_enable"] = config.Inverter[i].Command_Enable; obj["command_enable_night"] = config.Inverter[i].Command_Enable_Night; - - obj["type"] = "Unknown"; - obj["channels"] = 1; + obj["max_power"] = inv->DevInfo()->getMaxPower(); // okay if zero/unknown obj["type"] = inv->typeName(); auto channels = inv->Statistics()->getChannelsByType(TYPE_DC); obj["channels"] = channels.size(); diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index a4c5e6d7e..b0a89339f 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -664,6 +664,7 @@ "ConfigHintNoBatteryInterface": "SoC-basierte Schwellwerte können nur mit konfigurierter Batteriekommunikationsschnittstelle genutzt werden.", "General": "Allgemein", "Enable": "Aktiviert", + "GovernInverter": "Steuere Wechselrichter \"{name}\"", "VerboseLogging": "@:base.VerboseLogging", "SolarPassthrough": "Solar-Passthrough", "EnableSolarPassthrough": "Aktiviere Solar-Passthrough", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 707313cf1..a798a9cfc 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -666,6 +666,7 @@ "ConfigHintNoBatteryInterface": "SoC-based thresholds can only be used if a battery communication interface is configured.", "General": "General", "Enable": "Enable", + "GovernInverter": "Govern Inverter \"{name}\"", "VerboseLogging": "@:base.VerboseLogging", "SolarPassthrough": "Solar-Passthrough", "EnableSolarPassthrough": "Enable Solar-Passthrough", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index b2c43dbc4..9a99f42e5 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -732,6 +732,7 @@ "ConfigHintNoBatteryInterface": "SoC-based thresholds can only be used if a battery communication interface is configured.", "General": "General", "Enable": "Enable", + "GovernInverter": "Govern Inverter \"{name}\"", "VerboseLogging": "@:base.VerboseLogging", "SolarPassthrough": "Solar-Passthrough", "EnableSolarPassthrough": "Enable Solar-Passthrough", diff --git a/webapp/src/types/PowerLimiterConfig.ts b/webapp/src/types/PowerLimiterConfig.ts index 009604312..a2189ec50 100644 --- a/webapp/src/types/PowerLimiterConfig.ts +++ b/webapp/src/types/PowerLimiterConfig.ts @@ -1,10 +1,12 @@ export interface PowerLimiterInverterInfo { + serial: string; pos: number; name: string; poll_enable: boolean; poll_enable_night: boolean; command_enable: boolean; command_enable_night: boolean; + max_power: number; type: string; channels: number; } @@ -15,11 +17,12 @@ export interface PowerLimiterMetaData { power_meter_enabled: boolean; battery_enabled: boolean; charge_controller_enabled: boolean; - inverters: { [key: string]: PowerLimiterInverterInfo }; + inverters: PowerLimiterInverterInfo[]; } export interface PowerLimiterInverterConfig { serial: string; + is_governed: boolean; is_behind_power_meter: boolean; is_solar_powered: boolean; use_overscaling_to_compensate_shading: boolean; diff --git a/webapp/src/views/PowerLimiterAdminView.vue b/webapp/src/views/PowerLimiterAdminView.vue index 57ed9be6b..0803ad384 100644 --- a/webapp/src/views/PowerLimiterAdminView.vue +++ b/webapp/src/views/PowerLimiterAdminView.vue @@ -42,7 +42,18 @@ wide /> -