Skip to content

Commit

Permalink
powermeter refactor: test HTTP+JSON power meter as a whole
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
schlimmchen committed Jun 27, 2024
1 parent 20ecf2a commit a08ef4c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 58 deletions.
16 changes: 11 additions & 5 deletions src/PowerMeterHttpJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ void PowerMeterHttpJson::loop()
return;
}

_powerValues = std::get<power_values_t>(res);
gotUpdate();
}

Expand All @@ -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];

Expand All @@ -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
Expand All @@ -112,6 +117,7 @@ PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
if (cfg.SignInverted) { cache[i] *= -1; }
}

_powerValues = cache;
return cache;
}

Expand Down
32 changes: 13 additions & 19 deletions src/WebApi_powermeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
52 changes: 20 additions & 32 deletions webapp/src/views/PowerMeterAdminView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,23 @@
v-model="httpJson.sign_inverted"
:tooltip="$t('powermeteradmin.httpSignInvertedHint')"
type="checkbox" />
</div>
</CardElement>

<div class="text-center mb-3">
<button type="button" class="btn btn-danger" @click="testHttpJsonRequest(index)">
{{ $t('powermeteradmin.testHttpJsonRequest') }}
</button>
</div>
<CardElement
:text="$t('powermeteradmin.testHttpJsonHeader')"
textVariant="text-bg-primary"
add-space>

<BootstrapAlert v-model="testHttpJsonRequestAlert[index].show" dismissible :variant="testHttpJsonRequestAlert[index].type">
{{ testHttpJsonRequestAlert[index].message }}
</BootstrapAlert>
<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>

Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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; }
};
},
Expand All @@ -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) {
Expand All @@ -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",
Expand All @@ -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,
Expand Down

0 comments on commit a08ef4c

Please sign in to comment.