252 lines
8.8 KiB
C++
252 lines
8.8 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
|
*/
|
|
#include "WebApi_powermeter.h"
|
|
#include "VeDirectFrameHandler.h"
|
|
#include "ArduinoJson.h"
|
|
#include "AsyncJson.h"
|
|
#include "Configuration.h"
|
|
#include "MqttHandleVedirectHass.h"
|
|
#include "MqttHandleHass.h"
|
|
#include "MqttSettings.h"
|
|
#include "PowerLimiter.h"
|
|
#include "PowerMeter.h"
|
|
#include "PowerMeterHttpJson.h"
|
|
#include "PowerMeterHttpSml.h"
|
|
#include "WebApi.h"
|
|
#include "helper.h"
|
|
|
|
void WebApiPowerMeterClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|
{
|
|
using std::placeholders::_1;
|
|
|
|
_server = &server;
|
|
|
|
_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/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)
|
|
{
|
|
if (!WebApi.checkCredentialsReadonly(request)) {
|
|
return;
|
|
}
|
|
|
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
|
auto& root = response->getRoot();
|
|
const CONFIG_T& config = Configuration.get();
|
|
|
|
root["enabled"] = config.PowerMeter.Enabled;
|
|
root["verbose_logging"] = config.PowerMeter.VerboseLogging;
|
|
root["source"] = config.PowerMeter.Source;
|
|
|
|
auto mqtt = root["mqtt"].to<JsonObject>();
|
|
Configuration.serializePowerMeterMqttConfig(config.PowerMeter.Mqtt, mqtt);
|
|
|
|
auto serialSdm = root["serial_sdm"].to<JsonObject>();
|
|
Configuration.serializePowerMeterSerialSdmConfig(config.PowerMeter.SerialSdm, serialSdm);
|
|
|
|
auto httpJson = root["http_json"].to<JsonObject>();
|
|
Configuration.serializePowerMeterHttpJsonConfig(config.PowerMeter.HttpJson, httpJson);
|
|
|
|
auto httpSml = root["http_sml"].to<JsonObject>();
|
|
Configuration.serializePowerMeterHttpSmlConfig(config.PowerMeter.HttpSml, httpSml);
|
|
|
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void WebApiPowerMeterClass::onAdminGet(AsyncWebServerRequest* request)
|
|
{
|
|
if (!WebApi.checkCredentials(request)) {
|
|
return;
|
|
}
|
|
|
|
this->onStatus(request);
|
|
}
|
|
|
|
void WebApiPowerMeterClass::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<bool>() && root["source"].is<uint32_t>())) {
|
|
retMsg["message"] = "Values are missing!";
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
auto checkHttpConfig = [&](JsonObject const& cfg) -> bool {
|
|
if (!cfg["url"].is<String>()
|
|
|| (!cfg["url"].as<String>().startsWith("http://")
|
|
&& !cfg["url"].as<String>().startsWith("https://"))) {
|
|
retMsg["message"] = "URL must either start with http:// or https://!";
|
|
response->setLength();
|
|
request->send(response);
|
|
return false;
|
|
}
|
|
|
|
if ((cfg["auth_type"].as<uint8_t>() != HttpRequestConfig::Auth::None)
|
|
&& (cfg["username"].as<String>().length() == 0 || cfg["password"].as<String>().length() == 0)) {
|
|
retMsg["message"] = "Username or password must not be empty!";
|
|
response->setLength();
|
|
request->send(response);
|
|
return false;
|
|
}
|
|
|
|
if (!cfg["timeout"].is<uint16_t>()
|
|
|| cfg["timeout"].as<uint16_t>() <= 0) {
|
|
retMsg["message"] = "Timeout must be greater than 0 ms!";
|
|
response->setLength();
|
|
request->send(response);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
if (static_cast<PowerMeterProvider::Type>(root["source"].as<uint8_t>()) == PowerMeterProvider::Type::HTTP_JSON) {
|
|
JsonObject httpJson = root["http_json"];
|
|
JsonArray valueConfigs = httpJson["values"];
|
|
for (uint8_t i = 0; i < valueConfigs.size(); i++) {
|
|
JsonObject valueConfig = valueConfigs[i].as<JsonObject>();
|
|
|
|
if (i > 0 && !valueConfig["enabled"].as<bool>()) {
|
|
continue;
|
|
}
|
|
|
|
if (i == 0 || httpJson["individual_requests"].as<bool>()) {
|
|
if (!checkHttpConfig(valueConfig["http_request"].as<JsonObject>())) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!valueConfig["json_path"].is<String>()
|
|
|| valueConfig["json_path"].as<String>().length() == 0) {
|
|
retMsg["message"] = "Json path must not be empty!";
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (static_cast<PowerMeterProvider::Type>(root["source"].as<uint8_t>()) == PowerMeterProvider::Type::HTTP_SML) {
|
|
JsonObject httpSml = root["http_sml"];
|
|
if (!checkHttpConfig(httpSml["http_request"].as<JsonObject>())) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
CONFIG_T& config = Configuration.get();
|
|
config.PowerMeter.Enabled = root["enabled"].as<bool>();
|
|
config.PowerMeter.VerboseLogging = root["verbose_logging"].as<bool>();
|
|
config.PowerMeter.Source = root["source"].as<uint8_t>();
|
|
|
|
Configuration.deserializePowerMeterMqttConfig(root["mqtt"].as<JsonObject>(),
|
|
config.PowerMeter.Mqtt);
|
|
|
|
Configuration.deserializePowerMeterSerialSdmConfig(root["serial_sdm"].as<JsonObject>(),
|
|
config.PowerMeter.SerialSdm);
|
|
|
|
Configuration.deserializePowerMeterHttpJsonConfig(root["http_json"].as<JsonObject>(),
|
|
config.PowerMeter.HttpJson);
|
|
|
|
Configuration.deserializePowerMeterHttpSmlConfig(root["http_sml"].as<JsonObject>(),
|
|
config.PowerMeter.HttpSml);
|
|
|
|
WebApi.writeConfig(retMsg);
|
|
|
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
|
|
|
PowerMeter.updateSettings();
|
|
}
|
|
|
|
void WebApiPowerMeterClass::onTestHttpJsonRequest(AsyncWebServerRequest* request)
|
|
{
|
|
if (!WebApi.checkCredentials(request)) {
|
|
return;
|
|
}
|
|
|
|
AsyncJsonResponse* asyncJsonResponse = new AsyncJsonResponse();
|
|
JsonDocument root;
|
|
if (!WebApi.parseRequestData(request, asyncJsonResponse, root)) {
|
|
return;
|
|
}
|
|
|
|
auto& retMsg = asyncJsonResponse->getRoot();
|
|
|
|
char response[256];
|
|
|
|
auto powerMeterConfig = std::make_unique<PowerMeterHttpJsonConfig>();
|
|
Configuration.deserializePowerMeterHttpJsonConfig(root["http_json"].as<JsonObject>(),
|
|
*powerMeterConfig);
|
|
auto upMeter = std::make_unique<PowerMeterHttpJson>(*powerMeterConfig);
|
|
upMeter->init();
|
|
auto res = upMeter->poll();
|
|
using values_t = PowerMeterHttpJson::power_values_t;
|
|
if (std::holds_alternative<values_t>(res)) {
|
|
retMsg["type"] = "success";
|
|
auto vals = std::get<values_t>(res);
|
|
auto pos = snprintf(response, sizeof(response), "Result: %5.2fW", vals[0]);
|
|
for (size_t i = 1; i < vals.size(); ++i) {
|
|
if (!powerMeterConfig->Values[i].Enabled) { continue; }
|
|
pos += snprintf(response + pos, sizeof(response) - pos, ", %5.2fW", vals[i]);
|
|
}
|
|
snprintf(response + pos, sizeof(response) - pos, ", Total: %5.2f", upMeter->getPowerTotal());
|
|
} else {
|
|
snprintf(response, sizeof(response), "%s", std::get<String>(res).c_str());
|
|
}
|
|
|
|
retMsg["message"] = response;
|
|
asyncJsonResponse->setLength();
|
|
request->send(asyncJsonResponse);
|
|
}
|
|
|
|
void WebApiPowerMeterClass::onTestHttpSmlRequest(AsyncWebServerRequest* request)
|
|
{
|
|
if (!WebApi.checkCredentials(request)) {
|
|
return;
|
|
}
|
|
|
|
AsyncJsonResponse* asyncJsonResponse = new AsyncJsonResponse();
|
|
JsonDocument root;
|
|
if (!WebApi.parseRequestData(request, asyncJsonResponse, root)) {
|
|
return;
|
|
}
|
|
|
|
auto& retMsg = asyncJsonResponse->getRoot();
|
|
|
|
char response[256];
|
|
|
|
auto powerMeterConfig = std::make_unique<PowerMeterHttpSmlConfig>();
|
|
Configuration.deserializePowerMeterHttpSmlConfig(root["http_sml"].as<JsonObject>(),
|
|
*powerMeterConfig);
|
|
auto upMeter = std::make_unique<PowerMeterHttpSml>(*powerMeterConfig);
|
|
upMeter->init();
|
|
auto res = upMeter->poll();
|
|
if (res.isEmpty()) {
|
|
retMsg["type"] = "success";
|
|
snprintf(response, sizeof(response), "Result: %5.2fW", upMeter->getPowerTotal());
|
|
} else {
|
|
snprintf(response, sizeof(response), "%s", res.c_str());
|
|
}
|
|
|
|
retMsg["message"] = response;
|
|
asyncJsonResponse->setLength();
|
|
request->send(asyncJsonResponse);
|
|
}
|