Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development

This commit is contained in:
helgeerbe 2024-02-19 16:03:56 +01:00
commit c72ae561c7
79 changed files with 858 additions and 602 deletions

View File

@ -23,14 +23,14 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Cache pip - name: Cache pip
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- uses: actions/setup-python@v4 - uses: actions/setup-python@v5
with: with:
python-version: "3.x" python-version: "3.x"
@ -61,7 +61,7 @@ jobs:
run: git fetch --force --tags origin run: git fetch --force --tags origin
- name: Cache pip - name: Cache pip
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -69,13 +69,13 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Cache PlatformIO - name: Cache PlatformIO
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: ~/.platformio path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: "3.x" python-version: "3.x"

View File

@ -9,7 +9,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: "3.x" python-version: "3.x"
- name: Install dependencies - name: Install dependencies

View File

@ -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
}
}
]

View File

@ -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
}
}
]

View File

@ -11,4 +11,5 @@ public:
static int getTimezoneOffset(); static int getTimezoneOffset();
static void restartDtu(); static void restartDtu();
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line); static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
static void removeAllFiles();
}; };

View File

@ -14,6 +14,4 @@ private:
void onConfigListGet(AsyncWebServerRequest* request); void onConfigListGet(AsyncWebServerRequest* request);
void onConfigUploadFinish(AsyncWebServerRequest* request); void onConfigUploadFinish(AsyncWebServerRequest* request);
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server;
}; };

View File

@ -11,6 +11,4 @@ public:
private: private:
void onDeviceAdminGet(AsyncWebServerRequest* request); void onDeviceAdminGet(AsyncWebServerRequest* request);
void onDeviceAdminPost(AsyncWebServerRequest* request); void onDeviceAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -10,6 +10,4 @@ public:
private: private:
void onDevInfoStatus(AsyncWebServerRequest* request); void onDevInfoStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -13,8 +13,6 @@ private:
void onDtuAdminGet(AsyncWebServerRequest* request); void onDtuAdminGet(AsyncWebServerRequest* request);
void onDtuAdminPost(AsyncWebServerRequest* request); void onDtuAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
Task _applyDataTask; Task _applyDataTask;
void applyDataTaskCb(); void applyDataTaskCb();
}; };

View File

@ -10,6 +10,4 @@ public:
private: private:
void onEventlogStatus(AsyncWebServerRequest* request); void onEventlogStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -11,6 +11,4 @@ public:
private: private:
void onFirmwareUpdateFinish(AsyncWebServerRequest* request); void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server;
}; };

View File

@ -11,6 +11,4 @@ public:
private: private:
void onGridProfileStatus(AsyncWebServerRequest* request); void onGridProfileStatus(AsyncWebServerRequest* request);
void onGridProfileRawdata(AsyncWebServerRequest* request); void onGridProfileRawdata(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -14,6 +14,4 @@ private:
void onInverterEdit(AsyncWebServerRequest* request); void onInverterEdit(AsyncWebServerRequest* request);
void onInverterDelete(AsyncWebServerRequest* request); void onInverterDelete(AsyncWebServerRequest* request);
void onInverterOrder(AsyncWebServerRequest* request); void onInverterOrder(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -11,6 +11,4 @@ public:
private: private:
void onLimitStatus(AsyncWebServerRequest* request); void onLimitStatus(AsyncWebServerRequest* request);
void onLimitPost(AsyncWebServerRequest* request); void onLimitPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -10,6 +10,4 @@ public:
private: private:
void onRebootPost(AsyncWebServerRequest* request); void onRebootPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -15,6 +15,4 @@ private:
void onMqttAdminGet(AsyncWebServerRequest* request); void onMqttAdminGet(AsyncWebServerRequest* request);
void onMqttAdminPost(AsyncWebServerRequest* request); void onMqttAdminPost(AsyncWebServerRequest* request);
String getTlsCertInfo(const char* cert); String getTlsCertInfo(const char* cert);
AsyncWebServer* _server;
}; };

View File

@ -12,6 +12,4 @@ private:
void onNetworkStatus(AsyncWebServerRequest* request); void onNetworkStatus(AsyncWebServerRequest* request);
void onNetworkAdminGet(AsyncWebServerRequest* request); void onNetworkAdminGet(AsyncWebServerRequest* request);
void onNetworkAdminPost(AsyncWebServerRequest* request); void onNetworkAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -14,6 +14,4 @@ private:
void onNtpAdminPost(AsyncWebServerRequest* request); void onNtpAdminPost(AsyncWebServerRequest* request);
void onNtpTimeGet(AsyncWebServerRequest* request); void onNtpTimeGet(AsyncWebServerRequest* request);
void onNtpTimePost(AsyncWebServerRequest* request); void onNtpTimePost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -11,6 +11,4 @@ public:
private: private:
void onPowerStatus(AsyncWebServerRequest* request); void onPowerStatus(AsyncWebServerRequest* request);
void onPowerPost(AsyncWebServerRequest* request); void onPowerPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -17,8 +17,6 @@ private:
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel); void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
AsyncWebServer* _server;
enum MetricType_t { enum MetricType_t {
NONE = 0, NONE = 0,
GAUGE, GAUGE,

View File

@ -13,6 +13,4 @@ private:
void onSecurityPost(AsyncWebServerRequest* request); void onSecurityPost(AsyncWebServerRequest* request);
void onAuthenticateGet(AsyncWebServerRequest* request); void onAuthenticateGet(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -10,6 +10,4 @@ public:
private: private:
void onSystemStatus(AsyncWebServerRequest* request); void onSystemStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
}; };

View File

@ -9,5 +9,5 @@ public:
void init(AsyncWebServer& server, Scheduler& scheduler); void init(AsyncWebServer& server, Scheduler& scheduler);
private: private:
AsyncWebServer* _server; void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len);
}; };

View File

@ -10,7 +10,6 @@ public:
void init(AsyncWebServer& server, Scheduler& scheduler); void init(AsyncWebServer& server, Scheduler& scheduler);
private: private:
AsyncWebServer* _server;
AsyncWebSocket _ws; AsyncWebSocket _ws;
Task _wsCleanupTask; Task _wsCleanupTask;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "Configuration.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <Hoymiles.h> #include <Hoymiles.h>
@ -12,17 +13,19 @@ public:
void init(AsyncWebServer& server, Scheduler& scheduler); void init(AsyncWebServer& server, Scheduler& scheduler);
private: private:
void generateJsonResponse(JsonVariant& root); static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = ""); static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits); static void generateCommonJsonResponse(JsonVariant& root);
static void addField(JsonObject& root, std::shared_ptr<InverterAbstract> 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 onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws; AsyncWebSocket _ws;
uint32_t _lastWsPublish = 0; uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
uint32_t _newestInverterTimestamp = 0;
std::mutex _mutex; std::mutex _mutex;

View File

@ -239,8 +239,11 @@ CountryModeId_t HoymilesRadio_CMT::getCountryMode() const
void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode) void HoymilesRadio_CMT::setCountryMode(const CountryModeId_t mode)
{ {
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
_countryMode = mode; _countryMode = mode;
if (!_isInitialized) {
return;
}
_radio->setFrequencyBand(countryDefinition.at(mode).Band);
} }
uint32_t HoymilesRadio_CMT::getInvBootFrequency() const uint32_t HoymilesRadio_CMT::getInvBootFrequency() const

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 6, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 }, { 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_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_UAC, UNIT_V, 14, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 }, { 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_T, UNIT_C, 26, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMS_1CH::HMS_1CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 10, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, { 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_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_UAC, UNIT_V, 26, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 }, { 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_T, UNIT_C, 38, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 18, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 10, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 4, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 12, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 18, 4, 1000, false, 3 }, { 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_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_UAC, UNIT_V, 26, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 }, { 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_T, UNIT_C, 38, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMS_2CH::HMS_2CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 10, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, { 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_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_UDC, UNIT_V, 4, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 8, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 12, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 }, { 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_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_UDC, UNIT_V, 26, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 30, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 34, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 46, 2, 1, false, 0 }, { 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_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_UDC, UNIT_V, 28, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 32, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 36, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_YD, UNIT_WH, 48, 2, 1, false, 0 }, { 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_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_UAC, UNIT_V, 50, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 58, 2, 100, false, 2 }, { 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_T, UNIT_C, 62, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 64, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMS_4CH::HMS_4CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 8, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 2, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 10, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 24, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 30, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 24, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 32, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, { 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_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, UNIT_V, 74, 2, 10, false, 1 }, // dummy
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 }, { 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_T, UNIT_C, 94, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMT_4CH::HMT_4CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 8, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 12, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 2, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 10, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 24, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 30, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_YT, UNIT_KWH, 34, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 24, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_IDC, UNIT_A, 28, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 32, 2, 10, false, 1 },
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 46, 2, 10, false, 1 },
{ TYPE_DC, CH4, FLD_IDC, UNIT_A, 48, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 52, 2, 10, false, 1 },
{ TYPE_DC, CH4, FLD_YT, UNIT_KWH, 56, 4, 1000, false, 3 }, { 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_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_UDC, UNIT_V, 46, 2, 10, false, 1 },
{ TYPE_DC, CH5, FLD_IDC, UNIT_A, 50, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 54, 2, 10, false, 1 },
{ TYPE_DC, CH5, FLD_YT, UNIT_KWH, 60, 4, 1000, false, 3 }, { 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_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, UNIT_V, 74, 2, 10, false, 1 }, // dummy
{ TYPE_AC, CH0, FLD_UAC_1N, UNIT_V, 68, 2, 10, false, 1 }, { 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_F, UNIT_HZ, 80, 2, 100, false, 2 },
{ TYPE_AC, CH0, FLD_PAC, UNIT_W, 82, 2, 10, false, 1 }, { 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_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_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_2, UNIT_A, 88, 2, 100, false, 2 },
{ TYPE_AC, CH0, FLD_IAC_3, UNIT_A, 90, 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_T, UNIT_C, 94, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 96, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HMT_6CH::HMT_6CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 6, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 12, 2, 1, false, 0 }, { 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_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_UAC, UNIT_V, 14, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 22, 2, 100, false, 2 }, { 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_T, UNIT_C, 26, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 28, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HM_1CH::HM_1CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 6, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 }, { 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_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_UDC, UNIT_V, 8, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_IDC, UNIT_A, 10, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 12, 2, 10, false, 1 },
{ TYPE_DC, CH1, FLD_YD, UNIT_WH, 24, 2, 1, false, 0 }, { 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_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_UAC, UNIT_V, 26, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 }, { 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_T, UNIT_C, 38, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HM_2CH::HM_2CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #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_PDC, UNIT_W, 8, 2, 10, false, 1 },
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 20, 2, 1, false, 0 }, { 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_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_IDC, UNIT_A, 6, 2, 100, false, 2 },
{ TYPE_DC, CH1, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 }, { 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_YD, UNIT_WH, 22, 2, 1, false, 0 },
{ TYPE_DC, CH1, FLD_YT, UNIT_KWH, 16, 4, 1000, false, 3 }, { 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_UDC, UNIT_V, 24, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_IDC, UNIT_A, 26, 2, 100, false, 2 }, { 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_PDC, UNIT_W, 30, 2, 10, false, 1 },
{ TYPE_DC, CH2, FLD_YD, UNIT_WH, 42, 2, 1, false, 0 }, { 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_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_IDC, UNIT_A, 28, 2, 100, false, 2 },
{ TYPE_DC, CH3, FLD_PDC, UNIT_W, 32, 2, 10, false, 1 }, { 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_YD, UNIT_WH, 44, 2, 1, false, 0 },
{ TYPE_DC, CH3, FLD_YT, UNIT_KWH, 38, 4, 1000, false, 3 }, { 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_UAC, UNIT_V, 46, 2, 10, false, 1 },
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 54, 2, 100, false, 2 }, { 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_T, UNIT_C, 58, 2, 10, true, 1 },
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 60, 2, 1, false, 0 }, { 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_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
{ TYPE_AC, CH0, FLD_YT, UNIT_KWH, CALC_YT_CH0, 0, CMD_CALC, false, 3 }, { TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
{ TYPE_AC, CH0, FLD_PDC, UNIT_W, CALC_PDC_CH0, 0, CMD_CALC, false, 1 }, { TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
{ TYPE_AC, CH0, FLD_EFF, UNIT_PCT, CALC_EFF_CH0, 0, CMD_CALC, false, 3 } { 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) HM_4CH::HM_4CH(HoymilesRadio* radio, const uint64_t serial)

View File

@ -28,9 +28,11 @@ const devInfo_t devInfo[] = {
{ { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500-4T" }, { { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500-4T" },
{ { 0x10, 0x10, 0x10, 0x15 }, static_cast<uint16_t>(300 * 0.7), "HM-300-1T" }, // HM-300 factory limitted to 70% { { 0x10, 0x10, 0x10, 0x15 }, static_cast<uint16_t>(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, 0x21, ALL }, 350, "HMS-350-1T" }, // 00
{ { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400-1T" }, // 00 { { 0x10, 0x20, 0x41, ALL }, 400, "HMS-400-1T" }, // 00
{ { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450-1T" }, // 01 { { 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, 0x10, 0x71, ALL }, 500, "HMS-500-1T" }, // 02
{ { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02 { { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02
{ { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600-2T" }, // 01 { { 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, 0x41, ALL }, 1600, "HMT-1600-4T" }, // 00
{ { 0x10, 0x32, 0x51, ALL }, 1800, "HMT-1800-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, 0x11, ALL }, 1800, "HMT-1800-6T" }, // 01
{ { 0x10, 0x33, 0x31, ALL }, 2250, "HMT-2250-6T" } // 01 { { 0x10, 0x33, 0x31, ALL }, 2250, "HMT-2250-6T" } // 01

View File

@ -5,12 +5,13 @@
#include "StatisticsParser.h" #include "StatisticsParser.h"
#include "../Hoymiles.h" #include "../Hoymiles.h"
static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0); static float calcTotalYieldTotal(StatisticsParser* iv, uint8_t arg0);
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0); static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0);
static float calcUdcCh(StatisticsParser* iv, uint8_t arg0); static float calcChUdc(StatisticsParser* iv, uint8_t arg0);
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0); static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0);
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0); static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0);
static float calcIrradiation(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); using func_t = float(StatisticsParser*, uint8_t);
@ -20,12 +21,13 @@ struct calcFunc_t {
}; };
const calcFunc_t calcFunctions[] = { const calcFunc_t calcFunctions[] = {
{ CALC_YT_CH0, &calcYieldTotalCh0 }, { CALC_TOTAL_YT, &calcTotalYieldTotal },
{ CALC_YD_CH0, &calcYieldDayCh0 }, { CALC_TOTAL_YD, &calcTotalYieldDay },
{ CALC_UDC_CH, &calcUdcCh }, { CALC_CH_UDC, &calcChUdc },
{ CALC_PDC_CH0, &calcPowerDcCh0 }, { CALC_TOTAL_PDC, &calcTotalPowerDc },
{ CALC_EFF_CH0, &calcEffiencyCh0 }, { CALC_TOTAL_EFF, &calcTotalEffiency },
{ CALC_IRR_CH, &calcIrradiation } { CALC_CH_IRR, &calcChIrradiation },
{ CALC_TOTAL_IAC, &calcTotalCurrentAc }
}; };
const FieldId_t runtimeFields[] = { 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; float yield = 0;
for (auto& channel : iv->getChannelsByType(TYPE_DC)) { for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
@ -395,7 +397,7 @@ static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0)
return yield; return yield;
} }
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0) static float calcTotalYieldDay(StatisticsParser* iv, uint8_t arg0)
{ {
float yield = 0; float yield = 0;
for (auto& channel : iv->getChannelsByType(TYPE_DC)) { for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
@ -405,12 +407,12 @@ static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0)
} }
// arg0 = channel of source // 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<ChannelNum_t>(arg0), FLD_UDC); return iv->getChannelFieldValue(TYPE_DC, static_cast<ChannelNum_t>(arg0), FLD_UDC);
} }
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0) static float calcTotalPowerDc(StatisticsParser* iv, uint8_t arg0)
{ {
float dcPower = 0; float dcPower = 0;
for (auto& channel : iv->getChannelsByType(TYPE_DC)) { for (auto& channel : iv->getChannelsByType(TYPE_DC)) {
@ -419,8 +421,7 @@ static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0)
return dcPower; return dcPower;
} }
// arg0 = channel static float calcTotalEffiency(StatisticsParser* iv, uint8_t arg0)
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0)
{ {
float acPower = 0; float acPower = 0;
for (auto& channel : iv->getChannelsByType(TYPE_AC)) { for (auto& channel : iv->getChannelsByType(TYPE_AC)) {
@ -439,7 +440,7 @@ static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0)
} }
// arg0 = channel // arg0 = channel
static float calcIrradiation(StatisticsParser* iv, uint8_t arg0) static float calcChIrradiation(StatisticsParser* iv, uint8_t arg0)
{ {
if (nullptr != iv) { if (nullptr != iv) {
if (iv->getStringMaxPower(arg0) > 0) if (iv->getStringMaxPower(arg0) > 0)
@ -447,3 +448,12 @@ static float calcIrradiation(StatisticsParser* iv, uint8_t arg0)
} }
return 0.0; 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;
}

View File

@ -55,12 +55,13 @@ const char* const fields[] = { "Voltage", "Current", "Power", "YieldDay", "Yield
// indices to calculation functions, defined in hmInverter.h // indices to calculation functions, defined in hmInverter.h
enum { enum {
CALC_YT_CH0 = 0, CALC_TOTAL_YT = 0,
CALC_YD_CH0, CALC_TOTAL_YD,
CALC_UDC_CH, CALC_CH_UDC,
CALC_PDC_CH0, CALC_TOTAL_PDC,
CALC_EFF_CH0, CALC_TOTAL_EFF,
CALC_IRR_CH CALC_CH_IRR,
CALC_TOTAL_IAC
}; };
enum { CMD_CALC = 0xffff }; enum { CMD_CALC = 0xffff };

View File

@ -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
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000
3 otadata, data, ota, 0xE000, 0x2000
4 app0, app, ota_0, 0x10000, 0x7E0000
5 app1, app, ota_1, 0x7F0000, 0x7E0000
6 spiffs, data, spiffs, 0xFD0000, 0x30000

View File

@ -36,9 +36,9 @@ build_unflags =
-std=gnu++11 -std=gnu++11
lib_deps = lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer mathieucarbou/ESP Async WebServer @ 2.7.0
bblanchon/ArduinoJson @ ^6.21.5 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 nrf24/RF24 @ ^1.4.8
olikraus/U8g2 @ ^2.35.9 olikraus/U8g2 @ ^2.35.9
buelowp/sunset @ ^1.1.7 buelowp/sunset @ ^1.1.7
@ -54,7 +54,7 @@ extra_scripts =
pre:pio-scripts/patch_apply.py pre:pio-scripts/patch_apply.py
post:pio-scripts/create_factory_bin.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.filesystem = littlefs
board_build.embed_files = board_build.embed_files =
webapp_dist/index.html.gz webapp_dist/index.html.gz
@ -80,6 +80,16 @@ board = esp32dev
build_flags = ${env.build_flags} 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] [env:generic_esp32c3]
board = esp32-c3-devkitc-02 board = esp32-c3-devkitc-02
custom_patches = ${env.custom_patches},esp32c3 custom_patches = ${env.custom_patches},esp32c3

View File

@ -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) { if (cfg->Poll_Enable) {
_totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YT);
_totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_INV, c, FLD_YD);
_totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); _totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YT));
_totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); _totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_INV, c, FLD_YD));
} }
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
_totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); _totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
_totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); _totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));

View File

@ -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_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_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_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" }; static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" };
DisplayGraphicClass::DisplayGraphicClass() DisplayGraphicClass::DisplayGraphicClass()
@ -74,16 +75,16 @@ void DisplayGraphicClass::calcLineHeights()
bool diagram = (_isLarge && _diagram_mode == DiagramMode_t::Small); bool diagram = (_isLarge && _diagram_mode == DiagramMode_t::Small);
// the diagram needs space. we need to keep // the diagram needs space. we need to keep
// away from the y-axis label in particular. // 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++) { for (uint8_t i = 0; i < 4; i++) {
setFont(i); setFont(i);
yOff += _display->getAscent(); yOff += _display->getAscent();
_lineOffsets[i] = yOff; _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 // the descent is a negative value and moves the *next* line's
// baseline. the first line never uses a letter with descent and // baseline. the first line never uses a letter with descent and
// we need that space when showing the small diagram. // 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) { if (!_isLarge) {
dispX = (line == 0) ? 5 : 0; dispX = (line == 0) ? 5 : 0;
} else { } else {
switch (line) { if (line == 0 && _diagram_mode == DiagramMode_t::Small) {
case 0:
if (_diagram_mode == DiagramMode_t::Small) {
// Center between left border and diagram // Center between left border and diagram
dispX = (CHART_POSX - _display->getStrWidth(text)) / 2; dispX = (CHART_POSX - _display->getStrWidth(text)) / 2;
} else { } else {
// Center on screen // Center on screen
dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2; dispX = (_display->getDisplayWidth() - _display->getStrWidth(text)) / 2;
} }
break;
case 3:
// 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); _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()); snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
printText(_fmtText, 1); 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); printText(_fmtText, 2);
//<======================= //<=======================

View File

@ -107,7 +107,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
const String serial = inv->serialString(); const String serial = inv->serialString();
String fieldName; String fieldName;
if (type == TYPE_AC && fieldType.fieldId == FLD_PDC) { if (type == TYPE_INV && fieldType.fieldId == FLD_PDC) {
fieldName = "PowerDC"; fieldName = "PowerDC";
} else { } else {
fieldName = inv->Statistics()->getChannelFieldName(type, channel, fieldType.fieldId); fieldName = inv->Statistics()->getChannelFieldName(type, channel, fieldType.fieldId);

View File

@ -141,7 +141,7 @@ String MqttHandleInverterClass::getTopic(std::shared_ptr<InverterAbstract> inv,
} }
String chanName; String chanName;
if (type == TYPE_AC && fieldId == FLD_PDC) { if (type == TYPE_INV && fieldId == FLD_PDC) {
chanName = "powerdc"; chanName = "powerdc";
} else { } else {
chanName = inv->Statistics()->getChannelFieldName(type, channel, fieldId); chanName = inv->Statistics()->getChannelFieldName(type, channel, fieldId);

View File

@ -128,7 +128,7 @@ void MqttSettingsClass::performConnect()
} else { } else {
static_cast<espMqttClientSecure*>(_mqttClient)->setCredentials(config.Mqtt.Username, config.Mqtt.Password); static_cast<espMqttClientSecure*>(_mqttClient)->setCredentials(config.Mqtt.Username, config.Mqtt.Password);
} }
static_cast<espMqttClientSecure*>(_mqttClient)->setWill(willTopic.c_str(), 2, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline); static_cast<espMqttClientSecure*>(_mqttClient)->setWill(willTopic.c_str(), config.Mqtt.Lwt.Qos, config.Mqtt.Retain, config.Mqtt.Lwt.Value_Offline);
static_cast<espMqttClientSecure*>(_mqttClient)->setClientId(clientId.c_str()); static_cast<espMqttClientSecure*>(_mqttClient)->setClientId(clientId.c_str());
static_cast<espMqttClientSecure*>(_mqttClient)->setCleanSession(config.Mqtt.CleanSession); static_cast<espMqttClientSecure*>(_mqttClient)->setCleanSession(config.Mqtt.CleanSession);
static_cast<espMqttClientSecure*>(_mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1)); static_cast<espMqttClientSecure*>(_mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1));

View File

@ -6,7 +6,9 @@
#include "Display_Graphic.h" #include "Display_Graphic.h"
#include "Led_Single.h" #include "Led_Single.h"
#include "MessageOutput.h" #include "MessageOutput.h"
#include "PinMapping.h"
#include <Esp.h> #include <Esp.h>
#include <LittleFS.h>
uint32_t Utils::getChipId() uint32_t Utils::getChipId()
{ {
@ -76,3 +78,17 @@ bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function,
return true; 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();
}
}

View File

@ -19,12 +19,10 @@ void WebApiConfigClass::init(AsyncWebServer& server, Scheduler& scheduler)
using std::placeholders::_5; using std::placeholders::_5;
using std::placeholders::_6; 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/get", HTTP_GET, std::bind(&WebApiConfigClass::onConfigGet, this, _1)); server.on("/api/config/list", HTTP_GET, std::bind(&WebApiConfigClass::onConfigListGet, this, _1));
_server->on("/api/config/delete", HTTP_POST, std::bind(&WebApiConfigClass::onConfigDelete, this, _1)); server.on("/api/config/upload", HTTP_POST,
_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::onConfigUploadFinish, this, _1),
std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6)); std::bind(&WebApiConfigClass::onConfigUpload, this, _1, _2, _3, _4, _5, _6));
} }
@ -110,7 +108,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
response->setLength(); response->setLength();
request->send(response); request->send(response);
LittleFS.remove(CONFIG_FILENAME); Utils::removeAllFiles();
Utils::restartDtu(); Utils::restartDtu();
} }

View File

@ -16,10 +16,8 @@ void WebApiDeviceClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)

View File

@ -12,9 +12,7 @@ void WebApiDevInfoClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)

View File

@ -18,10 +18,8 @@ void WebApiDtuClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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); scheduler.addTask(_applyDataTask);
} }

View File

@ -11,9 +11,7 @@ void WebApiEventlogClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)

View File

@ -19,9 +19,7 @@ void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler)
using std::placeholders::_5; using std::placeholders::_5;
using std::placeholders::_6; 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::onFirmwareUpdateFinish, this, _1),
std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6)); std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6));
} }

View File

@ -11,10 +11,8 @@ void WebApiGridProfileClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)

View File

@ -16,13 +16,11 @@ void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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/list", HTTP_GET, std::bind(&WebApiInverterClass::onInverterList, this, _1)); server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
_server->on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1)); server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
_server->on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1)); server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, 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) void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)

View File

@ -14,10 +14,8 @@ void WebApiLimitClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)

View File

@ -13,9 +13,7 @@ void WebApiMaintenanceClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)

View File

@ -19,11 +19,9 @@ void WebApiMqttClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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/status", HTTP_GET, std::bind(&WebApiMqttClass::onMqttStatus, this, _1)); server.on("/api/mqtt/config", HTTP_POST, std::bind(&WebApiMqttClass::onMqttAdminPost, 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) 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_cert"] = config.Mqtt.Tls.ClientCert;
root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey; root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey;
root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic; 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_offline"] = config.Mqtt.Lwt.Value_Offline;
root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos; root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos;
root["mqtt_publish_interval"] = config.Mqtt.PublishInterval; root["mqtt_publish_interval"] = config.Mqtt.PublishInterval;

View File

@ -14,11 +14,9 @@ void WebApiNetworkClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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/status", HTTP_GET, std::bind(&WebApiNetworkClass::onNetworkStatus, this, _1)); server.on("/api/network/config", HTTP_POST, std::bind(&WebApiNetworkClass::onNetworkAdminPost, 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) void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)

View File

@ -15,13 +15,11 @@ void WebApiNtpClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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/status", HTTP_GET, std::bind(&WebApiNtpClass::onNtpStatus, this, _1)); server.on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1));
_server->on("/api/ntp/config", HTTP_GET, std::bind(&WebApiNtpClass::onNtpAdminGet, this, _1)); server.on("/api/ntp/time", HTTP_GET, std::bind(&WebApiNtpClass::onNtpTimeGet, this, _1));
_server->on("/api/ntp/config", HTTP_POST, std::bind(&WebApiNtpClass::onNtpAdminPost, this, _1)); server.on("/api/ntp/time", HTTP_POST, std::bind(&WebApiNtpClass::onNtpTimePost, 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) void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)

View File

@ -12,10 +12,8 @@ void WebApiPowerClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)

View File

@ -15,9 +15,7 @@ void WebApiPrometheusClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* request)
@ -100,7 +98,7 @@ void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* reques
for (auto& c : inv->Statistics()->getChannelsByType(t)) { for (auto& c : inv->Statistics()->getChannelsByType(t)) {
addPanelInfo(stream, serial, i, inv, t, c); addPanelInfo(stream, serial, i, inv, t, c);
for (uint8_t f = 0; f < sizeof(_publishFields) / sizeof(_publishFields[0]); f++) { 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"); addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type], "PowerDC");
} else { } else {
addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type]); addField(stream, serial, i, inv, t, c, _publishFields[f].field, _metricTypes[_publishFields[f].type]);

View File

@ -13,11 +13,9 @@ void WebApiSecurityClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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/config", HTTP_GET, std::bind(&WebApiSecurityClass::onSecurityGet, this, _1)); server.on("/api/security/authenticate", HTTP_GET, std::bind(&WebApiSecurityClass::onAuthenticateGet, 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) void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)

View File

@ -24,9 +24,7 @@ void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
using std::placeholders::_1; 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) void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
@ -47,6 +45,8 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap(); root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap();
root["heap_max_block"] = ESP.getMaxAllocHeap(); root["heap_max_block"] = ESP.getMaxAllocHeap();
root["heap_min_free"] = ESP.getMinFreeHeap(); 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_total"] = ESP.getFreeSketchSpace();
root["sketch_used"] = ESP.getSketchSize(); root["sketch_used"] = ESP.getSketchSize();
root["littlefs_total"] = LittleFS.totalBytes(); root["littlefs_total"] = LittleFS.totalBytes();

View File

@ -3,6 +3,7 @@
* Copyright (C) 2022-2024 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_webapp.h" #include "WebApi_webapp.h"
#include <MD5Builder.h>
extern const uint8_t file_index_html_start[] asm("_binary_webapp_dist_index_html_gz_start"); 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"); extern const uint8_t file_favicon_ico_start[] asm("_binary_webapp_dist_favicon_ico_start");
@ -18,62 +19,22 @@ 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_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"); extern const uint8_t file_site_webmanifest_end[] asm("_binary_webapp_dist_site_webmanifest_end");
#ifdef AUTO_GIT_HASH void WebApiWebappClass::responseBinaryDataWithETagCache(AsyncWebServerRequest *request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len)
#define ETAG_HTTP_HEADER_VAL "\"" AUTO_GIT_HASH "\"" // ETag value must be between quotes
#endif
void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
_server = &server; auto md5 = MD5Builder();
md5.begin();
md5.add(const_cast<uint8_t *>(content), len);
md5.calculate();
_server->on("/", HTTP_GET, [](AsyncWebServerRequest* request) { String expectedEtag;
AsyncWebServerResponse* response = request->beginResponse_P(200, "text/html", file_index_html_start, file_index_html_end - file_index_html_start); expectedEtag = "\"";
response->addHeader("Content-Encoding", "gzip"); expectedEtag += md5.toString();
request->send(response); expectedEtag += "\"";
});
_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->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("/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.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("/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("/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("/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; bool eTagMatch = false;
if (request->hasHeader("If-None-Match")) { if (request->hasHeader("If-None-Match")) {
const AsyncWebHeader* h = request->getHeader("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 = h->value().equals(expectedEtag);
eTagMatch = true;
}
} }
// begin response 200 or 304 // begin response 200 or 304
@ -81,16 +42,55 @@ void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
if (eTagMatch) { if (eTagMatch) {
response = request->beginResponse(304); response = request->beginResponse(304);
} else { } else {
response = request->beginResponse_P(200, "text/javascript", file_app_js_start, file_app_js_end - file_app_js_start); response = request->beginResponse_P(200, contentType, content, len);
response->addHeader("Content-Encoding", "gzip"); if (contentEncoding.length() > 0) {
response->addHeader("Content-Encoding", contentEncoding);
} }
}
// HTTP requires cache headers in 200 and 304 to be identical // HTTP requires cache headers in 200 and 304 to be identical
response->addHeader("Cache-Control", "public, must-revalidate"); response->addHeader("Cache-Control", "public, must-revalidate");
response->addHeader("ETag", ETAG_HTTP_HEADER_VAL); response->addHeader("ETag", expectedEtag);
#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); request->send(response);
}
void WebApiWebappClass::init(AsyncWebServer& server, Scheduler& scheduler)
{
/*
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) {
responseBinaryDataWithETagCache(request, "text/html", "gzip", file_index_html_start, file_index_html_end - file_index_html_start);
});
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) {
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) {
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) {
responseBinaryDataWithETagCache(request, "image/png", "", file_favicon_png_start, file_favicon_png_end - file_favicon_png_start);
});
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) {
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) {
responseBinaryDataWithETagCache(request, "text/javascript", "gzip", file_app_js_start, file_app_js_end - file_app_js_start);
}); });
} }

View File

@ -16,8 +16,7 @@ WebApiWsConsoleClass::WebApiWsConsoleClass()
void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler) void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler)
{ {
_server = &server; server.addHandler(&_ws);
_server->addHandler(&_ws);
MessageOutput.register_ws_output(&_ws); MessageOutput.register_ws_output(&_ws);
scheduler.addTask(_wsCleanupTask); scheduler.addTask(_wsCleanupTask);

View File

@ -3,7 +3,6 @@
* Copyright (C) 2022-2024 Thomas Basler and others * Copyright (C) 2022-2024 Thomas Basler and others
*/ */
#include "WebApi_ws_live.h" #include "WebApi_ws_live.h"
#include "Configuration.h"
#include "Datastore.h" #include "Datastore.h"
#include "MessageOutput.h" #include "MessageOutput.h"
#include "Utils.h" #include "Utils.h"
@ -31,10 +30,9 @@ void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
using std::placeholders::_5; using std::placeholders::_5;
using std::placeholders::_6; 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)); _ws.onEvent(std::bind(&WebApiWsLiveClass::onWebsocketEvent, this, _1, _2, _3, _4, _5, _6));
scheduler.addTask(_wsCleanupTask); scheduler.addTask(_wsCleanupTask);
@ -63,43 +61,6 @@ void WebApiWsLiveClass::sendDataTaskCb()
return; return;
} }
uint32_t maxTimeStamp = 0;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
maxTimeStamp = std::max<uint32_t>(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<std::mutex> 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 // Loop all inverters
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i); auto inv = Hoymiles.getInverterByPos(i);
@ -107,64 +68,43 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
continue; continue;
} }
JsonObject invObject = invArray.createNestedObject(); const uint32_t lastUpdateInternal = inv->Statistics()->getLastUpdateFromInternal();
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); if (!((lastUpdateInternal > 0 && lastUpdateInternal > _lastPublishStats[i]) || (millis() - _lastPublishStats[i] > (10 * 1000)))) {
if (inv_cfg == nullptr) {
continue; continue;
} }
invObject["serial"] = inv->serialString(); _lastPublishStats[i] = millis();
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;
}
// Loop all channels try {
for (auto& t : inv->Statistics()->getChannelTypes()) { std::lock_guard<std::mutex> lock(_mutex);
JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t)); DynamicJsonDocument root(4096);
for (auto& c : inv->Statistics()->getChannelsByType(t)) { if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
if (t == TYPE_DC) { continue;
chanTypeObj[String(static_cast<uint8_t>(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);
}
}
} }
JsonVariant var = root;
if (inv->Statistics()->hasChannelFieldValue(TYPE_INV, CH0, FLD_EVT_LOG)) { auto invArray = var.createNestedArray("inverters");
invObject["events"] = inv->EventLog()->getEntryCount(); auto invObject = invArray.createNestedObject();
} else {
invObject["events"] = -1;
}
}
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"); JsonObject totalObj = root.createNestedObject("total");
addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits()); addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits()); addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
@ -174,11 +114,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
struct tm timeinfo; struct tm timeinfo;
hintObj["time_sync"] = !getLocalTime(&timeinfo, 5); hintObj["time_sync"] = !getLocalTime(&timeinfo, 5);
hintObj["radio_problem"] = (Hoymiles.getRadioNrf()->isInitialized() && (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant())) || (Hoymiles.getRadioCmt()->isInitialized() && (!Hoymiles.getRadioCmt()->isConnected())); 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"] = strcmp(Configuration.get().Security.Password, ACCESS_POINT_PASSWORD) == 0;
hintObj["default_password"] = true;
} else {
hintObj["default_password"] = false;
}
JsonObject vedirectObj = root.createNestedObject("vedirect"); JsonObject vedirectObj = root.createNestedObject("vedirect");
vedirectObj["enabled"] = Configuration.get().Vedirect.Enabled; vedirectObj["enabled"] = Configuration.get().Vedirect.Enabled;
@ -200,7 +136,73 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
JsonObject powerMeterObj = root.createNestedObject("power_meter"); JsonObject powerMeterObj = root.createNestedObject("power_meter");
powerMeterObj["enabled"] = Configuration.get().PowerMeter.Enabled; powerMeterObj["enabled"] = Configuration.get().PowerMeter.Enabled;
addTotalField(powerMeterObj, "Power", PowerMeter.getPowerTotal(false), "W", 1); addTotalField(powerMeterObj, "Power", PowerMeter.getPowerTotal(false), "W", 1);
}
void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> 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<InverterAbstract> 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<uint8_t>(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<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic) void WebApiWsLiveClass::addField(JsonObject& root, std::shared_ptr<InverterAbstract> 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 { try {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4200 * INV_MAX_COUNT); AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096);
auto& root = response->getRoot(); 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(); response->setLength();
request->send(response); request->send(response);

View File

@ -34,9 +34,13 @@
#include <Arduino.h> #include <Arduino.h>
#include <LittleFS.h> #include <LittleFS.h>
#include <TaskScheduler.h> #include <TaskScheduler.h>
#include <esp_heap_caps.h>
void setup() void setup()
{ {
// Move all dynamic allocations >512byte to psram (if available)
heap_caps_malloc_extmem_enable(512);
// Initialize serial output // Initialize serial output
Serial.begin(SERIAL_BAUDRATE); Serial.begin(SERIAL_BAUDRATE);
#if ARDUINO_USB_CDC_ON_BOOT #if ARDUINO_USB_CDC_ON_BOOT

View File

@ -18,8 +18,8 @@
"mitt": "^3.0.1", "mitt": "^3.0.1",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.2",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vue": "^3.4.15", "vue": "^3.4.19",
"vue-i18n": "^9.9.0", "vue-i18n": "^9.9.1",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
"devDependencies": { "devDependencies": {
@ -27,23 +27,23 @@
"@rushstack/eslint-patch": "^1.7.2", "@rushstack/eslint-patch": "^1.7.2",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/node": "^20.11.7", "@types/node": "^20.11.19",
"@types/pulltorefreshjs": "^0.1.7", "@types/pulltorefreshjs": "^0.1.7",
"@types/sortablejs": "^1.15.7", "@types/sortablejs": "^1.15.7",
"@types/spark-md5": "^3.0.4", "@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/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-vue": "^9.20.1", "eslint-plugin-vue": "^9.21.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pulltorefreshjs": "^0.1.22", "pulltorefreshjs": "^0.1.22",
"sass": "^1.70.0", "sass": "^1.71.0",
"terser": "^5.27.0", "terser": "^5.27.1",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.0.12", "vite": "^5.1.3",
"vite-plugin-compression": "^0.5.1", "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" "vue-tsc": "^1.8.27"
} }
} }

View File

@ -14,6 +14,8 @@
<tbody> <tbody>
<FsInfo :name="$t('memoryinfo.Heap')" :total="systemStatus.heap_total" <FsInfo :name="$t('memoryinfo.Heap')" :total="systemStatus.heap_total"
:used="systemStatus.heap_used" /> :used="systemStatus.heap_used" />
<FsInfo :name="$t('memoryinfo.PsRam')" :total="systemStatus.psram_total"
:used="systemStatus.psram_used" />
<FsInfo :name="$t('memoryinfo.LittleFs')" :total="systemStatus.littlefs_total" <FsInfo :name="$t('memoryinfo.LittleFs')" :total="systemStatus.littlefs_total"
:used="systemStatus.littlefs_used" /> :used="systemStatus.littlefs_used" />
<FsInfo :name="$t('memoryinfo.Sketch')" :total="systemStatus.sketch_total" <FsInfo :name="$t('memoryinfo.Sketch')" :total="systemStatus.sketch_total"

View File

@ -20,7 +20,7 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" ref="navbarCollapse" id="navbarNavAltMarkup"> <div class="collapse navbar-collapse" ref="navbarCollapse" id="navbarNavAltMarkup">
<ul class="navbar-nav me-auto"> <ul class="navbar-nav navbar-nav-scroll d-flex me-auto flex-sm-fill">
<li class="nav-item"> <li class="nav-item">
<router-link @click="onClick" class="nav-link" to="/">{{ $t('menu.LiveView') }}</router-link> <router-link @click="onClick" class="nav-link" to="/">{{ $t('menu.LiveView') }}</router-link>
</li> </li>
@ -114,8 +114,7 @@
<li class="nav-item"> <li class="nav-item">
<router-link @click="onClick" class="nav-link" to="/about">{{ $t('menu.About') }}</router-link> <router-link @click="onClick" class="nav-link" to="/about">{{ $t('menu.About') }}</router-link>
</li> </li>
</ul> <li class="flex-sm-fill"></li>
<ul class="navbar-nav flex-row flex-wrap ms-md-auto">
<ThemeSwitcher class="me-2" /> <ThemeSwitcher class="me-2" />
<form class="d-flex" role="search"> <form class="d-flex" role="search">
<LocaleSwitcher class="me-2" /> <LocaleSwitcher class="me-2" />

View File

@ -50,13 +50,13 @@
"1001": "Einstellungen gespeichert!", "1001": "Einstellungen gespeichert!",
"1002": "Keine Werte gefunden!", "1002": "Keine Werte gefunden!",
"1003": "Daten zu groß!", "1003": "Daten zu groß!",
"1004": "Fehler beim interpretieren der Daten!", "1004": "Fehler beim Interpretieren der Daten!",
"1005": "Benötigte Werte fehlen!", "1005": "Benötigte Werte fehlen!",
"1006": "Schreiben fehlgeschlagen!", "1006": "Schreiben fehlgeschlagen!",
"2001": "Die Seriennummer darf nicht 0 sein!", "2001": "Die Seriennummer darf nicht 0 sein!",
"2002": "Das Abfraginterval muss größer als 0 sein!", "2002": "Das Abfraginterval muss größer als 0 sein!",
"2003": "Ungültige Sendeleistung angegeben!", "2003": "Ungültige Sendeleistung angegeben!",
"2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein vielfaches von 250kHz betragen!", "2004": "Die Frequenz muss zwischen {min} und {max} kHz liegen und ein Vielfaches von 250kHz betragen!",
"2005": "Ungültige Landesauswahl!", "2005": "Ungültige Landesauswahl!",
"3001": "Nichts gelöscht!", "3001": "Nichts gelöscht!",
"3002": "Konfiguration zurückgesetzt. Starte jetzt neu...", "3002": "Konfiguration zurückgesetzt. Starte jetzt neu...",
@ -145,7 +145,8 @@
"Ok": "Ok", "Ok": "Ok",
"Unknown": "Unbekannt", "Unknown": "Unbekannt",
"ShowGridProfile": "Zeige Grid Profil", "ShowGridProfile": "Zeige Grid Profil",
"GridProfile": "Grid Profil" "GridProfile": "Grid Profil",
"LoadingInverter": "Warte auf Daten... (kann bis zu 10 Sekunden dauern)"
}, },
"vedirecthome": { "vedirecthome": {
"SerialNumber": "Seriennummer: ", "SerialNumber": "Seriennummer: ",
@ -214,7 +215,7 @@
"GridprofileSupportLong": "Weitere Informationen sind <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">hier</a> zu finden." "GridprofileSupportLong": "Weitere Informationen sind <a href=\"https://github.com/tbnobody/OpenDTU/wiki/Grid-Profile-Parser\" target=\"_blank\">hier</a> zu finden."
}, },
"systeminfo": { "systeminfo": {
"SystemInfo": "System Informationen", "SystemInfo": "Systeminformationen",
"VersionError": "Fehler beim Abrufen von Versionsinformationen", "VersionError": "Fehler beim Abrufen von Versionsinformationen",
"VersionNew": "Neue Version verfügbar! Zeige Änderungen!", "VersionNew": "Neue Version verfügbar! Zeige Änderungen!",
"VersionOk": "Aktuell!" "VersionOk": "Aktuell!"
@ -252,6 +253,7 @@
"Used": "Benutzt", "Used": "Benutzt",
"Size": "Größe", "Size": "Größe",
"Heap": "Heap", "Heap": "Heap",
"PsRam": "PSRAM",
"LittleFs": "LittleFs", "LittleFs": "LittleFs",
"Sketch": "Sketch" "Sketch": "Sketch"
}, },
@ -266,7 +268,7 @@
"RadioInformation": "Funkmodulinformationen", "RadioInformation": "Funkmodulinformationen",
"Status": "{module} Status", "Status": "{module} Status",
"ChipStatus": "{module} Chip-Status", "ChipStatus": "{module} Chip-Status",
"ChipType": "{module} Chip-Type", "ChipType": "{module} Chip-Typ",
"Connected": "verbunden", "Connected": "verbunden",
"NotConnected": "nicht verbunden", "NotConnected": "nicht verbunden",
"Configured": "konfiguriert", "Configured": "konfiguriert",
@ -335,18 +337,18 @@
"Server": "@:ntpinfo.Server", "Server": "@:ntpinfo.Server",
"Port": "Port", "Port": "Port",
"Username": "Benutzername", "Username": "Benutzername",
"BaseTopic": "Basis Topic", "BaseTopic": "Basis-Topic",
"PublishInterval": "Veröffentlichungsintervall", "PublishInterval": "Veröffentlichungsintervall",
"Seconds": "{sec} Sekunden", "Seconds": "{sec} Sekunden",
"CleanSession": "CleanSession Flag", "CleanSession": "CleanSession Flag",
"Retain": "Retain", "Retain": "Retain",
"Tls": "TLS", "Tls": "TLS",
"RootCertifcateInfo": "Root CA-Zertifikat-Informationen", "RootCertifcateInfo": "Root CA-Zertifikat-Informationen",
"TlsCertLogin": "Anmeldung mit TLS Zertifikat", "TlsCertLogin": "Anmeldung mit TLS-Zertifikat",
"ClientCertifcateInfo": "Client Zertifikat-Informationen", "ClientCertifcateInfo": "Client Zertifikat-Informationen",
"HassSummary": "Home Assistant MQTT-Auto-Discovery Konfigurationszusammenfassung", "HassSummary": "Home Assistant MQTT-Auto-Discovery Konfigurationszusammenfassung",
"Expire": "Ablaufen", "Expire": "Ablaufen",
"IndividualPanels": "Einzelne Paneele", "IndividualPanels": "Einzelne Panels",
"RuntimeSummary": "Laufzeitzusammenfassung", "RuntimeSummary": "Laufzeitzusammenfassung",
"ConnectionStatus": "Verbindungsstatus", "ConnectionStatus": "Verbindungsstatus",
"Connected": "verbunden", "Connected": "verbunden",
@ -367,7 +369,7 @@
"Console": "Konsole", "Console": "Konsole",
"VirtualDebugConsole": "Virtuelle Debug-Konsole", "VirtualDebugConsole": "Virtuelle Debug-Konsole",
"EnableAutoScroll": "Automatisches Scrollen aktivieren", "EnableAutoScroll": "Automatisches Scrollen aktivieren",
"ClearConsole": "Konsole löschen", "ClearConsole": "Konsole leeren",
"CopyToClipboard": "In die Zwischenablage kopieren" "CopyToClipboard": "In die Zwischenablage kopieren"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
@ -405,7 +407,7 @@
"PerformReboot": "Neustart durchführen", "PerformReboot": "Neustart durchführen",
"Reboot": "Neustarten!", "Reboot": "Neustarten!",
"Cancel": "@:base.Cancel", "Cancel": "@:base.Cancel",
"RebootOpenDTU": "OpenDTU neustarten", "RebootOpenDTU": "OpenDTU neu starten",
"RebootQuestion": "Möchten Sie das Gerät wirklich neu starten?", "RebootQuestion": "Möchten Sie das Gerät wirklich neu starten?",
"RebootHint": "<b>Hinweis:</b> Ein manueller Neustart muss normalerweise nicht durchgeführt werden. OpenDTU führt jeden erforderlichen Neustart (z. B. nach einem Firmware-Update) automatisch durch. Einstellungen werden auch ohne Neustart übernommen. Wenn Sie aufgrund eines Fehlers einen Neustart durchführen müssen, denken Sie bitte daran, diesen unter <a href=\"https://github.com/helgeerbe/OpenDTU-OnBattery/issues\" class=\"alert-link\" target=\"_blank\">Github</a> zu melden." "RebootHint": "<b>Hinweis:</b> Ein manueller Neustart muss normalerweise nicht durchgeführt werden. OpenDTU führt jeden erforderlichen Neustart (z. B. nach einem Firmware-Update) automatisch durch. Einstellungen werden auch ohne Neustart übernommen. Wenn Sie aufgrund eines Fehlers einen Neustart durchführen müssen, denken Sie bitte daran, diesen unter <a href=\"https://github.com/helgeerbe/OpenDTU-OnBattery/issues\" class=\"alert-link\" target=\"_blank\">Github</a> zu melden."
}, },
@ -427,7 +429,7 @@
"country_1": "Nordamerika ({min}MHz - {max}MHz)", "country_1": "Nordamerika ({min}MHz - {max}MHz)",
"country_2": "Brasilien ({min}MHz - {max}MHz)", "country_2": "Brasilien ({min}MHz - {max}MHz)",
"CmtFrequency": "CMT2300A Frequenz:", "CmtFrequency": "CMT2300A Frequenz:",
"CmtFrequencyHint": "Stelle sicher, dass du nur Frequenzen verwendet werden welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.", "CmtFrequencyHint": "Stelle sicher, dass nur Frequenzen verwendet werden, welche im entsprechenden Land erlaubt sind! Nach einer Frequenzänderung kann es bis zu 15min dauern bis eine Verbindung hergestellt wird.",
"CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.", "CmtFrequencyWarning": "Die gewählte Frequenz liegt außerhalb des zulässigen Bereichs in der gewählten Region/dem Land. Vergewissere dich, dass mit dieser Auswahl keine lokalen Regularien verletzt werden.",
"MHz": "{mhz} MHz", "MHz": "{mhz} MHz",
"dBm": "{dbm} dBm", "dBm": "{dbm} dBm",
@ -461,7 +463,7 @@
"NAUTICAL": "Nautische Dämmerung (102°)", "NAUTICAL": "Nautische Dämmerung (102°)",
"CIVIL": "Bürgerliche Dämmerung (96°)", "CIVIL": "Bürgerliche Dämmerung (96°)",
"ASTONOMICAL": "Astronomische Dämmerung (108°)", "ASTONOMICAL": "Astronomische Dämmerung (108°)",
"ManualTimeSynchronization": "Manuelle Zeitsynchronization", "ManualTimeSynchronization": "Manuelle Zeitsynchronisation",
"CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:", "CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:",
"CurrentLocalTime": "Aktuelle lokale Zeit:", "CurrentLocalTime": "Aktuelle lokale Zeit:",
"SynchronizeTime": "Zeit synchronisieren", "SynchronizeTime": "Zeit synchronisieren",
@ -514,7 +516,7 @@
"ClientKey": "TLS Client-Key:", "ClientKey": "TLS Client-Key:",
"LwtParameters": "LWT-Parameter", "LwtParameters": "LWT-Parameter",
"LwtTopic": "LWT-Topic:", "LwtTopic": "LWT-Topic:",
"LwtTopicHint": "LWT-Topic, wird der Basis Topic angehängt", "LwtTopicHint": "LWT-Topic, wird der Basis-Topic angehängt",
"LwtOnline": "LWT-Online-Nachricht:", "LwtOnline": "LWT-Online-Nachricht:",
"LwtOnlineHint": "Nachricht, die im LWT-Topic veröffentlicht wird, wenn OpenDTU online ist", "LwtOnlineHint": "Nachricht, die im LWT-Topic veröffentlicht wird, wenn OpenDTU online ist",
"LwtOffline": "LWT-Offline-Nachricht:", "LwtOffline": "LWT-Offline-Nachricht:",
@ -528,7 +530,7 @@
"HassPrefixTopicHint": "The prefix for the discovery topic", "HassPrefixTopicHint": "The prefix for the discovery topic",
"HassRetain": "Retain Flag aktivieren", "HassRetain": "Retain Flag aktivieren",
"HassExpire": "Ablauffunktion aktivieren", "HassExpire": "Ablauffunktion aktivieren",
"HassIndividual": "Einzelne Paneele" "HassIndividual": "Einzelne Panels"
}, },
"vedirectadmin": { "vedirectadmin": {
"VedirectSettings": "VE.Direct Einstellungen", "VedirectSettings": "VE.Direct Einstellungen",
@ -656,13 +658,13 @@
"Advanced": "Erweitert", "Advanced": "Erweitert",
"InverterSerial": "Wechselrichter Seriennummer:", "InverterSerial": "Wechselrichter Seriennummer:",
"InverterName": "Wechselrichter Name:", "InverterName": "Wechselrichter Name:",
"InverterNameHint": "Hier kann ein eigener Namen für den Wechselrichter angeben werden.", "InverterNameHint": "Hier kann ein eigener Name für den Wechselrichter angeben werden.",
"InverterStatus": "Empfangen / senden", "InverterStatus": "empfangen / senden",
"PollEnable": "Daten abrufen", "PollEnable": "Daten abrufen",
"PollEnableNight": "Daten auch nachts abrufen", "PollEnableNight": "Daten auch nachts abrufen",
"CommandEnable": "Befehle senden", "CommandEnable": "Befehle senden",
"CommandEnableNight": "Befehle auch nachts senden", "CommandEnableNight": "Befehle auch nachts senden",
"StringName": "Name String {num}:", "StringName": "Stringname {num}:",
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.", "StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
"StringMaxPower": "Max. Leistung String {num}:", "StringMaxPower": "Max. Leistung String {num}:",
"StringMaxPowerHint": "Eingabe der maximalen Leistung der angeschlossenen Solarmodule.", "StringMaxPowerHint": "Eingabe der maximalen Leistung der angeschlossenen Solarmodule.",
@ -717,12 +719,14 @@
"Back": "Zurück", "Back": "Zurück",
"Retry": "Wiederholen", "Retry": "Wiederholen",
"OtaStatus": "OTA-Status", "OtaStatus": "OTA-Status",
"OtaSuccess": "Das Hochladen der Firmware war erfolgreich. Das Gerät wurde automatisch neu gestartet. Wenn das Gerät wieder erreichbar ist wird die automatisch Oberfläche neu geladen.", "OtaSuccess": "Das Hochladen der Firmware war erfolgreich. Das Gerät wurde automatisch neu gestartet. Wenn das Gerät wieder erreichbar ist, wird die Oberfläche automatisch neu geladen.",
"FirmwareUpload": "Firmware hochladen", "FirmwareUpload": "Firmware hochladen",
"UploadProgress": "Hochlade-Fortschritt" "UploadProgress": "Hochlade-Fortschritt"
}, },
"about": { "about": {
"AboutOpendtu": "Über OpenDTU", "AboutOpendtu": "Über OpenDTU",
"Documentation": "Dokumentation",
"DocumentationBody": "Die Firmware- und Hardware-Dokumentation ist hier zu finden: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
"ProjectOrigin": "Projekt Ursprung", "ProjectOrigin": "Projekt Ursprung",
"ProjectOriginBody1": "Das Projekt wurde aus <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">dieser Diskussion (mikrocontroller.net)</a> heraus gestartet.", "ProjectOriginBody1": "Das Projekt wurde aus <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">dieser Diskussion (mikrocontroller.net)</a> heraus gestartet.",
"ProjectOriginBody2": "Das Hoymiles-Protokoll wurde durch die freiwilligen Bemühungen vieler Teilnehmer entschlüsselt. OpenDTU wurde unter anderem auf der Grundlage dieser Arbeit entwickelt. Das Projekt ist unter einer Open-Source-Lizenz lizenziert (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).", "ProjectOriginBody2": "Das Hoymiles-Protokoll wurde durch die freiwilligen Bemühungen vieler Teilnehmer entschlüsselt. OpenDTU wurde unter anderem auf der Grundlage dieser Arbeit entwickelt. Das Projekt ist unter einer Open-Source-Lizenz lizenziert (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",

View File

@ -145,7 +145,8 @@
"Ok": "Ok", "Ok": "Ok",
"Unknown": "Unknown", "Unknown": "Unknown",
"ShowGridProfile": "Show Grid Profile", "ShowGridProfile": "Show Grid Profile",
"GridProfile": "Grid Profile" "GridProfile": "Grid Profile",
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
}, },
"vedirecthome": { "vedirecthome": {
"SerialNumber": "Serial Number: ", "SerialNumber": "Serial Number: ",
@ -253,6 +254,7 @@
"Used": "Used", "Used": "Used",
"Size": "Size", "Size": "Size",
"Heap": "Heap", "Heap": "Heap",
"PsRam": "PSRAM",
"LittleFs": "LittleFs", "LittleFs": "LittleFs",
"Sketch": "Sketch" "Sketch": "Sketch"
}, },
@ -729,6 +731,8 @@
}, },
"about": { "about": {
"AboutOpendtu": "About OpenDTU", "AboutOpendtu": "About OpenDTU",
"Documentation": "Documentation",
"DocumentationBody": "The firmware and hardware documentation can be found here: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
"ProjectOrigin": "Project Origin", "ProjectOrigin": "Project Origin",
"ProjectOriginBody1": "This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a>", "ProjectOriginBody1": "This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a>",
"ProjectOriginBody2": "The Hoymiles protocol was decrypted through the voluntary efforts of many participants. OpenDTU, among others, was developed based on this work. The project is licensed under an Open Source License (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).", "ProjectOriginBody2": "The Hoymiles protocol was decrypted through the voluntary efforts of many participants. OpenDTU, among others, was developed based on this work. The project is licensed under an Open Source License (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",

View File

@ -145,7 +145,43 @@
"Ok": "OK", "Ok": "OK",
"Unknown": "Inconnu", "Unknown": "Inconnu",
"ShowGridProfile": "Show Grid Profile", "ShowGridProfile": "Show Grid Profile",
"GridProfile": "Grid Profile" "GridProfile": "Grid Profile",
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
},
"vedirecthome": {
"SerialNumber": "Serial Number: ",
"FirmwareNumber": "Firmware Number: ",
"DataAge": "Data Age: ",
"Seconds": "{val} seconds",
"DeviceInfo": "Device Info",
"Property": "Property",
"Value": "Value",
"Unit": "Unit",
"LoadOutputState": "Load output state",
"StateOfOperation": "State of operation",
"TrackerOperationMode": "Tracker operation mode",
"OffReason": "Off reason",
"ErrorCode": "Error code",
"DaySequenceNumber": "Day sequence number (0..364)",
"Battery": "Output (Battery)",
"output": {
"P": "Power (calculated)",
"V": "Voltage",
"I": "Current",
"E": "Efficiency (calculated)"
},
"Panel": "Input (Solar Panels)",
"input": {
"PPV": "Power",
"VPV": "Voltage",
"IPV": "Current (calculated)",
"YieldToday": "Yield today",
"YieldYesterday": "Yield yesterday",
"YieldTotal": "Yield total (user resettable counter)",
"MaximumPowerToday": "Maximum power today",
"MaximumPowerYesterday": "Maximum power yesterday"
},
"PowerLimiterState": "Power limiter state [off (charging), solar passthrough, on battery]"
}, },
"vedirecthome": { "vedirecthome": {
"SerialNumber": "Serial Number: ", "SerialNumber": "Serial Number: ",
@ -252,6 +288,7 @@
"Used": "Utilisée", "Used": "Utilisée",
"Size": "Taille", "Size": "Taille",
"Heap": "Heap", "Heap": "Heap",
"PsRam": "PSRAM",
"LittleFs": "LittleFs", "LittleFs": "LittleFs",
"Sketch": "Sketch" "Sketch": "Sketch"
}, },
@ -687,6 +724,8 @@
}, },
"about": { "about": {
"AboutOpendtu": "À propos d'OpenDTU", "AboutOpendtu": "À propos d'OpenDTU",
"Documentation": "Documentation",
"DocumentationBody": "The firmware and hardware documentation can be found here: <a href=\"https://www.opendtu.solar\" target=\"_blank\">https://www.opendtu.solar</a>",
"ProjectOrigin": "Origine du projet", "ProjectOrigin": "Origine du projet",
"ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">(Mikrocontroller.net)</a>.", "ProjectOriginBody1": "Ce projet a été démarré suite à cette discussion <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">(Mikrocontroller.net)</a>.",
"ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).", "ProjectOriginBody2": "Le protocole Hoymiles a été décrypté grâce aux efforts volontaires de nombreux participants. OpenDTU, entre autres, a été développé sur la base de ce travail. Le projet est sous licence Open Source (<a href=\"https://www.gnu.de/documents/gpl-2.0.de.html\" target=\"_blank\">GNU General Public License version 2</a>).",

View File

@ -26,6 +26,8 @@ export interface SystemStatus {
heap_min_free: number; heap_min_free: number;
littlefs_total: number; littlefs_total: number;
littlefs_used: number; littlefs_used: number;
psram_total: number;
psram_used: number;
sketch_total: number; sketch_total: number;
sketch_used: number; sketch_used: number;
// RadioInfo // RadioInfo

View File

@ -2,15 +2,31 @@
<BasePage :title="$t('about.AboutOpendtu')"> <BasePage :title="$t('about.AboutOpendtu')">
<div class="accordion" id="accordionExample"> <div class="accordion" id="accordionExample">
<div class="accordion-item"> <div class="accordion-item">
<h2 class="accordion-header" id="headingOne"> <h2 class="accordion-header" id="headingDocumentation">
<button class="accordion-button" type="button" data-bs-toggle="collapse" <button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> data-bs-target="#collapseDocumentation" aria-expanded="true" aria-controls="collapseOne">
<span class="badge text-bg-secondary">
<BIconInfoCircle class="fs-4" />
</span>&nbsp;{{ $t('about.Documentation') }}
</button>
</h2>
<div id="collapseDocumentation" class="accordion-collapse collapse show" aria-labelledby="headingDocumentation"
data-bs-parent="#accordionExample">
<div class="accordion-body">
<p class="fw-normal" v-html="$t('about.DocumentationBody')"></p>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
<span class="badge text-bg-secondary"> <span class="badge text-bg-secondary">
<BIconInfoCircle class="fs-4" /> <BIconInfoCircle class="fs-4" />
</span>&nbsp;{{ $t('about.ProjectOrigin') }} </span>&nbsp;{{ $t('about.ProjectOrigin') }}
</button> </button>
</h2> </h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" <div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne"
data-bs-parent="#accordionExample"> data-bs-parent="#accordionExample">
<div class="accordion-body"> <div class="accordion-body">
<p class="fw-normal" v-html="$t('about.ProjectOriginBody1')"></p> <p class="fw-normal" v-html="$t('about.ProjectOriginBody1')"></p>

View File

@ -103,6 +103,7 @@
<div class="card-body"> <div class="card-body">
<div class="row flex-row-reverse flex-wrap-reverse g-3"> <div class="row flex-row-reverse flex-wrap-reverse g-3">
<template v-for="chanType in [{obj: inverter.INV, name: 'INV'}, {obj: inverter.AC, name: 'AC'}, {obj: inverter.DC, name: 'DC'}].reverse()"> <template v-for="chanType in [{obj: inverter.INV, name: 'INV'}, {obj: inverter.AC, name: 'AC'}, {obj: inverter.DC, name: 'DC'}].reverse()">
<template v-if="chanType.obj != null">
<template v-for="channel in Object.keys(chanType.obj).sort().reverse().map(x=>+x)" :key="channel"> <template v-for="channel in Object.keys(chanType.obj).sort().reverse().map(x=>+x)" :key="channel">
<template v-if="(chanType.name != 'DC') || <template v-if="(chanType.name != 'DC') ||
(chanType.name == 'DC' && getSumIrridiation(inverter) == 0) || (chanType.name == 'DC' && getSumIrridiation(inverter) == 0) ||
@ -116,7 +117,16 @@
</template> </template>
</template> </template>
</template> </template>
</template>
</div> </div>
<BootstrapAlert class="m-3" :show="!inverter.hasOwnProperty('INV')">
<div class="d-flex justify-content-center align-items-center">
<div class="spinner-border m-1" role="status">
<span class="visually-hidden">{{ $t('home.LoadingInverter') }}</span>
</div>
<span>{{ $t('home.LoadingInverter') }}</span>
</div>
</BootstrapAlert>
</div> </div>
</div> </div>
</div> </div>
@ -450,7 +460,16 @@ export default defineComponent({
this.socket.onmessage = (event) => { this.socket.onmessage = (event) => {
console.log(event); console.log(event);
if (event.data != "{}") { if (event.data != "{}") {
this.liveData = JSON.parse(event.data); const newData = JSON.parse(event.data);
Object.assign(this.liveData.total, newData.total);
Object.assign(this.liveData.hints, newData.hints);
const foundIdx = this.liveData.inverters.findIndex((element) => element.serial == newData.inverters[0].serial);
if (foundIdx == -1) {
Object.assign(this.liveData.inverters, newData.inverters);
} else {
Object.assign(this.liveData.inverters[foundIdx], newData.inverters[0]);
}
this.dataLoading = false; this.dataLoading = false;
this.heartCheck(); // Reset heartbeat detection this.heartCheck(); // Reset heartbeat detection
} else { } else {

View File

@ -17,10 +17,10 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8"
integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==
"@babel/parser@^7.23.6": "@babel/parser@^7.23.9":
version "7.23.6" version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b"
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==
"@esbuild/android-arm64@0.19.5": "@esbuild/android-arm64@0.19.5":
version "0.19.5" version "0.19.5"
@ -211,20 +211,20 @@
source-map-js "^1.0.1" source-map-js "^1.0.1"
yaml-eslint-parser "^1.2.2" yaml-eslint-parser "^1.2.2"
"@intlify/core-base@9.9.0": "@intlify/core-base@9.9.1":
version "9.9.0" version "9.9.1"
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.9.0.tgz#edc55a5e3dbbf8dbbbf656529ed27832c4c4f522" resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.9.1.tgz#97ff0a98bf416c3f895e2a4fbcb0da353326b71a"
integrity sha512-C7UXPymDIOlMGSNjAhNLtKgzITc/8BjINK5gNKXg8GiWCTwL6n3MWr55czksxn8RM5wTMz0qcLOFT+adtaVQaA== integrity sha512-qsV15dg7jNX2faBRyKMgZS8UcFJViWEUPLdzZ9UR0kQZpFVeIpc0AG7ZOfeP7pX2T9SQ5jSiorq/tii9nkkafA==
dependencies: dependencies:
"@intlify/message-compiler" "9.9.0" "@intlify/message-compiler" "9.9.1"
"@intlify/shared" "9.9.0" "@intlify/shared" "9.9.1"
"@intlify/message-compiler@9.9.0": "@intlify/message-compiler@9.9.1":
version "9.9.0" version "9.9.1"
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.9.0.tgz#7952759329e7af0388afbce7a984820bbeff82eb" resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.9.1.tgz#4cd9c5a408be27784928e4cd57a77ea6ddb17e56"
integrity sha512-yDU/jdUm9KuhEzYfS+wuyja209yXgdl1XFhMlKtXEgSFTxz4COZQCRXXbbH8JrAjMsaJ7bdoPSLsKlY6mXG2iA== integrity sha512-zTvP6X6HeumHOXuAE1CMMsV6tTX+opKMOxO1OHTCg5N5Sm/F7d8o2jdT6W6L5oHUsJ/vvkGefHIs7Q3hfowmsA==
dependencies: dependencies:
"@intlify/shared" "9.9.0" "@intlify/shared" "9.9.1"
source-map-js "^1.0.2" source-map-js "^1.0.2"
"@intlify/message-compiler@^9.4.0": "@intlify/message-compiler@^9.4.0":
@ -240,10 +240,10 @@
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.4.0.tgz#4a78d462fc82433db900981e12eb5b1aae3d6085" resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.4.0.tgz#4a78d462fc82433db900981e12eb5b1aae3d6085"
integrity sha512-AFqymip2kToqA0B6KZPg5jSrdcVHoli9t/VhGKE2iiMq9utFuMoGdDC/JOCIZgwxo6aXAk86QyU2XtzEoMuZ6A== integrity sha512-AFqymip2kToqA0B6KZPg5jSrdcVHoli9t/VhGKE2iiMq9utFuMoGdDC/JOCIZgwxo6aXAk86QyU2XtzEoMuZ6A==
"@intlify/shared@9.9.0": "@intlify/shared@9.9.1":
version "9.9.0" version "9.9.1"
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.9.0.tgz#56907633c0f7b2d50f53269d31e88e7b24d39187" resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.9.1.tgz#b602d012b35f6c336b29a8098296dfac96a005f5"
integrity sha512-1ECUyAHRrzOJbOizyGufYP2yukqGrWXtkmTu4PcswVnWbkcjzk3YQGmJ0bLkM7JZ0ZYAaohLGdYvBYnTOGYJ9g== integrity sha512-b3Pta1nwkz5rGq434v0psHwEwHGy1pYCttfcM22IE//K9owbpkEvFptx9VcuRAxjQdrO2If249cmDDjBu5wMDA==
"@intlify/unplugin-vue-i18n@^2.0.0": "@intlify/unplugin-vue-i18n@^2.0.0":
version "2.0.0" version "2.0.0"
@ -435,10 +435,10 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
"@types/node@^20.11.7": "@types/node@^20.11.19":
version "20.11.7" version "20.11.19"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.7.tgz#cb49aedd758c978c30806d0c38b520ed2a3df6e0" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.19.tgz#b466de054e9cb5b3831bee38938de64ac7f81195"
integrity sha512-GPmeN1C3XAyV5uybAf4cMLWT9fDWcmQhZVtMFu7OR32WjrqGG+Wnk2V1d0bmtUyE/Zy1QJ9BxyiTih9z8Oks8A== integrity sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==
dependencies: dependencies:
undici-types "~5.26.4" undici-types "~5.26.4"
@ -552,10 +552,10 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@vitejs/plugin-vue@^5.0.3": "@vitejs/plugin-vue@^5.0.4":
version "5.0.3" version "5.0.4"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz#164b36653910d27c130cf6c945b4bd9bde5bcbee" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37"
integrity sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA== integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==
"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1": "@volar/language-core@1.11.1", "@volar/language-core@~1.11.1":
version "1.11.1" version "1.11.1"
@ -599,13 +599,13 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map-js "^1.0.2" source-map-js "^1.0.2"
"@vue/compiler-core@3.4.15": "@vue/compiler-core@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.15.tgz#be20d1bbe19626052500b48969302cb6f396d36e" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.19.tgz#3161b1ede69da00f3ce8155dfab907a3eaa0515e"
integrity sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw== integrity sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==
dependencies: dependencies:
"@babel/parser" "^7.23.6" "@babel/parser" "^7.23.9"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
entities "^4.5.0" entities "^4.5.0"
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map-js "^1.0.2" source-map-js "^1.0.2"
@ -618,13 +618,13 @@
"@vue/compiler-core" "3.2.47" "@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47" "@vue/shared" "3.2.47"
"@vue/compiler-dom@3.4.15": "@vue/compiler-dom@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz#753f5ed55f78d33dff04701fad4d76ff0cf81ee5" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz#2457e57e978f431e3b5fd11fc50a3e92d5816f9a"
integrity sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ== integrity sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==
dependencies: dependencies:
"@vue/compiler-core" "3.4.15" "@vue/compiler-core" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
"@vue/compiler-dom@^3.3.0": "@vue/compiler-dom@^3.3.0":
version "3.3.2" version "3.3.2"
@ -634,18 +634,18 @@
"@vue/compiler-core" "3.3.2" "@vue/compiler-core" "3.3.2"
"@vue/shared" "3.3.2" "@vue/shared" "3.3.2"
"@vue/compiler-sfc@3.4.15": "@vue/compiler-sfc@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz#4e5811e681955fcec886cebbec483f6ae463a64b" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz#33b238ded6d63e51f6a7048b742626f6007df129"
integrity sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA== integrity sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==
dependencies: dependencies:
"@babel/parser" "^7.23.6" "@babel/parser" "^7.23.9"
"@vue/compiler-core" "3.4.15" "@vue/compiler-core" "3.4.19"
"@vue/compiler-dom" "3.4.15" "@vue/compiler-dom" "3.4.19"
"@vue/compiler-ssr" "3.4.15" "@vue/compiler-ssr" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.30.5" magic-string "^0.30.6"
postcss "^8.4.33" postcss "^8.4.33"
source-map-js "^1.0.2" source-map-js "^1.0.2"
@ -673,13 +673,13 @@
"@vue/compiler-dom" "3.2.47" "@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47" "@vue/shared" "3.2.47"
"@vue/compiler-ssr@3.4.15": "@vue/compiler-ssr@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz#a910a5b89ba4f0a776e40b63d69bdae2f50616cf" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz#1f8ee06005ebbaa354f8783fad84e9f7ea4a69c2"
integrity sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw== integrity sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==
dependencies: dependencies:
"@vue/compiler-dom" "3.4.15" "@vue/compiler-dom" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
"@vue/devtools-api@^6.5.0": "@vue/devtools-api@^6.5.0":
version "6.5.0" version "6.5.0"
@ -721,37 +721,37 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.25.7" magic-string "^0.25.7"
"@vue/reactivity@3.4.15": "@vue/reactivity@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.15.tgz#ad9d9b83f5398d2e8660ad5cfc0f171e7679a9a1" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.19.tgz#8cf335d97d07881d8184cb23289289dc18b03f60"
integrity sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w== integrity sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==
dependencies: dependencies:
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
"@vue/runtime-core@3.4.15": "@vue/runtime-core@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.15.tgz#f81e2fd2108ea41a6d5c61c2462b11dfb754fdf0" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.19.tgz#ef10357fdf3afdf68523b55424541000105e2aeb"
integrity sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw== integrity sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==
dependencies: dependencies:
"@vue/reactivity" "3.4.15" "@vue/reactivity" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
"@vue/runtime-dom@3.4.15": "@vue/runtime-dom@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz#108ef86aa7334ead5d6b9c56a7d93679e1e45406" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz#079141e31d9f47515b9595f29843d51011f88739"
integrity sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw== integrity sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==
dependencies: dependencies:
"@vue/runtime-core" "3.4.15" "@vue/runtime-core" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
csstype "^3.1.3" csstype "^3.1.3"
"@vue/server-renderer@3.4.15": "@vue/server-renderer@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.15.tgz#34438f998e6f6370fac78883a75efe136631957f" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.19.tgz#e6f8ff5268d0758766ca9835375218924d5f0eb6"
integrity sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw== integrity sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==
dependencies: dependencies:
"@vue/compiler-ssr" "3.4.15" "@vue/compiler-ssr" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
"@vue/shared@3.2.47": "@vue/shared@3.2.47":
version "3.2.47" version "3.2.47"
@ -763,10 +763,10 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ== integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
"@vue/shared@3.4.15": "@vue/shared@3.4.19":
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.15.tgz#e7d2ea050c667480cb5e1a6df2ac13bcd03a8f30" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.19.tgz#28105147811bcf1e6612bf1c9ab0c6d91ada019c"
integrity sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g== integrity sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==
"@vue/tsconfig@^0.5.1": "@vue/tsconfig@^0.5.1":
version "0.5.1" version "0.5.1"
@ -1146,17 +1146,17 @@ escodegen@^2.0.0:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-plugin-vue@^9.20.1: eslint-plugin-vue@^9.21.1:
version "9.20.1" version "9.21.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.20.1.tgz#7ed78846898574b2cd26939f28b0b87798a7b528" resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.21.1.tgz#da5629efa48527cec98278dca0daa90fada4caf7"
integrity sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ== integrity sha512-XVtI7z39yOVBFJyi8Ljbn7kY9yHzznKXL02qQYn+ta63Iy4A9JFBw6o4OSB9hyD2++tVT+su9kQqetUyCCwhjw==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
natural-compare "^1.4.0" natural-compare "^1.4.0"
nth-check "^2.1.1" nth-check "^2.1.1"
postcss-selector-parser "^6.0.13" postcss-selector-parser "^6.0.13"
semver "^7.5.4" semver "^7.5.4"
vue-eslint-parser "^9.4.0" vue-eslint-parser "^9.4.2"
xml-name-validator "^4.0.0" xml-name-validator "^4.0.0"
eslint-scope@^7.1.1: eslint-scope@^7.1.1:
@ -1834,10 +1834,10 @@ magic-string@^0.30.0:
dependencies: dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13" "@jridgewell/sourcemap-codec" "^1.4.13"
magic-string@^0.30.5: magic-string@^0.30.6:
version "0.30.5" version "0.30.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505"
integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== integrity sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==
dependencies: dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15" "@jridgewell/sourcemap-codec" "^1.4.15"
@ -2128,19 +2128,19 @@ postcss@^8.1.10:
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
postcss@^8.4.32: postcss@^8.4.33:
version "8.4.32" version "8.4.33"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==
dependencies: dependencies:
nanoid "^3.3.7" nanoid "^3.3.7"
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
postcss@^8.4.33: postcss@^8.4.35:
version "8.4.33" version "8.4.35"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7"
integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==
dependencies: dependencies:
nanoid "^3.3.7" nanoid "^3.3.7"
picocolors "^1.0.0" picocolors "^1.0.0"
@ -2257,10 +2257,10 @@ safe-regex-test@^1.0.0:
get-intrinsic "^1.1.3" get-intrinsic "^1.1.3"
is-regex "^1.1.4" is-regex "^1.1.4"
sass@^1.70.0: sass@^1.71.0:
version "1.70.0" version "1.71.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.70.0.tgz#761197419d97b5358cb25f9dd38c176a8a270a75" resolved "https://registry.yarnpkg.com/sass/-/sass-1.71.0.tgz#b3085759b9b2ab503a977aecb7e91153bf941117"
integrity sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ== integrity sha512-HKKIKf49Vkxlrav3F/w6qRuPcmImGVbIXJ2I3Kg0VMA+3Bav+8yE9G5XmP5lMj6nl4OlqbPftGAscNaNu28b8w==
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0" immutable "^4.0.0"
@ -2450,10 +2450,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
terser@^5.27.0: terser@^5.27.1:
version "5.27.0" version "5.27.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.1.tgz#b0092975ea1b379d166088a1a57e32f0839d84a2"
integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== integrity sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==
dependencies: dependencies:
"@jridgewell/source-map" "^0.3.3" "@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2" acorn "^8.8.2"
@ -2565,18 +2565,18 @@ vite-plugin-compression@^0.5.1:
debug "^4.3.3" debug "^4.3.3"
fs-extra "^10.0.0" fs-extra "^10.0.0"
vite-plugin-css-injected-by-js@^3.3.1: vite-plugin-css-injected-by-js@^3.4.0:
version "3.3.1" version "3.4.0"
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.1.tgz#26b41f108c5554ee728359bdec01c68c93a48547" resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.4.0.tgz#b09a571ab50744623736a4b056ecc85d7516311a"
integrity sha512-PjM/X45DR3/V1K1fTRs8HtZHEQ55kIfdrn+dzaqNBFrOYO073SeSNCxp4j7gSYhV9NffVHaEnOL4myoko0ePAg== integrity sha512-wS5+UYtJXQ/vNornsqTQxOLBVO/UjXU54ZsYMeX0mj2OrbStMQ4GLgvneVDQGPwyGJcm/ntBPawc2lA7xx+Lpg==
vite@^5.0.12: vite@^5.1.3:
version "5.0.12" version "5.1.3"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" resolved "https://registry.yarnpkg.com/vite/-/vite-5.1.3.tgz#dd072653a80225702265550a4700561740dfde55"
integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== integrity sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==
dependencies: dependencies:
esbuild "^0.19.3" esbuild "^0.19.3"
postcss "^8.4.32" postcss "^8.4.35"
rollup "^4.2.0" rollup "^4.2.0"
optionalDependencies: optionalDependencies:
fsevents "~2.3.3" fsevents "~2.3.3"
@ -2594,10 +2594,10 @@ vue-eslint-parser@^9.3.1:
lodash "^4.17.21" lodash "^4.17.21"
semver "^7.3.6" semver "^7.3.6"
vue-eslint-parser@^9.4.0: vue-eslint-parser@^9.4.2:
version "9.4.0" version "9.4.2"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz#dfd22302e2992fe45748a76553cef7afa5bdde27" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz#02ffcce82042b082292f2d1672514615f0d95b6d"
integrity sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw== integrity sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==
dependencies: dependencies:
debug "^4.3.4" debug "^4.3.4"
eslint-scope "^7.1.1" eslint-scope "^7.1.1"
@ -2607,13 +2607,13 @@ vue-eslint-parser@^9.4.0:
lodash "^4.17.21" lodash "^4.17.21"
semver "^7.3.6" semver "^7.3.6"
vue-i18n@^9.9.0: vue-i18n@^9.9.1:
version "9.9.0" version "9.9.1"
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.9.0.tgz#20d348fa7e37fc88e4c84f69781b2f1215c7769f" resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.9.1.tgz#3c2fdf3c9db430572a1246439d541d01e2795c06"
integrity sha512-xQ5SxszUAqK5n84N+uUyHH/PiQl9xZ24FOxyAaNonmOQgXeN+rD9z/6DStOpOxNFQn4Cgcquot05gZc+CdOujA== integrity sha512-xyQ4VspLdNSPTKBFBPWa1tvtj+9HuockZwgFeD2OhxxXuC2CWeNvV4seu2o9+vbQOyQbhAM5Ez56oxUrrnTWdw==
dependencies: dependencies:
"@intlify/core-base" "9.9.0" "@intlify/core-base" "9.9.1"
"@intlify/shared" "9.9.0" "@intlify/shared" "9.9.1"
"@vue/devtools-api" "^6.5.0" "@vue/devtools-api" "^6.5.0"
vue-router@^4.2.5: vue-router@^4.2.5:
@ -2640,16 +2640,16 @@ vue-tsc@^1.8.27:
"@vue/language-core" "1.8.27" "@vue/language-core" "1.8.27"
semver "^7.5.4" semver "^7.5.4"
vue@^3.4.15: vue@^3.4.19:
version "3.4.15" version "3.4.19"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.15.tgz#91f979844ffca9239dff622ba4c79c5d5524b88c" resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.19.tgz#f9ae0a44db86628548736ff04152830726a97263"
integrity sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ== integrity sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==
dependencies: dependencies:
"@vue/compiler-dom" "3.4.15" "@vue/compiler-dom" "3.4.19"
"@vue/compiler-sfc" "3.4.15" "@vue/compiler-sfc" "3.4.19"
"@vue/runtime-dom" "3.4.15" "@vue/runtime-dom" "3.4.19"
"@vue/server-renderer" "3.4.15" "@vue/server-renderer" "3.4.19"
"@vue/shared" "3.4.15" "@vue/shared" "3.4.19"
webpack-sources@^3.2.3: webpack-sources@^3.2.3:
version "3.2.3" version "3.2.3"

Binary file not shown.