From d0b2b972e20d91ccdb48f98c9dfabab2818fe9cb Mon Sep 17 00:00:00 2001 From: janrombold <92722795+janrombold@users.noreply.github.com> Date: Fri, 4 Oct 2024 00:19:26 +0200 Subject: [PATCH 01/76] Update UpgradePartition.md Fixed typo --- docs/UpgradePartition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UpgradePartition.md b/docs/UpgradePartition.md index 782463f56..f919844c7 100644 --- a/docs/UpgradePartition.md +++ b/docs/UpgradePartition.md @@ -1,3 +1,3 @@ # Upgrade Partition -This documentation will has been moved and can be found here: +This documentation has been moved and can be found here: From bcf4b70dc9a5c0fe8f3bb14a2e4458a5ff7bb340 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 15 Oct 2024 19:11:17 +0200 Subject: [PATCH 02/76] Fix: cpplint errors --- .../commands/ActivePowerControlCommand.cpp | 4 ++-- .../src/commands/DevControlCommand.cpp | 4 ++-- .../src/commands/MultiDataCommand.cpp | 12 +++++------ lib/Hoymiles/src/inverters/HM_1CH.cpp | 6 +++--- lib/Hoymiles/src/inverters/HM_2CH.cpp | 6 +++--- lib/Hoymiles/src/inverters/HM_4CH.cpp | 6 +++--- lib/Hoymiles/src/inverters/HM_Abstract.cpp | 4 ++-- .../src/inverters/InverterAbstract.cpp | 4 ++-- lib/Hoymiles/src/parser/AlarmLogParser.cpp | 6 +++--- lib/Hoymiles/src/parser/DevInfoParser.cpp | 20 +++++++++---------- lib/Hoymiles/src/parser/GridProfileParser.cpp | 2 +- .../src/parser/SystemConfigParaParser.cpp | 6 +++--- src/WebApi_dtu.cpp | 4 ++-- src/WebApi_inverter.cpp | 4 ++-- src/main.cpp | 4 ++-- 15 files changed, 46 insertions(+), 46 deletions(-) diff --git a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp index dcd2370c1..4ce3c6e55 100644 --- a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp +++ b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp @@ -85,13 +85,13 @@ bool ActivePowerControlCommand::handleResponse(const fragment_t fragment[], cons float ActivePowerControlCommand::getLimit() const { - const float l = (((uint16_t)_payload[12] << 8) | _payload[13]); + const float l = (static_cast(_payload[12]) << 8) | _payload[13]; return l / 10; } PowerLimitControlType ActivePowerControlCommand::getType() { - return (PowerLimitControlType)(((uint16_t)_payload[14] << 8) | _payload[15]); + return (PowerLimitControlType)((static_cast(_payload[14]) << 8) | _payload[15]); } void ActivePowerControlCommand::gotTimeout() diff --git a/lib/Hoymiles/src/commands/DevControlCommand.cpp b/lib/Hoymiles/src/commands/DevControlCommand.cpp index b73f74f0c..8dc419d60 100644 --- a/lib/Hoymiles/src/commands/DevControlCommand.cpp +++ b/lib/Hoymiles/src/commands/DevControlCommand.cpp @@ -35,8 +35,8 @@ DevControlCommand::DevControlCommand(InverterAbstract* inv, const uint64_t route void DevControlCommand::udpateCRC(const uint8_t len) { const uint16_t crc = crc16(&_payload[10], len); - _payload[10 + len] = (uint8_t)(crc >> 8); - _payload[10 + len + 1] = (uint8_t)(crc); + _payload[10 + len] = static_cast(crc >> 8); + _payload[10 + len + 1] = static_cast(crc); } bool DevControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) diff --git a/lib/Hoymiles/src/commands/MultiDataCommand.cpp b/lib/Hoymiles/src/commands/MultiDataCommand.cpp index 0e7bf51f1..dd5d72ea0 100644 --- a/lib/Hoymiles/src/commands/MultiDataCommand.cpp +++ b/lib/Hoymiles/src/commands/MultiDataCommand.cpp @@ -63,10 +63,10 @@ uint8_t MultiDataCommand::getDataType() const void MultiDataCommand::setTime(const time_t time) { - _payload[12] = (uint8_t)(time >> 24); - _payload[13] = (uint8_t)(time >> 16); - _payload[14] = (uint8_t)(time >> 8); - _payload[15] = (uint8_t)(time); + _payload[12] = static_cast(time >> 24); + _payload[13] = static_cast(time >> 16); + _payload[14] = static_cast(time >> 8); + _payload[15] = static_cast(time); udpateCRC(); } @@ -112,8 +112,8 @@ bool MultiDataCommand::handleResponse(const fragment_t fragment[], const uint8_t void MultiDataCommand::udpateCRC() { const uint16_t crc = crc16(&_payload[10], 14); // From data_type till password - _payload[24] = (uint8_t)(crc >> 8); - _payload[25] = (uint8_t)(crc); + _payload[24] = static_cast(crc >> 8); + _payload[25] = static_cast(crc); } uint8_t MultiDataCommand::getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id) diff --git a/lib/Hoymiles/src/inverters/HM_1CH.cpp b/lib/Hoymiles/src/inverters/HM_1CH.cpp index 0f0c64c23..eef82c5c3 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_1CH.cpp @@ -36,10 +36,10 @@ bool HM_1CH::isValidSerial(const uint64_t serial) // serial >= 0x112100000000 && serial <= 0x1121ffffffff uint8_t preId[2]; - preId[0] = (uint8_t)(serial >> 40); - preId[1] = (uint8_t)(serial >> 32); + preId[0] = static_cast(serial >> 40); + preId[1] = static_cast(serial >> 32); - if ((uint8_t)(((((uint16_t)preId[0] << 8) | preId[1]) >> 4) & 0xff) == 0x12) { + if (static_cast((((static_cast(preId[0]) << 8) | preId[1]) >> 4) & 0xff) == 0x12) { return true; } diff --git a/lib/Hoymiles/src/inverters/HM_2CH.cpp b/lib/Hoymiles/src/inverters/HM_2CH.cpp index 02dd8ae4f..91228ff31 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_2CH.cpp @@ -44,10 +44,10 @@ bool HM_2CH::isValidSerial(const uint64_t serial) // serial >= 0x114100000000 && serial <= 0x1141ffffffff uint8_t preId[2]; - preId[0] = (uint8_t)(serial >> 40); - preId[1] = (uint8_t)(serial >> 32); + preId[0] = static_cast(serial >> 40); + preId[1] = static_cast(serial >> 32); - if ((uint8_t)(((((uint16_t)preId[0] << 8) | preId[1]) >> 4) & 0xff) == 0x14) { + if (static_cast((((static_cast(preId[0]) << 8) | preId[1]) >> 4) & 0xff) == 0x14) { return true; } diff --git a/lib/Hoymiles/src/inverters/HM_4CH.cpp b/lib/Hoymiles/src/inverters/HM_4CH.cpp index 586248b59..45ebab40a 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_4CH.cpp @@ -57,10 +57,10 @@ bool HM_4CH::isValidSerial(const uint64_t serial) // serial >= 0x116100000000 && serial <= 0x1161ffffffff uint8_t preId[2]; - preId[0] = (uint8_t)(serial >> 40); - preId[1] = (uint8_t)(serial >> 32); + preId[0] = static_cast(serial >> 40); + preId[1] = static_cast(serial >> 32); - if ((uint8_t)(((((uint16_t)preId[0] << 8) | preId[1]) >> 4) & 0xff) == 0x16) { + if (static_cast((((static_cast(preId[0]) << 8) | preId[1]) >> 4) & 0xff) == 0x16) { return true; } diff --git a/lib/Hoymiles/src/inverters/HM_Abstract.cpp b/lib/Hoymiles/src/inverters/HM_Abstract.cpp index 45efc99db..324f6a27d 100644 --- a/lib/Hoymiles/src/inverters/HM_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HM_Abstract.cpp @@ -50,13 +50,13 @@ bool HM_Abstract::sendAlarmLogRequest(const bool force) if (!force) { if (Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) { - if ((uint8_t)Statistics()->getChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG) == _lastAlarmLogCnt) { + if (static_cast(Statistics()->getChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG) == _lastAlarmLogCnt)) { return false; } } } - _lastAlarmLogCnt = (uint8_t)Statistics()->getChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG); + _lastAlarmLogCnt = static_cast(Statistics()->getChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)); time_t now; time(&now); diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.cpp b/lib/Hoymiles/src/inverters/InverterAbstract.cpp index 5d52a380c..26a89c131 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.cpp +++ b/lib/Hoymiles/src/inverters/InverterAbstract.cpp @@ -14,8 +14,8 @@ InverterAbstract::InverterAbstract(HoymilesRadio* radio, const uint64_t serial) char serial_buff[sizeof(uint64_t) * 8 + 1]; snprintf(serial_buff, sizeof(serial_buff), "%0x%08x", - ((uint32_t)((serial >> 32) & 0xFFFFFFFF)), - ((uint32_t)(serial & 0xFFFFFFFF))); + static_cast((serial >> 32) & 0xFFFFFFFF), + static_cast(serial & 0xFFFFFFFF)); _serialString = serial_buff; _alarmLogParser.reset(new AlarmLogParser()); diff --git a/lib/Hoymiles/src/parser/AlarmLogParser.cpp b/lib/Hoymiles/src/parser/AlarmLogParser.cpp index 30b813647..027dd84b5 100644 --- a/lib/Hoymiles/src/parser/AlarmLogParser.cpp +++ b/lib/Hoymiles/src/parser/AlarmLogParser.cpp @@ -243,7 +243,7 @@ void AlarmLogParser::getLogEntry(const uint8_t entryId, AlarmLogEntry_t& entry, HOY_SEMAPHORE_TAKE(); - const uint32_t wcode = (uint16_t)_payloadAlarmLog[entryStartOffset] << 8 | _payloadAlarmLog[entryStartOffset + 1]; + const uint32_t wcode = static_cast(_payloadAlarmLog[entryStartOffset]) << 8 | _payloadAlarmLog[entryStartOffset + 1]; uint32_t startTimeOffset = 0; if (((wcode >> 13) & 0x01) == 1) { startTimeOffset = 12 * 60 * 60; @@ -255,8 +255,8 @@ void AlarmLogParser::getLogEntry(const uint8_t entryId, AlarmLogEntry_t& entry, } entry.MessageId = _payloadAlarmLog[entryStartOffset + 1]; - entry.StartTime = (((uint16_t)_payloadAlarmLog[entryStartOffset + 4] << 8) | ((uint16_t)_payloadAlarmLog[entryStartOffset + 5])) + startTimeOffset + timezoneOffset; - entry.EndTime = ((uint16_t)_payloadAlarmLog[entryStartOffset + 6] << 8) | ((uint16_t)_payloadAlarmLog[entryStartOffset + 7]); + entry.StartTime = ((static_cast(_payloadAlarmLog[entryStartOffset + 4]) << 8) | static_cast(_payloadAlarmLog[entryStartOffset + 5])) + startTimeOffset + timezoneOffset; + entry.EndTime = (static_cast(_payloadAlarmLog[entryStartOffset + 6]) << 8) | static_cast(_payloadAlarmLog[entryStartOffset + 7]); HOY_SEMAPHORE_GIVE(); diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index 0c2e15e8d..0bb061425 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -149,7 +149,7 @@ void DevInfoParser::setLastUpdateSimple(const uint32_t lastUpdate) uint16_t DevInfoParser::getFwBuildVersion() const { HOY_SEMAPHORE_TAKE(); - const uint16_t ret = (((uint16_t)_payloadDevInfoAll[0]) << 8) | _payloadDevInfoAll[1]; + const uint16_t ret = (static_cast(_payloadDevInfoAll[0]) << 8) | _payloadDevInfoAll[1]; HOY_SEMAPHORE_GIVE(); return ret; } @@ -158,13 +158,13 @@ time_t DevInfoParser::getFwBuildDateTime() const { struct tm timeinfo = {}; HOY_SEMAPHORE_TAKE(); - timeinfo.tm_year = ((((uint16_t)_payloadDevInfoAll[2]) << 8) | _payloadDevInfoAll[3]) - 1900; + timeinfo.tm_year = ((static_cast(_payloadDevInfoAll[2]) << 8) | _payloadDevInfoAll[3]) - 1900; - timeinfo.tm_mon = ((((uint16_t)_payloadDevInfoAll[4]) << 8) | _payloadDevInfoAll[5]) / 100 - 1; - timeinfo.tm_mday = ((((uint16_t)_payloadDevInfoAll[4]) << 8) | _payloadDevInfoAll[5]) % 100; + timeinfo.tm_mon = ((static_cast(_payloadDevInfoAll[4]) << 8) | _payloadDevInfoAll[5]) / 100 - 1; + timeinfo.tm_mday = ((static_cast(_payloadDevInfoAll[4]) << 8) | _payloadDevInfoAll[5]) % 100; - timeinfo.tm_hour = ((((uint16_t)_payloadDevInfoAll[6]) << 8) | _payloadDevInfoAll[7]) / 100; - timeinfo.tm_min = ((((uint16_t)_payloadDevInfoAll[6]) << 8) | _payloadDevInfoAll[7]) % 100; + timeinfo.tm_hour = ((static_cast(_payloadDevInfoAll[6]) << 8) | _payloadDevInfoAll[7]) / 100; + timeinfo.tm_min = ((static_cast(_payloadDevInfoAll[6]) << 8) | _payloadDevInfoAll[7]) % 100; HOY_SEMAPHORE_GIVE(); return timegm(&timeinfo); @@ -181,7 +181,7 @@ String DevInfoParser::getFwBuildDateTimeStr() const uint16_t DevInfoParser::getFwBootloaderVersion() const { HOY_SEMAPHORE_TAKE(); - const uint16_t ret = (((uint16_t)_payloadDevInfoAll[8]) << 8) | _payloadDevInfoAll[9]; + const uint16_t ret = (static_cast(_payloadDevInfoAll[8]) << 8) | _payloadDevInfoAll[9]; HOY_SEMAPHORE_GIVE(); return ret; } @@ -189,11 +189,11 @@ uint16_t DevInfoParser::getFwBootloaderVersion() const uint32_t DevInfoParser::getHwPartNumber() const { HOY_SEMAPHORE_TAKE(); - const uint16_t hwpn_h = (((uint16_t)_payloadDevInfoSimple[2]) << 8) | _payloadDevInfoSimple[3]; - const uint16_t hwpn_l = (((uint16_t)_payloadDevInfoSimple[4]) << 8) | _payloadDevInfoSimple[5]; + const uint16_t hwpn_h = (static_cast(_payloadDevInfoSimple[2]) << 8) | _payloadDevInfoSimple[3]; + const uint16_t hwpn_l = (static_cast(_payloadDevInfoSimple[4]) << 8) | _payloadDevInfoSimple[5]; HOY_SEMAPHORE_GIVE(); - return ((uint32_t)hwpn_h << 16) | ((uint32_t)hwpn_l); + return (static_cast(hwpn_h) << 16) | static_cast(hwpn_l); } String DevInfoParser::getHwVersion() const diff --git a/lib/Hoymiles/src/parser/GridProfileParser.cpp b/lib/Hoymiles/src/parser/GridProfileParser.cpp index 489565e19..2f50bd506 100644 --- a/lib/Hoymiles/src/parser/GridProfileParser.cpp +++ b/lib/Hoymiles/src/parser/GridProfileParser.cpp @@ -443,7 +443,7 @@ std::list GridProfileParser::getProfile() const for (uint8_t val_id = 0; val_id < section_size; val_id++) { auto itemDefinition = itemDefinitions.at(_profileValues[section_start + val_id].ItemDefinition); - float value = (int16_t)((_payloadGridProfile[pos] << 8) | _payloadGridProfile[pos + 1]); + float value = static_cast((_payloadGridProfile[pos] << 8) | _payloadGridProfile[pos + 1]); value /= itemDefinition.Divider; GridProfileItem_t v; diff --git a/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp b/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp index 346b5d468..f01af7070 100644 --- a/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp +++ b/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp @@ -45,7 +45,7 @@ void SystemConfigParaParser::appendFragment(const uint8_t offset, const uint8_t* float SystemConfigParaParser::getLimitPercent() const { HOY_SEMAPHORE_TAKE(); - const float ret = ((((uint16_t)_payload[2]) << 8) | _payload[3]) / 10.0; + const float ret = ((static_cast(_payload[2]) << 8) | _payload[3]) / 10.0; HOY_SEMAPHORE_GIVE(); return ret; } @@ -53,8 +53,8 @@ float SystemConfigParaParser::getLimitPercent() const void SystemConfigParaParser::setLimitPercent(const float value) { HOY_SEMAPHORE_TAKE(); - _payload[2] = ((uint16_t)(value * 10)) >> 8; - _payload[3] = ((uint16_t)(value * 10)); + _payload[2] = static_cast(value * 10) >> 8; + _payload[3] = static_cast(value * 10); HOY_SEMAPHORE_GIVE(); } diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index 7c6c3f738..90ea827f9 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -50,8 +50,8 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request) // DTU Serial is read as HEX char buffer[sizeof(uint64_t) * 8 + 1]; snprintf(buffer, sizeof(buffer), "%0" PRIx32 "%08" PRIx32, - ((uint32_t)((config.Dtu.Serial >> 32) & 0xFFFFFFFF)), - ((uint32_t)(config.Dtu.Serial & 0xFFFFFFFF))); + static_cast((config.Dtu.Serial >> 32) & 0xFFFFFFFF), + static_cast(config.Dtu.Serial & 0xFFFFFFFF)); root["serial"] = buffer; root["pollinterval"] = config.Dtu.PollInterval; root["nrf_enabled"] = Hoymiles.getRadioNrf()->isInitialized(); diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index ef353158f..3584964a7 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -46,8 +46,8 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) // Inverter Serial is read as HEX char buffer[sizeof(uint64_t) * 8 + 1]; snprintf(buffer, sizeof(buffer), "%0" PRIx32 "%08" PRIx32, - ((uint32_t)((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF)), - ((uint32_t)(config.Inverter[i].Serial & 0xFFFFFFFF))); + static_cast((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF), + static_cast(config.Inverter[i].Serial & 0xFFFFFFFF)); obj["serial"] = buffer; obj["poll_enable"] = config.Inverter[i].Poll_Enable; obj["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night; diff --git a/src/main.cpp b/src/main.cpp index b1d974d74..92e35da10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -152,8 +152,8 @@ void setup() MessageOutput.print("generate serial based on ESP chip id: "); const uint64_t dtuId = Utils::generateDtuSerial(); MessageOutput.printf("%0" PRIx32 "%08" PRIx32 "... ", - ((uint32_t)((dtuId >> 32) & 0xFFFFFFFF)), - ((uint32_t)(dtuId & 0xFFFFFFFF))); + static_cast((dtuId >> 32) & 0xFFFFFFFF), + static_cast(dtuId & 0xFFFFFFFF)); config.Dtu.Serial = dtuId; Configuration.write(); } From e5cf12cebd43b7c53ac878aec8eee82733be9f36 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Wed, 16 Oct 2024 21:24:01 +0200 Subject: [PATCH 03/76] Update nrf24/RF24 from 1.4.9 to 1.4.10 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 8602f8b26..6c494f889 100644 --- a/platformio.ini +++ b/platformio.ini @@ -44,7 +44,7 @@ lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.16 bblanchon/ArduinoJson @ 7.2.0 https://github.com/bertmelis/espMqttClient.git#v1.7.0 - nrf24/RF24 @ 1.4.9 + nrf24/RF24 @ 1.4.10 olikraus/U8g2 @ 2.36.2 buelowp/sunset @ 1.1.7 arkhipenko/TaskScheduler @ 3.8.5 From cf1693e1a0d6d06a76f4ed6be490391aa3d81d53 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Wed, 16 Oct 2024 21:25:29 +0200 Subject: [PATCH 04/76] Upgrade ESPAsyncWebServer from 3.3.16 to 3.3.17 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 6c494f889..161f88c21 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,7 +41,7 @@ build_unflags = -std=gnu++11 lib_deps = - mathieucarbou/ESPAsyncWebServer @ 3.3.16 + mathieucarbou/ESPAsyncWebServer @ 3.3.17 bblanchon/ArduinoJson @ 7.2.0 https://github.com/bertmelis/espMqttClient.git#v1.7.0 nrf24/RF24 @ 1.4.10 From aa9f36ee8ff80c1b74648533728beee9fd66d34c Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sat, 19 Oct 2024 11:07:15 +0200 Subject: [PATCH 05/76] Rename config API to file API --- include/WebApi.h | 4 ++-- include/WebApi_config.h | 17 -------------- include/WebApi_file.h | 17 ++++++++++++++ src/WebApi.cpp | 2 +- src/{WebApi_config.cpp => WebApi_file.cpp} | 26 +++++++++++----------- webapp/src/views/ConfigAdminView.vue | 8 +++---- webapp/src/views/DeviceAdminView.vue | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) delete mode 100644 include/WebApi_config.h create mode 100644 include/WebApi_file.h rename src/{WebApi_config.cpp => WebApi_file.cpp} (78%) diff --git a/include/WebApi.h b/include/WebApi.h index 2932f015a..2e530587d 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "WebApi_config.h" #include "WebApi_device.h" #include "WebApi_devinfo.h" #include "WebApi_dtu.h" #include "WebApi_errors.h" #include "WebApi_eventlog.h" +#include "WebApi_file.h" #include "WebApi_firmware.h" #include "WebApi_gridprofile.h" #include "WebApi_inverter.h" @@ -46,11 +46,11 @@ class WebApiClass { private: AsyncWebServer _server; - WebApiConfigClass _webApiConfig; WebApiDeviceClass _webApiDevice; WebApiDevInfoClass _webApiDevInfo; WebApiDtuClass _webApiDtu; WebApiEventlogClass _webApiEventlog; + WebApiFileClass _webApiFile; WebApiFirmwareClass _webApiFirmware; WebApiGridProfileClass _webApiGridprofile; WebApiInverterClass _webApiInverter; diff --git a/include/WebApi_config.h b/include/WebApi_config.h deleted file mode 100644 index f29dc8fcf..000000000 --- a/include/WebApi_config.h +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -#include -#include - -class WebApiConfigClass { -public: - void init(AsyncWebServer& server, Scheduler& scheduler); - -private: - void onConfigGet(AsyncWebServerRequest* request); - void onConfigDelete(AsyncWebServerRequest* request); - void onConfigListGet(AsyncWebServerRequest* request); - void onConfigUploadFinish(AsyncWebServerRequest* request); - void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); -}; diff --git a/include/WebApi_file.h b/include/WebApi_file.h new file mode 100644 index 000000000..2f6e33139 --- /dev/null +++ b/include/WebApi_file.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include +#include + +class WebApiFileClass { +public: + void init(AsyncWebServer& server, Scheduler& scheduler); + +private: + void onFileGet(AsyncWebServerRequest* request); + void onFileDelete(AsyncWebServerRequest* request); + void onFileListGet(AsyncWebServerRequest* request); + void onFileUploadFinish(AsyncWebServerRequest* request); + void onFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); +}; diff --git a/src/WebApi.cpp b/src/WebApi.cpp index b3255c417..35b1fda93 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -15,11 +15,11 @@ WebApiClass::WebApiClass() void WebApiClass::init(Scheduler& scheduler) { - _webApiConfig.init(_server, scheduler); _webApiDevice.init(_server, scheduler); _webApiDevInfo.init(_server, scheduler); _webApiDtu.init(_server, scheduler); _webApiEventlog.init(_server, scheduler); + _webApiFile.init(_server, scheduler); _webApiFirmware.init(_server, scheduler); _webApiGridprofile.init(_server, scheduler); _webApiInverter.init(_server, scheduler); diff --git a/src/WebApi_config.cpp b/src/WebApi_file.cpp similarity index 78% rename from src/WebApi_config.cpp rename to src/WebApi_file.cpp index 51a7aab14..c3a290a14 100644 --- a/src/WebApi_config.cpp +++ b/src/WebApi_file.cpp @@ -2,7 +2,7 @@ /* * Copyright (C) 2022-2024 Thomas Basler and others */ -#include "WebApi_config.h" +#include "WebApi_file.h" #include "Configuration.h" #include "RestartHelper.h" #include "Utils.h" @@ -11,7 +11,7 @@ #include #include -void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler) +void WebApiFileClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; using std::placeholders::_2; @@ -20,15 +20,15 @@ void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler) using std::placeholders::_5; using std::placeholders::_6; - server.on("/api/config/get", HTTP_GET, std::bind(&WebApiConfigClass::onConfigGet, this, _1)); - server.on("/api/config/delete", HTTP_POST, std::bind(&WebApiConfigClass::onConfigDelete, this, _1)); - server.on("/api/config/list", HTTP_GET, std::bind(&WebApiConfigClass::onConfigListGet, this, _1)); - server.on("/api/config/upload", HTTP_POST, - std::bind(&WebApiConfigClass::onConfigUploadFinish, this, _1), - std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6)); + server.on("/api/file/get", HTTP_GET, std::bind(&WebApiFileClass::onFileGet, this, _1)); + server.on("/api/file/delete", HTTP_POST, std::bind(&WebApiFileClass::onFileDelete, this, _1)); + server.on("/api/file/list", HTTP_GET, std::bind(&WebApiFileClass::onFileListGet, this, _1)); + server.on("/api/file/upload", HTTP_POST, + std::bind(&WebApiFileClass::onFileUploadFinish, this, _1), + std::bind(&WebApiFileClass::onFileUpload, this, _1, _2, _3, _4, _5, _6)); } -void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request) +void WebApiFileClass::onFileGet(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -48,7 +48,7 @@ void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request) request->send(LittleFS, requestFile, String(), true); } -void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) +void WebApiFileClass::onFileDelete(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -86,7 +86,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) RestartHelper.triggerRestart(); } -void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request) +void WebApiFileClass::onFileListGet(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -112,7 +112,7 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request) WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); } -void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request) +void WebApiFileClass::onFileUploadFinish(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; @@ -128,7 +128,7 @@ void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request) RestartHelper.triggerRestart(); } -void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) +void WebApiFileClass::onFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) { if (!WebApi.checkCredentials(request)) { return; diff --git a/webapp/src/views/ConfigAdminView.vue b/webapp/src/views/ConfigAdminView.vue index a5bf2f7d4..6005db012 100644 --- a/webapp/src/views/ConfigAdminView.vue +++ b/webapp/src/views/ConfigAdminView.vue @@ -163,7 +163,7 @@ export default defineComponent({ const formData = new FormData(); formData.append('data', JSON.stringify({ delete: true })); - fetch('/api/config/delete', { + fetch('/api/file/delete', { method: 'POST', headers: authHeader(), body: formData, @@ -178,7 +178,7 @@ export default defineComponent({ }, getFileList() { this.loading = true; - fetch('/api/config/list', { headers: authHeader() }) + fetch('/api/file/list', { headers: authHeader() }) .then((response) => handleResponse(response, this.$emitter, this.$router)) .then((data) => { this.fileList = data; @@ -189,7 +189,7 @@ export default defineComponent({ }); }, downloadConfig() { - fetch('/api/config/get?file=' + this.backupFileSelect, { headers: authHeader() }) + fetch('/api/file/get?file=' + this.backupFileSelect, { headers: authHeader() }) .then((res) => res.blob()) .then((blob) => { const file = window.URL.createObjectURL(blob); @@ -233,7 +233,7 @@ export default defineComponent({ request.withCredentials = true; formData.append('config', this.file, 'config'); - request.open('post', '/api/config/upload?file=' + this.restoreFileSelect); + request.open('post', '/api/file/upload?file=' + this.restoreFileSelect); authHeader().forEach((value, key) => { request.setRequestHeader(key, value); }); diff --git a/webapp/src/views/DeviceAdminView.vue b/webapp/src/views/DeviceAdminView.vue index 258ec5b2c..df374151b 100644 --- a/webapp/src/views/DeviceAdminView.vue +++ b/webapp/src/views/DeviceAdminView.vue @@ -316,7 +316,7 @@ export default defineComponent({ methods: { getPinMappingList() { this.pinMappingLoading = true; - fetch('/api/config/get?file=pin_mapping.json', { headers: authHeader() }) + fetch('/api/file/get?file=pin_mapping.json', { headers: authHeader() }) .then((response) => handleResponse(response, this.$emitter, this.$router, true)) .then((data) => { this.pinMappingList = data; From 16901482d9c005d9162568997035b2a2ab04518c Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sat, 19 Oct 2024 12:40:43 +0200 Subject: [PATCH 06/76] Refactor file handling API and add endpoint to delete files --- include/WebApi_errors.h | 7 +-- include/WebApi_file.h | 1 + src/WebApi_file.cpp | 108 +++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/include/WebApi_errors.h b/include/WebApi_errors.h index c4cdaf210..68e107d42 100644 --- a/include/WebApi_errors.h +++ b/include/WebApi_errors.h @@ -18,9 +18,10 @@ enum WebApiError { DtuInvalidCmtFrequency, DtuInvalidCmtCountry, - ConfigBase = 3000, - ConfigNotDeleted, - ConfigSuccess, + FileBase = 3000, + FileNotDeleted, + FileSuccess, + FileDeleteSuccess, InverterBase = 4000, InverterSerialZero, diff --git a/include/WebApi_file.h b/include/WebApi_file.h index 2f6e33139..ce958b059 100644 --- a/include/WebApi_file.h +++ b/include/WebApi_file.h @@ -11,6 +11,7 @@ class WebApiFileClass { private: void onFileGet(AsyncWebServerRequest* request); void onFileDelete(AsyncWebServerRequest* request); + void onFileDeleteAll(AsyncWebServerRequest* request); void onFileListGet(AsyncWebServerRequest* request); void onFileUploadFinish(AsyncWebServerRequest* request); void onFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); diff --git a/src/WebApi_file.cpp b/src/WebApi_file.cpp index c3a290a14..60c0bea02 100644 --- a/src/WebApi_file.cpp +++ b/src/WebApi_file.cpp @@ -22,12 +22,40 @@ void WebApiFileClass::init(AsyncWebServer& server, Scheduler& scheduler) server.on("/api/file/get", HTTP_GET, std::bind(&WebApiFileClass::onFileGet, this, _1)); server.on("/api/file/delete", HTTP_POST, std::bind(&WebApiFileClass::onFileDelete, this, _1)); + server.on("/api/file/delete_all", HTTP_POST, std::bind(&WebApiFileClass::onFileDeleteAll, this, _1)); server.on("/api/file/list", HTTP_GET, std::bind(&WebApiFileClass::onFileListGet, this, _1)); server.on("/api/file/upload", HTTP_POST, std::bind(&WebApiFileClass::onFileUploadFinish, this, _1), std::bind(&WebApiFileClass::onFileUpload, this, _1, _2, _3, _4, _5, _6)); } +void WebApiFileClass::onFileListGet(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentials(request)) { + return; + } + + AsyncJsonResponse* response = new AsyncJsonResponse(); + auto& root = response->getRoot(); + auto data = root.to(); + + File rootfs = LittleFS.open("/"); + File file = rootfs.openNextFile(); + while (file) { + if (file.isDirectory()) { + continue; + } + JsonObject obj = data.add(); + obj["name"] = String(file.name()); + obj["size"] = file.size(); + + file = rootfs.openNextFile(); + } + file.close(); + + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); +} + void WebApiFileClass::onFileGet(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { @@ -62,69 +90,63 @@ void WebApiFileClass::onFileDelete(AsyncWebServerRequest* request) auto& retMsg = response->getRoot(); - if (!(root["delete"].is())) { + if (!(root["file"].is())) { retMsg["message"] = "Values are missing!"; retMsg["code"] = WebApiError::GenericValueMissing; WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); return; } - if (root["delete"].as() == false) { - retMsg["message"] = "Not deleted anything!"; - retMsg["code"] = WebApiError::ConfigNotDeleted; - WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + String name = "/" + root["file"].as(); + if (!LittleFS.exists(name)) { + request->send(404); return; } + LittleFS.remove(name); + retMsg["type"] = "success"; - retMsg["message"] = "Configuration resettet. Rebooting now..."; - retMsg["code"] = WebApiError::ConfigSuccess; + retMsg["message"] = "File deleted"; + retMsg["code"] = WebApiError::FileDeleteSuccess; WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); - - Utils::removeAllFiles(); - RestartHelper.triggerRestart(); } -void WebApiFileClass::onFileListGet(AsyncWebServerRequest* request) +void WebApiFileClass::onFileDeleteAll(AsyncWebServerRequest* request) { if (!WebApi.checkCredentials(request)) { return; } AsyncJsonResponse* response = new AsyncJsonResponse(); - auto& root = response->getRoot(); - auto data = root["configs"].to(); + JsonDocument root; + if (!WebApi.parseRequestData(request, response, root)) { + return; + } - File rootfs = LittleFS.open("/"); - File file = rootfs.openNextFile(); - while (file) { - if (file.isDirectory()) { - continue; - } - JsonObject obj = data.add(); - obj["name"] = String(file.name()); + auto& retMsg = response->getRoot(); - file = rootfs.openNextFile(); + if (!(root["delete"].is())) { + retMsg["message"] = "Values are missing!"; + retMsg["code"] = WebApiError::GenericValueMissing; + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + return; } - file.close(); - WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); -} - -void WebApiFileClass::onFileUploadFinish(AsyncWebServerRequest* request) -{ - if (!WebApi.checkCredentials(request)) { + if (root["delete"].as() == false) { + retMsg["message"] = "Not deleted anything!"; + retMsg["code"] = WebApiError::FileNotDeleted; + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); return; } - // the request handler is triggered after the upload has finished... - // create the response, add header, and send response + retMsg["type"] = "success"; + retMsg["message"] = "Configuration resettet. Rebooting now..."; + retMsg["code"] = WebApiError::FileSuccess; - AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", "OK"); - response->addHeader("Connection", "close"); - response->addHeader("Access-Control-Allow-Origin", "*"); - request->send(response); + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + + Utils::removeAllFiles(); RestartHelper.triggerRestart(); } @@ -154,3 +176,19 @@ void WebApiFileClass::onFileUpload(AsyncWebServerRequest* request, String filena request->_tempFile.close(); } } + +void WebApiFileClass::onFileUploadFinish(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentials(request)) { + return; + } + + // the request handler is triggered after the upload has finished... + // create the response, add header, and send response + + AsyncWebServerResponse* response = request->beginResponse(200, "text/plain", "OK"); + response->addHeader("Connection", "close"); + response->addHeader("Access-Control-Allow-Origin", "*"); + request->send(response); + RestartHelper.triggerRestart(); +} From 1e857b79c1944f0a3cc81b3cb875d673a4c6d454 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sat, 19 Oct 2024 17:35:19 +0200 Subject: [PATCH 07/76] Feature: Refactor config management interface --- webapp/src/locales/de.json | 11 +- webapp/src/locales/en.json | 11 +- webapp/src/locales/fr.json | 11 +- webapp/src/types/Alert.ts | 6 + webapp/src/types/Config.ts | 7 - webapp/src/types/File.ts | 4 + webapp/src/views/ConfigAdminView.vue | 213 +++++++++++++++++---------- 7 files changed, 168 insertions(+), 95 deletions(-) create mode 100644 webapp/src/types/Alert.ts delete mode 100644 webapp/src/types/Config.ts create mode 100644 webapp/src/types/File.ts diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index d5b9f71e7..433b2512f 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -54,6 +54,7 @@ "2005": "Ungültige Landesauswahl!", "3001": "Nichts gelöscht!", "3002": "Konfiguration zurückgesetzt. Starte jetzt neu...", + "3003": "Datei erfolgreich gelöscht. Neustarten um Änderungen anzuwenden!", "4001": "@:apiresponse.2001", "4002": "Der Name muss zwischen 1 und {max} Zeichen lang sein!", "4003": "Es werden nur {max} Wechselrichter unterstützt!", @@ -556,11 +557,9 @@ "YieldDayCorrection": "Tagesertragskorrektur", "YieldDayCorrectionHint": "Summiert den Tagesertrag, auch wenn der Wechselrichter neu gestartet wird. Der Wert wird um Mitternacht zurückgesetzt" }, - "configadmin": { + "fileadmin": { "ConfigManagement": "Konfigurationsverwaltung", "BackupHeader": "Sicherung: Sicherung der Konfigurationsdatei", - "BackupConfig": "Sicherung der Konfigurationsdatei", - "Backup": "Sichern", "Restore": "Wiederherstellen", "NoFileSelected": "Keine Datei ausgewählt", "RestoreHeader": "Wiederherstellen: Wiederherstellen der Konfigurationsdatei", @@ -573,6 +572,12 @@ "FactoryReset": "Werksreset", "ResetMsg": "Sind Sie sicher, dass Sie die aktuelle Konfiguration löschen und alle Einstellungen auf die Werkseinstellungen zurücksetzen möchten?", "ResetConfirm": "Werksreset!", + "Download": "Herunterladen", + "Delete": "Löschen", + "DeleteMsg": "Sind Sie sicher, dass Sie die Datei löschen wollen: '{name}'? Es muss manuell neu gestartet werden um die Konfigurationsänderungen zu übernehmen!", + "Name": "Name", + "Size": "Größe", + "Action": "Aktion", "Cancel": "@:base.Cancel" }, "login": { diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index f129d9f3f..bd782139b 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -54,6 +54,7 @@ "2005": "Invalid country selection!", "3001": "Not deleted anything!", "3002": "Configuration resettet. Rebooting now...", + "3003": "File successful deleted. Restart to apply changes!", "4001": "@:apiresponse.2001", "4002": "Name must between 1 and {max} characters long!", "4003": "Only {max} inverters are supported!", @@ -556,11 +557,9 @@ "YieldDayCorrection": "Yield Day Correction", "YieldDayCorrectionHint": "Sum up daily yield even if the inverter is restarted. Value will be reset at midnight" }, - "configadmin": { + "fileadmin": { "ConfigManagement": "Config Management", "BackupHeader": "Backup: Configuration File Backup", - "BackupConfig": "Backup the configuration file", - "Backup": "Backup", "Restore": "Restore", "NoFileSelected": "No file selected", "RestoreHeader": "Restore: Restore the Configuration File", @@ -573,6 +572,12 @@ "FactoryReset": "Factory Reset", "ResetMsg": "Are you sure you want to delete the current configuration and reset all settings to their factory defaults?", "ResetConfirm": "Factory Reset!", + "Download": "Download", + "Delete": "Delete", + "DeleteMsg": "Are you sure you want to delete file: '{name}'? You have to manually reboot the device to apply config changes!", + "Name": "Name", + "Size": "Size", + "Action": "Action", "Cancel": "@:base.Cancel" }, "login": { diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 023fc0994..0387565f9 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -54,6 +54,7 @@ "2005": "Invalid country selection !", "3001": "Rien n'a été supprimé !", "3002": "Configuration réinitialisée. Redémarrage maintenant...", + "3003": "File successful deleted. Restart to apply changes!", "4001": "@:apiresponse.2001", "4002": "Le nom doit comporter entre 1 et {max} caractères !", "4003": "Seulement {max} onduleurs sont supportés !", @@ -538,11 +539,9 @@ "YieldDayCorrection": "Yield Day Correction", "YieldDayCorrectionHint": "Sum up daily yield even if the inverter is restarted. Value will be reset at midnight" }, - "configadmin": { + "fileadmin": { "ConfigManagement": "Gestion de la configuration", "BackupHeader": "Sauvegarder le fichier de configuration", - "BackupConfig": "Fichier de configuration", - "Backup": "Sauvegarder", "Restore": "Restaurer", "NoFileSelected": "Aucun fichier sélectionné", "RestoreHeader": "Restaurer le fichier de configuration", @@ -555,6 +554,12 @@ "FactoryReset": "Remise à zéro", "ResetMsg": "Êtes-vous sûr de vouloir supprimer la configuration actuelle et réinitialiser tous les paramètres à leurs valeurs par défaut ?", "ResetConfirm": "Remise à zéro !", + "Download": "Download", + "Delete": "Supprimer", + "DeleteMsg": "Are you sure you want to delete file: '{name}'? You have to manually reboot the device to apply config changes!", + "Name": "Name", + "Size": "Size", + "Action": "Action", "Cancel": "@:base.Cancel" }, "login": { diff --git a/webapp/src/types/Alert.ts b/webapp/src/types/Alert.ts new file mode 100644 index 000000000..f32f133ec --- /dev/null +++ b/webapp/src/types/Alert.ts @@ -0,0 +1,6 @@ +export interface AlertResponse { + message: string; + type: string; + code: number; + show: boolean; +} diff --git a/webapp/src/types/Config.ts b/webapp/src/types/Config.ts deleted file mode 100644 index a86512c5b..000000000 --- a/webapp/src/types/Config.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ConfigFileInfo { - name: string; -} - -export interface ConfigFileList { - configs: Array; -} diff --git a/webapp/src/types/File.ts b/webapp/src/types/File.ts new file mode 100644 index 000000000..0850bd9de --- /dev/null +++ b/webapp/src/types/File.ts @@ -0,0 +1,4 @@ +export interface FileInfo { + name: string; + size: number; +} diff --git a/webapp/src/views/ConfigAdminView.vue b/webapp/src/views/ConfigAdminView.vue index 6005db012..09335a6a2 100644 --- a/webapp/src/views/ConfigAdminView.vue +++ b/webapp/src/views/ConfigAdminView.vue @@ -1,30 +1,38 @@