Feature: HTTP power meter: support changing sign

This commit is contained in:
Bernhard Kirchen 2024-04-17 21:53:46 +02:00 committed by Bernhard Kirchen
parent ede1abb5e6
commit abe01ae36f
9 changed files with 26 additions and 5 deletions

View File

@ -75,6 +75,7 @@ struct POWERMETER_HTTP_PHASE_CONFIG_T {
uint16_t Timeout; uint16_t Timeout;
char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1]; char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1];
Unit PowerUnit; Unit PowerUnit;
bool SignInverted;
}; };
using PowerMeterHttpConfig = struct POWERMETER_HTTP_PHASE_CONFIG_T; using PowerMeterHttpConfig = struct POWERMETER_HTTP_PHASE_CONFIG_T;

View File

@ -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 tryGetFloatValueForPhase(int phase, const char* 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);
}; };

View File

@ -176,6 +176,7 @@ bool ConfigurationClass::write()
powermeter_phase["timeout"] = config.PowerMeter.Http_Phase[i].Timeout; powermeter_phase["timeout"] = config.PowerMeter.Http_Phase[i].Timeout;
powermeter_phase["json_path"] = config.PowerMeter.Http_Phase[i].JsonPath; powermeter_phase["json_path"] = config.PowerMeter.Http_Phase[i].JsonPath;
powermeter_phase["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit; powermeter_phase["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit;
powermeter_phase["sign_inverted"] = config.PowerMeter.Http_Phase[i].SignInverted;
} }
JsonObject powerlimiter = doc.createNestedObject("powerlimiter"); JsonObject powerlimiter = doc.createNestedObject("powerlimiter");
@ -429,6 +430,7 @@ bool ConfigurationClass::read()
config.PowerMeter.Http_Phase[i].Timeout = powermeter_phase["timeout"] | POWERMETER_HTTP_TIMEOUT; config.PowerMeter.Http_Phase[i].Timeout = powermeter_phase["timeout"] | POWERMETER_HTTP_TIMEOUT;
strlcpy(config.PowerMeter.Http_Phase[i].JsonPath, powermeter_phase["json_path"] | "", sizeof(config.PowerMeter.Http_Phase[i].JsonPath)); strlcpy(config.PowerMeter.Http_Phase[i].JsonPath, powermeter_phase["json_path"] | "", sizeof(config.PowerMeter.Http_Phase[i].JsonPath));
config.PowerMeter.Http_Phase[i].PowerUnit = powermeter_phase["unit"] | PowerMeterHttpConfig::Unit::Watts; config.PowerMeter.Http_Phase[i].PowerUnit = powermeter_phase["unit"] | PowerMeterHttpConfig::Unit::Watts;
config.PowerMeter.Http_Phase[i].SignInverted = powermeter_phase["sign_inverted"] | false;
} }
JsonObject powerlimiter = doc["powerlimiter"]; JsonObject powerlimiter = doc["powerlimiter"];

View File

@ -42,7 +42,7 @@ bool HttpPowerMeterClass::updateValues()
continue; continue;
} }
if(!tryGetFloatValueForPhase(i, phaseConfig.JsonPath, phaseConfig.PowerUnit)) { if(!tryGetFloatValueForPhase(i, phaseConfig.JsonPath, phaseConfig.PowerUnit, phaseConfig.SignInverted)) {
MessageOutput.printf("[HttpPowerMeter] Getting the power of phase %d (from JSON fetched with Phase 1 config) failed.\r\n", i + 1); MessageOutput.printf("[HttpPowerMeter] Getting the power of phase %d (from JSON fetched with Phase 1 config) failed.\r\n", i + 1);
MessageOutput.printf("%s\r\n", httpPowerMeterError); MessageOutput.printf("%s\r\n", httpPowerMeterError);
return false; return false;
@ -161,7 +161,7 @@ bool HttpPowerMeterClass::httpRequest(int phase, WiFiClient &wifiClient, const S
// TODO(schlimmchen): postpone calling tryGetFloatValueForPhase, as it // TODO(schlimmchen): postpone calling tryGetFloatValueForPhase, as it
// will be called twice for each phase when doing separate requests. // will be called twice for each phase when doing separate requests.
return tryGetFloatValueForPhase(phase, config.JsonPath, config.PowerUnit); return tryGetFloatValueForPhase(phase, config.JsonPath, config.PowerUnit, config.SignInverted);
} }
String HttpPowerMeterClass::extractParam(String& authReq, const String& param, const char delimit) { String HttpPowerMeterClass::extractParam(String& authReq, const String& param, const char delimit) {
@ -219,7 +219,7 @@ String HttpPowerMeterClass::getDigestAuth(String& authReq, const String& usernam
return authorization; return authorization;
} }
bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit) bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPath, Unit_t unit, bool signInverted)
{ {
FirebaseJson json; FirebaseJson json;
json.setJsonData(httpResponse); json.setJsonData(httpResponse);
@ -229,7 +229,9 @@ bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPa
return false; return false;
} }
power[phase] = value.to<float>(); // this is supposed to be in Watts // this value is supposed to be in Watts and positive if energy is consumed.
power[phase] = value.to<float>();
switch (unit) { switch (unit) {
case Unit_t::MilliWatts: case Unit_t::MilliWatts:
power[phase] /= 1000; power[phase] /= 1000;
@ -240,6 +242,9 @@ bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, const char* jsonPa
default: default:
break; break;
} }
if (signInverted) { power[phase] *= -1; }
return true; return true;
} }

View File

@ -40,6 +40,7 @@ void WebApiPowerMeterClass::decodeJsonPhaseConfig(JsonObject const& json, PowerM
config.Timeout = json["timeout"].as<uint16_t>(); config.Timeout = json["timeout"].as<uint16_t>();
strlcpy(config.JsonPath, json["json_path"].as<String>().c_str(), sizeof(config.JsonPath)); strlcpy(config.JsonPath, json["json_path"].as<String>().c_str(), sizeof(config.JsonPath));
config.PowerUnit = json["unit"].as<PowerMeterHttpConfig::Unit>(); config.PowerUnit = json["unit"].as<PowerMeterHttpConfig::Unit>();
config.SignInverted = json["sign_inverted"].as<bool>();
} }
void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request) void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request)
@ -75,6 +76,7 @@ void WebApiPowerMeterClass::onStatus(AsyncWebServerRequest* request)
phaseObject["timeout"] = config.PowerMeter.Http_Phase[i].Timeout; phaseObject["timeout"] = config.PowerMeter.Http_Phase[i].Timeout;
phaseObject["json_path"] = String(config.PowerMeter.Http_Phase[i].JsonPath); phaseObject["json_path"] = String(config.PowerMeter.Http_Phase[i].JsonPath);
phaseObject["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit; phaseObject["unit"] = config.PowerMeter.Http_Phase[i].PowerUnit;
phaseObject["sign_inverted"] = config.PowerMeter.Http_Phase[i].SignInverted;
} }
response->setLength(); response->setLength();

View File

@ -576,6 +576,8 @@
"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": "JSON Pfad um den Leistungswert zu finden. Es verwendet die Selektions-Syntax von mobizt/FirebaseJson. Beispiele gibt es oben.",
"httpUnit": "Einheit", "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.",
"httpTimeout": "Timeout", "httpTimeout": "Timeout",
"testHttpRequest": "Testen" "testHttpRequest": "Testen"
}, },

View File

@ -581,6 +581,8 @@
"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": "JSON path to find the power value in the response. This uses the JSON path query syntax from mobizt/FirebaseJson. See above for examples.",
"httpUnit": "Unit", "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.",
"httpTimeout": "Timeout", "httpTimeout": "Timeout",
"testHttpRequest": "Run test", "testHttpRequest": "Run test",
"milliSeconds": "ms" "milliSeconds": "ms"

View File

@ -10,6 +10,7 @@ export interface PowerMeterHttpPhaseConfig {
json_path: string; json_path: string;
timeout: number; timeout: number;
unit: number; unit: number;
sign_inverted: boolean;
} }
export interface PowerMeterConfig { export interface PowerMeterConfig {

View File

@ -202,6 +202,12 @@
</div> </div>
</div> </div>
<InputElement
:label="$t('powermeteradmin.httpSignInverted')"
v-model="http_phase.sign_inverted"
:tooltip="$t('powermeteradmin.httpSignInvertedHint')"
type="checkbox" />
<div class="text-center mb-3"> <div class="text-center mb-3">
<button type="button" class="btn btn-danger" @click="testHttpRequest(index)"> <button type="button" class="btn btn-danger" @click="testHttpRequest(index)">
{{ $t('powermeteradmin.testHttpRequest') }} {{ $t('powermeteradmin.testHttpRequest') }}