From 236c172b80e35fa575643176ff5423e0216d4b93 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Sat, 2 Nov 2024 21:45:50 +0100 Subject: [PATCH] webapp: refactor DPL inverter settings * remove the table of managed inverters with the row element before it, breaking the style of the webapp itself. * ditch the modals: all inverter settings are now visible if the respective inverter is selected to be governed. * solves the issue where changes to the inverter settings are not applied when the modal closes, as is expected by the users, but only when the whole DPL settings form is submitted. now it is intuitive that changed settings only apply once the whole form is submitted. --- include/Configuration.h | 1 + src/Configuration.cpp | 5 +- src/PowerLimiter.cpp | 4 +- src/WebApi_powerlimiter.cpp | 9 +- webapp/src/locales/de.json | 1 + webapp/src/locales/en.json | 1 + webapp/src/locales/fr.json | 1 + webapp/src/types/PowerLimiterConfig.ts | 5 +- webapp/src/views/PowerLimiterAdminView.vue | 908 +++++++++------------ 9 files changed, 414 insertions(+), 521 deletions(-) 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 /> -