diff --git a/include/HttpPowerMeter.h b/include/HttpPowerMeter.h index 3b5169230..aff05e64e 100644 --- a/include/HttpPowerMeter.h +++ b/include/HttpPowerMeter.h @@ -3,6 +3,7 @@ #include #include +#include class HttpPowerMeterClass { public: @@ -14,9 +15,12 @@ class HttpPowerMeterClass { float getFloatValueByJsonPath(const char* jsonString, const char* jsonPath, float &value); private: - float power[POWERMETER_MAX_PHASES]; void extractUrlComponents(const String& url, String& protocol, String& hostname, String& uri); + void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue); + HTTPClient httpClient; + float power[POWERMETER_MAX_PHASES]; String sha256(const String& data); + }; extern HttpPowerMeterClass HttpPowerMeter; diff --git a/include/MqttHandleDtu.h b/include/MqttHandleDtu.h index 7a6589249..fb5663450 100644 --- a/include/MqttHandleDtu.h +++ b/include/MqttHandleDtu.h @@ -9,7 +9,7 @@ class MqttHandleDtuClass { void loop(); private: - uint32_t _lastPublish; + uint32_t _lastPublish = 0; }; extern MqttHandleDtuClass MqttHandleDtu; \ No newline at end of file diff --git a/include/MqttHandleInverter.h b/include/MqttHandleInverter.h index 6f089cdf0..0194bacf4 100644 --- a/include/MqttHandleInverter.h +++ b/include/MqttHandleInverter.h @@ -17,8 +17,8 @@ class MqttHandleInverterClass { void publishField(std::shared_ptr inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId); void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); - uint32_t _lastPublishStats[INV_MAX_COUNT]; - uint32_t _lastPublish; + uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 }; + uint32_t _lastPublish = 0; FieldId_t _publishFields[14] = { FLD_UDC, diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index 8504d87ff..dbb396707 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -5,6 +5,7 @@ #include "Hoymiles.h" #include "Utils.h" #include "inverters/HMS_1CH.h" +#include "inverters/HMS_1CHv2.h" #include "inverters/HMS_2CH.h" #include "inverters/HMS_4CH.h" #include "inverters/HMT_6CH.h" @@ -147,6 +148,8 @@ std::shared_ptr HoymilesClass::addInverter(const char* name, u i = std::make_shared(_radioCmt.get(), serial); } else if (HMS_1CH::isValidSerial(serial)) { i = std::make_shared(_radioCmt.get(), serial); + } else if (HMS_1CHv2::isValidSerial(serial)) { + i = std::make_shared(_radioCmt.get(), serial); } else if (HM_4CH::isValidSerial(serial)) { i = std::make_shared(_radioNrf.get(), serial); } else if (HM_2CH::isValidSerial(serial)) { diff --git a/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp new file mode 100644 index 000000000..0091c99c1 --- /dev/null +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Thomas Basler and others + */ +#include "HMS_1CHv2.h" + +static const byteAssign_t byteAssignment[] = { + { TYPE_DC, CH0, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 }, + { TYPE_DC, CH0, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, + { TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, + { TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, + { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + + { TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 }, + { TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 }, + { TYPE_AC, CH0, FLD_PAC, UNIT_W, 30, 2, 10, false, 1 }, + { TYPE_AC, CH0, FLD_Q, UNIT_VAR, 20, 2, 10, false, 1 }, + { TYPE_AC, CH0, FLD_F, UNIT_HZ, 28, 2, 100, false, 2 }, + { TYPE_AC, CH0, FLD_PF, UNIT_NONE, 36, 2, 1000, false, 3 }, + + { TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 }, + { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 18, 2, 1, false, 0 }, + + { TYPE_AC, CH0, FLD_YD, UNIT_WH, CALC_YD_CH0, 0, CMD_CALC, false, 0 }, + { TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, + { TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, + { TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } +}; + +HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, uint64_t serial) + : HMS_Abstract(radio, serial) {}; + +bool HMS_1CHv2::isValidSerial(uint64_t serial) +{ + // serial >= 0x112500000000 && serial <= 0x112599999999 + uint16_t preSerial = (serial >> 32) & 0xffff; + return preSerial == 0x1125; +} + +String HMS_1CHv2::typeName() +{ + return "HMS-500 v2"; +} + +const byteAssign_t* HMS_1CHv2::getByteAssignment() +{ + return byteAssignment; +} + +uint8_t HMS_1CHv2::getByteAssignmentSize() +{ + return sizeof(byteAssignment) / sizeof(byteAssignment[0]); +} \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HMS_1CHv2.h b/lib/Hoymiles/src/inverters/HMS_1CHv2.h new file mode 100644 index 000000000..5f4981185 --- /dev/null +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include "HMS_Abstract.h" +#include + +class HMS_1CHv2 : public HMS_Abstract { +public: + explicit HMS_1CHv2(HoymilesRadio* radio, uint64_t serial); + static bool isValidSerial(uint64_t serial); + String typeName(); + const byteAssign_t* getByteAssignment(); + uint8_t getByteAssignmentSize(); +}; \ No newline at end of file diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index 978af6001..ddb76c0a7 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -32,6 +32,7 @@ const devInfo_t devInfo[] = { { { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400" }, // 00 { { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450" }, // 01 { { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500" }, // 02 + { { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500 v2" }, // 02 { { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600" }, // 01 { { 0x10, 0x21, 0x41, ALL }, 800, "HMS-800" }, // 00 { { 0x10, 0x11, 0x51, ALL }, 900, "HMS-900" }, // 01 diff --git a/src/HttpPowerMeter.cpp b/src/HttpPowerMeter.cpp index bae9f14cd..6f441705c 100644 --- a/src/HttpPowerMeter.cpp +++ b/src/HttpPowerMeter.cpp @@ -3,7 +3,6 @@ #include "HttpPowerMeter.h" #include "MessageOutput.h" #include -#include #include #include #include @@ -87,30 +86,19 @@ bool HttpPowerMeterClass::httpRequest(const char* url, Auth authType, const char wifiClient = std::make_unique(); } - HTTPClient httpClient; + if (!httpClient.begin(*wifiClient, newUrl)) { snprintf_P(error, errorSize, "httpClient.begin(%s) failed", newUrl.c_str()); return false; } - - httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - httpClient.setUserAgent("OpenDTU-OnBattery"); - httpClient.setConnectTimeout(timeout); - httpClient.setTimeout(timeout); - httpClient.addHeader("Content-Type", "application/json"); - httpClient.addHeader("Accept", "application/json"); - - if (strlen(httpHeader) > 0) { - httpClient.addHeader(httpHeader, httpValue); - } - + prepareRequest(timeout, httpHeader, httpValue); + if (authType == Auth::digest) { const char *headers[1] = {"WWW-Authenticate"}; httpClient.collectHeaders(headers, 1); } int httpCode = httpClient.GET(); - if (httpCode == HTTP_CODE_UNAUTHORIZED && authType == Auth::digest) { // Handle authentication challenge char realm[256]; // Buffer to store the realm received from the server @@ -160,6 +148,12 @@ bool HttpPowerMeterClass::httpRequest(const char* url, Auth authType, const char authorization += "\", nc=00000001, qop=auth, response=\""; authorization += response; authorization += "\", algorithm=SHA-256"; + httpClient.end(); + if (!httpClient.begin(*wifiClient, newUrl)) { + snprintf_P(error, errorSize, "httpClient.begin(%s) for digest auth failed", newUrl.c_str()); + return false; + } + prepareRequest(timeout, httpHeader, httpValue); httpClient.addHeader("Authorization", authorization); httpCode = httpClient.GET(); } @@ -259,4 +253,17 @@ String HttpPowerMeterClass::sha256(const String& data) { return hashStr; } +void HttpPowerMeterClass::prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue) { + httpClient.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + httpClient.setUserAgent("OpenDTU-OnBattery"); + httpClient.setConnectTimeout(timeout); + httpClient.setTimeout(timeout); + httpClient.addHeader("Content-Type", "application/json"); + httpClient.addHeader("Accept", "application/json"); + + if (strlen(httpHeader) > 0) { + httpClient.addHeader(httpHeader, httpValue); + } +} + HttpPowerMeterClass HttpPowerMeter; diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 9578daf55..d613355ac 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -143,7 +143,7 @@ void MqttHandleHassClass::publishField(std::shared_ptr inv, Ch root["stat_cla"] = stateCls; } - char buffer[512]; + String buffer; serializeJson(root, buffer); publish(configTopic, buffer); } else { @@ -181,7 +181,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr=3.0.0 <4.0.0" immutable "^4.0.0" @@ -2471,13 +2484,13 @@ vue-eslint-parser@^9.3.1: lodash "^4.17.21" semver "^7.3.6" -vue-i18n@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.4.0.tgz#13dd07102fd00da3809c5886e250cb505f071ac1" - integrity sha512-701V7wv6m/Fesbu58rRJv9Rd3en/9F7Nxyn/NiwcR7bJkEfwflMJg5wVezkeHy1tIqjXME1e1zbHhK1dNaR8mg== +vue-i18n@^9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.4.1.tgz#c7402662fe9b40b17798afffa9d8bfa4734dd7f5" + integrity sha512-vnQyYE9LBuNOqPpETIcCaGnAyLEqfeIvDcyZ9T+WBCWFTqWw1J8FuF1jfeDwpHBi5JKgAwgXyq1mt8jp/x/GPA== dependencies: - "@intlify/core-base" "9.4.0" - "@intlify/shared" "9.4.0" + "@intlify/core-base" "9.4.1" + "@intlify/shared" "9.4.1" "@vue/devtools-api" "^6.5.0" vue-router@^4.2.4: diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index 6c95fedb4..f22526180 100644 Binary files a/webapp_dist/js/app.js.gz and b/webapp_dist/js/app.js.gz differ