Merge upstream tag 'v24.9.26' into development
This commit is contained in:
commit
5d8bb8f810
@ -6,7 +6,7 @@
|
|||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {
|
enum DeviceClassType {
|
||||||
DEVICE_CLS_NONE = 0,
|
DEVICE_CLS_NONE = 0,
|
||||||
DEVICE_CLS_CURRENT,
|
DEVICE_CLS_CURRENT,
|
||||||
DEVICE_CLS_ENERGY,
|
DEVICE_CLS_ENERGY,
|
||||||
@ -15,20 +15,34 @@ enum {
|
|||||||
DEVICE_CLS_FREQ,
|
DEVICE_CLS_FREQ,
|
||||||
DEVICE_CLS_TEMP,
|
DEVICE_CLS_TEMP,
|
||||||
DEVICE_CLS_POWER_FACTOR,
|
DEVICE_CLS_POWER_FACTOR,
|
||||||
DEVICE_CLS_REACTIVE_POWER
|
DEVICE_CLS_REACTIVE_POWER,
|
||||||
|
DEVICE_CLS_CONNECTIVITY,
|
||||||
|
DEVICE_CLS_DURATION,
|
||||||
|
DEVICE_CLS_SIGNAL_STRENGTH,
|
||||||
|
DEVICE_CLS_TEMPERATURE,
|
||||||
|
DEVICE_CLS_RESTART
|
||||||
};
|
};
|
||||||
const char* const deviceClasses[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power" };
|
const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" };
|
||||||
enum {
|
|
||||||
|
enum StateClassType {
|
||||||
STATE_CLS_NONE = 0,
|
STATE_CLS_NONE = 0,
|
||||||
STATE_CLS_MEASUREMENT,
|
STATE_CLS_MEASUREMENT,
|
||||||
STATE_CLS_TOTAL_INCREASING
|
STATE_CLS_TOTAL_INCREASING
|
||||||
};
|
};
|
||||||
const char* const stateClasses[] = { 0, "measurement", "total_increasing" };
|
const char* const stateClass_name[] = { 0, "measurement", "total_increasing" };
|
||||||
|
|
||||||
|
enum CategoryType {
|
||||||
|
CATEGORY_NONE = 0,
|
||||||
|
CATEGORY_CONFIG,
|
||||||
|
CATEGORY_DIAGNOSTIC
|
||||||
|
};
|
||||||
|
const char* const category_name[] = { 0, "config", "diagnostic" };
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FieldId_t fieldId; // field id
|
FieldId_t fieldId; // field id
|
||||||
uint8_t deviceClsId; // device class
|
DeviceClassType deviceClsId; // device class
|
||||||
uint8_t stateClsId; // state class
|
StateClassType stateClsId; // state class
|
||||||
} byteAssign_fieldDeviceClass_t;
|
} byteAssign_fieldDeviceClass_t;
|
||||||
|
|
||||||
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
||||||
@ -61,13 +75,24 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publish(const String& subtopic, const String& payload);
|
static void publish(const String& subtopic, const String& payload);
|
||||||
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
|
static void publish(const String& subtopic, const JsonDocument& doc);
|
||||||
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
|
|
||||||
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
|
||||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100, float step = 1.0);
|
// Binary Sensor
|
||||||
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
|
||||||
|
// Sensor
|
||||||
|
static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
||||||
|
static void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const CategoryType category);
|
||||||
|
|
||||||
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||||
static void createDtuInfo(JsonDocument& doc);
|
static void createDtuInfo(JsonDocument& doc);
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
#include <frozen/map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
class MqttHandleInverterClass {
|
class MqttHandleInverterClass {
|
||||||
public:
|
public:
|
||||||
@ -19,7 +21,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
@ -41,6 +42,29 @@ private:
|
|||||||
FLD_IRR,
|
FLD_IRR,
|
||||||
FLD_Q
|
FLD_Q
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Topic : unsigned {
|
||||||
|
LimitPersistentRelative,
|
||||||
|
LimitPersistentAbsolute,
|
||||||
|
LimitNonPersistentRelative,
|
||||||
|
LimitNonPersistentAbsolute,
|
||||||
|
Power,
|
||||||
|
Restart,
|
||||||
|
ResetRfStats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr frozen::string _cmdtopic = "+/cmd/";
|
||||||
|
static constexpr frozen::map<frozen::string, Topic, 7> _subscriptions = {
|
||||||
|
{ "limit_persistent_relative", Topic::LimitPersistentRelative },
|
||||||
|
{ "limit_persistent_absolute", Topic::LimitPersistentAbsolute },
|
||||||
|
{ "limit_nonpersistent_relative", Topic::LimitNonPersistentRelative },
|
||||||
|
{ "limit_nonpersistent_absolute", Topic::LimitNonPersistentAbsolute },
|
||||||
|
{ "power", Topic::Power },
|
||||||
|
{ "restart", Topic::Restart },
|
||||||
|
{ "reset_rf_stats", Topic::ResetRfStats },
|
||||||
|
};
|
||||||
|
|
||||||
|
void onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterClass MqttHandleInverter;
|
extern MqttHandleInverterClass MqttHandleInverter;
|
||||||
|
|||||||
@ -32,6 +32,7 @@ enum WebApiError {
|
|||||||
InverterChanged,
|
InverterChanged,
|
||||||
InverterDeleted,
|
InverterDeleted,
|
||||||
InverterOrdered,
|
InverterOrdered,
|
||||||
|
InverterStatsResetted,
|
||||||
|
|
||||||
LimitBase = 5000,
|
LimitBase = 5000,
|
||||||
LimitSerialZero,
|
LimitSerialZero,
|
||||||
|
|||||||
@ -14,4 +14,5 @@ private:
|
|||||||
void onInverterEdit(AsyncWebServerRequest* request);
|
void onInverterEdit(AsyncWebServerRequest* request);
|
||||||
void onInverterDelete(AsyncWebServerRequest* request);
|
void onInverterDelete(AsyncWebServerRequest* request);
|
||||||
void onInverterOrder(AsyncWebServerRequest* request);
|
void onInverterOrder(AsyncWebServerRequest* request);
|
||||||
|
void onInverterStatReset(AsyncWebServerRequest* request);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -136,16 +136,7 @@ void HoymilesClass::loop()
|
|||||||
if (currentWeekDay != lastWeekDay) {
|
if (currentWeekDay != lastWeekDay) {
|
||||||
|
|
||||||
for (auto& inv : _inverters) {
|
for (auto& inv : _inverters) {
|
||||||
// Have to reset the offets first, otherwise it will
|
inv->performDailyTask();
|
||||||
// Substract the offset from zero which leads to a high value
|
|
||||||
inv->Statistics()->resetYieldDayCorrection();
|
|
||||||
if (inv->getZeroYieldDayOnMidnight()) {
|
|
||||||
inv->Statistics()->zeroDailyData();
|
|
||||||
}
|
|
||||||
if (inv->getClearEventlogOnMidnight()) {
|
|
||||||
inv->EventLog()->clearBuffer();
|
|
||||||
}
|
|
||||||
inv->resetRadioStats();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastWeekDay = currentWeekDay;
|
lastWeekDay = currentWeekDay;
|
||||||
@ -204,9 +195,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(const uint8_t
|
|||||||
|
|
||||||
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
for (auto& inv : _inverters) {
|
||||||
if (_inverters[i]->serial() == serial) {
|
if (inv->serial() == serial) {
|
||||||
return _inverters[i];
|
return inv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -218,9 +209,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(const fra
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InverterAbstract> inv;
|
for (auto& inv : _inverters) {
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
|
||||||
inv = _inverters[i];
|
|
||||||
serial_u p;
|
serial_u p;
|
||||||
p.u64 = inv->serial();
|
p.u64 = inv->serial();
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
||||||
// Statistics: Count RX Fail No Answer
|
// Statistics: Count RX Fail No Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
inv->RadioStats.RxFailNoAnswer++;
|
inv->RadioStats.RxFailNoAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
@ -75,7 +77,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
||||||
// Statistics: Count RX Fail Partial Answer
|
// Statistics: Count RX Fail Partial Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
inv->RadioStats.RxFailPartialAnswer++;
|
inv->RadioStats.RxFailPartialAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
@ -83,7 +87,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
||||||
Hoymiles.getMessageOutput()->println("Packet handling error");
|
Hoymiles.getMessageOutput()->println("Packet handling error");
|
||||||
// Statistics: Count RX Fail Corrupt Data
|
// Statistics: Count RX Fail Corrupt Data
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
inv->RadioStats.RxFailCorruptData++;
|
inv->RadioStats.RxFailCorruptData++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
@ -101,7 +107,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
// Successful received all packages
|
// Successful received all packages
|
||||||
Hoymiles.getMessageOutput()->println("Success");
|
Hoymiles.getMessageOutput()->println("Success");
|
||||||
// Statistics: Count RX Success
|
// Statistics: Count RX Success
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
inv->RadioStats.RxSuccess++;
|
inv->RadioStats.RxSuccess++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|||||||
@ -273,6 +273,20 @@ uint8_t InverterAbstract::verifyAllFragments(CommandAbstract& cmd)
|
|||||||
return FRAGMENT_OK;
|
return FRAGMENT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InverterAbstract::performDailyTask()
|
||||||
|
{
|
||||||
|
// Have to reset the offets first, otherwise it will
|
||||||
|
// Substract the offset from zero which leads to a high value
|
||||||
|
Statistics()->resetYieldDayCorrection();
|
||||||
|
if (getZeroYieldDayOnMidnight()) {
|
||||||
|
Statistics()->zeroDailyData();
|
||||||
|
}
|
||||||
|
if (getClearEventlogOnMidnight()) {
|
||||||
|
EventLog()->clearBuffer();
|
||||||
|
}
|
||||||
|
resetRadioStats();
|
||||||
|
}
|
||||||
|
|
||||||
void InverterAbstract::resetRadioStats()
|
void InverterAbstract::resetRadioStats()
|
||||||
{
|
{
|
||||||
RadioStats = {};
|
RadioStats = {};
|
||||||
|
|||||||
@ -65,6 +65,8 @@ public:
|
|||||||
void addRxFragment(const uint8_t fragment[], const uint8_t len);
|
void addRxFragment(const uint8_t fragment[], const uint8_t len);
|
||||||
uint8_t verifyAllFragments(CommandAbstract& cmd);
|
uint8_t verifyAllFragments(CommandAbstract& cmd);
|
||||||
|
|
||||||
|
void performDailyTask();
|
||||||
|
|
||||||
void resetRadioStats();
|
void resetRadioStats();
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@ -18,20 +18,64 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
|
env = DefaultEnvironment()
|
||||||
platform = env.PioPlatform()
|
platform = env.PioPlatform()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from os.path import join, getsize
|
import csv
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
from os.path import join, getsize, exists, isdir
|
||||||
|
from os import listdir
|
||||||
|
|
||||||
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
||||||
import esptool
|
import esptool
|
||||||
|
|
||||||
|
def esp32_build_filesystem(fs_name, fs_size):
|
||||||
|
filesystem_dir = env.subst("$PROJECT_DATA_DIR")
|
||||||
|
print("Creating %dKiB filesystem with content:" % (int(fs_size, 0)/1024) )
|
||||||
|
if not isdir(filesystem_dir) or not listdir(filesystem_dir):
|
||||||
|
print("No files added -> will NOT create littlefs.bin and NOT overwrite fs partition!")
|
||||||
|
return False
|
||||||
|
# this does not work on GitHub, results in 'mklittlefs: No such file or directory'
|
||||||
|
tool = shutil.which(env.subst(env["MKFSTOOL"]))
|
||||||
|
if tool is None or not exists(tool):
|
||||||
|
print("Using fallback mklittlefs")
|
||||||
|
tool = "~/.platformio/packages/tool-mklittlefs/mklittlefs"
|
||||||
|
|
||||||
|
cmd = (tool, "-c", filesystem_dir, "-s", fs_size, fs_name)
|
||||||
|
returncode = subprocess.call(cmd, shell=False)
|
||||||
|
print("Return Code:", returncode)
|
||||||
|
return True
|
||||||
|
|
||||||
def esp32_create_combined_bin(source, target, env):
|
def esp32_create_combined_bin(source, target, env):
|
||||||
print("Generating combined binary for serial flashing")
|
print("Generating combined binary for serial flashing")
|
||||||
|
|
||||||
# The offset from begin of the file where the app0 partition starts
|
# The offset from begin of the file where the app0 partition starts
|
||||||
# This is defined in the partition .csv file
|
# This is defined in the partition .csv file
|
||||||
app_offset = 0x10000
|
app_offset = 0x10000
|
||||||
|
fs_offset = -1
|
||||||
|
fs_name = env.subst("$BUILD_DIR/littlefs.bin")
|
||||||
|
|
||||||
|
with open(env.BoardConfig().get("build.partitions")) as csv_file:
|
||||||
|
print("Read partitions from ", env.BoardConfig().get("build.partitions"))
|
||||||
|
csv_reader = csv.reader(csv_file, delimiter=',')
|
||||||
|
line_count = 0
|
||||||
|
for row in csv_reader:
|
||||||
|
if line_count == 0:
|
||||||
|
print(f'{", ".join(row)}')
|
||||||
|
line_count += 1
|
||||||
|
else:
|
||||||
|
if (len(row) < 4):
|
||||||
|
continue
|
||||||
|
print(f'{row[0]} {row[1]} {row[2]} {row[3]} {row[4]}')
|
||||||
|
line_count += 1
|
||||||
|
if(row[0] == 'app0'):
|
||||||
|
app_offset = int(row[3], base=16)
|
||||||
|
elif(row[0] == 'spiffs'):
|
||||||
|
partition_size = row[4]
|
||||||
|
if esp32_build_filesystem(fs_name, partition_size):
|
||||||
|
fs_offset = int(row[3], base=16)
|
||||||
|
|
||||||
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
||||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
||||||
@ -77,6 +121,10 @@ def esp32_create_combined_bin(source, target, env):
|
|||||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||||
cmd += [hex(app_offset), firmware_name]
|
cmd += [hex(app_offset), firmware_name]
|
||||||
|
|
||||||
|
if fs_offset != -1:
|
||||||
|
print(f" - {hex(fs_offset)} | {fs_name}")
|
||||||
|
cmd += [hex(fs_offset), fs_name]
|
||||||
|
|
||||||
print('Using esptool.py arguments: %s' % ' '.join(cmd))
|
print('Using esptool.py arguments: %s' % ' '.join(cmd))
|
||||||
|
|
||||||
esptool.main(cmd)
|
esptool.main(cmd)
|
||||||
|
|||||||
@ -20,6 +20,8 @@ custom_ci_action = generic_esp32_4mb_no_ota,generic_esp32_8mb,generic_esp32s3,ge
|
|||||||
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = espressif32@6.8.1
|
platform = espressif32@6.8.1
|
||||||
|
platform_packages =
|
||||||
|
platformio/tool-mklittlefs
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
-DPIOENV=\"$PIOENV\"
|
-DPIOENV=\"$PIOENV\"
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "defaults.h"
|
|
||||||
#include "__compiled_constants.h"
|
#include "__compiled_constants.h"
|
||||||
|
#include "defaults.h"
|
||||||
|
|
||||||
MqttHandleHassClass MqttHandleHass;
|
MqttHandleHassClass MqttHandleHass;
|
||||||
|
|
||||||
@ -58,34 +58,45 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
const CONFIG_T& config = Configuration.get();
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
// publish DTU sensors
|
// publish DTU sensors
|
||||||
publishDtuSensor("IP", "", "diagnostic", "mdi:network-outline", "", "");
|
publishDtuSensor("IP", "dtu/ip", "", "mdi:network-outline", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi");
|
publishDtuSensor("WiFi Signal", "dtu/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", "");
|
publishDtuSensor("Uptime", "dtu/uptime", "s", "", DEVICE_CLS_DURATION, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Temperature", "temperature", "diagnostic", "mdi:thermometer", "°C", "temperature");
|
publishDtuSensor("Temperature", "dtu/temperature", "°C", "mdi:thermometer", DEVICE_CLS_TEMPERATURE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Heap Size", "", "diagnostic", "mdi:memory", "Bytes", "heap/size");
|
publishDtuSensor("Heap Size", "dtu/heap/size", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Heap Free", "", "diagnostic", "mdi:memory", "Bytes", "heap/free");
|
publishDtuSensor("Heap Free", "dtu/heap/free", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Largest Free Heap Block", "", "diagnostic", "mdi:memory", "Bytes", "heap/maxalloc");
|
publishDtuSensor("Largest Free Heap Block", "dtu/heap/maxalloc", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Lifetime Minimum Free Heap", "", "diagnostic", "mdi:memory", "Bytes", "heap/minfree");
|
publishDtuSensor("Lifetime Minimum Free Heap", "dtu/heap/minfree", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuBinarySensor("Status", "connectivity", "diagnostic", config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, config.Mqtt.Lwt.Topic);
|
|
||||||
|
|
||||||
yield();
|
publishDtuSensor("Yield Total", "ac/yieldtotal", "kWh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE);
|
||||||
|
publishDtuSensor("Yield Day", "ac/yieldday", "Wh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE);
|
||||||
|
publishDtuSensor("AC Power", "ac/power", "W", "", DEVICE_CLS_PWR, CATEGORY_NONE);
|
||||||
|
|
||||||
|
publishDtuBinarySensor("Status", config.Mqtt.Lwt.Topic, config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC);
|
||||||
|
|
||||||
// Loop all inverters
|
// Loop all inverters
|
||||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
auto inv = Hoymiles.getInverterByPos(i);
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
|
|
||||||
publishInverterButton(inv, "Turn Inverter Off", "mdi:power-plug-off", "config", "", "cmd/power", "0");
|
publishInverterButton(inv, "Turn Inverter Off", "cmd/power", "0", "mdi:power-plug-off", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
publishInverterButton(inv, "Turn Inverter On", "mdi:power-plug", "config", "", "cmd/power", "1");
|
publishInverterButton(inv, "Turn Inverter On", "cmd/power", "1", "mdi:power-plug", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
publishInverterButton(inv, "Restart Inverter", "", "config", "restart", "cmd/restart", "1");
|
publishInverterButton(inv, "Restart Inverter", "cmd/restart", "1", "", DEVICE_CLS_RESTART, CATEGORY_CONFIG);
|
||||||
|
publishInverterButton(inv, "Reset Radio Statistics", "cmd/reset_rf_stats", "1", "", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
publishInverterNumber(inv, "Limit NonPersistent Relative", "status/limit_relative", "cmd/limit_nonpersistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
publishInverterNumber(inv, "Limit Persistent Relative", "status/limit_relative", "cmd/limit_persistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterNumber(inv, "Limit NonPersistent Absolute", "mdi:speedometer", "config", "cmd/limit_nonpersistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
publishInverterNumber(inv, "Limit NonPersistent Absolute", "status/limit_absolute", "cmd/limit_nonpersistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
publishInverterNumber(inv, "Limit Persistent Absolute", "mdi:speedometer", "config", "cmd/limit_persistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
publishInverterNumber(inv, "Limit Persistent Absolute", "status/limit_absolute", "cmd/limit_persistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0");
|
publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0", DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC);
|
||||||
publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0");
|
publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0", DEVICE_CLS_NONE, CATEGORY_NONE);
|
||||||
|
|
||||||
|
publishInverterSensor(inv, "TX Requests", "radio/tx_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Success", "radio/rx_success", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Nothing", "radio/rx_fail_nothing", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Partial", "radio/rx_fail_partial", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Corrupt", "radio/rx_fail_corrupt", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "TX Re-Request Fragment", "radio/tx_re_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
|
||||||
// Loop all channels
|
// Loop all channels
|
||||||
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
||||||
@ -99,8 +110,6 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,8 +142,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
|||||||
|
|
||||||
if (!clear) {
|
if (!clear) {
|
||||||
const String stateTopic = MqttSettings.getPrefix() + MqttHandleInverter.getTopic(inv, type, channel, fieldType.fieldId);
|
const String stateTopic = MqttSettings.getPrefix() + MqttHandleInverter.getTopic(inv, type, channel, fieldType.fieldId);
|
||||||
const char* devCls = deviceClasses[fieldType.deviceClsId];
|
const char* stateCls = stateClass_name[fieldType.stateClsId];
|
||||||
const char* stateCls = stateClasses[fieldType.stateClsId];
|
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
if (type != TYPE_DC) {
|
if (type != TYPE_DC) {
|
||||||
@ -143,46 +151,34 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
|||||||
name = "CH" + chanNum + " " + fieldName;
|
name = "CH" + chanNum + " " + fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String unit_of_measure = inv->Statistics()->getChannelFieldUnit(type, channel, fieldType.fieldId);
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, unit_of_measure, "", fieldType.deviceClsId, CATEGORY_NONE);
|
||||||
|
|
||||||
root["name"] = name;
|
root["name"] = name;
|
||||||
root["stat_t"] = stateTopic;
|
root["stat_t"] = stateTopic;
|
||||||
root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName;
|
root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName;
|
||||||
|
|
||||||
String unit_of_measure = inv->Statistics()->getChannelFieldUnit(type, channel, fieldType.fieldId);
|
|
||||||
if (unit_of_measure != "") {
|
|
||||||
root["unit_of_meas"] = unit_of_measure;
|
|
||||||
}
|
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
|
||||||
|
|
||||||
if (Configuration.get().Mqtt.Hass.Expire) {
|
if (Configuration.get().Mqtt.Hass.Expire) {
|
||||||
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
||||||
}
|
}
|
||||||
if (devCls != 0) {
|
|
||||||
root["dev_cla"] = devCls;
|
|
||||||
}
|
|
||||||
if (stateCls != 0) {
|
if (stateCls != 0) {
|
||||||
root["stat_cla"] = stateCls;
|
root["stat_cla"] = stateCls;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
publish(configTopic, root);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
} else {
|
} else {
|
||||||
publish(configTopic, "");
|
publish(configTopic, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload)
|
void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
{
|
{
|
||||||
const String serial = inv->serialString();
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
String buttonId = caption;
|
String buttonId = name;
|
||||||
buttonId.replace(" ", "_");
|
buttonId.replace(" ", "_");
|
||||||
buttonId.toLowerCase();
|
buttonId.toLowerCase();
|
||||||
|
|
||||||
@ -190,41 +186,29 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
|||||||
+ "/" + buttonId
|
+ "/" + buttonId
|
||||||
+ "/config";
|
+ "/config";
|
||||||
|
|
||||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + state_topic;
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, "", icon, device_class, category);
|
||||||
|
|
||||||
root["name"] = caption;
|
root["name"] = name;
|
||||||
root["uniq_id"] = serial + "_" + buttonId;
|
root["uniq_id"] = serial + "_" + buttonId;
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
if (strcmp(deviceClass, "")) {
|
|
||||||
root["dev_cla"] = deviceClass;
|
|
||||||
}
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
root["cmd_t"] = cmdTopic;
|
root["cmd_t"] = cmdTopic;
|
||||||
root["payload_press"] = payload;
|
root["payload_press"] = payload;
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
publish(configTopic, root);
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterNumber(
|
void MqttHandleHassClass::publishInverterNumber(
|
||||||
std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category,
|
std::shared_ptr<InverterAbstract> inv, const String& name,
|
||||||
const char* commandTopic, const char* stateTopic, const char* unitOfMeasure,
|
const String& stateTopic, const String& command_topic,
|
||||||
const int16_t min, const int16_t max, float step)
|
const int16_t min, const int16_t max, float step,
|
||||||
|
const String& unit_of_measure, const String& icon, const CategoryType category)
|
||||||
{
|
{
|
||||||
const String serial = inv->serialString();
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
String buttonId = caption;
|
String buttonId = name;
|
||||||
buttonId.replace(" ", "_");
|
buttonId.replace(" ", "_");
|
||||||
buttonId.toLowerCase();
|
buttonId.toLowerCase();
|
||||||
|
|
||||||
@ -232,148 +216,22 @@ void MqttHandleHassClass::publishInverterNumber(
|
|||||||
+ "/" + buttonId
|
+ "/" + buttonId
|
||||||
+ "/config";
|
+ "/config";
|
||||||
|
|
||||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + commandTopic;
|
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + command_topic;
|
||||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
|
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, unit_of_measure, icon, DEVICE_CLS_NONE, category);
|
||||||
|
|
||||||
root["name"] = caption;
|
root["name"] = name;
|
||||||
root["uniq_id"] = serial + "_" + buttonId;
|
root["uniq_id"] = serial + "_" + buttonId;
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
root["cmd_t"] = cmdTopic;
|
root["cmd_t"] = cmdTopic;
|
||||||
root["stat_t"] = statTopic;
|
root["stat_t"] = statTopic;
|
||||||
root["unit_of_meas"] = unitOfMeasure;
|
|
||||||
root["min"] = min;
|
root["min"] = min;
|
||||||
root["max"] = max;
|
root["max"] = max;
|
||||||
root["step"] = step;
|
root["step"] = step;
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
publish(configTopic, root);
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off)
|
|
||||||
{
|
|
||||||
const String serial = inv->serialString();
|
|
||||||
|
|
||||||
String sensorId = caption;
|
|
||||||
sensorId.replace(" ", "_");
|
|
||||||
sensorId.toLowerCase();
|
|
||||||
|
|
||||||
const String configTopic = "binary_sensor/dtu_" + serial
|
|
||||||
+ "/" + sensorId
|
|
||||||
+ "/config";
|
|
||||||
|
|
||||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = caption;
|
|
||||||
root["uniq_id"] = serial + "_" + sensorId;
|
|
||||||
root["stat_t"] = statTopic;
|
|
||||||
root["pl_on"] = payload_on;
|
|
||||||
root["pl_off"] = payload_off;
|
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic)
|
|
||||||
{
|
|
||||||
String id = name;
|
|
||||||
id.toLowerCase();
|
|
||||||
id.replace(" ", "_");
|
|
||||||
String topic = subTopic;
|
|
||||||
if (topic == "") {
|
|
||||||
topic = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = name;
|
|
||||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
|
||||||
if (strcmp(device_class, "")) {
|
|
||||||
root["dev_cla"] = device_class;
|
|
||||||
}
|
|
||||||
if (strcmp(category, "")) {
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
}
|
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
if (strcmp(unit_of_measure, "")) {
|
|
||||||
root["unit_of_meas"] = unit_of_measure;
|
|
||||||
}
|
|
||||||
root["stat_t"] = MqttSettings.getPrefix() + "dtu" + "/" + topic;
|
|
||||||
|
|
||||||
root["avty_t"] = MqttSettings.getPrefix() + Configuration.get().Mqtt.Lwt.Topic;
|
|
||||||
|
|
||||||
const CONFIG_T& config = Configuration.get();
|
|
||||||
root["pl_avail"] = config.Mqtt.Lwt.Value_Online;
|
|
||||||
root["pl_not_avail"] = config.Mqtt.Lwt.Value_Offline;
|
|
||||||
|
|
||||||
createDtuInfo(root);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
const String configTopic = "sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic)
|
|
||||||
{
|
|
||||||
String id = name;
|
|
||||||
id.toLowerCase();
|
|
||||||
id.replace(" ", "_");
|
|
||||||
|
|
||||||
String topic = subTopic;
|
|
||||||
if (!strcmp(subTopic, "")) {
|
|
||||||
topic = String("dtu/") + "/" + id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = name;
|
|
||||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
|
||||||
root["stat_t"] = MqttSettings.getPrefix() + topic;
|
|
||||||
root["pl_on"] = payload_on;
|
|
||||||
root["pl_off"] = payload_off;
|
|
||||||
|
|
||||||
if (strcmp(device_class, "")) {
|
|
||||||
root["dev_cla"] = device_class;
|
|
||||||
}
|
|
||||||
if (strcmp(category, "")) {
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
}
|
|
||||||
|
|
||||||
createDtuInfo(root);
|
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
const String configTopic = "binary_sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::createInverterInfo(JsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
void MqttHandleHassClass::createInverterInfo(JsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
||||||
@ -436,4 +294,106 @@ void MqttHandleHassClass::publish(const String& subtopic, const String& payload)
|
|||||||
String topic = Configuration.get().Mqtt.Hass.Topic;
|
String topic = Configuration.get().Mqtt.Hass.Topic;
|
||||||
topic += subtopic;
|
topic += subtopic;
|
||||||
MqttSettings.publishGeneric(topic, payload, Configuration.get().Mqtt.Hass.Retain);
|
MqttSettings.publishGeneric(topic, payload, Configuration.get().Mqtt.Hass.Retain);
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publish(const String& subtopic, const JsonDocument& doc)
|
||||||
|
{
|
||||||
|
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String buffer;
|
||||||
|
serializeJson(doc, buffer);
|
||||||
|
publish(subtopic, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
if (unit_of_measure != "") {
|
||||||
|
doc["unit_of_meas"] = unit_of_measure;
|
||||||
|
}
|
||||||
|
if (icon != "") {
|
||||||
|
doc["ic"] = icon;
|
||||||
|
}
|
||||||
|
if (device_class != DEVICE_CLS_NONE) {
|
||||||
|
doc["dev_cla"] = deviceClass_name[device_class];
|
||||||
|
}
|
||||||
|
if (category != CATEGORY_NONE) {
|
||||||
|
doc["ent_cat"] = category_name[category];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
String sensor_id = name;
|
||||||
|
sensor_id.toLowerCase();
|
||||||
|
sensor_id.replace(" ", "_");
|
||||||
|
|
||||||
|
doc["name"] = name;
|
||||||
|
doc["uniq_id"] = unique_id_prefix + "_" + sensor_id;
|
||||||
|
doc["stat_t"] = MqttSettings.getPrefix() + state_topic;
|
||||||
|
doc["pl_on"] = payload_on;
|
||||||
|
doc["pl_off"] = payload_off;
|
||||||
|
|
||||||
|
addCommonMetadata(doc, "", "", device_class, category);
|
||||||
|
|
||||||
|
const String configTopic = "binary_sensor/" + root_device + "/" + sensor_id + "/config";
|
||||||
|
publish(configTopic, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String dtuId = getDtuUniqueId();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createDtuInfo(root);
|
||||||
|
publishBinarySensor(root, dtuId, dtuId, name, state_topic, payload_on, payload_off, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
publishBinarySensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, payload_on, payload_off, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
String sensor_id = name;
|
||||||
|
sensor_id.toLowerCase();
|
||||||
|
sensor_id.replace(" ", "_");
|
||||||
|
|
||||||
|
doc["name"] = name;
|
||||||
|
doc["uniq_id"] = unique_id_prefix + "_" + sensor_id;
|
||||||
|
doc["stat_t"] = MqttSettings.getPrefix() + state_topic;
|
||||||
|
|
||||||
|
addCommonMetadata(doc, unit_of_measure, icon, device_class, category);
|
||||||
|
|
||||||
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
doc["avty_t"] = MqttSettings.getPrefix() + config.Mqtt.Lwt.Topic;
|
||||||
|
doc["pl_avail"] = config.Mqtt.Lwt.Value_Online;
|
||||||
|
doc["pl_not_avail"] = config.Mqtt.Lwt.Value_Offline;
|
||||||
|
|
||||||
|
const String configTopic = "sensor/" + root_device + "/" + sensor_id + "/config";
|
||||||
|
publish(configTopic, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String dtuId = getDtuUniqueId();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createDtuInfo(root);
|
||||||
|
publishSensor(root, dtuId, dtuId, name, state_topic, unit_of_measure, icon, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
publishSensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, unit_of_measure, icon, device_class, category);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,6 @@
|
|||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#define TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE "limit_persistent_relative"
|
|
||||||
#define TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE "limit_persistent_absolute"
|
|
||||||
#define TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE "limit_nonpersistent_relative"
|
|
||||||
#define TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE "limit_nonpersistent_absolute"
|
|
||||||
#define TOPIC_SUB_POWER "power"
|
|
||||||
#define TOPIC_SUB_RESTART "restart"
|
|
||||||
|
|
||||||
#define PUBLISH_MAX_INTERVAL 60000
|
#define PUBLISH_MAX_INTERVAL 60000
|
||||||
|
|
||||||
MqttHandleInverterClass MqttHandleInverter;
|
MqttHandleInverterClass MqttHandleInverter;
|
||||||
@ -154,7 +147,7 @@ String MqttHandleInverterClass::getTopic(std::shared_ptr<InverterAbstract> inv,
|
|||||||
return inv->serialString() + "/" + chanNum + "/" + chanName;
|
return inv->serialString() + "/" + chanNum + "/" + chanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total)
|
void MqttHandleInverterClass::onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total)
|
||||||
{
|
{
|
||||||
const CONFIG_T& config = Configuration.get();
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
@ -162,15 +155,11 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char*
|
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char*
|
||||||
|
|
||||||
char* serial_str;
|
char* serial_str;
|
||||||
char* subtopic;
|
|
||||||
char* setting;
|
|
||||||
char* rest = &token_topic[strlen(config.Mqtt.Topic)];
|
char* rest = &token_topic[strlen(config.Mqtt.Topic)];
|
||||||
|
|
||||||
serial_str = strtok_r(rest, "/", &rest);
|
serial_str = strtok_r(rest, "/", &rest);
|
||||||
subtopic = strtok_r(rest, "/", &rest);
|
|
||||||
setting = strtok_r(rest, "/", &rest);
|
|
||||||
|
|
||||||
if (serial_str == NULL || subtopic == NULL || setting == NULL) {
|
if (serial_str == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,33 +172,30 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if subtopic is unequal cmd
|
std::string strValue(reinterpret_cast<const char*>(payload), len);
|
||||||
if (strcmp(subtopic, "cmd")) {
|
float payload_val = -1;
|
||||||
|
try {
|
||||||
|
payload_val = std::stof(strValue);
|
||||||
|
} catch (std::invalid_argument const& e) {
|
||||||
|
MessageOutput.printf("MQTT handler: cannot parse payload of topic '%s' as float: %s\r\n",
|
||||||
|
topic, strValue.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strlimit = new char[len + 1];
|
switch (t) {
|
||||||
memcpy(strlimit, payload, len);
|
case Topic::LimitPersistentRelative:
|
||||||
strlimit[len] = '\0';
|
|
||||||
const float payload_val = strtof(strlimit, NULL);
|
|
||||||
delete[] strlimit;
|
|
||||||
|
|
||||||
if (payload_val < 0) {
|
|
||||||
MessageOutput.printf("MQTT payload < 0 received --> ignoring\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) {
|
|
||||||
// Set inverter limit relative persistent
|
// Set inverter limit relative persistent
|
||||||
MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val);
|
MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val);
|
||||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent);
|
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) {
|
case Topic::LimitPersistentAbsolute:
|
||||||
// Set inverter limit absolute persistent
|
// Set inverter limit absolute persistent
|
||||||
MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val);
|
MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val);
|
||||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent);
|
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) {
|
case Topic::LimitNonPersistentRelative:
|
||||||
// Set inverter limit relative non persistent
|
// Set inverter limit relative non persistent
|
||||||
MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val);
|
MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val);
|
||||||
if (!properties.retain) {
|
if (!properties.retain) {
|
||||||
@ -217,8 +203,9 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained");
|
MessageOutput.println("Ignored because retained");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) {
|
case Topic::LimitNonPersistentAbsolute:
|
||||||
// Set inverter limit absolute non persistent
|
// Set inverter limit absolute non persistent
|
||||||
MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val);
|
MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val);
|
||||||
if (!properties.retain) {
|
if (!properties.retain) {
|
||||||
@ -226,13 +213,15 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained");
|
MessageOutput.println("Ignored because retained");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_POWER)) {
|
case Topic::Power:
|
||||||
// Turn inverter on or off
|
// Turn inverter on or off
|
||||||
MessageOutput.printf("Set inverter power to: %d\r\n", static_cast<int32_t>(payload_val));
|
MessageOutput.printf("Set inverter power to: %d\r\n", static_cast<int32_t>(payload_val));
|
||||||
inv->sendPowerControlRequest(static_cast<int32_t>(payload_val) > 0);
|
inv->sendPowerControlRequest(static_cast<int32_t>(payload_val) > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_RESTART)) {
|
case Topic::Restart:
|
||||||
// Restart inverter
|
// Restart inverter
|
||||||
MessageOutput.printf("Restart inverter\r\n");
|
MessageOutput.printf("Restart inverter\r\n");
|
||||||
if (!properties.retain && payload_val == 1) {
|
if (!properties.retain && payload_val == 1) {
|
||||||
@ -240,34 +229,41 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Topic::ResetRfStats:
|
||||||
|
// Reset RF Stats
|
||||||
|
MessageOutput.printf("Reset RF stats\r\n");
|
||||||
|
if (!properties.retain && payload_val == 1) {
|
||||||
|
inv->resetRadioStats();
|
||||||
|
} else {
|
||||||
|
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::subscribeTopics()
|
void MqttHandleInverterClass::subscribeTopics()
|
||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
String const& prefix = MqttSettings.getPrefix();
|
||||||
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();
|
auto subscribe = [&prefix, this](char const* subTopic, Topic t) {
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
String fullTopic(prefix + _cmdtopic.data() + subTopic);
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
MqttSettings.subscribe(fullTopic.c_str(), 0,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::bind(&MqttHandleInverterClass::onMqttMessage, this, t,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_1, std::placeholders::_2,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_5, std::placeholders::_6));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto const& s : _subscriptions) {
|
||||||
|
subscribe(s.first.data(), s.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::unsubscribeTopics()
|
void MqttHandleInverterClass::unsubscribeTopics()
|
||||||
{
|
{
|
||||||
const String topic = MqttSettings.getPrefix();
|
String const& prefix = MqttSettings.getPrefix() + _cmdtopic.data();
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE));
|
for (auto const& s : _subscriptions) {
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE));
|
MqttSettings.unsubscribe(prefix + s.first.data());
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,8 +132,7 @@ bool NetworkSettingsClass::onEvent(NetworkEventCb cbEvent, const network_event e
|
|||||||
|
|
||||||
void NetworkSettingsClass::raiseEvent(const network_event event)
|
void NetworkSettingsClass::raiseEvent(const network_event event)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < _cbEventList.size(); i++) {
|
for (auto& entry : _cbEventList) {
|
||||||
const NetworkEventCbList_t entry = _cbEventList[i];
|
|
||||||
if (entry.cb) {
|
if (entry.cb) {
|
||||||
if (entry.event == event || entry.event == network_event::NETWORK_EVENT_MAX) {
|
if (entry.event == event || entry.event == network_event::NETWORK_EVENT_MAX) {
|
||||||
entry.cb(event);
|
entry.cb(event);
|
||||||
|
|||||||
@ -21,6 +21,7 @@ void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
||||||
server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
||||||
server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
||||||
|
server.on("/api/inverter/stats_reset", HTTP_GET, std::bind(&WebApiInverterClass::onInverterStatReset, this, _1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||||
@ -349,3 +350,24 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebApiInverterClass::onInverterStatReset(AsyncWebServerRequest* request)
|
||||||
|
{
|
||||||
|
if (!WebApi.checkCredentials(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||||
|
auto retMsg = response->getRoot();
|
||||||
|
auto serial = WebApi.parseSerialFromRequest(request);
|
||||||
|
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||||
|
|
||||||
|
if (inv != nullptr) {
|
||||||
|
inv->resetRadioStats();
|
||||||
|
retMsg["type"] = "success";
|
||||||
|
retMsg["message"] = "Stats resetted";
|
||||||
|
retMsg["code"] = WebApiError::InverterStatsResetted;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"sortablejs": "^1.15.3",
|
"sortablejs": "^1.15.3",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue": "^3.5.8",
|
"vue": "^3.5.9",
|
||||||
"vue-i18n": "9.13.1",
|
"vue-i18n": "9.13.1",
|
||||||
"vue-router": "^4.4.5"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
@ -27,22 +27,22 @@
|
|||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||||
"@tsconfig/node22": "^22.0.0",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
"@types/bootstrap": "^5.2.10",
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@types/node": "^22.5.5",
|
"@types/node": "^22.7.2",
|
||||||
"@types/pulltorefreshjs": "^0.1.7",
|
"@types/pulltorefreshjs": "^0.1.7",
|
||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@types/spark-md5": "^3.0.4",
|
"@types/spark-md5": "^3.0.4",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"@vue/eslint-config-typescript": "^13.0.0",
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"eslint": "^9.11.0",
|
"eslint": "^9.11.1",
|
||||||
"eslint-plugin-vue": "^9.28.0",
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"pulltorefreshjs": "^0.1.22",
|
"pulltorefreshjs": "^0.1.22",
|
||||||
"sass": "^1.77.6",
|
"sass": "^1.77.6",
|
||||||
"terser": "^5.33.0",
|
"terser": "^5.34.0",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.4.7",
|
"vite": "^5.4.8",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.1",
|
"vite-plugin-css-injected-by-js": "^3.5.1",
|
||||||
"vue-tsc": "^2.1.6"
|
"vue-tsc": "^2.1.6"
|
||||||
|
|||||||
@ -157,7 +157,9 @@
|
|||||||
"RxFailNothing": "Empfang Fehler: Nichts empfangen",
|
"RxFailNothing": "Empfang Fehler: Nichts empfangen",
|
||||||
"RxFailPartial": "Empfang Fehler: Teilweise empfangen",
|
"RxFailPartial": "Empfang Fehler: Teilweise empfangen",
|
||||||
"RxFailCorrupt": "Empfang Fehler: Beschädigt empfangen",
|
"RxFailCorrupt": "Empfang Fehler: Beschädigt empfangen",
|
||||||
"TxReRequest": "Gesendete Fragment Wiederanforderungen"
|
"TxReRequest": "Gesendete Fragment Wiederanforderungen",
|
||||||
|
"StatsReset": "Statistiken zurücksetzen",
|
||||||
|
"StatsResetting": "Zurücksetzen..."
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Seriennummer",
|
"SerialNumber": "Seriennummer",
|
||||||
|
|||||||
@ -157,7 +157,9 @@
|
|||||||
"RxFailNothing": "RX Fail: Receive Nothing",
|
"RxFailNothing": "RX Fail: Receive Nothing",
|
||||||
"RxFailPartial": "RX Fail: Receive Partial",
|
"RxFailPartial": "RX Fail: Receive Partial",
|
||||||
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
||||||
"TxReRequest": "TX Re-Request Fragment"
|
"TxReRequest": "TX Re-Request Fragment",
|
||||||
|
"StatsReset": "Reset Statistics",
|
||||||
|
"StatsResetting": "Resetting..."
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Serial Number",
|
"SerialNumber": "Serial Number",
|
||||||
|
|||||||
@ -157,7 +157,9 @@
|
|||||||
"RxFailNothing": "RX Fail: Receive Nothing",
|
"RxFailNothing": "RX Fail: Receive Nothing",
|
||||||
"RxFailPartial": "RX Fail: Receive Partial",
|
"RxFailPartial": "RX Fail: Receive Partial",
|
||||||
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
||||||
"TxReRequest": "TX Re-Request Fragment"
|
"TxReRequest": "TX Re-Request Fragment",
|
||||||
|
"StatsReset": "Reset Statistics",
|
||||||
|
"StatsResetting": "Resetting..."
|
||||||
},
|
},
|
||||||
"vedirecthome": {
|
"vedirecthome": {
|
||||||
"SerialNumber": "Numéro de série",
|
"SerialNumber": "Numéro de série",
|
||||||
|
|||||||
@ -299,6 +299,23 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<button
|
||||||
|
:disabled="!isLogged || performRadioStatsReset"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-danger"
|
||||||
|
@click="onResetRadioStats(inverter.serial)"
|
||||||
|
>
|
||||||
|
<template v-if="!performRadioStatsReset">
|
||||||
|
{{ $t('home.StatsReset') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span
|
||||||
|
class="spinner-border spinner-border-sm"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
|
<span role="status"> {{ $t('home.StatsResetting') }}</span>
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -566,6 +583,7 @@ export default defineComponent({
|
|||||||
alertMessageLimit: '',
|
alertMessageLimit: '',
|
||||||
alertTypeLimit: 'info',
|
alertTypeLimit: 'info',
|
||||||
showAlertLimit: false,
|
showAlertLimit: false,
|
||||||
|
performRadioStatsReset: false,
|
||||||
|
|
||||||
powerSettingView: {} as bootstrap.Modal,
|
powerSettingView: {} as bootstrap.Modal,
|
||||||
powerSettingSerial: '',
|
powerSettingSerial: '',
|
||||||
@ -813,6 +831,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
this.limitSettingView.show();
|
this.limitSettingView.show();
|
||||||
},
|
},
|
||||||
|
onResetRadioStats(serial: string) {
|
||||||
|
this.performRadioStatsReset = true;
|
||||||
|
fetch('/api/inverter/stats_reset?inv=' + serial, { headers: authHeader() })
|
||||||
|
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||||
|
.then(() => {
|
||||||
|
this.performRadioStatsReset = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
onSetLimitSettings(setPersistent: boolean) {
|
onSetLimitSettings(setPersistent: boolean) {
|
||||||
this.targetLimitList.limit_type = (setPersistent ? 256 : 0) + this.targetLimitType;
|
this.targetLimitList.limit_type = (setPersistent ? 256 : 0) + this.targetLimitType;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|||||||
184
webapp/yarn.lock
184
webapp/yarn.lock
@ -191,6 +191,11 @@
|
|||||||
debug "^4.3.1"
|
debug "^4.3.1"
|
||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
|
|
||||||
|
"@eslint/core@^0.6.0":
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.6.0.tgz#9930b5ba24c406d67a1760e94cdbac616a6eb674"
|
||||||
|
integrity sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==
|
||||||
|
|
||||||
"@eslint/eslintrc@^3.1.0":
|
"@eslint/eslintrc@^3.1.0":
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
|
||||||
@ -206,10 +211,10 @@
|
|||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@eslint/js@9.11.0":
|
"@eslint/js@9.11.1":
|
||||||
version "9.11.0"
|
version "9.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.11.0.tgz#fca7533ef33aa608770734786e02f1041847f9bb"
|
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.11.1.tgz#8bcb37436f9854b3d9a561440daf916acd940986"
|
||||||
integrity sha512-LPkkenkDqyzTFauZLLAPhIb48fj6drrfMvRGSL9tS3AcZBSVTllemLSNyCvHNNL2t797S/6DJNSIwRwXgMO/eQ==
|
integrity sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==
|
||||||
|
|
||||||
"@eslint/object-schema@^2.1.4":
|
"@eslint/object-schema@^2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
@ -487,15 +492,25 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
|
||||||
integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
|
integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
|
||||||
|
|
||||||
|
"@types/estree@^1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||||
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
"@types/json-schema@^7.0.12":
|
"@types/json-schema@^7.0.12":
|
||||||
version "7.0.12"
|
version "7.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||||
|
|
||||||
"@types/node@^22.5.5":
|
"@types/json-schema@^7.0.15":
|
||||||
version "22.5.5"
|
version "7.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.5.tgz#52f939dd0f65fc552a4ad0b392f3c466cc5d7a44"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==
|
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||||
|
|
||||||
|
"@types/node@^22.7.2":
|
||||||
|
version "22.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.2.tgz#80ed66c0a5025ffa037587fd69a816f29b54e4c7"
|
||||||
|
integrity sha512-866lXSrpGpgyHBZUa2m9YNWqHDjjM0aBTJlNtYaGEw4rqY/dcD7deRVTbBBAJelfA7oaGDbNftXF/TL/A6RgoA==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.19.2"
|
undici-types "~6.19.2"
|
||||||
|
|
||||||
@ -652,13 +667,13 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
"@vue/compiler-core@3.5.8":
|
"@vue/compiler-core@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.8.tgz#03ee4a2fa022c9bc3e59f789a1e14593b1e95b10"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.9.tgz#d51fbfe6c18479b27fe6b1723344ba0832e4aacb"
|
||||||
integrity sha512-Uzlxp91EPjfbpeO5KtC0KnXPkuTfGsNDeaKQJxQN718uz+RqDYarEf7UhQJGK+ZYloD2taUbHTI2J4WrUaZQNA==
|
integrity sha512-KE1sCdwqSKq0CQ/ltg3XnlMTKeinjegIkuFsuq9DKvNPmqLGdmI51ChZdGBBRXIvEYTLm8X/JxOuBQ1HqF/+PA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.25.3"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
entities "^4.5.0"
|
entities "^4.5.0"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.2.0"
|
source-map-js "^1.2.0"
|
||||||
@ -671,13 +686,13 @@
|
|||||||
"@vue/compiler-core" "3.2.47"
|
"@vue/compiler-core" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-dom@3.5.8":
|
"@vue/compiler-dom@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.8.tgz#03e4a6bef00a1979613a1db2ab39e9b2dced3373"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.9.tgz#6fa2b7e536ae4c416fc2d60b7e9e33b3410eac7a"
|
||||||
integrity sha512-GUNHWvoDSbSa5ZSHT9SnV5WkStWfzJwwTd6NMGzilOE/HM5j+9EB9zGXdtu/fCNEmctBqMs6C9SvVPpVPuk1Eg==
|
integrity sha512-gEAURwPo902AsJF50vl59VaWR+Cx6cX9SoqLYHu1jq9hDbmQlXvpZyYNIIbxa2JTJ+FD/oBQweVUwuTQv79KTg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-core" "3.5.8"
|
"@vue/compiler-core" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
"@vue/compiler-dom@^3.4.0":
|
"@vue/compiler-dom@^3.4.0":
|
||||||
version "3.4.21"
|
version "3.4.21"
|
||||||
@ -687,16 +702,16 @@
|
|||||||
"@vue/compiler-core" "3.4.21"
|
"@vue/compiler-core" "3.4.21"
|
||||||
"@vue/shared" "3.4.21"
|
"@vue/shared" "3.4.21"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.5.8":
|
"@vue/compiler-sfc@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.8.tgz#b2091ec01c63ab02a1cd6783224322f245c6a308"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.9.tgz#020b7654f1fde7c606a49ec4e4d2838e8e1a43c5"
|
||||||
integrity sha512-taYpngQtSysrvO9GULaOSwcG5q821zCoIQBtQQSx7Uf7DxpR6CIHR90toPr9QfDD2mqHQPCSgoWBvJu0yV9zjg==
|
integrity sha512-kp9qawcTXakYm0TN6YAwH24IurSywoXh4fWhRbLu0at4UVyo994bhEzJlQn82eiyqtut4GjkQodSfn8drFbpZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.25.3"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/compiler-core" "3.5.8"
|
"@vue/compiler-core" "3.5.9"
|
||||||
"@vue/compiler-dom" "3.5.8"
|
"@vue/compiler-dom" "3.5.9"
|
||||||
"@vue/compiler-ssr" "3.5.8"
|
"@vue/compiler-ssr" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.30.11"
|
magic-string "^0.30.11"
|
||||||
postcss "^8.4.47"
|
postcss "^8.4.47"
|
||||||
@ -726,13 +741,13 @@
|
|||||||
"@vue/compiler-dom" "3.2.47"
|
"@vue/compiler-dom" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-ssr@3.5.8":
|
"@vue/compiler-ssr@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.8.tgz#fbad34f8bbed15aa6e7b9d78324d93af93403145"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.9.tgz#e30f8e866589392421abcbfc0e0241470f3ca9a6"
|
||||||
integrity sha512-W96PtryNsNG9u0ZnN5Q5j27Z/feGrFV6zy9q5tzJVyJaLiwYxvC0ek4IXClZygyhjm+XKM7WD9pdKi/wIRVC/Q==
|
integrity sha512-fb1g2mQv32QzIei76rlXRTz08Grw+ZzBXSQfHo4StGFutm/flyebw3dGJkexKwcU3GjX9s5fIGjEv/cjO8j8Yw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.5.8"
|
"@vue/compiler-dom" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
"@vue/compiler-vue2@^2.7.16":
|
"@vue/compiler-vue2@^2.7.16":
|
||||||
version "2.7.16"
|
version "2.7.16"
|
||||||
@ -786,38 +801,38 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.25.7"
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
"@vue/reactivity@3.5.8":
|
"@vue/reactivity@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.8.tgz#23e1bceceb9b94b136fa91f11b308e3f712dea6d"
|
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.9.tgz#8864a55e4c495666f3c679beb8f734489eeb042e"
|
||||||
integrity sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==
|
integrity sha512-88ApgNZ6yPYpyYkTfXzcbWk6O8+LrPRIpa/U4AdeTzpfRUO+EUt5jemnTBVSlAUNmlYY96xa5feUNEq+BouLog==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
"@vue/runtime-core@3.5.8":
|
"@vue/runtime-core@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.8.tgz#235251fa40dae61db7becacf6bda5bc6561cbbc5"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.9.tgz#e47f890734039f77dac86328cc059cf8188c5729"
|
||||||
integrity sha512-fJuPelh64agZ8vKkZgp5iCkPaEqFJsYzxLk9vSC0X3G8ppknclNDr61gDc45yBGTaN5Xqc1qZWU3/NoaBMHcjQ==
|
integrity sha512-YAeP0zNkjSl5mEc1NxOg9qoAhLNbREElHAhfYbMXT57oF0ixehEEJWBhg2uvVxslCGh23JhpEAyMvJrJHW9WGg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.5.8"
|
"@vue/reactivity" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
"@vue/runtime-dom@3.5.8":
|
"@vue/runtime-dom@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.8.tgz#9d3a4f4a9a9a0002b085a5e18a2ca16c009cb3ad"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.9.tgz#088746207f74963d09b31ce7b79add0bf96aa337"
|
||||||
integrity sha512-DpAUz+PKjTZPUOB6zJgkxVI3GuYc2iWZiNeeHQUw53kdrparSTG6HeXUrYDjaam8dVsCdvQxDz6ZWxnyjccUjQ==
|
integrity sha512-5Oq/5oenpB9lw94moKvOHqBDEaMSyDmcu2HS8AtAT6/pwdo/t9fR9aVtLh6FzYGGqZR9yRfoHAN6P7goblq1aA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.5.8"
|
"@vue/reactivity" "3.5.9"
|
||||||
"@vue/runtime-core" "3.5.8"
|
"@vue/runtime-core" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
|
|
||||||
"@vue/server-renderer@3.5.8":
|
"@vue/server-renderer@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.8.tgz#d6c292409e880db4151223c27fa0d1cd879cc239"
|
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.9.tgz#3bf0736001623960d120ef01dee5045fad6efadb"
|
||||||
integrity sha512-7AmC9/mEeV9mmXNVyUIm1a1AjUhyeeGNbkLh39J00E7iPeGks8OGRB5blJiMmvqSh8SkaS7jkLWSpXtxUCeagA==
|
integrity sha512-tbuUsZfMWGazR9LXLNiiDSTwkO8K9sLyR70diY+FbQmKmh7236PPz4jkTxymelV8D89IJUGtbfe4VdmpHkmuxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-ssr" "3.5.8"
|
"@vue/compiler-ssr" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
"@vue/shared@3.2.47":
|
"@vue/shared@3.2.47":
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
@ -829,10 +844,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1"
|
||||||
integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==
|
integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==
|
||||||
|
|
||||||
"@vue/shared@3.5.8":
|
"@vue/shared@3.5.9":
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.8.tgz#6ef14933872dcc4f7b79fee3aaecf648ff807fed"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.9.tgz#713257216ea2cbf4e200cb9ae395c34ae2349385"
|
||||||
integrity sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==
|
integrity sha512-8wiT/m0mnsLhTME0mPgc57jv+4TipRBSAAmheUdYgiOaO6AobZPNOmm87ub4np65VVDgLcWxc+Edc++5Wyz1uA==
|
||||||
|
|
||||||
"@vue/tsconfig@^0.5.1":
|
"@vue/tsconfig@^0.5.1":
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
@ -1265,20 +1280,23 @@ eslint-visitor-keys@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
||||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
||||||
|
|
||||||
eslint@^9.11.0:
|
eslint@^9.11.1:
|
||||||
version "9.11.0"
|
version "9.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.11.0.tgz#f7a7bf305a4d77f23be0c1e4537b9aa1617219be"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.11.1.tgz#701e5fc528990153f9cef696d8427003b5206567"
|
||||||
integrity sha512-yVS6XODx+tMFMDFcG4+Hlh+qG7RM6cCJXtQhCKLSsr3XkLvWggHjCqjfh0XsPPnt1c56oaT6PMgW9XWQQjdHXA==
|
integrity sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.2.0"
|
"@eslint-community/eslint-utils" "^4.2.0"
|
||||||
"@eslint-community/regexpp" "^4.11.0"
|
"@eslint-community/regexpp" "^4.11.0"
|
||||||
"@eslint/config-array" "^0.18.0"
|
"@eslint/config-array" "^0.18.0"
|
||||||
|
"@eslint/core" "^0.6.0"
|
||||||
"@eslint/eslintrc" "^3.1.0"
|
"@eslint/eslintrc" "^3.1.0"
|
||||||
"@eslint/js" "9.11.0"
|
"@eslint/js" "9.11.1"
|
||||||
"@eslint/plugin-kit" "^0.2.0"
|
"@eslint/plugin-kit" "^0.2.0"
|
||||||
"@humanwhocodes/module-importer" "^1.0.1"
|
"@humanwhocodes/module-importer" "^1.0.1"
|
||||||
"@humanwhocodes/retry" "^0.3.0"
|
"@humanwhocodes/retry" "^0.3.0"
|
||||||
"@nodelib/fs.walk" "^1.2.8"
|
"@nodelib/fs.walk" "^1.2.8"
|
||||||
|
"@types/estree" "^1.0.6"
|
||||||
|
"@types/json-schema" "^7.0.15"
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
@ -2488,10 +2506,10 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
terser@^5.33.0:
|
terser@^5.34.0:
|
||||||
version "5.33.0"
|
version "5.34.0"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.33.0.tgz#8f9149538c7468ffcb1246cfec603c16720d2db1"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.0.tgz#62f2496542290bc6d8bf886edaee7fac158e37e4"
|
||||||
integrity sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==
|
integrity sha512-y5NUX+U9HhVsK/zihZwoq4r9dICLyV2jXGOriDAVOeKhq3LKVjgJbGO90FisozXLlJfvjHqgckGmJFBb9KYoWQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/source-map" "^0.3.3"
|
"@jridgewell/source-map" "^0.3.3"
|
||||||
acorn "^8.8.2"
|
acorn "^8.8.2"
|
||||||
@ -2606,10 +2624,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"
|
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==
|
integrity sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ==
|
||||||
|
|
||||||
vite@^5.4.7:
|
vite@^5.4.8:
|
||||||
version "5.4.7"
|
version "5.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.7.tgz#d226f57c08b61379e955f3836253ed3efb2dcf00"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8"
|
||||||
integrity sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==
|
integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.21.3"
|
esbuild "^0.21.3"
|
||||||
postcss "^8.4.43"
|
postcss "^8.4.43"
|
||||||
@ -2673,16 +2691,16 @@ vue-tsc@^2.1.6:
|
|||||||
"@vue/language-core" "2.1.6"
|
"@vue/language-core" "2.1.6"
|
||||||
semver "^7.5.4"
|
semver "^7.5.4"
|
||||||
|
|
||||||
vue@^3.5.8:
|
vue@^3.5.9:
|
||||||
version "3.5.8"
|
version "3.5.9"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.8.tgz#7d2fa98ea85228dcb90f897ef5df74df1d5825a1"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.9.tgz#a065952d7a7c0e2cbfec8e016582b055ab984357"
|
||||||
integrity sha512-hvuvuCy51nP/1fSRvrrIqTLSvrSyz2Pq+KQ8S8SXCxTWVE0nMaOnSDnSOxV1eYmGfvK7mqiwvd1C59CEEz7dAQ==
|
integrity sha512-nHzQhZ5cjFKynAY2beAm7XtJ5C13VKAFTLTgRYXy+Id1KEKBeiK6hO2RcW1hUjdbHMadz1YzxyHgQigOC54wug==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.5.8"
|
"@vue/compiler-dom" "3.5.9"
|
||||||
"@vue/compiler-sfc" "3.5.8"
|
"@vue/compiler-sfc" "3.5.9"
|
||||||
"@vue/runtime-dom" "3.5.8"
|
"@vue/runtime-dom" "3.5.9"
|
||||||
"@vue/server-renderer" "3.5.8"
|
"@vue/server-renderer" "3.5.9"
|
||||||
"@vue/shared" "3.5.8"
|
"@vue/shared" "3.5.9"
|
||||||
|
|
||||||
webpack-sources@^3.2.3:
|
webpack-sources@^3.2.3:
|
||||||
version "3.2.3"
|
version "3.2.3"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user