inital version of full solar passthrough

Webinterface change to set full solar passthrough values

Adding webapi and config changes to enable full solar passthrough over certain battery Soc

inital version of full solar passthrough in power limiter

Passthrough mode can be enabled via MQTT

translations

re-enable comment

remove unused variable
This commit is contained in:
MalteSchm 2023-04-23 11:26:02 +02:00 committed by helgeerbe
commit e7c8a89bd3
36 changed files with 694 additions and 170 deletions

View File

@ -18,5 +18,30 @@
"type": 0, "type": 0,
"clk_mode": 0 "clk_mode": 0
} }
},
{
"name": "WT32-ETH01 with SSD1306",
"nrf24": {
"miso": 4,
"mosi": 2,
"clk": 32,
"irq": 33,
"en": 14,
"cs": 15
},
"eth": {
"enabled": true,
"phy_addr": 1,
"power": 16,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 0
},
"display": {
"type": 2,
"data": 5,
"clk": 17
}
} }
] ]

View File

@ -47,6 +47,7 @@ struct CHANNEL_CONFIG_T {
struct INVERTER_CONFIG_T { struct INVERTER_CONFIG_T {
uint64_t Serial; uint64_t Serial;
char Name[INV_MAX_NAME_STRLEN + 1]; char Name[INV_MAX_NAME_STRLEN + 1];
uint8_t Order;
bool Poll_Enable; bool Poll_Enable;
bool Poll_Enable_Night; bool Poll_Enable_Night;
bool Command_Enable; bool Command_Enable;
@ -82,6 +83,7 @@ struct CONFIG_T {
char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1]; char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
double Ntp_Longitude; double Ntp_Longitude;
double Ntp_Latitude; double Ntp_Latitude;
uint8_t Ntp_SunsetType;
bool Mqtt_Enabled; bool Mqtt_Enabled;
uint Mqtt_Port; uint Mqtt_Port;

View File

@ -2,6 +2,8 @@
#pragma once #pragma once
#include <TimeoutHelper.h> #include <TimeoutHelper.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
class DatastoreClass { class DatastoreClass {
public: public:
@ -10,52 +12,69 @@ public:
void loop(); void loop();
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included // Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
float totalAcYieldTotalEnabled = 0; float getTotalAcYieldTotalEnabled();
// Sum of yield day of all enabled inverters, a inverter which is just disabled at night is also included // Sum of yield day of all enabled inverters, a inverter which is just disabled at night is also included
float totalAcYieldDayEnabled = 0; float getTotalAcYieldDayEnabled();
// Sum of total AC power of all enabled inverters // Sum of total AC power of all enabled inverters
float totalAcPowerEnabled = 0; float getTotalAcPowerEnabled();
// Sum of total DC power of all enabled inverters // Sum of total DC power of all enabled inverters
float totalDcPowerEnabled = 0; float getTotalDcPowerEnabled();
// Sum of total DC power of all enabled inverters with maxStringPower set // Sum of total DC power of all enabled inverters with maxStringPower set
float totalDcPowerIrradiation = 0; float getTotalDcPowerIrradiation();
// Sum of total installed irradiation of all enabled inverters // Sum of total installed irradiation of all enabled inverters
float totalDcIrradiationInstalled = 0; float getTotalDcIrradiationInstalled();
// Percentage (1-100) of total irradiation // Percentage (1-100) of total irradiation
float totalDcIrradiation = 0; float getTotalDcIrradiation();
// Amount of relevant digits for yield total // Amount of relevant digits for yield total
unsigned int totalAcYieldTotalDigits = 0; unsigned int getTotalAcYieldTotalDigits();
// Amount of relevant digits for yield total // Amount of relevant digits for yield total
unsigned int totalAcYieldDayDigits = 0; unsigned int getTotalAcYieldDayDigits();
// Amount of relevant digits for AC power // Amount of relevant digits for AC power
unsigned int totalAcPowerDigits = 0; unsigned int getTotalAcPowerDigits();
// Amount of relevant digits for DC power // Amount of relevant digits for DC power
unsigned int totalDcPowerDigits = 0; unsigned int getTotalDcPowerDigits();
// True, if at least one inverter is reachable // True, if at least one inverter is reachable
bool isAtLeastOneReachable = false; bool getIsAtLeastOneReachable();
// True if at least one inverter is producing // True if at least one inverter is producing
bool isAtLeastOneProducing = false; bool getIsAtLeastOneProducing();
// True if all enabled inverters are producing // True if all enabled inverters are producing
bool isAllEnabledProducing = false; bool getIsAllEnabledProducing();
// True if all enabled inverters are reachable // True if all enabled inverters are reachable
bool isAllEnabledReachable = false; bool getIsAllEnabledReachable();
private: private:
TimeoutHelper _updateTimeout; TimeoutHelper _updateTimeout;
SemaphoreHandle_t _xSemaphore;
float _totalAcYieldTotalEnabled = 0;
float _totalAcYieldDayEnabled = 0;
float _totalAcPowerEnabled = 0;
float _totalDcPowerEnabled = 0;
float _totalDcPowerIrradiation = 0;
float _totalDcIrradiationInstalled = 0;
float _totalDcIrradiation = 0;
unsigned int _totalAcYieldTotalDigits = 0;
unsigned int _totalAcYieldDayDigits = 0;
unsigned int _totalAcPowerDigits = 0;
unsigned int _totalDcPowerDigits = 0;
bool _isAtLeastOneReachable = false;
bool _isAtLeastOneProducing = false;
bool _isAllEnabledProducing = false;
bool _isAllEnabledReachable = false;
}; };
extern DatastoreClass Datastore; extern DatastoreClass Datastore;

View File

@ -12,6 +12,7 @@ public:
void loop(); void loop();
bool isDayPeriod(); bool isDayPeriod();
bool isSunsetAvailable();
bool sunsetTime(struct tm* info); bool sunsetTime(struct tm* info);
bool sunriseTime(struct tm* info); bool sunriseTime(struct tm* info);
@ -20,6 +21,7 @@ private:
SunSet _sun; SunSet _sun;
bool _isDayPeriod = true; bool _isDayPeriod = true;
bool _isSunsetAvailable = true;
uint _sunriseMinutes = 0; uint _sunriseMinutes = 0;
uint _sunsetMinutes = 0; uint _sunsetMinutes = 0;

View File

@ -28,6 +28,7 @@ enum WebApiError {
InverterInvalidMaxChannel, InverterInvalidMaxChannel,
InverterChanged, InverterChanged,
InverterDeleted, InverterDeleted,
InverterOrdered,
LimitBase = 5000, LimitBase = 5000,
LimitSerialZero, LimitSerialZero,

View File

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

View File

@ -25,6 +25,7 @@
#define NTP_TIMEZONEDESCR "Europe/Berlin" #define NTP_TIMEZONEDESCR "Europe/Berlin"
#define NTP_LONGITUDE 10.4515f #define NTP_LONGITUDE 10.4515f
#define NTP_LATITUDE 51.1657f #define NTP_LATITUDE 51.1657f
#define NTP_SUNSETTYPE 1
#define MQTT_ENABLED false #define MQTT_ENABLED false
#define MQTT_HOST "" #define MQTT_HOST ""

View File

@ -121,17 +121,25 @@ void HoymilesRadio_CMT::loop()
if (!_rxBuffer.empty()) { if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back(); fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) { if (checkFragmentCrc(&f)) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (nullptr != inv) { serial_u dtuId = convertSerialToRadioId(_dtuSerial);
// Save packet in inverter rx buffer
Hoymiles.getMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel));
dumpBuf(f.fragment, f.len, false);
Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi);
inv->addRxFragment(f.fragment, f.len); // The CMT RF module does not filter foreign packages by itself.
} else { // Has to be done manually here.
Hoymiles.getMessageOutput()->println("Inverter Not found!"); if (memcmp(&f.fragment[5], &dtuId.b[1], 4) == 0) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (nullptr != inv) {
// Save packet in inverter rx buffer
Hoymiles.getMessageOutput()->printf("RX %.2f MHz --> ", getFrequencyFromChannel(f.channel));
dumpBuf(f.fragment, f.len, false);
Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi);
inv->addRxFragment(f.fragment, f.len);
} else {
Hoymiles.getMessageOutput()->println("Inverter Not found!");
}
} }
} else { } else {

View File

@ -68,6 +68,11 @@ bool MultiDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fra
uint16_t crc = 0xffff, crcRcv = 0; uint16_t crc = 0xffff, crcRcv = 0;
for (uint8_t i = 0; i < max_fragment_id; i++) { for (uint8_t i = 0; i < max_fragment_id; i++) {
// Doublecheck if correct answer package
if (fragment[i].mainCmd != (_payload[0] | 0x80)) {
return false;
}
if (i == max_fragment_id - 1) { if (i == max_fragment_id - 1) {
// Last packet // Last packet
crc = crc16(fragment[i].fragment, fragment[i].len - 2, crc); crc = crc16(fragment[i].fragment, fragment[i].len - 2, crc);

View File

@ -6,9 +6,10 @@
#include "../Hoymiles.h" #include "../Hoymiles.h"
#include <cstring> #include <cstring>
const std::array<const AlarmMessage_t, 76> AlarmLogParser::_alarmMessages = {{ const std::array<const AlarmMessage_t, ALARM_MSG_COUNT> AlarmLogParser::_alarmMessages = {{
{ AlarmMessageType_t::ALL, 1, "Inverter start" }, { AlarmMessageType_t::ALL, 1, "Inverter start" },
{ AlarmMessageType_t::ALL, 2, "DTU command failed" }, { AlarmMessageType_t::ALL, 2, "DTU command failed" },
{ AlarmMessageType_t::ALL, 73, "Temperature >80°C" }, // https://github.com/tbnobody/OpenDTU/discussions/590#discussioncomment-6049750
{ AlarmMessageType_t::ALL, 121, "Over temperature protection" }, { AlarmMessageType_t::ALL, 121, "Over temperature protection" },
{ AlarmMessageType_t::ALL, 124, "Shut down by remote control" }, { AlarmMessageType_t::ALL, 124, "Shut down by remote control" },
{ AlarmMessageType_t::ALL, 125, "Grid configuration parameter error" }, { AlarmMessageType_t::ALL, 125, "Grid configuration parameter error" },

View File

@ -9,6 +9,8 @@
#define ALARM_LOG_ENTRY_SIZE 12 #define ALARM_LOG_ENTRY_SIZE 12
#define ALARM_LOG_PAYLOAD_SIZE (ALARM_LOG_ENTRY_COUNT * ALARM_LOG_ENTRY_SIZE + 4) #define ALARM_LOG_PAYLOAD_SIZE (ALARM_LOG_ENTRY_COUNT * ALARM_LOG_ENTRY_SIZE + 4)
#define ALARM_MSG_COUNT 77
struct AlarmLogEntry_t { struct AlarmLogEntry_t {
uint16_t MessageId; uint16_t MessageId;
String Message; String Message;
@ -50,5 +52,5 @@ private:
AlarmMessageType_t _messageType = AlarmMessageType_t::ALL; AlarmMessageType_t _messageType = AlarmMessageType_t::ALL;
static const std::array<const AlarmMessage_t, 76> _alarmMessages; static const std::array<const AlarmMessage_t, ALARM_MSG_COUNT> _alarmMessages;
}; };

View File

@ -32,7 +32,8 @@ const devInfo_t devInfo[] = {
{ { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450" }, // 01 { { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450" }, // 01
{ { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500" }, // 02 { { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500" }, // 02
{ { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600" }, // 01 { { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600" }, // 01
{ { 0x10, 0x21, 0x41, ALL }, 800, "HMS-800" }, // 00 { { 0x10, 0x21, 0x41, ALL }, 800, "HMS-800" }, // 00
{ { 0x10, 0x11, 0x51, ALL }, 900, "HMS-900" }, // 01
{ { 0x10, 0x21, 0x71, ALL }, 1000, "HMS-1000" }, // 05 { { 0x10, 0x21, 0x71, ALL }, 1000, "HMS-1000" }, // 05
{ { 0x10, 0x22, 0x41, ALL }, 1600, "HMS-1600" }, // 4 { { 0x10, 0x22, 0x41, ALL }, 1600, "HMS-1600" }, // 4
{ { 0x10, 0x12, 0x51, ALL }, 1800, "HMS-1800" }, // 01 { { 0x10, 0x12, 0x51, ALL }, 1800, "HMS-1800" }, // 01

View File

@ -0,0 +1,77 @@
# Part of ESPEasy build toolchain.
#
# Combines separate bin files with their respective offsets into a single file
# This single file must then be flashed to an ESP32 node with 0 offset.
#
# Original implementation: Bartłomiej Zimoń (@uzi18)
# Maintainer: Gijs Noorlander (@TD-er)
#
# Special thanks to @Jason2866 (Tasmota) for helping debug flashing to >4MB flash
# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin
#
# Typical layout of the generated file:
# Offset | File
# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin
# - 0x8000 | ~\ESPEasy\.pio\build\<env name>\partitions.bin
# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin
# - 0x10000 | ~\ESPEasy\.pio\build\<env name>/<built binary>.bin
Import("env")
platform = env.PioPlatform()
import sys
from os.path import join
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
import esptool
def esp32_create_combined_bin(source, target, env):
print("Generating combined binary for serial flashing")
# The offset from begin of the file where the app0 partition starts
# This is defined in the partition .csv file
app_offset = 0x10000
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
chip = env.get("BOARD_MCU")
flash_size = env.BoardConfig().get("upload.flash_size")
flash_freq = env.BoardConfig().get("build.f_flash", '40m')
flash_freq = flash_freq.replace('000000L', 'm')
flash_mode = env.BoardConfig().get("build.flash_mode", "dio")
memory_type = env.BoardConfig().get("build.arduino.memory_type", "qio_qspi")
if flash_mode == "qio" or flash_mode == "qout":
flash_mode = "dio"
if memory_type == "opi_opi" or memory_type == "opi_qspi":
flash_mode = "dout"
cmd = [
"--chip",
chip,
"merge_bin",
"-o",
new_file_name,
"--flash_mode",
flash_mode,
"--flash_freq",
flash_freq,
"--flash_size",
flash_size,
]
print(" Offset | File")
for section in sections:
sect_adr, sect_file = section.split(" ", 1)
print(f" - {sect_adr} | {sect_file}")
cmd += [sect_adr, sect_file]
print(f" - {hex(app_offset)} | {firmware_name}")
cmd += [hex(app_offset), firmware_name]
print('Using esptool.py arguments: %s' % ' '.join(cmd))
esptool.main(cmd)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)

View File

@ -29,7 +29,7 @@ build_unflags =
lib_deps = lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer https://github.com/yubox-node-org/ESPAsyncWebServer
bblanchon/ArduinoJson @ ^6.21.2 bblanchon/ArduinoJson @ ^6.21.2
https://github.com/bertmelis/espMqttClient.git#v1.4.2 https://github.com/bertmelis/espMqttClient.git#v1.4.3
nrf24/RF24 @ ^1.4.5 nrf24/RF24 @ ^1.4.5
olikraus/U8g2 @ ^2.34.17 olikraus/U8g2 @ ^2.34.17
buelowp/sunset @ ^1.1.7 buelowp/sunset @ ^1.1.7
@ -40,6 +40,7 @@ lib_deps =
extra_scripts = extra_scripts =
pre:pio-scripts/auto_firmware_version.py pre:pio-scripts/auto_firmware_version.py
pre:pio-scripts/patch_apply.py pre:pio-scripts/patch_apply.py
post:pio-scripts/create_factory_bin.py
board_build.partitions = partitions_custom.csv board_build.partitions = partitions_custom.csv
board_build.filesystem = littlefs board_build.filesystem = littlefs

View File

@ -46,6 +46,7 @@ bool ConfigurationClass::write()
ntp["timezone_descr"] = config.Ntp_TimezoneDescr; ntp["timezone_descr"] = config.Ntp_TimezoneDescr;
ntp["latitude"] = config.Ntp_Latitude; ntp["latitude"] = config.Ntp_Latitude;
ntp["longitude"] = config.Ntp_Longitude; ntp["longitude"] = config.Ntp_Longitude;
ntp["sunsettype"] = config.Ntp_SunsetType;
JsonObject mqtt = doc.createNestedObject("mqtt"); JsonObject mqtt = doc.createNestedObject("mqtt");
mqtt["enabled"] = config.Mqtt_Enabled; mqtt["enabled"] = config.Mqtt_Enabled;
@ -102,6 +103,7 @@ bool ConfigurationClass::write()
JsonObject inv = inverters.createNestedObject(); JsonObject inv = inverters.createNestedObject();
inv["serial"] = config.Inverter[i].Serial; inv["serial"] = config.Inverter[i].Serial;
inv["name"] = config.Inverter[i].Name; inv["name"] = config.Inverter[i].Name;
inv["order"] = config.Inverter[i].Order;
inv["poll_enable"] = config.Inverter[i].Poll_Enable; inv["poll_enable"] = config.Inverter[i].Poll_Enable;
inv["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night; inv["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night;
inv["command_enable"] = config.Inverter[i].Command_Enable; inv["command_enable"] = config.Inverter[i].Command_Enable;
@ -241,6 +243,7 @@ bool ConfigurationClass::read()
strlcpy(config.Ntp_TimezoneDescr, ntp["timezone_descr"] | NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr)); strlcpy(config.Ntp_TimezoneDescr, ntp["timezone_descr"] | NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr));
config.Ntp_Latitude = ntp["latitude"] | NTP_LATITUDE; config.Ntp_Latitude = ntp["latitude"] | NTP_LATITUDE;
config.Ntp_Longitude = ntp["longitude"] | NTP_LONGITUDE; config.Ntp_Longitude = ntp["longitude"] | NTP_LONGITUDE;
config.Ntp_SunsetType = ntp["sunsettype"] | NTP_SUNSETTYPE;
JsonObject mqtt = doc["mqtt"]; JsonObject mqtt = doc["mqtt"];
config.Mqtt_Enabled = mqtt["enabled"] | MQTT_ENABLED; config.Mqtt_Enabled = mqtt["enabled"] | MQTT_ENABLED;
@ -297,6 +300,7 @@ bool ConfigurationClass::read()
JsonObject inv = inverters[i].as<JsonObject>(); JsonObject inv = inverters[i].as<JsonObject>();
config.Inverter[i].Serial = inv["serial"] | 0ULL; config.Inverter[i].Serial = inv["serial"] | 0ULL;
strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name)); strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name));
config.Inverter[i].Order = inv["order"] | 0;
config.Inverter[i].Poll_Enable = inv["poll_enable"] | true; config.Inverter[i].Poll_Enable = inv["poll_enable"] | true;
config.Inverter[i].Poll_Enable_Night = inv["poll_enable_night"] | true; config.Inverter[i].Poll_Enable_Night = inv["poll_enable_night"] | true;

View File

@ -6,10 +6,17 @@
#include "Configuration.h" #include "Configuration.h"
#include <Hoymiles.h> #include <Hoymiles.h>
#define DAT_SEMAPHORE_TAKE() \
do { \
} while (xSemaphoreTake(_xSemaphore, portMAX_DELAY) != pdPASS)
#define DAT_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
DatastoreClass Datastore; DatastoreClass Datastore;
DatastoreClass::DatastoreClass() DatastoreClass::DatastoreClass()
{ {
_xSemaphore = xSemaphoreCreateMutex();
DAT_SEMAPHORE_GIVE(); // release before first use
} }
void DatastoreClass::init() void DatastoreClass::init()
@ -24,23 +31,25 @@ void DatastoreClass::loop()
uint8_t isProducing = 0; uint8_t isProducing = 0;
uint8_t isReachable = 0; uint8_t isReachable = 0;
totalAcYieldTotalEnabled = 0; DAT_SEMAPHORE_TAKE();
totalAcYieldTotalDigits = 0;
totalAcYieldDayEnabled = 0; _totalAcYieldTotalEnabled = 0;
totalAcYieldDayDigits = 0; _totalAcYieldTotalDigits = 0;
totalAcPowerEnabled = 0; _totalAcYieldDayEnabled = 0;
totalAcPowerDigits = 0; _totalAcYieldDayDigits = 0;
totalDcPowerEnabled = 0; _totalAcPowerEnabled = 0;
totalDcPowerDigits = 0; _totalAcPowerDigits = 0;
totalDcPowerIrradiation = 0; _totalDcPowerEnabled = 0;
totalDcIrradiationInstalled = 0; _totalDcPowerDigits = 0;
isAllEnabledProducing = true; _totalDcPowerIrradiation = 0;
isAllEnabledReachable = true; _totalDcIrradiationInstalled = 0;
_isAllEnabledProducing = true;
_isAllEnabledReachable = true;
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);
@ -57,7 +66,7 @@ void DatastoreClass::loop()
isProducing++; isProducing++;
} else { } else {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
isAllEnabledProducing = false; _isAllEnabledProducing = false;
} }
} }
@ -65,42 +74,164 @@ void DatastoreClass::loop()
isReachable++; isReachable++;
} else { } else {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
isAllEnabledReachable = false; _isAllEnabledReachable = false;
} }
} }
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) { for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
if (cfg->Poll_Enable) { if (cfg->Poll_Enable) {
totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalAcYieldTotalDigits = max<unsigned int>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); _totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
totalAcYieldDayDigits = max<unsigned int>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); _totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
} }
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); _totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<unsigned int>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); _totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
} }
} }
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) { for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); _totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<unsigned int>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC)); _totalDcPowerDigits = max<unsigned int>(_totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));
if (inv->Statistics()->getStringMaxPower(c) > 0) { if (inv->Statistics()->getStringMaxPower(c) > 0) {
totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); _totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c); _totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c);
} }
} }
} }
} }
isAtLeastOneProducing = isProducing > 0; _isAtLeastOneProducing = isProducing > 0;
isAtLeastOneReachable = isReachable > 0; _isAtLeastOneReachable = isReachable > 0;
totalDcIrradiation = totalDcIrradiationInstalled > 0 ? totalDcPowerIrradiation / totalDcIrradiationInstalled * 100.0f : 0; _totalDcIrradiation = _totalDcIrradiationInstalled > 0 ? _totalDcPowerIrradiation / _totalDcIrradiationInstalled * 100.0f : 0;
DAT_SEMAPHORE_GIVE();
_updateTimeout.reset(); _updateTimeout.reset();
} }
} }
float DatastoreClass::getTotalAcYieldTotalEnabled()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalAcYieldTotalEnabled;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalAcYieldDayEnabled()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalAcYieldDayEnabled;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalAcPowerEnabled()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalAcPowerEnabled;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalDcPowerEnabled()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalDcPowerEnabled;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalDcPowerIrradiation()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalDcPowerIrradiation;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalDcIrradiationInstalled()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalDcIrradiationInstalled;
DAT_SEMAPHORE_GIVE();
return retval;
}
float DatastoreClass::getTotalDcIrradiation()
{
DAT_SEMAPHORE_TAKE();
float retval = _totalDcIrradiation;
DAT_SEMAPHORE_GIVE();
return retval;
}
unsigned int DatastoreClass::getTotalAcYieldTotalDigits()
{
DAT_SEMAPHORE_TAKE();
unsigned int retval = _totalAcYieldTotalDigits;
DAT_SEMAPHORE_GIVE();
return retval;
}
unsigned int DatastoreClass::getTotalAcYieldDayDigits()
{
DAT_SEMAPHORE_TAKE();
unsigned int retval = _totalAcYieldDayDigits;
DAT_SEMAPHORE_GIVE();
return retval;
}
unsigned int DatastoreClass::getTotalAcPowerDigits()
{
DAT_SEMAPHORE_TAKE();
unsigned int retval = _totalAcPowerDigits;
DAT_SEMAPHORE_GIVE();
return retval;
}
unsigned int DatastoreClass::getTotalDcPowerDigits()
{
DAT_SEMAPHORE_TAKE();
unsigned int retval = _totalDcPowerDigits;
DAT_SEMAPHORE_GIVE();
return retval;
}
bool DatastoreClass::getIsAtLeastOneReachable()
{
DAT_SEMAPHORE_TAKE();
bool retval = _isAtLeastOneReachable;
DAT_SEMAPHORE_GIVE();
return retval;
}
bool DatastoreClass::getIsAtLeastOneProducing()
{
DAT_SEMAPHORE_TAKE();
bool retval = _isAtLeastOneProducing;
DAT_SEMAPHORE_GIVE();
return retval;
}
bool DatastoreClass::getIsAllEnabledProducing()
{
DAT_SEMAPHORE_TAKE();
bool retval = _isAllEnabledProducing;
DAT_SEMAPHORE_GIVE();
return retval;
}
bool DatastoreClass::getIsAllEnabledReachable()
{
DAT_SEMAPHORE_TAKE();
bool retval = _isAllEnabledReachable;
DAT_SEMAPHORE_GIVE();
return retval;
}

View File

@ -141,12 +141,12 @@ void DisplayGraphicClass::loop()
_display->clearBuffer(); _display->clearBuffer();
//=====> Actual Production ========== //=====> Actual Production ==========
if (Datastore.isAtLeastOneReachable) { if (Datastore.getIsAtLeastOneReachable()) {
_display->setPowerSave(false); _display->setPowerSave(false);
if (Datastore.totalAcPowerEnabled > 999) { if (Datastore.getTotalAcPowerEnabled() > 999) {
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.totalAcPowerEnabled / 1000)); snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000));
} else { } else {
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.totalAcPowerEnabled); snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.getTotalAcPowerEnabled());
} }
printText(_fmtText, 0); printText(_fmtText, 0);
_previousMillis = millis(); _previousMillis = millis();
@ -164,10 +164,10 @@ void DisplayGraphicClass::loop()
//<======================= //<=======================
//=====> Today & Total Production ======= //=====> Today & Total Production =======
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.totalAcYieldDayEnabled); snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
printText(_fmtText, 1); printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.totalAcYieldTotalEnabled); snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled());
printText(_fmtText, 2); printText(_fmtText, 2);
//<======================= //<=======================

View File

@ -59,10 +59,10 @@ void LedSingleClass::loop()
_ledState[1] = LedState_t::Off; _ledState[1] = LedState_t::Off;
if (Hoymiles.getNumInverters()) { if (Hoymiles.getNumInverters()) {
// set LED status // set LED status
if (Datastore.isAllEnabledReachable && Datastore.isAllEnabledProducing) { if (Datastore.getIsAllEnabledReachable() && Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::On; _ledState[1] = LedState_t::On;
} }
if (Datastore.isAllEnabledReachable && !Datastore.isAllEnabledProducing) { if (Datastore.getIsAllEnabledReachable() && !Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::Blink; _ledState[1] = LedState_t::Blink;
} }
} }

View File

@ -22,13 +22,13 @@ void MqttHandleInverterTotalClass::loop()
} }
if (_lastPublish.occured()) { if (_lastPublish.occured()) {
MqttSettings.publish("ac/power", String(Datastore.totalAcPowerEnabled, Datastore.totalAcPowerDigits)); MqttSettings.publish("ac/power", String(Datastore.getTotalAcPowerEnabled(), Datastore.getTotalAcPowerDigits()));
MqttSettings.publish("ac/yieldtotal", String(Datastore.totalAcYieldTotalEnabled, Datastore.totalAcYieldTotalDigits)); MqttSettings.publish("ac/yieldtotal", String(Datastore.getTotalAcYieldTotalEnabled(), Datastore.getTotalAcYieldTotalDigits()));
MqttSettings.publish("ac/yieldday", String(Datastore.totalAcYieldDayEnabled, Datastore.totalAcYieldDayDigits)); MqttSettings.publish("ac/yieldday", String(Datastore.getTotalAcYieldDayEnabled(), Datastore.getTotalAcYieldDayDigits()));
MqttSettings.publish("ac/is_valid", String(Datastore.isAllEnabledReachable)); MqttSettings.publish("ac/is_valid", String(Datastore.getIsAllEnabledReachable()));
MqttSettings.publish("dc/power", String(Datastore.totalDcPowerEnabled, Datastore.totalDcPowerDigits)); MqttSettings.publish("dc/power", String(Datastore.getTotalDcPowerEnabled(), Datastore.getTotalDcPowerDigits()));
MqttSettings.publish("dc/irradiation", String(Datastore.totalDcIrradiation, 3)); MqttSettings.publish("dc/irradiation", String(Datastore.getTotalDcIrradiation(), 3));
MqttSettings.publish("dc/is_valid", String(Datastore.isAllEnabledReachable)); MqttSettings.publish("dc/is_valid", String(Datastore.getIsAllEnabledReachable()));
_lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000); _lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000);
} }

View File

@ -29,6 +29,11 @@ bool SunPositionClass::isDayPeriod()
return _isDayPeriod; return _isDayPeriod;
} }
bool SunPositionClass::isSunsetAvailable()
{
return _isSunsetAvailable;
}
void SunPositionClass::updateSunData() void SunPositionClass::updateSunData()
{ {
CONFIG_T const& config = Configuration.get(); CONFIG_T const& config = Configuration.get();
@ -37,7 +42,7 @@ void SunPositionClass::updateSunData()
struct tm timeinfo; struct tm timeinfo;
if (!getLocalTime(&timeinfo, 5)) { if (!getLocalTime(&timeinfo, 5)) {
_isDayPeriod = false; _isDayPeriod = true;
_sunriseMinutes = 0; _sunriseMinutes = 0;
_sunsetMinutes = 0; _sunsetMinutes = 0;
_isValidInfo = false; _isValidInfo = false;
@ -45,11 +50,43 @@ void SunPositionClass::updateSunData()
} }
_sun.setCurrentDate(1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday); _sun.setCurrentDate(1900 + timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday);
_sunriseMinutes = static_cast<int>(_sun.calcCustomSunrise(SunSet::SUNSET_NAUTICAL));
_sunsetMinutes = static_cast<int>(_sun.calcCustomSunset(SunSet::SUNSET_NAUTICAL)); double sunset_type;
switch (config.Ntp_SunsetType) {
case 0:
sunset_type = SunSet::SUNSET_OFFICIAL;
break;
case 2:
sunset_type = SunSet::SUNSET_CIVIL;
break;
case 3:
sunset_type = SunSet::SUNSET_ASTONOMICAL;
break;
default:
sunset_type = SunSet::SUNSET_NAUTICAL;
break;
}
double sunriseRaw = _sun.calcCustomSunrise(sunset_type);
double sunsetRaw = _sun.calcCustomSunset(sunset_type);
// If no sunset/sunrise exists (e.g. astronomical calculation in summer)
// assume it's day period
if (std::isnan(sunriseRaw) || std::isnan(sunsetRaw)) {
_isDayPeriod = true;
_isSunsetAvailable = false;
_sunriseMinutes = 0;
_sunsetMinutes = 0;
_isValidInfo = false;
return;
}
_sunriseMinutes = static_cast<int>(sunriseRaw);
_sunsetMinutes = static_cast<int>(sunsetRaw);
uint minutesPastMidnight = timeinfo.tm_hour * 60 + timeinfo.tm_min; uint minutesPastMidnight = timeinfo.tm_hour * 60 + timeinfo.tm_min;
_isDayPeriod = (minutesPastMidnight >= _sunriseMinutes) && (minutesPastMidnight < _sunsetMinutes); _isDayPeriod = (minutesPastMidnight >= _sunriseMinutes) && (minutesPastMidnight < _sunsetMinutes);
_isSunsetAvailable = true;
_isValidInfo = true; _isValidInfo = true;
} }

View File

@ -21,6 +21,7 @@ void WebApiInverterClass::init(AsyncWebServer* server)
_server->on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1)); _server->on("/api/inverter/add", HTTP_POST, std::bind(&WebApiInverterClass::onInverterAdd, this, _1));
_server->on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1)); _server->on("/api/inverter/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));
} }
void WebApiInverterClass::loop() void WebApiInverterClass::loop()
@ -44,6 +45,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
JsonObject obj = data.createNestedObject(); JsonObject obj = data.createNestedObject();
obj["id"] = i; obj["id"] = i;
obj["name"] = String(config.Inverter[i].Name); obj["name"] = String(config.Inverter[i].Name);
obj["order"] = config.Inverter[i].Order;
// Inverter Serial is read as HEX // Inverter Serial is read as HEX
char buffer[sizeof(uint64_t) * 8 + 1]; char buffer[sizeof(uint64_t) * 8 + 1];
@ -390,3 +392,72 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
MqttHandleHass.forceUpdate(); MqttHandleHass.forceUpdate();
} }
void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentials(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
retMsg["message"] = "No values found!";
retMsg["code"] = WebApiError::GenericNoValueFound;
response->setLength();
request->send(response);
return;
}
String json = request->getParam("data", true)->value();
if (json.length() > 1024) {
retMsg["message"] = "Data too large!";
retMsg["code"] = WebApiError::GenericDataTooLarge;
response->setLength();
request->send(response);
return;
}
DynamicJsonDocument root(1024);
DeserializationError error = deserializeJson(root, json);
if (error) {
retMsg["message"] = "Failed to parse data!";
retMsg["code"] = WebApiError::GenericParseError;
response->setLength();
request->send(response);
return;
}
if (!(root.containsKey("order"))) {
retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
return;
}
// The order array contains list or id in the right order
JsonArray orderArray = root["order"].as<JsonArray>();
uint8_t order = 0;
for(JsonVariant id : orderArray) {
uint8_t inverter_id = id.as<uint8_t>();
if (inverter_id < INV_MAX_COUNT) {
INVERTER_CONFIG_T& inverter = Configuration.get().Inverter[inverter_id];
inverter.Order = order;
}
order++;
}
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Inverter order saved!";
retMsg["code"] = WebApiError::InverterOrdered;
response->setLength();
request->send(response);
}

View File

@ -52,14 +52,21 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
root["ntp_localtime"] = timeStringBuff; root["ntp_localtime"] = timeStringBuff;
SunPosition.sunriseTime(&timeinfo); if (SunPosition.sunriseTime(&timeinfo)) {
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
} else {
strcpy(timeStringBuff, "--");
}
root["sun_risetime"] = timeStringBuff; root["sun_risetime"] = timeStringBuff;
SunPosition.sunsetTime(&timeinfo); if (SunPosition.sunsetTime(&timeinfo)) {
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo); strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
} else {
strcpy(timeStringBuff, "--");
}
root["sun_settime"] = timeStringBuff; root["sun_settime"] = timeStringBuff;
root["sun_isSunsetAvailable"] = SunPosition.isSunsetAvailable();
root["sun_isDayPeriod"] = SunPosition.isDayPeriod(); root["sun_isDayPeriod"] = SunPosition.isDayPeriod();
response->setLength(); response->setLength();
@ -81,6 +88,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
root["ntp_timezone_descr"] = config.Ntp_TimezoneDescr; root["ntp_timezone_descr"] = config.Ntp_TimezoneDescr;
root["longitude"] = config.Ntp_Longitude; root["longitude"] = config.Ntp_Longitude;
root["latitude"] = config.Ntp_Latitude; root["latitude"] = config.Ntp_Latitude;
root["sunsettype"] = config.Ntp_SunsetType;
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -125,7 +133,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
return; return;
} }
if (!(root.containsKey("ntp_server") && root.containsKey("ntp_timezone") && root.containsKey("longitude") && root.containsKey("latitude"))) { if (!(root.containsKey("ntp_server") && root.containsKey("ntp_timezone") && root.containsKey("longitude") && root.containsKey("latitude") && root.containsKey("sunsettype"))) {
retMsg["message"] = "Values are missing!"; retMsg["message"] = "Values are missing!";
retMsg["code"] = WebApiError::GenericValueMissing; retMsg["code"] = WebApiError::GenericValueMissing;
response->setLength(); response->setLength();
@ -166,6 +174,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
strlcpy(config.Ntp_TimezoneDescr, root["ntp_timezone_descr"].as<String>().c_str(), sizeof(config.Ntp_TimezoneDescr)); strlcpy(config.Ntp_TimezoneDescr, root["ntp_timezone_descr"].as<String>().c_str(), sizeof(config.Ntp_TimezoneDescr));
config.Ntp_Latitude = root["latitude"].as<double>(); config.Ntp_Latitude = root["latitude"].as<double>();
config.Ntp_Longitude = root["longitude"].as<double>(); config.Ntp_Longitude = root["longitude"].as<double>();
config.Ntp_SunsetType = root["sunsettype"].as<uint8_t>();
Configuration.write(); Configuration.write();
retMsg["type"] = "success"; retMsg["type"] = "success";

View File

@ -105,9 +105,14 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} }
JsonObject invObject = invArray.createNestedObject(); JsonObject invObject = invArray.createNestedObject();
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
if (inv_cfg == nullptr) {
continue;
}
invObject["serial"] = inv->serialString(); invObject["serial"] = inv->serialString();
invObject["name"] = inv->name(); invObject["name"] = inv->name();
invObject["order"] = inv_cfg->Order;
invObject["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000; invObject["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
invObject["poll_enabled"] = inv->getEnablePolling(); invObject["poll_enabled"] = inv->getEnablePolling();
invObject["reachable"] = inv->isReachable(); invObject["reachable"] = inv->isReachable();
@ -124,10 +129,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t)); JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t));
for (auto& c : inv->Statistics()->getChannelsByType(t)) { for (auto& c : inv->Statistics()->getChannelsByType(t)) {
if (t == TYPE_DC) { if (t == TYPE_DC) {
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial()); chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
if (inv_cfg != nullptr) {
chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
}
} }
addField(chanTypeObj, i, inv, t, c, FLD_PAC); addField(chanTypeObj, i, inv, t, c, FLD_PAC);
addField(chanTypeObj, i, inv, t, c, FLD_UAC); addField(chanTypeObj, i, inv, t, c, FLD_UAC);
@ -164,9 +166,9 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} }
JsonObject totalObj = root.createNestedObject("total"); JsonObject totalObj = root.createNestedObject("total");
addTotalField(totalObj, "Power", Datastore.totalAcPowerEnabled, "W", Datastore.totalAcPowerDigits); addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
addTotalField(totalObj, "YieldDay", Datastore.totalAcYieldDayEnabled, "Wh", Datastore.totalAcYieldDayDigits); addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
addTotalField(totalObj, "YieldTotal", Datastore.totalAcYieldTotalEnabled, "kWh", Datastore.totalAcYieldTotalDigits); addTotalField(totalObj, "YieldTotal", Datastore.getTotalAcYieldTotalEnabled(), "kWh", Datastore.getTotalAcYieldTotalDigits());
JsonObject hintObj = root.createNestedObject("hints"); JsonObject hintObj = root.createNestedObject("hints");
struct tm timeinfo; struct tm timeinfo;

View File

@ -11,32 +11,34 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.7", "@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.0-alpha3", "bootstrap": "^5.3.0",
"bootstrap-icons-vue": "^1.10.3", "bootstrap-icons-vue": "^1.10.3",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"sortablejs": "^1.15.0",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.2.1" "vue-router": "^4.2.2"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "^0.10.0", "@intlify/unplugin-vue-i18n": "^0.11.0",
"@rushstack/eslint-patch": "^1.3.0", "@rushstack/eslint-patch": "^1.3.0",
"@tsconfig/node18": "^2.0.1", "@tsconfig/node18": "^2.0.1",
"@types/bootstrap": "^5.2.6", "@types/bootstrap": "^5.2.6",
"@types/node": "^20.2.3", "@types/node": "^20.2.5",
"@types/sortablejs": "^1.15.1",
"@types/spark-md5": "^3.0.2", "@types/spark-md5": "^3.0.2",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.4.0",
"eslint": "^8.41.0", "eslint": "^8.41.0",
"eslint-plugin-vue": "^9.14.0", "eslint-plugin-vue": "^9.14.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"sass": "^1.62.1", "sass": "^1.62.1",
"terser": "^5.17.6", "terser": "^5.17.7",
"typescript": "^5.0.4", "typescript": "^5.1.3",
"vite": "^4.3.8", "vite": "^4.3.9",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-css-injected-by-js": "^3.1.1", "vite-plugin-css-injected-by-js": "^3.1.1",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.6.5"

View File

@ -56,6 +56,7 @@
"4006": "Ungültige Anzahl an Kanalwerten übergeben!", "4006": "Ungültige Anzahl an Kanalwerten übergeben!",
"4007": "Wechselrichter geändert!", "4007": "Wechselrichter geändert!",
"4008": "Wechselrichter gelöscht!", "4008": "Wechselrichter gelöscht!",
"4009": "Wechselrichter Reihenfolge gespeichert!",
"5001": "@:apiresponse.2001", "5001": "@:apiresponse.2001",
"5002": "Das Limit muss zwischen 1 und {max} sein!", "5002": "Das Limit muss zwischen 1 und {max} sein!",
"5003": "Ungültiten Typ angegeben!", "5003": "Ungültiten Typ angegeben!",
@ -276,8 +277,9 @@
"Synced": "synchronisiert", "Synced": "synchronisiert",
"NotSynced": "nicht synchronisiert", "NotSynced": "nicht synchronisiert",
"LocalTime": "Lokale Uhrzeit", "LocalTime": "Lokale Uhrzeit",
"Sunrise": "Nautische Morgendämmerung", "Sunrise": "Morgendämmerung",
"Sunset": "Nautische Abenddämmerung", "Sunset": "Abenddämmerung",
"NotAvailable": "Nicht verfügbar",
"Mode": "Modus", "Mode": "Modus",
"Day": "Tag", "Day": "Tag",
"Night": "Nacht" "Night": "Nacht"
@ -408,6 +410,12 @@
"LocationConfiguration": "Standortkonfiguration", "LocationConfiguration": "Standortkonfiguration",
"Longitude": "Längengrad:", "Longitude": "Längengrad:",
"Latitude": "Breitengrad:", "Latitude": "Breitengrad:",
"SunSetType": "Dämmerungstyp:",
"SunSetTypeHint": "Beeinflusst die Tag/Nacht Berechnung. Es kann bis zu einer Minute dauern bis der neue Typ angewendet wurde.",
"OFFICIAL": "Standard Dämmerung (90.8°)",
"NAUTICAL": "Nautische Dämmerung (102°)",
"CIVIL": "Bürgerliche Dämmerung (96°)",
"ASTONOMICAL": "Astronomische Dämmerung (108°)",
"Save": "@:dtuadmin.Save", "Save": "@:dtuadmin.Save",
"ManualTimeSynchronization": "Manuelle Zeitsynchronization", "ManualTimeSynchronization": "Manuelle Zeitsynchronization",
"CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:", "CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:",
@ -564,6 +572,7 @@
"StatusHint": "<b>Hinweis:</b> Der Wechselrichter wird über seinen DC-Eingang mit Strom versorgt. Wenn keine Sonne scheint, ist der Wechselrichter aus. Es können trotzdem Anfragen gesendet werden.", "StatusHint": "<b>Hinweis:</b> Der Wechselrichter wird über seinen DC-Eingang mit Strom versorgt. Wenn keine Sonne scheint, ist der Wechselrichter aus. Es können trotzdem Anfragen gesendet werden.",
"Type": "Typ", "Type": "Typ",
"Action": "Aktion", "Action": "Aktion",
"SaveOrder": "Reihenfolge speichern",
"DeleteInverter": "Wechselrichter löschen", "DeleteInverter": "Wechselrichter löschen",
"EditInverter": "Wechselrichter bearbeiten", "EditInverter": "Wechselrichter bearbeiten",
"InverterSerial": "Wechselrichter Seriennummer:", "InverterSerial": "Wechselrichter Seriennummer:",

View File

@ -56,6 +56,7 @@
"4006": "Invalid amount of max channel setting given!", "4006": "Invalid amount of max channel setting given!",
"4007": "Inverter changed!", "4007": "Inverter changed!",
"4008": "Inverter deleted!", "4008": "Inverter deleted!",
"4009": "Inverter order saved!",
"5001": "@:apiresponse.2001", "5001": "@:apiresponse.2001",
"5002": "Limit must between 1 and {max}!", "5002": "Limit must between 1 and {max}!",
"5003": "Invalid type specified!", "5003": "Invalid type specified!",
@ -276,8 +277,9 @@
"Synced": "synced", "Synced": "synced",
"NotSynced": "not synced", "NotSynced": "not synced",
"LocalTime": "Local Time", "LocalTime": "Local Time",
"Sunrise": "Nautical Sunrise", "Sunrise": "Sunrise",
"Sunset": "Nautical Sunset", "Sunset": "Sunset",
"NotAvailable": "Not Available",
"Mode": "Mode", "Mode": "Mode",
"Day": "Day", "Day": "Day",
"Night": "Night" "Night": "Night"
@ -408,6 +410,12 @@
"LocationConfiguration": "Location Configuration", "LocationConfiguration": "Location Configuration",
"Longitude": "Longitude", "Longitude": "Longitude",
"Latitude": "Latitude", "Latitude": "Latitude",
"SunSetType": "Sunset type",
"SunSetTypeHint": "Affects the day/night calculation. It can take up to one minute until the new type will be applied.",
"OFFICIAL": "Standard dawn (90.8°)",
"NAUTICAL": "Nautical dawn (102°)",
"CIVIL": "Civil dawn (96°)",
"ASTONOMICAL": "Astronomical dawn (108°)",
"Save": "@:dtuadmin.Save", "Save": "@:dtuadmin.Save",
"ManualTimeSynchronization": "Manual Time Synchronization", "ManualTimeSynchronization": "Manual Time Synchronization",
"CurrentOpenDtuTime": "Current OpenDTU Time:", "CurrentOpenDtuTime": "Current OpenDTU Time:",
@ -568,6 +576,7 @@
"StatusHint": "<b>Hint:</b> The inverter is powered by its DC input. If there is no sun, the inverter is off. Requests can still be sent.", "StatusHint": "<b>Hint:</b> The inverter is powered by its DC input. If there is no sun, the inverter is off. Requests can still be sent.",
"Type": "Type", "Type": "Type",
"Action": "Action", "Action": "Action",
"SaveOrder": "Save order",
"DeleteInverter": "Delete inverter", "DeleteInverter": "Delete inverter",
"EditInverter": "Edit inverter", "EditInverter": "Edit inverter",
"InverterSerial": "Inverter Serial:", "InverterSerial": "Inverter Serial:",

View File

@ -56,6 +56,7 @@
"4006": "Réglage du montant maximal de canaux invalide !", "4006": "Réglage du montant maximal de canaux invalide !",
"4007": "Onduleur modifié !", "4007": "Onduleur modifié !",
"4008": "Onduleur supprimé !", "4008": "Onduleur supprimé !",
"4009": "Inverter order saved!",
"5001": "@:apiresponse.2001", "5001": "@:apiresponse.2001",
"5002": "La limite doit être comprise entre 1 et {max} !", "5002": "La limite doit être comprise entre 1 et {max} !",
"5003": "Type spécifié invalide !", "5003": "Type spécifié invalide !",
@ -275,8 +276,9 @@
"Synced": "synchronisée", "Synced": "synchronisée",
"NotSynced": "pas synchronisée", "NotSynced": "pas synchronisée",
"LocalTime": "Heure locale", "LocalTime": "Heure locale",
"Sunrise": "Nautical Sunrise", "Sunrise": "Sunrise",
"Sunset": "Nautical Sunset", "Sunset": "Sunset",
"NotAvailable": "Not Available",
"Mode": "Mode", "Mode": "Mode",
"Day": "Day", "Day": "Day",
"Night": "Night" "Night": "Night"
@ -407,6 +409,12 @@
"LocationConfiguration": "Géolocalisation", "LocationConfiguration": "Géolocalisation",
"Longitude": "Longitude", "Longitude": "Longitude",
"Latitude": "Latitude", "Latitude": "Latitude",
"SunSetType": "Sunset type",
"SunSetTypeHint": "Affects the day/night calculation. It can take up to one minute until the new type will be applied.",
"OFFICIAL": "Standard dawn (90.8°)",
"NAUTICAL": "Nautical dawn (102°)",
"CIVIL": "Civil dawn (96°)",
"ASTONOMICAL": "Astronomical dawn (108°)",
"Save": "@:dtuadmin.Save", "Save": "@:dtuadmin.Save",
"ManualTimeSynchronization": "Synchronisation manuelle de l'heure", "ManualTimeSynchronization": "Synchronisation manuelle de l'heure",
"CurrentOpenDtuTime": "Heure actuelle de l'OpenDTU", "CurrentOpenDtuTime": "Heure actuelle de l'OpenDTU",
@ -491,6 +499,7 @@
"StatusHint": "<b>Astuce :</b> L'onduleur est alimenté par son entrée courant continu. S'il n'y a pas de soleil, l'onduleur est éteint, mais les requêtes peuvent toujours être envoyées.", "StatusHint": "<b>Astuce :</b> L'onduleur est alimenté par son entrée courant continu. S'il n'y a pas de soleil, l'onduleur est éteint, mais les requêtes peuvent toujours être envoyées.",
"Type": "Type", "Type": "Type",
"Action": "Action", "Action": "Action",
"SaveOrder": "Save order",
"DeleteInverter": "Supprimer l'onduleur", "DeleteInverter": "Supprimer l'onduleur",
"EditInverter": "Modifier l'onduleur", "EditInverter": "Modifier l'onduleur",
"InverterSerial": "Numéro de série de l'onduleur", "InverterSerial": "Numéro de série de l'onduleur",

View File

@ -23,6 +23,7 @@ export interface InverterStatistics {
export interface Inverter { export interface Inverter {
serial: number; serial: number;
name: string; name: string;
order: number;
data_age: number; data_age: number;
poll_enabled: boolean; poll_enabled: boolean;
reachable: boolean; reachable: boolean;

View File

@ -4,4 +4,5 @@ export interface NtpConfig {
ntp_timezone_descr: string; ntp_timezone_descr: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
sunsettype: number;
} }

View File

@ -7,4 +7,5 @@ export interface NtpStatus {
sun_risetime: string; sun_risetime: string;
sun_settime: string; sun_settime: string;
sun_isDayPeriod: boolean; sun_isDayPeriod: boolean;
sun_isSunsetAvailable: boolean;
} }

View File

@ -470,7 +470,9 @@ export default defineComponent({
'decimalTwoDigits'); 'decimalTwoDigits');
}, },
inverterData(): Inverter[] { inverterData(): Inverter[] {
return this.liveData.inverters; return this.liveData.inverters.slice().sort((a : Inverter, b: Inverter) => {
return a.order - b.order;
});
} }
}, },
methods: { methods: {

View File

@ -28,6 +28,7 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th>#</th>
<th scope="col">{{ $t('inverteradmin.Status') }}</th> <th scope="col">{{ $t('inverteradmin.Status') }}</th>
<th>{{ $t('inverteradmin.Serial') }}</th> <th>{{ $t('inverteradmin.Serial') }}</th>
<th>{{ $t('inverteradmin.Name') }}</th> <th>{{ $t('inverteradmin.Name') }}</th>
@ -35,8 +36,9 @@
<th>{{ $t('inverteradmin.Action') }}</th> <th>{{ $t('inverteradmin.Action') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody ref="invList">
<tr v-for="inverter in sortedInverters" v-bind:key="inverter.id"> <tr v-for="inverter in inverters" v-bind:key="inverter.id" :data-id="inverter.id">
<td><BIconGripHorizontal class="drag-handle" /></td>
<td> <td>
<span class="badge" :title="$t('inverteradmin.Receive')" :class="{ <span class="badge" :title="$t('inverteradmin.Receive')" :class="{
'text-bg-warning': !inverter.poll_enable_night, 'text-bg-warning': !inverter.poll_enable_night,
@ -63,6 +65,9 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="ml-auto text-right">
<button class="btn btn-primary my-2" @click="onSaveOrder()">{{ $t('inverteradmin.SaveOrder') }}</button>
</div>
</CardElement> </CardElement>
</BasePage> </BasePage>
@ -197,6 +202,7 @@ import BasePage from '@/components/BasePage.vue';
import BootstrapAlert from "@/components/BootstrapAlert.vue"; import BootstrapAlert from "@/components/BootstrapAlert.vue";
import CardElement from '@/components/CardElement.vue'; import CardElement from '@/components/CardElement.vue';
import InputElement from '@/components/InputElement.vue'; import InputElement from '@/components/InputElement.vue';
import Sortable from 'sortablejs';
import { authHeader, handleResponse } from '@/utils/authentication'; import { authHeader, handleResponse } from '@/utils/authentication';
import * as bootstrap from 'bootstrap'; import * as bootstrap from 'bootstrap';
import { import {
@ -205,6 +211,7 @@ import {
BIconTrash, BIconTrash,
BIconArrowDown, BIconArrowDown,
BIconArrowUp, BIconArrowUp,
BIconGripHorizontal,
} from 'bootstrap-icons-vue'; } from 'bootstrap-icons-vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
@ -219,6 +226,7 @@ declare interface Inverter {
serial: number; serial: number;
name: string; name: string;
type: string; type: string;
order: number;
poll_enable: boolean; poll_enable: boolean;
poll_enable_night: boolean; poll_enable_night: boolean;
command_enable: boolean; command_enable: boolean;
@ -244,6 +252,7 @@ export default defineComponent({
BIconTrash, BIconTrash,
BIconArrowDown, BIconArrowDown,
BIconArrowUp, BIconArrowUp,
BIconGripHorizontal,
}, },
data() { data() {
return { return {
@ -253,7 +262,8 @@ export default defineComponent({
selectedInverterData: {} as Inverter, selectedInverterData: {} as Inverter,
inverters: [] as Inverter[], inverters: [] as Inverter[],
dataLoading: true, dataLoading: true,
alert: {} as AlertResponse alert: {} as AlertResponse,
sortable: {} as Sortable,
}; };
}, },
mounted() { mounted() {
@ -263,21 +273,27 @@ export default defineComponent({
created() { created() {
this.getInverters(); this.getInverters();
}, },
computed: {
sortedInverters(): Inverter[] {
return this.inverters.slice().sort((a, b) => {
return a.serial - b.serial;
});
},
},
methods: { methods: {
getInverters() { getInverters() {
this.dataLoading = true; this.dataLoading = true;
fetch("/api/inverter/list", { headers: authHeader() }) fetch("/api/inverter/list", { headers: authHeader() })
.then((response) => handleResponse(response, this.$emitter, this.$router)) .then((response) => handleResponse(response, this.$emitter, this.$router))
.then((data) => { .then((data) => {
this.inverters = data.inverter; this.inverters = data.inverter.slice().sort((a : Inverter, b: Inverter) => {
return a.order - b.order;
});
this.dataLoading = false; this.dataLoading = false;
this.$nextTick(() => {
const table = this.$refs.invList as HTMLElement;
this.sortable = Sortable.create(table, {
sort: true,
handle: '.drag-handle',
animation: 150,
draggable: 'tr',
});
});
}); });
}, },
callInverterApiEndpoint(endpoint: string, jsonData: string) { callInverterApiEndpoint(endpoint: string, jsonData: string) {
@ -316,7 +332,16 @@ export default defineComponent({
}, },
onCloseModal(modal: bootstrap.Modal) { onCloseModal(modal: bootstrap.Modal) {
modal.hide(); modal.hide();
} },
onSaveOrder() {
this.callInverterApiEndpoint("order", JSON.stringify({ order: this.sortable.toArray() }));
},
}, },
}); });
</script> </script>
<style>
.drag-handle {
cursor: grab;
}
</style>

View File

@ -36,6 +36,21 @@
<InputElement :label="$t('ntpadmin.Longitude')" <InputElement :label="$t('ntpadmin.Longitude')"
v-model="ntpConfigList.longitude" v-model="ntpConfigList.longitude"
type="number" min="-180" max="180" step="any"/> type="number" min="-180" max="180" step="any"/>
<div class="row mb-3">
<label class="col-sm-2 col-form-label">
{{ $t('ntpadmin.SunSetType') }}
<BIconInfoCircle v-tooltip :title="$t('ntpadmin.SunSetTypeHint')" />
</label>
<div class="col-sm-10">
<select class="form-select" v-model="ntpConfigList.sunsettype">
<option v-for="sunsettype in sunsetTypeList" :key="sunsettype.key" :value="sunsettype.key">
{{ $t(`ntpadmin.` + sunsettype.value) }}
</option>
</select>
</div>
</div>
</CardElement> </CardElement>
<button type="submit" class="btn btn-primary mb-3">{{ $t('ntpadmin.Save') }}</button> <button type="submit" class="btn btn-primary mb-3">{{ $t('ntpadmin.Save') }}</button>
</form> </form>
@ -67,6 +82,7 @@ import InputElement from '@/components/InputElement.vue';
import type { NtpConfig } from "@/types/NtpConfig"; import type { NtpConfig } from "@/types/NtpConfig";
import { authHeader, handleResponse } from '@/utils/authentication'; import { authHeader, handleResponse } from '@/utils/authentication';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { BIconInfoCircle } from 'bootstrap-icons-vue';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -74,6 +90,7 @@ export default defineComponent({
BootstrapAlert, BootstrapAlert,
CardElement, CardElement,
InputElement, InputElement,
BIconInfoCircle,
}, },
data() { data() {
return { return {
@ -88,6 +105,12 @@ export default defineComponent({
alertMessage: "", alertMessage: "",
alertType: "info", alertType: "info",
showAlert: false, showAlert: false,
sunsetTypeList: [
{ key: 0, value: 'OFFICIAL' },
{ key: 1, value: 'NAUTICAL' },
{ key: 2, value: 'CIVIL' },
{ key: 3, value: 'ASTONOMICAL' },
],
}; };
}, },
watch: { watch: {

View File

@ -38,11 +38,13 @@
<tr> <tr>
<th>{{ $t('ntpinfo.Sunrise') }}</th> <th>{{ $t('ntpinfo.Sunrise') }}</th>
<td>{{ ntpDataList.sun_risetime }}</td> <td v-if="ntpDataList.sun_isSunsetAvailable">{{ ntpDataList.sun_risetime }}</td>
<td v-else>{{ $t('ntpinfo.NotAvailable') }}</td>
</tr> </tr>
<tr> <tr>
<th>{{ $t('ntpinfo.Sunset') }}</th> <th>{{ $t('ntpinfo.Sunset') }}</th>
<td>{{ ntpDataList.sun_settime }}</td> <td v-if="ntpDataList.sun_isSunsetAvailable">{{ ntpDataList.sun_settime }}</td>
<td v-else>{{ $t('ntpinfo.NotAvailable') }}</td>
</tr> </tr>
<tr> <tr>
<th>{{ $t('ntpinfo.Mode') }}</th> <th>{{ $t('ntpinfo.Mode') }}</th>

View File

@ -180,10 +180,10 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@intlify/bundle-utils@^5.4.0": "@intlify/bundle-utils@^6.0.1":
version "5.4.0" version "6.0.1"
resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-5.4.0.tgz#12d1e2316a52cdf4818f5f183dc2726da35886c0" resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-6.0.1.tgz#889ef90a07f54d3285082f1fff99be06db49c65f"
integrity sha512-oJbibbP5djdQYTv0cQC4PYRHPpS5nF/KZ7MWM1/yhdsGzjvCekJHWk25MCQIIOrfQ+aw5tKi2t66KpYEUki/tw== integrity sha512-BkeZNKZiC0B7K3OYMwiPLoAqsZmKH3SxTL75vYAkuQ//XWR8WO0NpfjXhTxgLTVFHxMcNb2agAopC0DP6fqDrg==
dependencies: dependencies:
"@intlify/message-compiler" "9.3.0-beta.17" "@intlify/message-compiler" "9.3.0-beta.17"
"@intlify/shared" "9.3.0-beta.17" "@intlify/shared" "9.3.0-beta.17"
@ -192,6 +192,7 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
jsonc-eslint-parser "^1.0.1" jsonc-eslint-parser "^1.0.1"
magic-string "^0.30.0" magic-string "^0.30.0"
mlly "^1.2.0"
source-map "0.6.1" source-map "0.6.1"
yaml-eslint-parser "^0.3.2" yaml-eslint-parser "^0.3.2"
@ -238,12 +239,12 @@
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.17.tgz#1180dcb0b30741555fad0b62e4621802e8272ee5" resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.17.tgz#1180dcb0b30741555fad0b62e4621802e8272ee5"
integrity sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ== integrity sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==
"@intlify/unplugin-vue-i18n@^0.10.0": "@intlify/unplugin-vue-i18n@^0.11.0":
version "0.10.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.10.0.tgz#28a05a7b9e0a7cc35e91e6762e5e6e57f954a45c" resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.11.0.tgz#43d2730d86ceb214e7e147b0cc8f78f7d7df0707"
integrity sha512-Sf8fe26/d8rBNcg+zBSb7RA1uyhrG9zhIM+CRX6lqcznMDjLRr/1tuVaJ9E6xqJkzjfPgRzNcCqwMt6rpNkL7Q== integrity sha512-ivcLZo08fvepHWV8o5lcKfhcKFSWqhwrqIAU6pUIbvq2ICo9fnXnIPYIZj7FeuHDLW1G3ADm44ZhQC3nYmvDlg==
dependencies: dependencies:
"@intlify/bundle-utils" "^5.4.0" "@intlify/bundle-utils" "^6.0.1"
"@intlify/shared" "9.3.0-beta.17" "@intlify/shared" "9.3.0-beta.17"
"@rollup/pluginutils" "^5.0.2" "@rollup/pluginutils" "^5.0.2"
"@vue/compiler-sfc" "^3.2.47" "@vue/compiler-sfc" "^3.2.47"
@ -283,10 +284,10 @@
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/source-map@^0.3.2": "@jridgewell/source-map@^0.3.3":
version "0.3.2" version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==
dependencies: dependencies:
"@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/trace-mapping" "^0.3.9"
@ -325,10 +326,10 @@
"@nodelib/fs.scandir" "2.1.5" "@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0" fastq "^1.6.0"
"@popperjs/core@^2.11.7": "@popperjs/core@^2.11.8":
version "2.11.7" version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw== integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@popperjs/core@^2.9.2": "@popperjs/core@^2.9.2":
version "2.11.5" version "2.11.5"
@ -371,16 +372,21 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/node@^20.2.3": "@types/node@^20.2.5":
version "20.2.3" version "20.2.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.3.tgz#b31eb300610c3835ac008d690de6f87e28f9b878" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb"
integrity sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw== integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==
"@types/semver@^7.3.12": "@types/semver@^7.3.12":
version "7.3.13" version "7.3.13"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
"@types/sortablejs@^1.15.1":
version "1.15.1"
resolved "https://registry.yarnpkg.com/@types/sortablejs/-/sortablejs-1.15.1.tgz#123abafbe936f754fee5eb5b49009ce1f1075aa5"
integrity sha512-g/JwBNToh6oCTAwNS8UGVmjO7NLDKsejVhvE4x1eWiPTC3uCuNsa/TD4ssvX3du+MLiM+SHPNDuijp8y76JzLQ==
"@types/spark-md5@^3.0.2": "@types/spark-md5@^3.0.2":
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.2.tgz#da2e8a778a20335fc4f40b6471c4b0d86b70da55" resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.2.tgz#da2e8a778a20335fc4f40b6471c4b0d86b70da55"
@ -766,16 +772,16 @@ acorn@^7.1.1, acorn@^7.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.5.0, acorn@^8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
acorn@^8.8.0: acorn@^8.8.0:
version "8.8.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
acorn@^8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
ajv@^6.10.0, ajv@^6.12.4: ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6" version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@ -843,10 +849,10 @@ bootstrap-icons-vue@^1.10.3:
resolved "https://registry.yarnpkg.com/bootstrap-icons-vue/-/bootstrap-icons-vue-1.10.3.tgz#ae725513c9655ce86effa2a0b09e9e65b02c8f1a" resolved "https://registry.yarnpkg.com/bootstrap-icons-vue/-/bootstrap-icons-vue-1.10.3.tgz#ae725513c9655ce86effa2a0b09e9e65b02c8f1a"
integrity sha512-BzqmLufgHjFvSReJ1GQqNkl780UFK0rWT4Y1IQC7lZClXyOSsM5Ipw04BnuVmmrqgtSxzak83jcBwLJgCK3scg== integrity sha512-BzqmLufgHjFvSReJ1GQqNkl780UFK0rWT4Y1IQC7lZClXyOSsM5Ipw04BnuVmmrqgtSxzak83jcBwLJgCK3scg==
bootstrap@^5.3.0-alpha3: bootstrap@^5.3.0:
version "5.3.0-alpha3" version "5.3.0"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.0-alpha3.tgz#ad64d9a663c53ab7aca99c560e0bd16b5e023441" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.0.tgz#0718a7cc29040ee8dbf1bd652b896f3436a87c29"
integrity sha512-FBhOWMxkCFr74hesJdchLXhqagPTXS+kRNU3gE0FR5Ki/AdPSz32Ik96Z28+yBluCnE/pc9st7l1yPwKgbtfSA== integrity sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
@ -1119,10 +1125,10 @@ escodegen@^2.0.0:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" source-map "~0.6.1"
eslint-plugin-vue@^9.14.0: eslint-plugin-vue@^9.14.1:
version "9.14.0" version "9.14.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.14.0.tgz#73004a62d794e276a60d471114d81ed8887efcb8" resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz#3b0c9857642dac547c7564031cfb09d283eafdd4"
integrity sha512-4O7EuiqPGVQA1wYCzLvCzsBTv9JIPHLHhrf0k55DLzbwtmJbSw2TKS0G/l7pOwi9RWMSkjIT7ftChU5gZpgnJw== integrity sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.3.0" "@eslint-community/eslint-utils" "^4.3.0"
natural-compare "^1.4.0" natural-compare "^1.4.0"
@ -1746,6 +1752,11 @@ jsonc-eslint-parser@^1.0.1:
espree "^6.0.0" espree "^6.0.0"
semver "^6.3.0" semver "^6.3.0"
jsonc-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
jsonfile@^6.0.1: jsonfile@^6.0.1:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@ -1856,6 +1867,16 @@ mitt@^3.0.0:
resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
mlly@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.3.0.tgz#3184cb80c6437bda861a9f452ae74e3434ed9cd1"
integrity sha512-HT5mcgIQKkOrZecOjOX3DJorTikWXwsBfpcr/MGBkhfWcjiqvnaL/9ppxvIUXfjT6xt4DVIAsN9fMUz1ev4bIw==
dependencies:
acorn "^8.8.2"
pathe "^1.1.0"
pkg-types "^1.0.3"
ufo "^1.1.2"
ms@2.1.2: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -2045,7 +2066,7 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pathe@^1.0.0: pathe@^1.0.0, pathe@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w== integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==
@ -2070,6 +2091,15 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
pkg-types@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==
dependencies:
jsonc-parser "^3.2.0"
mlly "^1.2.0"
pathe "^1.1.0"
postcss-selector-parser@^6.0.9: postcss-selector-parser@^6.0.9:
version "6.0.10" version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
@ -2266,6 +2296,11 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
sortablejs@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@ -2383,13 +2418,13 @@ 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.17.6: terser@^5.17.7:
version "5.17.6" version "5.17.7"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de" resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.7.tgz#2a8b134826fe179b711969fd9d9a0c2479b2a8c3"
integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ== integrity sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==
dependencies: dependencies:
"@jridgewell/source-map" "^0.3.2" "@jridgewell/source-map" "^0.3.3"
acorn "^8.5.0" acorn "^8.8.2"
commander "^2.20.0" commander "^2.20.0"
source-map-support "~0.5.20" source-map-support "~0.5.20"
@ -2436,10 +2471,15 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
typescript@^5.0.4: typescript@^5.1.3:
version "5.0.4" version "5.1.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==
ufo@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76"
integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==
unbox-primitive@^1.0.2: unbox-primitive@^1.0.2:
version "1.0.2" version "1.0.2"
@ -2500,10 +2540,10 @@ vite-plugin-css-injected-by-js@^3.1.1:
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.1.1.tgz#8324412636cf6fdada1a86f595aa2e78458e5ddb" resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.1.1.tgz#8324412636cf6fdada1a86f595aa2e78458e5ddb"
integrity sha512-mwrFvEEy0TuH8Ul0cb2HgjmNboQ/JnEFy+kHCWqAJph3ikMOiIuyYVdx0JO4nEIWJyzSnc4TTdmoTulsikvJEg== integrity sha512-mwrFvEEy0TuH8Ul0cb2HgjmNboQ/JnEFy+kHCWqAJph3ikMOiIuyYVdx0JO4nEIWJyzSnc4TTdmoTulsikvJEg==
vite@^4.3.8: vite@^4.3.9:
version "4.3.8" version "4.3.9"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.8.tgz#70cd6a294ab52d7fb8f37f5bc63d117dd19e9918" resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d"
integrity sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ== integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==
dependencies: dependencies:
esbuild "^0.17.5" esbuild "^0.17.5"
postcss "^8.4.23" postcss "^8.4.23"
@ -2547,10 +2587,10 @@ vue-i18n@^9.2.2:
"@intlify/vue-devtools" "9.2.2" "@intlify/vue-devtools" "9.2.2"
"@vue/devtools-api" "^6.2.1" "@vue/devtools-api" "^6.2.1"
vue-router@^4.2.1: vue-router@^4.2.2:
version "4.2.1" version "4.2.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.1.tgz#f8ab85c89e74682cad71519480fdf2b855e8c9e0" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.2.tgz#b0097b66d89ca81c0986be03da244c7b32a4fd81"
integrity sha512-nW28EeifEp8Abc5AfmAShy5ZKGsGzjcnZ3L1yc2DYUo+MqbBClrRP9yda3dIekM4I50/KnEwo1wkBLf7kHH5Cw== integrity sha512-cChBPPmAflgBGmy3tBsjeoe3f3VOSG6naKyY5pjtrqLGbNEXdzCigFUHgBvp9e3ysAtFtEx7OLqcSDh/1Cq2TQ==
dependencies: dependencies:
"@vue/devtools-api" "^6.5.0" "@vue/devtools-api" "^6.5.0"

Binary file not shown.