From 87280dfcc230f1d7cb383e440b07e351c8942841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 20 Nov 2024 17:54:13 +0100 Subject: [PATCH 01/13] remove vedirect info Because this view shows the same information as the configuration I figured that we can remove it and avoid the hassle of refactoring it. --- src/WebApi_vedirect.cpp | 103 -------------------------- webapp/src/components/NavBar.vue | 5 -- webapp/src/locales/de.json | 12 --- webapp/src/locales/en.json | 12 --- webapp/src/locales/fr.json | 12 --- webapp/src/router/index.ts | 6 -- webapp/src/types/VedirectConfig.ts | 5 -- webapp/src/views/VedirectInfoView.vue | 79 -------------------- 8 files changed, 234 deletions(-) delete mode 100644 src/WebApi_vedirect.cpp delete mode 100644 webapp/src/types/VedirectConfig.ts delete mode 100644 webapp/src/views/VedirectInfoView.vue diff --git a/src/WebApi_vedirect.cpp b/src/WebApi_vedirect.cpp deleted file mode 100644 index 06493ab88..000000000 --- a/src/WebApi_vedirect.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2022-2024 Thomas Basler and others - */ -#include "WebApi_vedirect.h" -#include "VictronMppt.h" -#include "ArduinoJson.h" -#include "AsyncJson.h" -#include "Configuration.h" -#include "WebApi.h" -#include "WebApi_errors.h" -#include "helper.h" -#include "MqttHandlePowerLimiterHass.h" - -void WebApiVedirectClass::init(AsyncWebServer& server, Scheduler& scheduler) -{ - using std::placeholders::_1; - - _server = &server; - - _server->on("/api/vedirect/status", HTTP_GET, std::bind(&WebApiVedirectClass::onVedirectStatus, this, _1)); - _server->on("/api/vedirect/config", HTTP_GET, std::bind(&WebApiVedirectClass::onVedirectAdminGet, this, _1)); - _server->on("/api/vedirect/config", HTTP_POST, std::bind(&WebApiVedirectClass::onVedirectAdminPost, this, _1)); -} - -void WebApiVedirectClass::onVedirectStatus(AsyncWebServerRequest* request) -{ - if (!WebApi.checkCredentialsReadonly(request)) { - return; - } - - AsyncJsonResponse* response = new AsyncJsonResponse(); - auto& root = response->getRoot(); - auto const& config = Configuration.get(); - - root["vedirect_enabled"] = config.Vedirect.Enabled; - root["verbose_logging"] = config.Vedirect.VerboseLogging; - root["vedirect_updatesonly"] = config.Vedirect.UpdatesOnly; - - response->setLength(); - request->send(response); -} - -void WebApiVedirectClass::onVedirectAdminGet(AsyncWebServerRequest* request) -{ - if (!WebApi.checkCredentials(request)) { - return; - } - - AsyncJsonResponse* response = new AsyncJsonResponse(); - auto& root = response->getRoot(); - auto const& config = Configuration.get(); - - root["vedirect_enabled"] = config.Vedirect.Enabled; - root["verbose_logging"] = config.Vedirect.VerboseLogging; - root["vedirect_updatesonly"] = config.Vedirect.UpdatesOnly; - - response->setLength(); - request->send(response); -} - -void WebApiVedirectClass::onVedirectAdminPost(AsyncWebServerRequest* request) -{ - if (!WebApi.checkCredentials(request)) { - return; - } - - AsyncJsonResponse* response = new AsyncJsonResponse(); - JsonDocument root; - if (!WebApi.parseRequestData(request, response, root)) { - return; - } - - auto& retMsg = response->getRoot(); - - if (!root["vedirect_enabled"].is() || - !root["verbose_logging"].is() || - !root["vedirect_updatesonly"].is() ) { - retMsg["message"] = "Values are missing!"; - retMsg["code"] = WebApiError::GenericValueMissing; - response->setLength(); - request->send(response); - return; - } - - { - auto guard = Configuration.getWriteGuard(); - auto& config = guard.getConfig(); - config.Vedirect.Enabled = root["vedirect_enabled"].as(); - config.Vedirect.VerboseLogging = root["verbose_logging"].as(); - config.Vedirect.UpdatesOnly = root["vedirect_updatesonly"].as(); - } - - WebApi.writeConfig(retMsg); - - WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); - - - VictronMppt.updateSettings(); - - // potentially make solar passthrough thresholds auto-discoverable - MqttHandlePowerLimiterHass.forceUpdate(); -} diff --git a/webapp/src/components/NavBar.vue b/webapp/src/components/NavBar.vue index 34536eea3..f1b7369e7 100644 --- a/webapp/src/components/NavBar.vue +++ b/webapp/src/components/NavBar.vue @@ -153,11 +153,6 @@ $t('menu.MQTT') }} -
  • - {{ - $t('menu.Vedirect') - }} -
  • diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 8159e4cd0..fca088c49 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -22,7 +22,6 @@ "NTP": "NTP", "MQTT": "MQTT", "Console": "Konsole", - "Vedirect": "VE.Direct", "About": "Über", "Logout": "Abmelden", "Login": "Anmelden" @@ -413,17 +412,6 @@ "Connected": "verbunden", "Disconnected": "getrennt" }, - "vedirectinfo": { - "VedirectInformation": "VE.Direct Info", - "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", - "Status": "@:ntpinfo.Status", - "Enabled": "@:mqttinfo.Enabled", - "Disabled": "@:mqttinfo.Disabled", - "VerboseLogging": "@:base.VerboseLogging", - "UpdatesOnly": "@:vedirectadmin.UpdatesOnly", - "UpdatesEnabled": "@:mqttinfo.Enabled", - "UpdatesDisabled": "@:mqttinfo.Disabled" - }, "console": { "Console": "Konsole", "VirtualDebugConsole": "Virtuelle Debug-Konsole", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index cad075339..25017e0f3 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -22,7 +22,6 @@ "NTP": "NTP", "MQTT": "MQTT", "Console": "Console", - "Vedirect": "VE.Direct", "About": "About", "Logout": "Logout", "Login": "Login" @@ -415,17 +414,6 @@ "Connected": "connected", "Disconnected": "disconnected" }, - "vedirectinfo": { - "VedirectInformation": "VE.Direct Info", - "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", - "Status": "@:ntpinfo.Status", - "Enabled": "@:mqttinfo.Enabled", - "Disabled": "@:mqttinfo.Disabled", - "VerboseLogging": "@:base.VerboseLogging", - "UpdatesOnly": "@:vedirectadmin.UpdatesOnly", - "UpdatesEnabled": "@:mqttinfo.Enabled", - "UpdatesDisabled": "@:mqttinfo.Disabled" - }, "console": { "Console": "Console", "VirtualDebugConsole": "Virtual Debug Console", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 5dc795908..8b75ef51e 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -22,7 +22,6 @@ "NTP": "NTP", "MQTT": "MQTT", "Console": "Console", - "Vedirect": "VE.Direct", "About": "A propos", "Logout": "Déconnexion", "Login": "Connexion" @@ -430,17 +429,6 @@ "Connected": "connecté", "Disconnected": "déconnecté" }, - "vedirectinfo": { - "VedirectInformation": "VE.Direct Info", - "ConfigurationSummary": "@:ntpinfo.ConfigurationSummary", - "Status": "@:ntpinfo.Status", - "Enabled": "@:mqttinfo.Enabled", - "Disabled": "@:mqttinfo.Disabled", - "VerboseLogging": "@:base.VerboseLogging", - "UpdatesOnly": "@:vedirectadmin.UpdatesOnly", - "UpdatesEnabled": "@:mqttinfo.Enabled", - "UpdatesDisabled": "@:mqttinfo.Disabled" - }, "console": { "Console": "Console", "VirtualDebugConsole": "Console de débogage", diff --git a/webapp/src/router/index.ts b/webapp/src/router/index.ts index 52d29de3a..82688e159 100644 --- a/webapp/src/router/index.ts +++ b/webapp/src/router/index.ts @@ -11,7 +11,6 @@ import HomeView from '@/views/HomeView.vue'; import VedirectAdminView from '@/views/VedirectAdminView.vue'; import PowerMeterAdminView from '@/views/PowerMeterAdminView.vue'; import PowerLimiterAdminView from '@/views/PowerLimiterAdminView.vue'; -import VedirectInfoView from '@/views/VedirectInfoView.vue'; import InverterAdminView from '@/views/InverterAdminView.vue'; import LoginView from '@/views/LoginView.vue'; import MaintenanceRebootView from '@/views/MaintenanceRebootView.vue'; @@ -82,11 +81,6 @@ const router = createRouter({ name: 'Web Console', component: ConsoleInfoView, }, - { - path: '/info/vedirect', - name: 'VE.Direct', - component: VedirectInfoView, - }, { path: '/settings/network', name: 'Network Settings', diff --git a/webapp/src/types/VedirectConfig.ts b/webapp/src/types/VedirectConfig.ts deleted file mode 100644 index 8b2e356cf..000000000 --- a/webapp/src/types/VedirectConfig.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface VedirectConfig { - vedirect_enabled: boolean; - verbose_logging: boolean; - vedirect_updatesonly: boolean; -} diff --git a/webapp/src/views/VedirectInfoView.vue b/webapp/src/views/VedirectInfoView.vue deleted file mode 100644 index aff6da75f..000000000 --- a/webapp/src/views/VedirectInfoView.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - From d44d6493252cd3d3a95fc6bda8d5061a66fa36aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 20 Nov 2024 19:49:47 +0100 Subject: [PATCH 02/13] rename (almost) all occurences of vedirect to solarcharger --- include/Configuration.h | 16 ++- include/MqttHandleVedirect.h | 8 -- include/SolarChargerProvider.h | 9 ++ include/WebApi.h | 8 +- ...ebApi_vedirect.h => WebApi_solarcharger.h} | 10 +- include/WebApi_ws_live.h | 2 +- ...t_live.h => WebApi_ws_solarcharger_live.h} | 4 +- include/defaults.h | 7 +- src/Configuration.cpp | 36 ++++-- src/MqttHandlePowerLimiter.cpp | 4 +- src/MqttHandlePowerLimiterHass.cpp | 4 +- src/MqttHandleVedirect.cpp | 13 +- src/MqttHandleVedirectHass.cpp | 11 +- src/VictronMppt.cpp | 8 +- src/WebApi.cpp | 6 +- src/WebApi_powerlimiter.cpp | 3 +- src/WebApi_powermeter.cpp | 1 - src/WebApi_solar_charger.cpp | 88 +++++++++++++ src/WebApi_ws_live.cpp | 16 +-- ...ve.cpp => WebApi_ws_solarcharger_live.cpp} | 48 ++++---- webapp/src/components/InverterTotalInfo.vue | 34 ++--- webapp/src/components/NavBar.vue | 4 +- ...{VedirectView.vue => SolarChargerView.vue} | 54 ++++---- webapp/src/locales/de.json | 18 +-- webapp/src/locales/en.json | 18 +-- webapp/src/locales/fr.json | 61 +++------ webapp/src/router/index.ts | 8 +- webapp/src/types/LiveDataStatus.ts | 4 +- webapp/src/types/SolarChargerConfig.ts | 6 + ...tatus.ts => SolarChargerLiveDataStatus.ts} | 6 +- webapp/src/types/VedirectStatus.ts | 5 - webapp/src/views/HomeView.vue | 12 +- webapp/src/views/SolarChargerAdminView.vue | 116 ++++++++++++++++++ webapp/src/views/VedirectAdminView.vue | 103 ---------------- webapp/vite.config.ts | 2 +- 35 files changed, 429 insertions(+), 324 deletions(-) create mode 100644 include/SolarChargerProvider.h rename include/{WebApi_vedirect.h => WebApi_solarcharger.h} (53%) rename include/{WebApi_ws_vedirect_live.h => WebApi_ws_solarcharger_live.h} (93%) create mode 100644 src/WebApi_solar_charger.cpp rename src/{WebApi_ws_vedirect_live.cpp => WebApi_ws_solarcharger_live.cpp} (81%) rename webapp/src/components/{VedirectView.vue => SolarChargerView.vue} (82%) create mode 100644 webapp/src/types/SolarChargerConfig.ts rename webapp/src/types/{VedirectLiveDataStatus.ts => SolarChargerLiveDataStatus.ts} (72%) delete mode 100644 webapp/src/types/VedirectStatus.ts create mode 100644 webapp/src/views/SolarChargerAdminView.vue delete mode 100644 webapp/src/views/VedirectAdminView.vue diff --git a/include/Configuration.h b/include/Configuration.h index 54f912d2a..716432b62 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -196,6 +196,14 @@ struct BATTERY_CONFIG_T { }; using BatteryConfig = struct BATTERY_CONFIG_T; +struct SOLAR_CHARGER_CONFIG_T { + bool Enabled; + bool VerboseLogging; + uint8_t Provider; + bool PublishUpdatesOnly; +}; +using SolarChargerConfig = struct SOLAR_CHARGER_CONFIG_T; + struct CONFIG_T { struct { uint32_t Version; @@ -307,11 +315,7 @@ struct CONFIG_T { uint8_t Brightness; } Led_Single[PINMAPPING_LED_COUNT]; - struct { - bool Enabled; - bool VerboseLogging; - bool UpdatesOnly; - } Vedirect; + SolarChargerConfig SolarCharger; struct PowerMeterConfig { bool Enabled; @@ -373,6 +377,7 @@ class ConfigurationClass { void deleteInverterById(const uint8_t id); static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target); + static void serializeSolarChargerConfig(SolarChargerConfig const& source, JsonObject& target); static void serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target); static void serializePowerMeterSerialSdmConfig(PowerMeterSerialSdmConfig const& source, JsonObject& target); static void serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target); @@ -381,6 +386,7 @@ class ConfigurationClass { static void serializePowerLimiterConfig(PowerLimiterConfig const& source, JsonObject& target); static void deserializeHttpRequestConfig(JsonObject const& source_http_config, HttpRequestConfig& target); + static void deserializeSolarChargerConfig(JsonObject const& source, SolarChargerConfig& target); static void deserializePowerMeterMqttConfig(JsonObject const& source, PowerMeterMqttConfig& target); static void deserializePowerMeterSerialSdmConfig(JsonObject const& source, PowerMeterSerialSdmConfig& target); static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target); diff --git a/include/MqttHandleVedirect.h b/include/MqttHandleVedirect.h index 016ee804a..7e00df2cb 100644 --- a/include/MqttHandleVedirect.h +++ b/include/MqttHandleVedirect.h @@ -7,14 +7,6 @@ #include #include -#ifndef VICTRON_PIN_RX -#define VICTRON_PIN_RX 22 -#endif - -#ifndef VICTRON_PIN_TX -#define VICTRON_PIN_TX 21 -#endif - class MqttHandleVedirectClass { public: void init(Scheduler& scheduler); diff --git a/include/SolarChargerProvider.h b/include/SolarChargerProvider.h new file mode 100644 index 000000000..314694032 --- /dev/null +++ b/include/SolarChargerProvider.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +class SolarChargerProvider { +public: + enum class Type : unsigned { + VEDIRECT = 0 + }; +}; diff --git a/include/WebApi.h b/include/WebApi.h index 07ffaa22b..c6da12d93 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -27,8 +27,8 @@ #include "WebApi_ws_console.h" #include "WebApi_ws_live.h" #include -#include "WebApi_ws_vedirect_live.h" -#include "WebApi_vedirect.h" +#include "WebApi_ws_solarcharger_live.h" +#include "WebApi_solarcharger.h" #include "WebApi_ws_Huawei.h" #include "WebApi_Huawei.h" #include "WebApi_ws_battery.h" @@ -79,8 +79,8 @@ class WebApiClass { WebApiWebappClass _webApiWebapp; WebApiWsConsoleClass _webApiWsConsole; WebApiWsLiveClass _webApiWsLive; - WebApiWsVedirectLiveClass _webApiWsVedirectLive; - WebApiVedirectClass _webApiVedirect; + WebApiWsSolarChargerLiveClass _webApiWsSolarChargerLive; + WebApiSolarChargerlass _webApiSolarCharger; WebApiHuaweiClass _webApiHuaweiClass; WebApiWsHuaweiLiveClass _webApiWsHuaweiLive; WebApiWsBatteryLiveClass _webApiWsBatteryLive; diff --git a/include/WebApi_vedirect.h b/include/WebApi_solarcharger.h similarity index 53% rename from include/WebApi_vedirect.h rename to include/WebApi_solarcharger.h index dd4ec90ac..38d155683 100644 --- a/include/WebApi_vedirect.h +++ b/include/WebApi_solarcharger.h @@ -5,14 +5,14 @@ #include -class WebApiVedirectClass { +class WebApiSolarChargerlass { public: void init(AsyncWebServer& server, Scheduler& scheduler); private: - void onVedirectStatus(AsyncWebServerRequest* request); - void onVedirectAdminGet(AsyncWebServerRequest* request); - void onVedirectAdminPost(AsyncWebServerRequest* request); + void onStatus(AsyncWebServerRequest* request); + void onAdminGet(AsyncWebServerRequest* request); + void onAdminPost(AsyncWebServerRequest* request); AsyncWebServer* _server; -}; \ No newline at end of file +}; diff --git a/include/WebApi_ws_live.h b/include/WebApi_ws_live.h index e02f9a8c1..88654edd9 100644 --- a/include/WebApi_ws_live.h +++ b/include/WebApi_ws_live.h @@ -31,7 +31,7 @@ class WebApiWsLiveClass { AuthenticationMiddleware _simpleDigestAuth; uint32_t _lastPublishOnBatteryFull = 0; - uint32_t _lastPublishVictron = 0; + uint32_t _lastPublishSolarCharger = 0; uint32_t _lastPublishHuawei = 0; uint32_t _lastPublishBattery = 0; uint32_t _lastPublishPowerMeter = 0; diff --git a/include/WebApi_ws_vedirect_live.h b/include/WebApi_ws_solarcharger_live.h similarity index 93% rename from include/WebApi_ws_vedirect_live.h rename to include/WebApi_ws_solarcharger_live.h index 7c3bedf60..b49800b1f 100644 --- a/include/WebApi_ws_vedirect_live.h +++ b/include/WebApi_ws_solarcharger_live.h @@ -8,9 +8,9 @@ #include #include -class WebApiWsVedirectLiveClass { +class WebApiWsSolarChargerLiveClass { public: - WebApiWsVedirectLiveClass(); + WebApiWsSolarChargerLiveClass(); void init(AsyncWebServer& server, Scheduler& scheduler); void reload(); diff --git a/include/defaults.h b/include/defaults.h index 7d8a50a16..add5437cd 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -115,9 +115,10 @@ #define LANG_PACK_SUFFIX ".lang.json" // values specific to downstream project OpenDTU-OnBattery start here: -#define VEDIRECT_ENABLED false -#define VEDIRECT_VERBOSE_LOGGING false -#define VEDIRECT_UPDATESONLY true +#define SOLAR_CHARGER_ENABLED false +#define SOLAR_CHARGER_VERBOSE_LOGGING false +#define SOLAR_CHARGER_PROVIDER 0 // Victron MPPT(s) via Ve.Direct +#define SOLAR_CHARGER_PUBLISH_UPDATES_ONLY true #define POWERMETER_ENABLED false #define POWERMETER_POLLING_INTERVAL 10 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index facf0ab7b..6961f0d79 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -38,6 +38,14 @@ void ConfigurationClass::serializeHttpRequestConfig(HttpRequestConfig const& sou target_http_config["timeout"] = source.Timeout; } +void ConfigurationClass::serializeSolarChargerConfig(SolarChargerConfig const& source, JsonObject& target) +{ + target["enabled"] = source.Enabled; + target["verbose_logging"] = source.VerboseLogging; + target["provider"] = source.Provider; + target["publish_updates_only"] = source.PublishUpdatesOnly; +} + void ConfigurationClass::serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target) { JsonArray values = target["values"].to(); @@ -297,10 +305,8 @@ bool ConfigurationClass::write() } } - JsonObject vedirect = doc["vedirect"].to(); - vedirect["enabled"] = config.Vedirect.Enabled; - vedirect["verbose_logging"] = config.Vedirect.VerboseLogging; - vedirect["updates_only"] = config.Vedirect.UpdatesOnly; + JsonObject solarcharger = doc["solarcharger"].to(); + serializeSolarChargerConfig(config.SolarCharger, solarcharger); JsonObject powermeter = doc["powermeter"].to(); powermeter["enabled"] = config.PowerMeter.Enabled; @@ -364,6 +370,14 @@ void ConfigurationClass::deserializeHttpRequestConfig(JsonObject const& source_h target.Timeout = source_http_config["timeout"] | HTTP_REQUEST_TIMEOUT_MS; } +void ConfigurationClass::deserializeSolarChargerConfig(JsonObject const& source, SolarChargerConfig& target) +{ + target.Enabled = source["enabled"] | SOLAR_CHARGER_ENABLED; + target.VerboseLogging = source["verbose_logging"] | VERBOSE_LOGGING; + target.Provider = source["provider"] | SOLAR_CHARGER_PROVIDER; + target.PublishUpdatesOnly = source["publish_updates_only"] | SOLAR_CHARGER_PUBLISH_UPDATES_ONLY; +} + void ConfigurationClass::deserializePowerMeterMqttConfig(JsonObject const& source, PowerMeterMqttConfig& target) { for (size_t i = 0; i < POWERMETER_MQTT_MAX_VALUES; ++i) { @@ -654,10 +668,16 @@ bool ConfigurationClass::read() } } - JsonObject vedirect = doc["vedirect"]; - config.Vedirect.Enabled = vedirect["enabled"] | VEDIRECT_ENABLED; - config.Vedirect.VerboseLogging = vedirect["verbose_logging"] | VEDIRECT_VERBOSE_LOGGING; - config.Vedirect.UpdatesOnly = vedirect["updates_only"] | VEDIRECT_UPDATESONLY; + deserializeSolarChargerConfig(doc["solarcharger"], config.SolarCharger); + + // process settings from legacy config if they are present + // TODO(andreasboehm): remove end of 2025. + if (!doc["vedirect"].isNull()) { + JsonObject vedirect = doc["vedirect"]; + config.SolarCharger.Enabled = vedirect["enabled"] | SOLAR_CHARGER_ENABLED; + config.SolarCharger.VerboseLogging = vedirect["verbose_logging"] | SOLAR_CHARGER_VERBOSE_LOGGING; + config.SolarCharger.PublishUpdatesOnly = vedirect["updates_only"] | SOLAR_CHARGER_PUBLISH_UPDATES_ONLY; + } JsonObject powermeter = doc["powermeter"]; config.PowerMeter.Enabled = powermeter["enabled"] | POWERMETER_ENABLED; diff --git a/src/MqttHandlePowerLimiter.cpp b/src/MqttHandlePowerLimiter.cpp index d4861e629..74b9874f6 100644 --- a/src/MqttHandlePowerLimiter.cpp +++ b/src/MqttHandlePowerLimiter.cpp @@ -100,7 +100,7 @@ void MqttHandlePowerLimiterClass::loop() MqttSettings.publish("powerlimiter/status/threshold/voltage/start", String(config.PowerLimiter.VoltageStartThreshold)); MqttSettings.publish("powerlimiter/status/threshold/voltage/stop", String(config.PowerLimiter.VoltageStopThreshold)); - if (config.Vedirect.Enabled) { + if (config.SolarCharger.Enabled) { MqttSettings.publish("powerlimiter/status/full_solar_passthrough_active", String(PowerLimiter.getFullSolarPassThroughEnabled())); MqttSettings.publish("powerlimiter/status/threshold/voltage/full_solar_passthrough_start", String(config.PowerLimiter.FullSolarPassThroughStartVoltage)); MqttSettings.publish("powerlimiter/status/threshold/voltage/full_solar_passthrough_stop", String(config.PowerLimiter.FullSolarPassThroughStopVoltage)); @@ -111,7 +111,7 @@ void MqttHandlePowerLimiterClass::loop() MqttSettings.publish("powerlimiter/status/threshold/soc/start", String(config.PowerLimiter.BatterySocStartThreshold)); MqttSettings.publish("powerlimiter/status/threshold/soc/stop", String(config.PowerLimiter.BatterySocStopThreshold)); - if (config.Vedirect.Enabled) { + if (config.SolarCharger.Enabled) { MqttSettings.publish("powerlimiter/status/threshold/soc/full_solar_passthrough", String(config.PowerLimiter.FullSolarPassThroughSoc)); } } diff --git a/src/MqttHandlePowerLimiterHass.cpp b/src/MqttHandlePowerLimiterHass.cpp index 43642f9fb..7187df946 100644 --- a/src/MqttHandlePowerLimiterHass.cpp +++ b/src/MqttHandlePowerLimiterHass.cpp @@ -75,7 +75,7 @@ void MqttHandlePowerLimiterHassClass::publishConfig() publishNumber("DPL battery voltage stop threshold", "mdi:battery-charging", "config", "threshold/voltage/stop", "threshold/voltage/stop", "V", 16, 60, 0.1); - if (config.Vedirect.Enabled) { + if (config.SolarCharger.Enabled) { publishBinarySensor("full solar passthrough active", "mdi:transmission-tower-import", "full_solar_passthrough_active", "1", "0"); @@ -96,7 +96,7 @@ void MqttHandlePowerLimiterHassClass::publishConfig() publishNumber("DPL battery SoC stop threshold", "mdi:battery-charging", "config", "threshold/soc/stop", "threshold/soc/stop", "%", 0, 100, 1.0); - if (config.Vedirect.Enabled) { + if (config.SolarCharger.Enabled) { publishNumber("DPL full solar passthrough SoC", "mdi:transmission-tower-import", "config", "threshold/soc/full_solar_passthrough", diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 55334df50..5c2c75132 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -6,9 +6,7 @@ #include "MqttHandleVedirect.h" #include "MqttSettings.h" #include "MessageOutput.h" - - - +#include "SolarChargerProvider.h" MqttHandleVedirectClass MqttHandleVedirect; @@ -36,8 +34,9 @@ void MqttHandleVedirectClass::forceUpdate() void MqttHandleVedirectClass::loop() { auto const& config = Configuration.get(); - - if (!MqttSettings.getConnected() || !config.Vedirect.Enabled) { + if (!MqttSettings.getConnected() + || !config.SolarCharger.Enabled + || static_cast(config.SolarCharger.Provider) != SolarChargerProvider::Type::VEDIRECT) { return; } @@ -46,7 +45,7 @@ void MqttHandleVedirectClass::loop() if (_nextPublishFull <= _nextPublishUpdatesOnly) { _PublishFull = true; } else { - _PublishFull = !config.Vedirect.UpdatesOnly; + _PublishFull = !config.SolarCharger.PublishUpdatesOnly; } #ifdef MQTTHANDLEVEDIRECT_DEBUG @@ -76,7 +75,7 @@ void MqttHandleVedirectClass::loop() // when Home Assistant MQTT-Auto-Discovery is active, // and "enable expiration" is active, all values must be published at // least once before the announced expiry interval is reached - if ((config.Vedirect.UpdatesOnly) && (config.Mqtt.Hass.Enabled) && (config.Mqtt.Hass.Expire)) { + if ((config.SolarCharger.PublishUpdatesOnly) && (config.Mqtt.Hass.Enabled) && (config.Mqtt.Hass.Expire)) { _nextPublishFull = millis() + (((config.Mqtt.PublishInterval * 3) - 1) * 1000); #ifdef MQTTHANDLEVEDIRECT_DEBUG diff --git a/src/MqttHandleVedirectHass.cpp b/src/MqttHandleVedirectHass.cpp index 279c258d7..03bf1f7b2 100644 --- a/src/MqttHandleVedirectHass.cpp +++ b/src/MqttHandleVedirectHass.cpp @@ -8,6 +8,7 @@ #include "MqttHandleHass.h" #include "NetworkSettings.h" #include "MessageOutput.h" +#include "SolarChargerProvider.h" #include "VictronMppt.h" #include "Utils.h" #include "__compiled_constants.h" @@ -24,9 +25,12 @@ void MqttHandleVedirectHassClass::init(Scheduler& scheduler) void MqttHandleVedirectHassClass::loop() { - if (!Configuration.get().Vedirect.Enabled) { + if (!Configuration.get().Mqtt.Hass.Enabled + || !Configuration.get().SolarCharger.Enabled + || static_cast(Configuration.get().SolarCharger.Provider) != SolarChargerProvider::Type::VEDIRECT) { return; } + if (_updateForced) { publishConfig(); _updateForced = false; @@ -49,11 +53,6 @@ void MqttHandleVedirectHassClass::forceUpdate() void MqttHandleVedirectHassClass::publishConfig() { - if ((!Configuration.get().Mqtt.Hass.Enabled) || - (!Configuration.get().Vedirect.Enabled)) { - return; - } - if (!MqttSettings.getConnected()) { return; } diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index 86373f453..b84f8e205 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -28,18 +28,18 @@ void VictronMpptClass::updateSettings() _serialPortOwners.clear(); auto const& config = Configuration.get(); - if (!config.Vedirect.Enabled) { return; } + if (!config.SolarCharger.Enabled) { return; } const PinMapping_t& pin = PinMapping.get(); initController(pin.victron_rx, pin.victron_tx, - config.Vedirect.VerboseLogging, 1); + config.SolarCharger.VerboseLogging, 1); initController(pin.victron_rx2, pin.victron_tx2, - config.Vedirect.VerboseLogging, 2); + config.SolarCharger.VerboseLogging, 2); initController(pin.victron_rx3, pin.victron_tx3, - config.Vedirect.VerboseLogging, 3); + config.SolarCharger.VerboseLogging, 3); } bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 08026c4ec..9e869a52e 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -39,8 +39,8 @@ void WebApiClass::init(Scheduler& scheduler) _webApiBattery.init(_server, scheduler); _webApiPowerMeter.init(_server, scheduler); _webApiPowerLimiter.init(_server, scheduler); - _webApiWsVedirectLive.init(_server, scheduler); - _webApiVedirect.init(_server, scheduler); + _webApiWsSolarChargerLive.init(_server, scheduler); + _webApiSolarCharger.init(_server, scheduler); _webApiWsHuaweiLive.init(_server, scheduler); _webApiHuaweiClass.init(_server, scheduler); _webApiWsBatteryLive.init(_server, scheduler); @@ -53,7 +53,7 @@ void WebApiClass::reload() _webApiWsConsole.reload(); _webApiWsLive.reload(); _webApiWsBatteryLive.reload(); - _webApiWsVedirectLive.reload(); + _webApiWsSolarChargerLive.reload(); _webApiWsHuaweiLive.reload(); } diff --git a/src/WebApi_powerlimiter.cpp b/src/WebApi_powerlimiter.cpp index 8301c738b..ada1812a1 100644 --- a/src/WebApi_powerlimiter.cpp +++ b/src/WebApi_powerlimiter.cpp @@ -3,7 +3,6 @@ * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_powerlimiter.h" -#include "VeDirectFrameHandler.h" #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" @@ -50,7 +49,7 @@ void WebApiPowerLimiterClass::onMetaData(AsyncWebServerRequest* request) root["power_meter_enabled"] = config.PowerMeter.Enabled; root["battery_enabled"] = config.Battery.Enabled; - root["charge_controller_enabled"] = config.Vedirect.Enabled; + root["charge_controller_enabled"] = config.SolarCharger.Enabled; JsonArray inverters = root["inverters"].to(); for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { diff --git a/src/WebApi_powermeter.cpp b/src/WebApi_powermeter.cpp index 911bcd398..e691ad79d 100644 --- a/src/WebApi_powermeter.cpp +++ b/src/WebApi_powermeter.cpp @@ -3,7 +3,6 @@ * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_powermeter.h" -#include "VeDirectFrameHandler.h" #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" diff --git a/src/WebApi_solar_charger.cpp b/src/WebApi_solar_charger.cpp new file mode 100644 index 000000000..0695fd911 --- /dev/null +++ b/src/WebApi_solar_charger.cpp @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2022-2024 Thomas Basler and others + */ +#include "WebApi_solarcharger.h" +#include "VictronMppt.h" +#include "ArduinoJson.h" +#include "AsyncJson.h" +#include "Configuration.h" +#include "WebApi.h" +#include "WebApi_errors.h" +#include "helper.h" +#include "MqttHandlePowerLimiterHass.h" + +void WebApiSolarChargerlass::init(AsyncWebServer& server, Scheduler& scheduler) +{ + using std::placeholders::_1; + + _server = &server; + + _server->on("/api/solarcharger/status", HTTP_GET, std::bind(&WebApiSolarChargerlass::onStatus, this, _1)); + _server->on("/api/solarcharger/config", HTTP_GET, std::bind(&WebApiSolarChargerlass::onAdminGet, this, _1)); + _server->on("/api/solarcharger/config", HTTP_POST, std::bind(&WebApiSolarChargerlass::onAdminPost, this, _1)); +} + +void WebApiSolarChargerlass::onStatus(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentialsReadonly(request)) { + return; + } + + AsyncJsonResponse* response = new AsyncJsonResponse(); + auto root = response->getRoot().as(); + auto const& config = Configuration.get(); + + ConfigurationClass::serializeSolarChargerConfig(config.SolarCharger, root); + + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); +} + +void WebApiSolarChargerlass::onAdminGet(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentials(request)) { + return; + } + + onStatus(request); +} + +void WebApiSolarChargerlass::onAdminPost(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentials(request)) { + return; + } + + AsyncJsonResponse* response = new AsyncJsonResponse(); + JsonDocument root; + if (!WebApi.parseRequestData(request, response, root)) { + return; + } + + auto& retMsg = response->getRoot(); + + if (!root["enabled"].is() || + !root["provider"].is() || + !root["verbose_logging"].is() || + !root["updates_only"].is() ) { + retMsg["message"] = "Values are missing!"; + retMsg["code"] = WebApiError::GenericValueMissing; + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + return; + } + + { + auto guard = Configuration.getWriteGuard(); + auto& config = guard.getConfig(); + ConfigurationClass::deserializeSolarChargerConfig(root.as(), config.SolarCharger); + } + + WebApi.writeConfig(retMsg); + + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + + VictronMppt.updateSettings(); + + // potentially make solar passthrough thresholds auto-discoverable + MqttHandlePowerLimiterHass.forceUpdate(); +} diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 36031a357..6857dd584 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -72,19 +72,19 @@ void WebApiWsLiveClass::generateOnBatteryJsonResponse(JsonVariant& root, bool al auto const& config = Configuration.get(); auto constexpr halfOfAllMillis = std::numeric_limits::max() / 2; - auto victronAge = VictronMppt.getDataAgeMillis(); - if (all || (victronAge > 0 && (millis() - _lastPublishVictron) > victronAge)) { - auto vedirectObj = root["vedirect"].to(); - vedirectObj["enabled"] = config.Vedirect.Enabled; + auto solarChargerAge = VictronMppt.getDataAgeMillis(); + if (all || (solarChargerAge > 0 && (millis() - _lastPublishSolarCharger) > solarChargerAge)) { + auto solarchargerObj = root["solarcharger"].to(); + solarchargerObj["solarcharger"] = config.SolarCharger.Enabled; - if (config.Vedirect.Enabled) { - auto totalVeObj = vedirectObj["total"].to(); + if (config.SolarCharger.Enabled) { + auto totalVeObj = solarchargerObj["total"].to(); addTotalField(totalVeObj, "Power", VictronMppt.getPanelPowerWatts(), "W", 1); addTotalField(totalVeObj, "YieldDay", VictronMppt.getYieldDay() * 1000, "Wh", 0); addTotalField(totalVeObj, "YieldTotal", VictronMppt.getYieldTotal(), "kWh", 2); } - if (!all) { _lastPublishVictron = millis(); } + if (!all) { _lastPublishSolarCharger = millis(); } } if (all || (HuaweiCan.getLastUpdate() - _lastPublishHuawei) < halfOfAllMillis ) { @@ -370,7 +370,7 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request) generateOnBatteryJsonResponse(root, true); WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); - + } catch (const std::bad_alloc& bad_alloc) { MessageOutput.printf("Calling /api/livedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); WebApi.sendTooManyRequests(request); diff --git a/src/WebApi_ws_vedirect_live.cpp b/src/WebApi_ws_solarcharger_live.cpp similarity index 81% rename from src/WebApi_ws_vedirect_live.cpp rename to src/WebApi_ws_solarcharger_live.cpp index 5189d0b3a..a810a7b38 100644 --- a/src/WebApi_ws_vedirect_live.cpp +++ b/src/WebApi_ws_solarcharger_live.cpp @@ -2,7 +2,7 @@ /* * Copyright (C) 2022-2024 Thomas Basler and others */ -#include "WebApi_ws_vedirect_live.h" +#include "WebApi_ws_solarcharger_live.h" #include "AsyncJson.h" #include "Configuration.h" #include "MessageOutput.h" @@ -12,12 +12,12 @@ #include "PowerLimiter.h" #include "VictronMppt.h" -WebApiWsVedirectLiveClass::WebApiWsVedirectLiveClass() - : _ws("/vedirectlivedata") +WebApiWsSolarChargerLiveClass::WebApiWsSolarChargerLiveClass() + : _ws("/solarchargerlivedata") { } -void WebApiWsVedirectLiveClass::init(AsyncWebServer& server, Scheduler& scheduler) +void WebApiWsSolarChargerLiveClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; using std::placeholders::_2; @@ -27,31 +27,31 @@ void WebApiWsVedirectLiveClass::init(AsyncWebServer& server, Scheduler& schedule using std::placeholders::_6; _server = &server; - _server->on("/api/vedirectlivedata/status", HTTP_GET, std::bind(&WebApiWsVedirectLiveClass::onLivedataStatus, this, _1)); + _server->on("/api/solarchargerlivedata/status", HTTP_GET, std::bind(&WebApiWsSolarChargerLiveClass::onLivedataStatus, this, _1)); _server->addHandler(&_ws); - _ws.onEvent(std::bind(&WebApiWsVedirectLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); + _ws.onEvent(std::bind(&WebApiWsSolarChargerLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); scheduler.addTask(_wsCleanupTask); - _wsCleanupTask.setCallback(std::bind(&WebApiWsVedirectLiveClass::wsCleanupTaskCb, this)); + _wsCleanupTask.setCallback(std::bind(&WebApiWsSolarChargerLiveClass::wsCleanupTaskCb, this)); _wsCleanupTask.setIterations(TASK_FOREVER); _wsCleanupTask.setInterval(1 * TASK_SECOND); _wsCleanupTask.enable(); scheduler.addTask(_sendDataTask); - _sendDataTask.setCallback(std::bind(&WebApiWsVedirectLiveClass::sendDataTaskCb, this)); + _sendDataTask.setCallback(std::bind(&WebApiWsSolarChargerLiveClass::sendDataTaskCb, this)); _sendDataTask.setIterations(TASK_FOREVER); _sendDataTask.setInterval(500 * TASK_MILLISECOND); _sendDataTask.enable(); _simpleDigestAuth.setUsername(AUTH_USERNAME); - _simpleDigestAuth.setRealm("vedirect websocket"); + _simpleDigestAuth.setRealm("solarcharger websocket"); reload(); } -void WebApiWsVedirectLiveClass::reload() +void WebApiWsSolarChargerLiveClass::reload() { _ws.removeMiddleware(&_simpleDigestAuth); @@ -66,13 +66,13 @@ void WebApiWsVedirectLiveClass::reload() _ws.enable(true); } -void WebApiWsVedirectLiveClass::wsCleanupTaskCb() +void WebApiWsSolarChargerLiveClass::wsCleanupTaskCb() { // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients _ws.cleanupClients(); } -bool WebApiWsVedirectLiveClass::hasUpdate(size_t idx) +bool WebApiWsSolarChargerLiveClass::hasUpdate(size_t idx) { auto dataAgeMillis = VictronMppt.getDataAgeMillis(idx); if (dataAgeMillis == 0) { return false; } @@ -80,13 +80,13 @@ bool WebApiWsVedirectLiveClass::hasUpdate(size_t idx) return dataAgeMillis < publishAgeMillis; } -uint16_t WebApiWsVedirectLiveClass::responseSize() const +uint16_t WebApiWsSolarChargerLiveClass::responseSize() const { // estimated with ArduinoJson assistant return VictronMppt.controllerAmount() * (1024 + 512) + 128/*DPL status and structure*/; } -void WebApiWsVedirectLiveClass::sendDataTaskCb() +void WebApiWsSolarChargerLiveClass::sendDataTaskCb() { // do nothing if no WS client is connected if (_ws.count() == 0) { return; } @@ -118,9 +118,9 @@ void WebApiWsVedirectLiveClass::sendDataTaskCb() _ws.textAll(buffer);; } } catch (std::bad_alloc& bad_alloc) { - MessageOutput.printf("Calling /api/vedirectlivedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + MessageOutput.printf("Calling /api/solarchargerlivedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); } catch (const std::exception& exc) { - MessageOutput.printf("Unknown exception in /api/vedirectlivedata/status. Reason: \"%s\".\r\n", exc.what()); + MessageOutput.printf("Unknown exception in /api/solarchargerlivedata/status. Reason: \"%s\".\r\n", exc.what()); } } @@ -129,10 +129,10 @@ void WebApiWsVedirectLiveClass::sendDataTaskCb() } } -void WebApiWsVedirectLiveClass::generateCommonJsonResponse(JsonVariant& root, bool fullUpdate) +void WebApiWsSolarChargerLiveClass::generateCommonJsonResponse(JsonVariant& root, bool fullUpdate) { - auto array = root["vedirect"]["instances"].to(); - root["vedirect"]["full_update"] = fullUpdate; + auto array = root["solarcharger"]["instances"].to(); + root["solarcharger"]["full_update"] = fullUpdate; for (size_t idx = 0; idx < VictronMppt.controllerAmount(); ++idx) { auto optMpptData = VictronMppt.getData(idx); @@ -157,7 +157,7 @@ void WebApiWsVedirectLiveClass::generateCommonJsonResponse(JsonVariant& root, bo root["dpl"]["PLLIMIT"] = PowerLimiter.getInverterOutput(); } -void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDirectMpptController::data_t &mpptData) { +void WebApiWsSolarChargerLiveClass::populateJson(const JsonObject &root, const VeDirectMpptController::data_t &mpptData) { root["product_id"] = mpptData.getPidAsString(); root["firmware_version"] = mpptData.getFwVersionFormatted(); @@ -254,7 +254,7 @@ void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDir input["MaximumPowerYesterday"]["d"] = 0; } -void WebApiWsVedirectLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) +void WebApiWsSolarChargerLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { if (type == WS_EVT_CONNECT) { char str[64]; @@ -269,7 +269,7 @@ void WebApiWsVedirectLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWe } } -void WebApiWsVedirectLiveClass::onLivedataStatus(AsyncWebServerRequest* request) +void WebApiWsSolarChargerLiveClass::onLivedataStatus(AsyncWebServerRequest* request) { if (!WebApi.checkCredentialsReadonly(request)) { return; @@ -283,10 +283,10 @@ void WebApiWsVedirectLiveClass::onLivedataStatus(AsyncWebServerRequest* request) WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); } catch (std::bad_alloc& bad_alloc) { - MessageOutput.printf("Calling /api/vedirectlivedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + MessageOutput.printf("Calling /api/solarcharger/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); WebApi.sendTooManyRequests(request); } catch (const std::exception& exc) { - MessageOutput.printf("Unknown exception in /api/vedirectlivedata/status. Reason: \"%s\".\r\n", exc.what()); + MessageOutput.printf("Unknown exception in /api/solarcharger/status. Reason: \"%s\".\r\n", exc.what()); WebApi.sendTooManyRequests(request); } } diff --git a/webapp/src/components/InverterTotalInfo.vue b/webapp/src/components/InverterTotalInfo.vue index 6cb609e7d..5c081c6f1 100644 --- a/webapp/src/components/InverterTotalInfo.vue +++ b/webapp/src/components/InverterTotalInfo.vue @@ -6,7 +6,7 @@
    -
    +

    {{ - $n(totalVeData.total.YieldTotal.v, 'decimal', { - minimumFractionDigits: totalVeData.total.YieldTotal.d, - maximumFractionDigits: totalVeData.total.YieldTotal.d, + $n(solarChargerData.total.YieldTotal.v, 'decimal', { + minimumFractionDigits: solarChargerData.total.YieldTotal.d, + maximumFractionDigits: solarChargerData.total.YieldTotal.d, }) }} - {{ totalVeData.total.YieldTotal.u }} + {{ solarChargerData.total.YieldTotal.u }}

    -
    +

    {{ - $n(totalVeData.total.YieldDay.v, 'decimal', { - minimumFractionDigits: totalVeData.total.YieldDay.d, - maximumFractionDigits: totalVeData.total.YieldDay.d, + $n(solarChargerData.total.YieldDay.v, 'decimal', { + minimumFractionDigits: solarChargerData.total.YieldDay.d, + maximumFractionDigits: solarChargerData.total.YieldDay.d, }) }} - {{ totalVeData.total.YieldDay.u }} + {{ solarChargerData.total.YieldDay.u }}

    -
    +

    {{ - $n(totalVeData.total.Power.v, 'decimal', { - minimumFractionDigits: totalVeData.total.Power.d, - maximumFractionDigits: totalVeData.total.Power.d, + $n(solarChargerData.total.Power.v, 'decimal', { + minimumFractionDigits: solarChargerData.total.Power.d, + maximumFractionDigits: solarChargerData.total.Power.d, }) }} - {{ totalVeData.total.Power.u }} + {{ solarChargerData.total.Power.u }}

    @@ -194,7 +194,7 @@ diff --git a/webapp/src/views/VedirectAdminView.vue b/webapp/src/views/VedirectAdminView.vue deleted file mode 100644 index 314babe7f..000000000 --- a/webapp/src/views/VedirectAdminView.vue +++ /dev/null @@ -1,103 +0,0 @@ - - - diff --git a/webapp/vite.config.ts b/webapp/vite.config.ts index 545236a0b..f071c0702 100644 --- a/webapp/vite.config.ts +++ b/webapp/vite.config.ts @@ -71,7 +71,7 @@ export default defineConfig(({ command }) => { return { ws: true, changeOrigin: true }, - '^/vedirectlivedata': { + '^/solarchargerlivedata': { target: 'ws://' + proxy_target, ws: true, changeOrigin: true From 51372cb2414f865eee49d8e223a2494db7f47d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 20 Nov 2024 20:47:23 +0100 Subject: [PATCH 03/13] wrap VictronMppt in SolarCharger --- include/SolarCharger.h | 48 ++++++++ include/SolarChargerProvider.h | 31 +++++ include/VictronMppt.h | 42 +++---- src/MqttHandleVedirect.cpp | 6 +- src/MqttHandleVedirectHass.cpp | 6 +- src/PowerLimiter.cpp | 14 +-- src/SolarCharger.cpp | 168 ++++++++++++++++++++++++++++ src/VictronMppt.cpp | 74 +++++------- src/WebApi_solar_charger.cpp | 4 +- src/WebApi_ws_live.cpp | 10 +- src/WebApi_ws_solarcharger_live.cpp | 14 +-- src/main.cpp | 5 +- 12 files changed, 320 insertions(+), 102 deletions(-) create mode 100644 include/SolarCharger.h create mode 100644 src/SolarCharger.cpp diff --git a/include/SolarCharger.h b/include/SolarCharger.h new file mode 100644 index 000000000..2fcdfc0e3 --- /dev/null +++ b/include/SolarCharger.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include +#include +#include +#include "SolarChargerProvider.h" +#include "VeDirectMpptController.h" + +class SolarChargerClass { +public: + void init(Scheduler&); + void updateSettings(); + + // TODO(andreasboehm): below methods are taken from VictronMppt to start abstracting + // solar chargers without breaking everything. + size_t controllerAmount(); + uint32_t getDataAgeMillis(); + uint32_t getDataAgeMillis(size_t idx); + + // total output of all MPPT charge controllers in Watts + int32_t getPowerOutputWatts(); + + // total panel input power of all MPPT charge controllers in Watts + int32_t getPanelPowerWatts(); + + // sum of total yield of all MPPT charge controllers in kWh + float getYieldTotal(); + + // sum of today's yield of all MPPT charge controllers in kWh + float getYieldDay(); + + // minimum of all MPPT charge controllers' output voltages in V + float getOutputVoltage(); + + std::optional getData(size_t idx = 0); + + bool isDataValid(); + +private: + void loop(); + + Task _loopTask; + mutable std::mutex _mutex; + std::unique_ptr _upProvider = nullptr; +}; + +extern SolarChargerClass SolarCharger; diff --git a/include/SolarChargerProvider.h b/include/SolarChargerProvider.h index 314694032..237d249ae 100644 --- a/include/SolarChargerProvider.h +++ b/include/SolarChargerProvider.h @@ -1,9 +1,40 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "VeDirectMpptController.h" + class SolarChargerProvider { public: enum class Type : unsigned { VEDIRECT = 0 }; + + // returns true if the provider is ready for use, false otherwise + virtual bool init(bool verboseLogging) = 0; + virtual void deinit() = 0; + virtual void loop() = 0; + + // TODO(andreasboehm): below methods are taken from VictronMppt to start abstracting + // solar chargers without breaking everything. + virtual size_t controllerAmount() = 0; + virtual uint32_t getDataAgeMillis() = 0; + virtual uint32_t getDataAgeMillis(size_t idx) = 0; + // total output of all MPPT charge controllers in Watts + virtual int32_t getPowerOutputWatts() = 0; + + // total panel input power of all MPPT charge controllers in Watts + virtual int32_t getPanelPowerWatts() = 0; + + // sum of total yield of all MPPT charge controllers in kWh + virtual float getYieldTotal() = 0; + + // sum of today's yield of all MPPT charge controllers in kWh + virtual float getYieldDay() = 0; + + // minimum of all MPPT charge controllers' output voltages in V + virtual float getOutputVoltage() = 0; + + virtual std::optional getData(size_t idx = 0) = 0; + + virtual bool isDataValid() = 0; }; diff --git a/include/VictronMppt.h b/include/VictronMppt.h index e79564e87..7a98c2c81 100644 --- a/include/VictronMppt.h +++ b/include/VictronMppt.h @@ -5,42 +5,40 @@ #include #include "VeDirectMpptController.h" +#include "SolarChargerProvider.h" #include "Configuration.h" #include -class VictronMpptClass { +class VictronMppt : public SolarChargerProvider { public: - VictronMpptClass() = default; - ~VictronMpptClass() = default; + bool init(bool verboseLogging) final; + void deinit() final; + void loop() final; - void init(Scheduler& scheduler); - void updateSettings(); - - bool isDataValid() const; - bool isDataValid(size_t idx) const; + bool isDataValid() final; // returns the data age of all controllers, // i.e, the youngest data's age is returned. - uint32_t getDataAgeMillis() const; - uint32_t getDataAgeMillis(size_t idx) const; + uint32_t getDataAgeMillis() final; + uint32_t getDataAgeMillis(size_t idx) final; - size_t controllerAmount() const { return _controllers.size(); } - std::optional getData(size_t idx = 0) const; + size_t controllerAmount() final { return _controllers.size(); } + std::optional getData(size_t idx = 0) final; // total output of all MPPT charge controllers in Watts - int32_t getPowerOutputWatts() const; + int32_t getPowerOutputWatts() final; // total panel input power of all MPPT charge controllers in Watts - int32_t getPanelPowerWatts() const; + int32_t getPanelPowerWatts() final; // sum of total yield of all MPPT charge controllers in kWh - float getYieldTotal() const; + float getYieldTotal() final; // sum of today's yield of all MPPT charge controllers in kWh - float getYieldDay() const; + float getYieldDay() final; // minimum of all MPPT charge controllers' output voltages in V - float getOutputVoltage() const; + float getOutputVoltage() final; // returns the state of operation from the first available controller std::optional getStateOfOperation() const; @@ -54,14 +52,6 @@ class VictronMpptClass { std::optional getVoltage(MPPTVoltage kindOf) const; private: - void loop(); - VictronMpptClass(VictronMpptClass const& other) = delete; - VictronMpptClass(VictronMpptClass&& other) = delete; - VictronMpptClass& operator=(VictronMpptClass const& other) = delete; - VictronMpptClass& operator=(VictronMpptClass&& other) = delete; - - Task _loopTask; - mutable std::mutex _mutex; using controller_t = std::unique_ptr; std::vector _controllers; @@ -70,5 +60,3 @@ class VictronMpptClass { bool initController(int8_t rx, int8_t tx, bool logging, uint8_t instance); }; - -extern VictronMpptClass VictronMppt; diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 5c2c75132..4a8e8f8ac 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -2,11 +2,11 @@ /* * Copyright (C) 2022 Helge Erbe and others */ -#include "VictronMppt.h" #include "MqttHandleVedirect.h" #include "MqttSettings.h" #include "MessageOutput.h" #include "SolarChargerProvider.h" +#include MqttHandleVedirectClass MqttHandleVedirect; @@ -57,8 +57,8 @@ void MqttHandleVedirectClass::loop() } #endif - for (int idx = 0; idx < VictronMppt.controllerAmount(); ++idx) { - std::optional optMpptData = VictronMppt.getData(idx); + for (int idx = 0; idx < SolarCharger.controllerAmount(); ++idx) { + std::optional optMpptData = SolarCharger.getData(idx); if (!optMpptData.has_value()) { continue; } auto const& kvFrame = _kvFrames[optMpptData->serialNr_SER]; diff --git a/src/MqttHandleVedirectHass.cpp b/src/MqttHandleVedirectHass.cpp index 03bf1f7b2..fc02fa871 100644 --- a/src/MqttHandleVedirectHass.cpp +++ b/src/MqttHandleVedirectHass.cpp @@ -9,9 +9,9 @@ #include "NetworkSettings.h" #include "MessageOutput.h" #include "SolarChargerProvider.h" -#include "VictronMppt.h" #include "Utils.h" #include "__compiled_constants.h" +#include MqttHandleVedirectHassClass MqttHandleVedirectHass; @@ -58,8 +58,8 @@ void MqttHandleVedirectHassClass::publishConfig() } // device info - for (int idx = 0; idx < VictronMppt.controllerAmount(); ++idx) { - auto optMpptData = VictronMppt.getData(idx); + for (int idx = 0; idx < SolarCharger.controllerAmount(); ++idx) { + auto optMpptData = SolarCharger.getData(idx); if (!optMpptData.has_value()) { continue; } publishSensor("MPPT serial number", "mdi:counter", "SER", nullptr, nullptr, nullptr, *optMpptData); diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index e3e7e461a..b58ae1472 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -10,13 +10,13 @@ #include "MqttSettings.h" #include "NetworkSettings.h" #include "Huawei_can.h" -#include #include "MessageOutput.h" #include #include #include #include #include "SunPosition.h" +#include static auto sBatteryPoweredFilter = [](PowerLimiterInverter const& inv) { return !inv.isSolarPowered(); @@ -370,8 +370,8 @@ float PowerLimiterClass::getBatteryVoltage(bool log) { if (inverter.first > 0) { res = inverter.first; } float chargeControllerVoltage = -1; - if (VictronMppt.isDataValid()) { - res = chargeControllerVoltage = static_cast(VictronMppt.getOutputVoltage()); + if (SolarCharger.isDataValid()) { + res = chargeControllerVoltage = static_cast(SolarCharger.getOutputVoltage()); } float bmsVoltage = -1; @@ -425,8 +425,8 @@ void PowerLimiterClass::fullSolarPassthrough(PowerLimiterClass::Status reason) uint16_t targetOutput = 0; - if (VictronMppt.isDataValid()) { - targetOutput = static_cast(std::max(0, VictronMppt.getPowerOutputWatts())); + if (SolarCharger.isDataValid()) { + targetOutput = static_cast(std::max(0, SolarCharger.getPowerOutputWatts())); targetOutput = dcPowerBusToInverterAc(targetOutput); } @@ -678,11 +678,11 @@ uint16_t PowerLimiterClass::getSolarPassthroughPower() if (!config.PowerLimiter.SolarPassThroughEnabled || isBelowStopThreshold() - || !VictronMppt.isDataValid()) { + || !SolarCharger.isDataValid()) { return 0; } - return VictronMppt.getPowerOutputWatts(); + return SolarCharger.getPowerOutputWatts(); } float PowerLimiterClass::getBatteryInvertersOutputAcWatts() diff --git a/src/SolarCharger.cpp b/src/SolarCharger.cpp new file mode 100644 index 000000000..a6d1d01aa --- /dev/null +++ b/src/SolarCharger.cpp @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "SolarCharger.h" +#include +#include +#include + +SolarChargerClass SolarCharger; + +void SolarChargerClass::init(Scheduler& scheduler) +{ + scheduler.addTask(_loopTask); + _loopTask.setCallback(std::bind(&SolarChargerClass::loop, this)); + _loopTask.setIterations(TASK_FOREVER); + _loopTask.enable(); + + this->updateSettings(); +} + +void SolarChargerClass::updateSettings() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + _upProvider->deinit(); + _upProvider = nullptr; + } + + auto const& config = Configuration.get(); + if (!config.SolarCharger.Enabled) { return; } + + bool verboseLogging = config.SolarCharger.VerboseLogging; + + switch (static_cast(config.SolarCharger.Provider)) { + case SolarChargerProvider::Type::VEDIRECT: + _upProvider = std::make_unique(); + break; + default: + MessageOutput.printf("[SolarCharger] Unknown provider: %d\r\n", config.SolarCharger.Provider); + return; + } + + if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; } +} + +void SolarChargerClass::loop() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + _upProvider->loop(); + } +} + +size_t SolarChargerClass::controllerAmount() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->controllerAmount(); + } + + return 0; +} + +bool SolarChargerClass::isDataValid() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->isDataValid(); + } + + return false; +} + +uint32_t SolarChargerClass::getDataAgeMillis() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getDataAgeMillis(); + } + + return 0; +} + +uint32_t SolarChargerClass::getDataAgeMillis(size_t idx) +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getDataAgeMillis(idx); + } + + return 0; +} + + +// total output of all MPPT charge controllers in Watts +int32_t SolarChargerClass::getPowerOutputWatts() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getPowerOutputWatts(); + } + + return 0; +} + +// total panel input power of all MPPT charge controllers in Watts +int32_t SolarChargerClass::getPanelPowerWatts() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getPanelPowerWatts(); + } + + return 0; +} + +// sum of total yield of all MPPT charge controllers in kWh +float SolarChargerClass::getYieldTotal() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getYieldTotal(); + } + + return 0; +} + +// sum of today's yield of all MPPT charge controllers in kWh +float SolarChargerClass::getYieldDay() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getYieldDay(); + } + + return 0; +} + +// minimum of all MPPT charge controllers' output voltages in V +float SolarChargerClass::getOutputVoltage() +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getOutputVoltage(); + } + + return 0; +} + +std::optional SolarChargerClass::getData(size_t idx) +{ + std::lock_guard lock(_mutex); + + if (_upProvider) { + return _upProvider->getData(idx); + } + + return std::nullopt; +} diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index b84f8e205..f52bf18c1 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -5,30 +5,9 @@ #include "MessageOutput.h" #include "SerialPortManager.h" -VictronMpptClass VictronMppt; - -void VictronMpptClass::init(Scheduler& scheduler) -{ - scheduler.addTask(_loopTask); - _loopTask.setCallback([this] { loop(); }); - _loopTask.setIterations(TASK_FOREVER); - _loopTask.enable(); - - this->updateSettings(); -} - -void VictronMpptClass::updateSettings() +bool VictronMppt::init(bool verboseLogging) { - std::lock_guard lock(_mutex); - - _controllers.clear(); - for (auto const& o: _serialPortOwners) { - SerialPortManager.freePort(o.c_str()); - } - _serialPortOwners.clear(); - auto const& config = Configuration.get(); - if (!config.SolarCharger.Enabled) { return; } const PinMapping_t& pin = PinMapping.get(); @@ -40,9 +19,23 @@ void VictronMpptClass::updateSettings() initController(pin.victron_rx3, pin.victron_tx3, config.SolarCharger.VerboseLogging, 3); + + // TODO(andreasboehm): return false if no controller was initialized + return true; +} + +void VictronMppt::deinit() +{ + std::lock_guard lock(_mutex); + + _controllers.clear(); + for (auto const& o: _serialPortOwners) { + SerialPortManager.freePort(o.c_str()); + } + _serialPortOwners.clear(); } -bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, +bool VictronMppt::initController(int8_t rx, int8_t tx, bool logging, uint8_t instance) { MessageOutput.printf("[VictronMppt Instance %d] rx = %d, tx = %d\r\n", @@ -66,7 +59,7 @@ bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, return true; } -void VictronMpptClass::loop() +void VictronMppt::loop() { std::lock_guard lock(_mutex); @@ -79,7 +72,7 @@ void VictronMpptClass::loop() * isDataValid() * return: true = if at least one of the MPPT controllers delivers valid data */ -bool VictronMpptClass::isDataValid() const +bool VictronMppt::isDataValid() { std::lock_guard lock(_mutex); @@ -90,18 +83,7 @@ bool VictronMpptClass::isDataValid() const return false; } -bool VictronMpptClass::isDataValid(size_t idx) const -{ - std::lock_guard lock(_mutex); - - if (_controllers.empty() || idx >= _controllers.size()) { - return false; - } - - return _controllers[idx]->isDataValid(); -} - -uint32_t VictronMpptClass::getDataAgeMillis() const +uint32_t VictronMppt::getDataAgeMillis() { std::lock_guard lock(_mutex); @@ -121,7 +103,7 @@ uint32_t VictronMpptClass::getDataAgeMillis() const return age; } -uint32_t VictronMpptClass::getDataAgeMillis(size_t idx) const +uint32_t VictronMppt::getDataAgeMillis(size_t idx) { std::lock_guard lock(_mutex); @@ -130,7 +112,7 @@ uint32_t VictronMpptClass::getDataAgeMillis(size_t idx) const return millis() - _controllers[idx]->getLastUpdate(); } -std::optional VictronMpptClass::getData(size_t idx) const +std::optional VictronMppt::getData(size_t idx) { std::lock_guard lock(_mutex); @@ -145,7 +127,7 @@ std::optional VictronMpptClass::getData(size_t i return _controllers[idx]->getData(); } -int32_t VictronMpptClass::getPowerOutputWatts() const +int32_t VictronMppt::getPowerOutputWatts() { int32_t sum = 0; @@ -168,7 +150,7 @@ int32_t VictronMpptClass::getPowerOutputWatts() const return sum; } -int32_t VictronMpptClass::getPanelPowerWatts() const +int32_t VictronMppt::getPanelPowerWatts() { int32_t sum = 0; @@ -189,7 +171,7 @@ int32_t VictronMpptClass::getPanelPowerWatts() const return sum; } -float VictronMpptClass::getYieldTotal() const +float VictronMppt::getYieldTotal() { float sum = 0; @@ -201,7 +183,7 @@ float VictronMpptClass::getYieldTotal() const return sum; } -float VictronMpptClass::getYieldDay() const +float VictronMppt::getYieldDay() { float sum = 0; @@ -213,7 +195,7 @@ float VictronMpptClass::getYieldDay() const return sum; } -float VictronMpptClass::getOutputVoltage() const +float VictronMppt::getOutputVoltage() { float min = -1; @@ -227,7 +209,7 @@ float VictronMpptClass::getOutputVoltage() const return min; } -std::optional VictronMpptClass::getStateOfOperation() const +std::optional VictronMppt::getStateOfOperation() const { for (const auto& upController : _controllers) { if (upController->isDataValid()) { @@ -238,7 +220,7 @@ std::optional VictronMpptClass::getStateOfOperation() const return std::nullopt; } -std::optional VictronMpptClass::getVoltage(MPPTVoltage kindOf) const +std::optional VictronMppt::getVoltage(MPPTVoltage kindOf) const { for (const auto& upController : _controllers) { switch (kindOf) { diff --git a/src/WebApi_solar_charger.cpp b/src/WebApi_solar_charger.cpp index 0695fd911..5579bae88 100644 --- a/src/WebApi_solar_charger.cpp +++ b/src/WebApi_solar_charger.cpp @@ -3,7 +3,6 @@ * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_solarcharger.h" -#include "VictronMppt.h" #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" @@ -11,6 +10,7 @@ #include "WebApi_errors.h" #include "helper.h" #include "MqttHandlePowerLimiterHass.h" +#include void WebApiSolarChargerlass::init(AsyncWebServer& server, Scheduler& scheduler) { @@ -81,7 +81,7 @@ void WebApiSolarChargerlass::onAdminPost(AsyncWebServerRequest* request) WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); - VictronMppt.updateSettings(); + SolarCharger.updateSettings(); // potentially make solar passthrough thresholds auto-discoverable MqttHandlePowerLimiterHass.forceUpdate(); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 6857dd584..cded18f25 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -10,9 +10,9 @@ #include "Battery.h" #include "Huawei_can.h" #include "PowerMeter.h" -#include "VictronMppt.h" #include "defaults.h" #include +#include WebApiWsLiveClass::WebApiWsLiveClass() : _ws("/livedata") @@ -72,16 +72,16 @@ void WebApiWsLiveClass::generateOnBatteryJsonResponse(JsonVariant& root, bool al auto const& config = Configuration.get(); auto constexpr halfOfAllMillis = std::numeric_limits::max() / 2; - auto solarChargerAge = VictronMppt.getDataAgeMillis(); + auto solarChargerAge = SolarCharger.getDataAgeMillis(); if (all || (solarChargerAge > 0 && (millis() - _lastPublishSolarCharger) > solarChargerAge)) { auto solarchargerObj = root["solarcharger"].to(); solarchargerObj["solarcharger"] = config.SolarCharger.Enabled; if (config.SolarCharger.Enabled) { auto totalVeObj = solarchargerObj["total"].to(); - addTotalField(totalVeObj, "Power", VictronMppt.getPanelPowerWatts(), "W", 1); - addTotalField(totalVeObj, "YieldDay", VictronMppt.getYieldDay() * 1000, "Wh", 0); - addTotalField(totalVeObj, "YieldTotal", VictronMppt.getYieldTotal(), "kWh", 2); + addTotalField(totalVeObj, "Power", SolarCharger.getPanelPowerWatts(), "W", 1); + addTotalField(totalVeObj, "YieldDay", SolarCharger.getYieldDay() * 1000, "Wh", 0); + addTotalField(totalVeObj, "YieldTotal", SolarCharger.getYieldTotal(), "kWh", 2); } if (!all) { _lastPublishSolarCharger = millis(); } diff --git a/src/WebApi_ws_solarcharger_live.cpp b/src/WebApi_ws_solarcharger_live.cpp index a810a7b38..dbb71ad46 100644 --- a/src/WebApi_ws_solarcharger_live.cpp +++ b/src/WebApi_ws_solarcharger_live.cpp @@ -10,7 +10,7 @@ #include "WebApi.h" #include "defaults.h" #include "PowerLimiter.h" -#include "VictronMppt.h" +#include WebApiWsSolarChargerLiveClass::WebApiWsSolarChargerLiveClass() : _ws("/solarchargerlivedata") @@ -74,7 +74,7 @@ void WebApiWsSolarChargerLiveClass::wsCleanupTaskCb() bool WebApiWsSolarChargerLiveClass::hasUpdate(size_t idx) { - auto dataAgeMillis = VictronMppt.getDataAgeMillis(idx); + auto dataAgeMillis = SolarCharger.getDataAgeMillis(idx); if (dataAgeMillis == 0) { return false; } auto publishAgeMillis = millis() - _lastPublish; return dataAgeMillis < publishAgeMillis; @@ -83,7 +83,7 @@ bool WebApiWsSolarChargerLiveClass::hasUpdate(size_t idx) uint16_t WebApiWsSolarChargerLiveClass::responseSize() const { // estimated with ArduinoJson assistant - return VictronMppt.controllerAmount() * (1024 + 512) + 128/*DPL status and structure*/; + return SolarCharger.controllerAmount() * (1024 + 512) + 128/*DPL status and structure*/; } void WebApiWsSolarChargerLiveClass::sendDataTaskCb() @@ -95,7 +95,7 @@ void WebApiWsSolarChargerLiveClass::sendDataTaskCb() bool fullUpdate = (millis() - _lastFullPublish > (10 * 1000)); bool updateAvailable = false; if (!fullUpdate) { - for (size_t idx = 0; idx < VictronMppt.controllerAmount(); ++idx) { + for (size_t idx = 0; idx < SolarCharger.controllerAmount(); ++idx) { if (hasUpdate(idx)) { updateAvailable = true; break; @@ -134,8 +134,8 @@ void WebApiWsSolarChargerLiveClass::generateCommonJsonResponse(JsonVariant& root auto array = root["solarcharger"]["instances"].to(); root["solarcharger"]["full_update"] = fullUpdate; - for (size_t idx = 0; idx < VictronMppt.controllerAmount(); ++idx) { - auto optMpptData = VictronMppt.getData(idx); + for (size_t idx = 0; idx < SolarCharger.controllerAmount(); ++idx) { + auto optMpptData = SolarCharger.getData(idx); if (!optMpptData.has_value()) { continue; } if (!fullUpdate && !hasUpdate(idx)) { continue; } @@ -144,7 +144,7 @@ void WebApiWsSolarChargerLiveClass::generateCommonJsonResponse(JsonVariant& root if (serial.isEmpty()) { continue; } // serial required as index JsonObject nested = array[serial].to(); - nested["data_age_ms"] = VictronMppt.getDataAgeMillis(idx); + nested["data_age_ms"] = SolarCharger.getDataAgeMillis(idx); populateJson(nested, *optMpptData); } diff --git a/src/main.cpp b/src/main.cpp index 9a4ac2506..8475c16b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include "Led_Single.h" #include "MessageOutput.h" #include "SerialPortManager.h" -#include "VictronMppt.h" #include "Battery.h" #include "Huawei_can.h" #include "MqttHandleDtu.h" @@ -40,6 +39,7 @@ #include #include #include +#include void setup() { @@ -178,7 +178,8 @@ void setup() Datastore.init(scheduler); RestartHelper.init(scheduler); - VictronMppt.init(scheduler); + // Initialize Solar Charger + SolarCharger.init(scheduler); // Power meter PowerMeter.init(scheduler); From 9f02cab9408bc4bfc9e92bd63ee2b8c8d820ae22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 21 Nov 2024 10:21:39 +0100 Subject: [PATCH 04/13] fix typos in webapi --- src/WebApi_solar_charger.cpp | 3 +-- src/WebApi_ws_live.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/WebApi_solar_charger.cpp b/src/WebApi_solar_charger.cpp index 5579bae88..6096f316a 100644 --- a/src/WebApi_solar_charger.cpp +++ b/src/WebApi_solar_charger.cpp @@ -63,8 +63,7 @@ void WebApiSolarChargerlass::onAdminPost(AsyncWebServerRequest* request) if (!root["enabled"].is() || !root["provider"].is() || - !root["verbose_logging"].is() || - !root["updates_only"].is() ) { + !root["verbose_logging"].is()) { retMsg["message"] = "Values are missing!"; retMsg["code"] = WebApiError::GenericValueMissing; WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index cded18f25..26926ed15 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -75,7 +75,7 @@ void WebApiWsLiveClass::generateOnBatteryJsonResponse(JsonVariant& root, bool al auto solarChargerAge = SolarCharger.getDataAgeMillis(); if (all || (solarChargerAge > 0 && (millis() - _lastPublishSolarCharger) > solarChargerAge)) { auto solarchargerObj = root["solarcharger"].to(); - solarchargerObj["solarcharger"] = config.SolarCharger.Enabled; + solarchargerObj["enabled"] = config.SolarCharger.Enabled; if (config.SolarCharger.Enabled) { auto totalVeObj = solarchargerObj["total"].to(); From 3a57c0ede7665e7d4fff5d69bd4caa53ef496ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 21 Nov 2024 10:56:24 +0100 Subject: [PATCH 05/13] replace v-show with v-if in SolarChargerAdminView --- webapp/src/views/SolarChargerAdminView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/views/SolarChargerAdminView.vue b/webapp/src/views/SolarChargerAdminView.vue index 6bde765cb..be2f6f6e2 100644 --- a/webapp/src/views/SolarChargerAdminView.vue +++ b/webapp/src/views/SolarChargerAdminView.vue @@ -37,7 +37,7 @@ From a6043f8fa0c622447d2d6141c1d6d0b5ebb289c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 21 Nov 2024 11:09:29 +0100 Subject: [PATCH 06/13] remove mqtt option --- webapp/src/locales/de.json | 1 - webapp/src/locales/en.json | 1 - webapp/src/locales/fr.json | 1 - webapp/src/views/SolarChargerAdminView.vue | 5 +---- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 26cac4a0a..aa6acfd77 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -591,7 +591,6 @@ "EnableSolarCharger": "Aktiviere Solar Laderegler", "Provider": "Datenanbieter", "ProviderVeDirect": "Victron MPPT(s) per VE.Direct Schnittstelle", - "ProviderMqtt": "Ladereglerwerte aus MQTT Broker", "VerboseLogging": "@:base.VerboseLogging", "MqttPublishUpdatesOnly": "Werte nur bei Änderung an MQTT broker senden" }, diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 6efec46de..47d580df5 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -593,7 +593,6 @@ "EnableSolarCharger": "Enable Solar Charger", "Provider": "Data Provider", "ProviderVeDirect": "Victron MPPT(s) using VE.Direct interface", - "ProviderMqtt": "Solar Charger data from MQTT broker", "VerboseLogging": "@:base.VerboseLogging", "MqttPublishUpdatesOnly": "Publish values to MQTT only when they change" }, diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index d31980025..fc4f07188 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -577,7 +577,6 @@ "EnableSolarCharger": "Enable Solar Charger", "Provider": "Data Provider", "ProviderVeDirect": "Victron MPPT(s) using VE.Direct interface", - "ProviderMqtt": "Solar Charger data from MQTT broker", "VerboseLogging": "@:base.VerboseLogging", "MqttPublishUpdatesOnly": "Publish values to MQTT only when they change" }, diff --git a/webapp/src/views/SolarChargerAdminView.vue b/webapp/src/views/SolarChargerAdminView.vue index be2f6f6e2..b4b901ced 100644 --- a/webapp/src/views/SolarChargerAdminView.vue +++ b/webapp/src/views/SolarChargerAdminView.vue @@ -74,10 +74,7 @@ export default defineComponent({ alertMessage: '', alertType: 'info', showAlert: false, - providerTypeList: [ - { key: 0, value: 'VeDirect' }, - { key: 1, value: 'Mqtt' }, - ], + providerTypeList: [{ key: 0, value: 'VeDirect' }], }; }, created() { From 02c063f1a79f320af2bf80bc37078c4519e8d4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Thu, 21 Nov 2024 11:14:59 +0100 Subject: [PATCH 07/13] remove unused endpoint --- src/WebApi_solar_charger.cpp | 14 ++------------ src/WebApi_ws_solarcharger_live.cpp | 4 ++-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/WebApi_solar_charger.cpp b/src/WebApi_solar_charger.cpp index 6096f316a..0e6849d95 100644 --- a/src/WebApi_solar_charger.cpp +++ b/src/WebApi_solar_charger.cpp @@ -18,14 +18,13 @@ void WebApiSolarChargerlass::init(AsyncWebServer& server, Scheduler& scheduler) _server = &server; - _server->on("/api/solarcharger/status", HTTP_GET, std::bind(&WebApiSolarChargerlass::onStatus, this, _1)); _server->on("/api/solarcharger/config", HTTP_GET, std::bind(&WebApiSolarChargerlass::onAdminGet, this, _1)); _server->on("/api/solarcharger/config", HTTP_POST, std::bind(&WebApiSolarChargerlass::onAdminPost, this, _1)); } -void WebApiSolarChargerlass::onStatus(AsyncWebServerRequest* request) +void WebApiSolarChargerlass::onAdminGet(AsyncWebServerRequest* request) { - if (!WebApi.checkCredentialsReadonly(request)) { + if (!WebApi.checkCredentials(request)) { return; } @@ -38,15 +37,6 @@ void WebApiSolarChargerlass::onStatus(AsyncWebServerRequest* request) WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); } -void WebApiSolarChargerlass::onAdminGet(AsyncWebServerRequest* request) -{ - if (!WebApi.checkCredentials(request)) { - return; - } - - onStatus(request); -} - void WebApiSolarChargerlass::onAdminPost(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { diff --git a/src/WebApi_ws_solarcharger_live.cpp b/src/WebApi_ws_solarcharger_live.cpp index dbb71ad46..83ef0b329 100644 --- a/src/WebApi_ws_solarcharger_live.cpp +++ b/src/WebApi_ws_solarcharger_live.cpp @@ -283,10 +283,10 @@ void WebApiWsSolarChargerLiveClass::onLivedataStatus(AsyncWebServerRequest* requ WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); } catch (std::bad_alloc& bad_alloc) { - MessageOutput.printf("Calling /api/solarcharger/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + MessageOutput.printf("Calling /api/solarchargerlivedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); WebApi.sendTooManyRequests(request); } catch (const std::exception& exc) { - MessageOutput.printf("Unknown exception in /api/solarcharger/status. Reason: \"%s\".\r\n", exc.what()); + MessageOutput.printf("Unknown exception in /api/solarchargerlivedata/status. Reason: \"%s\".\r\n", exc.what()); WebApi.sendTooManyRequests(request); } } From 17e91ceb10118bb44bf4f333d90e8707c5366b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Fri, 22 Nov 2024 21:34:19 +0100 Subject: [PATCH 08/13] properly report status of VictronMppt.init() --- src/VictronMppt.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index f52bf18c1..e33fe556b 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -7,21 +7,22 @@ bool VictronMppt::init(bool verboseLogging) { - auto const& config = Configuration.get(); - const PinMapping_t& pin = PinMapping.get(); + auto controllerCount = 0; - initController(pin.victron_rx, pin.victron_tx, - config.SolarCharger.VerboseLogging, 1); + if (initController(pin.victron_rx, pin.victron_tx, verboseLogging, 1)) { + controllerCount++; + } - initController(pin.victron_rx2, pin.victron_tx2, - config.SolarCharger.VerboseLogging, 2); + if (initController(pin.victron_rx2, pin.victron_tx2, verboseLogging, 2)) { + controllerCount++; + } - initController(pin.victron_rx3, pin.victron_tx3, - config.SolarCharger.VerboseLogging, 3); + if (initController(pin.victron_rx3, pin.victron_tx3, verboseLogging, 3)) { + controllerCount++; + } - // TODO(andreasboehm): return false if no controller was initialized - return true; + return controllerCount > 0; } void VictronMppt::deinit() From 1fcb4ff88313ff9bb02e93798d797a2726d06120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 4 Dec 2024 08:30:21 +0100 Subject: [PATCH 09/13] revert unneccessary changes, fix includes, fix german translations --- include/SolarChargerProvider.h | 22 +++++++++++----------- include/VictronMppt.h | 28 ++++++++++++++++++---------- include/defaults.h | 2 +- src/MqttHandleVedirect.cpp | 2 +- src/MqttHandleVedirectHass.cpp | 2 +- src/PowerLimiter.cpp | 2 +- src/VictronMppt.cpp | 18 +++++++++--------- src/WebApi_solar_charger.cpp | 8 +++----- src/WebApi_ws_live.cpp | 2 +- src/WebApi_ws_solarcharger_live.cpp | 2 +- src/main.cpp | 3 +-- webapp/src/locales/de.json | 8 ++++---- webapp/src/locales/en.json | 2 +- webapp/src/locales/fr.json | 2 +- 14 files changed, 54 insertions(+), 49 deletions(-) diff --git a/include/SolarChargerProvider.h b/include/SolarChargerProvider.h index 237d249ae..19bdc1187 100644 --- a/include/SolarChargerProvider.h +++ b/include/SolarChargerProvider.h @@ -9,32 +9,32 @@ class SolarChargerProvider { VEDIRECT = 0 }; - // returns true if the provider is ready for use, false otherwise + // returns true if the provider is ready for use, false otherwise virtual bool init(bool verboseLogging) = 0; virtual void deinit() = 0; virtual void loop() = 0; // TODO(andreasboehm): below methods are taken from VictronMppt to start abstracting // solar chargers without breaking everything. - virtual size_t controllerAmount() = 0; - virtual uint32_t getDataAgeMillis() = 0; - virtual uint32_t getDataAgeMillis(size_t idx) = 0; + virtual size_t controllerAmount() const = 0; + virtual uint32_t getDataAgeMillis() const = 0; + virtual uint32_t getDataAgeMillis(size_t idx) const = 0; // total output of all MPPT charge controllers in Watts - virtual int32_t getPowerOutputWatts() = 0; + virtual int32_t getPowerOutputWatts() const = 0; // total panel input power of all MPPT charge controllers in Watts - virtual int32_t getPanelPowerWatts() = 0; + virtual int32_t getPanelPowerWatts() const = 0; // sum of total yield of all MPPT charge controllers in kWh - virtual float getYieldTotal() = 0; + virtual float getYieldTotal() const = 0; // sum of today's yield of all MPPT charge controllers in kWh - virtual float getYieldDay() = 0; + virtual float getYieldDay() const = 0; // minimum of all MPPT charge controllers' output voltages in V - virtual float getOutputVoltage() = 0; + virtual float getOutputVoltage() const = 0; - virtual std::optional getData(size_t idx = 0) = 0; + virtual std::optional getData(size_t idx = 0) const = 0; - virtual bool isDataValid() = 0; + virtual bool isDataValid() const = 0; }; diff --git a/include/VictronMppt.h b/include/VictronMppt.h index 7a98c2c81..bd09e456b 100644 --- a/include/VictronMppt.h +++ b/include/VictronMppt.h @@ -11,34 +11,37 @@ class VictronMppt : public SolarChargerProvider { public: + VictronMppt() = default; + ~VictronMppt() = default; + bool init(bool verboseLogging) final; void deinit() final; void loop() final; - bool isDataValid() final; + bool isDataValid() const final; // returns the data age of all controllers, // i.e, the youngest data's age is returned. - uint32_t getDataAgeMillis() final; - uint32_t getDataAgeMillis(size_t idx) final; + uint32_t getDataAgeMillis() const final; + uint32_t getDataAgeMillis(size_t idx) const final; - size_t controllerAmount() final { return _controllers.size(); } - std::optional getData(size_t idx = 0) final; + size_t controllerAmount() const final { return _controllers.size(); } + std::optional getData(size_t idx = 0) const final; // total output of all MPPT charge controllers in Watts - int32_t getPowerOutputWatts() final; + int32_t getPowerOutputWatts() const final; // total panel input power of all MPPT charge controllers in Watts - int32_t getPanelPowerWatts() final; + int32_t getPanelPowerWatts() const final; // sum of total yield of all MPPT charge controllers in kWh - float getYieldTotal() final; + float getYieldTotal() const final; // sum of today's yield of all MPPT charge controllers in kWh - float getYieldDay() final; + float getYieldDay() const final; // minimum of all MPPT charge controllers' output voltages in V - float getOutputVoltage() final; + float getOutputVoltage() const final; // returns the state of operation from the first available controller std::optional getStateOfOperation() const; @@ -52,6 +55,11 @@ class VictronMppt : public SolarChargerProvider { std::optional getVoltage(MPPTVoltage kindOf) const; private: + VictronMppt(VictronMppt const& other) = delete; + VictronMppt(VictronMppt&& other) = delete; + VictronMppt& operator=(VictronMppt const& other) = delete; + VictronMppt& operator=(VictronMppt&& other) = delete; + mutable std::mutex _mutex; using controller_t = std::unique_ptr; std::vector _controllers; diff --git a/include/defaults.h b/include/defaults.h index add5437cd..bf9613552 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -117,7 +117,7 @@ // values specific to downstream project OpenDTU-OnBattery start here: #define SOLAR_CHARGER_ENABLED false #define SOLAR_CHARGER_VERBOSE_LOGGING false -#define SOLAR_CHARGER_PROVIDER 0 // Victron MPPT(s) via Ve.Direct +#define SOLAR_CHARGER_PROVIDER 0 // Victron MPPT(s) via VE.Direct #define SOLAR_CHARGER_PUBLISH_UPDATES_ONLY true #define POWERMETER_ENABLED false diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 4a8e8f8ac..6dd52ae7b 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -6,7 +6,7 @@ #include "MqttSettings.h" #include "MessageOutput.h" #include "SolarChargerProvider.h" -#include +#include "SolarCharger.h" MqttHandleVedirectClass MqttHandleVedirect; diff --git a/src/MqttHandleVedirectHass.cpp b/src/MqttHandleVedirectHass.cpp index fc02fa871..e8d6944a8 100644 --- a/src/MqttHandleVedirectHass.cpp +++ b/src/MqttHandleVedirectHass.cpp @@ -11,7 +11,7 @@ #include "SolarChargerProvider.h" #include "Utils.h" #include "__compiled_constants.h" -#include +#include "SolarCharger.h" MqttHandleVedirectHassClass MqttHandleVedirectHass; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index b58ae1472..a1045c644 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -16,7 +16,7 @@ #include #include #include "SunPosition.h" -#include +#include "SolarCharger.h" static auto sBatteryPoweredFilter = [](PowerLimiterInverter const& inv) { return !inv.isSolarPowered(); diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index e33fe556b..c88445a9d 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -73,7 +73,7 @@ void VictronMppt::loop() * isDataValid() * return: true = if at least one of the MPPT controllers delivers valid data */ -bool VictronMppt::isDataValid() +bool VictronMppt::isDataValid() const { std::lock_guard lock(_mutex); @@ -84,7 +84,7 @@ bool VictronMppt::isDataValid() return false; } -uint32_t VictronMppt::getDataAgeMillis() +uint32_t VictronMppt::getDataAgeMillis() const { std::lock_guard lock(_mutex); @@ -104,7 +104,7 @@ uint32_t VictronMppt::getDataAgeMillis() return age; } -uint32_t VictronMppt::getDataAgeMillis(size_t idx) +uint32_t VictronMppt::getDataAgeMillis(size_t idx) const { std::lock_guard lock(_mutex); @@ -113,7 +113,7 @@ uint32_t VictronMppt::getDataAgeMillis(size_t idx) return millis() - _controllers[idx]->getLastUpdate(); } -std::optional VictronMppt::getData(size_t idx) +std::optional VictronMppt::getData(size_t idx) const { std::lock_guard lock(_mutex); @@ -128,7 +128,7 @@ std::optional VictronMppt::getData(size_t idx) return _controllers[idx]->getData(); } -int32_t VictronMppt::getPowerOutputWatts() +int32_t VictronMppt::getPowerOutputWatts() const { int32_t sum = 0; @@ -151,7 +151,7 @@ int32_t VictronMppt::getPowerOutputWatts() return sum; } -int32_t VictronMppt::getPanelPowerWatts() +int32_t VictronMppt::getPanelPowerWatts() const { int32_t sum = 0; @@ -172,7 +172,7 @@ int32_t VictronMppt::getPanelPowerWatts() return sum; } -float VictronMppt::getYieldTotal() +float VictronMppt::getYieldTotal() const { float sum = 0; @@ -184,7 +184,7 @@ float VictronMppt::getYieldTotal() return sum; } -float VictronMppt::getYieldDay() +float VictronMppt::getYieldDay() const { float sum = 0; @@ -196,7 +196,7 @@ float VictronMppt::getYieldDay() return sum; } -float VictronMppt::getOutputVoltage() +float VictronMppt::getOutputVoltage() const { float min = -1; diff --git a/src/WebApi_solar_charger.cpp b/src/WebApi_solar_charger.cpp index 0e6849d95..fcdb8aeba 100644 --- a/src/WebApi_solar_charger.cpp +++ b/src/WebApi_solar_charger.cpp @@ -1,7 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2022-2024 Thomas Basler and others - */ #include "WebApi_solarcharger.h" #include "ArduinoJson.h" #include "AsyncJson.h" @@ -10,7 +7,7 @@ #include "WebApi_errors.h" #include "helper.h" #include "MqttHandlePowerLimiterHass.h" -#include +#include "SolarCharger.h" void WebApiSolarChargerlass::init(AsyncWebServer& server, Scheduler& scheduler) { @@ -53,7 +50,8 @@ void WebApiSolarChargerlass::onAdminPost(AsyncWebServerRequest* request) if (!root["enabled"].is() || !root["provider"].is() || - !root["verbose_logging"].is()) { + !root["verbose_logging"].is() || + !root["publish_updates_only"].is()) { retMsg["message"] = "Values are missing!"; retMsg["code"] = WebApiError::GenericValueMissing; WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 26926ed15..e731081b4 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -11,8 +11,8 @@ #include "Huawei_can.h" #include "PowerMeter.h" #include "defaults.h" +#include "SolarCharger.h" #include -#include WebApiWsLiveClass::WebApiWsLiveClass() : _ws("/livedata") diff --git a/src/WebApi_ws_solarcharger_live.cpp b/src/WebApi_ws_solarcharger_live.cpp index 83ef0b329..ed5595314 100644 --- a/src/WebApi_ws_solarcharger_live.cpp +++ b/src/WebApi_ws_solarcharger_live.cpp @@ -10,7 +10,7 @@ #include "WebApi.h" #include "defaults.h" #include "PowerLimiter.h" -#include +#include "SolarCharger.h" WebApiWsSolarChargerLiveClass::WebApiWsSolarChargerLiveClass() : _ws("/solarchargerlivedata") diff --git a/src/main.cpp b/src/main.cpp index 8475c16b5..383568ff1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,12 +34,12 @@ #include "PowerMeter.h" #include "PowerLimiter.h" #include "defaults.h" +#include "SolarCharger.h" #include #include #include #include #include -#include void setup() { @@ -178,7 +178,6 @@ void setup() Datastore.init(scheduler); RestartHelper.init(scheduler); - // Initialize Solar Charger SolarCharger.init(scheduler); // Power meter diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index aa6acfd77..361118716 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -9,7 +9,7 @@ "SecuritySettings": "Sicherheit", "DTUSettings": "DTU", "DeviceManager": "Hardware", - "SolarChargerSettings": "Solar Laderegler", + "SolarChargerSettings": "Solarladeregler", "PowerMeterSettings": "Stromzähler", "BatterySettings": "Batterie", "AcChargerSettings": "AC Ladegerät", @@ -586,9 +586,9 @@ "HassIndividual": "Einzelne Panels" }, "solarchargeradmin": { - "SolarChargerSettings": "Solar Laderegler Einstellungen", - "SolarChargerConfiguration": "Solar Laderegler Konfiguration", - "EnableSolarCharger": "Aktiviere Solar Laderegler", + "SolarChargerSettings": "Solarladeregler Einstellungen", + "SolarChargerConfiguration": "Solarladeregler Konfiguration", + "EnableSolarCharger": "Aktiviere Schnittstelle", "Provider": "Datenanbieter", "ProviderVeDirect": "Victron MPPT(s) per VE.Direct Schnittstelle", "VerboseLogging": "@:base.VerboseLogging", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 47d580df5..13316b129 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -590,7 +590,7 @@ "solarchargeradmin": { "SolarChargerSettings": "Solar Charger Settings", "SolarChargerConfiguration": "Solar Charger Configuration", - "EnableSolarCharger": "Enable Solar Charger", + "EnableSolarCharger": "Enable Interface", "Provider": "Data Provider", "ProviderVeDirect": "Victron MPPT(s) using VE.Direct interface", "VerboseLogging": "@:base.VerboseLogging", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index fc4f07188..21149a986 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -574,7 +574,7 @@ "solarchargeradmin": { "SolarChargerSettings": "Solar Charger Settings", "SolarChargerConfiguration": "Solar Charger Configuration", - "EnableSolarCharger": "Enable Solar Charger", + "EnableSolarCharger": "Enable Interface", "Provider": "Data Provider", "ProviderVeDirect": "Victron MPPT(s) using VE.Direct interface", "VerboseLogging": "@:base.VerboseLogging", From 898ae399303df269e3488f1253f24f56a5033f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 4 Dec 2024 08:51:55 +0100 Subject: [PATCH 10/13] rename 'getPowerOutputWatts' to 'getOutputPowerWatts' --- include/SolarCharger.h | 2 +- include/SolarChargerProvider.h | 2 +- include/VictronMppt.h | 6 +++--- src/PowerLimiter.cpp | 4 ++-- src/SolarCharger.cpp | 4 ++-- src/VictronMppt.cpp | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/SolarCharger.h b/include/SolarCharger.h index 2fcdfc0e3..74b2d31cd 100644 --- a/include/SolarCharger.h +++ b/include/SolarCharger.h @@ -19,7 +19,7 @@ class SolarChargerClass { uint32_t getDataAgeMillis(size_t idx); // total output of all MPPT charge controllers in Watts - int32_t getPowerOutputWatts(); + int32_t getOutputPowerWatts(); // total panel input power of all MPPT charge controllers in Watts int32_t getPanelPowerWatts(); diff --git a/include/SolarChargerProvider.h b/include/SolarChargerProvider.h index 19bdc1187..d75846321 100644 --- a/include/SolarChargerProvider.h +++ b/include/SolarChargerProvider.h @@ -20,7 +20,7 @@ class SolarChargerProvider { virtual uint32_t getDataAgeMillis() const = 0; virtual uint32_t getDataAgeMillis(size_t idx) const = 0; // total output of all MPPT charge controllers in Watts - virtual int32_t getPowerOutputWatts() const = 0; + virtual int32_t getOutputPowerWatts() const = 0; // total panel input power of all MPPT charge controllers in Watts virtual int32_t getPanelPowerWatts() const = 0; diff --git a/include/VictronMppt.h b/include/VictronMppt.h index bd09e456b..11f4840c1 100644 --- a/include/VictronMppt.h +++ b/include/VictronMppt.h @@ -3,11 +3,11 @@ #include #include +#include -#include "VeDirectMpptController.h" #include "SolarChargerProvider.h" +#include "VeDirectMpptController.h" #include "Configuration.h" -#include class VictronMppt : public SolarChargerProvider { public: @@ -29,7 +29,7 @@ class VictronMppt : public SolarChargerProvider { std::optional getData(size_t idx = 0) const final; // total output of all MPPT charge controllers in Watts - int32_t getPowerOutputWatts() const final; + int32_t getOutputPowerWatts() const final; // total panel input power of all MPPT charge controllers in Watts int32_t getPanelPowerWatts() const final; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index a1045c644..81707593c 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -426,7 +426,7 @@ void PowerLimiterClass::fullSolarPassthrough(PowerLimiterClass::Status reason) uint16_t targetOutput = 0; if (SolarCharger.isDataValid()) { - targetOutput = static_cast(std::max(0, SolarCharger.getPowerOutputWatts())); + targetOutput = static_cast(std::max(0, SolarCharger.getOutputPowerWatts())); targetOutput = dcPowerBusToInverterAc(targetOutput); } @@ -682,7 +682,7 @@ uint16_t PowerLimiterClass::getSolarPassthroughPower() return 0; } - return SolarCharger.getPowerOutputWatts(); + return SolarCharger.getOutputPowerWatts(); } float PowerLimiterClass::getBatteryInvertersOutputAcWatts() diff --git a/src/SolarCharger.cpp b/src/SolarCharger.cpp index a6d1d01aa..43255d97a 100644 --- a/src/SolarCharger.cpp +++ b/src/SolarCharger.cpp @@ -97,12 +97,12 @@ uint32_t SolarChargerClass::getDataAgeMillis(size_t idx) // total output of all MPPT charge controllers in Watts -int32_t SolarChargerClass::getPowerOutputWatts() +int32_t SolarChargerClass::getOutputPowerWatts() { std::lock_guard lock(_mutex); if (_upProvider) { - return _upProvider->getPowerOutputWatts(); + return _upProvider->getOutputPowerWatts(); } return 0; diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index c88445a9d..a6bc4f7a0 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -128,7 +128,7 @@ std::optional VictronMppt::getData(size_t idx) c return _controllers[idx]->getData(); } -int32_t VictronMppt::getPowerOutputWatts() const +int32_t VictronMppt::getOutputPowerWatts() const { int32_t sum = 0; From 8c0cab1a77bbfd16b5508c0f5a02a16642ac21a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Wed, 4 Dec 2024 08:58:04 +0100 Subject: [PATCH 11/13] use 'migrateOnBattery()' for config migration --- include/Configuration.h | 2 +- src/Configuration.cpp | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index 716432b62..97f0725c8 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -10,7 +10,7 @@ #define CONFIG_FILENAME "/config.json" #define CONFIG_VERSION 0x00011d00 // 0.1.29 // make sure to clean all after change -#define CONFIG_VERSION_ONBATTERY 2 +#define CONFIG_VERSION_ONBATTERY 3 #define WIFI_MAX_SSID_STRLEN 32 #define WIFI_MAX_PASSWORD_STRLEN 64 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 6961f0d79..3b1f8d565 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -670,15 +670,6 @@ bool ConfigurationClass::read() deserializeSolarChargerConfig(doc["solarcharger"], config.SolarCharger); - // process settings from legacy config if they are present - // TODO(andreasboehm): remove end of 2025. - if (!doc["vedirect"].isNull()) { - JsonObject vedirect = doc["vedirect"]; - config.SolarCharger.Enabled = vedirect["enabled"] | SOLAR_CHARGER_ENABLED; - config.SolarCharger.VerboseLogging = vedirect["verbose_logging"] | SOLAR_CHARGER_VERBOSE_LOGGING; - config.SolarCharger.PublishUpdatesOnly = vedirect["updates_only"] | SOLAR_CHARGER_PUBLISH_UPDATES_ONLY; - } - JsonObject powermeter = doc["powermeter"]; config.PowerMeter.Enabled = powermeter["enabled"] | POWERMETER_ENABLED; config.PowerMeter.VerboseLogging = powermeter["verbose_logging"] | VERBOSE_LOGGING; @@ -919,6 +910,13 @@ void ConfigurationClass::migrateOnBattery() config.PowerLimiter.ConductionLosses = doc["powerlimiter"]["solar_passthrough_losses"].as(); } + if (config.Cfg.VersionOnBattery < 3) { + JsonObject vedirect = doc["vedirect"]; + config.SolarCharger.Enabled = vedirect["enabled"] | SOLAR_CHARGER_ENABLED; + config.SolarCharger.VerboseLogging = vedirect["verbose_logging"] | SOLAR_CHARGER_VERBOSE_LOGGING; + config.SolarCharger.PublishUpdatesOnly = vedirect["updates_only"] | SOLAR_CHARGER_PUBLISH_UPDATES_ONLY; + } + f.close(); config.Cfg.VersionOnBattery = CONFIG_VERSION_ONBATTERY; From afcbc95d9fd87904faafc2fb5052cf287c959736 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Wed, 4 Dec 2024 21:44:03 +0100 Subject: [PATCH 12/13] VE.Direct MQTT HASS: make publishConfig private --- include/MqttHandleVedirectHass.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/MqttHandleVedirectHass.h b/include/MqttHandleVedirectHass.h index 6d7a17ac6..b8ea4534b 100644 --- a/include/MqttHandleVedirectHass.h +++ b/include/MqttHandleVedirectHass.h @@ -8,11 +8,11 @@ class MqttHandleVedirectHassClass { public: void init(Scheduler& scheduler); - void publishConfig(); void forceUpdate(); private: void loop(); + void publishConfig(); void publish(const String& subtopic, const String& payload); void publishBinarySensor(const char *caption, const char *icon, const char *subTopic, const char *payload_on, const char *payload_off, From f43b7a5afd9ce39f784dfee30bf0da24ae8df87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bo=CC=88hm?= Date: Sat, 7 Dec 2024 22:55:45 +0100 Subject: [PATCH 13/13] use 'SolarChargerProviderType' in Configuration --- include/Configuration.h | 4 +++- include/SolarChargerProvider.h | 4 ---- include/defaults.h | 1 - src/Configuration.cpp | 2 +- src/MqttHandleVedirect.cpp | 3 +-- src/MqttHandleVedirectHass.cpp | 3 +-- src/SolarCharger.cpp | 4 ++-- 7 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index 97f0725c8..aac192836 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -196,10 +196,12 @@ struct BATTERY_CONFIG_T { }; using BatteryConfig = struct BATTERY_CONFIG_T; +enum SolarChargerProviderType { VEDIRECT = 0 }; + struct SOLAR_CHARGER_CONFIG_T { bool Enabled; bool VerboseLogging; - uint8_t Provider; + SolarChargerProviderType Provider; bool PublishUpdatesOnly; }; using SolarChargerConfig = struct SOLAR_CHARGER_CONFIG_T; diff --git a/include/SolarChargerProvider.h b/include/SolarChargerProvider.h index d75846321..77321412b 100644 --- a/include/SolarChargerProvider.h +++ b/include/SolarChargerProvider.h @@ -5,10 +5,6 @@ class SolarChargerProvider { public: - enum class Type : unsigned { - VEDIRECT = 0 - }; - // returns true if the provider is ready for use, false otherwise virtual bool init(bool verboseLogging) = 0; virtual void deinit() = 0; diff --git a/include/defaults.h b/include/defaults.h index bf9613552..238ae9aa0 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -117,7 +117,6 @@ // values specific to downstream project OpenDTU-OnBattery start here: #define SOLAR_CHARGER_ENABLED false #define SOLAR_CHARGER_VERBOSE_LOGGING false -#define SOLAR_CHARGER_PROVIDER 0 // Victron MPPT(s) via VE.Direct #define SOLAR_CHARGER_PUBLISH_UPDATES_ONLY true #define POWERMETER_ENABLED false diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 3b1f8d565..57d6612d1 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -374,7 +374,7 @@ void ConfigurationClass::deserializeSolarChargerConfig(JsonObject const& source, { target.Enabled = source["enabled"] | SOLAR_CHARGER_ENABLED; target.VerboseLogging = source["verbose_logging"] | VERBOSE_LOGGING; - target.Provider = source["provider"] | SOLAR_CHARGER_PROVIDER; + target.Provider = source["provider"] | SolarChargerProviderType::VEDIRECT; target.PublishUpdatesOnly = source["publish_updates_only"] | SOLAR_CHARGER_PUBLISH_UPDATES_ONLY; } diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index 6dd52ae7b..ce2cf76a4 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -5,7 +5,6 @@ #include "MqttHandleVedirect.h" #include "MqttSettings.h" #include "MessageOutput.h" -#include "SolarChargerProvider.h" #include "SolarCharger.h" MqttHandleVedirectClass MqttHandleVedirect; @@ -36,7 +35,7 @@ void MqttHandleVedirectClass::loop() auto const& config = Configuration.get(); if (!MqttSettings.getConnected() || !config.SolarCharger.Enabled - || static_cast(config.SolarCharger.Provider) != SolarChargerProvider::Type::VEDIRECT) { + || config.SolarCharger.Provider != SolarChargerProviderType::VEDIRECT) { return; } diff --git a/src/MqttHandleVedirectHass.cpp b/src/MqttHandleVedirectHass.cpp index e8d6944a8..251bf93b8 100644 --- a/src/MqttHandleVedirectHass.cpp +++ b/src/MqttHandleVedirectHass.cpp @@ -8,7 +8,6 @@ #include "MqttHandleHass.h" #include "NetworkSettings.h" #include "MessageOutput.h" -#include "SolarChargerProvider.h" #include "Utils.h" #include "__compiled_constants.h" #include "SolarCharger.h" @@ -27,7 +26,7 @@ void MqttHandleVedirectHassClass::loop() { if (!Configuration.get().Mqtt.Hass.Enabled || !Configuration.get().SolarCharger.Enabled - || static_cast(Configuration.get().SolarCharger.Provider) != SolarChargerProvider::Type::VEDIRECT) { + || Configuration.get().SolarCharger.Provider != SolarChargerProviderType::VEDIRECT) { return; } diff --git a/src/SolarCharger.cpp b/src/SolarCharger.cpp index 43255d97a..ba2a56945 100644 --- a/src/SolarCharger.cpp +++ b/src/SolarCharger.cpp @@ -30,8 +30,8 @@ void SolarChargerClass::updateSettings() bool verboseLogging = config.SolarCharger.VerboseLogging; - switch (static_cast(config.SolarCharger.Provider)) { - case SolarChargerProvider::Type::VEDIRECT: + switch (config.SolarCharger.Provider) { + case SolarChargerProviderType::VEDIRECT: _upProvider = std::make_unique(); break; default: