Merge branch 'master' into Database

This commit is contained in:
Ralf Bauer 2023-06-01 23:45:50 +02:00
commit 5447e558f6
40 changed files with 1075 additions and 367 deletions

1
.gitignore vendored
View File

@ -5,5 +5,6 @@
.vscode/ipch
.vscode/settings.json
platformio-device-monitor*.log
logs/device-monitor*.log
platformio_override.ini
.DS_Store

View File

@ -18,5 +18,30 @@
"type": 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

@ -40,6 +40,7 @@ struct CHANNEL_CONFIG_T {
struct INVERTER_CONFIG_T {
uint64_t Serial;
char Name[INV_MAX_NAME_STRLEN + 1];
uint8_t Order;
bool Poll_Enable;
bool Poll_Enable_Night;
bool Command_Enable;
@ -66,6 +67,7 @@ struct CONFIG_T {
char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
double Ntp_Longitude;
double Ntp_Latitude;
uint8_t Ntp_SunsetType;
bool Mqtt_Enabled;
uint Mqtt_Port;
@ -109,6 +111,7 @@ struct CONFIG_T {
bool Display_ScreenSaver;
uint8_t Display_Rotation;
uint8_t Display_Contrast;
uint8_t Display_Language;
};
class ConfigurationClass {

80
include/Datastore.h Normal file
View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TimeoutHelper.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
class DatastoreClass {
public:
DatastoreClass();
void init();
void loop();
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
float getTotalAcYieldTotalEnabled();
// Sum of yield day of all enabled inverters, a inverter which is just disabled at night is also included
float getTotalAcYieldDayEnabled();
// Sum of total AC power of all enabled inverters
float getTotalAcPowerEnabled();
// Sum of total DC power of all enabled inverters
float getTotalDcPowerEnabled();
// Sum of total DC power of all enabled inverters with maxStringPower set
float getTotalDcPowerIrradiation();
// Sum of total installed irradiation of all enabled inverters
float getTotalDcIrradiationInstalled();
// Percentage (1-100) of total irradiation
float getTotalDcIrradiation();
// Amount of relevant digits for yield total
unsigned int getTotalAcYieldTotalDigits();
// Amount of relevant digits for yield total
unsigned int getTotalAcYieldDayDigits();
// Amount of relevant digits for AC power
unsigned int getTotalAcPowerDigits();
// Amount of relevant digits for DC power
unsigned int getTotalDcPowerDigits();
// True, if at least one inverter is reachable
bool getIsAtLeastOneReachable();
// True if at least one inverter is producing
bool getIsAtLeastOneProducing();
// True if all enabled inverters are producing
bool getIsAllEnabledProducing();
// True if all enabled inverters are reachable
bool getIsAllEnabledReachable();
private:
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;

View File

@ -20,6 +20,7 @@ public:
void loop();
void setContrast(uint8_t contrast);
void setOrientation(uint8_t rotation = DISPLAY_ROTATION);
void setLanguage(uint8_t language);
void setStartupDisplay();
bool enablePowerSafe = true;
@ -33,6 +34,7 @@ private:
U8G2* _display;
DisplayType_t _display_type = DisplayType_t::None;
uint8_t _display_language = DISPLAY_LANGUAGE;
uint8_t _mExtra;
uint16_t _period = 1000;
uint16_t _interval = 60000; // interval at which to power save (milliseconds)

View File

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

View File

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

View File

@ -25,6 +25,7 @@
#define NTP_TIMEZONEDESCR "Europe/Berlin"
#define NTP_LONGITUDE 10.4515f
#define NTP_LATITUDE 51.1657f
#define NTP_SUNSETTYPE 1
#define MQTT_ENABLED false
#define MQTT_HOST ""
@ -92,3 +93,4 @@
#define DISPLAY_SCREENSAVER true
#define DISPLAY_ROTATION 2
#define DISPLAY_CONTRAST 60
#define DISPLAY_LANGUAGE 0

View File

@ -121,6 +121,13 @@ void HoymilesRadio_CMT::loop()
if (!_rxBuffer.empty()) {
fragment_t f = _rxBuffer.back();
if (checkFragmentCrc(&f)) {
serial_u dtuId = convertSerialToRadioId(_dtuSerial);
// The CMT RF module does not filter foreign packages by itself.
// Has to be done manually here.
if (memcmp(&f.fragment[5], &dtuId.b[1], 4) == 0) {
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterByFragment(&f);
if (nullptr != inv) {
@ -133,6 +140,7 @@ void HoymilesRadio_CMT::loop()
} else {
Hoymiles.getMessageOutput()->println("Inverter Not found!");
}
}
} else {
Hoymiles.getMessageOutput()->println("Frame kaputt"); // ;-)

View File

@ -68,6 +68,11 @@ bool MultiDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fra
uint16_t crc = 0xffff, crcRcv = 0;
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) {
// Last packet
crc = crc16(fragment[i].fragment, fragment[i].len - 2, crc);

View File

@ -152,26 +152,32 @@ void InverterAbstract::addRxFragment(uint8_t fragment[], uint8_t len)
}
uint8_t fragmentCount = fragment[9];
if (fragmentCount == 0) {
Hoymiles.getMessageOutput()->println("ERROR: fragment number zero received and ignored");
// Packets with 0x81 will be seen as 1
uint8_t fragmentId = fragmentCount & 0b01111111; // fragmentId is 1 based
if (fragmentId == 0) {
Hoymiles.getMessageOutput()->println("ERROR: fragment id zero received and ignored");
return;
}
if ((fragmentCount & 0b01111111) < MAX_RF_FRAGMENT_COUNT) {
// Packets with 0x81 will be seen as 1
memcpy(_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].fragment, &fragment[10], len - 11);
_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].len = len - 11;
_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].mainCmd = fragment[0];
_rxFragmentBuffer[(fragmentCount & 0b01111111) - 1].wasReceived = true;
if ((fragmentCount & 0b01111111) > _rxFragmentLastPacketId) {
_rxFragmentLastPacketId = fragmentCount & 0b01111111;
if (fragmentId >= MAX_RF_FRAGMENT_COUNT) {
Hoymiles.getMessageOutput()->printf("ERROR: fragment id %d is too large for buffer and ignored\r\n", fragmentId);
return;
}
memcpy(_rxFragmentBuffer[fragmentId - 1].fragment, &fragment[10], len - 11);
_rxFragmentBuffer[fragmentId - 1].len = len - 11;
_rxFragmentBuffer[fragmentId - 1].mainCmd = fragment[0];
_rxFragmentBuffer[fragmentId - 1].wasReceived = true;
if (fragmentId > _rxFragmentLastPacketId) {
_rxFragmentLastPacketId = fragmentId;
}
// 0b10000000 == 0x80
if ((fragmentCount & 0b10000000) == 0b10000000) {
_rxFragmentMaxPacketId = fragmentCount & 0b01111111;
_rxFragmentMaxPacketId = fragmentId;
}
}

View File

@ -6,9 +6,10 @@
#include "../Hoymiles.h"
#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, 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, 124, "Shut down by remote control" },
{ AlarmMessageType_t::ALL, 125, "Grid configuration parameter error" },

View File

@ -9,6 +9,8 @@
#define ALARM_LOG_ENTRY_SIZE 12
#define ALARM_LOG_PAYLOAD_SIZE (ALARM_LOG_ENTRY_COUNT * ALARM_LOG_ENTRY_SIZE + 4)
#define ALARM_MSG_COUNT 77
struct AlarmLogEntry_t {
uint16_t MessageId;
String Message;
@ -50,5 +52,5 @@ private:
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

@ -29,9 +29,11 @@ const devInfo_t devInfo[] = {
{ { 0x10, 0x10, 0x10, 0x15 }, static_cast<uint16_t>(300 * 0.7), "HM-300" }, // HM-300 factory limitted to 70%
{ { 0x10, 0x20, 0x21, ALL }, 350, "HMS-350" }, // 00
{ { 0x10, 0x10, 0x51, ALL }, 450, "HMS-450" }, // 01
{ { 0x10, 0x10, 0x71, ALL }, 500, "HMS-500" }, // 02
{ { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600" }, // 01
{ { 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, 0x22, 0x41, ALL }, 1600, "HMS-1600" }, // 4
{ { 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

@ -15,7 +15,7 @@ extra_configs =
[env]
framework = arduino
platform = espressif32@6.1.0
platform = espressif32@6.3.0
build_flags =
-DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz
@ -29,7 +29,7 @@ build_unflags =
lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer
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
olikraus/U8g2 @ ^2.34.17
buelowp/sunset @ ^1.1.7
@ -37,6 +37,7 @@ lib_deps =
extra_scripts =
pre:pio-scripts/auto_firmware_version.py
pre:pio-scripts/patch_apply.py
post:pio-scripts/create_factory_bin.py
board_build.partitions = partitions_custom.csv
board_build.filesystem = littlefs

View File

@ -46,6 +46,7 @@ bool ConfigurationClass::write()
ntp["timezone_descr"] = config.Ntp_TimezoneDescr;
ntp["latitude"] = config.Ntp_Latitude;
ntp["longitude"] = config.Ntp_Longitude;
ntp["sunsettype"] = config.Ntp_SunsetType;
JsonObject mqtt = doc.createNestedObject("mqtt");
mqtt["enabled"] = config.Mqtt_Enabled;
@ -95,12 +96,14 @@ bool ConfigurationClass::write()
display["screensaver"] = config.Display_ScreenSaver;
display["rotation"] = config.Display_Rotation;
display["contrast"] = config.Display_Contrast;
display["language"] = config.Display_Language;
JsonArray inverters = doc.createNestedArray("inverters");
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
JsonObject inv = inverters.createNestedObject();
inv["serial"] = config.Inverter[i].Serial;
inv["name"] = config.Inverter[i].Name;
inv["order"] = config.Inverter[i].Order;
inv["poll_enable"] = config.Inverter[i].Poll_Enable;
inv["poll_enable_night"] = config.Inverter[i].Poll_Enable_Night;
inv["command_enable"] = config.Inverter[i].Command_Enable;
@ -188,6 +191,7 @@ bool ConfigurationClass::read()
strlcpy(config.Ntp_TimezoneDescr, ntp["timezone_descr"] | NTP_TIMEZONEDESCR, sizeof(config.Ntp_TimezoneDescr));
config.Ntp_Latitude = ntp["latitude"] | NTP_LATITUDE;
config.Ntp_Longitude = ntp["longitude"] | NTP_LONGITUDE;
config.Ntp_SunsetType = ntp["sunsettype"] | NTP_SUNSETTYPE;
JsonObject mqtt = doc["mqtt"];
config.Mqtt_Enabled = mqtt["enabled"] | MQTT_ENABLED;
@ -237,12 +241,14 @@ bool ConfigurationClass::read()
config.Display_ScreenSaver = display["screensaver"] | DISPLAY_SCREENSAVER;
config.Display_Rotation = display["rotation"] | DISPLAY_ROTATION;
config.Display_Contrast = display["contrast"] | DISPLAY_CONTRAST;
config.Display_Language = display["language"] | DISPLAY_LANGUAGE;
JsonArray inverters = doc["inverters"];
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
JsonObject inv = inverters[i].as<JsonObject>();
config.Inverter[i].Serial = inv["serial"] | 0ULL;
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_Night = inv["poll_enable_night"] | true;

237
src/Datastore.cpp Normal file
View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023 Thomas Basler and others
*/
#include "Datastore.h"
#include "Configuration.h"
#include <Hoymiles.h>
#define DAT_SEMAPHORE_TAKE() \
do { \
} while (xSemaphoreTake(_xSemaphore, portMAX_DELAY) != pdPASS)
#define DAT_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
DatastoreClass Datastore;
DatastoreClass::DatastoreClass()
{
_xSemaphore = xSemaphoreCreateMutex();
DAT_SEMAPHORE_GIVE(); // release before first use
}
void DatastoreClass::init()
{
_updateTimeout.set(1000);
}
void DatastoreClass::loop()
{
if (Hoymiles.isAllRadioIdle() && _updateTimeout.occured()) {
uint8_t isProducing = 0;
uint8_t isReachable = 0;
DAT_SEMAPHORE_TAKE();
_totalAcYieldTotalEnabled = 0;
_totalAcYieldTotalDigits = 0;
_totalAcYieldDayEnabled = 0;
_totalAcYieldDayDigits = 0;
_totalAcPowerEnabled = 0;
_totalAcPowerDigits = 0;
_totalDcPowerEnabled = 0;
_totalDcPowerDigits = 0;
_totalDcPowerIrradiation = 0;
_totalDcIrradiationInstalled = 0;
_isAllEnabledProducing = true;
_isAllEnabledReachable = true;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr) {
continue;
}
auto cfg = Configuration.getInverterConfig(inv->serial());
if (cfg == nullptr) {
continue;
}
if (inv->isProducing()) {
isProducing++;
} else {
if (inv->getEnablePolling()) {
_isAllEnabledProducing = false;
}
}
if (inv->isReachable()) {
isReachable++;
} else {
if (inv->getEnablePolling()) {
_isAllEnabledReachable = false;
}
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
if (cfg->Poll_Enable) {
_totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
_totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
_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));
}
if (inv->getEnablePolling()) {
_totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(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)) {
if (inv->getEnablePolling()) {
_totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
_totalDcPowerDigits = max<unsigned int>(_totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));
if (inv->Statistics()->getStringMaxPower(c) > 0) {
_totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
_totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c);
}
}
}
}
_isAtLeastOneProducing = isProducing > 0;
_isAtLeastOneReachable = isReachable > 0;
_totalDcIrradiation = _totalDcIrradiationInstalled > 0 ? _totalDcPowerIrradiation / _totalDcIrradiationInstalled * 100.0f : 0;
DAT_SEMAPHORE_GIVE();
_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

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Display_Graphic.h"
#include <Hoymiles.h>
#include "Datastore.h"
#include <NetworkSettings.h>
#include <map>
#include <time.h>
@ -11,6 +11,25 @@ std::map<DisplayType_t, std::function<U8G2*(uint8_t, uint8_t, uint8_t, uint8_t)>
{ DisplayType_t::SH1106, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs) { return new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, reset, clock, data); } },
};
// Language defintion, respect order in languages[] and translation lists
#define I18N_LOCALE_EN 0
#define I18N_LOCALE_DE 1
#define I18N_LOCALE_FR 2
// Languages supported. Note: the order is important and must match locale_translations.h
const uint8_t languages[] = {
I18N_LOCALE_EN,
I18N_LOCALE_DE,
I18N_LOCALE_FR
};
static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" };
static const char* const i18n_current_power_w[] = { "%3.0f W", "%3.0f W", "%3.0f W" };
static const char* const i18n_current_power_kw[] = { "%2.1f kW", "%2.1f kW", "%2.1f kW" };
static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" };
static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" };
static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" };
DisplayGraphicClass::DisplayGraphicClass()
{
}
@ -95,6 +114,11 @@ void DisplayGraphicClass::setOrientation(uint8_t rotation)
calcLineHeights();
}
void DisplayGraphicClass::setLanguage(uint8_t language)
{
_display_language = language < sizeof(languages) / sizeof(languages[0]) ? language : DISPLAY_LANGUAGE;
}
void DisplayGraphicClass::setStartupDisplay()
{
if (_display_type == DisplayType_t::None) {
@ -113,38 +137,16 @@ void DisplayGraphicClass::loop()
}
if ((millis() - _lastDisplayUpdate) > _period) {
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
uint8_t isprod = 0;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr) {
continue;
}
if (inv->isProducing()) {
isprod++;
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
totalPower += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalYieldDay += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalYieldTotal += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
}
}
_display->clearBuffer();
//=====> Actual Production ==========
if ((totalPower > 0) && (isprod > 0)) {
if (Datastore.getIsAtLeastOneReachable()) {
_display->setPowerSave(false);
if (totalPower > 999) {
snprintf(_fmtText, sizeof(_fmtText), "%2.1f kW", (totalPower / 1000));
if (Datastore.getTotalAcPowerEnabled() > 999) {
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000));
} else {
snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", totalPower);
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.getTotalAcPowerEnabled());
}
printText(_fmtText, 0);
_previousMillis = millis();
@ -153,7 +155,7 @@ void DisplayGraphicClass::loop()
//=====> Offline ===========
else {
printText("offline", 0);
printText(i18n_offline[_display_language], 0);
// check if it's time to enter power saving mode
if (millis() - _previousMillis >= (_interval * 2)) {
_display->setPowerSave(enablePowerSafe);
@ -162,10 +164,10 @@ void DisplayGraphicClass::loop()
//<=======================
//=====> Today & Total Production =======
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled());
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled());
printText(_fmtText, 2);
//<=======================
@ -175,7 +177,7 @@ void DisplayGraphicClass::loop()
} else {
// Get current time
time_t now = time(nullptr);
strftime(_fmtText, sizeof(_fmtText), "%a %d.%m.%Y %H:%M", localtime(&now));
strftime(_fmtText, sizeof(_fmtText), i18n_date_format[_display_language], localtime(&now));
printText(_fmtText, 3);
}
_display->sendBuffer();

View File

@ -4,6 +4,7 @@
*/
#include "Led_Single.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MqttSettings.h"
#include "NetworkSettings.h"
#include "PinMapping.h"
@ -57,27 +58,11 @@ void LedSingleClass::loop()
// Update inverter status
_ledState[1] = LedState_t::Off;
if (Hoymiles.getNumInverters()) {
bool allReachable = true;
bool allProducing = true;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr) {
continue;
}
if (inv->getEnablePolling()) {
if (!inv->isReachable()) {
allReachable = false;
}
if (!inv->isProducing()) {
allProducing = false;
}
}
}
// set LED status
if (allReachable && allProducing) {
if (Datastore.getIsAllEnabledReachable() && Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::On;
}
if (allReachable && !allProducing) {
if (Datastore.getIsAllEnabledReachable() && !Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::Blink;
}
}

View File

@ -4,6 +4,7 @@
*/
#include "MqttHandleInverterTotal.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MqttSettings.h"
#include <Hoymiles.h>
#include "WebApi_database.h"
@ -23,56 +24,13 @@ void MqttHandleInverterTotalClass::loop()
}
if (_lastPublish.occured()) {
float totalAcPower = 0;
float totalDcPower = 0;
float totalDcPowerIrr = 0;
float totalDcPowerIrrInst = 0;
float totalAcYieldDay = 0;
float totalAcYieldTotal = 0;
uint8_t totalAcPowerDigits = 0;
uint8_t totalDcPowerDigits = 0;
uint8_t totalAcYieldDayDigits = 0;
uint8_t totalAcYieldTotalDigits = 0;
bool totalReachable = true;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr || !inv->getEnablePolling()) {
continue;
}
if (!inv->isReachable()) {
totalReachable = false;
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
totalAcPower += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<uint8_t>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
totalAcYieldDay += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalAcYieldDayDigits = max<uint8_t>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
totalAcYieldTotal += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
totalAcYieldTotalDigits = max<uint8_t>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
totalDcPower += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<uint8_t>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));
if (inv->Statistics()->getStringMaxPower(c) > 0) {
totalDcPowerIrr += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerIrrInst += inv->Statistics()->getStringMaxPower(c);
}
}
}
MqttSettings.publish("ac/power", String(totalAcPower, static_cast<unsigned int>(totalAcPowerDigits)));
MqttSettings.publish("ac/yieldtotal", String(totalAcYieldTotal, static_cast<unsigned int>(totalAcYieldTotalDigits)));
MqttSettings.publish("ac/yieldday", String(totalAcYieldDay, static_cast<unsigned int>(totalAcYieldDayDigits)));
MqttSettings.publish("ac/is_valid", String(totalReachable));
MqttSettings.publish("dc/power", String(totalDcPower, static_cast<unsigned int>(totalAcPowerDigits)));
MqttSettings.publish("dc/irradiation", String(totalDcPowerIrrInst > 0 ? totalDcPowerIrr / totalDcPowerIrrInst * 100.0f : 0, 3));
MqttSettings.publish("dc/is_valid", String(totalReachable));
MqttSettings.publish("ac/power", String(Datastore.getTotalAcPowerEnabled(), Datastore.getTotalAcPowerDigits()));
MqttSettings.publish("ac/yieldtotal", String(Datastore.getTotalAcYieldTotalEnabled(), Datastore.getTotalAcYieldTotalDigits()));
MqttSettings.publish("ac/yieldday", String(Datastore.getTotalAcYieldDayEnabled(), Datastore.getTotalAcYieldDayDigits()));
MqttSettings.publish("ac/is_valid", String(Datastore.getIsAllEnabledReachable()));
MqttSettings.publish("dc/power", String(Datastore.getTotalDcPowerEnabled(), Datastore.getTotalDcPowerDigits()));
MqttSettings.publish("dc/irradiation", String(Datastore.getTotalDcIrradiation(), 3));
MqttSettings.publish("dc/is_valid", String(Datastore.getIsAllEnabledReachable()));
_lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000);

View File

@ -45,8 +45,25 @@ void SunPositionClass::updateSunData()
}
_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;
}
_sunriseMinutes = static_cast<int>(_sun.calcCustomSunrise(sunset_type));
_sunsetMinutes = static_cast<int>(_sun.calcCustomSunset(sunset_type));
uint minutesPastMidnight = timeinfo.tm_hour * 60 + timeinfo.tm_min;
_isDayPeriod = (minutesPastMidnight >= _sunriseMinutes) && (minutesPastMidnight < _sunsetMinutes);

View File

@ -80,6 +80,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
display["power_safe"] = config.Display_PowerSafe;
display["screensaver"] = config.Display_ScreenSaver;
display["contrast"] = config.Display_Contrast;
display["language"] = config.Display_Language;
response->setLength();
request->send(response);
@ -149,11 +150,13 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
config.Display_PowerSafe = root["display"]["power_safe"].as<bool>();
config.Display_ScreenSaver = root["display"]["screensaver"].as<bool>();
config.Display_Contrast = root["display"]["contrast"].as<uint8_t>();
config.Display_Language = root["display"]["language"].as<uint8_t>();
Display.setOrientation(config.Display_Rotation);
Display.enablePowerSafe = config.Display_PowerSafe;
Display.enableScreensaver = config.Display_ScreenSaver;
Display.setContrast(config.Display_Contrast);
Display.setLanguage(config.Display_Language);
Configuration.write();

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/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
_server->on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
_server->on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
}
void WebApiInverterClass::loop()
@ -44,6 +45,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
JsonObject obj = data.createNestedObject();
obj["id"] = i;
obj["name"] = String(config.Inverter[i].Name);
obj["order"] = config.Inverter[i].Order;
// Inverter Serial is read as HEX
char buffer[sizeof(uint64_t) * 8 + 1];
@ -390,3 +392,72 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
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

@ -81,6 +81,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
root["ntp_timezone_descr"] = config.Ntp_TimezoneDescr;
root["longitude"] = config.Ntp_Longitude;
root["latitude"] = config.Ntp_Latitude;
root["sunsettype"] = config.Ntp_SunsetType;
response->setLength();
request->send(response);
@ -125,7 +126,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
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["code"] = WebApiError::GenericValueMissing;
response->setLength();
@ -166,6 +167,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
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_Longitude = root["longitude"].as<double>();
config.Ntp_SunsetType = root["sunsettype"].as<uint8_t>();
Configuration.write();
retMsg["type"] = "success";

View File

@ -4,6 +4,7 @@
*/
#include "WebApi_ws_live.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MessageOutput.h"
#include "WebApi.h"
#include "defaults.h"
@ -90,10 +91,6 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
{
JsonArray invArray = root.createNestedArray("inverters");
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
// Loop all inverters
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
@ -102,9 +99,14 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
}
JsonObject invObject = invArray.createNestedObject();
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
if (inv_cfg == nullptr) {
continue;
}
invObject["serial"] = inv->serialString();
invObject["name"] = inv->name();
invObject["order"] = inv_cfg->Order;
invObject["data_age"] = (millis() - inv->Statistics()->getLastUpdate()) / 1000;
invObject["poll_enabled"] = inv->getEnablePolling();
invObject["reachable"] = inv->isReachable();
@ -121,11 +123,8 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
JsonObject chanTypeObj = invObject.createNestedObject(inv->Statistics()->getChannelTypeName(t));
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
if (t == TYPE_DC) {
INVERTER_CONFIG_T* inv_cfg = Configuration.getInverterConfig(inv->serial());
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_UAC);
addField(chanTypeObj, i, inv, t, c, FLD_IAC);
@ -158,19 +157,12 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
if (inv->Statistics()->getLastUpdate() > _newestInverterTimestamp) {
_newestInverterTimestamp = inv->Statistics()->getLastUpdate();
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
totalPower += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalYieldDay += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalYieldTotal += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
}
}
JsonObject totalObj = root.createNestedObject("total");
// todo: Fixed hard coded name, unit and digits
addTotalField(totalObj, "Power", totalPower, "W", 1);
addTotalField(totalObj, "YieldDay", totalYieldDay, "Wh", 0);
addTotalField(totalObj, "YieldTotal", totalYieldTotal, "kWh", 2);
addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
addTotalField(totalObj, "YieldTotal", Datastore.getTotalAcYieldTotalEnabled(), "kWh", Datastore.getTotalAcYieldTotalDigits());
JsonObject hintObj = root.createNestedObject("hints");
struct tm timeinfo;

View File

@ -3,6 +3,7 @@
* Copyright (C) 2022 Thomas Basler and others
*/
#include "Configuration.h"
#include "Datastore.h"
#include "Display_Graphic.h"
#include "InverterSettings.h"
#include "Led_Single.h"
@ -119,6 +120,7 @@ void setup()
Display.enablePowerSafe = config.Display_PowerSafe;
Display.enableScreensaver = config.Display_ScreenSaver;
Display.setContrast(config.Display_Contrast);
Display.setLanguage(config.Display_Language);
Display.setStartupDisplay();
MessageOutput.println("done");
@ -141,6 +143,8 @@ void setup()
MessageOutput.println("done");
InverterSettings.init();
Datastore.init();
}
void loop()
@ -149,11 +153,14 @@ void loop()
yield();
InverterSettings.loop();
yield();
Datastore.loop();
yield();
MqttHandleDtu.loop();
yield();
MqttHandleInverter.loop();
yield();
MqttHandleInverterTotal.loop();
yield();
MqttHandleHass.loop();
yield();
WebApi.loop();

View File

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

View File

@ -51,6 +51,7 @@
"4006": "Ungültige Anzahl an Kanalwerten übergeben!",
"4007": "Wechselrichter geändert!",
"4008": "Wechselrichter gelöscht!",
"4009": "Wechselrichter Reihenfolge gespeichert!",
"5001": "@:apiresponse.2001",
"5002": "Das Limit muss zwischen 1 und {max} sein!",
"5003": "Ungültiten Typ angegeben!",
@ -243,8 +244,8 @@
"Synced": "synchronisiert",
"NotSynced": "nicht synchronisiert",
"LocalTime": "Lokale Uhrzeit",
"Sunrise": "Nautische Morgendämmerung",
"Sunset": "Nautische Abenddämmerung",
"Sunrise": "Morgendämmerung",
"Sunset": "Abenddämmerung",
"Mode": "Modus",
"Day": "Tag",
"Night": "Nacht"
@ -362,6 +363,12 @@
"LocationConfiguration": "Standortkonfiguration",
"Longitude": "Längengrad:",
"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",
"ManualTimeSynchronization": "Manuelle Zeitsynchronization",
"CurrentOpenDtuTime": "Aktuelle OpenDTU-Zeit:",
@ -436,6 +443,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.",
"Type": "Typ",
"Action": "Aktion",
"SaveOrder": "Reihenfolge speichern",
"DeleteInverter": "Wechselrichter löschen",
"EditInverter": "Wechselrichter bearbeiten",
"InverterSerial": "Wechselrichter Seriennummer:",
@ -533,6 +541,10 @@
"Rotation": "Rotation:",
"rot0": "Keine Rotation",
"rot90": "90 Grad Drehung",
"DisplayLanguage": "Displaysprache:",
"en": "Englisch",
"de": "Deutsch",
"fr": "Französisch",
"rot180": "180 Grad Drehung",
"rot270": "270 Grad Drehung",
"Save": "@:dtuadmin.Save"

View File

@ -51,6 +51,7 @@
"4006": "Invalid amount of max channel setting given!",
"4007": "Inverter changed!",
"4008": "Inverter deleted!",
"4009": "Inverter order saved!",
"5001": "@:apiresponse.2001",
"5002": "Limit must between 1 and {max}!",
"5003": "Invalid type specified!",
@ -243,8 +244,8 @@
"Synced": "synced",
"NotSynced": "not synced",
"LocalTime": "Local Time",
"Sunrise": "Nautical Sunrise",
"Sunset": "Nautical Sunset",
"Sunrise": "Sunrise",
"Sunset": "Sunset",
"Mode": "Mode",
"Day": "Day",
"Night": "Night"
@ -362,6 +363,12 @@
"LocationConfiguration": "Location Configuration",
"Longitude": "Longitude",
"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",
"ManualTimeSynchronization": "Manual Time Synchronization",
"CurrentOpenDtuTime": "Current OpenDTU Time:",
@ -436,6 +443,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.",
"Type": "Type",
"Action": "Action",
"SaveOrder": "Save order",
"DeleteInverter": "Delete inverter",
"EditInverter": "Edit inverter",
"InverterSerial": "Inverter Serial:",
@ -535,6 +543,10 @@
"rot90": "90 degree rotation",
"rot180": "180 degree rotation",
"rot270": "270 degree rotation",
"DisplayLanguage": "Display language:",
"en": "English",
"de": "German",
"fr": "French",
"Save": "@:dtuadmin.Save"
},
"pininfo": {

View File

@ -51,6 +51,7 @@
"4006": "Réglage du montant maximal de canaux invalide !",
"4007": "Onduleur modifié !",
"4008": "Onduleur supprimé !",
"4009": "Inverter order saved!",
"5001": "@:apiresponse.2001",
"5002": "La limite doit être comprise entre 1 et {max} !",
"5003": "Type spécifié invalide !",
@ -243,8 +244,8 @@
"Synced": "synchronisée",
"NotSynced": "pas synchronisée",
"LocalTime": "Heure locale",
"Sunrise": "Nautical Sunrise",
"Sunset": "Nautical Sunset",
"Sunrise": "Sunrise",
"Sunset": "Sunset",
"Mode": "Mode",
"Day": "Day",
"Night": "Night"
@ -362,6 +363,12 @@
"LocationConfiguration": "Géolocalisation",
"Longitude": "Longitude",
"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",
"ManualTimeSynchronization": "Synchronisation manuelle de l'heure",
"CurrentOpenDtuTime": "Heure actuelle de l'OpenDTU",
@ -436,6 +443,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.",
"Type": "Type",
"Action": "Action",
"SaveOrder": "Save order",
"DeleteInverter": "Supprimer l'onduleur",
"EditInverter": "Modifier l'onduleur",
"InverterSerial": "Numéro de série de l'onduleur",
@ -535,6 +543,10 @@
"rot90": "90 degree rotation",
"rot180": "180 degree rotation",
"rot270": "270 degree rotation",
"DisplayLanguage": "Langue d'affichage",
"en": "Anglais",
"de": "Allemand",
"fr": "Français",
"Save": "@:dtuadmin.Save"
},
"pininfo": {

View File

@ -5,6 +5,7 @@ export interface Display {
power_safe: boolean;
screensaver: boolean;
contrast: number;
language: number;
}
export interface DeviceConfig {

View File

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

View File

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

View File

@ -56,6 +56,19 @@
v-model="deviceConfigList.display.screensaver" type="checkbox"
:tooltip="$t('deviceadmin.ScreensaverHint')" />
<div class="row mb-3">
<label class="col-sm-2 col-form-label">
{{ $t('deviceadmin.DisplayLanguage') }}
</label>
<div class="col-sm-10">
<select class="form-select" v-model="deviceConfigList.display.language">
<option v-for="language in displayLanguageList" :key="language.key" :value="language.key">
{{ $t(`deviceadmin.` + language.value) }}
</option>
</select>
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label">
{{ $t('deviceadmin.Rotation') }}
@ -123,6 +136,11 @@ export default defineComponent({
{ key: 2, value: 'rot180' },
{ key: 3, value: 'rot270' },
],
displayLanguageList: [
{ key: 0, value: "en" },
{ key: 1, value: "de" },
{ key: 2, value: "fr" },
],
}
},
created() {

View File

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

View File

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

View File

@ -36,6 +36,21 @@
<InputElement :label="$t('ntpadmin.Longitude')"
v-model="ntpConfigList.longitude"
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>
<button type="submit" class="btn btn-primary mb-3">{{ $t('ntpadmin.Save') }}</button>
</form>
@ -67,6 +82,7 @@ import InputElement from '@/components/InputElement.vue';
import type { NtpConfig } from "@/types/NtpConfig";
import { authHeader, handleResponse } from '@/utils/authentication';
import { defineComponent } from 'vue';
import { BIconInfoCircle } from 'bootstrap-icons-vue';
export default defineComponent({
components: {
@ -74,6 +90,7 @@ export default defineComponent({
BootstrapAlert,
CardElement,
InputElement,
BIconInfoCircle,
},
data() {
return {
@ -88,6 +105,12 @@ export default defineComponent({
alertMessage: "",
alertType: "info",
showAlert: false,
sunsetTypeList: [
{ key: 0, value: 'OFFICIAL' },
{ key: 1, value: 'NAUTICAL' },
{ key: 2, value: 'CIVIL' },
{ key: 3, value: 'ASTONOMICAL' },
],
};
},
watch: {

View File

@ -156,10 +156,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.40.0.tgz#3ba73359e11f5a7bd3e407f70b3528abfae69cec"
integrity sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==
"@eslint/js@8.41.0":
version "8.41.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.41.0.tgz#080321c3b68253522f7646b55b577dd99d2950b3"
integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
@ -180,10 +180,10 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@intlify/bundle-utils@^5.4.0":
version "5.4.0"
resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-5.4.0.tgz#12d1e2316a52cdf4818f5f183dc2726da35886c0"
integrity sha512-oJbibbP5djdQYTv0cQC4PYRHPpS5nF/KZ7MWM1/yhdsGzjvCekJHWk25MCQIIOrfQ+aw5tKi2t66KpYEUki/tw==
"@intlify/bundle-utils@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-6.0.1.tgz#889ef90a07f54d3285082f1fff99be06db49c65f"
integrity sha512-BkeZNKZiC0B7K3OYMwiPLoAqsZmKH3SxTL75vYAkuQ//XWR8WO0NpfjXhTxgLTVFHxMcNb2agAopC0DP6fqDrg==
dependencies:
"@intlify/message-compiler" "9.3.0-beta.17"
"@intlify/shared" "9.3.0-beta.17"
@ -192,6 +192,7 @@
estree-walker "^2.0.2"
jsonc-eslint-parser "^1.0.1"
magic-string "^0.30.0"
mlly "^1.2.0"
source-map "0.6.1"
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"
integrity sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==
"@intlify/unplugin-vue-i18n@^0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.10.0.tgz#28a05a7b9e0a7cc35e91e6762e5e6e57f954a45c"
integrity sha512-Sf8fe26/d8rBNcg+zBSb7RA1uyhrG9zhIM+CRX6lqcznMDjLRr/1tuVaJ9E6xqJkzjfPgRzNcCqwMt6rpNkL7Q==
"@intlify/unplugin-vue-i18n@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.11.0.tgz#43d2730d86ceb214e7e147b0cc8f78f7d7df0707"
integrity sha512-ivcLZo08fvepHWV8o5lcKfhcKFSWqhwrqIAU6pUIbvq2ICo9fnXnIPYIZj7FeuHDLW1G3ADm44ZhQC3nYmvDlg==
dependencies:
"@intlify/bundle-utils" "^5.4.0"
"@intlify/bundle-utils" "^6.0.1"
"@intlify/shared" "9.3.0-beta.17"
"@rollup/pluginutils" "^5.0.2"
"@vue/compiler-sfc" "^3.2.47"
@ -325,10 +326,10 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@popperjs/core@^2.11.7":
version "2.11.7"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
"@popperjs/core@^2.11.8":
version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@popperjs/core@^2.9.2":
version "2.11.5"
@ -344,15 +345,15 @@
estree-walker "^2.0.2"
picomatch "^2.3.1"
"@rushstack/eslint-patch@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
"@rushstack/eslint-patch@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz#f5635b36fc0dad96ef1e542a302cd914230188c0"
integrity sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==
"@tsconfig/node18@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-2.0.0.tgz#58b52430d00913dc26c4459fbc7f05396e47886a"
integrity sha512-uI/B0ShkiEwTk036pncXucVlj4y11EW6mycQvCEzC1PkR2TBvdQZ5Wf96dp+XXWAc70FEDfvwTqanoaDpP6rPw==
"@tsconfig/node18@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-2.0.1.tgz#2d2e11333ef2b75a4623203daca264e6697d693b"
integrity sha512-UqdfvuJK0SArA2CxhKWwwAWfnVSXiYe63bVpMutc27vpngCntGUZQETO24pEJ46zU6XM+7SpqYoMgcO3bM11Ew==
"@types/bootstrap@^5.2.6":
version "5.2.6"
@ -371,16 +372,21 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/node@^18.16.6":
version "18.16.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.6.tgz#d0ffffe201b253989b17ea157ddabec677a4f4fe"
integrity sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==
"@types/node@^20.2.5":
version "20.2.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb"
integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==
"@types/semver@^7.3.12":
version "7.3.13"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
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":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.2.tgz#da2e8a778a20335fc4f40b6471c4b0d86b70da55"
@ -470,10 +476,10 @@
"@typescript-eslint/types" "5.59.1"
eslint-visitor-keys "^3.3.0"
"@vitejs/plugin-vue@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.2.1.tgz#c3ccce9956e8cdca946f465188777e4e3e488f6a"
integrity sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==
"@vitejs/plugin-vue@^4.2.3":
version "4.2.3"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz#ee0b6dfcc62fe65364e6395bf38fa2ba10bb44b6"
integrity sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==
"@volar/language-core@1.4.1":
version "1.4.1"
@ -489,35 +495,35 @@
dependencies:
muggle-string "^0.2.2"
"@volar/typescript@1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.4.1.tgz#a013419e6f029155e5467443f3ab72815da608b5"
integrity sha512-phTy6p9yG6bgMIKQWEeDOi/aeT0njZsb1a/G1mrEuDsLmAn24Le4gDwSsGNhea6Uhu+3gdpUZn2PmZXa+WG2iQ==
"@volar/typescript@1.4.1-patch.2":
version "1.4.1-patch.2"
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.4.1-patch.2.tgz#89f4bd199ca81a832d86d1449b01f49f2b72137c"
integrity sha512-lPFYaGt8OdMEzNGJJChF40uYqMO4Z/7Q9fHPQC/NRVtht43KotSXLrkPandVVMf9aPbiJ059eAT+fwHGX16k4w==
dependencies:
"@volar/language-core" "1.4.1"
"@volar/vue-language-core@1.6.4":
version "1.6.4"
resolved "https://registry.yarnpkg.com/@volar/vue-language-core/-/vue-language-core-1.6.4.tgz#b1d695861945e63c65ff4e74609b07cb06772b7c"
integrity sha512-1o+cAtN2DIDNAX/HS8rkjZc8wTMTK+zCab/qtYbvEVlmokhZiDrQeoD9/l0Ug7YCNg+mVuMNHKNBY7pX8U2/Jw==
"@volar/vue-language-core@1.6.5":
version "1.6.5"
resolved "https://registry.yarnpkg.com/@volar/vue-language-core/-/vue-language-core-1.6.5.tgz#db42520f1a29737c7e40fbb3e6aead8def85ba75"
integrity sha512-IF2b6hW4QAxfsLd5mePmLgtkXzNi+YnH6ltCd80gb7+cbdpFMjM1I+w+nSg2kfBTyfu+W8useCZvW89kPTBpzg==
dependencies:
"@volar/language-core" "1.4.1"
"@volar/source-map" "1.4.1"
"@vue/compiler-dom" "^3.3.0-beta.3"
"@vue/compiler-sfc" "^3.3.0-beta.3"
"@vue/reactivity" "^3.3.0-beta.3"
"@vue/shared" "^3.3.0-beta.3"
"@vue/compiler-dom" "^3.3.0"
"@vue/compiler-sfc" "^3.3.0"
"@vue/reactivity" "^3.3.0"
"@vue/shared" "^3.3.0"
minimatch "^9.0.0"
muggle-string "^0.2.2"
vue-template-compiler "^2.7.14"
"@volar/vue-typescript@1.6.4":
version "1.6.4"
resolved "https://registry.yarnpkg.com/@volar/vue-typescript/-/vue-typescript-1.6.4.tgz#9358e2c7cdb5bdc3ef05926084be4bb6cd3673f7"
integrity sha512-qKwgP0KVQR/aaH/SN3AP7RB8NnXPWDn3tjyXP6IT6etxkDeZLBLsXWUD9KMak/RvV1DgbXDuz4F9yuZlbt29rA==
"@volar/vue-typescript@1.6.5":
version "1.6.5"
resolved "https://registry.yarnpkg.com/@volar/vue-typescript/-/vue-typescript-1.6.5.tgz#6ca9bfefa5dc64ff97fcdbc74249e5e7da44e533"
integrity sha512-er9rVClS4PHztMUmtPMDTl+7c7JyrxweKSAEe/o/Noeq2bQx6v3/jZHVHBe8ZNUti5ubJL/+Tg8L3bzmlalV8A==
dependencies:
"@volar/typescript" "1.4.1"
"@volar/vue-language-core" "1.6.4"
"@volar/typescript" "1.4.1-patch.2"
"@volar/vue-language-core" "1.6.5"
"@vue/compiler-core@3.2.47":
version "3.2.47"
@ -529,13 +535,23 @@
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-core@3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.0-beta.3.tgz#3cc98d9ef25d2f890717f8a15fe65870910e984f"
integrity sha512-mv2rPo4JHou6ebm7+U/wO1HpA6W1zDfTqbt4fqjoXrMwU4DWNgRcLKTXG6G3cXV4mOe+2YgWspfxEzo7fPTMKg==
"@vue/compiler-core@3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.2.tgz#39567bd15c7f97add97bfc4d44e814df36eb797b"
integrity sha512-CKZWo1dzsQYTNTft7whzjL0HsrEpMfiK7pjZ2WFE3bC1NA7caUjWioHSK+49y/LK7Bsm4poJZzAMnvZMQ7OTeg==
dependencies:
"@babel/parser" "^7.21.3"
"@vue/shared" "3.3.0-beta.3"
"@vue/shared" "3.3.2"
estree-walker "^2.0.2"
source-map-js "^1.0.2"
"@vue/compiler-core@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.4.tgz#7fbf591c1c19e1acd28ffd284526e98b4f581128"
integrity sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==
dependencies:
"@babel/parser" "^7.21.3"
"@vue/shared" "3.3.4"
estree-walker "^2.0.2"
source-map-js "^1.0.2"
@ -547,15 +563,39 @@
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-dom@3.3.0-beta.3", "@vue/compiler-dom@^3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.0-beta.3.tgz#8b1d76428413d51d76a7b3f567ccea47428d5ebe"
integrity sha512-e7VpjN9wYiuJdJos6Uoe501CzdMkfaEr/27Ks4Ss7Irtcj5YA/S1OROZ35Xl2Pc3ctx6beq5RpcOvnMqh0hcaA==
"@vue/compiler-dom@3.3.2", "@vue/compiler-dom@^3.3.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.2.tgz#2012ef4879375a4ca4ee68012a9256398b848af2"
integrity sha512-6gS3auANuKXLw0XH6QxkWqyPYPunziS2xb6VRenM3JY7gVfZcJvkCBHkb5RuNY1FCbBO3lkIi0CdXUCW1c7SXw==
dependencies:
"@vue/compiler-core" "3.3.0-beta.3"
"@vue/shared" "3.3.0-beta.3"
"@vue/compiler-core" "3.3.2"
"@vue/shared" "3.3.2"
"@vue/compiler-sfc@3.2.47", "@vue/compiler-sfc@^3.2.47":
"@vue/compiler-dom@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz#f56e09b5f4d7dc350f981784de9713d823341151"
integrity sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==
dependencies:
"@vue/compiler-core" "3.3.4"
"@vue/shared" "3.3.4"
"@vue/compiler-sfc@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz#b19d942c71938893535b46226d602720593001df"
integrity sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==
dependencies:
"@babel/parser" "^7.20.15"
"@vue/compiler-core" "3.3.4"
"@vue/compiler-dom" "3.3.4"
"@vue/compiler-ssr" "3.3.4"
"@vue/reactivity-transform" "3.3.4"
"@vue/shared" "3.3.4"
estree-walker "^2.0.2"
magic-string "^0.30.0"
postcss "^8.1.10"
source-map-js "^1.0.2"
"@vue/compiler-sfc@^3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
@ -571,17 +611,17 @@
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-sfc@^3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.0-beta.3.tgz#8d60a57eaf6a8675463c4640124dafecf78c8961"
integrity sha512-6shZNooetShjSMHJvgVoE0EM8pOMV5vnrzsHoCU06stzV+kqRJQpbN7xf2s9wK2fgHMIBSMINrM9AuZiQnNCJg==
"@vue/compiler-sfc@^3.3.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.2.tgz#d6467acba8446655bcee7e751441232e5ddebcbf"
integrity sha512-jG4jQy28H4BqzEKsQqqW65BZgmo3vzdLHTBjF+35RwtDdlFE+Fk1VWJYUnDMMqkFBo6Ye1ltSKVOMPgkzYj7SQ==
dependencies:
"@babel/parser" "^7.20.15"
"@vue/compiler-core" "3.3.0-beta.3"
"@vue/compiler-dom" "3.3.0-beta.3"
"@vue/compiler-ssr" "3.3.0-beta.3"
"@vue/reactivity-transform" "3.3.0-beta.3"
"@vue/shared" "3.3.0-beta.3"
"@vue/compiler-core" "3.3.2"
"@vue/compiler-dom" "3.3.2"
"@vue/compiler-ssr" "3.3.2"
"@vue/reactivity-transform" "3.3.2"
"@vue/shared" "3.3.2"
estree-walker "^2.0.2"
magic-string "^0.30.0"
postcss "^8.1.10"
@ -595,19 +635,32 @@
"@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-ssr@3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.0-beta.3.tgz#179e2c0a4de617addfb6ebc0776225265f5c7829"
integrity sha512-egJ0lEVAod3Hpnw96cJ/0a9qv5f5h5/VCBpKYT8scqkzoMsikh8AJant2omokBCL/Ut5UAMLVQlA5b66+2Ys/g==
"@vue/compiler-ssr@3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.2.tgz#75ac4ccafa2d78c91d2e257ad243c86409493cc4"
integrity sha512-K8OfY5FQtZaSOJHHe8xhEfIfLrefL/Y9frv4k4NsyQL3+0lRKxr9QuJhfdBDjkl7Fhz8CzKh63mULvmOfx3l2w==
dependencies:
"@vue/compiler-dom" "3.3.0-beta.3"
"@vue/shared" "3.3.0-beta.3"
"@vue/compiler-dom" "3.3.2"
"@vue/shared" "3.3.2"
"@vue/devtools-api@^6.2.1", "@vue/devtools-api@^6.4.5":
"@vue/compiler-ssr@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz#9d1379abffa4f2b0cd844174ceec4a9721138777"
integrity sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==
dependencies:
"@vue/compiler-dom" "3.3.4"
"@vue/shared" "3.3.4"
"@vue/devtools-api@^6.2.1":
version "6.4.5"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380"
integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==
"@vue/devtools-api@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==
"@vue/eslint-config-typescript@^11.0.3":
version "11.0.3"
resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz#c720efa657d102cd2945bc54b4a79f35d57f6307"
@ -628,70 +681,86 @@
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity-transform@3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.0-beta.3.tgz#424a70d43fb4893131b2c1fba9a9e6806ebeabe0"
integrity sha512-aM3TgBca9QMMu/9B9ASRVvckeZpAdJO9nmQh5UCznhoDYjVxQPS+sCQvH6TLOjPB1MDQMVQYg4ZiPqfVVo7NbA==
"@vue/reactivity-transform@3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.2.tgz#e1991d52d7ecefb65b214d8a3385a9dbe2cca74c"
integrity sha512-iu2WaQvlJHdnONrsyv4ibIEnSsuKF+aHFngGj/y1lwpHQtalpVhKg9wsKMoiKXS9zPNjG9mNKzJS9vudvjzvyg==
dependencies:
"@babel/parser" "^7.20.15"
"@vue/compiler-core" "3.3.0-beta.3"
"@vue/shared" "3.3.0-beta.3"
"@vue/compiler-core" "3.3.2"
"@vue/shared" "3.3.2"
estree-walker "^2.0.2"
magic-string "^0.30.0"
"@vue/reactivity@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.47.tgz#1d6399074eadfc3ed35c727e2fd707d6881140b6"
integrity sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==
"@vue/reactivity-transform@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz#52908476e34d6a65c6c21cd2722d41ed8ae51929"
integrity sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==
dependencies:
"@vue/shared" "3.2.47"
"@babel/parser" "^7.20.15"
"@vue/compiler-core" "3.3.4"
"@vue/shared" "3.3.4"
estree-walker "^2.0.2"
magic-string "^0.30.0"
"@vue/reactivity@^3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.3.0-beta.3.tgz#8f4929df4195f9d1b5a083d4c66d22932dfcc89d"
integrity sha512-9VjWfWgZJ18YXEkfnDfZr33RyLBa6zc0RARLkMqMApWvM26eusZAZ4hhyxlgODBU/mEFk4XOGIAtwwSQedA0MQ==
"@vue/reactivity@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.3.4.tgz#a27a29c6cd17faba5a0e99fbb86ee951653e2253"
integrity sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==
dependencies:
"@vue/shared" "3.3.0-beta.3"
"@vue/shared" "3.3.4"
"@vue/runtime-core@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz#406ebade3d5551c00fc6409bbc1eeb10f32e121d"
integrity sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==
"@vue/reactivity@^3.3.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.3.2.tgz#c4ddc5087039070c0c11810f6bc1aa59c99f0cb5"
integrity sha512-yX8C4uTgg2Tdj+512EEMnMKbLveoITl7YdQX35AYgx8vBvQGszKiiCN46g4RY6/deeo/5DLbeUUGxCq1qWMf5g==
dependencies:
"@vue/reactivity" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/shared" "3.3.2"
"@vue/runtime-dom@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz#93e760eeaeab84dedfb7c3eaf3ed58d776299382"
integrity sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==
"@vue/runtime-core@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.3.4.tgz#4bb33872bbb583721b340f3088888394195967d1"
integrity sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==
dependencies:
"@vue/runtime-core" "3.2.47"
"@vue/shared" "3.2.47"
csstype "^2.6.8"
"@vue/reactivity" "3.3.4"
"@vue/shared" "3.3.4"
"@vue/server-renderer@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz#8aa1d1871fc4eb5a7851aa7f741f8f700e6de3c0"
integrity sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==
"@vue/runtime-dom@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz#992f2579d0ed6ce961f47bbe9bfe4b6791251566"
integrity sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==
dependencies:
"@vue/compiler-ssr" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/runtime-core" "3.3.4"
"@vue/shared" "3.3.4"
csstype "^3.1.1"
"@vue/server-renderer@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.3.4.tgz#ea46594b795d1536f29bc592dd0f6655f7ea4c4c"
integrity sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==
dependencies:
"@vue/compiler-ssr" "3.3.4"
"@vue/shared" "3.3.4"
"@vue/shared@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c"
integrity sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==
"@vue/shared@3.3.0-beta.3", "@vue/shared@^3.3.0-beta.3":
version "3.3.0-beta.3"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.0-beta.3.tgz#dc19df6124e157030b3e7c8f471f497f9528a7be"
integrity sha512-st1SnB/Bkbb9TsieeI4TRX9TqHYIR5wvIma3ZtEben55EYSWa1q5u2BhTNgABSdH+rv3Xwfrvpwh5PmCw6Y53g==
"@vue/shared@3.3.2", "@vue/shared@^3.3.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
"@vue/tsconfig@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.3.2.tgz#612ba0b6aefde5ac1a513545eee7d4ed01c407f5"
integrity sha512-jWzZbGyrZAEbHYGn0kPzJ+MMtIkIxb0+hL5+RghBowyOxMRs9jMdp5XvpXz3wgCzjRZiUucy29042HBe9cxoYA==
"@vue/shared@3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.4.tgz#06e83c5027f464eef861c329be81454bc8b70780"
integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==
"@vue/tsconfig@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.4.0.tgz#f01e2f6089b5098136fb084a0dd0cdd4533b72b0"
integrity sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==
acorn-jsx@^5.2.0, acorn-jsx@^5.3.2:
version "5.3.2"
@ -780,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"
integrity sha512-BzqmLufgHjFvSReJ1GQqNkl780UFK0rWT4Y1IQC7lZClXyOSsM5Ipw04BnuVmmrqgtSxzak83jcBwLJgCK3scg==
bootstrap@^5.3.0-alpha3:
version "5.3.0-alpha3"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.0-alpha3.tgz#ad64d9a663c53ab7aca99c560e0bd16b5e023441"
integrity sha512-FBhOWMxkCFr74hesJdchLXhqagPTXS+kRNU3gE0FR5Ki/AdPSz32Ik96Z28+yBluCnE/pc9st7l1yPwKgbtfSA==
bootstrap@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.0.tgz#0718a7cc29040ee8dbf1bd652b896f3436a87c29"
integrity sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==
brace-expansion@^1.1.7:
version "1.1.11"
@ -916,10 +985,10 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
csstype@^2.6.8:
version "2.6.20"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
csstype@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
de-indent@^1.0.2:
version "1.0.2"
@ -1056,17 +1125,17 @@ escodegen@^2.0.0:
optionalDependencies:
source-map "~0.6.1"
eslint-plugin-vue@^9.11.1:
version "9.11.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.11.1.tgz#71e37cc74ac0a9bca07d2e8c471a047b8ac254fb"
integrity sha512-SNtBGDrRkPUFsREswPceqdvZ7YVdWY+iCYiDC+RoxwVieeQ7GJU1FLDlkcaYTOD2os/YuVgI1Fdu8YGM7fmoow==
eslint-plugin-vue@^9.14.1:
version "9.14.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz#3b0c9857642dac547c7564031cfb09d283eafdd4"
integrity sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==
dependencies:
"@eslint-community/eslint-utils" "^4.3.0"
natural-compare "^1.4.0"
nth-check "^2.0.1"
postcss-selector-parser "^6.0.9"
semver "^7.3.5"
vue-eslint-parser "^9.0.1"
vue-eslint-parser "^9.3.0"
xml-name-validator "^4.0.0"
eslint-scope@^5.1.1:
@ -1115,15 +1184,15 @@ eslint-visitor-keys@^3.4.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
eslint@^8.40.0:
version "8.40.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.40.0.tgz#a564cd0099f38542c4e9a2f630fa45bf33bc42a4"
integrity sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==
eslint@^8.41.0:
version "8.41.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.41.0.tgz#3062ca73363b4714b16dbc1e60f035e6134b6f1c"
integrity sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.4.0"
"@eslint/eslintrc" "^2.0.3"
"@eslint/js" "8.40.0"
"@eslint/js" "8.41.0"
"@humanwhocodes/config-array" "^0.11.8"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
@ -1143,13 +1212,12 @@ eslint@^8.40.0:
find-up "^5.0.0"
glob-parent "^6.0.2"
globals "^13.19.0"
grapheme-splitter "^1.0.4"
graphemer "^1.4.0"
ignore "^5.2.0"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
is-path-inside "^3.0.3"
js-sdsl "^4.1.4"
js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
@ -1424,6 +1492,11 @@ grapheme-splitter@^1.0.4:
resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
@ -1641,11 +1714,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
js-sdsl@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@ -1684,6 +1752,11 @@ jsonc-eslint-parser@^1.0.1:
espree "^6.0.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:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@ -1794,6 +1867,16 @@ mitt@^3.0.0:
resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
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:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -1983,7 +2066,7 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pathe@^1.0.0:
pathe@^1.0.0, pathe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==
@ -2008,6 +2091,15 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
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:
version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
@ -2204,6 +2296,11 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@ -2321,10 +2418,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
terser@^5.17.3:
version "5.17.3"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.3.tgz#7f908f16b3cdf3f6c0f8338e6c1c674837f90d25"
integrity sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==
terser@^5.17.6:
version "5.17.6"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de"
integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==
dependencies:
"@jridgewell/source-map" "^0.3.2"
acorn "^8.5.0"
@ -2379,6 +2476,11 @@ typescript@^5.0.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@ -2438,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"
integrity sha512-mwrFvEEy0TuH8Ul0cb2HgjmNboQ/JnEFy+kHCWqAJph3ikMOiIuyYVdx0JO4nEIWJyzSnc4TTdmoTulsikvJEg==
vite@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.5.tgz#3871fe0f4b582ea7f49a85386ac80e84826367d9"
integrity sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==
vite@^4.3.9:
version "4.3.9"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d"
integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==
dependencies:
esbuild "^0.17.5"
postcss "^8.4.23"
@ -2449,10 +2551,10 @@ vite@^4.3.5:
optionalDependencies:
fsevents "~2.3.2"
vue-eslint-parser@^9.0.1:
version "9.0.2"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz#d2535516f3f55adb387939427fe741065eb7948a"
integrity sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==
vue-eslint-parser@^9.1.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.1.1.tgz#3f4859be7e9bb7edaa1dc7edb05abffee72bf3dd"
integrity sha512-C2aI/r85Q6tYcz4dpgvrs4wH/MqVrRAVIdpYedrxnATDHHkb+TroeRcDpKWGZCx/OcECMWfz7tVwQ8e+Opy6rA==
dependencies:
debug "^4.3.4"
eslint-scope "^7.1.1"
@ -2462,10 +2564,10 @@ vue-eslint-parser@^9.0.1:
lodash "^4.17.21"
semver "^7.3.6"
vue-eslint-parser@^9.1.1:
version "9.1.1"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.1.1.tgz#3f4859be7e9bb7edaa1dc7edb05abffee72bf3dd"
integrity sha512-C2aI/r85Q6tYcz4dpgvrs4wH/MqVrRAVIdpYedrxnATDHHkb+TroeRcDpKWGZCx/OcECMWfz7tVwQ8e+Opy6rA==
vue-eslint-parser@^9.3.0:
version "9.3.0"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.3.0.tgz#775a974a0603c9a73d85fed8958ed9e814a4a816"
integrity sha512-48IxT9d0+wArT1+3wNIy0tascRoywqSUe2E1YalIC1L8jsUGe5aJQItWfRok7DVFGz3UYvzEI7n5wiTXsCMAcQ==
dependencies:
debug "^4.3.4"
eslint-scope "^7.1.1"
@ -2485,12 +2587,12 @@ vue-i18n@^9.2.2:
"@intlify/vue-devtools" "9.2.2"
"@vue/devtools-api" "^6.2.1"
vue-router@^4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1"
integrity sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==
vue-router@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.2.tgz#b0097b66d89ca81c0986be03da244c7b32a4fd81"
integrity sha512-cChBPPmAflgBGmy3tBsjeoe3f3VOSG6naKyY5pjtrqLGbNEXdzCigFUHgBvp9e3ysAtFtEx7OLqcSDh/1Cq2TQ==
dependencies:
"@vue/devtools-api" "^6.4.5"
"@vue/devtools-api" "^6.5.0"
vue-template-compiler@^2.7.14:
version "2.7.14"
@ -2500,25 +2602,25 @@ vue-template-compiler@^2.7.14:
de-indent "^1.0.2"
he "^1.2.0"
vue-tsc@^1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.6.4.tgz#ca4e931e9d3b9c55cd7a0f551bc0c9536edb6386"
integrity sha512-8rg8S1AhRJ6/WriENQEhyqH5wsxSxuD5iaD+QnkZn2ArZ6evlhqfBAIcVN8mfSyCV9DeLkQXkOSv/MaeJiJPAQ==
vue-tsc@^1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.6.5.tgz#cd18804b12087c300b6c9ee2a1da41a63f11103e"
integrity sha512-Wtw3J7CC+JM2OR56huRd5iKlvFWpvDiU+fO1+rqyu4V2nMTotShz4zbOZpW5g9fUOcjnyZYfBo5q5q+D/q27JA==
dependencies:
"@volar/vue-language-core" "1.6.4"
"@volar/vue-typescript" "1.6.4"
"@volar/vue-language-core" "1.6.5"
"@volar/vue-typescript" "1.6.5"
semver "^7.3.8"
vue@^3.2.47:
version "3.2.47"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.47.tgz#3eb736cbc606fc87038dbba6a154707c8a34cff0"
integrity sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==
vue@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.3.4.tgz#8ed945d3873667df1d0fcf3b2463ada028f88bd6"
integrity sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==
dependencies:
"@vue/compiler-dom" "3.2.47"
"@vue/compiler-sfc" "3.2.47"
"@vue/runtime-dom" "3.2.47"
"@vue/server-renderer" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-dom" "3.3.4"
"@vue/compiler-sfc" "3.3.4"
"@vue/runtime-dom" "3.3.4"
"@vue/server-renderer" "3.3.4"
"@vue/shared" "3.3.4"
webpack-sources@^3.2.3:
version "3.2.3"

Binary file not shown.