Skip to content

Commit 35491ca

Browse files
authored
Prepare Release 2024.05.06 (merge development into master)
2 parents d2990bd + 2f7e1f3 commit 35491ca

File tree

7 files changed

+55
-38
lines changed

7 files changed

+55
-38
lines changed

include/HttpPowerMeter.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class HttpPowerMeterClass {
2626
String extractParam(String& authReq, const String& param, const char delimit);
2727
String getcNonce(const int len);
2828
String getDigestAuth(String& authReq, const String& username, const String& password, const String& method, const String& uri, unsigned int counter);
29-
bool tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit, bool signInverted);
29+
bool tryGetFloatValueForPhase(int phase, String jsonPath, Unit_t unit, bool signInverted);
3030
void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue);
3131
String sha256(const String& data);
3232
};

platformio.ini

-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ lib_deps =
4747
https://github.com/coryjfowler/MCP_CAN_lib
4848
plerup/EspSoftwareSerial @ ^8.0.1
4949
https://github.com/dok-net/ghostl @ ^1.0.1
50-
mobizt/FirebaseJson @ ^3.0.6
51-
rweather/Crypto@^0.4.0
5250

5351
extra_scripts =
5452
pre:pio-scripts/auto_firmware_version.py

src/Configuration.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ bool ConfigurationClass::write()
179179
powerlimiter["enabled"] = config.PowerLimiter.Enabled;
180180
powerlimiter["verbose_logging"] = config.PowerLimiter.VerboseLogging;
181181
powerlimiter["solar_passtrough_enabled"] = config.PowerLimiter.SolarPassThroughEnabled;
182-
powerlimiter["solar_passtrough_losses"] = config.PowerLimiter.SolarPassThroughLosses;
182+
powerlimiter["solar_passthrough_losses"] = config.PowerLimiter.SolarPassThroughLosses;
183183
powerlimiter["battery_always_use_at_night"] = config.PowerLimiter.BatteryAlwaysUseAtNight;
184184
powerlimiter["interval"] = config.PowerLimiter.Interval;
185185
powerlimiter["is_inverter_behind_powermeter"] = config.PowerLimiter.IsInverterBehindPowerMeter;
@@ -438,7 +438,7 @@ bool ConfigurationClass::read()
438438
config.PowerLimiter.Enabled = powerlimiter["enabled"] | POWERLIMITER_ENABLED;
439439
config.PowerLimiter.VerboseLogging = powerlimiter["verbose_logging"] | VERBOSE_LOGGING;
440440
config.PowerLimiter.SolarPassThroughEnabled = powerlimiter["solar_passtrough_enabled"] | POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED;
441-
config.PowerLimiter.SolarPassThroughLosses = powerlimiter["solar_passthrough_losses"] | POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES;
441+
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.
442442
config.PowerLimiter.BatteryAlwaysUseAtNight = powerlimiter["battery_always_use_at_night"] | POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT;
443443
if (powerlimiter["battery_drain_strategy"].as<uint8_t>() == 1) { config.PowerLimiter.BatteryAlwaysUseAtNight = true; } // convert legacy setting
444444
config.PowerLimiter.Interval = powerlimiter["interval"] | POWERLIMITER_INTERVAL;

src/HttpPowerMeter.cpp

+48-27
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
#include "HttpPowerMeter.h"
44
#include "MessageOutput.h"
55
#include <WiFiClientSecure.h>
6-
#include <FirebaseJson.h>
7-
#include <Crypto.h>
8-
#include <SHA256.h>
6+
#include <ArduinoJson.h>
7+
#include "mbedtls/sha256.h"
98
#include <base64.h>
109
#include <memory>
1110
#include <ESPmDNS.h>
@@ -219,18 +218,43 @@ String HttpPowerMeterClass::getDigestAuth(String& authReq, const String& usernam
219218
return authorization;
220219
}
221220

222-
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit, bool signInverted)
221+
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, String jsonPath, Unit_t unit, bool signInverted)
223222
{
224-
FirebaseJson json;
225-
json.setJsonData(httpResponse);
226-
FirebaseJsonData value;
227-
if (!json.get(value, jsonPath)) {
228-
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), PSTR("[HttpPowerMeter] Couldn't find a value for phase %i with Json query \"%s\""), phase, jsonPath);
223+
JsonDocument root;
224+
const DeserializationError error = deserializeJson(root, httpResponse);
225+
if (error) {
226+
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError),
227+
PSTR("[HttpPowerMeter] Unable to parse server response as JSON"));
228+
return false;
229+
}
230+
231+
constexpr char delimiter = '/';
232+
int start = 0;
233+
int end = jsonPath.indexOf(delimiter);
234+
auto value = root.as<JsonVariantConst>();
235+
236+
// NOTE: "Because ArduinoJson implements the Null Object Pattern, it is
237+
// always safe to read the object: if the key doesn't exist, it returns an
238+
// empty value."
239+
while (end != -1) {
240+
String key = jsonPath.substring(start, end);
241+
value = value[key];
242+
start = end + 1;
243+
end = jsonPath.indexOf(delimiter, start);
244+
}
245+
246+
String lastKey = jsonPath.substring(start);
247+
value = value[lastKey];
248+
249+
if (value.isNull()) {
250+
snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError),
251+
PSTR("[HttpPowerMeter] Unable to find a value for phase %i with JSON path \"%s\""),
252+
phase+1, jsonPath.c_str());
229253
return false;
230254
}
231255

232256
// this value is supposed to be in Watts and positive if energy is consumed.
233-
power[phase] = value.to<float>();
257+
power[phase] = value.as<float>();
234258

235259
switch (unit) {
236260
case Unit_t::MilliWatts:
@@ -299,27 +323,24 @@ bool HttpPowerMeterClass::extractUrlComponents(String url, String& _protocol, St
299323
return true;
300324
}
301325

302-
#define HASH_SIZE 32
303-
304326
String HttpPowerMeterClass::sha256(const String& data) {
305-
SHA256 sha256;
306-
uint8_t hash[HASH_SIZE];
307-
308-
sha256.reset();
309-
sha256.update(data.c_str(), data.length());
310-
sha256.finalize(hash, HASH_SIZE);
311-
312-
String hashStr = "";
313-
for (int i = 0; i < HASH_SIZE; i++) {
314-
String hex = String(hash[i], HEX);
315-
if (hex.length() == 1) {
316-
hashStr += "0";
317-
}
318-
hashStr += hex;
327+
uint8_t hash[32];
328+
329+
mbedtls_sha256_context ctx;
330+
mbedtls_sha256_init(&ctx);
331+
mbedtls_sha256_starts(&ctx, 0); // select SHA256
332+
mbedtls_sha256_update(&ctx, reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
333+
mbedtls_sha256_finish(&ctx, hash);
334+
mbedtls_sha256_free(&ctx);
335+
336+
char res[sizeof(hash) * 2 + 1];
337+
for (int i = 0; i < sizeof(hash); i++) {
338+
snprintf(res + (i*2), sizeof(res) - (i*2), "%02x", hash[i]);
319339
}
320340

321-
return hashStr;
341+
return res;
322342
}
343+
323344
void HttpPowerMeterClass::prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue) {
324345
httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
325346
httpClient.setUserAgent("OpenDTU-OnBattery");

webapp/src/locales/de.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@
577577
"httpHeaderKeyDescription": "Ein individueller HTTP request header kann hier definiert werden. Das kann z.B. verwendet werden um einen eigenen Authorization header mitzugeben.",
578578
"httpHeaderValue": "Optional: HTTP request header - Wert",
579579
"httpJsonPath": "JSON Pfad",
580-
"httpJsonPathDescription": "JSON Pfad um den Leistungswert zu finden. Es verwendet die Selektions-Syntax von mobizt/FirebaseJson. Beispiele gibt es oben.",
580+
"httpJsonPathDescription": "Anwendungsspezifischer JSON-Pfad um den Leistungswert in the HTTP(S) Antwort zu finden, z.B. 'power/total/watts' oder nur 'total'.",
581581
"httpUnit": "Einheit",
582582
"httpSignInverted": "Vorzeichen umkehren",
583583
"httpSignInvertedHint": "Positive Werte werden als Leistungsabnahme aus dem Netz interpretiert. Diese Option muss aktiviert werden, wenn das Vorzeichen des Wertes die gegenteilige Bedeutung hat.",

webapp/src/locales/en.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@
582582
"httpHeaderKeyDescription": "A custom HTTP request header can be defined. Might be useful if you have to send something like a custom Authorization header.",
583583
"httpHeaderValue": "Optional: HTTP request header - Value",
584584
"httpJsonPath": "JSON path",
585-
"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.",
585+
"httpJsonPathDescription": "Application specific JSON path to find the power value in the HTTP(S) response, e.g., 'power/total/watts' or simply 'total'.",
586586
"httpUnit": "Unit",
587587
"httpSignInverted": "Change Sign",
588588
"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.",

webapp/src/views/PowerMeterAdminView.vue

+2-4
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,9 @@
116116

117117
<h2>JSON path examples:</h2>
118118
<ul>
119-
<li>total_power - { "othervalue": "blah", "total_power": 123.4 }</li>
120-
<li>testarray/[2]/myvalue - { "testarray": [ {}, { "power": 123.4 } ] }</li>
119+
<li><code>power/total/watts</code> - Finds 123.4 in <code>{ "power": { "phase1": { "factor": 0.98, "watts": 42 }, "total": { "watts": 123.4 } } }</code></li>
120+
<li><code>total</code> - Finds 123.4 in <code>{ "othervalue": 66, "total": 123.4 }</code></li>
121121
</ul>
122-
123-
More info: <a href="https://github.com/mobizt/FirebaseJson">https://github.com/mobizt/FirebaseJson</a>
124122
</div>
125123

126124
<CardElement

0 commit comments

Comments
 (0)