Prepare Release 2024.05.06 (merge development into master)
This commit is contained in:
commit
35491cafed
@ -26,7 +26,7 @@ private:
|
|||||||
String extractParam(String& authReq, const String& param, const char delimit);
|
String extractParam(String& authReq, const String& param, const char delimit);
|
||||||
String getcNonce(const int len);
|
String getcNonce(const int len);
|
||||||
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter);
|
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter);
|
||||||
bool tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit, bool signInverted);
|
bool tryGetFloatValueForPhase(int phase, String jsonPath, Unit_t unit, bool signInverted);
|
||||||
void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue);
|
void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue);
|
||||||
String sha256(const String& data);
|
String sha256(const String& data);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -47,8 +47,6 @@ lib_deps =
|
|||||||
https://github.com/coryjfowler/MCP_CAN_lib
|
https://github.com/coryjfowler/MCP_CAN_lib
|
||||||
plerup/EspSoftwareSerial @ ^8.0.1
|
plerup/EspSoftwareSerial @ ^8.0.1
|
||||||
https://github.com/dok-net/ghostl @ ^1.0.1
|
https://github.com/dok-net/ghostl @ ^1.0.1
|
||||||
mobizt/FirebaseJson @ ^3.0.6
|
|
||||||
rweather/Crypto@^0.4.0
|
|
||||||
|
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:pio-scripts/auto_firmware_version.py
|
pre:pio-scripts/auto_firmware_version.py
|
||||||
|
|||||||
@ -179,7 +179,7 @@ bool ConfigurationClass::write()
|
|||||||
powerlimiter["enabled"] = config.PowerLimiter.Enabled;
|
powerlimiter["enabled"] = config.PowerLimiter.Enabled;
|
||||||
powerlimiter["verbose_logging"] = config.PowerLimiter.VerboseLogging;
|
powerlimiter["verbose_logging"] = config.PowerLimiter.VerboseLogging;
|
||||||
powerlimiter["solar_passtrough_enabled"] = config.PowerLimiter.SolarPassThroughEnabled;
|
powerlimiter["solar_passtrough_enabled"] = config.PowerLimiter.SolarPassThroughEnabled;
|
||||||
powerlimiter["solar_passtrough_losses"] = config.PowerLimiter.SolarPassThroughLosses;
|
powerlimiter["solar_passthrough_losses"] = config.PowerLimiter.SolarPassThroughLosses;
|
||||||
powerlimiter["battery_always_use_at_night"] = config.PowerLimiter.BatteryAlwaysUseAtNight;
|
powerlimiter["battery_always_use_at_night"] = config.PowerLimiter.BatteryAlwaysUseAtNight;
|
||||||
powerlimiter["interval"] = config.PowerLimiter.Interval;
|
powerlimiter["interval"] = config.PowerLimiter.Interval;
|
||||||
powerlimiter["is_inverter_behind_powermeter"] = config.PowerLimiter.IsInverterBehindPowerMeter;
|
powerlimiter["is_inverter_behind_powermeter"] = config.PowerLimiter.IsInverterBehindPowerMeter;
|
||||||
@ -438,7 +438,7 @@ bool ConfigurationClass::read()
|
|||||||
config.PowerLimiter.Enabled = powerlimiter["enabled"] | POWERLIMITER_ENABLED;
|
config.PowerLimiter.Enabled = powerlimiter["enabled"] | POWERLIMITER_ENABLED;
|
||||||
config.PowerLimiter.VerboseLogging = powerlimiter["verbose_logging"] | VERBOSE_LOGGING;
|
config.PowerLimiter.VerboseLogging = powerlimiter["verbose_logging"] | VERBOSE_LOGGING;
|
||||||
config.PowerLimiter.SolarPassThroughEnabled = powerlimiter["solar_passtrough_enabled"] | POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED;
|
config.PowerLimiter.SolarPassThroughEnabled = powerlimiter["solar_passtrough_enabled"] | POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED;
|
||||||
config.PowerLimiter.SolarPassThroughLosses = powerlimiter["solar_passthrough_losses"] | POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES;
|
config.PowerLimiter.SolarPassThroughLosses = powerlimiter["solar_passthrough_losses"] | powerlimiter["solar_passtrough_losses"] | POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES; // solar_passthrough_losses was previously saved as solar_passtrough_losses. Be nice and also try mistyped key.
|
||||||
config.PowerLimiter.BatteryAlwaysUseAtNight = powerlimiter["battery_always_use_at_night"] | POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT;
|
config.PowerLimiter.BatteryAlwaysUseAtNight = powerlimiter["battery_always_use_at_night"] | POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT;
|
||||||
if (powerlimiter["battery_drain_strategy"].as<uint8_t>() == 1) { config.PowerLimiter.BatteryAlwaysUseAtNight = true; } // convert legacy setting
|
if (powerlimiter["battery_drain_strategy"].as<uint8_t>() == 1) { config.PowerLimiter.BatteryAlwaysUseAtNight = true; } // convert legacy setting
|
||||||
config.PowerLimiter.Interval = powerlimiter["interval"] | POWERLIMITER_INTERVAL;
|
config.PowerLimiter.Interval = powerlimiter["interval"] | POWERLIMITER_INTERVAL;
|
||||||
|
|||||||
@ -3,9 +3,8 @@
|
|||||||
#include "HttpPowerMeter.h"
|
#include "HttpPowerMeter.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include <FirebaseJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <Crypto.h>
|
#include "mbedtls/sha256.h"
|
||||||
#include <SHA256.h>
|
|
||||||
#include <base64.h>
|
#include <base64.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ESPmDNS.h>
|
#include <ESPmDNS.h>
|
||||||
@ -219,18 +218,43 @@ String HttpPowerMeterClass::getDigestAuth(String& authReq, const String& usernam
|
|||||||
return authorization;
|
return authorization;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit, bool signInverted)
|
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, String jsonPath, Unit_t unit, bool signInverted)
|
||||||
{
|
{
|
||||||
FirebaseJson json;
|
JsonDocument root;
|
||||||
json.setJsonData(httpResponse);
|
const DeserializationError error = deserializeJson(root, httpResponse);
|
||||||
FirebaseJsonData value;
|
if (error) {
|
||||||
if (!json.get(value, jsonPath)) {
|
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError),
|
||||||
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("[HttpPowerMeter] Couldn't find a value for phase %i with Json query \"%s\""), phase, jsonPath);
|
PSTR("[HttpPowerMeter] Unable to parse server response as JSON"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char delimiter = '/';
|
||||||
|
int start = 0;
|
||||||
|
int end = jsonPath.indexOf(delimiter);
|
||||||
|
auto value = root.as<JsonVariantConst>();
|
||||||
|
|
||||||
|
// NOTE: "Because ArduinoJson implements the Null Object Pattern, it is
|
||||||
|
// always safe to read the object: if the key doesn't exist, it returns an
|
||||||
|
// empty value."
|
||||||
|
while (end != -1) {
|
||||||
|
String key = jsonPath.substring(start, end);
|
||||||
|
value = value[key];
|
||||||
|
start = end + 1;
|
||||||
|
end = jsonPath.indexOf(delimiter, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
String lastKey = jsonPath.substring(start);
|
||||||
|
value = value[lastKey];
|
||||||
|
|
||||||
|
if (value.isNull()) {
|
||||||
|
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError),
|
||||||
|
PSTR("[HttpPowerMeter] Unable to find a value for phase %i with JSON path \"%s\""),
|
||||||
|
phase+1, jsonPath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this value is supposed to be in Watts and positive if energy is consumed.
|
// this value is supposed to be in Watts and positive if energy is consumed.
|
||||||
power[phase] = value.to<float>();
|
power[phase] = value.as<float>();
|
||||||
|
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case Unit_t::MilliWatts:
|
case Unit_t::MilliWatts:
|
||||||
@ -299,27 +323,24 @@ bool HttpPowerMeterClass::extractUrlComponents(String url, String& _protocol, St
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HASH_SIZE 32
|
|
||||||
|
|
||||||
String HttpPowerMeterClass::sha256(const String& data) {
|
String HttpPowerMeterClass::sha256(const String& data) {
|
||||||
SHA256 sha256;
|
uint8_t hash[32];
|
||||||
uint8_t hash[HASH_SIZE];
|
|
||||||
|
|
||||||
sha256.reset();
|
mbedtls_sha256_context ctx;
|
||||||
sha256.update(data.c_str(), data.length());
|
mbedtls_sha256_init(&ctx);
|
||||||
sha256.finalize(hash, HASH_SIZE);
|
mbedtls_sha256_starts(&ctx, 0); // select SHA256
|
||||||
|
mbedtls_sha256_update(&ctx, reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
|
||||||
|
mbedtls_sha256_finish(&ctx, hash);
|
||||||
|
mbedtls_sha256_free(&ctx);
|
||||||
|
|
||||||
String hashStr = "";
|
char res[sizeof(hash) * 2 + 1];
|
||||||
for (int i = 0; i < HASH_SIZE; i++) {
|
for (int i = 0; i < sizeof(hash); i++) {
|
||||||
String hex = String(hash[i], HEX);
|
snprintf(res + (i*2), sizeof(res) - (i*2), "%02x", hash[i]);
|
||||||
if (hex.length() == 1) {
|
|
||||||
hashStr += "0";
|
|
||||||
}
|
|
||||||
hashStr += hex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashStr;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpPowerMeterClass::prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue) {
|
void HttpPowerMeterClass::prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue) {
|
||||||
httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
httpClient.setUserAgent("OpenDTU-OnBattery");
|
httpClient.setUserAgent("OpenDTU-OnBattery");
|
||||||
|
|||||||
@ -577,7 +577,7 @@
|
|||||||
"httpHeaderKeyDescription": "Ein individueller HTTP request header kann hier definiert werden. Das kann z.B. verwendet werden um einen eigenen Authorization header mitzugeben.",
|
"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",
|
"httpHeaderValue": "Optional: HTTP request header - Wert",
|
||||||
"httpJsonPath": "JSON Pfad",
|
"httpJsonPath": "JSON Pfad",
|
||||||
"httpJsonPathDescription": "JSON Pfad um den Leistungswert zu finden. Es verwendet die Selektions-Syntax von mobizt/FirebaseJson. Beispiele gibt es oben.",
|
"httpJsonPathDescription": "Anwendungsspezifischer JSON-Pfad um den Leistungswert in the HTTP(S) Antwort zu finden, z.B. 'power/total/watts' oder nur 'total'.",
|
||||||
"httpUnit": "Einheit",
|
"httpUnit": "Einheit",
|
||||||
"httpSignInverted": "Vorzeichen umkehren",
|
"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.",
|
"httpSignInvertedHint": "Positive Werte werden als Leistungsabnahme aus dem Netz interpretiert. Diese Option muss aktiviert werden, wenn das Vorzeichen des Wertes die gegenteilige Bedeutung hat.",
|
||||||
|
|||||||
@ -582,7 +582,7 @@
|
|||||||
"httpHeaderKeyDescription": "A custom HTTP request header can be defined. Might be useful if you have to send something like a custom Authorization header.",
|
"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",
|
"httpHeaderValue": "Optional: HTTP request header - Value",
|
||||||
"httpJsonPath": "JSON path",
|
"httpJsonPath": "JSON path",
|
||||||
"httpJsonPathDescription": "JSON path to find the power value in the response. This uses the JSON path query syntax from mobizt/FirebaseJson. See above for examples.",
|
"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",
|
"httpUnit": "Unit",
|
||||||
"httpSignInverted": "Change Sign",
|
"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.",
|
"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.",
|
||||||
|
|||||||
@ -116,11 +116,9 @@
|
|||||||
|
|
||||||
<h2>JSON path examples:</h2>
|
<h2>JSON path examples:</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>total_power - { "othervalue": "blah", "total_power": 123.4 }</li>
|
<li><code>power/total/watts</code> - Finds 123.4 in <code>{ "power": { "phase1": { "factor": 0.98, "watts": 42 }, "total": { "watts": 123.4 } } }</code></li>
|
||||||
<li>testarray/[2]/myvalue - { "testarray": [ {}, { "power": 123.4 } ] }</li>
|
<li><code>total</code> - Finds 123.4 in <code>{ "othervalue": 66, "total": 123.4 }</code></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
More info: <a href="https://github.com/mobizt/FirebaseJson">https://github.com/mobizt/FirebaseJson</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CardElement
|
<CardElement
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user