OpenDTU-old/src/PowerMeterHttpJson.cpp
Bernhard Kirchen a08ef4cb43 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.
2024-06-27 22:18:41 +02:00

137 lines
3.9 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#include "Utils.h"
#include "Configuration.h"
#include "PowerMeterHttpJson.h"
#include "MessageOutput.h"
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "mbedtls/sha256.h"
#include <base64.h>
#include <ESPmDNS.h>
bool PowerMeterHttpJson::init()
{
auto const& config = Configuration.get();
for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) {
auto const& valueConfig = config.PowerMeter.HttpJson[i];
_httpGetters[i] = nullptr;
if (i == 0 || (config.PowerMeter.HttpIndividualRequests && valueConfig.Enabled)) {
_httpGetters[i] = std::make_unique<HttpGetter>(valueConfig.HttpRequest);
}
if (!_httpGetters[i]) { continue; }
if (_httpGetters[i]->init()) {
_httpGetters[i]->addHeader("Content-Type", "application/json");
_httpGetters[i]->addHeader("Accept", "application/json");
continue;
}
MessageOutput.printf("[PowerMeterHttpJson] Initializing HTTP getter for value %d failed:\r\n", i + 1);
MessageOutput.printf("[PowerMeterHttpJson] %s\r\n", _httpGetters[i]->getErrorText());
return false;
}
return true;
}
void PowerMeterHttpJson::loop()
{
auto const& config = Configuration.get();
if ((millis() - _lastPoll) < (config.PowerMeter.Interval * 1000)) {
return;
}
_lastPoll = millis();
auto res = poll();
if (std::holds_alternative<String>(res)) {
MessageOutput.printf("[PowerMeterHttpJson] %s\r\n", std::get<String>(res).c_str());
return;
}
gotUpdate();
}
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];
if (!cfg.Enabled) {
cache[i] = 0.0;
continue;
}
auto const& upGetter = _httpGetters[i];
if (upGetter) {
auto res = upGetter->performGetRequest();
if (!res) {
return prefixedError(i, upGetter->getErrorText());
}
auto pStream = res.getStream();
if (!pStream) {
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 prefixedError(i, String(msg + error.c_str()).c_str());
}
}
auto pathResolutionResult = Utils::getJsonValueByPath<float>(jsonResponse, cfg.JsonPath);
if (!pathResolutionResult.second.isEmpty()) {
return prefixedError(i, pathResolutionResult.second.c_str());
}
// this value is supposed to be in Watts and positive if energy is consumed
cache[i] = pathResolutionResult.first;
switch (cfg.PowerUnit) {
case Unit_t::MilliWatts:
cache[i] /= 1000;
break;
case Unit_t::KiloWatts:
cache[i] *= 1000;
break;
default:
break;
}
if (cfg.SignInverted) { cache[i] *= -1; }
}
_powerValues = cache;
return cache;
}
float PowerMeterHttpJson::getPowerTotal() const
{
float sum = 0.0;
for (auto v: _powerValues) { sum += v; }
return sum;
}
void PowerMeterHttpJson::doMqttPublish() const
{
mqttPublish("power1", _powerValues[0]);
mqttPublish("power2", _powerValues[1]);
mqttPublish("power3", _powerValues[2]);
}