diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 112e2b22..544684f4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -5,11 +5,18 @@ body: - type: markdown attributes: value: > - ### ✋ **This is bug tracker, not a support forum** + ### ⚠️ Please remember: issues are for *bugs* + That is, something you believe affects every single user of OpenDTU, not just you. If you're not sure, start with one of the other options below. + - type: markdown + attributes: + value: | + #### Have a question? 👉 [Start a new discussion](https://github.com/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB). - If something isn't working right, you have questions or need help, [**get in touch on the Discussions**](https://github.com/tbnobody/OpenDTU/discussions). + #### Before opening an issue, please double check: - Please quickly search existing issues first before submitting a bug. + - [Documentation](https://www.opendtu.solar). + - [The FAQs](https://www.opendtu.solar/firmware/faq/). + - [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues). - type: textarea id: what-happened attributes: @@ -65,4 +72,15 @@ body: Links? References? Anything that will give us more context about the issue you are encountering! Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. validations: - required: false \ No newline at end of file + required: false + - type: checkboxes + id: required-checks + attributes: + label: Please confirm the following + options: + - label: I believe this issue is a bug that affects all users of OpenDTU, not something specific to my installation. + required: true + - label: I have already searched for relevant existing issues and discussions before opening this report. + required: true + - label: I have updated the title field above with a concise description. + required: true diff --git a/include/Configuration.h b/include/Configuration.h index c9399152..cd810d84 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -56,6 +56,7 @@ struct INVERTER_CONFIG_T { uint8_t ReachableThreshold; bool ZeroRuntimeDataIfUnrechable; bool ZeroYieldDayOnMidnight; + bool ClearEventlogOnMidnight; bool YieldDayCorrection; CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT]; }; diff --git a/include/MqttHandleInverter.h b/include/MqttHandleInverter.h index 446f30af..7c86a809 100644 --- a/include/MqttHandleInverter.h +++ b/include/MqttHandleInverter.h @@ -13,6 +13,9 @@ public: static String getTopic(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId); + void subscribeTopics(); + void unsubscribeTopics(); + private: void loop(); void publishField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId); diff --git a/lib/CpuTemperature/src/CpuTemperature.cpp b/lib/CpuTemperature/src/CpuTemperature.cpp new file mode 100644 index 00000000..60e3fc7b --- /dev/null +++ b/lib/CpuTemperature/src/CpuTemperature.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Thomas Basler and others + */ + +#include "CpuTemperature.h" +#include + +#if defined(CONFIG_IDF_TARGET_ESP32) +// there is no official API available on the original ESP32 +extern "C" { +uint8_t temprature_sens_read(); +} +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) +#include "driver/temp_sensor.h" +#endif + +CpuTemperatureClass CpuTemperature; + +float CpuTemperatureClass::read() +{ + std::lock_guard lock(_mutex); + + float temperature = NAN; + bool success = false; + +#if defined(CONFIG_IDF_TARGET_ESP32) + uint8_t raw = temprature_sens_read(); + ESP_LOGV(TAG, "Raw temperature value: %d", raw); + temperature = (raw - 32) / 1.8f; + success = (raw != 128); +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); + temp_sensor_set_config(tsens); + temp_sensor_start(); +#if defined(CONFIG_IDF_TARGET_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3)) +#error \ + "ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271" +#endif + esp_err_t result = temp_sensor_read_celsius(&temperature); + temp_sensor_stop(); + success = (result == ESP_OK); +#endif + + if (success && std::isfinite(temperature)) { + return temperature; + } else { + ESP_LOGD(TAG, "Ignoring invalid temperature (success=%d, value=%.1f)", success, temperature); + return NAN; + } +} diff --git a/lib/CpuTemperature/src/CpuTemperature.h b/lib/CpuTemperature/src/CpuTemperature.h new file mode 100644 index 00000000..06199c82 --- /dev/null +++ b/lib/CpuTemperature/src/CpuTemperature.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +class CpuTemperatureClass { +public: + float read(); + +private: + std::mutex _mutex; +}; + +extern CpuTemperatureClass CpuTemperature; diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index 384276d5..67fe497c 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -141,6 +141,9 @@ void HoymilesClass::loop() if (inv->getZeroYieldDayOnMidnight()) { inv->Statistics()->zeroDailyData(); } + if (inv->getClearEventlogOnMidnight()) { + inv->EventLog()->clearBuffer(); + } } lastWeekDay = currentWeekDay; diff --git a/lib/Hoymiles/src/HoymilesRadio.h b/lib/Hoymiles/src/HoymilesRadio.h index cb2a947c..296b479b 100644 --- a/lib/Hoymiles/src/HoymilesRadio.h +++ b/lib/Hoymiles/src/HoymilesRadio.h @@ -22,9 +22,9 @@ public: } template - std::shared_ptr prepareCommand() + std::shared_ptr prepareCommand(InverterAbstract* inv) { - return std::make_shared(); + return std::make_shared(inv); } protected: diff --git a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp index 95af23cf..b9e8eea2 100644 --- a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp +++ b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -25,8 +25,8 @@ ID Target Addr Source Addr Cmd SCmd ? Limit Type CRC16 CRC8 #define CRC_SIZE 6 -ActivePowerControlCommand::ActivePowerControlCommand(const uint64_t target_address, const uint64_t router_address) - : DevControlCommand(target_address, router_address) +ActivePowerControlCommand::ActivePowerControlCommand(InverterAbstract* inv, const uint64_t router_address) + : DevControlCommand(inv, router_address) { _payload[10] = 0x0b; _payload[11] = 0x00; @@ -62,24 +62,24 @@ void ActivePowerControlCommand::setActivePowerLimit(const float limit, const Pow udpateCRC(CRC_SIZE); } -bool ActivePowerControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool ActivePowerControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { - if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!DevControlCommand::handleResponse(fragment, max_fragment_id)) { return false; } if ((getType() == PowerLimitControlType::RelativNonPersistent) || (getType() == PowerLimitControlType::RelativPersistent)) { - inverter.SystemConfigPara()->setLimitPercent(getLimit()); + _inv->SystemConfigPara()->setLimitPercent(getLimit()); } else { - const uint16_t max_power = inverter.DevInfo()->getMaxPower(); + const uint16_t max_power = _inv->DevInfo()->getMaxPower(); if (max_power > 0) { - inverter.SystemConfigPara()->setLimitPercent(static_cast(getLimit()) / max_power * 100); + _inv->SystemConfigPara()->setLimitPercent(static_cast(getLimit()) / max_power * 100); } else { // TODO(tbnobody): Not implemented yet because we only can publish the percentage value } } - inverter.SystemConfigPara()->setLastUpdateCommand(millis()); - inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK); + _inv->SystemConfigPara()->setLastUpdateCommand(millis()); + _inv->SystemConfigPara()->setLastLimitCommandSuccess(CMD_OK); return true; } @@ -94,7 +94,7 @@ PowerLimitControlType ActivePowerControlCommand::getType() return (PowerLimitControlType)(((uint16_t)_payload[14] << 8) | _payload[15]); } -void ActivePowerControlCommand::gotTimeout(InverterAbstract& inverter) +void ActivePowerControlCommand::gotTimeout() { - inverter.SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK); -} \ No newline at end of file + _inv->SystemConfigPara()->setLastLimitCommandSuccess(CMD_NOK); +} diff --git a/lib/Hoymiles/src/commands/ActivePowerControlCommand.h b/lib/Hoymiles/src/commands/ActivePowerControlCommand.h index b7831fb8..375b278b 100644 --- a/lib/Hoymiles/src/commands/ActivePowerControlCommand.h +++ b/lib/Hoymiles/src/commands/ActivePowerControlCommand.h @@ -12,14 +12,14 @@ typedef enum { // ToDo: to be verified by field tests class ActivePowerControlCommand : public DevControlCommand { public: - explicit ActivePowerControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0); + explicit ActivePowerControlCommand(InverterAbstract* inv, const uint64_t router_address = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); - virtual void gotTimeout(InverterAbstract& inverter); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); + virtual void gotTimeout(); void setActivePowerLimit(const float limit, const PowerLimitControlType type = RelativNonPersistent); float getLimit() const; PowerLimitControlType getType(); -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/commands/AlarmDataCommand.cpp b/lib/Hoymiles/src/commands/AlarmDataCommand.cpp index 143a6cd5..98a97d0b 100644 --- a/lib/Hoymiles/src/commands/AlarmDataCommand.cpp +++ b/lib/Hoymiles/src/commands/AlarmDataCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -23,8 +23,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap AlarmId Pa #include "AlarmDataCommand.h" #include "inverters/InverterAbstract.h" -AlarmDataCommand::AlarmDataCommand(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +AlarmDataCommand::AlarmDataCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x11); @@ -36,28 +36,28 @@ String AlarmDataCommand::getCommandName() const return "AlarmData"; } -bool AlarmDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool AlarmDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } // Move all fragments into target buffer uint8_t offs = 0; - inverter.EventLog()->beginAppendFragment(); - inverter.EventLog()->clearBuffer(); + _inv->EventLog()->beginAppendFragment(); + _inv->EventLog()->clearBuffer(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len); + _inv->EventLog()->appendFragment(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.EventLog()->endAppendFragment(); - inverter.EventLog()->setLastAlarmRequestSuccess(CMD_OK); - inverter.EventLog()->setLastUpdate(millis()); + _inv->EventLog()->endAppendFragment(); + _inv->EventLog()->setLastAlarmRequestSuccess(CMD_OK); + _inv->EventLog()->setLastUpdate(millis()); return true; } -void AlarmDataCommand::gotTimeout(InverterAbstract& inverter) +void AlarmDataCommand::gotTimeout() { - inverter.EventLog()->setLastAlarmRequestSuccess(CMD_NOK); -} \ No newline at end of file + _inv->EventLog()->setLastAlarmRequestSuccess(CMD_NOK); +} diff --git a/lib/Hoymiles/src/commands/AlarmDataCommand.h b/lib/Hoymiles/src/commands/AlarmDataCommand.h index abdfc5f8..ef8404c3 100644 --- a/lib/Hoymiles/src/commands/AlarmDataCommand.h +++ b/lib/Hoymiles/src/commands/AlarmDataCommand.h @@ -5,10 +5,10 @@ class AlarmDataCommand : public MultiDataCommand { public: - explicit AlarmDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit AlarmDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); - virtual void gotTimeout(InverterAbstract& inverter); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); + virtual void gotTimeout(); +}; diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp index 30178509..ad89f2d5 100644 --- a/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.cpp @@ -19,8 +19,8 @@ ID Target Addr Source Addr ? ? ? CH ? CRC8 */ #include "ChannelChangeCommand.h" -ChannelChangeCommand::ChannelChangeCommand(const uint64_t target_address, const uint64_t router_address, const uint8_t channel) - : CommandAbstract(target_address, router_address) +ChannelChangeCommand::ChannelChangeCommand(InverterAbstract* inv, const uint64_t router_address, const uint8_t channel) + : CommandAbstract(inv, router_address) { _payload[0] = 0x56; _payload[13] = 0x14; @@ -67,7 +67,7 @@ void ChannelChangeCommand::setCountryMode(const CountryModeId_t mode) } } -bool ChannelChangeCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool ChannelChangeCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { return true; } diff --git a/lib/Hoymiles/src/commands/ChannelChangeCommand.h b/lib/Hoymiles/src/commands/ChannelChangeCommand.h index 44a4c9eb..70b5f64c 100644 --- a/lib/Hoymiles/src/commands/ChannelChangeCommand.h +++ b/lib/Hoymiles/src/commands/ChannelChangeCommand.h @@ -6,7 +6,7 @@ class ChannelChangeCommand : public CommandAbstract { public: - explicit ChannelChangeCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const uint8_t channel = 0); + explicit ChannelChangeCommand(InverterAbstract* inv, const uint64_t router_address = 0, const uint8_t channel = 0); virtual String getCommandName() const; @@ -15,7 +15,7 @@ public: void setCountryMode(const CountryModeId_t mode); - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); virtual uint8_t getMaxResendCount(); }; diff --git a/lib/Hoymiles/src/commands/CommandAbstract.cpp b/lib/Hoymiles/src/commands/CommandAbstract.cpp index dafe2b17..16a7857e 100644 --- a/lib/Hoymiles/src/commands/CommandAbstract.cpp +++ b/lib/Hoymiles/src/commands/CommandAbstract.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -29,13 +29,16 @@ Source Address: 80 12 23 04 #include "CommandAbstract.h" #include "crc.h" #include +#include "../inverters/InverterAbstract.h" -CommandAbstract::CommandAbstract(const uint64_t target_address, const uint64_t router_address) +CommandAbstract::CommandAbstract(InverterAbstract* inv, const uint64_t router_address) { memset(_payload, 0, RF_LEN); _payload_size = 0; - setTargetAddress(target_address); + _inv = inv; + + setTargetAddress(_inv->serial()); setRouterAddress(router_address); setSendCount(0); setTimeout(0); @@ -122,7 +125,7 @@ void CommandAbstract::convertSerialToPacketId(uint8_t buffer[], const uint64_t s buffer[0] = s.b[3]; } -void CommandAbstract::gotTimeout(InverterAbstract& inverter) +void CommandAbstract::gotTimeout() { } diff --git a/lib/Hoymiles/src/commands/CommandAbstract.h b/lib/Hoymiles/src/commands/CommandAbstract.h index 677fc0d1..c93cb341 100644 --- a/lib/Hoymiles/src/commands/CommandAbstract.h +++ b/lib/Hoymiles/src/commands/CommandAbstract.h @@ -13,7 +13,7 @@ class InverterAbstract; class CommandAbstract { public: - explicit CommandAbstract(const uint64_t target_address = 0, const uint64_t router_address = 0); + explicit CommandAbstract(InverterAbstract* inv, const uint64_t router_address = 0); virtual ~CommandAbstract() {}; const uint8_t* getDataPayload(); @@ -21,7 +21,6 @@ public: uint8_t getDataSize() const; - void setTargetAddress(const uint64_t address); uint64_t getTargetAddress() const; void setRouterAddress(const uint64_t address); @@ -38,8 +37,8 @@ public: virtual CommandAbstract* getRequestFrameCommand(const uint8_t frame_no); - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) = 0; - virtual void gotTimeout(InverterAbstract& inverter); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) = 0; + virtual void gotTimeout(); // Sets the amount how often the specific command is resent if all fragments where missing virtual uint8_t getMaxResendCount() const; @@ -56,6 +55,9 @@ protected: uint64_t _targetAddress; uint64_t _routerAddress; + InverterAbstract* _inv; + private: + void setTargetAddress(const uint64_t address); static void convertSerialToPacketId(uint8_t buffer[], const uint64_t serial); -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/commands/DevControlCommand.cpp b/lib/Hoymiles/src/commands/DevControlCommand.cpp index a5e7d2b6..b73f74f0 100644 --- a/lib/Hoymiles/src/commands/DevControlCommand.cpp +++ b/lib/Hoymiles/src/commands/DevControlCommand.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -23,8 +23,8 @@ ID Target Addr Source Addr Cmd Payload CRC16 CRC8 #include "DevControlCommand.h" #include "crc.h" -DevControlCommand::DevControlCommand(const uint64_t target_address, const uint64_t router_address) - : CommandAbstract(target_address, router_address) +DevControlCommand::DevControlCommand(InverterAbstract* inv, const uint64_t router_address) + : CommandAbstract(inv, router_address) { _payload[0] = 0x51; _payload[9] = 0x81; @@ -39,7 +39,7 @@ void DevControlCommand::udpateCRC(const uint8_t len) _payload[10 + len + 1] = (uint8_t)(crc); } -bool DevControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool DevControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { for (uint8_t i = 0; i < max_fragment_id; i++) { if (fragment[i].mainCmd != (_payload[0] | 0x80)) { @@ -48,4 +48,4 @@ bool DevControlCommand::handleResponse(InverterAbstract& inverter, const fragmen } return true; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/DevControlCommand.h b/lib/Hoymiles/src/commands/DevControlCommand.h index c24bc60b..7e7637ed 100644 --- a/lib/Hoymiles/src/commands/DevControlCommand.h +++ b/lib/Hoymiles/src/commands/DevControlCommand.h @@ -5,10 +5,10 @@ class DevControlCommand : public CommandAbstract { public: - explicit DevControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0); + explicit DevControlCommand(InverterAbstract* inv, const uint64_t router_address = 0); - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); protected: void udpateCRC(const uint8_t len); -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/commands/DevInfoAllCommand.cpp b/lib/Hoymiles/src/commands/DevInfoAllCommand.cpp index c7bd8027..8a258ac2 100644 --- a/lib/Hoymiles/src/commands/DevInfoAllCommand.cpp +++ b/lib/Hoymiles/src/commands/DevInfoAllCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -21,8 +21,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "DevInfoAllCommand.h" #include "inverters/InverterAbstract.h" -DevInfoAllCommand::DevInfoAllCommand(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +DevInfoAllCommand::DevInfoAllCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x01); @@ -34,22 +34,22 @@ String DevInfoAllCommand::getCommandName() const return "DevInfoAll"; } -bool DevInfoAllCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool DevInfoAllCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } // Move all fragments into target buffer uint8_t offs = 0; - inverter.DevInfo()->beginAppendFragment(); - inverter.DevInfo()->clearBufferAll(); + _inv->DevInfo()->beginAppendFragment(); + _inv->DevInfo()->clearBufferAll(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.DevInfo()->appendFragmentAll(offs, fragment[i].fragment, fragment[i].len); + _inv->DevInfo()->appendFragmentAll(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.DevInfo()->endAppendFragment(); - inverter.DevInfo()->setLastUpdateAll(millis()); + _inv->DevInfo()->endAppendFragment(); + _inv->DevInfo()->setLastUpdateAll(millis()); return true; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/DevInfoAllCommand.h b/lib/Hoymiles/src/commands/DevInfoAllCommand.h index 3facffa7..8ddfd834 100644 --- a/lib/Hoymiles/src/commands/DevInfoAllCommand.h +++ b/lib/Hoymiles/src/commands/DevInfoAllCommand.h @@ -5,9 +5,9 @@ class DevInfoAllCommand : public MultiDataCommand { public: - explicit DevInfoAllCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit DevInfoAllCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); +}; diff --git a/lib/Hoymiles/src/commands/DevInfoSimpleCommand.cpp b/lib/Hoymiles/src/commands/DevInfoSimpleCommand.cpp index 2afaae4b..d134a0ac 100644 --- a/lib/Hoymiles/src/commands/DevInfoSimpleCommand.cpp +++ b/lib/Hoymiles/src/commands/DevInfoSimpleCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -21,8 +21,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "DevInfoSimpleCommand.h" #include "inverters/InverterAbstract.h" -DevInfoSimpleCommand::DevInfoSimpleCommand(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +DevInfoSimpleCommand::DevInfoSimpleCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x00); @@ -34,22 +34,22 @@ String DevInfoSimpleCommand::getCommandName() const return "DevInfoSimple"; } -bool DevInfoSimpleCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool DevInfoSimpleCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } // Move all fragments into target buffer uint8_t offs = 0; - inverter.DevInfo()->beginAppendFragment(); - inverter.DevInfo()->clearBufferSimple(); + _inv->DevInfo()->beginAppendFragment(); + _inv->DevInfo()->clearBufferSimple(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len); + _inv->DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.DevInfo()->endAppendFragment(); - inverter.DevInfo()->setLastUpdateSimple(millis()); + _inv->DevInfo()->endAppendFragment(); + _inv->DevInfo()->setLastUpdateSimple(millis()); return true; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/DevInfoSimpleCommand.h b/lib/Hoymiles/src/commands/DevInfoSimpleCommand.h index 66a7301a..927f1eab 100644 --- a/lib/Hoymiles/src/commands/DevInfoSimpleCommand.h +++ b/lib/Hoymiles/src/commands/DevInfoSimpleCommand.h @@ -5,9 +5,9 @@ class DevInfoSimpleCommand : public MultiDataCommand { public: - explicit DevInfoSimpleCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit DevInfoSimpleCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); +}; diff --git a/lib/Hoymiles/src/commands/GridOnProFilePara.cpp b/lib/Hoymiles/src/commands/GridOnProFilePara.cpp index c98c7e5a..77930377 100644 --- a/lib/Hoymiles/src/commands/GridOnProFilePara.cpp +++ b/lib/Hoymiles/src/commands/GridOnProFilePara.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "Hoymiles.h" #include "inverters/InverterAbstract.h" -GridOnProFilePara::GridOnProFilePara(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +GridOnProFilePara::GridOnProFilePara(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x02); @@ -35,22 +35,22 @@ String GridOnProFilePara::getCommandName() const return "GridOnProFilePara"; } -bool GridOnProFilePara::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool GridOnProFilePara::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } // Move all fragments into target buffer uint8_t offs = 0; - inverter.GridProfile()->beginAppendFragment(); - inverter.GridProfile()->clearBuffer(); + _inv->GridProfile()->beginAppendFragment(); + _inv->GridProfile()->clearBuffer(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.GridProfile()->appendFragment(offs, fragment[i].fragment, fragment[i].len); + _inv->GridProfile()->appendFragment(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.GridProfile()->endAppendFragment(); - inverter.GridProfile()->setLastUpdate(millis()); + _inv->GridProfile()->endAppendFragment(); + _inv->GridProfile()->setLastUpdate(millis()); return true; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/GridOnProFilePara.h b/lib/Hoymiles/src/commands/GridOnProFilePara.h index 382ebcbb..b2380c75 100644 --- a/lib/Hoymiles/src/commands/GridOnProFilePara.h +++ b/lib/Hoymiles/src/commands/GridOnProFilePara.h @@ -5,9 +5,9 @@ class GridOnProFilePara : public MultiDataCommand { public: - explicit GridOnProFilePara(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit GridOnProFilePara(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); +}; diff --git a/lib/Hoymiles/src/commands/MultiDataCommand.cpp b/lib/Hoymiles/src/commands/MultiDataCommand.cpp index bbd32091..0e7bf51f 100644 --- a/lib/Hoymiles/src/commands/MultiDataCommand.cpp +++ b/lib/Hoymiles/src/commands/MultiDataCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -28,8 +28,9 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "MultiDataCommand.h" #include "crc.h" -MultiDataCommand::MultiDataCommand(const uint64_t target_address, const uint64_t router_address, const uint8_t data_type, const time_t time) - : CommandAbstract(target_address, router_address) +MultiDataCommand::MultiDataCommand(InverterAbstract* inv, const uint64_t router_address, const uint8_t data_type, const time_t time) + : CommandAbstract(inv, router_address) + , _cmdRequestFrame(inv) { _payload[0] = 0x15; _payload[9] = 0x80; @@ -79,13 +80,12 @@ time_t MultiDataCommand::getTime() const CommandAbstract* MultiDataCommand::getRequestFrameCommand(const uint8_t frame_no) { - _cmdRequestFrame.setTargetAddress(getTargetAddress()); _cmdRequestFrame.setFrameNo(frame_no); return &_cmdRequestFrame; } -bool MultiDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool MultiDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // All fragments are available --> Check CRC uint16_t crc = 0xffff, crcRcv = 0; diff --git a/lib/Hoymiles/src/commands/MultiDataCommand.h b/lib/Hoymiles/src/commands/MultiDataCommand.h index 82107474..5693287f 100644 --- a/lib/Hoymiles/src/commands/MultiDataCommand.h +++ b/lib/Hoymiles/src/commands/MultiDataCommand.h @@ -7,14 +7,14 @@ class MultiDataCommand : public CommandAbstract { public: - explicit MultiDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const uint8_t data_type = 0, const time_t time = 0); + explicit MultiDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const uint8_t data_type = 0, const time_t time = 0); void setTime(const time_t time); time_t getTime() const; CommandAbstract* getRequestFrameCommand(const uint8_t frame_no); - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); protected: void setDataType(const uint8_t data_type); @@ -23,4 +23,4 @@ protected: static uint8_t getTotalFragmentSize(const fragment_t fragment[], const uint8_t max_fragment_id); RequestFrameCommand _cmdRequestFrame; -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/commands/ParaSetCommand.cpp b/lib/Hoymiles/src/commands/ParaSetCommand.cpp index a3374945..8b71867b 100644 --- a/lib/Hoymiles/src/commands/ParaSetCommand.cpp +++ b/lib/Hoymiles/src/commands/ParaSetCommand.cpp @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "ParaSetCommand.h" -ParaSetCommand::ParaSetCommand(const uint64_t target_address, const uint64_t router_address) - : CommandAbstract(target_address, router_address) +ParaSetCommand::ParaSetCommand(InverterAbstract* inv, const uint64_t router_address) + : CommandAbstract(inv, router_address) { _payload[0] = 0x52; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/ParaSetCommand.h b/lib/Hoymiles/src/commands/ParaSetCommand.h index 424d0e37..224aba39 100644 --- a/lib/Hoymiles/src/commands/ParaSetCommand.h +++ b/lib/Hoymiles/src/commands/ParaSetCommand.h @@ -5,5 +5,5 @@ class ParaSetCommand : public CommandAbstract { public: - explicit ParaSetCommand(const uint64_t target_address = 0, const uint64_t router_address = 0); -}; \ No newline at end of file + explicit ParaSetCommand(InverterAbstract* inv, const uint64_t router_address = 0); +}; diff --git a/lib/Hoymiles/src/commands/PowerControlCommand.cpp b/lib/Hoymiles/src/commands/PowerControlCommand.cpp index fbf12db8..927c3330 100644 --- a/lib/Hoymiles/src/commands/PowerControlCommand.cpp +++ b/lib/Hoymiles/src/commands/PowerControlCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -26,8 +26,8 @@ ID Target Addr Source Addr Cmd SCmd ? CRC16 CRC8 #define CRC_SIZE 2 -PowerControlCommand::PowerControlCommand(const uint64_t target_address, const uint64_t router_address) - : DevControlCommand(target_address, router_address) +PowerControlCommand::PowerControlCommand(InverterAbstract* inv, const uint64_t router_address) + : DevControlCommand(inv, router_address) { _payload[10] = 0x00; // TurnOn _payload[11] = 0x00; @@ -44,20 +44,20 @@ String PowerControlCommand::getCommandName() const return "PowerControl"; } -bool PowerControlCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool PowerControlCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { - if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!DevControlCommand::handleResponse(fragment, max_fragment_id)) { return false; } - inverter.PowerCommand()->setLastUpdateCommand(millis()); - inverter.PowerCommand()->setLastPowerCommandSuccess(CMD_OK); + _inv->PowerCommand()->setLastUpdateCommand(millis()); + _inv->PowerCommand()->setLastPowerCommandSuccess(CMD_OK); return true; } -void PowerControlCommand::gotTimeout(InverterAbstract& inverter) +void PowerControlCommand::gotTimeout() { - inverter.PowerCommand()->setLastPowerCommandSuccess(CMD_NOK); + _inv->PowerCommand()->setLastPowerCommandSuccess(CMD_NOK); } void PowerControlCommand::setPowerOn(const bool state) @@ -76,4 +76,4 @@ void PowerControlCommand::setRestart() _payload[10] = 0x02; // Restart udpateCRC(CRC_SIZE); // 2 byte crc -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/PowerControlCommand.h b/lib/Hoymiles/src/commands/PowerControlCommand.h index 8b9f11ac..d40c356d 100644 --- a/lib/Hoymiles/src/commands/PowerControlCommand.h +++ b/lib/Hoymiles/src/commands/PowerControlCommand.h @@ -5,13 +5,13 @@ class PowerControlCommand : public DevControlCommand { public: - explicit PowerControlCommand(const uint64_t target_address = 0, const uint64_t router_address = 0); + explicit PowerControlCommand(InverterAbstract* inv, const uint64_t router_address = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); - virtual void gotTimeout(InverterAbstract& inverter); + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); + virtual void gotTimeout(); void setPowerOn(const bool state); void setRestart(); -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/commands/RealTimeRunDataCommand.cpp b/lib/Hoymiles/src/commands/RealTimeRunDataCommand.cpp index 5f04c948..b1396a4d 100644 --- a/lib/Hoymiles/src/commands/RealTimeRunDataCommand.cpp +++ b/lib/Hoymiles/src/commands/RealTimeRunDataCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "Hoymiles.h" #include "inverters/InverterAbstract.h" -RealTimeRunDataCommand::RealTimeRunDataCommand(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +RealTimeRunDataCommand::RealTimeRunDataCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x0b); @@ -35,10 +35,10 @@ String RealTimeRunDataCommand::getCommandName() const return "RealTimeRunData"; } -bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool RealTimeRunDataCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } @@ -46,7 +46,7 @@ bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fr // In case of low power in the inverter it occours that some incomplete fragments // with a valid CRC are received. const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id); - const uint8_t expectedSize = inverter.Statistics()->getExpectedByteCount(); + const uint8_t expectedSize = _inv->Statistics()->getExpectedByteCount(); if (fragmentsSize < expectedSize) { Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %d, min expected size: %d\r\n", getCommandName().c_str(), fragmentsSize, expectedSize); @@ -56,19 +56,19 @@ bool RealTimeRunDataCommand::handleResponse(InverterAbstract& inverter, const fr // Move all fragments into target buffer uint8_t offs = 0; - inverter.Statistics()->beginAppendFragment(); - inverter.Statistics()->clearBuffer(); + _inv->Statistics()->beginAppendFragment(); + _inv->Statistics()->clearBuffer(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.Statistics()->appendFragment(offs, fragment[i].fragment, fragment[i].len); + _inv->Statistics()->appendFragment(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.Statistics()->endAppendFragment(); - inverter.Statistics()->resetRxFailureCount(); - inverter.Statistics()->setLastUpdate(millis()); + _inv->Statistics()->endAppendFragment(); + _inv->Statistics()->resetRxFailureCount(); + _inv->Statistics()->setLastUpdate(millis()); return true; } -void RealTimeRunDataCommand::gotTimeout(InverterAbstract& inverter) +void RealTimeRunDataCommand::gotTimeout() { - inverter.Statistics()->incrementRxFailureCount(); -} \ No newline at end of file + _inv->Statistics()->incrementRxFailureCount(); +} diff --git a/lib/Hoymiles/src/commands/RealTimeRunDataCommand.h b/lib/Hoymiles/src/commands/RealTimeRunDataCommand.h index 7a0eeec1..9341247f 100644 --- a/lib/Hoymiles/src/commands/RealTimeRunDataCommand.h +++ b/lib/Hoymiles/src/commands/RealTimeRunDataCommand.h @@ -5,10 +5,10 @@ class RealTimeRunDataCommand : public MultiDataCommand { public: - explicit RealTimeRunDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit RealTimeRunDataCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); - virtual void gotTimeout(InverterAbstract& inverter); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); + virtual void gotTimeout(); +}; diff --git a/lib/Hoymiles/src/commands/RequestFrameCommand.cpp b/lib/Hoymiles/src/commands/RequestFrameCommand.cpp index 68c4977f..0abb5235 100644 --- a/lib/Hoymiles/src/commands/RequestFrameCommand.cpp +++ b/lib/Hoymiles/src/commands/RequestFrameCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -22,8 +22,8 @@ ID Target Addr Source Addr Frm CRC8 */ #include "RequestFrameCommand.h" -RequestFrameCommand::RequestFrameCommand(const uint64_t target_address, const uint64_t router_address, uint8_t frame_no) - : SingleDataCommand(target_address, router_address) +RequestFrameCommand::RequestFrameCommand(InverterAbstract* inv, const uint64_t router_address, uint8_t frame_no) + : SingleDataCommand(inv, router_address) { if (frame_no > 127) { frame_no = 0; @@ -47,7 +47,7 @@ uint8_t RequestFrameCommand::getFrameNo() const return _payload[9] & (~0x80); } -bool RequestFrameCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool RequestFrameCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { return true; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/commands/RequestFrameCommand.h b/lib/Hoymiles/src/commands/RequestFrameCommand.h index 92663b70..2924e69b 100644 --- a/lib/Hoymiles/src/commands/RequestFrameCommand.h +++ b/lib/Hoymiles/src/commands/RequestFrameCommand.h @@ -5,12 +5,12 @@ class RequestFrameCommand : public SingleDataCommand { public: - explicit RequestFrameCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, uint8_t frame_no = 0); + explicit RequestFrameCommand(InverterAbstract* inv, const uint64_t router_address = 0, uint8_t frame_no = 0); virtual String getCommandName() const; void setFrameNo(const uint8_t frame_no); uint8_t getFrameNo() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); +}; diff --git a/lib/Hoymiles/src/commands/SingleDataCommand.cpp b/lib/Hoymiles/src/commands/SingleDataCommand.cpp index 4f775146..3b648814 100644 --- a/lib/Hoymiles/src/commands/SingleDataCommand.cpp +++ b/lib/Hoymiles/src/commands/SingleDataCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -19,8 +19,8 @@ ID Target Addr Source Addr CRC8 */ #include "SingleDataCommand.h" -SingleDataCommand::SingleDataCommand(const uint64_t target_address, const uint64_t router_address) - : CommandAbstract(target_address, router_address) +SingleDataCommand::SingleDataCommand(InverterAbstract* inv, const uint64_t router_address) + : CommandAbstract(inv, router_address) { _payload[0] = 0x15; setTimeout(100); diff --git a/lib/Hoymiles/src/commands/SingleDataCommand.h b/lib/Hoymiles/src/commands/SingleDataCommand.h index d0515169..39f3c480 100644 --- a/lib/Hoymiles/src/commands/SingleDataCommand.h +++ b/lib/Hoymiles/src/commands/SingleDataCommand.h @@ -5,5 +5,5 @@ class SingleDataCommand : public CommandAbstract { public: - explicit SingleDataCommand(const uint64_t target_address = 0, const uint64_t router_address = 0); -}; \ No newline at end of file + explicit SingleDataCommand(InverterAbstract* inv, const uint64_t router_address = 0); +}; diff --git a/lib/Hoymiles/src/commands/SystemConfigParaCommand.cpp b/lib/Hoymiles/src/commands/SystemConfigParaCommand.cpp index 0c8e7ded..0c142afc 100644 --- a/lib/Hoymiles/src/commands/SystemConfigParaCommand.cpp +++ b/lib/Hoymiles/src/commands/SystemConfigParaCommand.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ /* @@ -22,8 +22,8 @@ ID Target Addr Source Addr Idx DT ? Time Gap Pa #include "Hoymiles.h" #include "inverters/InverterAbstract.h" -SystemConfigParaCommand::SystemConfigParaCommand(const uint64_t target_address, const uint64_t router_address, const time_t time) - : MultiDataCommand(target_address, router_address) +SystemConfigParaCommand::SystemConfigParaCommand(InverterAbstract* inv, const uint64_t router_address, const time_t time) + : MultiDataCommand(inv, router_address) { setTime(time); setDataType(0x05); @@ -35,10 +35,10 @@ String SystemConfigParaCommand::getCommandName() const return "SystemConfigPara"; } -bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id) +bool SystemConfigParaCommand::handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id) { // Check CRC of whole payload - if (!MultiDataCommand::handleResponse(inverter, fragment, max_fragment_id)) { + if (!MultiDataCommand::handleResponse(fragment, max_fragment_id)) { return false; } @@ -46,7 +46,7 @@ bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const f // In case of low power in the inverter it occours that some incomplete fragments // with a valid CRC are received. const uint8_t fragmentsSize = getTotalFragmentSize(fragment, max_fragment_id); - const uint8_t expectedSize = inverter.SystemConfigPara()->getExpectedByteCount(); + const uint8_t expectedSize = _inv->SystemConfigPara()->getExpectedByteCount(); if (fragmentsSize < expectedSize) { Hoymiles.getMessageOutput()->printf("ERROR in %s: Received fragment size: %d, min expected size: %d\r\n", getCommandName().c_str(), fragmentsSize, expectedSize); @@ -56,19 +56,19 @@ bool SystemConfigParaCommand::handleResponse(InverterAbstract& inverter, const f // Move all fragments into target buffer uint8_t offs = 0; - inverter.SystemConfigPara()->beginAppendFragment(); - inverter.SystemConfigPara()->clearBuffer(); + _inv->SystemConfigPara()->beginAppendFragment(); + _inv->SystemConfigPara()->clearBuffer(); for (uint8_t i = 0; i < max_fragment_id; i++) { - inverter.SystemConfigPara()->appendFragment(offs, fragment[i].fragment, fragment[i].len); + _inv->SystemConfigPara()->appendFragment(offs, fragment[i].fragment, fragment[i].len); offs += (fragment[i].len); } - inverter.SystemConfigPara()->endAppendFragment(); - inverter.SystemConfigPara()->setLastUpdateRequest(millis()); - inverter.SystemConfigPara()->setLastLimitRequestSuccess(CMD_OK); + _inv->SystemConfigPara()->endAppendFragment(); + _inv->SystemConfigPara()->setLastUpdateRequest(millis()); + _inv->SystemConfigPara()->setLastLimitRequestSuccess(CMD_OK); return true; } -void SystemConfigParaCommand::gotTimeout(InverterAbstract& inverter) +void SystemConfigParaCommand::gotTimeout() { - inverter.SystemConfigPara()->setLastLimitRequestSuccess(CMD_NOK); -} \ No newline at end of file + _inv->SystemConfigPara()->setLastLimitRequestSuccess(CMD_NOK); +} diff --git a/lib/Hoymiles/src/commands/SystemConfigParaCommand.h b/lib/Hoymiles/src/commands/SystemConfigParaCommand.h index e2480a97..147f18da 100644 --- a/lib/Hoymiles/src/commands/SystemConfigParaCommand.h +++ b/lib/Hoymiles/src/commands/SystemConfigParaCommand.h @@ -5,10 +5,10 @@ class SystemConfigParaCommand : public MultiDataCommand { public: - explicit SystemConfigParaCommand(const uint64_t target_address = 0, const uint64_t router_address = 0, const time_t time = 0); + explicit SystemConfigParaCommand(InverterAbstract* inv, const uint64_t router_address = 0, const time_t time = 0); virtual String getCommandName() const; - virtual bool handleResponse(InverterAbstract& inverter, const fragment_t fragment[], const uint8_t max_fragment_id); - virtual void gotTimeout(InverterAbstract& inverter); -}; \ No newline at end of file + virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id); + virtual void gotTimeout(); +}; diff --git a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp index ffa7d721..4fc64b03 100644 --- a/lib/Hoymiles/src/inverters/HMS_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMS_Abstract.cpp @@ -18,10 +18,9 @@ bool HMS_Abstract::sendChangeChannelRequest() return false; } - auto cmdChannel = _radio->prepareCommand(); + auto cmdChannel = _radio->prepareCommand(this); cmdChannel->setCountryMode(Hoymiles.getRadioCmt()->getCountryMode()); cmdChannel->setChannel(Hoymiles.getRadioCmt()->getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); - cmdChannel->setTargetAddress(serial()); _radio->enqueCommand(cmdChannel); return true; diff --git a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp index 5c232b90..50c895cc 100644 --- a/lib/Hoymiles/src/inverters/HMT_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HMT_Abstract.cpp @@ -20,10 +20,9 @@ bool HMT_Abstract::sendChangeChannelRequest() return false; } - auto cmdChannel = _radio->prepareCommand(); + auto cmdChannel = _radio->prepareCommand(this); cmdChannel->setCountryMode(Hoymiles.getRadioCmt()->getCountryMode()); cmdChannel->setChannel(Hoymiles.getRadioCmt()->getChannelFromFrequency(Hoymiles.getRadioCmt()->getInverterTargetFrequency())); - cmdChannel->setTargetAddress(serial()); _radio->enqueCommand(cmdChannel); return true; diff --git a/lib/Hoymiles/src/inverters/HM_Abstract.cpp b/lib/Hoymiles/src/inverters/HM_Abstract.cpp index 38515ab8..c99d4cfc 100644 --- a/lib/Hoymiles/src/inverters/HM_Abstract.cpp +++ b/lib/Hoymiles/src/inverters/HM_Abstract.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ #include "HM_Abstract.h" #include "HoymilesRadio.h" @@ -30,9 +30,8 @@ bool HM_Abstract::sendStatsRequest() time_t now; time(&now); - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setTime(now); - cmd->setTargetAddress(serial()); _radio->enqueCommand(cmd); return true; @@ -62,9 +61,8 @@ bool HM_Abstract::sendAlarmLogRequest(const bool force) time_t now; time(&now); - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setTime(now); - cmd->setTargetAddress(serial()); EventLog()->setLastAlarmRequestSuccess(CMD_PENDING); _radio->enqueCommand(cmd); @@ -85,14 +83,12 @@ bool HM_Abstract::sendDevInfoRequest() time_t now; time(&now); - auto cmdAll = _radio->prepareCommand(); + auto cmdAll = _radio->prepareCommand(this); cmdAll->setTime(now); - cmdAll->setTargetAddress(serial()); _radio->enqueCommand(cmdAll); - auto cmdSimple = _radio->prepareCommand(); + auto cmdSimple = _radio->prepareCommand(this); cmdSimple->setTime(now); - cmdSimple->setTargetAddress(serial()); _radio->enqueCommand(cmdSimple); return true; @@ -112,9 +108,8 @@ bool HM_Abstract::sendSystemConfigParaRequest() time_t now; time(&now); - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setTime(now); - cmd->setTargetAddress(serial()); SystemConfigPara()->setLastLimitRequestSuccess(CMD_PENDING); _radio->enqueCommand(cmd); @@ -138,9 +133,8 @@ bool HM_Abstract::sendActivePowerControlRequest(float limit, const PowerLimitCon _activePowerControlLimit = limit; _activePowerControlType = type; - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setActivePowerLimit(limit, type); - cmd->setTargetAddress(serial()); SystemConfigPara()->setLastLimitCommandSuccess(CMD_PENDING); _radio->enqueCommand(cmd); @@ -168,9 +162,8 @@ bool HM_Abstract::sendPowerControlRequest(const bool turnOn) _powerState = 0; } - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setPowerOn(turnOn); - cmd->setTargetAddress(serial()); PowerCommand()->setLastPowerCommandSuccess(CMD_PENDING); _radio->enqueCommand(cmd); @@ -185,9 +178,8 @@ bool HM_Abstract::sendRestartControlRequest() _powerState = 2; - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setRestart(); - cmd->setTargetAddress(serial()); PowerCommand()->setLastPowerCommandSuccess(CMD_PENDING); _radio->enqueCommand(cmd); @@ -227,9 +219,8 @@ bool HM_Abstract::sendGridOnProFileParaRequest() time_t now; time(&now); - auto cmd = _radio->prepareCommand(); + auto cmd = _radio->prepareCommand(this); cmd->setTime(now); - cmd->setTargetAddress(serial()); _radio->enqueCommand(cmd); return true; diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.cpp b/lib/Hoymiles/src/inverters/InverterAbstract.cpp index d80d0e53..68d61183 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.cpp +++ b/lib/Hoymiles/src/inverters/InverterAbstract.cpp @@ -127,6 +127,16 @@ bool InverterAbstract::getZeroYieldDayOnMidnight() const return _zeroYieldDayOnMidnight; } +void InverterAbstract::setClearEventlogOnMidnight(const bool enabled) +{ + _clearEventlogOnMidnight = enabled; +} + +bool InverterAbstract::getClearEventlogOnMidnight() const +{ + return _clearEventlogOnMidnight; +} + bool InverterAbstract::sendChangeChannelRequest() { return false; @@ -226,7 +236,7 @@ uint8_t InverterAbstract::verifyAllFragments(CommandAbstract& cmd) if (cmd.getSendCount() <= cmd.getMaxResendCount()) { return FRAGMENT_ALL_MISSING_RESEND; } else { - cmd.gotTimeout(*this); + cmd.gotTimeout(); return FRAGMENT_ALL_MISSING_TIMEOUT; } } @@ -237,7 +247,7 @@ uint8_t InverterAbstract::verifyAllFragments(CommandAbstract& cmd) if (_rxFragmentRetransmitCnt++ < cmd.getMaxRetransmitCount()) { return _rxFragmentLastPacketId + 1; } else { - cmd.gotTimeout(*this); + cmd.gotTimeout(); return FRAGMENT_RETRANSMIT_TIMEOUT; } } @@ -249,16 +259,16 @@ uint8_t InverterAbstract::verifyAllFragments(CommandAbstract& cmd) if (_rxFragmentRetransmitCnt++ < cmd.getMaxRetransmitCount()) { return i + 1; } else { - cmd.gotTimeout(*this); + cmd.gotTimeout(); return FRAGMENT_RETRANSMIT_TIMEOUT; } } } - if (!cmd.handleResponse(*this, _rxFragmentBuffer, _rxFragmentMaxPacketId)) { - cmd.gotTimeout(*this); + if (!cmd.handleResponse(_rxFragmentBuffer, _rxFragmentMaxPacketId)) { + cmd.gotTimeout(); return FRAGMENT_HANDLE_ERROR; } return FRAGMENT_OK; -} \ No newline at end of file +} diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index 3d9929d7..2a51079b 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -58,6 +58,9 @@ public: void setZeroYieldDayOnMidnight(const bool enabled); bool getZeroYieldDayOnMidnight() const; + void setClearEventlogOnMidnight(const bool enabled); + bool getClearEventlogOnMidnight() const; + void clearRxFragmentBuffer(); void addRxFragment(const uint8_t fragment[], const uint8_t len); uint8_t verifyAllFragments(CommandAbstract& cmd); @@ -102,6 +105,7 @@ private: bool _zeroValuesIfUnreachable = false; bool _zeroYieldDayOnMidnight = false; + bool _clearEventlogOnMidnight = false; std::unique_ptr _alarmLogParser; std::unique_ptr _devInfoParser; @@ -109,4 +113,4 @@ private: std::unique_ptr _powerCommandParser; std::unique_ptr _statisticsParser; std::unique_ptr _systemConfigParaParser; -}; \ No newline at end of file +}; diff --git a/lib/Hoymiles/src/parser/AlarmLogParser.cpp b/lib/Hoymiles/src/parser/AlarmLogParser.cpp index 65215900..e08baf05 100644 --- a/lib/Hoymiles/src/parser/AlarmLogParser.cpp +++ b/lib/Hoymiles/src/parser/AlarmLogParser.cpp @@ -1,7 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022-2023 Thomas Basler and others + * Copyright (C) 2022-2024 Thomas Basler and others */ + +/* +This parser is used to parse the response of 'AlarmDataCommand'. + +Data structure: +* wcode: + * right 8 bit: Event ID + * bit 13: Start time = PM (12h has to be added to start time) + * bit 12: End time = PM (12h has to be added to start time) +* Start: 12h based start time of the event (PM indicator in wcode) +* End: 12h based start time of the event (PM indicator in wcode) + +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 00 01 02 03 04 05 06 07 08 09 10 11 + |<-------------- First log entry -------------->| |<->| +----------------------------------------------------------------------------------------------------------------------------- +95 80 14 82 66 80 14 33 28 01 00 01 80 01 00 01 91 EA 91 EA 00 00 00 00 00 8F 65 -- -- -- -- -- +^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ +ID Source Addr Target Addr Idx ? wcode ? Start End ? ? ? ? wcode CRC8 +*/ #include "AlarmLogParser.h" #include "../Hoymiles.h" #include diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index 26e3c9d4..c34ecb3f 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -1,7 +1,32 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 - 2023 Thomas Basler and others + * Copyright (C) 2022 - 2024 Thomas Basler and others */ + +/* +This parser is used to parse the response of 'DevInfoAllCommand' and 'DevInfoSimpleCommand'. +It contains version information of the hardware and firmware. It can also be used to determine +the exact inverter type. + +Data structure (DevInfoAllCommand): + +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 +------------------------------------------------------------------------------------------------------------------------------------------------- +95 80 14 82 66 80 14 33 28 81 27 1C 07 E5 04 01 07 2D 00 01 00 00 00 00 DF DD 1E -- -- -- -- -- +^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ +ID Source Addr Target Addr Idx FW Version FW Year FW Month/Date FW Hour/Minute Bootloader ? ? CRC16 CRC8 + + +Data structure (DevInfoSimpleCommand): + +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 +------------------------------------------------------------------------------------------------------------------------------------------------- +95 80 14 82 66 80 14 33 28 81 27 1C 10 12 71 01 01 00 0A 00 20 01 00 00 E5 F8 95 +^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ +ID Source Addr Target Addr Idx FW Version HW Part No. HW Version ? ? ? CRC16 CRC8 +*/ #include "DevInfoParser.h" #include "../Hoymiles.h" #include diff --git a/lib/Hoymiles/src/parser/GridProfileParser.cpp b/lib/Hoymiles/src/parser/GridProfileParser.cpp index a7b912a9..489565e1 100644 --- a/lib/Hoymiles/src/parser/GridProfileParser.cpp +++ b/lib/Hoymiles/src/parser/GridProfileParser.cpp @@ -2,6 +2,23 @@ /* * Copyright (C) 2023 - 2024 Thomas Basler and others */ + +/* +This parser is used to parse the response of 'GridOnProFilePara'. +It contains the whole grid profile of the inverter. + +Data structure: + +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 + |<---------- Returns till the end of the payload ---------->| +--------------------------------------------------------------------------------------------------------------------------------------------------------------- +95 80 14 82 66 80 14 33 28 01 0A 00 20 01 00 0C 08 FC 07 A3 00 0F 09 E2 00 1E E6 -- -- -- -- -- +^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ +ID Source Addr Target Addr Idx Profile ID Profile Version Section ID Section Version Value Value Value Value CRC16 CRC8 + +The number of values depends on the respective section and its version. After the last value of a section follows the next section id. +*/ #include "GridProfileParser.h" #include "../Hoymiles.h" #include @@ -11,10 +28,10 @@ const std::array GridProfileParser::_profileTypes = { { { 0x02, 0x00, "US - NA_IEEE1547_240V" }, { 0x03, 0x00, "DE - DE_VDE4105_2018" }, - { 0x03, 0x01, "XX - unknown" }, + { 0x03, 0x01, "DE - DE_VDE4105_2011" }, { 0x0a, 0x00, "XX - EN 50549-1:2019" }, { 0x0c, 0x00, "AT - AT_TOR_Erzeuger_default" }, - { 0x0d, 0x04, "FR -" }, + { 0x0d, 0x04, "XX - NF_EN_50549-1:2019" }, { 0x10, 0x00, "ES - ES_RD1699" }, { 0x12, 0x00, "PL - EU_EN50438" }, { 0x29, 0x00, "NL - NL_NEN-EN50549-1_2019" }, @@ -82,7 +99,7 @@ constexpr frozen::map itemDefinition { 0x1f, make_value("Start of Frequency Watt Droop (Fstart)", "Hz", 100) }, { 0x20, make_value("FW Droop Slope (Kpower_Freq)", "Pn%/Hz", 10) }, { 0x21, make_value("Recovery Ramp Rate (RRR)", "Pn%/s", 100) }, - { 0x22, make_value("Recovery High Frequency (RVHF)", "Hz", 100) }, + { 0x22, make_value("Recovery High Frequency (RVHF)", "Hz", 10) }, { 0x23, make_value("Recovery Low Frequency (RVLF)", "Hz", 100) }, { 0x24, make_value("VW Function Activated", "bool", 1) }, { 0x25, make_value("Start of Voltage Watt Droop (Vstart)", "V", 10) }, diff --git a/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp b/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp index e866e874..346b5d46 100644 --- a/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp +++ b/lib/Hoymiles/src/parser/SystemConfigParaParser.cpp @@ -1,7 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 - 2023 Thomas Basler and others + * Copyright (C) 2022 - 2024 Thomas Basler and others */ + +/* +This parser is used to parse the response of 'SystemConfigParaCommand'. +It contains the set inverter limit. + +Data structure: + +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 00 01 02 03 04 05 06 07 08 09 10 11 12 13 +--------------------------------------------------------------------------------------------------------------------------------- +95 80 14 82 66 80 14 33 28 81 00 01 03 E8 00 00 03 E8 00 00 00 00 00 00 3C F8 2E -- -- -- -- -- +^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^^^^ ^^ +ID Source Addr Target Addr Idx ? Limit percent ? ? ? ? ? CRC16 CRC8 +*/ #include "SystemConfigParaParser.h" #include "../Hoymiles.h" #include diff --git a/patches/async_tcp/event_queue_size.patch b/patches/async_tcp/event_queue_size.patch deleted file mode 100644 index 1280d46a..00000000 --- a/patches/async_tcp/event_queue_size.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --color -ruN a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp ---- a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp -+++ b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp -@@ -97,7 +97,7 @@ - - static inline bool _init_async_event_queue(){ - if(!_async_queue){ -- _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); -+ _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE, sizeof(lwip_event_packet_t *)); - if(!_async_queue){ - return false; - } -diff --color -ruN a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h ---- a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h -+++ b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h -@@ -53,6 +53,10 @@ - #define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2 - #endif - -+#ifndef CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE -+#define CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE 32 -+#endif -+ - class AsyncClient; - - #define ASYNC_MAX_ACK_TIME 5000 diff --git a/platformio.ini b/platformio.ini index b70c3207..3d0c8e3f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ extra_configs = custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb framework = arduino -platform = espressif32@6.6.0 +platform = espressif32@6.7.0 build_flags = -DPIOENV=\"$PIOENV\" @@ -38,9 +38,9 @@ build_unflags = -std=gnu++11 lib_deps = - mathieucarbou/ESP Async WebServer @ 2.9.5 + mathieucarbou/ESP Async WebServer @ 2.10.8 bblanchon/ArduinoJson @ 7.0.4 - https://github.com/bertmelis/espMqttClient.git#v1.6.0 + https://github.com/bertmelis/espMqttClient.git#v1.7.0 nrf24/RF24 @ 1.4.8 olikraus/U8g2 @ 2.35.19 buelowp/sunset @ 1.1.7 @@ -64,7 +64,7 @@ board_build.embed_files = webapp_dist/js/app.js.gz webapp_dist/site.webmanifest -custom_patches = async_tcp +custom_patches = monitor_filters = esp32_exception_decoder, time, log2file, colorize monitor_speed = 115200 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 1495753d..9adda28d 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -130,6 +130,7 @@ bool ConfigurationClass::write() inv["reachable_threshold"] = config.Inverter[i].ReachableThreshold; inv["zero_runtime"] = config.Inverter[i].ZeroRuntimeDataIfUnrechable; inv["zero_day"] = config.Inverter[i].ZeroYieldDayOnMidnight; + inv["clear_eventlog"] = config.Inverter[i].ClearEventlogOnMidnight; inv["yieldday_correction"] = config.Inverter[i].YieldDayCorrection; JsonArray channel = inv["channel"].to(); @@ -391,6 +392,7 @@ bool ConfigurationClass::read() config.Inverter[i].ReachableThreshold = inv["reachable_threshold"] | REACHABLE_THRESHOLD; config.Inverter[i].ZeroRuntimeDataIfUnrechable = inv["zero_runtime"] | false; config.Inverter[i].ZeroYieldDayOnMidnight = inv["zero_day"] | false; + config.Inverter[i].ClearEventlogOnMidnight = inv["clear_eventlog"] | false; config.Inverter[i].YieldDayCorrection = inv["yieldday_correction"] | false; JsonArray channel = inv["channel"]; diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index 19605af1..3be1927c 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -85,6 +85,7 @@ void InverterSettingsClass::init(Scheduler& scheduler) inv->setReachableThreshold(config.Inverter[i].ReachableThreshold); inv->setZeroValuesIfUnreachable(config.Inverter[i].ZeroRuntimeDataIfUnrechable); inv->setZeroYieldDayOnMidnight(config.Inverter[i].ZeroYieldDayOnMidnight); + inv->setClearEventlogOnMidnight(config.Inverter[i].ClearEventlogOnMidnight); inv->Statistics()->setYieldDayCorrection(config.Inverter[i].YieldDayCorrection); for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { inv->Statistics()->setStringMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower); diff --git a/src/MqttHandleInverter.cpp b/src/MqttHandleInverter.cpp index de2778d1..624033e1 100644 --- a/src/MqttHandleInverter.cpp +++ b/src/MqttHandleInverter.cpp @@ -25,20 +25,7 @@ MqttHandleInverterClass::MqttHandleInverterClass() void MqttHandleInverterClass::init(Scheduler& scheduler) { - using std::placeholders::_1; - using std::placeholders::_2; - using std::placeholders::_3; - using std::placeholders::_4; - using std::placeholders::_5; - using std::placeholders::_6; - - const String topic = MqttSettings.getPrefix(); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); - MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + subscribeTopics(); scheduler.addTask(_loopTask); _loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND); @@ -247,3 +234,32 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro } } } + +void MqttHandleInverterClass::subscribeTopics() +{ + using std::placeholders::_1; + using std::placeholders::_2; + using std::placeholders::_3; + using std::placeholders::_4; + using std::placeholders::_5; + using std::placeholders::_6; + + const String topic = MqttSettings.getPrefix(); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); + MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); +} + +void MqttHandleInverterClass::unsubscribeTopics() +{ + const String topic = MqttSettings.getPrefix(); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER)); + MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART)); +} diff --git a/src/Utils.cpp b/src/Utils.cpp index 6bedd2cb..6abe4dd1 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Copyright (C) 2022 - 2023 Thomas Basler and others + * Copyright (C) 2022 - 2024 Thomas Basler and others */ + #include "Utils.h" #include "Display_Graphic.h" #include "Led_Single.h" #include "MessageOutput.h" -#include "PinMapping.h" #include #include diff --git a/src/WebApi_inverter.cpp b/src/WebApi_inverter.cpp index 2d9a5634..5a8585f7 100644 --- a/src/WebApi_inverter.cpp +++ b/src/WebApi_inverter.cpp @@ -55,6 +55,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request) obj["reachable_threshold"] = config.Inverter[i].ReachableThreshold; obj["zero_runtime"] = config.Inverter[i].ZeroRuntimeDataIfUnrechable; obj["zero_day"] = config.Inverter[i].ZeroYieldDayOnMidnight; + obj["clear_eventlog"] = config.Inverter[i].ClearEventlogOnMidnight; obj["yieldday_correction"] = config.Inverter[i].YieldDayCorrection; auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial); @@ -213,20 +214,21 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) inverter.Serial = new_serial; strncpy(inverter.Name, root["name"].as().c_str(), INV_MAX_NAME_STRLEN); + inverter.Poll_Enable = root["poll_enable"] | true; + inverter.Poll_Enable_Night = root["poll_enable_night"] | true; + inverter.Command_Enable = root["command_enable"] | true; + inverter.Command_Enable_Night = root["command_enable_night"] | true; + inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD; + inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false; + inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false; + inverter.ClearEventlogOnMidnight = root["clear_eventlog"] | false; + inverter.YieldDayCorrection = root["yieldday_correction"] | false; + uint8_t arrayCount = 0; for (JsonVariant channel : channelArray) { inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as(); inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as(); strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name)); - inverter.Poll_Enable = root["poll_enable"] | true; - inverter.Poll_Enable_Night = root["poll_enable_night"] | true; - inverter.Command_Enable = root["command_enable"] | true; - inverter.Command_Enable_Night = root["command_enable_night"] | true; - inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD; - inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false; - inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false; - inverter.YieldDayCorrection = root["yieldday_correction"] | false; - arrayCount++; } @@ -254,6 +256,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request) inv->setReachableThreshold(inverter.ReachableThreshold); inv->setZeroValuesIfUnreachable(inverter.ZeroRuntimeDataIfUnrechable); inv->setZeroYieldDayOnMidnight(inverter.ZeroYieldDayOnMidnight); + inv->setClearEventlogOnMidnight(inverter.ClearEventlogOnMidnight); inv->Statistics()->setYieldDayCorrection(inverter.YieldDayCorrection); for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) { inv->Statistics()->setStringMaxPower(c, inverter.channel[c].MaxChannelPower); diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index a032a34d..94f9b2c9 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -5,6 +5,7 @@ #include "WebApi_mqtt.h" #include "Configuration.h" #include "MqttHandleHass.h" +#include "MqttHandleInverter.h" #include "MqttHandleVedirectHass.h" #include "MqttHandleVedirect.h" #include "MqttSettings.h" @@ -82,7 +83,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request) root["mqtt_client_cert"] = config.Mqtt.Tls.ClientCert; root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey; root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic; - root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online;; + root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online; root["mqtt_lwt_offline"] = config.Mqtt.Lwt.Value_Offline; root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos; root["mqtt_publish_interval"] = config.Mqtt.PublishInterval; @@ -280,7 +281,6 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as().c_str(), sizeof(config.Mqtt.Hostname)); strlcpy(config.Mqtt.Username, root["mqtt_username"].as().c_str(), sizeof(config.Mqtt.Username)); strlcpy(config.Mqtt.Password, root["mqtt_password"].as().c_str(), sizeof(config.Mqtt.Password)); - strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as().c_str(), sizeof(config.Mqtt.Topic)); strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as().c_str(), sizeof(config.Mqtt.Lwt.Topic)); strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Online)); strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline)); @@ -293,6 +293,13 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as(); strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as().c_str(), sizeof(config.Mqtt.Hass.Topic)); + // Check if base topic was changed + if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as().c_str())) { + MqttHandleInverter.unsubscribeTopics(); + strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as().c_str(), sizeof(config.Mqtt.Topic)); + MqttHandleInverter.subscribeTopics(); + } + WebApi.writeConfig(retMsg); WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); diff --git a/src/WebApi_sysstatus.cpp b/src/WebApi_sysstatus.cpp index 6d07f2fb..3a6b8f44 100644 --- a/src/WebApi_sysstatus.cpp +++ b/src/WebApi_sysstatus.cpp @@ -7,11 +7,12 @@ #include "NetworkSettings.h" #include "PinMapping.h" #include "WebApi.h" +#include "__compiled_constants.h" #include +#include #include #include #include -#include "__compiled_constants.h" void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler) { @@ -33,6 +34,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request) root["sdkversion"] = ESP.getSdkVersion(); root["cpufreq"] = ESP.getCpuFreqMHz(); + root["cputemp"] = CpuTemperature.read(); root["heap_total"] = ESP.getHeapSize(); root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap(); diff --git a/webapp/package.json b/webapp/package.json index e758594d..bb66b4ac 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,31 +18,31 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.2", "spark-md5": "^3.0.2", - "vue": "^3.4.26", + "vue": "^3.4.27", "vue-i18n": "^9.13.1", - "vue-router": "^4.3.2" + "vue-router": "^4.3.3" }, "devDependencies": { "@intlify/unplugin-vue-i18n": "^4.0.0", "@tsconfig/node18": "^18.2.4", "@types/bootstrap": "^5.2.10", - "@types/node": "^20.12.10", + "@types/node": "^20.14.2", "@types/pulltorefreshjs": "^0.1.7", "@types/sortablejs": "^1.15.8", "@types/spark-md5": "^3.0.4", - "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue": "^5.0.5", "@vue/eslint-config-typescript": "^13.0.0", "@vue/tsconfig": "^0.5.1", - "eslint": "^9.2.0", - "eslint-plugin-vue": "^9.25.0", + "eslint": "^9.4.0", + "eslint-plugin-vue": "^9.26.0", "npm-run-all": "^4.1.5", "pulltorefreshjs": "^0.1.22", - "sass": "^1.76.0", - "terser": "^5.31.0", + "sass": "^1.77.4", + "terser": "^5.31.1", "typescript": "^5.4.5", - "vite": "^5.2.11", + "vite": "^5.2.13", "vite-plugin-compression": "^0.5.1", "vite-plugin-css-injected-by-js": "^3.5.1", - "vue-tsc": "^2.0.16" + "vue-tsc": "^2.0.21" } } diff --git a/webapp/src/components/HardwareInfo.vue b/webapp/src/components/HardwareInfo.vue index c7f342fb..ec95c98c 100644 --- a/webapp/src/components/HardwareInfo.vue +++ b/webapp/src/components/HardwareInfo.vue @@ -19,11 +19,15 @@ {{ $t('hardwareinfo.CpuFrequency') }} {{ systemStatus.cpufreq }} {{ $t('hardwareinfo.Mhz') }} + + {{ $t('hardwareinfo.CpuTemperature') }} + {{ $n(systemStatus.cputemp, 'celsius') }} + {{ $t('hardwareinfo.FlashSize') }} - {{ systemStatus.flashsize }} {{ $t('hardwareinfo.Bytes') }} - ({{ systemStatus.flashsize / 1024 / 1024 }} {{ $t('hardwareinfo.MegaBytes') }}) + {{ $n(systemStatus.flashsize, 'byte') }} + ({{ $n(systemStatus.flashsize / 1024 / 1024, 'megabyte') }}) diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 3f992d31..120cd55c 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -74,8 +74,8 @@ "4009": "Wechselrichter Reihenfolge gespeichert!", "5001": "@:apiresponse.2001", "5002": "Das Limit muss zwischen 1 und {max} sein!", - "5003": "Ungültiten Typ angegeben!", - "5004": "Ungültigen Inverter angegeben!", + "5003": "Ungültiger Typ angegeben!", + "5004": "Ungültiger Inverter angegeben!", "6001": "Neustart durchgeführt!", "6002": "Neustart abgebrochen!", "7001": "MQTT-Server muss zwischen 1 und {max} Zeichen lang sein!", @@ -191,7 +191,7 @@ "PowerLimiterState": "Power limiter Status [aus (laden), nur die Sonne nutzen, Nutzung der Batterie]" }, "eventlog": { - "Start": "Begin", + "Start": "Beginn", "Stop": "Ende", "Id": "ID", "Message": "Meldung" @@ -251,9 +251,8 @@ "ChipCores": "Chip-Kerne", "CpuFrequency": "CPU-Frequenz", "Mhz": "MHz", - "FlashSize": "Flash-Speichergröße", - "Bytes": "Bytes", - "MegaBytes": "MB" + "CpuTemperature": "CPU-Temperatur", + "FlashSize": "Flash-Speichergröße" }, "memoryinfo": { "MemoryInformation": "Speicherinformationen", @@ -706,6 +705,7 @@ "ZeroRuntimeHint": "Nulle Laufzeit Daten (keine Ertragsdaten), wenn der Wechselrichter nicht erreichbar ist.", "ZeroDay": "Nulle Tagesertrag um Mitternacht", "ZeroDayHint": "Das funktioniert nur wenn der Wechselrichter nicht erreichbar ist. Wenn Daten aus dem Wechselrichter gelesen werden, werden deren Werte verwendet. (Ein Reset erfolgt nur beim Neustarten)", + "ClearEventlog": "Lösche Ereignisanzeige um Mitternacht", "Cancel": "@:base.Cancel", "Save": "@:base.Save", "DeleteMsg": "Soll der Wechselrichter \"{name}\" mit der Seriennummer {serial} wirklich gelöscht werden?", @@ -783,9 +783,9 @@ "DefaultProfile": "(Standardeinstellungen)", "ProfileHint": "Ihr Gerät reagiert möglicherweise nicht mehr, wenn Sie ein inkompatibles Profil wählen. In diesem Fall müssen Sie eine Löschung über das serielle Interface durchführen.", "Display": "Display", - "PowerSafe": "Stromsparen aktivieren:", + "PowerSafe": "Ausschalten wenn kein Inverter erreichbar:", "PowerSafeHint": "Schaltet das Display aus, wenn kein Wechselrichter Strom erzeugt", - "Screensaver": "Bildschirmschoner aktivieren:", + "Screensaver": "OLED-Schutz gegen Einbrennen:", "ScreensaverHint": "Bewegt die Ausgabe bei jeder Aktualisierung um ein Einbrennen zu verhindern (v. a. für OLED-Displays nützlich)", "DiagramMode": "Diagramm Modus:", "off": "Deaktiviert", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index c89045c1..61fa972b 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -252,9 +252,8 @@ "ChipCores": "Chip Cores", "CpuFrequency": "CPU Frequency", "Mhz": "MHz", - "FlashSize": "Flash Memory Size", - "Bytes": "Bytes", - "MegaBytes": "MB" + "CpuTemperature": "CPU Temperature", + "FlashSize": "Flash Memory Size" }, "memoryinfo": { "MemoryInformation": "Memory Information", @@ -712,6 +711,7 @@ "ZeroRuntimeHint": "Zero runtime data (no yield data) if inverter becomes unreachable.", "ZeroDay": "Zero daily yield at midnight", "ZeroDayHint": "This only works if the inverter is unreachable. If data is read from the inverter, it's values will be used. (Reset only occours on power cycle)", + "ClearEventlog": "Clear Eventlog at midnight", "Cancel": "@:base.Cancel", "Save": "@:base.Save", "DeleteMsg": "Are you sure you want to delete the inverter \"{name}\" with serial number {serial}?", @@ -789,9 +789,9 @@ "DefaultProfile": "(Default settings)", "ProfileHint": "Your device may stop responding if you select an incompatible profile. In this case, you must perform a deletion via the serial interface.", "Display": "Display", - "PowerSafe": "Enable Power Save:", + "PowerSafe": "Switch off if no solar:", "PowerSafeHint": "Turn off the display if no inverter is producing.", - "Screensaver": "Enable Screensaver:", + "Screensaver": "OLED Anti burn-in:", "ScreensaverHint": "Move the display a little bit on each update to prevent burn-in. (Useful especially for OLED displays)", "DiagramMode": "Diagram mode:", "off": "Off", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 2db449fc..c91fe3a2 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -286,9 +286,8 @@ "ChipCores": "Nombre de cœurs", "CpuFrequency": "Fréquence du CPU", "Mhz": "MHz", - "FlashSize": "Taille de la mémoire flash", - "Bytes": "octets", - "MegaBytes": "Mo" + "CpuTemperature": "CPU Temperature", + "FlashSize": "Taille de la mémoire flash" }, "memoryinfo": { "MemoryInformation": "Informations sur la mémoire", @@ -646,6 +645,7 @@ "ZeroRuntimeHint": "Zero runtime data (no yield data) if inverter becomes unreachable.", "ZeroDay": "Zero daily yield at midnight", "ZeroDayHint": "This only works if the inverter is unreachable. If data is read from the inverter, it's values will be used. (Reset only occours on power cycle)", + "ClearEventlog": "Clear Eventlog at midnight", "Cancel": "@:base.Cancel", "Save": "@:base.Save", "DeleteMsg": "Êtes-vous sûr de vouloir supprimer l'onduleur \"{name}\" avec le numéro de série \"{serial}\" ?", @@ -773,9 +773,9 @@ "DefaultProfile": "(Réglages par défaut)", "ProfileHint": "Votre appareil peut cesser de répondre si vous sélectionnez un profil incompatible. Dans ce cas, vous devez effectuer une suppression via l'interface série.", "Display": "Affichage", - "PowerSafe": "Activer l'économiseur d'énergie", + "PowerSafe": "Economiseur d'énergie", "PowerSafeHint": "Eteindre l'écran si aucun onduleur n'est en production.", - "Screensaver": "Activer l'écran de veille", + "Screensaver": "OLED Anti burn-in", "ScreensaverHint": "Déplacez un peu l'écran à chaque mise à jour pour éviter le phénomène de brûlure. (Utile surtout pour les écrans OLED)", "DiagramMode": "Diagram mode:", "off": "Off", diff --git a/webapp/src/locales/index.ts b/webapp/src/locales/index.ts index 57589c7f..7ea42cf4 100644 --- a/webapp/src/locales/index.ts +++ b/webapp/src/locales/index.ts @@ -12,44 +12,23 @@ export const LOCALES = [ { value: Locales.FR, caption: 'Français' }, ] -export const dateTimeFormats: I18nOptions["datetimeFormats"] = { - [Locales.EN]: { - 'datetime': { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour12: false - } - }, - [Locales.DE]: { - 'datetime': { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour12: false - } - }, - [Locales.FR]: { - 'datetime': { - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour12: false - } - } -}; +export const dateTimeFormats: I18nOptions["datetimeFormats"] = {}; +export const numberFormats: I18nOptions["numberFormats"] = {}; -export const numberFormats: I18nOptions["numberFormats"] = { - [Locales.EN]: { +LOCALES.forEach((locale) => { + dateTimeFormats[locale.value] = { + 'datetime': { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour12: false + } + }; + + numberFormats[locale.value] = { decimal: { style: 'decimal', }, @@ -62,44 +41,19 @@ export const numberFormats: I18nOptions["numberFormats"] = { percent: { style: 'percent', }, - kilobyte: { - style: 'unit', unit: 'kilobyte', - }, - }, - [Locales.DE]: { - decimal: { - style: 'decimal', - }, - decimalNoDigits: { - style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 - }, - decimalTwoDigits: { - style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 - }, - percent: { - style: 'percent', + byte: { + style: 'unit', unit: 'byte', }, kilobyte: { style: 'unit', unit: 'kilobyte', }, - }, - [Locales.FR]: { - decimal: { - style: 'decimal', + megabyte: { + style: 'unit', unit: 'megabyte', }, - decimalNoDigits: { - style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 + celsius: { + style: 'unit', unit: 'celsius', maximumFractionDigits: 1, }, - decimalTwoDigits: { - style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 - }, - percent: { - style: 'percent', - }, - kilobyte: { - style: 'unit', unit: 'kilobyte', - }, - }, -}; + }; +}); -export const defaultLocale = Locales.EN; \ No newline at end of file +export const defaultLocale = Locales.EN; diff --git a/webapp/src/types/InverterConfig.ts b/webapp/src/types/InverterConfig.ts index da7fa43c..ba268c4f 100644 --- a/webapp/src/types/InverterConfig.ts +++ b/webapp/src/types/InverterConfig.ts @@ -17,6 +17,7 @@ export interface Inverter { reachable_threshold: number; zero_runtime: boolean; zero_day: boolean; + clear_eventlog: boolean; yieldday_correction: boolean; channel: Array; } diff --git a/webapp/src/types/SystemStatus.ts b/webapp/src/types/SystemStatus.ts index f9a5b70e..3937361d 100644 --- a/webapp/src/types/SystemStatus.ts +++ b/webapp/src/types/SystemStatus.ts @@ -4,6 +4,7 @@ export interface SystemStatus { chiprevision: number; chipcores: number; cpufreq: number; + cputemp: number; flashsize: number; // FirmwareInfo hostname: string; diff --git a/webapp/src/views/InverterAdminView.vue b/webapp/src/views/InverterAdminView.vue index 9216f592..5953a7a2 100644 --- a/webapp/src/views/InverterAdminView.vue +++ b/webapp/src/views/InverterAdminView.vue @@ -176,6 +176,8 @@ + + diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 57f29ff9..bfe79588 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -161,10 +161,19 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== -"@eslint/eslintrc@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.0.2.tgz#36180f8e85bf34d2fe3ccc2261e8e204a411ab4e" - integrity sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg== +"@eslint/config-array@^0.15.1": + version "0.15.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.15.1.tgz#1fa78b422d98f4e7979f2211a1fde137e26c7d61" + integrity sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ== + dependencies: + "@eslint/object-schema" "^2.1.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -176,34 +185,25 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.2.0.tgz#b0a9123e8e91a3d9a2eed3a04a6ed44fdab639aa" - integrity sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA== +"@eslint/js@9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.4.0.tgz#96a2edd37ec0551ce5f9540705be23951c008a0c" + integrity sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg== -"@humanwhocodes/config-array@^0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" - integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== - dependencies: - "@humanwhocodes/object-schema" "^2.0.3" - debug "^4.3.1" - minimatch "^3.0.5" +"@eslint/object-schema@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.3.tgz#e65ae80ee2927b4fd8c5c26b15ecacc2b2a6cc2a" + integrity sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw== "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== - -"@humanwhocodes/retry@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.2.3.tgz#c9aa036d1afa643f1250e83150f39efb3a15a631" - integrity sha512-X38nUbachlb01YMlvPFojKoiXq+LzZvuSce70KPMPdeM1Rj03k4dR7lDslhbqXn3Ang4EU3+EAmwEAsbrjHW3g== +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== "@intlify/bundle-utils@^8.0.0": version "8.0.0" @@ -449,10 +449,10 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== -"@types/node@^20.12.10": - version "20.12.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.10.tgz#8f0c3f12b0f075eee1fe20c1afb417e9765bef76" - integrity sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw== +"@types/node@^20.14.2": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== dependencies: undici-types "~5.26.4" @@ -562,32 +562,33 @@ "@typescript-eslint/types" "7.2.0" eslint-visitor-keys "^3.4.1" -"@vitejs/plugin-vue@^5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37" - integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ== +"@vitejs/plugin-vue@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz#e3dc11e427d4b818b7e3202766ad156e3d5e2eaa" + integrity sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ== -"@volar/language-core@2.2.1", "@volar/language-core@~2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.2.1.tgz#bb4a28f93cd8598a2e2ca1c811ae113a848b5529" - integrity sha512-iHJAZKcYldZgyS8gx6DfIZApViVBeqbf6iPhqoZpG5A6F4zsZiFldKfwaKaBA3/wnOTWE2i8VUbXywI1WywCPg== +"@volar/language-core@2.3.0", "@volar/language-core@~2.3.0-alpha.15": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.3.0.tgz#ffb9b64c8b19d7f45b1fdcd9ae9d98d94bad7179" + integrity sha512-pvhL24WUh3VDnv7Yw5N1sjhPtdx7q9g+Wl3tggmnkMcyK8GcCNElF2zHiKznryn0DiUGk+eez/p2qQhz+puuHw== dependencies: - "@volar/source-map" "2.2.1" + "@volar/source-map" "2.3.0" -"@volar/source-map@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.2.1.tgz#d75b0c38659d3ea7e780d4251ac2b9436845ab97" - integrity sha512-w1Bgpguhbp7YTr7VUFu6gb4iAZjeEPsOX4zpgiuvlldbzvIWDWy4t0jVifsIsxZ99HAu+c3swiME7wt+GeNqhA== +"@volar/source-map@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.3.0.tgz#faf4df8f10ca40788f03c35eed3e2b7848110cc9" + integrity sha512-G/228aZjAOGhDjhlyZ++nDbKrS9uk+5DMaEstjvzglaAw7nqtDyhnQAsYzUg6BMP9BtwZ59RIw5HGePrutn00Q== dependencies: muggle-string "^0.4.0" -"@volar/typescript@~2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.2.1.tgz#21585b46cd61c9d63715642ee10418b144b12321" - integrity sha512-Z/tqluR7Hz5/5dCqQp7wo9C/6tSv/IYl+tTzgzUt2NjTq95bKSsuO4E+V06D0c+3aP9x5S9jggLqw451hpnc6Q== +"@volar/typescript@~2.3.0-alpha.15": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.3.0.tgz#00306942e95e2e22fed8daf73ec386cd72601ecf" + integrity sha512-PtUwMM87WsKVeLJN33GSTUjBexlKfKgouWlOUIv7pjrOnTwhXHZNSmpc312xgXdTjQPpToK6KXSIcKu9sBQ5LQ== dependencies: - "@volar/language-core" "2.2.1" + "@volar/language-core" "2.3.0" path-browserify "^1.0.1" + vscode-uri "^3.0.8" "@vue/compiler-core@3.2.47": version "3.2.47" @@ -610,13 +611,13 @@ estree-walker "^2.0.2" source-map-js "^1.0.2" -"@vue/compiler-core@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.26.tgz#d507886520e83a6f8339ed55ed0b2b5d84b44b73" - integrity sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ== +"@vue/compiler-core@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz#e69060f4b61429fe57976aa5872cfa21389e4d91" + integrity sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg== dependencies: "@babel/parser" "^7.24.4" - "@vue/shared" "3.4.26" + "@vue/shared" "3.4.27" entities "^4.5.0" estree-walker "^2.0.2" source-map-js "^1.2.0" @@ -629,13 +630,13 @@ "@vue/compiler-core" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-dom@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz#acc7b788b48152d087d4bb9e655b795e3dbec554" - integrity sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA== +"@vue/compiler-dom@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz#d51d35f40d00ce235d7afc6ad8b09dfd92b1cc1c" + integrity sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw== dependencies: - "@vue/compiler-core" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/compiler-core" "3.4.27" + "@vue/shared" "3.4.27" "@vue/compiler-dom@^3.4.0": version "3.4.21" @@ -645,16 +646,16 @@ "@vue/compiler-core" "3.4.21" "@vue/shared" "3.4.21" -"@vue/compiler-sfc@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz#c679f206829954c3c078d8a9be76d0098b8377ae" - integrity sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw== +"@vue/compiler-sfc@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz#399cac1b75c6737bf5440dc9cf3c385bb2959701" + integrity sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA== dependencies: "@babel/parser" "^7.24.4" - "@vue/compiler-core" "3.4.26" - "@vue/compiler-dom" "3.4.26" - "@vue/compiler-ssr" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/compiler-core" "3.4.27" + "@vue/compiler-dom" "3.4.27" + "@vue/compiler-ssr" "3.4.27" + "@vue/shared" "3.4.27" estree-walker "^2.0.2" magic-string "^0.30.10" postcss "^8.4.38" @@ -684,13 +685,13 @@ "@vue/compiler-dom" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-ssr@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz#22842d8adfff972d87bb798b8d496111f7f814b5" - integrity sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ== +"@vue/compiler-ssr@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz#2a8ecfef1cf448b09be633901a9c020360472e3d" + integrity sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw== dependencies: - "@vue/compiler-dom" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/compiler-dom" "3.4.27" + "@vue/shared" "3.4.27" "@vue/devtools-api@^6.5.0": version "6.5.0" @@ -711,12 +712,12 @@ "@typescript-eslint/parser" "^7.1.1" vue-eslint-parser "^9.3.1" -"@vue/language-core@2.0.16": - version "2.0.16" - resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.0.16.tgz#c059228e6a0a17b4505421da0e5747a4a04facbe" - integrity sha512-Bc2sexRH99pznOph8mLw2BlRZ9edm7tW51kcBXgx8adAoOcZUWJj3UNSsdQ6H9Y8meGz7BoazVrVo/jUukIsPw== +"@vue/language-core@2.0.21": + version "2.0.21" + resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.0.21.tgz#882667d0c9f07bc884f163e75eed666234df77fe" + integrity sha512-vjs6KwnCK++kIXT+eI63BGpJHfHNVJcUCr3RnvJsccT3vbJnZV5IhHR2puEkoOkIbDdp0Gqi1wEnv3hEd3WsxQ== dependencies: - "@volar/language-core" "~2.2.0" + "@volar/language-core" "~2.3.0-alpha.15" "@vue/compiler-dom" "^3.4.0" "@vue/shared" "^3.4.0" computeds "^0.0.1" @@ -735,37 +736,37 @@ estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/reactivity@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.26.tgz#1191f543809d4c93e5b3e842ba83022350a3f205" - integrity sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ== +"@vue/reactivity@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.27.tgz#6ece72331bf719953f5eaa95ec60b2b8d49e3791" + integrity sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA== dependencies: - "@vue/shared" "3.4.26" + "@vue/shared" "3.4.27" -"@vue/runtime-core@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.26.tgz#51ee971cb700370a67e5a510c4a84eff7491d658" - integrity sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw== +"@vue/runtime-core@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.27.tgz#1b6e1d71e4604ba7442dd25ed22e4a1fc6adbbda" + integrity sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA== dependencies: - "@vue/reactivity" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/reactivity" "3.4.27" + "@vue/shared" "3.4.27" -"@vue/runtime-dom@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz#179aa7c8dc964112e6d096bc8ec5f361111009a1" - integrity sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw== +"@vue/runtime-dom@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz#fe8d1ce9bbe8921d5dd0ad5c10df0e04ef7a5ee7" + integrity sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q== dependencies: - "@vue/runtime-core" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/runtime-core" "3.4.27" + "@vue/shared" "3.4.27" csstype "^3.1.3" -"@vue/server-renderer@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.26.tgz#6d0c6b0366bfe0232579aea00e3ff6784e5a1c60" - integrity sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw== +"@vue/server-renderer@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.27.tgz#3306176f37e648ba665f97dda3ce705687be63d2" + integrity sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA== dependencies: - "@vue/compiler-ssr" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/compiler-ssr" "3.4.27" + "@vue/shared" "3.4.27" "@vue/shared@3.2.47": version "3.2.47" @@ -777,10 +778,10 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1" integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g== -"@vue/shared@3.4.26": - version "3.4.26" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.26.tgz#f17854fb1faf889854aed4b23b60e86a8cab6403" - integrity sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ== +"@vue/shared@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.27.tgz#f05e3cd107d157354bb4ae7a7b5fc9cf73c63b50" + integrity sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA== "@vue/tsconfig@^0.5.1": version "0.5.1" @@ -1158,10 +1159,10 @@ escodegen@^2.1.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-vue@^9.25.0: - version "9.25.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz#615cb7bb6d0e2140d21840b9aa51dce69e803e7a" - integrity sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA== +eslint-plugin-vue@^9.26.0: + version "9.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.26.0.tgz#bf7f5cce62c8f878059b91edae44d22974133af5" + integrity sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" globals "^13.24.0" @@ -1208,18 +1209,18 @@ eslint-visitor-keys@^4.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== -eslint@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.2.0.tgz#0700ebc99528753315d78090876911d3cdbf19fe" - integrity sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og== +eslint@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.4.0.tgz#79150c3610ae606eb131f1d648d5f43b3d45f3cd" + integrity sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^3.0.2" - "@eslint/js" "9.2.0" - "@humanwhocodes/config-array" "^0.13.0" + "@eslint/config-array" "^0.15.1" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.4.0" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.2.3" + "@humanwhocodes/retry" "^0.3.0" "@nodelib/fs.walk" "^1.2.8" ajv "^6.12.4" chalk "^4.0.0" @@ -2201,10 +2202,10 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -sass@^1.76.0: - version "1.76.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.76.0.tgz#fe15909500735ac154f0dc7386d656b62b03987d" - integrity sha512-nc3LeqvF2FNW5xGF1zxZifdW3ffIz5aBb7I7tSvOoNu7z1RQ6pFt9MBuiPtjgaI62YWrM/txjWlOCFiGtf2xpw== +sass@^1.77.4: + version "1.77.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.4.tgz#92059c7bfc56b827c56eb116778d157ec017a5cd" + integrity sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -2406,10 +2407,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" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -terser@^5.31.0: - version "5.31.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.0.tgz#06eef86f17007dbad4593f11a574c7f5eb02c6a1" - integrity sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg== +terser@^5.31.1: + version "5.31.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.1.tgz#735de3c987dd671e95190e6b98cfe2f07f3cf0d4" + integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -2519,10 +2520,10 @@ vite-plugin-css-injected-by-js@^3.5.1: resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.1.tgz#b9c568c21b131d08e31aa6d368ee39c9d6c1b6c1" integrity sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ== -vite@^5.2.11: - version "5.2.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.11.tgz#726ec05555431735853417c3c0bfb36003ca0cbd" - integrity sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ== +vite@^5.2.13: + version "5.2.13" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.13.tgz#945ababcbe3d837ae2479c29f661cd20bc5e1a80" + integrity sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A== dependencies: esbuild "^0.20.1" postcss "^8.4.38" @@ -2530,6 +2531,11 @@ vite@^5.2.11: optionalDependencies: fsevents "~2.3.3" +vscode-uri@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + vue-eslint-parser@^9.3.1: version "9.3.1" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz#429955e041ae5371df5f9e37ebc29ba046496182" @@ -2565,10 +2571,10 @@ vue-i18n@^9.13.1: "@intlify/shared" "9.13.1" "@vue/devtools-api" "^6.5.0" -vue-router@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.3.2.tgz#08096c7765dacc6832f58e35f7a081a8b34116a7" - integrity sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q== +vue-router@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.3.3.tgz#7505509d429a36694b12ba1f6530016c5ce5f6bf" + integrity sha512-8Q+u+WP4N2SXY38FDcF2H1dUEbYVHVPtPCPZj/GTZx8RCbiB8AtJP9+YIxn4Vs0svMTNQcLIzka4GH7Utkx9xQ== dependencies: "@vue/devtools-api" "^6.5.1" @@ -2580,25 +2586,25 @@ vue-template-compiler@^2.7.14: de-indent "^1.0.2" he "^1.2.0" -vue-tsc@^2.0.16: - version "2.0.16" - resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.0.16.tgz#ba82c4cdac283e8e39e30e817c8c1c967e528358" - integrity sha512-/gHAWJa216PeEhfxtAToIbxdWgw01wuQzo48ZUqMYVEyNqDp+OYV9xMO5HaPS2P3Ls0+EsjguMZLY4cGobX4Ew== +vue-tsc@^2.0.21: + version "2.0.21" + resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.0.21.tgz#c574a2c20e8a5e5643af546c6051319cdf983239" + integrity sha512-E6x1p1HaHES6Doy8pqtm7kQern79zRtIewkf9fiv7Y43Zo4AFDS5hKi+iHi2RwEhqRmuiwliB1LCEFEGwvxQnw== dependencies: - "@volar/typescript" "~2.2.0" - "@vue/language-core" "2.0.16" + "@volar/typescript" "~2.3.0-alpha.15" + "@vue/language-core" "2.0.21" semver "^7.5.4" -vue@^3.4.26: - version "3.4.26" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.26.tgz#936c97e37672c737705d7bdfa62c31af18742269" - integrity sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg== +vue@^3.4.27: + version "3.4.27" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.27.tgz#40b7d929d3e53f427f7f5945386234d2854cc2a1" + integrity sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA== dependencies: - "@vue/compiler-dom" "3.4.26" - "@vue/compiler-sfc" "3.4.26" - "@vue/runtime-dom" "3.4.26" - "@vue/server-renderer" "3.4.26" - "@vue/shared" "3.4.26" + "@vue/compiler-dom" "3.4.27" + "@vue/compiler-sfc" "3.4.27" + "@vue/runtime-dom" "3.4.27" + "@vue/server-renderer" "3.4.27" + "@vue/shared" "3.4.27" webpack-sources@^3.2.3: version "3.2.3"