powermeter refactor: test HTTP+JSON power meter as a whole

apply all config values from the webfrontend, then perform one polling
cycle. display values seperately in the result, and show the resulting
value as well.
This commit is contained in:
Bernhard Kirchen 2024-05-17 21:12:14 +02:00
parent 20ecf2a66b
commit a08ef4cb43
5 changed files with 50 additions and 60 deletions

View File

@ -53,7 +53,6 @@ void PowerMeterHttpJson::loop()
return;
}
_powerValues = std::get<power_values_t>(res);
gotUpdate();
}
@ -62,6 +61,12 @@ PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
power_values_t cache;
JsonDocument jsonResponse;
auto prefixedError = [](uint8_t idx, char const* err) -> String {
String res("Value ");
res.reserve(strlen(err) + 16);
return res + String(idx + 1) + ": " + err;
};
for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) {
auto const& cfg = Configuration.get().PowerMeter.HttpJson[i];
@ -75,24 +80,24 @@ PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
if (upGetter) {
auto res = upGetter->performGetRequest();
if (!res) {
return upGetter->getErrorText();
return prefixedError(i, upGetter->getErrorText());
}
auto pStream = res.getStream();
if (!pStream) {
return String("Programmer error: HTTP request yields no stream");
return prefixedError(i, "Programmer error: HTTP request yields no stream");
}
const DeserializationError error = deserializeJson(jsonResponse, *pStream);
if (error) {
String msg("Unable to parse server response as JSON: ");
return msg + error.c_str();
return prefixedError(i, String(msg + error.c_str()).c_str());
}
}
auto pathResolutionResult = Utils::getJsonValueByPath<float>(jsonResponse, cfg.JsonPath);
if (!pathResolutionResult.second.isEmpty()) {
return pathResolutionResult.second;
return prefixedError(i, pathResolutionResult.second.c_str());
}
// this value is supposed to be in Watts and positive if energy is consumed
@ -112,6 +117,7 @@ PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
if (cfg.SignInverted) { cache[i] *= -1; }
}
_powerValues = cache;
return cache;
}

View File

@ -192,26 +192,15 @@ void WebApiPowerMeterClass::onTestHttpJsonRequest(AsyncWebServerRequest* request
auto& retMsg = asyncJsonResponse->getRoot();
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);
return;
}
char response[256];
auto powerMeterConfig = std::make_unique<CONFIG_T::PowerMeterConfig>();
Configuration.deserializePowerMeterHttpJsonConfig(root.as<JsonObject>(), powerMeterConfig->HttpJson[0]);
powerMeterConfig->HttpIndividualRequests = root["http_individual_requests"].as<bool>();
JsonArray httpJson = root["http_json"];
for (uint8_t i = 0; i < httpJson.size(); i++) {
Configuration.deserializePowerMeterHttpJsonConfig(httpJson[i].as<JsonObject>(),
powerMeterConfig->HttpJson[i]);
}
auto backup = std::make_unique<CONFIG_T::PowerMeterConfig>(Configuration.get().PowerMeter);
Configuration.get().PowerMeter = *powerMeterConfig;
auto upMeter = std::make_unique<PowerMeterHttpJson>();
@ -222,9 +211,14 @@ void WebApiPowerMeterClass::onTestHttpJsonRequest(AsyncWebServerRequest* request
if (std::holds_alternative<values_t>(res)) {
retMsg["type"] = "success";
auto vals = std::get<values_t>(res);
snprintf_P(response, sizeof(response), "Result: %5.2fW", vals[0]);
auto pos = snprintf(response, sizeof(response), "Result: %5.2fW", vals[0]);
for (size_t i = 1; i < POWERMETER_HTTP_JSON_MAX_VALUES; ++i) {
if (!powerMeterConfig->HttpJson[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_P(response, sizeof(response), "%s", std::get<String>(res).c_str());
snprintf(response, sizeof(response), "%s", std::get<String>(res).c_str());
}
retMsg["message"] = response;

View File

@ -581,7 +581,8 @@
"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.",
"testHttpJsonRequest": "Konfiguration testen (HTTP(S)-Anfrage senden)",
"testHttpJsonHeader": "Konfiguration testen",
"testHttpJsonRequest": "HTTP(S)-Anfrage(n) senden und Antwort(en) verarbeiten",
"testHttpSmlRequest": "Konfiguration testen (HTTP(S)-Anfrage senden)",
"HTTP_SML": "HTTP(S) + SML - Konfiguration"
},

View File

@ -583,7 +583,8 @@
"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.",
"testHttpJsonRequest": "Test configuration (send HTTP(S) request)",
"testHttpJsonHeader": "Test Configuration",
"testHttpJsonRequest": "Send HTTP(S) request(s) and process response(s)",
"testHttpSmlRequest": "Test configuration (send HTTP(S) request)",
"HTTP_SML": "Configuration"
},

View File

@ -154,18 +154,24 @@
v-model="httpJson.sign_inverted"
:tooltip="$t('powermeteradmin.httpSignInvertedHint')"
type="checkbox" />
<div class="text-center mb-3">
<button type="button" class="btn btn-danger" @click="testHttpJsonRequest(index)">
{{ $t('powermeteradmin.testHttpJsonRequest') }}
</button>
</div>
<BootstrapAlert v-model="testHttpJsonRequestAlert[index].show" dismissible :variant="testHttpJsonRequestAlert[index].type">
{{ testHttpJsonRequestAlert[index].message }}
</BootstrapAlert>
</div>
</CardElement>
<CardElement
:text="$t('powermeteradmin.testHttpJsonHeader')"
textVariant="text-bg-primary"
add-space>
<div class="text-center mt-3 mb-3">
<button type="button" class="btn btn-primary" @click="testHttpJsonRequest()">
{{ $t('powermeteradmin.testHttpJsonRequest') }}
</button>
</div>
<BootstrapAlert v-model="testHttpJsonRequestAlert.show" dismissible :variant="testHttpJsonRequestAlert.type">
{{ testHttpJsonRequestAlert.message }}
</BootstrapAlert>
</CardElement>
</div>
<div v-if="powerMeterConfigList.source === 6">
@ -203,7 +209,7 @@ 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 { PowerMeterHttpJsonConfig, PowerMeterConfig } from "@/types/PowerMeterConfig";
import type { PowerMeterConfig } from "@/types/PowerMeterConfig";
export default defineComponent({
components: {
@ -235,7 +241,7 @@ export default defineComponent({
alertMessage: "",
alertType: "info",
showAlert: false,
testHttpJsonRequestAlert: [{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; }
};
},
@ -250,14 +256,6 @@ export default defineComponent({
.then((data) => {
this.powerMeterConfigList = data;
this.dataLoading = false;
for (let i = 0; i < this.powerMeterConfigList.http_json.length; i++) {
this.testHttpJsonRequestAlert.push({
message: "",
type: "",
show: false,
});
}
});
},
savePowerMeterConfig(e: Event) {
@ -281,25 +279,15 @@ export default defineComponent({
}
);
},
testHttpJsonRequest(index: number) {
let valueConfig:PowerMeterHttpJsonConfig;
if (this.powerMeterConfigList.http_individual_requests) {
valueConfig = this.powerMeterConfigList.http_json[index];
} else {
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.testHttpJsonRequestAlert[index] = {
testHttpJsonRequest() {
this.testHttpJsonRequestAlert = {
message: "Sending HTTP request...",
type: "info",
show: true,
};
const formData = new FormData();
formData.append("data", JSON.stringify(valueConfig));
formData.append("data", JSON.stringify(this.powerMeterConfigList));
fetch("/api/powermeter/testhttpjsonrequest", {
method: "POST",
@ -309,7 +297,7 @@ export default defineComponent({
.then((response) => handleResponse(response, this.$emitter, this.$router))
.then(
(response) => {
this.testHttpJsonRequestAlert[index] = {
this.testHttpJsonRequestAlert = {
message: response.message,
type: response.type,
show: true,