diff --git a/include/Configuration.h b/include/Configuration.h index 3c0c41a1..66fb4861 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -3,6 +3,7 @@ #include "PinMapping.h" #include +#include #define CONFIG_FILENAME "/config.json" #define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change @@ -30,14 +31,14 @@ #define DEV_MAX_MAPPING_NAME_STRLEN 63 -#define POWERMETER_MAX_PHASES 3 -#define POWERMETER_MAX_HTTP_URL_STRLEN 1024 -#define POWERMETER_MAX_USERNAME_STRLEN 64 -#define POWERMETER_MAX_PASSWORD_STRLEN 64 -#define POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN 64 -#define POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN 256 -#define POWERMETER_MAX_HTTP_JSON_PATH_STRLEN 256 -#define POWERMETER_HTTP_TIMEOUT 1000 +#define HTTP_REQUEST_MAX_URL_STRLEN 1024 +#define HTTP_REQUEST_MAX_USERNAME_STRLEN 64 +#define HTTP_REQUEST_MAX_PASSWORD_STRLEN 64 +#define HTTP_REQUEST_MAX_HEADER_KEY_STRLEN 64 +#define HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN 256 + +#define POWERMETER_HTTP_JSON_MAX_VALUES 3 +#define POWERMETER_HTTP_JSON_MAX_PATH_STRLEN 256 struct CHANNEL_CONFIG_T { uint16_t MaxChannelPower; @@ -61,30 +62,36 @@ struct INVERTER_CONFIG_T { CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT]; }; -struct POWERMETER_HTTP_PHASE_CONFIG_T { +struct HTTP_REQUEST_CONFIG_T { + char Url[HTTP_REQUEST_MAX_URL_STRLEN + 1]; + enum Auth { None, Basic, Digest }; - enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 }; - bool Enabled; - char Url[POWERMETER_MAX_HTTP_URL_STRLEN + 1]; Auth AuthType; - char Username[POWERMETER_MAX_USERNAME_STRLEN +1]; - char Password[POWERMETER_MAX_USERNAME_STRLEN +1]; - char HeaderKey[POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN + 1]; - char HeaderValue[POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN + 1]; + + char Username[HTTP_REQUEST_MAX_USERNAME_STRLEN + 1]; + char Password[HTTP_REQUEST_MAX_PASSWORD_STRLEN + 1]; + char HeaderKey[HTTP_REQUEST_MAX_HEADER_KEY_STRLEN + 1]; + char HeaderValue[HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN + 1]; uint16_t Timeout; - char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1]; +}; +using HttpRequestConfig = struct HTTP_REQUEST_CONFIG_T; + +struct POWERMETER_HTTP_JSON_CONFIG_T { + HttpRequestConfig HttpRequest; + bool Enabled; + char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1]; + + enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 }; Unit PowerUnit; + bool SignInverted; }; -using PowerMeterHttpConfig = struct POWERMETER_HTTP_PHASE_CONFIG_T; +using PowerMeterHttpJsonConfig = struct POWERMETER_HTTP_JSON_CONFIG_T; -struct POWERMETER_TIBBER_CONFIG_T { - char Url[POWERMETER_MAX_HTTP_URL_STRLEN + 1]; - char Username[POWERMETER_MAX_USERNAME_STRLEN + 1]; - char Password[POWERMETER_MAX_USERNAME_STRLEN + 1]; - uint16_t Timeout; +struct POWERMETER_HTTP_SML_CONFIG_T { + HttpRequestConfig HttpRequest; }; -using PowerMeterTibberConfig = struct POWERMETER_TIBBER_CONFIG_T; +using PowerMeterHttpSmlConfig = struct POWERMETER_HTTP_SML_CONFIG_T; struct CONFIG_T { struct { @@ -204,10 +211,9 @@ struct CONFIG_T { char MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1]; char MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1]; uint32_t SdmAddress; - uint32_t HttpInterval; bool HttpIndividualRequests; - PowerMeterHttpConfig Http_Phase[POWERMETER_MAX_PHASES]; - PowerMeterTibberConfig Tibber; + PowerMeterHttpJsonConfig HttpJson[POWERMETER_HTTP_JSON_MAX_VALUES]; + PowerMeterHttpSmlConfig HttpSml; } PowerMeter; struct { @@ -280,6 +286,14 @@ public: INVERTER_CONFIG_T* getFreeInverterSlot(); INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial); void deleteInverterById(const uint8_t id); + + static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target); + static void serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target); + static void serializePowerMeterHttpSmlConfig(PowerMeterHttpSmlConfig const& source, JsonObject& target); + + static void deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target); + static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target); + static void deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target); }; extern ConfigurationClass Configuration; diff --git a/include/PowerMeterHttpJson.h b/include/PowerMeterHttpJson.h index 692bd982..50bca8c5 100644 --- a/include/PowerMeterHttpJson.h +++ b/include/PowerMeterHttpJson.h @@ -8,8 +8,8 @@ #include "Configuration.h" #include "PowerMeterProvider.h" -using Auth_t = PowerMeterHttpConfig::Auth; -using Unit_t = PowerMeterHttpConfig::Unit; +using Auth_t = HttpRequestConfig::Auth; +using Unit_t = PowerMeterHttpJsonConfig::Unit; class PowerMeterHttpJson : public PowerMeterProvider { public: @@ -20,19 +20,19 @@ public: float getPowerTotal() const final; void doMqttPublish() const final; - bool queryPhase(int phase, PowerMeterHttpConfig const& config); + bool queryValue(int phase, PowerMeterHttpJsonConfig const& config); char httpPowerMeterError[256]; float getCached(size_t idx) { return _cache[idx]; } private: uint32_t _lastPoll; - std::array _cache; - std::array _powerValues; + std::array _cache; + std::array _powerValues; std::unique_ptr wifiClient; std::unique_ptr httpClient; String httpResponse; - bool httpRequest(int phase, const String& host, uint16_t port, const String& uri, bool https, PowerMeterHttpConfig const& config); + bool httpRequest(int phase, const String& host, uint16_t port, const String& uri, bool https, PowerMeterHttpJsonConfig const& config); bool extractUrlComponents(String url, String& _protocol, String& _hostname, String& _uri, uint16_t& uint16_t, String& _base64Authorization); String extractParam(String& authReq, const String& param, const char delimit); String getcNonce(const int len); diff --git a/include/PowerMeterHttpSml.h b/include/PowerMeterHttpSml.h index c6e46bdc..73bc882c 100644 --- a/include/PowerMeterHttpSml.h +++ b/include/PowerMeterHttpSml.h @@ -16,7 +16,7 @@ public: void loop() final; bool updateValues(); char tibberPowerMeterError[256]; - bool query(PowerMeterTibberConfig const& config); + bool query(HttpRequestConfig const& config); private: uint32_t _lastPoll = 0; @@ -24,7 +24,7 @@ private: std::unique_ptr wifiClient; std::unique_ptr httpClient; String httpResponse; - bool httpRequest(const String& host, uint16_t port, const String& uri, bool https, PowerMeterTibberConfig const& config); + bool httpRequest(const String& host, uint16_t port, const String& uri, bool https, HttpRequestConfig const& config); bool extractUrlComponents(String url, String& _protocol, String& _hostname, String& _uri, uint16_t& uint16_t, String& _base64Authorization); void prepareRequest(uint32_t timeout); }; diff --git a/include/WebApi_powermeter.h b/include/WebApi_powermeter.h index 12e5afae..3cfe2a2d 100644 --- a/include/WebApi_powermeter.h +++ b/include/WebApi_powermeter.h @@ -14,10 +14,8 @@ private: void onStatus(AsyncWebServerRequest* request); void onAdminGet(AsyncWebServerRequest* request); void onAdminPost(AsyncWebServerRequest* request); - void decodeJsonPhaseConfig(JsonObject const& json, PowerMeterHttpConfig& config) const; - void decodeJsonTibberConfig(JsonObject const& json, PowerMeterTibberConfig& config) const; - void onTestHttpRequest(AsyncWebServerRequest* request); - void onTestTibberRequest(AsyncWebServerRequest* request); + void onTestHttpJsonRequest(AsyncWebServerRequest* request); + void onTestHttpSmlRequest(AsyncWebServerRequest* request); AsyncWebServer* _server; }; diff --git a/include/defaults.h b/include/defaults.h index 865e595c..6b191cbd 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -119,6 +119,8 @@ #define POWERMETER_SOURCE 2 #define POWERMETER_SDMADDRESS 1 +#define HTTP_REQUEST_TIMEOUT_MS 1000 + #define POWERLIMITER_ENABLED false #define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED true #define POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES 3 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index e899de9b..ffe96459 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -6,7 +6,6 @@ #include "MessageOutput.h" #include "Utils.h" #include "defaults.h" -#include #include #include @@ -17,6 +16,33 @@ void ConfigurationClass::init() memset(&config, 0x0, sizeof(config)); } +void ConfigurationClass::serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target) +{ + JsonObject target_http_config = target["http_request"].to(); + target_http_config["url"] = source.Url; + target_http_config["auth_type"] = source.AuthType; + target_http_config["username"] = source.Username; + target_http_config["password"] = source.Password; + target_http_config["header_key"] = source.HeaderKey; + target_http_config["header_value"] = source.HeaderValue; + target_http_config["timeout"] = source.Timeout; +} + +void ConfigurationClass::serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target) +{ + serializeHttpRequestConfig(source.HttpRequest, target); + + target["enabled"] = source.Enabled; + target["json_path"] = source.JsonPath; + target["unit"] = source.PowerUnit; + target["sign_inverted"] = source.SignInverted; +} + +void ConfigurationClass::serializePowerMeterHttpSmlConfig(PowerMeterHttpSmlConfig const& source, JsonObject& target) +{ + serializeHttpRequestConfig(source.HttpRequest, target); +} + bool ConfigurationClass::write() { File f = LittleFS.open(CONFIG_FILENAME, "w"); @@ -158,27 +184,14 @@ bool ConfigurationClass::write() powermeter["sdmaddress"] = config.PowerMeter.SdmAddress; powermeter["http_individual_requests"] = config.PowerMeter.HttpIndividualRequests; - JsonObject tibber = powermeter["tibber"].to(); - tibber["url"] = config.PowerMeter.Tibber.Url; - tibber["username"] = config.PowerMeter.Tibber.Username; - tibber["password"] = config.PowerMeter.Tibber.Password; - tibber["timeout"] = config.PowerMeter.Tibber.Timeout; + JsonObject powermeter_http_sml = powermeter["http_sml"].to(); + serializePowerMeterHttpSmlConfig(config.PowerMeter.HttpSml, powermeter_http_sml); - JsonArray powermeter_http_phases = powermeter["http_phases"].to(); - for (uint8_t i = 0; i < POWERMETER_MAX_PHASES; i++) { - JsonObject powermeter_phase = powermeter_http_phases.add(); - - powermeter_phase["enabled"] = config.PowerMeter.Http_Phase[i].Enabled; - powermeter_phase["url"] = config.PowerMeter.Http_Phase[i].Url; - powermeter_phase["auth_type"] = config.PowerMeter.Http_Phase[i].AuthType; - powermeter_phase["username"] = config.PowerMeter.Http_Phase[i].Username; - powermeter_phase["password"] = config.PowerMeter.Http_Phase[i].Password; - powermeter_phase["header_key"] = config.PowerMeter.Http_Phase[i].HeaderKey; - powermeter_phase["header_value"] = config.PowerMeter.Http_Phase[i].HeaderValue; - powermeter_phase["timeout"] = config.PowerMeter.Http_Phase[i].Timeout; - powermeter_phase["json_path"] = config.PowerMeter.Http_Phase[i].JsonPath; - powermeter_phase["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit; - powermeter_phase["sign_inverted"] = config.PowerMeter.Http_Phase[i].SignInverted; + JsonArray powermeter_http_json = powermeter["http_json"].to(); + for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) { + JsonObject powermeter_json_config = powermeter_http_json.add(); + serializePowerMeterHttpJsonConfig(config.PowerMeter.HttpJson[i], + powermeter_json_config); } JsonObject powerlimiter = doc["powerlimiter"].to(); @@ -246,6 +259,38 @@ bool ConfigurationClass::write() return true; } +void ConfigurationClass::deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target) +{ + JsonObject source_http_config = source["http_request"]; + + // http request parameters of HTTP/JSON power meter were + // previously stored alongside other settings + if (source_http_config.isNull()) { source_http_config = source; } + + strlcpy(target.Url, source_http_config["url"] | "", sizeof(target.Url)); + target.AuthType = source_http_config["auth_type"] | HttpRequestConfig::Auth::None; + strlcpy(target.Username, source_http_config["username"] | "", sizeof(target.Username)); + strlcpy(target.Password, source_http_config["password"] | "", sizeof(target.Password)); + strlcpy(target.HeaderKey, source_http_config["header_key"] | "", sizeof(target.HeaderKey)); + strlcpy(target.HeaderValue, source_http_config["header_value"] | "", sizeof(target.HeaderValue)); + target.Timeout = source_http_config["timeout"] | HTTP_REQUEST_TIMEOUT_MS; +} + +void ConfigurationClass::deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target) +{ + deserializeHttpRequestConfig(source, target.HttpRequest); + + target.Enabled = source["enabled"] | false; + strlcpy(target.JsonPath, source["json_path"] | "", sizeof(target.JsonPath)); + target.PowerUnit = source["unit"] | PowerMeterHttpJsonConfig::Unit::Watts; + target.SignInverted = source["sign_inverted"] | false; +} + +void ConfigurationClass::deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target) +{ + deserializeHttpRequestConfig(source, target.HttpRequest); +} + bool ConfigurationClass::read() { File f = LittleFS.open(CONFIG_FILENAME, "r", false); @@ -424,27 +469,16 @@ bool ConfigurationClass::read() config.PowerMeter.SdmAddress = powermeter["sdmaddress"] | POWERMETER_SDMADDRESS; config.PowerMeter.HttpIndividualRequests = powermeter["http_individual_requests"] | false; - JsonObject tibber = powermeter["tibber"]; - strlcpy(config.PowerMeter.Tibber.Url, tibber["url"] | "", sizeof(config.PowerMeter.Tibber.Url)); - strlcpy(config.PowerMeter.Tibber.Username, tibber["username"] | "", sizeof(config.PowerMeter.Tibber.Username)); - strlcpy(config.PowerMeter.Tibber.Password, tibber["password"] | "", sizeof(config.PowerMeter.Tibber.Password)); - config.PowerMeter.Tibber.Timeout = tibber["timeout"] | POWERMETER_HTTP_TIMEOUT; + JsonObject powermeter_sml = powermeter["http_sml"]; + deserializePowerMeterHttpSmlConfig(powermeter_sml, config.PowerMeter.HttpSml); - JsonArray powermeter_http_phases = powermeter["http_phases"]; - for (uint8_t i = 0; i < POWERMETER_MAX_PHASES; i++) { - JsonObject powermeter_phase = powermeter_http_phases[i].as(); - - config.PowerMeter.Http_Phase[i].Enabled = powermeter_phase["enabled"] | (i == 0); - strlcpy(config.PowerMeter.Http_Phase[i].Url, powermeter_phase["url"] | "", sizeof(config.PowerMeter.Http_Phase[i].Url)); - config.PowerMeter.Http_Phase[i].AuthType = powermeter_phase["auth_type"] | PowerMeterHttpConfig::Auth::None; - strlcpy(config.PowerMeter.Http_Phase[i].Username, powermeter_phase["username"] | "", sizeof(config.PowerMeter.Http_Phase[i].Username)); - strlcpy(config.PowerMeter.Http_Phase[i].Password, powermeter_phase["password"] | "", sizeof(config.PowerMeter.Http_Phase[i].Password)); - strlcpy(config.PowerMeter.Http_Phase[i].HeaderKey, powermeter_phase["header_key"] | "", sizeof(config.PowerMeter.Http_Phase[i].HeaderKey)); - strlcpy(config.PowerMeter.Http_Phase[i].HeaderValue, powermeter_phase["header_value"] | "", sizeof(config.PowerMeter.Http_Phase[i].HeaderValue)); - config.PowerMeter.Http_Phase[i].Timeout = powermeter_phase["timeout"] | POWERMETER_HTTP_TIMEOUT; - strlcpy(config.PowerMeter.Http_Phase[i].JsonPath, powermeter_phase["json_path"] | "", sizeof(config.PowerMeter.Http_Phase[i].JsonPath)); - config.PowerMeter.Http_Phase[i].PowerUnit = powermeter_phase["unit"] | PowerMeterHttpConfig::Unit::Watts; - config.PowerMeter.Http_Phase[i].SignInverted = powermeter_phase["sign_inverted"] | false; + JsonArray powermeter_http_json = powermeter["http_json"]; + if (powermeter_http_json.isNull()) { + powermeter_http_json = powermeter["http_phases"]; // http_phases is a legacy key + } + for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) { + JsonObject powermeter_json_config = powermeter_http_json[i].as(); + deserializePowerMeterHttpJsonConfig(powermeter_json_config, config.PowerMeter.HttpJson[i]); } JsonObject powerlimiter = doc["powerlimiter"]; diff --git a/src/PowerMeterHttpJson.cpp b/src/PowerMeterHttpJson.cpp index 73d1439e..785775cc 100644 --- a/src/PowerMeterHttpJson.cpp +++ b/src/PowerMeterHttpJson.cpp @@ -27,16 +27,16 @@ void PowerMeterHttpJson::loop() _lastPoll = millis(); - for (uint8_t i = 0; i < POWERMETER_MAX_PHASES; i++) { - auto const& phaseConfig = config.PowerMeter.Http_Phase[i]; + for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) { + auto const& valueConfig = config.PowerMeter.HttpJson[i]; - if (!phaseConfig.Enabled) { + if (!valueConfig.Enabled) { _cache[i] = 0.0; continue; } if (i == 0 || config.PowerMeter.HttpIndividualRequests) { - if (!queryPhase(i, phaseConfig)) { + if (!queryValue(i, valueConfig)) { MessageOutput.printf("[PowerMeterHttpJson] Getting HTTP response for phase %d failed.\r\n", i + 1); MessageOutput.printf("%s\r\n", httpPowerMeterError); return; @@ -44,7 +44,7 @@ void PowerMeterHttpJson::loop() continue; } - if(!tryGetFloatValueForPhase(i, phaseConfig.JsonPath, phaseConfig.PowerUnit, phaseConfig.SignInverted)) { + if(!tryGetFloatValueForPhase(i, valueConfig.JsonPath, valueConfig.PowerUnit, valueConfig.SignInverted)) { MessageOutput.printf("[PowerMeterHttpJson] Reading power of phase %d (from JSON fetched with Phase 1 config) failed.\r\n", i + 1); MessageOutput.printf("%s\r\n", httpPowerMeterError); return; @@ -70,7 +70,7 @@ void PowerMeterHttpJson::doMqttPublish() const mqttPublish("power3", _powerValues[2]); } -bool PowerMeterHttpJson::queryPhase(int phase, PowerMeterHttpConfig const& config) +bool PowerMeterHttpJson::queryValue(int phase, PowerMeterHttpJsonConfig const& config) { //hostByName in WiFiGeneric fails to resolve local names. issue described in //https://github.com/espressif/arduino-esp32/issues/3822 @@ -82,7 +82,7 @@ bool PowerMeterHttpJson::queryPhase(int phase, PowerMeterHttpConfig const& confi String uri; String base64Authorization; uint16_t port; - extractUrlComponents(config.Url, protocol, host, uri, port, base64Authorization); + extractUrlComponents(config.HttpRequest.Url, protocol, host, uri, port, base64Authorization); IPAddress ipaddr((uint32_t)0); //first check if "host" is already an IP adress @@ -123,7 +123,7 @@ bool PowerMeterHttpJson::queryPhase(int phase, PowerMeterHttpConfig const& confi return httpRequest(phase, ipaddr.toString(), port, uri, https, config); } -bool PowerMeterHttpJson::httpRequest(int phase, const String& host, uint16_t port, const String& uri, bool https, PowerMeterHttpConfig const& config) +bool PowerMeterHttpJson::httpRequest(int phase, const String& host, uint16_t port, const String& uri, bool https, PowerMeterHttpJsonConfig const& powerMeterConfig) { if (!httpClient) { httpClient = std::make_unique(); } @@ -132,6 +132,7 @@ bool PowerMeterHttpJson::httpRequest(int phase, const String& host, uint16_t por return false; } + auto const& config = powerMeterConfig.HttpRequest; prepareRequest(config.Timeout, config.HeaderKey, config.HeaderValue); if (config.AuthType == Auth_t::Digest) { const char *headers[1] = {"WWW-Authenticate"}; @@ -178,7 +179,7 @@ bool PowerMeterHttpJson::httpRequest(int phase, const String& host, uint16_t por // TODO(schlimmchen): postpone calling tryGetFloatValueForPhase, as it // will be called twice for each phase when doing separate requests. - return tryGetFloatValueForPhase(phase, config.JsonPath, config.PowerUnit, config.SignInverted); + return tryGetFloatValueForPhase(phase, powerMeterConfig.JsonPath, powerMeterConfig.PowerUnit, powerMeterConfig.SignInverted); } String PowerMeterHttpJson::extractParam(String& authReq, const String& param, const char delimit) { diff --git a/src/PowerMeterHttpSml.cpp b/src/PowerMeterHttpSml.cpp index 5e0df2c2..17750bba 100644 --- a/src/PowerMeterHttpSml.cpp +++ b/src/PowerMeterHttpSml.cpp @@ -24,15 +24,13 @@ void PowerMeterHttpSml::loop() _lastPoll = millis(); - auto const& tibberConfig = config.PowerMeter.Tibber; - - if (!query(tibberConfig)) { + if (!query(config.PowerMeter.HttpSml.HttpRequest)) { MessageOutput.printf("[PowerMeterHttpSml] Getting the power value failed.\r\n"); MessageOutput.printf("%s\r\n", tibberPowerMeterError); } } -bool PowerMeterHttpSml::query(PowerMeterTibberConfig const& config) +bool PowerMeterHttpSml::query(HttpRequestConfig const& config) { //hostByName in WiFiGeneric fails to resolve local names. issue described in //https://github.com/espressif/arduino-esp32/issues/3822 @@ -85,7 +83,7 @@ bool PowerMeterHttpSml::query(PowerMeterTibberConfig const& config) return httpRequest(ipaddr.toString(), port, uri, https, config); } -bool PowerMeterHttpSml::httpRequest(const String& host, uint16_t port, const String& uri, bool https, PowerMeterTibberConfig const& config) +bool PowerMeterHttpSml::httpRequest(const String& host, uint16_t port, const String& uri, bool https, HttpRequestConfig const& config) { if (!httpClient) { httpClient = std::make_unique(); } diff --git a/src/WebApi_powermeter.cpp b/src/WebApi_powermeter.cpp index bf60beea..51f37353 100644 --- a/src/WebApi_powermeter.cpp +++ b/src/WebApi_powermeter.cpp @@ -26,31 +26,8 @@ void WebApiPowerMeterClass::init(AsyncWebServer& server, Scheduler& scheduler) _server->on("/api/powermeter/status", HTTP_GET, std::bind(&WebApiPowerMeterClass::onStatus, this, _1)); _server->on("/api/powermeter/config", HTTP_GET, std::bind(&WebApiPowerMeterClass::onAdminGet, this, _1)); _server->on("/api/powermeter/config", HTTP_POST, std::bind(&WebApiPowerMeterClass::onAdminPost, this, _1)); - _server->on("/api/powermeter/testhttprequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestHttpRequest, this, _1)); - _server->on("/api/powermeter/testtibberrequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestTibberRequest, this, _1)); -} - -void WebApiPowerMeterClass::decodeJsonPhaseConfig(JsonObject const& json, PowerMeterHttpConfig& config) const -{ - config.Enabled = json["enabled"].as(); - strlcpy(config.Url, json["url"].as().c_str(), sizeof(config.Url)); - config.AuthType = json["auth_type"].as(); - strlcpy(config.Username, json["username"].as().c_str(), sizeof(config.Username)); - strlcpy(config.Password, json["password"].as().c_str(), sizeof(config.Password)); - strlcpy(config.HeaderKey, json["header_key"].as().c_str(), sizeof(config.HeaderKey)); - strlcpy(config.HeaderValue, json["header_value"].as().c_str(), sizeof(config.HeaderValue)); - config.Timeout = json["timeout"].as(); - strlcpy(config.JsonPath, json["json_path"].as().c_str(), sizeof(config.JsonPath)); - config.PowerUnit = json["unit"].as(); - config.SignInverted = json["sign_inverted"].as(); -} - -void WebApiPowerMeterClass::decodeJsonTibberConfig(JsonObject const& json, PowerMeterTibberConfig& config) const -{ - strlcpy(config.Url, json["url"].as().c_str(), sizeof(config.Url)); - strlcpy(config.Username, json["username"].as().c_str(), sizeof(config.Username)); - strlcpy(config.Password, json["password"].as().c_str(), sizeof(config.Password)); - config.Timeout = json["timeout"].as(); + _server->on("/api/powermeter/testhttpjsonrequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestHttpJsonRequest, this, _1)); + _server->on("/api/powermeter/testhttpsmlrequest", HTTP_POST, std::bind(&WebApiPowerMeterClass::onTestHttpSmlRequest, this, _1)); } void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request) @@ -69,29 +46,14 @@ void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request) root["sdmaddress"] = config.PowerMeter.SdmAddress; root["http_individual_requests"] = config.PowerMeter.HttpIndividualRequests; - auto tibber = root["tibber"].to(); - tibber["url"] = String(config.PowerMeter.Tibber.Url); - tibber["username"] = String(config.PowerMeter.Tibber.Username); - tibber["password"] = String(config.PowerMeter.Tibber.Password); - tibber["timeout"] = config.PowerMeter.Tibber.Timeout; + auto httpSml = root["http_sml"].to(); + Configuration.serializePowerMeterHttpSmlConfig(config.PowerMeter.HttpSml, httpSml); - auto httpPhases = root["http_phases"].to(); - - for (uint8_t i = 0; i < POWERMETER_MAX_PHASES; i++) { - auto phaseObject = httpPhases.add(); - - phaseObject["index"] = i + 1; - phaseObject["enabled"] = config.PowerMeter.Http_Phase[i].Enabled; - phaseObject["url"] = String(config.PowerMeter.Http_Phase[i].Url); - phaseObject["auth_type"]= config.PowerMeter.Http_Phase[i].AuthType; - phaseObject["username"] = String(config.PowerMeter.Http_Phase[i].Username); - phaseObject["password"] = String(config.PowerMeter.Http_Phase[i].Password); - phaseObject["header_key"] = String(config.PowerMeter.Http_Phase[i].HeaderKey); - phaseObject["header_value"] = String(config.PowerMeter.Http_Phase[i].HeaderValue); - phaseObject["timeout"] = config.PowerMeter.Http_Phase[i].Timeout; - phaseObject["json_path"] = String(config.PowerMeter.Http_Phase[i].JsonPath); - phaseObject["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit; - phaseObject["sign_inverted"] = config.PowerMeter.Http_Phase[i].SignInverted; + auto httpJson = root["http_json"].to(); + for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) { + auto valueConfig = httpJson.add(); + valueConfig["index"] = i + 1; + Configuration.serializePowerMeterHttpJsonConfig(config.PowerMeter.HttpJson[i], valueConfig); } WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); @@ -127,44 +89,52 @@ void WebApiPowerMeterClass::onAdminPost(AsyncWebServerRequest* request) return; } - if (static_cast(root["source"].as()) == PowerMeterProvider::Type::HTTP_JSON) { - JsonArray http_phases = root["http_phases"]; - for (uint8_t i = 0; i < http_phases.size(); i++) { - JsonObject phase = http_phases[i].as(); + auto checkHttpConfig = [&](JsonObject const& cfg) -> bool { + if (!cfg.containsKey("url") + || (!cfg["url"].as().startsWith("http://") + && !cfg["url"].as().startsWith("https://"))) { + retMsg["message"] = "URL must either start with http:// or https://!"; + response->setLength(); + request->send(response); + return false; + } - if (i > 0 && !phase["enabled"].as()) { + if ((cfg["auth_type"].as() != HttpRequestConfig::Auth::None) + && (cfg["username"].as().length() == 0 || cfg["password"].as().length() == 0)) { + retMsg["message"] = "Username or password must not be empty!"; + response->setLength(); + request->send(response); + return false; + } + + if (!cfg.containsKey("timeout") + || cfg["timeout"].as() <= 0) { + retMsg["message"] = "Timeout must be greater than 0 ms!"; + response->setLength(); + request->send(response); + return false; + } + + return true; + }; + + if (static_cast(root["source"].as()) == PowerMeterProvider::Type::HTTP_JSON) { + JsonArray httpJson = root["http_json"]; + for (uint8_t i = 0; i < httpJson.size(); i++) { + JsonObject valueConfig = httpJson[i].as(); + + if (i > 0 && !valueConfig["enabled"].as()) { continue; } - if (i == 0 || phase["http_individual_requests"].as()) { - if (!phase.containsKey("url") - || (!phase["url"].as().startsWith("http://") - && !phase["url"].as().startsWith("https://"))) { - retMsg["message"] = "URL must either start with http:// or https://!"; - response->setLength(); - request->send(response); - return; - } - - if ((phase["auth_type"].as() != PowerMeterHttpConfig::Auth::None) - && ( phase["username"].as().length() == 0 || phase["password"].as().length() == 0)) { - retMsg["message"] = "Username or password must not be empty!"; - response->setLength(); - request->send(response); - return; - } - - if (!phase.containsKey("timeout") - || phase["timeout"].as() <= 0) { - retMsg["message"] = "Timeout must be greater than 0 ms!"; - response->setLength(); - request->send(response); + if (i == 0 || valueConfig["http_individual_requests"].as()) { + if (!checkHttpConfig(valueConfig["http_request"].as())) { return; } } - if (!phase.containsKey("json_path") - || phase["json_path"].as().length() == 0) { + if (!valueConfig.containsKey("json_path") + || valueConfig["json_path"].as().length() == 0) { retMsg["message"] = "Json path must not be empty!"; response->setLength(); request->send(response); @@ -174,29 +144,8 @@ void WebApiPowerMeterClass::onAdminPost(AsyncWebServerRequest* request) } if (static_cast(root["source"].as()) == PowerMeterProvider::Type::HTTP_SML) { - JsonObject tibber = root["tibber"]; - - if (!tibber.containsKey("url") - || (!tibber["url"].as().startsWith("http://") - && !tibber["url"].as().startsWith("https://"))) { - retMsg["message"] = "URL must either start with http:// or https://!"; - response->setLength(); - request->send(response); - return; - } - - if ((tibber["username"].as().length() == 0 || tibber["password"].as().length() == 0)) { - retMsg["message"] = "Username or password must not be empty!"; - response->setLength(); - request->send(response); - return; - } - - if (!tibber.containsKey("timeout") - || tibber["timeout"].as() <= 0) { - retMsg["message"] = "Timeout must be greater than 0 ms!"; - response->setLength(); - request->send(response); + JsonObject httpSml = root["http_sml"]; + if (!checkHttpConfig(httpSml["http_request"].as())) { return; } } @@ -212,13 +161,15 @@ void WebApiPowerMeterClass::onAdminPost(AsyncWebServerRequest* request) config.PowerMeter.SdmAddress = root["sdmaddress"].as(); config.PowerMeter.HttpIndividualRequests = root["http_individual_requests"].as(); - decodeJsonTibberConfig(root["tibber"].as(), config.PowerMeter.Tibber); + Configuration.deserializePowerMeterHttpSmlConfig(root["http_sml"].as(), + config.PowerMeter.HttpSml); - JsonArray http_phases = root["http_phases"]; - for (uint8_t i = 0; i < http_phases.size(); i++) { - decodeJsonPhaseConfig(http_phases[i].as(), config.PowerMeter.Http_Phase[i]); + JsonArray httpJson = root["http_json"]; + for (uint8_t i = 0; i < httpJson.size(); i++) { + Configuration.deserializePowerMeterHttpJsonConfig(httpJson[i].as(), + config.PowerMeter.HttpJson[i]); } - config.PowerMeter.Http_Phase[0].Enabled = true; + config.PowerMeter.HttpJson[0].Enabled = true; WebApi.writeConfig(retMsg); @@ -227,7 +178,7 @@ void WebApiPowerMeterClass::onAdminPost(AsyncWebServerRequest* request) PowerMeter.updateSettings(); } -void WebApiPowerMeterClass::onTestHttpRequest(AsyncWebServerRequest* request) +void WebApiPowerMeterClass::onTestHttpJsonRequest(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -241,9 +192,15 @@ void WebApiPowerMeterClass::onTestHttpRequest(AsyncWebServerRequest* request) auto& retMsg = asyncJsonResponse->getRoot(); - if (!root.containsKey("url") || !root.containsKey("auth_type") || !root.containsKey("username") || !root.containsKey("password") - || !root.containsKey("header_key") || !root.containsKey("header_value") - || !root.containsKey("timeout") || !root.containsKey("json_path")) { + JsonObject requestConfig = root["http_request"]; + if (!requestConfig.containsKey("url") + || !requestConfig.containsKey("auth_type") + || !requestConfig.containsKey("username") + || !requestConfig.containsKey("password") + || !requestConfig.containsKey("header_key") + || !requestConfig.containsKey("header_value") + || !requestConfig.containsKey("timeout") + || !root.containsKey("json_path")) { retMsg["message"] = "Missing fields!"; asyncJsonResponse->setLength(); request->send(asyncJsonResponse); @@ -253,10 +210,10 @@ void WebApiPowerMeterClass::onTestHttpRequest(AsyncWebServerRequest* request) char response[256]; - PowerMeterHttpConfig phaseConfig; - decodeJsonPhaseConfig(root.as(), phaseConfig); + PowerMeterHttpJsonConfig httpJsonConfig; + Configuration.deserializePowerMeterHttpJsonConfig(root.as(), httpJsonConfig); auto upMeter = std::make_unique(); - if (upMeter->queryPhase(0/*phase*/, phaseConfig)) { + if (upMeter->queryValue(0/*value index*/, httpJsonConfig)) { retMsg["type"] = "success"; snprintf_P(response, sizeof(response), "Success! Power: %5.2fW", upMeter->getCached(0)); } else { @@ -268,7 +225,7 @@ void WebApiPowerMeterClass::onTestHttpRequest(AsyncWebServerRequest* request) request->send(asyncJsonResponse); } -void WebApiPowerMeterClass::onTestTibberRequest(AsyncWebServerRequest* request) +void WebApiPowerMeterClass::onTestHttpSmlRequest(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -293,10 +250,10 @@ void WebApiPowerMeterClass::onTestTibberRequest(AsyncWebServerRequest* request) char response[256]; - PowerMeterTibberConfig tibberConfig; - decodeJsonTibberConfig(root.as(), tibberConfig); + PowerMeterHttpSmlConfig httpSmlConfig; + Configuration.deserializePowerMeterHttpSmlConfig(root.as(), httpSmlConfig); auto upMeter = std::make_unique(); - if (upMeter->query(tibberConfig)) { + if (upMeter->query(httpSmlConfig.HttpRequest)) { retMsg["type"] = "success"; snprintf_P(response, sizeof(response), "Success! Power: %5.2fW", upMeter->getPowerTotal()); } else { diff --git a/webapp/src/components/HttpRequestSettings.vue b/webapp/src/components/HttpRequestSettings.vue new file mode 100644 index 00000000..7c922c1c --- /dev/null +++ b/webapp/src/components/HttpRequestSettings.vue @@ -0,0 +1,77 @@ + + + diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index c7021b99..a982a563 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -558,37 +558,47 @@ "PowerMeterSource": "Stromzählertyp", "MQTT": "MQTT Konfiguration", "typeMQTT": "MQTT", - "typeSDM1ph": "SDM 1 phase (SDM120/220/230)", - "typeSDM3ph": "SDM 3 phase (SDM72/630)", - "typeHTTP": "HTTP(S) + JSON", - "typeSML": "SML (OBIS 16.7.0)", + "typeSDM1ph": "SDM mit 1 Phase (SDM120/220/230)", + "typeSDM3ph": "SDM mit 3 Phasen (SDM72/630)", + "typeHTTP_JSON": "HTTP(S) + JSON", + "typeSML": "SML/OBIS via serieller Verbindung (z.B. Hichi TTL)", "typeSMAHM2": "SMA Homemanager 2.0", - "typeTIBBER": "Tibber Pulse (via Tibber Bridge)", + "typeHTTP_SML": "HTTP(S) + SML (z.B. Tibber Pulse via Tibber Bridge)", "MqttTopicPowerMeter1": "MQTT topic - Stromzähler #1", "MqttTopicPowerMeter2": "MQTT topic - Stromzähler #2 (Optional)", "MqttTopicPowerMeter3": "MQTT topic - Stromzähler #3 (Optional)", "SDM": "SDM-Stromzähler Konfiguration", "sdmaddress": "Modbus Adresse", - "HTTP": "HTTP(S) + JSON - Allgemeine Konfiguration", - "httpIndividualRequests": "Individuelle HTTP requests pro Phase", + "HTTP_JSON": "HTTP(S) + JSON - Allgemeine Konfiguration", + "httpIndividualRequests": "Individuelle HTTP Anfragen pro Wert", "urlExamplesHeading": "Beispiele für URLs", "jsonPathExamplesHeading": "Beispiele für JSON Pfade", "jsonPathExamplesExplanation": "Die folgenden Pfade finden jeweils den Wert '123.4' im jeweiligen Beispiel-JSON.", - "httpUrlDescription": "Die URL muss mit http:// oder https:// beginnen. Manche Zeichen wie Leerzeichen und = müssen mit URL-Kodierung kodiert werden (%xx). Achtung: Ein Überprüfung von SSL Server Zertifikaten ist nicht implementiert (MITM-Attacken sind möglich)!.", - "httpPhase": "HTTP(S) + JSON Konfiguration - Phase {phaseNumber}", - "httpEnabled": "Phase aktiviert", - "httpUrl": "URL", - "httpHeaderKey": "Optional: HTTP request header - Key", - "httpHeaderKeyDescription": "Ein individueller HTTP request header kann hier definiert werden. Das kann z.B. verwendet werden um einen eigenen Authorization header mitzugeben.", - "httpHeaderValue": "Optional: HTTP request header - Wert", + "httpValue": "Konfiguration für Wert {valueNumber}", + "httpEnabled": "Wert aktiviert", "httpJsonPath": "JSON Pfad", "httpJsonPathDescription": "Anwendungsspezifischer JSON-Pfad um den Leistungswert in the HTTP(S) Antwort zu finden, z.B. 'power/total/watts' oder nur 'total'.", "httpUnit": "Einheit", "httpSignInverted": "Vorzeichen umkehren", "httpSignInvertedHint": "Positive Werte werden als Leistungsabnahme aus dem Netz interpretiert. Diese Option muss aktiviert werden, wenn das Vorzeichen des Wertes die gegenteilige Bedeutung hat.", - "httpTimeout": "Timeout", - "testHttpRequest": "Testen", - "TIBBER": "Tibber Pulse (via Tibber Bridge) - Konfiguration" + "testHttpJsonRequest": "Konfiguration testen (HTTP(S)-Anfrage senden)", + "testHttpSmlRequest": "Konfiguration testen (HTTP(S)-Anfrage senden)", + "HTTP_SML": "HTTP(S) + SML - Konfiguration" + }, + "httprequestsettings": { + "url": "URL", + "urlDescription": "Die URL muss mit 'http://' oder 'https://' beginnen. Zeichen wie Leerzeichen und = müssen mit URL-kodiert werden (%xx). Achtung: Eine Überprüfung von SSL-Server-Zertifikaten ist nicht implementiert (MITM-Attacken sind möglich)!.", + "authorization": "Authentifizierungsverfahren", + "authTypeNone": "Ohne", + "authTypeBasic": "Basic", + "authTypeDigest": "Digest", + "username": "Benutzername", + "password": "Passwort", + "headerKey": "HTTP Header - Name", + "headerKeyDescription": "Optional. Ein benutzerdefinierter HTTP header kann definiert werden. Nützlich um z.B. ein (zusätzlichen) Authentifizierungstoken zu übermitteln.", + "headerValue": "HTTP Header - Wert", + "timeout": "Zeitüberschreitung", + "milliSeconds": "ms" }, "powerlimiteradmin": { "PowerLimiterSettings": "Dynamic Power Limiter Einstellungen", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 4b7a3959..30407c7d 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -560,41 +560,47 @@ "PowerMeterSource": "Power Meter type", "MQTT": "MQTT Parameter", "typeMQTT": "MQTT", - "typeSDM1ph": "SDM 1 phase (SDM120/220/230)", - "typeSDM3ph": "SDM 3 phase (SDM72/630)", - "typeHTTP": "HTTP(s) + JSON", - "typeSML": "SML (OBIS 16.7.0)", + "typeSDM1ph": "SDM for 1 phase (SDM120/220/230)", + "typeSDM3ph": "SDM for 3 phases (SDM72/630)", + "typeHTTP_JSON": "HTTP(S) + JSON", + "typeSML": "SML/OBIS via serial connection (e.g. Hichi TTL)", "typeSMAHM2": "SMA Homemanager 2.0", - "typeTIBBER": "Tibber Pulse (via Tibber Bridge)", + "typeHTTP_SML": "HTTP(S) + SML (e.g. Tibber Pulse via Tibber Bridge)", "MqttTopicPowerMeter1": "MQTT topic - Power meter #1", "MqttTopicPowerMeter2": "MQTT topic - Power meter #2", "MqttTopicPowerMeter3": "MQTT topic - Power meter #3", "SDM": "SDM-Power Meter Parameter", "sdmaddress": "Modbus Address", - "HTTP": "HTTP(S) + Json - General configuration", - "httpIndividualRequests": "Individual HTTP requests per phase", + "HTTP": "HTTP(S) + JSON - General configuration", + "httpIndividualRequests": "Individual HTTP requests per value", "urlExamplesHeading": "URL Examples", "jsonPathExamplesHeading": "JSON Path Examples", "jsonPathExamplesExplanation": "The following paths each find the value '123.4' in the respective example JSON.", - "httpPhase": "HTTP(S) + Json configuration - Phase {phaseNumber}", - "httpEnabled": "Phase enabled", - "httpUrl": "URL", - "httpUrlDescription": "URL must start with http:// or https://. Some characters like spaces and = have to be encoded with URL encoding (%xx). Warning: SSL server certificate check is not implemented (MITM attacks are possible)!", - "httpAuthorization": "Authorization Type", - "httpUsername": "Username", - "httpPassword": "Password", - "httpHeaderKey": "Optional: HTTP request header - Key", - "httpHeaderKeyDescription": "A custom HTTP request header can be defined. Might be useful if you have to send something like a custom Authorization header.", - "httpHeaderValue": "Optional: HTTP request header - Value", + "httpValue": "Configuration for value {valueNumber}", + "httpEnabled": "Value enabled", "httpJsonPath": "JSON path", "httpJsonPathDescription": "Application specific JSON path to find the power value in the HTTP(S) response, e.g., 'power/total/watts' or simply 'total'.", "httpUnit": "Unit", "httpSignInverted": "Change Sign", "httpSignInvertedHint": "Is is expected that positive values denote power usage from the grid. Check this option if the sign of this value has the opposite meaning.", - "httpTimeout": "Timeout", - "testHttpRequest": "Run test", - "milliSeconds": "ms", - "TIBBER": "Tibber Pulse (via Tibber Bridge) - Configuration" + "testHttpJsonRequest": "Test configuration (send HTTP(S) request)", + "testHttpSmlRequest": "Test configuration (send HTTP(S) request)", + "HTTP_SML": "Configuration" + }, + "httprequestsettings": { + "url": "URL", + "urlDescription": "URL must start with 'http://' or 'https://'. Characters like spaces and '=' have to be URL-encoded (%xx). Warning: SSL server certificate check is not implemented (MITM attacks are possible)!", + "authorization": "Authorization Type", + "authTypeNone": "None", + "authTypeBasic": "Basic", + "authTypeDigest": "Digest", + "username": "Username", + "password": "Password", + "headerKey": "HTTP Header - Key", + "headerKeyDescription": "Optional. A custom HTTP header key-value pair can be defined. Useful, e.g., to send an (additional) authentication token.", + "headerValue": "HTTP Header - Value", + "timeout": "Timeout", + "milliSeconds": "ms" }, "powerlimiteradmin": { "PowerLimiterSettings": "Dynamic Power Limiter Settings", diff --git a/webapp/src/types/HttpRequestConfig.ts b/webapp/src/types/HttpRequestConfig.ts new file mode 100644 index 00000000..46592457 --- /dev/null +++ b/webapp/src/types/HttpRequestConfig.ts @@ -0,0 +1,9 @@ +export interface HttpRequestConfig { + url: string; + auth_type: number; + username: string; + password: string; + header_key: string; + header_value: string; + timeout: number; +} diff --git a/webapp/src/types/PowerMeterConfig.ts b/webapp/src/types/PowerMeterConfig.ts index 1675d8d3..fe9fa09a 100644 --- a/webapp/src/types/PowerMeterConfig.ts +++ b/webapp/src/types/PowerMeterConfig.ts @@ -1,23 +1,16 @@ -export interface PowerMeterHttpPhaseConfig { +import type { HttpRequestConfig } from '@/types/HttpRequestConfig'; + +export interface PowerMeterHttpJsonConfig { index: number; + http_request: HttpRequestConfig; enabled: boolean; - url: string; - auth_type: number; - username: string; - password: string; - header_key: string; - header_value: string; json_path: string; - timeout: number; unit: number; sign_inverted: boolean; } -export interface PowerMeterTibberConfig { - url: string; - username: string; - password: string; - timeout: number; +export interface PowerMeterHttpSmlConfig { + http_request: HttpRequestConfig; } export interface PowerMeterConfig { @@ -30,6 +23,6 @@ export interface PowerMeterConfig { mqtt_topic_powermeter_3: string; sdmaddress: number; http_individual_requests: boolean; - http_phases: Array; - tibber: PowerMeterTibberConfig; + http_json: Array; + http_sml: PowerMeterHttpSmlConfig; } diff --git a/webapp/src/views/PowerMeterAdminView.vue b/webapp/src/views/PowerMeterAdminView.vue index fd600069..a7dbae77 100644 --- a/webapp/src/views/PowerMeterAdminView.vue +++ b/webapp/src/views/PowerMeterAdminView.vue @@ -114,66 +114,23 @@ -
-
- +
-
- -
- -
-
-
- - - -
- - - - - - -
+
- +
-
- - {{ testHttpRequestAlert[index].message }} + + {{ testHttpJsonRequestAlert[index].message }}
- - - - - - - - +
-
- - {{ testTibberRequestAlert.message }} + + {{ testHttpSmlRequestAlert.message }}
@@ -263,8 +201,9 @@ import BootstrapAlert from "@/components/BootstrapAlert.vue"; import CardElement from '@/components/CardElement.vue'; import FormFooter from '@/components/FormFooter.vue'; import InputElement from '@/components/InputElement.vue'; +import HttpRequestSettings from '@/components/HttpRequestSettings.vue'; import { handleResponse, authHeader } from '@/utils/authentication'; -import type { PowerMeterHttpPhaseConfig, PowerMeterConfig } from "@/types/PowerMeterConfig"; +import type { PowerMeterHttpJsonConfig, PowerMeterConfig } from "@/types/PowerMeterConfig"; export default defineComponent({ components: { @@ -272,6 +211,7 @@ export default defineComponent({ BootstrapAlert, CardElement, FormFooter, + HttpRequestSettings, InputElement }, data() { @@ -282,21 +222,21 @@ export default defineComponent({ { key: 0, value: this.$t('powermeteradmin.typeMQTT') }, { key: 1, value: this.$t('powermeteradmin.typeSDM1ph') }, { key: 2, value: this.$t('powermeteradmin.typeSDM3ph') }, - { key: 3, value: this.$t('powermeteradmin.typeHTTP') }, + { key: 3, value: this.$t('powermeteradmin.typeHTTP_JSON') }, { key: 4, value: this.$t('powermeteradmin.typeSML') }, { key: 5, value: this.$t('powermeteradmin.typeSMAHM2') }, - { key: 6, value: this.$t('powermeteradmin.typeTIBBER') }, + { key: 6, value: this.$t('powermeteradmin.typeHTTP_SML') }, ], - powerMeterAuthList: [ - { key: 0, value: "None" }, - { key: 1, value: "Basic" }, - { key: 2, value: "Digest" }, + unitTypeList: [ + { key: 1, value: "mW" }, + { key: 0, value: "W" }, + { key: 2, value: "kW" }, ], alertMessage: "", alertType: "info", showAlert: false, - testHttpRequestAlert: [{message: "", type: "", show: false}] as { message: string; type: string; show: boolean; }[], - testTibberRequestAlert: {message: "", type: "", show: false} as { message: string; type: string; show: boolean; } + testHttpJsonRequestAlert: [{message: "", type: "", show: false}] as { message: string; type: string; show: boolean; }[], + testHttpSmlRequestAlert: {message: "", type: "", show: false} as { message: string; type: string; show: boolean; } }; }, created() { @@ -311,8 +251,8 @@ export default defineComponent({ this.powerMeterConfigList = data; this.dataLoading = false; - for (let i = 0; i < this.powerMeterConfigList.http_phases.length; i++) { - this.testHttpRequestAlert.push({ + for (let i = 0; i < this.powerMeterConfigList.http_json.length; i++) { + this.testHttpJsonRequestAlert.push({ message: "", type: "", show: false, @@ -341,27 +281,27 @@ export default defineComponent({ } ); }, - testHttpRequest(index: number) { - let phaseConfig:PowerMeterHttpPhaseConfig; + testHttpJsonRequest(index: number) { + let valueConfig:PowerMeterHttpJsonConfig; if (this.powerMeterConfigList.http_individual_requests) { - phaseConfig = this.powerMeterConfigList.http_phases[index]; + valueConfig = this.powerMeterConfigList.http_json[index]; } else { - phaseConfig = { ...this.powerMeterConfigList.http_phases[0] }; - phaseConfig.index = this.powerMeterConfigList.http_phases[index].index; - phaseConfig.json_path = this.powerMeterConfigList.http_phases[index].json_path; + valueConfig = { ...this.powerMeterConfigList.http_json[0] }; + valueConfig.index = this.powerMeterConfigList.http_json[index].index; + valueConfig.json_path = this.powerMeterConfigList.http_json[index].json_path; } - this.testHttpRequestAlert[index] = { + this.testHttpJsonRequestAlert[index] = { message: "Sending HTTP request...", type: "info", show: true, }; const formData = new FormData(); - formData.append("data", JSON.stringify(phaseConfig)); + formData.append("data", JSON.stringify(valueConfig)); - fetch("/api/powermeter/testhttprequest", { + fetch("/api/powermeter/testhttpjsonrequest", { method: "POST", headers: authHeader(), body: formData, @@ -369,7 +309,7 @@ export default defineComponent({ .then((response) => handleResponse(response, this.$emitter, this.$router)) .then( (response) => { - this.testHttpRequestAlert[index] = { + this.testHttpJsonRequestAlert[index] = { message: response.message, type: response.type, show: true, @@ -377,17 +317,17 @@ export default defineComponent({ } ) }, - testTibberRequest() { - this.testTibberRequestAlert = { - message: "Sending Tibber request...", + testHttpSmlRequest() { + this.testHttpSmlRequestAlert = { + message: "Sending HTTP SML request...", type: "info", show: true, }; const formData = new FormData(); - formData.append("data", JSON.stringify(this.powerMeterConfigList.tibber)); + formData.append("data", JSON.stringify(this.powerMeterConfigList.http_sml)); - fetch("/api/powermeter/testtibberrequest", { + fetch("/api/powermeter/testhttpsmlrequest", { method: "POST", headers: authHeader(), body: formData, @@ -395,7 +335,7 @@ export default defineComponent({ .then((response) => handleResponse(response, this.$emitter, this.$router)) .then( (response) => { - this.testTibberRequestAlert = { + this.testHttpSmlRequestAlert = { message: response.message, type: response.type, show: true,