diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd88f54c..d8a9b1f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,14 +23,14 @@ jobs: - uses: actions/checkout@v4 - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.x" @@ -61,7 +61,7 @@ jobs: run: git fetch --force --tags origin - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -69,13 +69,13 @@ jobs: ${{ runner.os }}-pip- - name: Cache PlatformIO - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.platformio key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.x" diff --git a/.github/workflows/cpplint.yml b/.github/workflows/cpplint.yml index af5e8b79..4ee4b4a8 100644 --- a/.github/workflows/cpplint.yml +++ b/.github/workflows/cpplint.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install dependencies diff --git a/docs/DeviceProfiles/AhoyDTU-ESP32.json b/docs/DeviceProfiles/AhoyDTU-ESP32.json new file mode 100644 index 00000000..5de69446 --- /dev/null +++ b/docs/DeviceProfiles/AhoyDTU-ESP32.json @@ -0,0 +1,76 @@ +[ + { + "name": "AhoyDTU ESP32 Display LED", + "links": [ + {"name": "Information", "url": "https://ahoydtu.de/getting_started/"} + ], + "nrf24": { + "miso": 19, + "mosi": 23, + "clk": 18, + "irq": 16, + "en": 4, + "cs": 5 + }, + "led": { + "led0": 25, + "led1": 26 + }, + "display": { + "type": 2, + "data": 21, + "clk": 22 + } + }, + { + "name": "AhoyDTU ESP32 Display", + "links": [ + {"name": "Information", "url": "https://ahoydtu.de/getting_started/"} + ], + "nrf24": { + "miso": 19, + "mosi": 23, + "clk": 18, + "irq": 16, + "en": 4, + "cs": 5 + }, + "display": { + "type": 2, + "data": 21, + "clk": 22 + } + }, + { + "name": "AhoyDTU ESP32 LED", + "links": [ + {"name": "Information", "url": "https://ahoydtu.de/getting_started/"} + ], + "nrf24": { + "miso": 19, + "mosi": 23, + "clk": 18, + "irq": 16, + "en": 4, + "cs": 5 + }, + "led": { + "led0": 25, + "led1": 26 + } + }, + { + "name": "AhoyDTU ESP32", + "links": [ + {"name": "Information", "url": "https://ahoydtu.de/getting_started/"} + ], + "nrf24": { + "miso": 19, + "mosi": 23, + "clk": 18, + "irq": 16, + "en": 4, + "cs": 5 + } + } +] \ No newline at end of file diff --git a/docs/DeviceProfiles/liligo_t-eth-lite_poe.json b/docs/DeviceProfiles/liligo_t-eth-lite_poe.json new file mode 100644 index 00000000..be91f95b --- /dev/null +++ b/docs/DeviceProfiles/liligo_t-eth-lite_poe.json @@ -0,0 +1,74 @@ +[ + { + "name": "LILYGO T-ETH-Lite-POE CMT", + "links": [ + {"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"} + ], + "eth": { + "enabled": true, + "phy_addr": 0, + "power": 12, + "mdc": 23, + "mdio": 18, + "type": 2, + "clk_mode": 0 + }, + "cmt": { + "clk": 15, + "cs": 32, + "fcs": 33, + "sdio": 4 + } + }, + { + "name": "LILYGO T-ETH-Lite-POE NRF24", + "links": [ + {"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"} + ], + "eth": { + "enabled": true, + "phy_addr": 0, + "power": 12, + "mdc": 23, + "mdio": 18, + "type": 2, + "clk_mode": 0 + }, + "nrf24": { + "miso": 34, + "mosi": 13, + "clk": 14, + "irq": 35, + "en": 4, + "cs": 2 + } + }, + { + "name": "LILYGO T-ETH-Lite-POE NRF24 + Display", + "links": [ + {"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"} + ], + "eth": { + "enabled": true, + "phy_addr": 0, + "power": 12, + "mdc": 23, + "mdio": 18, + "type": 2, + "clk_mode": 0 + }, + "nrf24": { + "miso": 34, + "mosi": 13, + "clk": 14, + "irq": 35, + "en": 4, + "cs": 2 + }, + "display": { + "type": 3, + "data": 32, + "clk": 33 + } + } +] diff --git a/include/Utils.h b/include/Utils.h index 4d4bfee3..fddc2ab9 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -11,4 +11,5 @@ public: static int getTimezoneOffset(); static void restartDtu(); static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line); + static void removeAllFiles(); }; diff --git a/include/WebApi_config.h b/include/WebApi_config.h index 91243c18..f29dc8fc 100644 --- a/include/WebApi_config.h +++ b/include/WebApi_config.h @@ -14,6 +14,4 @@ private: 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); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_device.h b/include/WebApi_device.h index f74315e3..48976bce 100644 --- a/include/WebApi_device.h +++ b/include/WebApi_device.h @@ -11,6 +11,4 @@ public: private: void onDeviceAdminGet(AsyncWebServerRequest* request); void onDeviceAdminPost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_devinfo.h b/include/WebApi_devinfo.h index d1924ecc..e312ecdf 100644 --- a/include/WebApi_devinfo.h +++ b/include/WebApi_devinfo.h @@ -10,6 +10,4 @@ public: private: void onDevInfoStatus(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_dtu.h b/include/WebApi_dtu.h index 3acf4494..20f5274e 100644 --- a/include/WebApi_dtu.h +++ b/include/WebApi_dtu.h @@ -13,8 +13,6 @@ private: void onDtuAdminGet(AsyncWebServerRequest* request); void onDtuAdminPost(AsyncWebServerRequest* request); - AsyncWebServer* _server; - Task _applyDataTask; void applyDataTaskCb(); }; diff --git a/include/WebApi_eventlog.h b/include/WebApi_eventlog.h index 3cba7e5e..e7fe9874 100644 --- a/include/WebApi_eventlog.h +++ b/include/WebApi_eventlog.h @@ -10,6 +10,4 @@ public: private: void onEventlogStatus(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_firmware.h b/include/WebApi_firmware.h index fd9a9642..990a5e06 100644 --- a/include/WebApi_firmware.h +++ b/include/WebApi_firmware.h @@ -11,6 +11,4 @@ public: private: void onFirmwareUpdateFinish(AsyncWebServerRequest* request); void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_gridprofile.h b/include/WebApi_gridprofile.h index 73c15654..cff4ddb8 100644 --- a/include/WebApi_gridprofile.h +++ b/include/WebApi_gridprofile.h @@ -11,6 +11,4 @@ public: private: void onGridProfileStatus(AsyncWebServerRequest* request); void onGridProfileRawdata(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_inverter.h b/include/WebApi_inverter.h index b8f05425..c316622e 100644 --- a/include/WebApi_inverter.h +++ b/include/WebApi_inverter.h @@ -14,6 +14,4 @@ private: void onInverterEdit(AsyncWebServerRequest* request); void onInverterDelete(AsyncWebServerRequest* request); void onInverterOrder(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_limit.h b/include/WebApi_limit.h index 84d48d3a..285be27c 100644 --- a/include/WebApi_limit.h +++ b/include/WebApi_limit.h @@ -11,6 +11,4 @@ public: private: void onLimitStatus(AsyncWebServerRequest* request); void onLimitPost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_maintenance.h b/include/WebApi_maintenance.h index 02dc4702..5a00bbab 100644 --- a/include/WebApi_maintenance.h +++ b/include/WebApi_maintenance.h @@ -10,6 +10,4 @@ public: private: void onRebootPost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_mqtt.h b/include/WebApi_mqtt.h index 6ec971c9..b259752b 100644 --- a/include/WebApi_mqtt.h +++ b/include/WebApi_mqtt.h @@ -15,6 +15,4 @@ private: void onMqttAdminGet(AsyncWebServerRequest* request); void onMqttAdminPost(AsyncWebServerRequest* request); String getTlsCertInfo(const char* cert); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_network.h b/include/WebApi_network.h index 7587bbbd..179fa492 100644 --- a/include/WebApi_network.h +++ b/include/WebApi_network.h @@ -12,6 +12,4 @@ private: void onNetworkStatus(AsyncWebServerRequest* request); void onNetworkAdminGet(AsyncWebServerRequest* request); void onNetworkAdminPost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_ntp.h b/include/WebApi_ntp.h index 75e02c54..5ce040ed 100644 --- a/include/WebApi_ntp.h +++ b/include/WebApi_ntp.h @@ -14,6 +14,4 @@ private: void onNtpAdminPost(AsyncWebServerRequest* request); void onNtpTimeGet(AsyncWebServerRequest* request); void onNtpTimePost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_power.h b/include/WebApi_power.h index 7d186eb4..aed11b0e 100644 --- a/include/WebApi_power.h +++ b/include/WebApi_power.h @@ -11,6 +11,4 @@ public: private: void onPowerStatus(AsyncWebServerRequest* request); void onPowerPost(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_prometheus.h b/include/WebApi_prometheus.h index 08e2221d..b3ee6a18 100644 --- a/include/WebApi_prometheus.h +++ b/include/WebApi_prometheus.h @@ -17,8 +17,6 @@ private: void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel); - AsyncWebServer* _server; - enum MetricType_t { NONE = 0, GAUGE, diff --git a/include/WebApi_security.h b/include/WebApi_security.h index b5981e3d..ac76522a 100644 --- a/include/WebApi_security.h +++ b/include/WebApi_security.h @@ -13,6 +13,4 @@ private: void onSecurityPost(AsyncWebServerRequest* request); void onAuthenticateGet(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_sysstatus.h b/include/WebApi_sysstatus.h index 32bdc7e3..c754ac0d 100644 --- a/include/WebApi_sysstatus.h +++ b/include/WebApi_sysstatus.h @@ -10,6 +10,4 @@ public: private: void onSystemStatus(AsyncWebServerRequest* request); - - AsyncWebServer* _server; }; diff --git a/include/WebApi_webapp.h b/include/WebApi_webapp.h index 401408c6..5330fbdf 100644 --- a/include/WebApi_webapp.h +++ b/include/WebApi_webapp.h @@ -9,5 +9,5 @@ public: void init(AsyncWebServer& server, Scheduler& scheduler); private: - AsyncWebServer* _server; + void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len); }; diff --git a/include/WebApi_ws_console.h b/include/WebApi_ws_console.h index 4289afd0..cf7beecc 100644 --- a/include/WebApi_ws_console.h +++ b/include/WebApi_ws_console.h @@ -10,7 +10,6 @@ public: void init(AsyncWebServer& server, Scheduler& scheduler); private: - AsyncWebServer* _server; AsyncWebSocket _ws; Task _wsCleanupTask; diff --git a/include/WebApi_ws_live.h b/include/WebApi_ws_live.h index 392ca869..05f8ab8f 100644 --- a/include/WebApi_ws_live.h +++ b/include/WebApi_ws_live.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "Configuration.h" #include #include #include @@ -12,17 +13,19 @@ public: void init(AsyncWebServer& server, Scheduler& scheduler); private: - void generateJsonResponse(JsonVariant& root); - void addField(JsonObject& root, std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = ""); - void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits); + static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr inv); + static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr inv); + static void generateCommonJsonResponse(JsonVariant& root); + + static void addField(JsonObject& root, std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = ""); + static void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits); + void onLivedataStatus(AsyncWebServerRequest* request); void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); - AsyncWebServer* _server; AsyncWebSocket _ws; - uint32_t _lastWsPublish = 0; - uint32_t _newestInverterTimestamp = 0; + uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 }; std::mutex _mutex; diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp index d112fd6f..035e52f4 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp @@ -239,8 +239,11 @@ CountryModeId_t HoymilesRadio_CMT::getCountryMode() const void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode) { - _radio->setFrequencyBand(countryDefinition.at(mode).Band); _countryMode = mode; + if (!_isInitialized) { + return; + } + _radio->setFrequencyBand(countryDefinition.at(mode).Band); } uint32_t HoymilesRadio_CMT::getInvBootFrequency() const diff --git a/lib/Hoymiles/src/inverters/HMS_1CH.cpp b/lib/Hoymiles/src/inverters/HMS_1CH.cpp index 312cf630..5d906e58 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_1CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMS_1CH.h" @@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 }, { TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 }, @@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMS_1CH::HMS_1CH(HoymilesRadio* radio, const uint64_t serial) @@ -51,4 +51,4 @@ const byteAssign_t* HMS_1CH::getByteAssignment() const uint8_t HMS_1CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp index b6a9d93e..2cfaa28b 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMS_1CHv2.h" @@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = { { 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_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, 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 }, @@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = { { 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, const uint64_t serial) @@ -51,4 +51,4 @@ const byteAssign_t* HMS_1CHv2::getByteAssignment() const uint8_t HMS_1CHv2::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HMS_2CH.cpp b/lib/Hoymiles/src/inverters/HMS_2CH.cpp index d038e772..56c7fc69 100644 --- a/lib/Hoymiles/src/inverters/HMS_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_2CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMS_2CH.h" @@ -10,14 +10,14 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, 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 }, @@ -29,10 +29,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMS_2CH::HMS_2CH(HoymilesRadio* radio, const uint64_t serial) @@ -58,4 +58,4 @@ const byteAssign_t* HMS_2CH::getByteAssignment() const uint8_t HMS_2CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HMS_4CH.cpp b/lib/Hoymiles/src/inverters/HMS_4CH.cpp index eff44abc..9aeaf106 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_4CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMS_4CH.h" @@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = { { 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_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_DC, CH1, FLD_UDC, UNIT_V, 4, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 }, { TYPE_DC, CH2, FLD_UDC, UNIT_V, 26, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_IDC, UNIT_A, 30, 2, 100, false, 2 }, { TYPE_DC, CH2, FLD_PDC, UNIT_W, 34, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_YD, UNIT_WH, 46, 2, 1, false, 0 }, { TYPE_DC, CH2, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, - { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 }, + { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 }, { TYPE_DC, CH3, FLD_UDC, UNIT_V, 28, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_IDC, UNIT_A, 32, 2, 100, false, 2 }, { TYPE_DC, CH3, FLD_PDC, UNIT_W, 36, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_YD, UNIT_WH, 48, 2, 1, false, 0 }, { TYPE_DC, CH3, FLD_YT, UNIT_KWH, 42, 4, 1000, false, 3 }, - { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 }, + { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 50, 2, 10, false, 1 }, { TYPE_AC, CH0, FLD_IAC, UNIT_A, 58, 2, 100, false, 2 }, @@ -43,10 +43,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 62, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 64, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMS_4CH::HMS_4CH(HoymilesRadio* radio, const uint64_t serial) @@ -72,4 +72,4 @@ const byteAssign_t* HMS_4CH::getByteAssignment() const uint8_t HMS_4CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HMT_4CH.cpp b/lib/Hoymiles/src/inverters/HMT_4CH.cpp index 717099b7..d92a510f 100644 --- a/lib/Hoymiles/src/inverters/HMT_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HMT_4CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMT_4CH.h" @@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 }, { TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 }, { TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 }, - { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 }, + { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 }, { TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 }, { TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, { TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 }, - { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 }, + { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy { TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 }, @@ -52,10 +52,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMT_4CH::HMT_4CH(HoymilesRadio* radio, const uint64_t serial) diff --git a/lib/Hoymiles/src/inverters/HMT_6CH.cpp b/lib/Hoymiles/src/inverters/HMT_6CH.cpp index 6cbd2097..757cf91d 100644 --- a/lib/Hoymiles/src/inverters/HMT_6CH.cpp +++ b/lib/Hoymiles/src/inverters/HMT_6CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2023 Thomas Basler and others + * Copyright (C) 2023-2024 Thomas Basler and others */ #include "HMT_6CH.h" @@ -10,42 +10,42 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_DC, CH1, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 }, { TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 }, { TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 }, - { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 }, + { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 }, { TYPE_DC, CH3, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 }, { TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, { TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 }, - { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 }, + { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 }, { TYPE_DC, CH4, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 }, { TYPE_DC, CH4, FLD_IDC, UNIT_A, 48, 2, 100, false, 2 }, { TYPE_DC, CH4, FLD_PDC, UNIT_W, 52, 2, 10, false, 1 }, { TYPE_DC, CH4, FLD_YT, UNIT_KWH, 56, 4, 1000, false, 3 }, { TYPE_DC, CH4, FLD_YD, UNIT_WH, 64, 2, 1, false, 0 }, - { TYPE_DC, CH4, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH4, CMD_CALC, false, 3 }, + { TYPE_DC, CH4, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH4, CMD_CALC, false, 3 }, { TYPE_DC, CH5, FLD_UDC, UNIT_V, 46, 2, 10, false, 1 }, { TYPE_DC, CH5, FLD_IDC, UNIT_A, 50, 2, 100, false, 2 }, { TYPE_DC, CH5, FLD_PDC, UNIT_W, 54, 2, 10, false, 1 }, { TYPE_DC, CH5, FLD_YT, UNIT_KWH, 60, 4, 1000, false, 3 }, { TYPE_DC, CH5, FLD_YD, UNIT_WH, 66, 2, 1, false, 0 }, - { TYPE_DC, CH5, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH5, CMD_CALC, false, 3 }, + { TYPE_DC, CH5, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH5, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 74, 2, 10, false, 1 }, // dummy { TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 }, @@ -57,7 +57,7 @@ static const byteAssign_t byteAssignment[] = { { TYPE_AC, CH0, FLD_F, UNIT_HZ, 80, 2, 100, false, 2 }, { TYPE_AC, CH0, FLD_PAC, UNIT_W, 82, 2, 10, false, 1 }, { TYPE_AC, CH0, FLD_Q, UNIT_VAR, 84, 2, 10, true, 1 }, - { TYPE_AC, CH0, FLD_IAC, UNIT_A, 86, 2, 100, false, 2 }, // dummy + { TYPE_AC, CH0, FLD_IAC, UNIT_A, CALC_TOTAL_IAC, 0, CMD_CALC, false, 2 }, { TYPE_AC, CH0, FLD_IAC_1, UNIT_A, 86, 2, 100, false, 2 }, { TYPE_AC, CH0, FLD_IAC_2, UNIT_A, 88, 2, 100, false, 2 }, { TYPE_AC, CH0, FLD_IAC_3, UNIT_A, 90, 2, 100, false, 2 }, @@ -66,10 +66,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 94, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HMT_6CH::HMT_6CH(HoymilesRadio* radio, const uint64_t serial) diff --git a/lib/Hoymiles/src/inverters/HM_1CH.cpp b/lib/Hoymiles/src/inverters/HM_1CH.cpp index 7b23207d..670b7dbe 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_1CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "HM_1CH.h" @@ -10,7 +10,7 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 8, 4, 1000, false, 3 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 14, 2, 10, false, 1 }, { TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 }, @@ -22,10 +22,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 26, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HM_1CH::HM_1CH(HoymilesRadio* radio, const uint64_t serial) @@ -64,4 +64,4 @@ const byteAssign_t* HM_1CH::getByteAssignment() const uint8_t HM_1CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HM_2CH.cpp b/lib/Hoymiles/src/inverters/HM_2CH.cpp index 2f56ec3e..6d9b7ca9 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_2CH.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "HM_2CH.h" @@ -11,14 +11,14 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 6, 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_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, { TYPE_DC, CH1, FLD_UDC, UNIT_V, 8, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 10, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 12, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, 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 }, @@ -30,10 +30,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HM_2CH::HM_2CH(HoymilesRadio* radio, const uint64_t serial) @@ -72,4 +72,4 @@ const byteAssign_t* HM_2CH::getByteAssignment() const uint8_t HM_2CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/HM_4CH.cpp b/lib/Hoymiles/src/inverters/HM_4CH.cpp index bcad2536..13ca061a 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_4CH.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "HM_4CH.h" @@ -10,28 +10,28 @@ static const byteAssign_t byteAssignment[] = { { TYPE_DC, CH0, FLD_PDC, UNIT_W, 8, 2, 10, false, 1 }, { TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 }, { TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 }, - { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH0, CMD_CALC, false, 3 }, + { TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 }, - { TYPE_DC, CH1, FLD_UDC, UNIT_V, CALC_UDC_CH, CH0, CMD_CALC, false, 1 }, + { TYPE_DC, CH1, FLD_UDC, UNIT_V, CALC_CH_UDC, CH0, CMD_CALC, false, 1 }, { TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, { TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, { TYPE_DC, CH1, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, { TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, - { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH1, CMD_CALC, false, 3 }, + { TYPE_DC, CH1, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH1, CMD_CALC, false, 3 }, { TYPE_DC, CH2, FLD_UDC, UNIT_V, 24, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { TYPE_DC, CH2, FLD_PDC, UNIT_W, 30, 2, 10, false, 1 }, { TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 }, { TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 }, - { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH2, CMD_CALC, false, 3 }, + { TYPE_DC, CH2, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH2, CMD_CALC, false, 3 }, - { TYPE_DC, CH3, FLD_UDC, UNIT_V, CALC_UDC_CH, CH2, CMD_CALC, false, 1 }, + { TYPE_DC, CH3, FLD_UDC, UNIT_V, CALC_CH_UDC, CH2, CMD_CALC, false, 1 }, { TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 }, { TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 }, { TYPE_DC, CH3, FLD_YD, UNIT_WH, 44, 2, 1, false, 0 }, { TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, - { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_IRR_CH, CH3, CMD_CALC, false, 3 }, + { TYPE_DC, CH3, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH3, CMD_CALC, false, 3 }, { TYPE_AC, CH0, FLD_UAC, UNIT_V, 46, 2, 10, false, 1 }, { TYPE_AC, CH0, FLD_IAC, UNIT_A, 54, 2, 100, false, 2 }, @@ -43,10 +43,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_T, UNIT_C, 58, 2, 10, true, 1 }, { TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 60, 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 } + { TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 }, + { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 }, + { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 }, + { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; HM_4CH::HM_4CH(HoymilesRadio* radio, const uint64_t serial) @@ -85,4 +85,4 @@ const byteAssign_t* HM_4CH::getByteAssignment() const uint8_t HM_4CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index fd6ed5cc..b2b30a2e 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -28,9 +28,11 @@ const devInfo_t devInfo[] = { { { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500-4T" }, { { 0x10, 0x10, 0x10, 0x15 }, static_cast(300 * 0.7), "HM-300-1T" }, // HM-300 factory limitted to 70% + { { 0x10, 0x20, 0x11, ALL }, 300, "HMS-300-1T" }, // 00 { { 0x10, 0x20, 0x21, ALL }, 350, "HMS-350-1T" }, // 00 { { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400-1T" }, // 00 { { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450-1T" }, // 01 + { { 0x10, 0x20, 0x51, ALL }, 450, "HMS-450-1T" }, // 03 { { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500-1T" }, // 02 { { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02 { { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600-2T" }, // 01 @@ -47,6 +49,7 @@ const devInfo_t devInfo[] = { { { 0x10, 0x32, 0x41, ALL }, 1600, "HMT-1600-4T" }, // 00 { { 0x10, 0x32, 0x51, ALL }, 1800, "HMT-1800-4T" }, // 00 + { { 0x10, 0x32, 0x71, ALL }, 2000, "HMT-2000-4T" }, // 0 { { 0x10, 0x33, 0x11, ALL }, 1800, "HMT-1800-6T" }, // 01 { { 0x10, 0x33, 0x31, ALL }, 2250, "HMT-2250-6T" } // 01 diff --git a/lib/Hoymiles/src/parser/StatisticsParser.cpp b/lib/Hoymiles/src/parser/StatisticsParser.cpp index 831c1ad1..bd405611 100644 --- a/lib/Hoymiles/src/parser/StatisticsParser.cpp +++ b/lib/Hoymiles/src/parser/StatisticsParser.cpp @@ -5,12 +5,13 @@ #include "StatisticsParser.h" #include "../Hoymiles.h" -static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0); -static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0); -static float calcUdcCh(StatisticsParser* iv, uint8_t arg0); -static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0); -static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0); -static float calcIrradiation(StatisticsParser* iv, uint8_t arg0); +static float calcTotalYieldTotal(StatisticsParser* iv, uint8_t arg0); +static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0); +static float calcChUdc(StatisticsParser* iv, uint8_t arg0); +static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0); +static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0); +static float calcChIrradiation(StatisticsParser* iv, uint8_t arg0); +static float calcTotalCurrentAc(StatisticsParser* iv, uint8_t arg0); using func_t = float(StatisticsParser*, uint8_t); @@ -20,12 +21,13 @@ struct calcFunc_t { }; const calcFunc_t calcFunctions[] = { - { CALC_YT_CH0, &calcYieldTotalCh0 }, - { CALC_YD_CH0, &calcYieldDayCh0 }, - { CALC_UDC_CH, &calcUdcCh }, - { CALC_PDC_CH0, &calcPowerDcCh0 }, - { CALC_EFF_CH0, &calcEffiencyCh0 }, - { CALC_IRR_CH, &calcIrradiation } + { CALC_TOTAL_YT, &calcTotalYieldTotal }, + { CALC_TOTAL_YD, &calcTotalYieldDay }, + { CALC_CH_UDC, &calcChUdc }, + { CALC_TOTAL_PDC, &calcTotalPowerDc }, + { CALC_TOTAL_EFF, &calcTotalEffiency }, + { CALC_CH_IRR, &calcChIrradiation }, + { CALC_TOTAL_IAC, &calcTotalCurrentAc } }; const FieldId_t runtimeFields[] = { @@ -386,7 +388,7 @@ void StatisticsParser::resetYieldDayCorrection() } } -static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0) +static float calcTotalYieldTotal(StatisticsParser* iv, uint8_t arg0) { float yield = 0; for (auto& channel : iv->getChannelsByType(TYPE_DC)) { @@ -395,7 +397,7 @@ static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0) return yield; } -static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0) +static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0) { float yield = 0; for (auto& channel : iv->getChannelsByType(TYPE_DC)) { @@ -405,12 +407,12 @@ static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0) } // arg0 = channel of source -static float calcUdcCh(StatisticsParser* iv, uint8_t arg0) +static float calcChUdc(StatisticsParser* iv, uint8_t arg0) { return iv->getChannelFieldValue(TYPE_DC, static_cast(arg0), FLD_UDC); } -static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0) +static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0) { float dcPower = 0; for (auto& channel : iv->getChannelsByType(TYPE_DC)) { @@ -419,8 +421,7 @@ static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0) return dcPower; } -// arg0 = channel -static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0) +static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0) { float acPower = 0; for (auto& channel : iv->getChannelsByType(TYPE_AC)) { @@ -439,7 +440,7 @@ static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0) } // arg0 = channel -static float calcIrradiation(StatisticsParser* iv, uint8_t arg0) +static float calcChIrradiation(StatisticsParser* iv, uint8_t arg0) { if (nullptr != iv) { if (iv->getStringMaxPower(arg0) > 0) @@ -447,3 +448,12 @@ static float calcIrradiation(StatisticsParser* iv, uint8_t arg0) } return 0.0; } + +static float calcTotalCurrentAc(StatisticsParser* iv, uint8_t arg0) +{ + float acCurrent = 0; + acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_1); + acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_2); + acCurrent += iv->getChannelFieldValue(TYPE_AC, CH0, FLD_IAC_3); + return acCurrent; +} diff --git a/lib/Hoymiles/src/parser/StatisticsParser.h b/lib/Hoymiles/src/parser/StatisticsParser.h index 10f06e04..90b9a5a9 100644 --- a/lib/Hoymiles/src/parser/StatisticsParser.h +++ b/lib/Hoymiles/src/parser/StatisticsParser.h @@ -55,12 +55,13 @@ const char* const fields[] = { "Voltage", "Current", "Power", "YieldDay", "Yield // indices to calculation functions, defined in hmInverter.h enum { - CALC_YT_CH0 = 0, - CALC_YD_CH0, - CALC_UDC_CH, - CALC_PDC_CH0, - CALC_EFF_CH0, - CALC_IRR_CH + CALC_TOTAL_YT = 0, + CALC_TOTAL_YD, + CALC_CH_UDC, + CALC_TOTAL_PDC, + CALC_TOTAL_EFF, + CALC_CH_IRR, + CALC_TOTAL_IAC }; enum { CMD_CALC = 0xffff }; @@ -169,4 +170,4 @@ private: bool _enableYieldDayCorrection = false; float _lastYieldDay[CH_CNT] = {}; -}; \ No newline at end of file +}; diff --git a/partitions_custom_16mb.csv b/partitions_custom_16mb.csv new file mode 100644 index 00000000..1c48e6bb --- /dev/null +++ b/partitions_custom_16mb.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000 +otadata, data, ota, 0xE000, 0x2000 +app0, app, ota_0, 0x10000, 0x7E0000 +app1, app, ota_1, 0x7F0000, 0x7E0000 +spiffs, data, spiffs, 0xFD0000, 0x30000 diff --git a/partitions_custom.csv b/partitions_custom_4mb.csv similarity index 100% rename from partitions_custom.csv rename to partitions_custom_4mb.csv diff --git a/platformio.ini b/platformio.ini index e145e14e..adf7eb87 100644 --- a/platformio.ini +++ b/platformio.ini @@ -36,9 +36,9 @@ build_unflags = -std=gnu++11 lib_deps = - https://github.com/yubox-node-org/ESPAsyncWebServer + mathieucarbou/ESP Async WebServer @ 2.7.0 bblanchon/ArduinoJson @ ^6.21.5 - https://github.com/bertmelis/espMqttClient.git#v1.5.0 + https://github.com/bertmelis/espMqttClient.git#v1.6.0 nrf24/RF24 @ ^1.4.8 olikraus/U8g2 @ ^2.35.9 buelowp/sunset @ ^1.1.7 @@ -54,7 +54,7 @@ extra_scripts = pre:pio-scripts/patch_apply.py post:pio-scripts/create_factory_bin.py -board_build.partitions = partitions_custom.csv +board_build.partitions = partitions_custom_4mb.csv board_build.filesystem = littlefs board_build.embed_files = webapp_dist/index.html.gz @@ -80,6 +80,16 @@ board = esp32dev build_flags = ${env.build_flags} +[env:generic_esp32_16mb_psram] +board = esp32dev +board_build.flash_mode = qio +board_build.partitions = partitions_custom_16mb.csv +board_upload.flash_size = 16MB +build_flags = ${env.build_flags} + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue + + [env:generic_esp32c3] board = esp32-c3-devkitc-02 custom_patches = ${env.custom_patches},esp32c3 diff --git a/src/Datastore.cpp b/src/Datastore.cpp index 5bfbb98e..15a6dad0 100644 --- a/src/Datastore.cpp +++ b/src/Datastore.cpp @@ -81,14 +81,17 @@ void DatastoreClass::loop() } } - for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) { + for (auto& c : inv->Statistics()->getChannelsByType(TYPE_INV)) { if (cfg->Poll_Enable) { - _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); - _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); + _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YT); + _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YD); - _totalAcYieldTotalDigits = max(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); - _totalAcYieldDayDigits = max(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); + _totalAcYieldTotalDigits = max(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YT)); + _totalAcYieldDayDigits = max(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YD)); } + } + + for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) { if (inv->getEnablePolling()) { _totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); _totalAcPowerDigits = max(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); diff --git a/src/Display_Graphic.cpp b/src/Display_Graphic.cpp index ae9973d6..98d46eea 100644 --- a/src/Display_Graphic.cpp +++ b/src/Display_Graphic.cpp @@ -37,6 +37,7 @@ static const char* const i18n_meter_power_w[] = { "grid: %.0f W", "Netz: %.0 static const char* const i18n_meter_power_kw[] = { "grid: %.1f kW", "Netz: %.1f kW", "reseau: %.1f kW" }; static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" }; static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" }; +static const char* const i18n_yield_total_mwh[] = { "total: %.0f kWh", "Ges.: %.0f kWh", "total: %.0f kWh" }; static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" }; DisplayGraphicClass::DisplayGraphicClass() @@ -74,16 +75,16 @@ void DisplayGraphicClass::calcLineHeights() bool diagram = (_isLarge && _diagram_mode == DiagramMode_t::Small); // the diagram needs space. we need to keep // away from the y-axis label in particular. - uint8_t yOff = (diagram?7:0); + uint8_t yOff = (diagram ? 7 : 0); for (uint8_t i = 0; i < 4; i++) { setFont(i); yOff += _display->getAscent(); _lineOffsets[i] = yOff; - yOff += ((!_isLarge || diagram)?2:3); + yOff += ((!_isLarge || diagram) ? 2 : 3); // the descent is a negative value and moves the *next* line's // baseline. the first line never uses a letter with descent and // we need that space when showing the small diagram. - yOff -= ((i == 0 && diagram)?0:_display->getDescent()); + yOff -= ((i == 0 && diagram) ? 0 : _display->getDescent()); } } @@ -115,27 +116,23 @@ void DisplayGraphicClass::printText(const char* text, const uint8_t line) if (!_isLarge) { dispX = (line == 0) ? 5 : 0; } else { - switch (line) { - case 0: - if (_diagram_mode == DiagramMode_t::Small) { - // Center between left border and diagram - dispX = (CHART_POSX - _display->getStrWidth(text)) / 2; - } else { - // Center on screen - dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2; - } - break; - case 3: + if (line == 0 && _diagram_mode == DiagramMode_t::Small) { + // Center between left border and diagram + dispX = (CHART_POSX - _display->getStrWidth(text)) / 2; + } else { // Center on screen dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2; - break; - default: - dispX = 5; - break; } } - dispX += enableScreensaver ? (_mExtra % 7) : 0; + if (enableScreensaver) { + unsigned maxOffset = (_isLarge ? 8 : 6); + unsigned period = 2 * maxOffset; + unsigned step = _mExtra % period; + int offset = (step <= maxOffset) ? step : (period - step); + offset -= (_isLarge ? 5 : 0); // oscillate around center on large screens + dispX += offset; + } _display->drawStr(dispX, _lineOffsets[line], text); } @@ -248,7 +245,9 @@ void DisplayGraphicClass::loop() snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled()); printText(_fmtText, 1); - snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled()); + const float watts = Datastore.getTotalAcYieldTotalEnabled(); + auto const format = (watts >= 1000) ? i18n_yield_total_mwh : i18n_yield_total_kwh; + snprintf(_fmtText, sizeof(_fmtText), format[_display_language], watts); printText(_fmtText, 2); //<======================= diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 1df7237e..21ff0fa2 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -107,7 +107,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr const String serial = inv->serialString(); String fieldName; - if (type == TYPE_AC && fieldType.fieldId == FLD_PDC) { + if (type == TYPE_INV && fieldType.fieldId == FLD_PDC) { fieldName = "PowerDC"; } else { fieldName = inv->Statistics()->getChannelFieldName(type, channel, fieldType.fieldId); diff --git a/src/MqttHandleInverter.cpp b/src/MqttHandleInverter.cpp index 53cf490b..de2778d1 100644 --- a/src/MqttHandleInverter.cpp +++ b/src/MqttHandleInverter.cpp @@ -141,7 +141,7 @@ String MqttHandleInverterClass::getTopic(std::shared_ptr inv, } String chanName; - if (type == TYPE_AC && fieldId == FLD_PDC) { + if (type == TYPE_INV && fieldId == FLD_PDC) { chanName = "powerdc"; } else { chanName = inv->Statistics()->getChannelFieldName(type, channel, fieldId); diff --git a/src/MqttSettings.cpp b/src/MqttSettings.cpp index a0b23686..c2b94662 100644 --- a/src/MqttSettings.cpp +++ b/src/MqttSettings.cpp @@ -128,7 +128,7 @@ void MqttSettingsClass::performConnect() } else { static_cast(_mqttClient)->setCredentials(config.Mqtt.Username, config.Mqtt.Password); } - static_cast(_mqttClient)->setWill(willTopic.c_str(), 2, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline); + static_cast(_mqttClient)->setWill(willTopic.c_str(), config.Mqtt.Lwt.Qos, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline); static_cast(_mqttClient)->setClientId(clientId.c_str()); static_cast(_mqttClient)->setCleanSession(config.Mqtt.CleanSession); static_cast(_mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1)); @@ -226,4 +226,4 @@ void MqttSettingsClass::createMqttClientObject() } } -MqttSettingsClass MqttSettings; \ No newline at end of file +MqttSettingsClass MqttSettings; diff --git a/src/Utils.cpp b/src/Utils.cpp index 386e0ed1..7ad07293 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -6,7 +6,9 @@ #include "Display_Graphic.h" #include "Led_Single.h" #include "MessageOutput.h" +#include "PinMapping.h" #include +#include uint32_t Utils::getChipId() { @@ -76,3 +78,17 @@ bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, return true; } + +/// @brief Remove all files but the PINMAPPING_FILENAME +void Utils::removeAllFiles() +{ + auto root = LittleFS.open("/"); + auto file = root.getNextFileName(); + + while (file != "") { + if (file != PINMAPPING_FILENAME) { + LittleFS.remove(file); + } + file = root.getNextFileName(); + } +} diff --git a/src/WebApi_config.cpp b/src/WebApi_config.cpp index 3372e4f4..29f35319 100644 --- a/src/WebApi_config.cpp +++ b/src/WebApi_config.cpp @@ -19,12 +19,10 @@ void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler) using std::placeholders::_5; using std::placeholders::_6; - _server = &server; - - _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, + 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)); } @@ -110,7 +108,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) response->setLength(); request->send(response); - LittleFS.remove(CONFIG_FILENAME); + Utils::removeAllFiles(); Utils::restartDtu(); } diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 4f55de8d..cc08dfaa 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -16,10 +16,8 @@ void WebApiDeviceClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/device/config", HTTP_GET, std::bind(&WebApiDeviceClass::onDeviceAdminGet, this, _1)); - _server->on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1)); + server.on("/api/device/config", HTTP_GET, std::bind(&WebApiDeviceClass::onDeviceAdminGet, this, _1)); + server.on("/api/device/config", HTTP_POST, std::bind(&WebApiDeviceClass::onDeviceAdminPost, this, _1)); } void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) diff --git a/src/WebApi_devinfo.cpp b/src/WebApi_devinfo.cpp index a27cb31e..212a7f7d 100644 --- a/src/WebApi_devinfo.cpp +++ b/src/WebApi_devinfo.cpp @@ -12,9 +12,7 @@ void WebApiDevInfoClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/devinfo/status", HTTP_GET, std::bind(&WebApiDevInfoClass::onDevInfoStatus, this, _1)); + server.on("/api/devinfo/status", HTTP_GET, std::bind(&WebApiDevInfoClass::onDevInfoStatus, this, _1)); } void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_dtu.cpp b/src/WebApi_dtu.cpp index adfd411c..bbdfd070 100644 --- a/src/WebApi_dtu.cpp +++ b/src/WebApi_dtu.cpp @@ -18,10 +18,8 @@ void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/dtu/config", HTTP_GET, std::bind(&WebApiDtuClass::onDtuAdminGet, this, _1)); - _server->on("/api/dtu/config", HTTP_POST, std::bind(&WebApiDtuClass::onDtuAdminPost, this, _1)); + server.on("/api/dtu/config", HTTP_GET, std::bind(&WebApiDtuClass::onDtuAdminGet, this, _1)); + server.on("/api/dtu/config", HTTP_POST, std::bind(&WebApiDtuClass::onDtuAdminPost, this, _1)); scheduler.addTask(_applyDataTask); } diff --git a/src/WebApi_eventlog.cpp b/src/WebApi_eventlog.cpp index a92e515e..51e85aff 100644 --- a/src/WebApi_eventlog.cpp +++ b/src/WebApi_eventlog.cpp @@ -11,9 +11,7 @@ void WebApiEventlogClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/eventlog/status", HTTP_GET, std::bind(&WebApiEventlogClass::onEventlogStatus, this, _1)); + server.on("/api/eventlog/status", HTTP_GET, std::bind(&WebApiEventlogClass::onEventlogStatus, this, _1)); } void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_firmware.cpp b/src/WebApi_firmware.cpp index 617fca06..9491f935 100644 --- a/src/WebApi_firmware.cpp +++ b/src/WebApi_firmware.cpp @@ -19,9 +19,7 @@ void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler) using std::placeholders::_5; using std::placeholders::_6; - _server = &server; - - _server->on("/api/firmware/update", HTTP_POST, + server.on("/api/firmware/update", HTTP_POST, std::bind(&WebApiFirmwareClass::onFirmwareUpdateFinish, this, _1), std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6)); } diff --git a/src/WebApi_gridprofile.cpp b/src/WebApi_gridprofile.cpp index 587f4640..60c340fa 100644 --- a/src/WebApi_gridprofile.cpp +++ b/src/WebApi_gridprofile.cpp @@ -11,10 +11,8 @@ void WebApiGridProfileClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/gridprofile/status", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileStatus, this, _1)); - _server->on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1)); + server.on("/api/gridprofile/status", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileStatus, this, _1)); + server.on("/api/gridprofile/rawdata", HTTP_GET, std::bind(&WebApiGridProfileClass::onGridProfileRawdata, this, _1)); } void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index f1bfc2aa..32a47235 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -16,13 +16,11 @@ void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/inverter/list", HTTP_GET, std::bind(&WebApiInverterClass::onInverterList, this, _1)); - _server->on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1)); - _server->on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1)); - _server->on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1)); - _server->on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1)); + server.on("/api/inverter/list", HTTP_GET, std::bind(&WebApiInverterClass::onInverterList, this, _1)); + server.on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1)); + server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1)); + server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1)); + server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1)); } void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) diff --git a/src/WebApi_limit.cpp b/src/WebApi_limit.cpp index be8e1202..1d9c111a 100644 --- a/src/WebApi_limit.cpp +++ b/src/WebApi_limit.cpp @@ -14,10 +14,8 @@ void WebApiLimitClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/limit/status", HTTP_GET, std::bind(&WebApiLimitClass::onLimitStatus, this, _1)); - _server->on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1)); + server.on("/api/limit/status", HTTP_GET, std::bind(&WebApiLimitClass::onLimitStatus, this, _1)); + server.on("/api/limit/config", HTTP_POST, std::bind(&WebApiLimitClass::onLimitPost, this, _1)); } void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_maintenance.cpp b/src/WebApi_maintenance.cpp index 922b0ba0..ba257efa 100644 --- a/src/WebApi_maintenance.cpp +++ b/src/WebApi_maintenance.cpp @@ -13,9 +13,7 @@ void WebApiMaintenanceClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/maintenance/reboot", HTTP_POST, std::bind(&WebApiMaintenanceClass::onRebootPost, this, _1)); + server.on("/api/maintenance/reboot", HTTP_POST, std::bind(&WebApiMaintenanceClass::onRebootPost, this, _1)); } void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request) diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index bdb19ef0..9e7411bf 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -19,11 +19,9 @@ void WebApiMqttClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/mqtt/status", HTTP_GET, std::bind(&WebApiMqttClass::onMqttStatus, this, _1)); - _server->on("/api/mqtt/config", HTTP_GET, std::bind(&WebApiMqttClass::onMqttAdminGet, this, _1)); - _server->on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1)); + server.on("/api/mqtt/status", HTTP_GET, std::bind(&WebApiMqttClass::onMqttStatus, this, _1)); + server.on("/api/mqtt/config", HTTP_GET, std::bind(&WebApiMqttClass::onMqttAdminGet, this, _1)); + server.on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, this, _1)); } void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request) @@ -85,7 +83,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request) root["mqtt_client_cert"] = config.Mqtt.Tls.ClientCert; root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey; root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic; - root["mqtt_lwt_online"] = config.Mqtt.CleanSession; + root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online;; root["mqtt_lwt_offline"] = config.Mqtt.Lwt.Value_Offline; root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos; root["mqtt_publish_interval"] = config.Mqtt.PublishInterval; diff --git a/src/WebApi_network.cpp b/src/WebApi_network.cpp index ba998053..12f637ad 100644 --- a/src/WebApi_network.cpp +++ b/src/WebApi_network.cpp @@ -14,11 +14,9 @@ void WebApiNetworkClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/network/status", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkStatus, this, _1)); - _server->on("/api/network/config", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkAdminGet, this, _1)); - _server->on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1)); + server.on("/api/network/status", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkStatus, this, _1)); + server.on("/api/network/config", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkAdminGet, this, _1)); + server.on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, this, _1)); } void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index e0bcd699..02bbfb10 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -15,13 +15,11 @@ void WebApiNtpClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/ntp/status", HTTP_GET, std::bind(&WebApiNtpClass::onNtpStatus, this, _1)); - _server->on("/api/ntp/config", HTTP_GET, std::bind(&WebApiNtpClass::onNtpAdminGet, this, _1)); - _server->on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1)); - _server->on("/api/ntp/time", HTTP_GET, std::bind(&WebApiNtpClass::onNtpTimeGet, this, _1)); - _server->on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1)); + server.on("/api/ntp/status", HTTP_GET, std::bind(&WebApiNtpClass::onNtpStatus, this, _1)); + server.on("/api/ntp/config", HTTP_GET, std::bind(&WebApiNtpClass::onNtpAdminGet, this, _1)); + server.on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1)); + server.on("/api/ntp/time", HTTP_GET, std::bind(&WebApiNtpClass::onNtpTimeGet, this, _1)); + server.on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, this, _1)); } void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_power.cpp b/src/WebApi_power.cpp index 54fc664e..b5196789 100644 --- a/src/WebApi_power.cpp +++ b/src/WebApi_power.cpp @@ -12,10 +12,8 @@ void WebApiPowerClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/power/status", HTTP_GET, std::bind(&WebApiPowerClass::onPowerStatus, this, _1)); - _server->on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1)); + server.on("/api/power/status", HTTP_GET, std::bind(&WebApiPowerClass::onPowerStatus, this, _1)); + server.on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1)); } void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request) diff --git a/src/WebApi_prometheus.cpp b/src/WebApi_prometheus.cpp index 275e568b..8e6be8c9 100644 --- a/src/WebApi_prometheus.cpp +++ b/src/WebApi_prometheus.cpp @@ -15,9 +15,7 @@ void WebApiPrometheusClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/prometheus/metrics", HTTP_GET, std::bind(&WebApiPrometheusClass::onPrometheusMetricsGet, this, _1)); + server.on("/api/prometheus/metrics", HTTP_GET, std::bind(&WebApiPrometheusClass::onPrometheusMetricsGet, this, _1)); } void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request) @@ -100,7 +98,7 @@ void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* reques for (auto& c : inv->Statistics()->getChannelsByType(t)) { addPanelInfo(stream, serial, i, inv, t, c); for (uint8_t f = 0; f < sizeof(_publishFields) / sizeof(_publishFields[0]); f++) { - if (t == TYPE_AC && _publishFields[f].field == FLD_PDC) { + if (t == TYPE_INV && _publishFields[f].field == FLD_PDC) { addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type], "PowerDC"); } else { addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type]); diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index 20519681..b95ebb29 100644 --- a/src/WebApi_security.cpp +++ b/src/WebApi_security.cpp @@ -13,11 +13,9 @@ void WebApiSecurityClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/security/config", HTTP_GET, std::bind(&WebApiSecurityClass::onSecurityGet, this, _1)); - _server->on("/api/security/config", HTTP_POST, std::bind(&WebApiSecurityClass::onSecurityPost, this, _1)); - _server->on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1)); + server.on("/api/security/config", HTTP_GET, std::bind(&WebApiSecurityClass::onSecurityGet, this, _1)); + server.on("/api/security/config", HTTP_POST, std::bind(&WebApiSecurityClass::onSecurityPost, this, _1)); + server.on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, this, _1)); } void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request) diff --git a/src/WebApi_sysstatus.cpp b/src/WebApi_sysstatus.cpp index 9c822376..b8c366b3 100644 --- a/src/WebApi_sysstatus.cpp +++ b/src/WebApi_sysstatus.cpp @@ -24,9 +24,7 @@ void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler) { using std::placeholders::_1; - _server = &server; - - _server->on("/api/system/status", HTTP_GET, std::bind(&WebApiSysstatusClass::onSystemStatus, this, _1)); + server.on("/api/system/status", HTTP_GET, std::bind(&WebApiSysstatusClass::onSystemStatus, this, _1)); } void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) @@ -47,6 +45,8 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap(); root["heap_max_block"] = ESP.getMaxAllocHeap(); root["heap_min_free"] = ESP.getMinFreeHeap(); + root["psram_total"] = ESP.getPsramSize(); + root["psram_used"] = ESP.getPsramSize() - ESP.getFreePsram(); root["sketch_total"] = ESP.getFreeSketchSpace(); root["sketch_used"] = ESP.getSketchSize(); root["littlefs_total"] = LittleFS.totalBytes(); diff --git a/src/WebApi_webapp.cpp b/src/WebApi_webapp.cpp index 9203505b..b8b81385 100644 --- a/src/WebApi_webapp.cpp +++ b/src/WebApi_webapp.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_webapp.h" +#include extern const uint8_t file_index_html_start[] asm("_binary_webapp_dist_index_html_gz_start"); extern const uint8_t file_favicon_ico_start[] asm("_binary_webapp_dist_favicon_ico_start"); @@ -18,79 +19,78 @@ extern const uint8_t file_zones_json_end[] asm("_binary_webapp_dist_zones_json_g extern const uint8_t file_app_js_end[] asm("_binary_webapp_dist_js_app_js_gz_end"); extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_webmanifest_end"); -#ifdef AUTO_GIT_HASH -#define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes -#endif +void WebApiWebappClass::responseBinaryDataWithETagCache(AsyncWebServerRequest *request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len) +{ + auto md5 = MD5Builder(); + md5.begin(); + md5.add(const_cast(content), len); + md5.calculate(); + + String expectedEtag; + expectedEtag = "\""; + expectedEtag += md5.toString(); + expectedEtag += "\""; + + bool eTagMatch = false; + if (request->hasHeader("If-None-Match")) { + const AsyncWebHeader* h = request->getHeader("If-None-Match"); + eTagMatch = h->value().equals(expectedEtag); + } + + // begin response 200 or 304 + AsyncWebServerResponse* response; + if (eTagMatch) { + response = request->beginResponse(304); + } else { + response = request->beginResponse_P(200, contentType, content, len); + if (contentEncoding.length() > 0) { + response->addHeader("Content-Encoding", contentEncoding); + } + } + + // HTTP requires cache headers in 200 and 304 to be identical + response->addHeader("Cache-Control", "public, must-revalidate"); + response->addHeader("ETag", expectedEtag); + + request->send(response); +} void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler) { - _server = &server; + /* + We don't validate the request header "Accept-Encoding" if gzip compression is supported! + We just have the gzipped data available - so we ship them! + */ - _server->on("/", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start); - response->addHeader("Content-Encoding", "gzip"); - request->send(response); + server.on("/", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start); }); - _server->onNotFound([](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start); - response->addHeader("Content-Encoding", "gzip"); - request->send(response); + server.onNotFound([&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start); }); - _server->on("/index.html", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start); - response->addHeader("Content-Encoding", "gzip"); - request->send(response); + server.on("/index.html", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start); }); - _server->on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "image/x-icon", file_favicon_ico_start, file_favicon_ico_end - file_favicon_ico_start); - request->send(response); + server.on("/favicon.ico", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "image/x-icon", "", file_favicon_ico_start, file_favicon_ico_end - file_favicon_ico_start); }); - _server->on("/favicon.png", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "image/png", file_favicon_png_start, file_favicon_png_end - file_favicon_png_start); - request->send(response); + server.on("/favicon.png", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "image/png", "", file_favicon_png_start, file_favicon_png_end - file_favicon_png_start); }); - _server->on("/zones.json", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "application/json", file_zones_json_start, file_zones_json_end - file_zones_json_start); - response->addHeader("Content-Encoding", "gzip"); - request->send(response); + server.on("/zones.json", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "application/json", "gzip", file_zones_json_start, file_zones_json_end - file_zones_json_start); }); - _server->on("/site.webmanifest", HTTP_GET, [](AsyncWebServerRequest* request) { - AsyncWebServerResponse* response = request->beginResponse_P(200, "application/json", file_site_webmanifest_start, file_site_webmanifest_end - file_site_webmanifest_start); - request->send(response); + server.on("/site.webmanifest", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "application/json", "", file_site_webmanifest_start, file_site_webmanifest_end - file_site_webmanifest_start); }); - _server->on("/js/app.js", HTTP_GET, [](AsyncWebServerRequest* request) { -#ifdef ETAG_HTTP_HEADER_VAL - // check client If-None-Match header vs ETag/AUTO_GIT_HASH - bool eTagMatch = false; - if (request->hasHeader("If-None-Match")) { - const AsyncWebHeader* h = request->getHeader("If-None-Match"); - if (strncmp(ETAG_HTTP_HEADER_VAL, h->value().c_str(), strlen(ETAG_HTTP_HEADER_VAL)) == 0) { - eTagMatch = true; - } - } - - // begin response 200 or 304 - AsyncWebServerResponse* response; - if (eTagMatch) { - response = request->beginResponse(304); - } else { - response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start); - response->addHeader("Content-Encoding", "gzip"); - } - // HTTP requires cache headers in 200 and 304 to be identical - response->addHeader("Cache-Control", "public, must-revalidate"); - response->addHeader("ETag", ETAG_HTTP_HEADER_VAL); -#else - AsyncWebServerResponse* response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start); - response->addHeader("Content-Encoding", "gzip"); -#endif - request->send(response); + server.on("/js/app.js", HTTP_GET, [&](AsyncWebServerRequest* request) { + responseBinaryDataWithETagCache(request, "text/javascript", "gzip", file_app_js_start, file_app_js_end - file_app_js_start); }); } diff --git a/src/WebApi_ws_console.cpp b/src/WebApi_ws_console.cpp index aaca6d89..1f1efcb2 100644 --- a/src/WebApi_ws_console.cpp +++ b/src/WebApi_ws_console.cpp @@ -16,8 +16,7 @@ WebApiWsConsoleClass::WebApiWsConsoleClass() void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler) { - _server = &server; - _server->addHandler(&_ws); + server.addHandler(&_ws); MessageOutput.register_ws_output(&_ws); scheduler.addTask(_wsCleanupTask); diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 867b7f8f..e51361d7 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -3,7 +3,6 @@ * Copyright (C) 2022-2024 Thomas Basler and others */ #include "WebApi_ws_live.h" -#include "Configuration.h" #include "Datastore.h" #include "MessageOutput.h" #include "Utils.h" @@ -31,10 +30,9 @@ void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler) using std::placeholders::_5; using std::placeholders::_6; - _server = &server; - _server->on("/api/livedata/status", HTTP_GET, std::bind(&WebApiWsLiveClass::onLivedataStatus, this, _1)); + server.on("/api/livedata/status", HTTP_GET, std::bind(&WebApiWsLiveClass::onLivedataStatus, this, _1)); - _server->addHandler(&_ws); + server.addHandler(&_ws); _ws.onEvent(std::bind(&WebApiWsLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6)); scheduler.addTask(_wsCleanupTask); @@ -63,43 +61,6 @@ void WebApiWsLiveClass::sendDataTaskCb() return; } - uint32_t maxTimeStamp = 0; - for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { - auto inv = Hoymiles.getInverterByPos(i); - maxTimeStamp = std::max(maxTimeStamp, inv->Statistics()->getLastUpdate()); - } - - // Update on every inverter change or at least after 10 seconds - if (millis() - _lastWsPublish > (10 * 1000) || (maxTimeStamp != _newestInverterTimestamp)) { - - try { - std::lock_guard lock(_mutex); - DynamicJsonDocument root(4200 * INV_MAX_COUNT); - if (Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { - JsonVariant var = root; - generateJsonResponse(var); - - String buffer; - serializeJson(root, buffer); - - _ws.textAll(buffer); - _newestInverterTimestamp = maxTimeStamp; - } - - } catch (const std::bad_alloc& bad_alloc) { - MessageOutput.printf("Calling /api/livedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); - } catch (const std::exception& exc) { - MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what()); - } - - _lastWsPublish = millis(); - } -} - -void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) -{ - JsonArray invArray = root.createNestedArray("inverters"); - // Loop all inverters for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); @@ -107,64 +68,43 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) continue; } - JsonObject invObject = invArray.createNestedObject(); - INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); - if (inv_cfg == nullptr) { + const uint32_t lastUpdateInternal = inv->Statistics()->getLastUpdateFromInternal(); + if (!((lastUpdateInternal > 0 && lastUpdateInternal > _lastPublishStats[i]) || (millis() - _lastPublishStats[i] > (10 * 1000)))) { continue; } - invObject["serial"] = inv->serialString(); - invObject["name"] = inv->name(); - invObject["order"] = inv_cfg->Order; - invObject["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000; - invObject["poll_enabled"] = inv->getEnablePolling(); - invObject["reachable"] = inv->isReachable(); - invObject["producing"] = inv->isProducing(); - invObject["limit_relative"] = inv->SystemConfigPara()->getLimitPercent(); - if (inv->DevInfo()->getMaxPower() > 0) { - invObject["limit_absolute"] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0; - } else { - invObject["limit_absolute"] = -1; - } + _lastPublishStats[i] = millis(); - // Loop all channels - for (auto& t : inv->Statistics()->getChannelTypes()) { - JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t)); - for (auto& c : inv->Statistics()->getChannelsByType(t)) { - if (t == TYPE_DC) { - chanTypeObj[String(static_cast(c))]["name"]["u"] = inv_cfg->channel[c].Name; - } - addField(chanTypeObj, inv, t, c, FLD_PAC); - addField(chanTypeObj, inv, t, c, FLD_UAC); - addField(chanTypeObj, inv, t, c, FLD_IAC); - if (t == TYPE_AC) { - addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC"); - } else { - addField(chanTypeObj, inv, t, c, FLD_PDC); - } - addField(chanTypeObj, inv, t, c, FLD_UDC); - addField(chanTypeObj, inv, t, c, FLD_IDC); - addField(chanTypeObj, inv, t, c, FLD_YD); - addField(chanTypeObj, inv, t, c, FLD_YT); - addField(chanTypeObj, inv, t, c, FLD_F); - addField(chanTypeObj, inv, t, c, FLD_T); - addField(chanTypeObj, inv, t, c, FLD_PF); - addField(chanTypeObj, inv, t, c, FLD_Q); - addField(chanTypeObj, inv, t, c, FLD_EFF); - if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) { - addField(chanTypeObj, inv, t, c, FLD_IRR); - chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c); - } + try { + std::lock_guard lock(_mutex); + DynamicJsonDocument root(4096); + if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { + continue; } - } + JsonVariant var = root; - if (inv->Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) { - invObject["events"] = inv->EventLog()->getEntryCount(); - } else { - invObject["events"] = -1; + auto invArray = var.createNestedArray("inverters"); + auto invObject = invArray.createNestedObject(); + + generateCommonJsonResponse(var); + generateInverterCommonJsonResponse(invObject, inv); + generateInverterChannelJsonResponse(invObject, inv); + + String buffer; + serializeJson(root, buffer); + + _ws.textAll(buffer); + + } catch (const std::bad_alloc& bad_alloc) { + MessageOutput.printf("Calling /api/livedata/status has temporarily run out of resources. Reason: \"%s\".\r\n", bad_alloc.what()); + } catch (const std::exception& exc) { + MessageOutput.printf("Unknown exception in /api/livedata/status. Reason: \"%s\".\r\n", exc.what()); } } +} +void WebApiWsLiveClass::generateCommonJsonResponse(JsonVariant& root) +{ JsonObject totalObj = root.createNestedObject("total"); addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits()); addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits()); @@ -174,11 +114,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) struct tm timeinfo; hintObj["time_sync"] = !getLocalTime(&timeinfo, 5); hintObj["radio_problem"] = (Hoymiles.getRadioNrf()->isInitialized() && (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant())) || (Hoymiles.getRadioCmt()->isInitialized() && (!Hoymiles.getRadioCmt()->isConnected())); - if (!strcmp(Configuration.get().Security.Password, ACCESS_POINT_PASSWORD)) { - hintObj["default_password"] = true; - } else { - hintObj["default_password"] = false; - } + hintObj["default_password"] = strcmp(Configuration.get().Security.Password, ACCESS_POINT_PASSWORD) == 0; JsonObject vedirectObj = root.createNestedObject("vedirect"); vedirectObj["enabled"] = Configuration.get().Vedirect.Enabled; @@ -200,7 +136,73 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) JsonObject powerMeterObj = root.createNestedObject("power_meter"); powerMeterObj["enabled"] = Configuration.get().PowerMeter.Enabled; addTotalField(powerMeterObj, "Power", PowerMeter.getPowerTotal(false), "W", 1); +} +void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr inv) +{ + const INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); + if (inv_cfg == nullptr) { + return; + } + + root["serial"] = inv->serialString(); + root["name"] = inv->name(); + root["order"] = inv_cfg->Order; + root["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000; + root["poll_enabled"] = inv->getEnablePolling(); + root["reachable"] = inv->isReachable(); + root["producing"] = inv->isProducing(); + root["limit_relative"] = inv->SystemConfigPara()->getLimitPercent(); + if (inv->DevInfo()->getMaxPower() > 0) { + root["limit_absolute"] = inv->SystemConfigPara()->getLimitPercent() * inv->DevInfo()->getMaxPower() / 100.0; + } else { + root["limit_absolute"] = -1; + } +} + +void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr inv) +{ + const INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); + if (inv_cfg == nullptr) { + return; + } + + // Loop all channels + for (auto& t : inv->Statistics()->getChannelTypes()) { + JsonObject chanTypeObj = root.createNestedObject(inv->Statistics()->getChannelTypeName(t)); + for (auto& c : inv->Statistics()->getChannelsByType(t)) { + if (t == TYPE_DC) { + chanTypeObj[String(static_cast(c))]["name"]["u"] = inv_cfg->channel[c].Name; + } + addField(chanTypeObj, inv, t, c, FLD_PAC); + addField(chanTypeObj, inv, t, c, FLD_UAC); + addField(chanTypeObj, inv, t, c, FLD_IAC); + if (t == TYPE_INV) { + addField(chanTypeObj, inv, t, c, FLD_PDC, "Power DC"); + } else { + addField(chanTypeObj, inv, t, c, FLD_PDC); + } + addField(chanTypeObj, inv, t, c, FLD_UDC); + addField(chanTypeObj, inv, t, c, FLD_IDC); + addField(chanTypeObj, inv, t, c, FLD_YD); + addField(chanTypeObj, inv, t, c, FLD_YT); + addField(chanTypeObj, inv, t, c, FLD_F); + addField(chanTypeObj, inv, t, c, FLD_T); + addField(chanTypeObj, inv, t, c, FLD_PF); + addField(chanTypeObj, inv, t, c, FLD_Q); + addField(chanTypeObj, inv, t, c, FLD_EFF); + if (t == TYPE_DC && inv->Statistics()->getStringMaxPower(c) > 0) { + addField(chanTypeObj, inv, t, c, FLD_IRR); + chanTypeObj[String(c)][inv->Statistics()->getChannelFieldName(t, c, FLD_IRR)]["max"] = inv->Statistics()->getStringMaxPower(c); + } + } + } + + if (inv->Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) { + root["events"] = inv->EventLog()->getEntryCount(); + } else { + root["events"] = -1; + } } void WebApiWsLiveClass::addField(JsonObject& root, std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic) @@ -244,10 +246,38 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request) try { std::lock_guard lock(_mutex); - AsyncJsonResponse* response = new AsyncJsonResponse(false, 4200 * INV_MAX_COUNT); + AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096); auto& root = response->getRoot(); - generateJsonResponse(root); + JsonArray invArray = root.createNestedArray("inverters"); + + uint64_t serial = 0; + if (request->hasParam("inv")) { + String s = request->getParam("inv")->value(); + serial = strtoll(s.c_str(), NULL, 16); + } + + if (serial > 0) { + auto inv = Hoymiles.getInverterBySerial(serial); + if (inv != nullptr) { + JsonObject invObject = invArray.createNestedObject(); + generateInverterCommonJsonResponse(invObject, inv); + generateInverterChannelJsonResponse(invObject, inv); + } + } else { + // Loop all inverters + for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { + auto inv = Hoymiles.getInverterByPos(i); + if (inv == nullptr) { + continue; + } + + JsonObject invObject = invArray.createNestedObject(); + generateInverterCommonJsonResponse(invObject, inv); + } + } + + generateCommonJsonResponse(root); response->setLength(); request->send(response); diff --git a/src/main.cpp b/src/main.cpp index 726a4a97..c72bde4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,9 +34,13 @@ #include #include #include +#include void setup() { + // Move all dynamic allocations >512byte to psram (if available) + heap_caps_malloc_extmem_enable(512); + // Initialize serial output Serial.begin(SERIAL_BAUDRATE); #if ARDUINO_USB_CDC_ON_BOOT diff --git a/webapp/package.json b/webapp/package.json index ac787a8e..418eae1a 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,8 +18,8 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.2", "spark-md5": "^3.0.2", - "vue": "^3.4.15", - "vue-i18n": "^9.9.0", + "vue": "^3.4.19", + "vue-i18n": "^9.9.1", "vue-router": "^4.2.5" }, "devDependencies": { @@ -27,23 +27,23 @@ "@rushstack/eslint-patch": "^1.7.2", "@tsconfig/node18": "^18.2.2", "@types/bootstrap": "^5.2.10", - "@types/node": "^20.11.7", + "@types/node": "^20.11.19", "@types/pulltorefreshjs": "^0.1.7", "@types/sortablejs": "^1.15.7", "@types/spark-md5": "^3.0.4", - "@vitejs/plugin-vue": "^5.0.3", + "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-typescript": "^12.0.0", "@vue/tsconfig": "^0.5.1", "eslint": "^8.56.0", - "eslint-plugin-vue": "^9.20.1", + "eslint-plugin-vue": "^9.21.1", "npm-run-all": "^4.1.5", "pulltorefreshjs": "^0.1.22", - "sass": "^1.70.0", - "terser": "^5.27.0", + "sass": "^1.71.0", + "terser": "^5.27.1", "typescript": "^5.3.3", - "vite": "^5.0.12", + "vite": "^5.1.3", "vite-plugin-compression": "^0.5.1", - "vite-plugin-css-injected-by-js": "^3.3.1", + "vite-plugin-css-injected-by-js": "^3.4.0", "vue-tsc": "^1.8.27" } } diff --git a/webapp/src/components/MemoryInfo.vue b/webapp/src/components/MemoryInfo.vue index 132d63ca..b9153dac 100644 --- a/webapp/src/components/MemoryInfo.vue +++ b/webapp/src/components/MemoryInfo.vue @@ -14,6 +14,8 @@ +