Merge remote-tracking branch

'tbnobody/OpenDTU/master'
This commit is contained in:
helgeerbe 2022-10-07 10:32:51 +02:00
commit 366e7dc409
40 changed files with 736 additions and 243 deletions

View File

@ -64,6 +64,7 @@ Sends text raw data as difined in VE.Direct spec.
* Show inverters internal event log * Show inverters internal event log
* Show inverter information like firmware version, firmware build date, hardware revision and hardware version * Show inverter information like firmware version, firmware build date, hardware revision and hardware version
* Show and set the current inverter limit * Show and set the current inverter limit
* Function to turn the inverter off an on
* Uses ESP32 microcontroller and NRF24L01+ * Uses ESP32 microcontroller and NRF24L01+
* Multi-Inverter support * Multi-Inverter support
* MQTT support (with TLS) * MQTT support (with TLS)
@ -95,7 +96,7 @@ Sample Picture:
Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware) Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware)
#### NRF24L01+ radio board ### NRF24L01+ radio board
The PLUS sign is IMPORTANT! There are different variants available, with antenna on the printed circuit board or external antenna. The PLUS sign is IMPORTANT! There are different variants available, with antenna on the printed circuit board or external antenna.
Sample picture: Sample picture:
@ -113,7 +114,7 @@ A heavily incomplete list of trusted hardware shops in germany is:
This list is for your convenience only, the project is not related to any of these shops. This list is for your convenience only, the project is not related to any of these shops.
#### Power supply ### Power supply
Use a power suppy with 5V and 1A. The USB cable connected to your PC/Notebook may be powerful enough or may be not. Use a power suppy with 5V and 1A. The USB cable connected to your PC/Notebook may be powerful enough or may be not.

View File

@ -66,4 +66,5 @@ cmd topics are used to set values. Status topics are updated from values set in
| [serial]/cmd/limit_persistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % | | [serial]/cmd/limit_persistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % |
| [serial]/cmd/limit_persistent_absolute | W | Set the inverter limit as a absolute value. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) | | [serial]/cmd/limit_persistent_absolute | W | Set the inverter limit as a absolute value. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) |
| [serial]/cmd/limit_nonpersistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % | | [serial]/cmd/limit_nonpersistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % |
| [serial]/cmd/limit_nonpersistent_absolute | W | Set the inverter limit as a absolute value. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) | | [serial]/cmd/limit_nonpersistent_absolute | W | Set the inverter limit as a absolute value. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic after around 4 minutes. | Watt (W) |
| [serial]/cmd/power | W | Turn the inverter on (1) or off (0) | 0 or 1 |

View File

@ -11,6 +11,7 @@
#include "WebApi_mqtt.h" #include "WebApi_mqtt.h"
#include "WebApi_network.h" #include "WebApi_network.h"
#include "WebApi_ntp.h" #include "WebApi_ntp.h"
#include "WebApi_power.h"
#include "WebApi_sysstatus.h" #include "WebApi_sysstatus.h"
#include "WebApi_webapp.h" #include "WebApi_webapp.h"
#include "WebApi_ws_live.h" #include "WebApi_ws_live.h"
@ -38,6 +39,7 @@ private:
WebApiMqttClass _webApiMqtt; WebApiMqttClass _webApiMqtt;
WebApiNetworkClass _webApiNetwork; WebApiNetworkClass _webApiNetwork;
WebApiNtpClass _webApiNtp; WebApiNtpClass _webApiNtp;
WebApiPowerClass _webApiPower;
WebApiSysstatusClass _webApiSysstatus; WebApiSysstatusClass _webApiSysstatus;
WebApiWebappClass _webApiWebapp; WebApiWebappClass _webApiWebapp;
WebApiWsLiveClass _webApiWsLive; WebApiWsLiveClass _webApiWsLive;

16
include/WebApi_power.h Normal file
View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiPowerClass {
public:
void init(AsyncWebServer* server);
void loop();
private:
void onPowerStatus(AsyncWebServerRequest* request);
void onPowerPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -46,8 +46,14 @@ void HoymilesClass::loop()
iv->resendActivePowerControlRequest(_radio.get()); iv->resendActivePowerControlRequest(_radio.get());
} }
// Set power status if required
if (iv->PowerCommand()->getLastPowerCommandSuccess() == CMD_NOK) {
Serial.println(F("Resend PowerCommand"));
iv->resendPowerControlRequest(_radio.get());
}
// Fetch dev info (but first fetch stats) // Fetch dev info (but first fetch stats)
if (iv->Statistics()->getLastUpdate() > 0 && (iv->DevInfo()->getLastUpdateAll() == 0 || iv->DevInfo()->getLastUpdateSample() == 0)) { if (iv->Statistics()->getLastUpdate() > 0 && (iv->DevInfo()->getLastUpdateAll() == 0 || iv->DevInfo()->getLastUpdateSimple() == 0)) {
Serial.println(F("Request device info")); Serial.println(F("Request device info"));
iv->sendDevInfoRequest(_radio.get()); iv->sendDevInfoRequest(_radio.get());
} }

View File

@ -28,6 +28,6 @@ bool DevInfoSimpleCommand::handleResponse(InverterAbstract* inverter, fragment_t
inverter->DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len); inverter->DevInfo()->appendFragmentSimple(offs, fragment[i].fragment, fragment[i].len);
offs += (fragment[i].len); offs += (fragment[i].len);
} }
inverter->DevInfo()->setLastUpdateSample(millis()); inverter->DevInfo()->setLastUpdateSimple(millis());
return true; return true;
} }

View File

@ -61,7 +61,7 @@ CommandAbstract* MultiDataCommand::getRequestFrameCommand(uint8_t frame_no)
bool MultiDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id) bool MultiDataCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id)
{ {
// All fragments are available --> Check CRC // All fragments are available --> Check CRC
uint16_t crc = 0xffff, crcRcv; 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++) {
if (i == max_fragment_id - 1) { if (i == max_fragment_id - 1) {

View File

@ -0,0 +1,56 @@
#include "PowerControlCommand.h"
#include "inverters/InverterAbstract.h"
#define CRC_SIZE 2
PowerControlCommand::PowerControlCommand(uint64_t target_address, uint64_t router_address)
: DevControlCommand(target_address, router_address)
{
_payload[10] = 0x00; // TurnOn
_payload[11] = 0x00;
udpateCRC(CRC_SIZE); // 2 byte crc
_payload_size = 14;
setTimeout(2000);
}
String PowerControlCommand::getCommandName()
{
return "PowerControl";
}
bool PowerControlCommand::handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id)
{
if (!DevControlCommand::handleResponse(inverter, fragment, max_fragment_id)) {
return false;
}
inverter->PowerCommand()->setLastUpdateCommand(millis());
inverter->PowerCommand()->setLastPowerCommandSuccess(CMD_OK);
return true;
}
void PowerControlCommand::gotTimeout(InverterAbstract* inverter)
{
inverter->PowerCommand()->setLastPowerCommandSuccess(CMD_NOK);
}
void PowerControlCommand::setPowerOn(bool state)
{
if (state) {
_payload[10] = 0x00; // TurnOn
} else {
_payload[10] = 0x01; // TurnOff
}
udpateCRC(CRC_SIZE); // 2 byte crc
}
void PowerControlCommand::setRestart()
{
_payload[10] = 0x02; // Restart
udpateCRC(CRC_SIZE); // 2 byte crc
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "DevControlCommand.h"
class PowerControlCommand : public DevControlCommand {
public:
explicit PowerControlCommand(uint64_t target_address = 0, uint64_t router_address = 0);
virtual String getCommandName();
virtual bool handleResponse(InverterAbstract* inverter, fragment_t fragment[], uint8_t max_fragment_id);
virtual void gotTimeout(InverterAbstract* inverter);
void setPowerOn(bool state);
void setRestart();
};

View File

@ -4,6 +4,7 @@
#include "commands/AlarmDataCommand.h" #include "commands/AlarmDataCommand.h"
#include "commands/DevInfoAllCommand.h" #include "commands/DevInfoAllCommand.h"
#include "commands/DevInfoSimpleCommand.h" #include "commands/DevInfoSimpleCommand.h"
#include "commands/PowerControlCommand.h"
#include "commands/RealTimeRunDataCommand.h" #include "commands/RealTimeRunDataCommand.h"
#include "commands/SystemConfigParaCommand.h" #include "commands/SystemConfigParaCommand.h"
@ -69,9 +70,9 @@ bool HM_Abstract::sendDevInfoRequest(HoymilesRadio* radio)
cmdAll->setTime(now); cmdAll->setTime(now);
cmdAll->setTargetAddress(serial()); cmdAll->setTargetAddress(serial());
DevInfoSimpleCommand* cmdSample = radio->enqueCommand<DevInfoSimpleCommand>(); DevInfoSimpleCommand* cmdSimple = radio->enqueCommand<DevInfoSimpleCommand>();
cmdSample->setTime(now); cmdSimple->setTime(now);
cmdSample->setTargetAddress(serial()); cmdSimple->setTargetAddress(serial());
return true; return true;
} }
@ -110,4 +111,21 @@ bool HM_Abstract::sendActivePowerControlRequest(HoymilesRadio* radio, float limi
bool HM_Abstract::resendActivePowerControlRequest(HoymilesRadio* radio) bool HM_Abstract::resendActivePowerControlRequest(HoymilesRadio* radio)
{ {
return sendActivePowerControlRequest(radio, _activePowerControlLimit, _activePowerControlType); return sendActivePowerControlRequest(radio, _activePowerControlLimit, _activePowerControlType);
}
bool HM_Abstract::sendPowerControlRequest(HoymilesRadio* radio, bool turnOn)
{
_powerState = turnOn;
PowerControlCommand* cmd = radio->enqueCommand<PowerControlCommand>();
cmd->setPowerOn(turnOn);
cmd->setTargetAddress(serial());
PowerCommand()->setLastPowerCommandSuccess(CMD_PENDING);
return true;
}
bool HM_Abstract::resendPowerControlRequest(HoymilesRadio* radio)
{
return sendPowerControlRequest(radio, _powerState);
} }

View File

@ -11,9 +11,13 @@ public:
bool sendSystemConfigParaRequest(HoymilesRadio* radio); bool sendSystemConfigParaRequest(HoymilesRadio* radio);
bool sendActivePowerControlRequest(HoymilesRadio* radio, float limit, PowerLimitControlType type); bool sendActivePowerControlRequest(HoymilesRadio* radio, float limit, PowerLimitControlType type);
bool resendActivePowerControlRequest(HoymilesRadio* radio); bool resendActivePowerControlRequest(HoymilesRadio* radio);
bool sendPowerControlRequest(HoymilesRadio* radio, bool turnOn);
bool resendPowerControlRequest(HoymilesRadio* radio);
private: private:
uint8_t _lastAlarmLogCnt = 0; uint8_t _lastAlarmLogCnt = 0;
float _activePowerControlLimit = 0; float _activePowerControlLimit = 0;
PowerLimitControlType _activePowerControlType = PowerLimitControlType::AbsolutNonPersistent; PowerLimitControlType _activePowerControlType = PowerLimitControlType::AbsolutNonPersistent;
bool _powerState = true;
}; };

View File

@ -7,6 +7,7 @@ InverterAbstract::InverterAbstract(uint64_t serial)
_serial.u64 = serial; _serial.u64 = serial;
_alarmLogParser.reset(new AlarmLogParser()); _alarmLogParser.reset(new AlarmLogParser());
_devInfoParser.reset(new DevInfoParser()); _devInfoParser.reset(new DevInfoParser());
_powerCommandParser.reset(new PowerCommandParser());
_statisticsParser.reset(new StatisticsParser()); _statisticsParser.reset(new StatisticsParser());
_systemConfigParaParser.reset(new SystemConfigParaParser()); _systemConfigParaParser.reset(new SystemConfigParaParser());
} }
@ -64,6 +65,11 @@ DevInfoParser* InverterAbstract::DevInfo()
return _devInfoParser.get(); return _devInfoParser.get();
} }
PowerCommandParser* InverterAbstract::PowerCommand()
{
return _powerCommandParser.get();
}
StatisticsParser* InverterAbstract::Statistics() StatisticsParser* InverterAbstract::Statistics()
{ {
return _statisticsParser.get(); return _statisticsParser.get();

View File

@ -3,6 +3,7 @@
#include "../commands/ActivePowerControlCommand.h" #include "../commands/ActivePowerControlCommand.h"
#include "../parser/AlarmLogParser.h" #include "../parser/AlarmLogParser.h"
#include "../parser/DevInfoParser.h" #include "../parser/DevInfoParser.h"
#include "../parser/PowerCommandParser.h"
#include "../parser/StatisticsParser.h" #include "../parser/StatisticsParser.h"
#include "../parser/SystemConfigParaParser.h" #include "../parser/SystemConfigParaParser.h"
#include "HoymilesRadio.h" #include "HoymilesRadio.h"
@ -51,9 +52,12 @@ public:
virtual bool sendSystemConfigParaRequest(HoymilesRadio* radio) = 0; virtual bool sendSystemConfigParaRequest(HoymilesRadio* radio) = 0;
virtual bool sendActivePowerControlRequest(HoymilesRadio* radio, float limit, PowerLimitControlType type) = 0; virtual bool sendActivePowerControlRequest(HoymilesRadio* radio, float limit, PowerLimitControlType type) = 0;
virtual bool resendActivePowerControlRequest(HoymilesRadio* radio) = 0; virtual bool resendActivePowerControlRequest(HoymilesRadio* radio) = 0;
virtual bool sendPowerControlRequest(HoymilesRadio* radio, bool turnOn) = 0;
virtual bool resendPowerControlRequest(HoymilesRadio* radio) = 0;
AlarmLogParser* EventLog(); AlarmLogParser* EventLog();
DevInfoParser* DevInfo(); DevInfoParser* DevInfo();
PowerCommandParser* PowerCommand();
StatisticsParser* Statistics(); StatisticsParser* Statistics();
SystemConfigParaParser* SystemConfigPara(); SystemConfigParaParser* SystemConfigPara();
@ -67,6 +71,7 @@ private:
std::unique_ptr<AlarmLogParser> _alarmLogParser; std::unique_ptr<AlarmLogParser> _alarmLogParser;
std::unique_ptr<DevInfoParser> _devInfoParser; std::unique_ptr<DevInfoParser> _devInfoParser;
std::unique_ptr<PowerCommandParser> _powerCommandParser;
std::unique_ptr<StatisticsParser> _statisticsParser; std::unique_ptr<StatisticsParser> _statisticsParser;
std::unique_ptr<SystemConfigParaParser> _systemConfigParaParser; std::unique_ptr<SystemConfigParaParser> _systemConfigParaParser;
}; };

View File

@ -40,12 +40,12 @@ void AlarmLogParser::getLogEntry(uint8_t entryId, AlarmLogEntry_t* entry)
uint32_t wcode = (uint16_t)_payloadAlarmLog[entryStartOffset] << 8 | _payloadAlarmLog[entryStartOffset + 1]; uint32_t wcode = (uint16_t)_payloadAlarmLog[entryStartOffset] << 8 | _payloadAlarmLog[entryStartOffset + 1];
uint32_t startTimeOffset = 0; uint32_t startTimeOffset = 0;
if ((wcode >> 13) & 0x01 == 1) { if (((wcode >> 13) & 0x01) == 1) {
startTimeOffset = 12 * 60 * 60; startTimeOffset = 12 * 60 * 60;
} }
uint32_t endTimeOffset = 0; uint32_t endTimeOffset = 0;
if ((wcode >> 12) & 0x01 == 1) { if (((wcode >> 12) & 0x01) == 1) {
endTimeOffset = 12 * 60 * 60; endTimeOffset = 12 * 60 * 60;
} }

View File

@ -26,7 +26,7 @@ void DevInfoParser::clearBufferSimple()
void DevInfoParser::appendFragmentSimple(uint8_t offset, uint8_t* payload, uint8_t len) void DevInfoParser::appendFragmentSimple(uint8_t offset, uint8_t* payload, uint8_t len)
{ {
if (offset + len > DEV_INFO_SIZE) { if (offset + len > DEV_INFO_SIZE) {
Serial.printf("FATAL: (%s, %d) dev info Sample packet too large for buffer\n", __FILE__, __LINE__); Serial.printf("FATAL: (%s, %d) dev info Simple packet too large for buffer\n", __FILE__, __LINE__);
return; return;
} }
memcpy(&_payloadDevInfoSimple[offset], payload, len); memcpy(&_payloadDevInfoSimple[offset], payload, len);
@ -44,14 +44,14 @@ void DevInfoParser::setLastUpdateAll(uint32_t lastUpdate)
setLastUpdate(lastUpdate); setLastUpdate(lastUpdate);
} }
uint32_t DevInfoParser::getLastUpdateSample() uint32_t DevInfoParser::getLastUpdateSimple()
{ {
return _lastUpdateSample; return _lastUpdateSimple;
} }
void DevInfoParser::setLastUpdateSample(uint32_t lastUpdate) void DevInfoParser::setLastUpdateSimple(uint32_t lastUpdate)
{ {
_lastUpdateSample = lastUpdate; _lastUpdateSimple = lastUpdate;
setLastUpdate(lastUpdate); setLastUpdate(lastUpdate);
} }
@ -90,9 +90,11 @@ uint32_t DevInfoParser::getHwPartNumber()
return ((uint32_t)hwpn_h << 16) | ((uint32_t)hwpn_l); return ((uint32_t)hwpn_h << 16) | ((uint32_t)hwpn_l);
} }
uint16_t DevInfoParser::getHwVersion() String DevInfoParser::getHwVersion()
{ {
return (((uint16_t)_payloadDevInfoSimple[6]) << 8) | _payloadDevInfoSimple[7]; char buf[6];
snprintf(buf, sizeof(buf), "%02X.%02X", _payloadDevInfoSimple[6], _payloadDevInfoSimple[7]);
return String(buf);
} }
/* struct tm to seconds since Unix epoch */ /* struct tm to seconds since Unix epoch */

View File

@ -15,21 +15,21 @@ public:
uint32_t getLastUpdateAll(); uint32_t getLastUpdateAll();
void setLastUpdateAll(uint32_t lastUpdate); void setLastUpdateAll(uint32_t lastUpdate);
uint32_t getLastUpdateSample(); uint32_t getLastUpdateSimple();
void setLastUpdateSample(uint32_t lastUpdate); void setLastUpdateSimple(uint32_t lastUpdate);
uint16_t getFwBuildVersion(); uint16_t getFwBuildVersion();
time_t getFwBuildDateTime(); time_t getFwBuildDateTime();
uint16_t getFwBootloaderVersion(); uint16_t getFwBootloaderVersion();
uint32_t getHwPartNumber(); uint32_t getHwPartNumber();
uint16_t getHwVersion(); String getHwVersion();
private: private:
time_t timegm(struct tm* tm); time_t timegm(struct tm* tm);
uint32_t _lastUpdateAll = 0; uint32_t _lastUpdateAll = 0;
uint32_t _lastUpdateSample = 0; uint32_t _lastUpdateSimple = 0;
uint8_t _payloadDevInfoAll[DEV_INFO_SIZE] = {}; uint8_t _payloadDevInfoAll[DEV_INFO_SIZE] = {};
uint8_t _devInfoAllLength = 0; uint8_t _devInfoAllLength = 0;

View File

@ -0,0 +1,22 @@
#include "PowerCommandParser.h"
void PowerCommandParser::setLastPowerCommandSuccess(LastCommandSuccess status)
{
_lastLimitCommandSuccess = status;
}
LastCommandSuccess PowerCommandParser::getLastPowerCommandSuccess()
{
return _lastLimitCommandSuccess;
}
uint32_t PowerCommandParser::getLastUpdateCommand()
{
return _lastUpdateCommand;
}
void PowerCommandParser::setLastUpdateCommand(uint32_t lastUpdate)
{
_lastUpdateCommand = lastUpdate;
setLastUpdate(lastUpdate);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "Parser.h"
#include <Arduino.h>
class PowerCommandParser : public Parser {
public:
void setLastPowerCommandSuccess(LastCommandSuccess status);
LastCommandSuccess getLastPowerCommandSuccess();
uint32_t getLastUpdateCommand();
void setLastUpdateCommand(uint32_t lastUpdate);
private:
LastCommandSuccess _lastLimitCommandSuccess = CMD_OK; // Set to OK because we have to assume nothing is done at startup
uint32_t _lastUpdateCommand = 0;
};

View File

@ -1,5 +1,28 @@
#include "StatisticsParser.h" #include "StatisticsParser.h"
static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0);
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0);
static float calcUdcCh(StatisticsParser* iv, uint8_t arg0);
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0);
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0);
static float calcIrradiation(StatisticsParser* iv, uint8_t arg0);
using func_t = float(StatisticsParser*, uint8_t);
struct calcFunc_t {
uint8_t funcId; // unique id
func_t* func; // function pointer
};
const calcFunc_t calcFunctions[] = {
{ CALC_YT_CH0, &calcYieldTotalCh0 },
{ CALC_YD_CH0, &calcYieldDayCh0 },
{ CALC_UDC_CH, &calcUdcCh },
{ CALC_PDC_CH0, &calcPowerDcCh0 },
{ CALC_EFF_CH0, &calcEffiencyCh0 },
{ CALC_IRR_CH, &calcIrradiation }
};
void StatisticsParser::setByteAssignment(const byteAssign_t* byteAssignment, const uint8_t count) void StatisticsParser::setByteAssignment(const byteAssign_t* byteAssignment, const uint8_t count)
{ {
_byteAssignment = byteAssignment; _byteAssignment = byteAssignment;

View File

@ -70,31 +70,6 @@ typedef struct {
uint16_t div; // divisor / calc command uint16_t div; // divisor / calc command
} byteAssign_t; } byteAssign_t;
// prototypes
class StatisticsParser;
static float calcYieldTotalCh0(StatisticsParser* iv, uint8_t arg0);
static float calcYieldDayCh0(StatisticsParser* iv, uint8_t arg0);
static float calcUdcCh(StatisticsParser* iv, uint8_t arg0);
static float calcPowerDcCh0(StatisticsParser* iv, uint8_t arg0);
static float calcEffiencyCh0(StatisticsParser* iv, uint8_t arg0);
static float calcIrradiation(StatisticsParser* iv, uint8_t arg0);
using func_t = float(StatisticsParser*, uint8_t);
struct calcFunc_t {
uint8_t funcId; // unique id
func_t* func; // function pointer
};
const calcFunc_t calcFunctions[] = {
{ CALC_YT_CH0, &calcYieldTotalCh0 },
{ CALC_YD_CH0, &calcYieldDayCh0 },
{ CALC_UDC_CH, &calcUdcCh },
{ CALC_PDC_CH0, &calcPowerDcCh0 },
{ CALC_EFF_CH0, &calcEffiencyCh0 },
{ CALC_IRR_CH, &calcIrradiation }
};
class StatisticsParser : public Parser { class StatisticsParser : public Parser {
public: public:
void clearBuffer(); void clearBuffer();

View File

@ -13,16 +13,17 @@ default_envs = generic
[env] [env]
framework = arduino framework = arduino
platform = espressif32@>=5 platform = espressif32@>=5.2.0
build_flags = build_flags =
-D=${PIOENV} -D=${PIOENV}
-DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz
-Wall
lib_deps = lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer https://github.com/yubox-node-org/ESPAsyncWebServer
bblanchon/ArduinoJson @ ^6.19.4 bblanchon/ArduinoJson @ ^6.19.4
https://github.com/bertmelis/espMqttClient.git#v1.3.1 https://github.com/bertmelis/espMqttClient.git#v1.3.2
nrf24/RF24 @ ^1.4.5 nrf24/RF24 @ ^1.4.5
extra_scripts = extra_scripts =

View File

@ -74,7 +74,7 @@ void MqttHassPublishingClass::publishField(std::shared_ptr<InverterAbstract> inv
} }
char serial[sizeof(uint64_t) * 8 + 1]; char serial[sizeof(uint64_t) * 8 + 1];
snprintf(serial, sizeof(serial), "%0lx%08lx", snprintf(serial, sizeof(serial), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));

View File

@ -34,7 +34,7 @@ void MqttPublishingClass::loop()
auto inv = Hoymiles.getInverterByPos(i); auto inv = Hoymiles.getInverterByPos(i);
char buffer[sizeof(uint64_t) * 8 + 1]; char buffer[sizeof(uint64_t) * 8 + 1];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));
String subtopic = String(buffer); String subtopic = String(buffer);
@ -59,7 +59,7 @@ void MqttPublishingClass::loop()
MqttSettings.publish(subtopic + "/device/hwpartnumber", String(inv->DevInfo()->getHwPartNumber())); MqttSettings.publish(subtopic + "/device/hwpartnumber", String(inv->DevInfo()->getHwPartNumber()));
// Hardware version // Hardware version
MqttSettings.publish(subtopic + "/device/hwversion", String(inv->DevInfo()->getHwVersion())); MqttSettings.publish(subtopic + "/device/hwversion", inv->DevInfo()->getHwVersion());
} }
if (inv->SystemConfigPara()->getLastUpdate() > 0) { if (inv->SystemConfigPara()->getLastUpdate() > 0) {
@ -106,7 +106,7 @@ String MqttPublishingClass::getTopic(std::shared_ptr<InverterAbstract> inv, uint
} }
char buffer[sizeof(uint64_t) * 8 + 1]; char buffer[sizeof(uint64_t) * 8 + 1];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));
String invSerial = String(buffer); String invSerial = String(buffer);

View File

@ -14,6 +14,7 @@
#define TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE "limit_persistent_absolute" #define TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE "limit_persistent_absolute"
#define TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE "limit_nonpersistent_relative" #define TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE "limit_nonpersistent_relative"
#define TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE "limit_nonpersistent_absolute" #define TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE "limit_nonpersistent_absolute"
#define TOPIC_SUB_POWER "power"
MqttSettingsClass::MqttSettingsClass() MqttSettingsClass::MqttSettingsClass()
{ {
@ -30,6 +31,8 @@ void MqttSettingsClass::NetworkEvent(network_event event)
Serial.println(F("Network lost connection")); Serial.println(F("Network lost connection"));
mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
break; break;
default:
break;
} }
} }
@ -44,6 +47,7 @@ void MqttSettingsClass::onMqttConnect(bool sessionPresent)
mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE).c_str(), 0); mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE).c_str(), 0);
mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE).c_str(), 0); mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE).c_str(), 0);
mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE).c_str(), 0); mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE).c_str(), 0);
mqttClient->subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER).c_str(), 0);
} }
void MqttSettingsClass::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) void MqttSettingsClass::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason)
@ -118,38 +122,43 @@ void MqttSettingsClass::onMqttMessage(const espMqttClientTypes::MessagePropertie
char* strlimit = new char[len + 1]; char* strlimit = new char[len + 1];
memcpy(strlimit, payload, len); memcpy(strlimit, payload, len);
strlimit[len] = '\0'; strlimit[len] = '\0';
uint32_t limit = strtol(strlimit, NULL, 10); uint32_t payload_val = strtol(strlimit, NULL, 10);
delete[] strlimit; delete[] strlimit;
if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) { if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) {
// Set inverter limit relative persistent // Set inverter limit relative persistent
limit = min<uint32_t>(100, limit); payload_val = min<uint32_t>(100, payload_val);
Serial.printf("Limit Persistent: %d %%\n", limit); Serial.printf("Limit Persistent: %d %%\n", payload_val);
inv->sendActivePowerControlRequest(Hoymiles.getRadio(), limit, PowerLimitControlType::RelativPersistent); inv->sendActivePowerControlRequest(Hoymiles.getRadio(), payload_val, PowerLimitControlType::RelativPersistent);
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) { } else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) {
// Set inverter limit absolute persistent // Set inverter limit absolute persistent
Serial.printf("Limit Persistent: %d W\n", limit); Serial.printf("Limit Persistent: %d W\n", payload_val);
inv->sendActivePowerControlRequest(Hoymiles.getRadio(), limit, PowerLimitControlType::AbsolutPersistent); inv->sendActivePowerControlRequest(Hoymiles.getRadio(), payload_val, PowerLimitControlType::AbsolutPersistent);
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) { } else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) {
// Set inverter limit relative non persistent // Set inverter limit relative non persistent
limit = min<uint32_t>(100, limit); payload_val = min<uint32_t>(100, payload_val);
Serial.printf("Limit Non-Persistent: %d %%\n", limit); Serial.printf("Limit Non-Persistent: %d %%\n", payload_val);
if (!properties.retain) { if (!properties.retain) {
inv->sendActivePowerControlRequest(Hoymiles.getRadio(), limit, PowerLimitControlType::RelativNonPersistent); inv->sendActivePowerControlRequest(Hoymiles.getRadio(), payload_val, PowerLimitControlType::RelativNonPersistent);
} else { } else {
Serial.println("Ignored because retained"); Serial.println("Ignored because retained");
} }
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) { } else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) {
// Set inverter limit absolute non persistent // Set inverter limit absolute non persistent
Serial.printf("Limit Non-Persistent: %d W\n", limit); Serial.printf("Limit Non-Persistent: %d W\n", payload_val);
if (!properties.retain) { if (!properties.retain) {
inv->sendActivePowerControlRequest(Hoymiles.getRadio(), limit, PowerLimitControlType::AbsolutNonPersistent); inv->sendActivePowerControlRequest(Hoymiles.getRadio(), payload_val, PowerLimitControlType::AbsolutNonPersistent);
} else { } else {
Serial.println("Ignored because retained"); Serial.println("Ignored because retained");
} }
} else if(!strcmp(setting, TOPIC_SUB_POWER)) {
// Turn inverter on or off
Serial.printf("Set inverter power to: %d\n", payload_val);
inv->sendPowerControlRequest(Hoymiles.getRadio(), payload_val > 0);
} }
} }

View File

@ -27,6 +27,7 @@ void WebApiClass::init()
_webApiMqtt.init(&_server); _webApiMqtt.init(&_server);
_webApiNetwork.init(&_server); _webApiNetwork.init(&_server);
_webApiNtp.init(&_server); _webApiNtp.init(&_server);
_webApiPower.init(&_server);
_webApiSysstatus.init(&_server); _webApiSysstatus.init(&_server);
_webApiWebapp.init(&_server); _webApiWebapp.init(&_server);
_webApiWsLive.init(&_server); _webApiWsLive.init(&_server);
@ -48,6 +49,7 @@ void WebApiClass::loop()
_webApiMqtt.loop(); _webApiMqtt.loop();
_webApiNetwork.loop(); _webApiNetwork.loop();
_webApiNtp.loop(); _webApiNtp.loop();
_webApiPower.loop();
_webApiSysstatus.loop(); _webApiSysstatus.loop();
_webApiWebapp.loop(); _webApiWebapp.loop();
_webApiWsLive.loop(); _webApiWsLive.loop();

View File

@ -31,11 +31,12 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
// 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];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));
JsonObject devInfoObj = root[buffer].createNestedObject(); JsonObject devInfoObj = root[buffer].createNestedObject();
devInfoObj[F("valid_data")] = inv->DevInfo()->getLastUpdate() > 0;
devInfoObj[F("fw_bootloader_version")] = inv->DevInfo()->getFwBootloaderVersion(); devInfoObj[F("fw_bootloader_version")] = inv->DevInfo()->getFwBootloaderVersion();
devInfoObj[F("fw_build_version")] = inv->DevInfo()->getFwBuildVersion(); devInfoObj[F("fw_build_version")] = inv->DevInfo()->getFwBuildVersion();
devInfoObj[F("hw_part_number")] = inv->DevInfo()->getHwPartNumber(); devInfoObj[F("hw_part_number")] = inv->DevInfo()->getHwPartNumber();

View File

@ -30,7 +30,7 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
// DTU Serial is read as HEX // DTU Serial is read as HEX
char buffer[sizeof(uint64_t) * 8 + 1]; char buffer[sizeof(uint64_t) * 8 + 1];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((config.Dtu_Serial >> 32) & 0xFFFFFFFF)), ((uint32_t)((config.Dtu_Serial >> 32) & 0xFFFFFFFF)),
((uint32_t)(config.Dtu_Serial & 0xFFFFFFFF))); ((uint32_t)(config.Dtu_Serial & 0xFFFFFFFF)));
root[F("dtu_serial")] = buffer; root[F("dtu_serial")] = buffer;

View File

@ -36,7 +36,7 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
if (inv != nullptr) { if (inv != nullptr) {
// 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];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));

View File

@ -42,7 +42,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
// 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];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF)), ((uint32_t)((config.Inverter[i].Serial >> 32) & 0xFFFFFFFF)),
((uint32_t)(config.Inverter[i].Serial & 0xFFFFFFFF))); ((uint32_t)(config.Inverter[i].Serial & 0xFFFFFFFF)));
obj[F("serial")] = buffer; obj[F("serial")] = buffer;
@ -210,11 +210,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
} }
JsonArray maxPowerArray = root[F("max_power")].as<JsonArray>(); JsonArray maxPowerArray = root[F("max_power")].as<JsonArray>();
uint8_t arrayCount = 0; if (maxPowerArray.size() != INV_MAX_CHAN_COUNT) {
for (JsonVariant maxPower : maxPowerArray) {
arrayCount++;
}
if (arrayCount != INV_MAX_CHAN_COUNT) {
retMsg[F("message")] = F("Invalid amount of max channel setting given!"); retMsg[F("message")] = F("Invalid amount of max channel setting given!");
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -230,7 +226,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
inverter.Serial = new_serial; inverter.Serial = new_serial;
strncpy(inverter.Name, root[F("name")].as<String>().c_str(), INV_MAX_NAME_STRLEN); strncpy(inverter.Name, root[F("name")].as<String>().c_str(), INV_MAX_NAME_STRLEN);
arrayCount = 0; uint8_t arrayCount = 0;
for (JsonVariant maxPower : maxPowerArray) { for (JsonVariant maxPower : maxPowerArray) {
inverter.MaxChannelPower[arrayCount] = maxPower.as<uint16_t>(); inverter.MaxChannelPower[arrayCount] = maxPower.as<uint16_t>();
arrayCount++; arrayCount++;
@ -319,7 +315,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
Hoymiles.removeInverterBySerial(inverter.Serial); Hoymiles.removeInverterBySerial(inverter.Serial);
inverter.Serial = 0; inverter.Serial = 0;
strncpy(inverter.Name, "", 0); strncpy(inverter.Name, "", sizeof(inverter.Name));
Configuration.write(); Configuration.write();
retMsg[F("type")] = F("success"); retMsg[F("type")] = F("success");

View File

@ -31,7 +31,7 @@ void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
// 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];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));

121
src/WebApi_power.cpp Normal file
View File

@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Thomas Basler and others
*/
#include "WebApi_power.h"
#include "ArduinoJson.h"
#include "AsyncJson.h"
#include "Hoymiles.h"
void WebApiPowerClass::init(AsyncWebServer* server)
{
using std::placeholders::_1;
_server = server;
_server->on("/api/power/status", HTTP_GET, std::bind(&WebApiPowerClass::onPowerStatus, this, _1));
_server->on("/api/power/config", HTTP_POST, std::bind(&WebApiPowerClass::onPowerPost, this, _1));
}
void WebApiPowerClass::loop()
{
}
void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
{
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
// Inverter Serial is read as HEX
char buffer[sizeof(uint64_t) * 8 + 1];
snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF)));
LastCommandSuccess status = inv->PowerCommand()->getLastPowerCommandSuccess();
String limitStatus = "Unknown";
if (status == LastCommandSuccess::CMD_OK) {
limitStatus = "Ok";
}
else if (status == LastCommandSuccess::CMD_NOK) {
limitStatus = "Failure";
}
else if (status == LastCommandSuccess::CMD_PENDING) {
limitStatus = "Pending";
}
root[buffer]["power_set_status"] = limitStatus;
}
response->setLength();
request->send(response);
}
void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
{
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
retMsg[F("type")] = F("warning");
if (!request->hasParam("data", true)) {
retMsg[F("message")] = F("No values found!");
response->setLength();
request->send(response);
return;
}
String json = request->getParam("data", true)->value();
if (json.length() > 1024) {
retMsg[F("message")] = F("Data too large!");
response->setLength();
request->send(response);
return;
}
DynamicJsonDocument root(1024);
DeserializationError error = deserializeJson(root, json);
if (error) {
retMsg[F("message")] = F("Failed to parse data!");
response->setLength();
request->send(response);
return;
}
if (!(root.containsKey("serial")
&& root.containsKey("power"))) {
retMsg[F("message")] = F("Values are missing!");
response->setLength();
request->send(response);
return;
}
if (root[F("serial")].as<uint64_t>() == 0) {
retMsg[F("message")] = F("Serial must be a number > 0!");
response->setLength();
request->send(response);
return;
}
uint64_t serial = strtoll(root[F("serial")].as<String>().c_str(), NULL, 16);
uint16_t power = root[F("power")].as<bool>();
auto inv = Hoymiles.getInverterBySerial(serial);
if (inv == nullptr) {
retMsg[F("message")] = F("Invalid inverter specified!");
response->setLength();
request->send(response);
return;
}
inv->sendPowerControlRequest(Hoymiles.getRadio(), power);
retMsg[F("type")] = F("success");
retMsg[F("message")] = F("Settings saved!");
response->setLength();
request->send(response);
}

View File

@ -59,7 +59,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
char version[16]; char version[16];
snprintf(version, sizeof(version), "%d.%d.%d", CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff); snprintf(version, sizeof(version), "%d.%d.%d", CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff);
root[F("firmware_version")] = version; root[F("config_version")] = version;
root[F("git_hash")] = AUTO_GIT_HASH; root[F("git_hash")] = AUTO_GIT_HASH;
root[F("uptime")] = esp_timer_get_time() / 1000000; root[F("uptime")] = esp_timer_get_time() / 1000000;

View File

@ -78,7 +78,7 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
auto inv = Hoymiles.getInverterByPos(i); auto inv = Hoymiles.getInverterByPos(i);
char buffer[sizeof(uint64_t) * 8 + 1]; char buffer[sizeof(uint64_t) * 8 + 1];
snprintf(buffer, sizeof(buffer), "%0lx%08lx", snprintf(buffer, sizeof(buffer), "%0x%08x",
((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)), ((uint32_t)((inv->serial() >> 32) & 0xFFFFFFFF)),
((uint32_t)(inv->serial() & 0xFFFFFFFF))); ((uint32_t)(inv->serial() & 0xFFFFFFFF)));

View File

@ -9,21 +9,21 @@
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.1", "bootstrap": "^5.2.2",
"bootstrap-icons-vue": "^1.8.1", "bootstrap-icons-vue": "^1.8.1",
"core-js": "^3.25.3", "core-js": "^3.25.5",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"vue": "^3.2.39", "vue": "^3.2.40",
"vue-class-component": "^8.0.0-0", "vue-class-component": "^8.0.0-0",
"vue-router": "^4.1.5" "vue-router": "^4.1.5"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.19.1", "@babel/core": "^7.19.3",
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.19.1",
"@types/bootstrap": "^5.2.4", "@types/bootstrap": "^5.2.5",
"@types/node": "^18.7.21", "@types/node": "^18.8.2",
"@types/spark-md5": "^3.0.2", "@types/spark-md5": "^3.0.2",
"@typescript-eslint/parser": "^5.37.0", "@typescript-eslint/parser": "^5.38.1",
"@vue/cli-plugin-babel": "~5.0.8", "@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8", "@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-router": "^5.0.6", "@vue/cli-plugin-router": "^5.0.6",
@ -31,8 +31,8 @@
"@vue/cli-service": "~5.0.8", "@vue/cli-service": "~5.0.8",
"@vue/eslint-config-typescript": "^11.0.2", "@vue/eslint-config-typescript": "^11.0.2",
"eslint": "^8.24.0", "eslint": "^8.24.0",
"eslint-plugin-vue": "^9.5.1", "eslint-plugin-vue": "^9.6.0",
"typescript": "^4.8.3", "typescript": "^4.8.4",
"vue-cli-plugin-compression": "~2.0.0" "vue-cli-plugin-compression": "~2.0.0"
}, },
"eslintConfig": { "eslintConfig": {

View File

@ -52,6 +52,14 @@
</button> </button>
</div> </div>
<div class="btn-group me-2" role="group">
<button type="button" class="btn btn-sm btn-danger"
@click="onShowPowerSettings(inverter.serial)" title="Turn Inverter on/off">
<BIconPower style="font-size:24px;" />
</button>
</div>
<div class="btn-group me-2" role="group"> <div class="btn-group me-2" role="group">
<button type="button" class="btn btn-sm btn-info" <button type="button" class="btn btn-sm btn-info"
@click="onShowDevInfo(inverter.serial)" title="Show Inverter Info"> @click="onShowDevInfo(inverter.serial)" title="Show Inverter Info">
@ -232,6 +240,59 @@
</div> </div>
</div> </div>
</div> </div>
<div class="modal" id="powerSettingView" ref="powerSettingView" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Power Settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<BootstrapAlert v-model="showAlertPower" :variant="alertTypePower">
{{ alertMessagePower }}
</BootstrapAlert>
<div class="text-center" v-if="powerSettingLoading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<template v-if="!powerSettingLoading">
<div class="row mb-3 align-items-center">
<label for="inputLastPowerSet" class="col col-form-label">Last Power Set
Status:</label>
<div class="col">
<span class="badge" :class="{
'bg-danger': successCommandPower == 'Failure',
'bg-warning': successCommandPower == 'Pending',
'bg-success': successCommandPower == 'Ok',
'bg-secondary': successCommandPower == 'Unknown',
}">
{{ successCommandPower }}
</span>
</div>
</div>
<div class="d-grid gap-2 col-6 mx-auto">
<button type="button" class="btn btn-success" @click="onSetPowerSettings(true)">
<BIconToggleOn class="fs-4" />&nbsp;Turn On
</button>
<button type="button" class="btn btn-danger" @click="onSetPowerSettings(false)">
<BIconToggleOff class="fs-4" />&nbsp;Turn Off
</button>
</div>
</template>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div> </div>
</template> </template>
@ -292,6 +353,14 @@ export default defineComponent({
alertMessageLimit: "", alertMessageLimit: "",
alertTypeLimit: "info", alertTypeLimit: "info",
showAlertLimit: false, showAlertLimit: false,
powerSettingView: {} as bootstrap.Modal,
powerSettingSerial: 0,
powerSettingLoading: true,
alertMessagePower: "",
alertTypePower: "info",
showAlertPower: false,
successCommandPower: "",
}; };
}, },
created() { created() {
@ -303,8 +372,10 @@ export default defineComponent({
this.eventLogView = new bootstrap.Modal('#eventView'); this.eventLogView = new bootstrap.Modal('#eventView');
this.devInfoView = new bootstrap.Modal('#devInfoView'); this.devInfoView = new bootstrap.Modal('#devInfoView');
this.limitSettingView = new bootstrap.Modal('#limitSettingView'); this.limitSettingView = new bootstrap.Modal('#limitSettingView');
this.powerSettingView = new bootstrap.Modal('#powerSettingView');
(this.$refs.limitSettingView as HTMLElement).addEventListener("hide.bs.modal", this.onHideLimitSettings); (this.$refs.limitSettingView as HTMLElement).addEventListener("hide.bs.modal", this.onHideLimitSettings);
(this.$refs.powerSettingView as HTMLElement).addEventListener("hide.bs.modal", this.onHidePowerSettings);
}, },
unmounted() { unmounted() {
this.closeSocket(); this.closeSocket();
@ -483,6 +554,57 @@ export default defineComponent({
} }
this.targetLimitType = type; this.targetLimitType = type;
}, },
onShowPowerSettings(serial: number) {
this.powerSettingLoading = true;
fetch("/api/power/status")
.then((response) => response.json())
.then((data) => {
this.successCommandPower = data[serial].power_set_status;
this.powerSettingSerial = serial;
this.powerSettingLoading = false;
});
this.powerSettingView.show();
},
onHidePowerSettings() {
this.powerSettingSerial = 0;
this.showAlertPower = false;
},
onSetPowerSettings(turnOn: boolean) {
const data = {
serial: this.powerSettingSerial,
power: turnOn,
};
const formData = new FormData();
formData.append("data", JSON.stringify(data));
console.log(data);
fetch("/api/power/config", {
method: "POST",
body: formData,
})
.then(function (response) {
if (response.status != 200) {
throw response.status;
} else {
return response.json();
}
})
.then(
(response) => {
if (response.type == "success") {
this.powerSettingView.hide();
} else {
this.alertMessagePower = response.message;
this.alertTypePower = response.type;
this.showAlertPower = true;
}
}
)
},
}, },
}); });
</script> </script>

View File

@ -45,12 +45,15 @@ export default defineComponent({
// FirmwareInfo // FirmwareInfo
hostname: "", hostname: "",
sdkversion: "", sdkversion: "",
firmware_version: "", config_version: "",
git_hash: "", git_hash: "",
resetreason_0: "", resetreason_0: "",
resetreason_1: "", resetreason_1: "",
cfgsavecount: 0, cfgsavecount: 0,
uptime: 0, uptime: 0,
update_text: "",
update_url: "",
update_status: "",
// MemoryInfo // MemoryInfo
heap_total: 0, heap_total: 0,
heap_used: 0, heap_used: 0,
@ -72,8 +75,35 @@ export default defineComponent({
.then((data) => { .then((data) => {
this.systemDataList = data; this.systemDataList = data;
this.dataLoading = false; this.dataLoading = false;
this.getUpdateInfo();
}) })
}, },
getUpdateInfo() {
const fetchUrl = "https://api.github.com/repos/tbnobody/OpenDTU/compare/"
+ this.systemDataList.git_hash?.substring(1) + "...HEAD";
fetch(fetchUrl)
.then((response) => {
if (response.ok) {
return response.json()
}
throw new Error('Error fetching version information');
})
.then((data) => {
if (data.total_commits > 0) {
this.systemDataList.update_text = "New version available! Show changes!"
this.systemDataList.update_status = "text-bg-danger";
this.systemDataList.update_url = data.html_url;
} else {
this.systemDataList.update_text = "Up to date!"
this.systemDataList.update_status = "text-bg-success";
}
})
.catch((error: Error) => {
this.systemDataList.update_text = error.message;
this.systemDataList.update_status = "text-bg-secondary";
});
}
}, },
}); });
</script> </script>

View File

@ -1,5 +1,10 @@
<template> <template>
<table class="table table-hover"> <BootstrapAlert :show="!devInfoList.valid_data">
<h4 class="alert-heading">
<BIconInfoSquare class="fs-2" />&nbsp;No Information available
</h4>Did not receive any valid data from the inverter till now. Still trying...
</BootstrapAlert>
<table v-if="devInfoList.valid_data" class="table table-hover">
<tbody> <tbody>
<tr> <tr>
<td>Bootloader Version</td> <td>Bootloader Version</td>
@ -27,8 +32,10 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import BootstrapAlert from '@/components/partials/BootstrapAlert.vue';
declare interface DevInfoData { declare interface DevInfoData {
valid_data: boolean,
fw_bootloader_version: number, fw_bootloader_version: number,
fw_build_version: number, fw_build_version: number,
fw_build_datetime: Date, fw_build_datetime: Date,
@ -37,6 +44,9 @@ declare interface DevInfoData {
} }
export default defineComponent({ export default defineComponent({
components: {
BootstrapAlert,
},
props: { props: {
devInfoList: { type: Object as () => DevInfoData, required: true }, devInfoList: { type: Object as () => DevInfoData, required: true },
}, },

View File

@ -16,12 +16,18 @@
<td>{{ sdkversion }}</td> <td>{{ sdkversion }}</td>
</tr> </tr>
<tr> <tr>
<th>Firmware Version</th> <th>Config Version</th>
<td>{{ firmware_version }}</td> <td>{{ config_version }}</td>
</tr> </tr>
<tr> <tr>
<th>Git Hash</th> <th>Firmware Version / Git Hash</th>
<td><a :href="'https://github.com/tbnobody/OpenDTU/commits/' + git_hash?.substring(1)" target="_blank">{{ git_hash?.substring(1) }}</a></td> <td><a :href="'https://github.com/tbnobody/OpenDTU/commits/' + git_hash?.substring(1)"
target="_blank">{{ git_hash?.substring(1) }}</a></td>
</tr>
<tr>
<th>Firmware Update</th>
<td><a :href="update_url" target="_blank"><span class="badge" :class="update_status">{{
update_text }}</span></a></td>
</tr> </tr>
<tr> <tr>
<th>Reset Reason CPU 0</th> <th>Reset Reason CPU 0</th>
@ -53,12 +59,15 @@ export default defineComponent({
props: { props: {
hostname: String, hostname: String,
sdkversion: String, sdkversion: String,
firmware_version: String, config_version: String,
git_hash: String, git_hash: String,
resetreason_0: String, resetreason_0: String,
resetreason_1: String, resetreason_1: String,
cfgsavecount: { type: Number, required: true }, cfgsavecount: { type: Number, required: true },
uptime: { type: Number, required: true }, uptime: { type: Number, required: true },
update_text: String,
update_url: String,
update_status: String,
}, },
computed: { computed: {
timeInHours() { timeInHours() {

View File

@ -36,10 +36,10 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
"@babel/compat-data@^7.19.1": "@babel/compat-data@^7.19.3":
version "7.19.1" version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.1.tgz#72d647b4ff6a4f82878d184613353af1dd0290f9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151"
integrity sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg== integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==
"@babel/core@^7.12.16": "@babel/core@^7.12.16":
version "7.18.10" version "7.18.10"
@ -62,21 +62,21 @@
json5 "^2.2.1" json5 "^2.2.1"
semver "^6.3.0" semver "^6.3.0"
"@babel/core@^7.19.1": "@babel/core@^7.19.3":
version "7.19.1" version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.1.tgz#c8fa615c5e88e272564ace3d42fbc8b17bfeb22b" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c"
integrity sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw== integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==
dependencies: dependencies:
"@ampproject/remapping" "^2.1.0" "@ampproject/remapping" "^2.1.0"
"@babel/code-frame" "^7.18.6" "@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.19.0" "@babel/generator" "^7.19.3"
"@babel/helper-compilation-targets" "^7.19.1" "@babel/helper-compilation-targets" "^7.19.3"
"@babel/helper-module-transforms" "^7.19.0" "@babel/helper-module-transforms" "^7.19.0"
"@babel/helpers" "^7.19.0" "@babel/helpers" "^7.19.0"
"@babel/parser" "^7.19.1" "@babel/parser" "^7.19.3"
"@babel/template" "^7.18.10" "@babel/template" "^7.18.10"
"@babel/traverse" "^7.19.1" "@babel/traverse" "^7.19.3"
"@babel/types" "^7.19.0" "@babel/types" "^7.19.3"
convert-source-map "^1.7.0" convert-source-map "^1.7.0"
debug "^4.1.0" debug "^4.1.0"
gensync "^1.0.0-beta.2" gensync "^1.0.0-beta.2"
@ -110,6 +110,15 @@
"@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1" jsesc "^2.5.1"
"@babel/generator@^7.19.3":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.3.tgz#d7f4d1300485b4547cb6f94b27d10d237b42bf59"
integrity sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==
dependencies:
"@babel/types" "^7.19.3"
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.16.7": "@babel/helper-annotate-as-pure@^7.16.7":
version "7.16.7" version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
@ -135,12 +144,12 @@
browserslist "^4.20.2" browserslist "^4.20.2"
semver "^6.3.0" semver "^6.3.0"
"@babel/helper-compilation-targets@^7.19.1": "@babel/helper-compilation-targets@^7.19.3":
version "7.19.1" version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz#7f630911d83b408b76fe584831c98e5395d7a17c" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca"
integrity sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg== integrity sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==
dependencies: dependencies:
"@babel/compat-data" "^7.19.1" "@babel/compat-data" "^7.19.3"
"@babel/helper-validator-option" "^7.18.6" "@babel/helper-validator-option" "^7.18.6"
browserslist "^4.21.3" browserslist "^4.21.3"
semver "^6.3.0" semver "^6.3.0"
@ -366,6 +375,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
"@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/helper-validator-option@^7.16.7": "@babel/helper-validator-option@^7.16.7":
version "7.16.7" version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
@ -423,10 +437,10 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.0.tgz#497fcafb1d5b61376959c1c338745ef0577aa02c"
integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw== integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==
"@babel/parser@^7.19.1": "@babel/parser@^7.19.3":
version "7.19.1" version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
version "7.17.12" version "7.17.12"
@ -1107,19 +1121,19 @@
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
"@babel/traverse@^7.19.1": "@babel/traverse@^7.19.3":
version "7.19.1" version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4"
integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==
dependencies: dependencies:
"@babel/code-frame" "^7.18.6" "@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.19.0" "@babel/generator" "^7.19.3"
"@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.19.0" "@babel/helper-function-name" "^7.19.0"
"@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.19.1" "@babel/parser" "^7.19.3"
"@babel/types" "^7.19.0" "@babel/types" "^7.19.3"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
@ -1141,6 +1155,15 @@
"@babel/helper-validator-identifier" "^7.18.6" "@babel/helper-validator-identifier" "^7.18.6"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.19.3":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624"
integrity sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==
dependencies:
"@babel/helper-string-parser" "^7.18.10"
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@eslint/eslintrc@^1.3.2": "@eslint/eslintrc@^1.3.2":
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356"
@ -1344,10 +1367,10 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/bootstrap@^5.2.4": "@types/bootstrap@^5.2.5":
version "5.2.4" version "5.2.5"
resolved "https://registry.yarnpkg.com/@types/bootstrap/-/bootstrap-5.2.4.tgz#7f4f4af8e22af8247385549bd2f687088d00d2d3" resolved "https://registry.yarnpkg.com/@types/bootstrap/-/bootstrap-5.2.5.tgz#0bb5dea7720611b2bb7ba16bd8a64fafd86fb658"
integrity sha512-jGNB81zuDHu1DPvBV7Ox3Z3eyzdWPNguYwrt0j7X90VExA8H7c6qxJh0cz5j3xp0XvSy1TYaP2pkyXCHeo8CaA== integrity sha512-VnalUJ3E/oaV3DYrauEc/sSPpaEPxTV09twSEzY4KFRvyuGlrZUSqG95XZ6ReAi0YMZIs7rXxdngDK2X1YONQA==
dependencies: dependencies:
"@popperjs/core" "^2.9.2" "@popperjs/core" "^2.9.2"
@ -1446,10 +1469,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39"
integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg== integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==
"@types/node@^18.7.21": "@types/node@^18.8.2":
version "18.7.22" version "18.8.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.22.tgz#76f7401362ad63d9d7eefa7dcdfa5fcd9baddff3" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.2.tgz#17d42c6322d917764dd3d2d3a10d7884925de067"
integrity sha512-TsmoXYd4zrkkKjJB0URF/mTIKPl+kVcbqClB2F/ykU7vil1BfWZVndOnpEIozPv4fURD28gyPFeIkW2G+KXOvw== integrity sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==
"@types/normalize-package-data@^2.4.0": "@types/normalize-package-data@^2.4.0":
version "2.4.1" version "2.4.1"
@ -1540,14 +1563,14 @@
"@typescript-eslint/typescript-estree" "5.32.0" "@typescript-eslint/typescript-estree" "5.32.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/parser@^5.37.0": "@typescript-eslint/parser@^5.38.1":
version "5.38.0" version "5.39.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.0.tgz#5a59a1ff41a7b43aacd1bb2db54f6bf1c02b2ff8" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.39.0.tgz#93fa0bc980a3a501e081824f6097f7ca30aaa22b"
integrity sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA== integrity sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.38.0" "@typescript-eslint/scope-manager" "5.39.0"
"@typescript-eslint/types" "5.38.0" "@typescript-eslint/types" "5.39.0"
"@typescript-eslint/typescript-estree" "5.38.0" "@typescript-eslint/typescript-estree" "5.39.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@5.29.0": "@typescript-eslint/scope-manager@5.29.0":
@ -1566,13 +1589,13 @@
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.32.0"
"@typescript-eslint/visitor-keys" "5.32.0" "@typescript-eslint/visitor-keys" "5.32.0"
"@typescript-eslint/scope-manager@5.38.0": "@typescript-eslint/scope-manager@5.39.0":
version "5.38.0" version "5.39.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz#8f0927024b6b24e28671352c93b393a810ab4553" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz#873e1465afa3d6c78d8ed2da68aed266a08008d0"
integrity sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA== integrity sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==
dependencies: dependencies:
"@typescript-eslint/types" "5.38.0" "@typescript-eslint/types" "5.39.0"
"@typescript-eslint/visitor-keys" "5.38.0" "@typescript-eslint/visitor-keys" "5.39.0"
"@typescript-eslint/type-utils@5.29.0": "@typescript-eslint/type-utils@5.29.0":
version "5.29.0" version "5.29.0"
@ -1593,10 +1616,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8"
integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ== integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ==
"@typescript-eslint/types@5.38.0": "@typescript-eslint/types@5.39.0":
version "5.38.0" version "5.39.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.0.tgz#8cd15825e4874354e31800dcac321d07548b8a5f" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.39.0.tgz#f4e9f207ebb4579fd854b25c0bf64433bb5ed78d"
integrity sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA== integrity sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==
"@typescript-eslint/typescript-estree@5.29.0": "@typescript-eslint/typescript-estree@5.29.0":
version "5.29.0" version "5.29.0"
@ -1624,13 +1647,13 @@
semver "^7.3.7" semver "^7.3.7"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@5.38.0": "@typescript-eslint/typescript-estree@5.39.0":
version "5.38.0" version "5.39.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz#89f86b2279815c6fb7f57d68cf9b813f0dc25d98" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz#c0316aa04a1a1f4f7f9498e3c13ef1d3dc4cf88b"
integrity sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg== integrity sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==
dependencies: dependencies:
"@typescript-eslint/types" "5.38.0" "@typescript-eslint/types" "5.39.0"
"@typescript-eslint/visitor-keys" "5.38.0" "@typescript-eslint/visitor-keys" "5.39.0"
debug "^4.3.4" debug "^4.3.4"
globby "^11.1.0" globby "^11.1.0"
is-glob "^4.0.3" is-glob "^4.0.3"
@ -1665,12 +1688,12 @@
"@typescript-eslint/types" "5.32.0" "@typescript-eslint/types" "5.32.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@5.38.0": "@typescript-eslint/visitor-keys@5.39.0":
version "5.38.0" version "5.39.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz#60591ca3bf78aa12b25002c0993d067c00887e34" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz#8f41f7d241b47257b081ddba5d3ce80deaae61e2"
integrity sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w== integrity sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==
dependencies: dependencies:
"@typescript-eslint/types" "5.38.0" "@typescript-eslint/types" "5.39.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@vue/babel-helper-vue-jsx-merge-props@^1.2.1": "@vue/babel-helper-vue-jsx-merge-props@^1.2.1":
@ -1929,47 +1952,47 @@
semver "^7.3.4" semver "^7.3.4"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
"@vue/compiler-core@3.2.39": "@vue/compiler-core@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.39.tgz#0d77e635f4bdb918326669155a2dc977c053943e" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.40.tgz#c785501f09536748121e937fb87605bbb1ada8e5"
integrity sha512-mf/36OWXqWn0wsC40nwRRGheR/qoID+lZXbIuLnr4/AngM0ov8Xvv8GHunC0rKRIkh60bTqydlqTeBo49rlbqw== integrity sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==
dependencies: dependencies:
"@babel/parser" "^7.16.4" "@babel/parser" "^7.16.4"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-dom@3.2.39": "@vue/compiler-dom@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.39.tgz#bd69d35c1a48fe2cea4ab9e96d2a3a735d146fdf" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz#c225418773774db536174d30d3f25ba42a33e7e4"
integrity sha512-HMFI25Be1C8vLEEv1hgEO1dWwG9QQ8LTTPmCkblVJY/O3OvWx6r1+zsox5mKPMGvqYEZa6l8j+xgOfUspgo7hw== integrity sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==
dependencies: dependencies:
"@vue/compiler-core" "3.2.39" "@vue/compiler-core" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
"@vue/compiler-sfc@3.2.39": "@vue/compiler-sfc@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.39.tgz#8fe29990f672805b7c5a2ecfa5b05e681c862ea2" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz#61823283efc84d25d9d2989458f305d32a2ed141"
integrity sha512-fqAQgFs1/BxTUZkd0Vakn3teKUt//J3c420BgnYgEOoVdTwYpBTSXCMJ88GOBCylmUBbtquGPli9tVs7LzsWIA== integrity sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==
dependencies: dependencies:
"@babel/parser" "^7.16.4" "@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.39" "@vue/compiler-core" "3.2.40"
"@vue/compiler-dom" "3.2.39" "@vue/compiler-dom" "3.2.40"
"@vue/compiler-ssr" "3.2.39" "@vue/compiler-ssr" "3.2.40"
"@vue/reactivity-transform" "3.2.39" "@vue/reactivity-transform" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.25.7" magic-string "^0.25.7"
postcss "^8.1.10" postcss "^8.1.10"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-ssr@3.2.39": "@vue/compiler-ssr@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.39.tgz#4f3bfb535cb98b764bee45e078700e03ccc60633" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz#67df95a096c63e9ec4b50b84cc6f05816793629c"
integrity sha512-EoGCJ6lincKOZGW+0Ky4WOKsSmqL7hp1ZYgen8M7u/mlvvEQUaO9tKKOy7K43M9U2aA3tPv0TuYYQFrEbK2eFQ== integrity sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==
dependencies: dependencies:
"@vue/compiler-dom" "3.2.39" "@vue/compiler-dom" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
"@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.3.0": "@vue/component-compiler-utils@^3.1.0", "@vue/component-compiler-utils@^3.3.0":
version "3.3.0" version "3.3.0"
@ -2001,53 +2024,53 @@
"@typescript-eslint/parser" "^5.0.0" "@typescript-eslint/parser" "^5.0.0"
vue-eslint-parser "^9.0.0" vue-eslint-parser "^9.0.0"
"@vue/reactivity-transform@3.2.39": "@vue/reactivity-transform@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.39.tgz#da6ae6c8fd77791b9ae21976720d116591e1c4aa" resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz#dc24b9074b26f0d9dd2034c6349f5bb2a51c86ac"
integrity sha512-HGuWu864zStiWs9wBC6JYOP1E00UjMdDWIG5W+FpUx28hV3uz9ODOKVNm/vdOy/Pvzg8+OcANxAVC85WFBbl3A== integrity sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==
dependencies: dependencies:
"@babel/parser" "^7.16.4" "@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.39" "@vue/compiler-core" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.25.7" magic-string "^0.25.7"
"@vue/reactivity@3.2.39": "@vue/reactivity@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.39.tgz#e6e3615fe2288d4232b104640ddabd0729a78c80" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.40.tgz#ae65496f5b364e4e481c426f391568ed7d133cca"
integrity sha512-vlaYX2a3qMhIZfrw3Mtfd+BuU+TZmvDrPMa+6lpfzS9k/LnGxkSuf0fhkP0rMGfiOHPtyKoU9OJJJFGm92beVQ== integrity sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==
dependencies: dependencies:
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
"@vue/runtime-core@3.2.39": "@vue/runtime-core@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.39.tgz#dc1faccab11b3e81197aba33fb30c9447c1d2c84" resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.40.tgz#e814358bf1b0ff6d4a6b4f8f62d9f341964fb275"
integrity sha512-xKH5XP57JW5JW+8ZG1khBbuLakINTgPuINKL01hStWLTTGFOrM49UfCFXBcFvWmSbci3gmJyLl2EAzCaZWsx8g== integrity sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==
dependencies: dependencies:
"@vue/reactivity" "3.2.39" "@vue/reactivity" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
"@vue/runtime-dom@3.2.39": "@vue/runtime-dom@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.39.tgz#4a8cb132bcef316e8151c5ed07fc7272eb064614" resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz#975119feac5ab703aa9bbbf37c9cc966602c8eab"
integrity sha512-4G9AEJP+sLhsqf5wXcyKVWQKUhI+iWfy0hWQgea+CpaTD7BR0KdQzvoQdZhwCY6B3oleSyNLkLAQwm0ya/wNoA== integrity sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==
dependencies: dependencies:
"@vue/runtime-core" "3.2.39" "@vue/runtime-core" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
csstype "^2.6.8" csstype "^2.6.8"
"@vue/server-renderer@3.2.39": "@vue/server-renderer@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.39.tgz#4358292d925233b0d8b54cf0513eaece8b2351c5" resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.40.tgz#55eaac31f7105c3907e1895129bf4efb6b0ce393"
integrity sha512-1yn9u2YBQWIgytFMjz4f/t0j43awKytTGVptfd3FtBk76t1pd8mxbek0G/DrnjJhd2V7mSTb5qgnxMYt8Z5iSQ== integrity sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==
dependencies: dependencies:
"@vue/compiler-ssr" "3.2.39" "@vue/compiler-ssr" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
"@vue/shared@3.2.39": "@vue/shared@3.2.40":
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.39.tgz#302df167559a1a5156da162d8cc6760cef67f8e3" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.40.tgz#e57799da2a930b975321981fcee3d1e90ed257ae"
integrity sha512-D3dl2ZB9qE6mTuWPk9RlhDeP1dgNRUKC3NJxji74A4yL8M2MwlhLKUC/49WHjrNzSPug58fWx/yFbaTzGAQSBw== integrity sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==
"@vue/vue-loader-v15@npm:vue-loader@^15.9.7": "@vue/vue-loader-v15@npm:vue-loader@^15.9.7":
version "15.9.8" version "15.9.8"
@ -2487,10 +2510,10 @@ bootstrap-icons-vue@^1.8.1:
resolved "https://registry.yarnpkg.com/bootstrap-icons-vue/-/bootstrap-icons-vue-1.8.1.tgz#ce4a0c1f6efe41dabcc1341f2cb191d307fbaf50" resolved "https://registry.yarnpkg.com/bootstrap-icons-vue/-/bootstrap-icons-vue-1.8.1.tgz#ce4a0c1f6efe41dabcc1341f2cb191d307fbaf50"
integrity sha512-uItRULwQz0epETi9x/RBEqfjHmTAmkIIczpH1R6L9T6yyaaijk0826PzTWnWNm15tw66JT/8GNuXjB0HI5PHLw== integrity sha512-uItRULwQz0epETi9x/RBEqfjHmTAmkIIczpH1R6L9T6yyaaijk0826PzTWnWNm15tw66JT/8GNuXjB0HI5PHLw==
bootstrap@^5.2.1: bootstrap@^5.2.2:
version "5.2.1" version "5.2.2"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.1.tgz#45f97ff05cbe828bad807b014d8425f3aeb8ec3a" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.2.2.tgz#834e053eed584a65e244d8aa112a6959f56e27a0"
integrity sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA== integrity sha512-dEtzMTV71n6Fhmbg4fYJzQsw1N29hJKO1js5ackCgIpDcGid2ETMGC6zwSYw09v05Y+oRdQ9loC54zB1La3hHQ==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
@ -2880,10 +2903,10 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.3:
browserslist "^4.20.3" browserslist "^4.20.3"
semver "7.0.0" semver "7.0.0"
core-js@^3.25.3: core-js@^3.25.5:
version "3.25.3" version "3.25.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.3.tgz#cbc2be50b5ddfa7981837bd8c41639f27b166593" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.5.tgz#e86f651a2ca8a0237a5f064c2fe56cef89646e27"
integrity sha512-y1hvKXmPHvm5B7w4ln1S4uc9eV/O5+iFExSRUimnvIph11uaizFR8LFMdONN8hG3P2pipUfX4Y/fR8rAEtcHcQ== integrity sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==
core-js@^3.8.3: core-js@^3.8.3:
version "3.24.1" version "3.24.1"
@ -3330,10 +3353,10 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-plugin-vue@^9.5.1: eslint-plugin-vue@^9.6.0:
version "9.5.1" version "9.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.5.1.tgz#87ce075882cf7d824b95f46c224f91495fafcc54" resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.6.0.tgz#5d1825b93d54595b1ba97106843e1d28cf3bb291"
integrity sha512-Y0sL2RY7Xc9S8kNih9lbwHIDmewUg9bfas6WSzsOWRgDXhIHKxRBZYNAnVcXBFfE+bMWHUA5GLChl7TcTYUI8w== integrity sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==
dependencies: dependencies:
eslint-utils "^3.0.0" eslint-utils "^3.0.0"
natural-compare "^1.4.0" natural-compare "^1.4.0"
@ -6201,10 +6224,10 @@ type-is@~1.6.18:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
typescript@^4.8.3: typescript@^4.8.4:
version "4.8.3" version "4.8.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig== integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
unicode-canonical-property-names-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0" version "2.0.0"
@ -6346,16 +6369,16 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@^3.2.39: vue@^3.2.40:
version "3.2.39" version "3.2.40"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.39.tgz#de071c56c4c32c41cbd54e55f11404295c0dd62d" resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.40.tgz#23f387f6f9b3a0767938db6751e4fb5900f0ee34"
integrity sha512-tRkguhRTw9NmIPXhzk21YFBqXHT2t+6C6wPOgQ50fcFVWnPdetmRqbmySRHznrYjX2E47u0cGlKGcxKZJ38R/g== integrity sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==
dependencies: dependencies:
"@vue/compiler-dom" "3.2.39" "@vue/compiler-dom" "3.2.40"
"@vue/compiler-sfc" "3.2.39" "@vue/compiler-sfc" "3.2.40"
"@vue/runtime-dom" "3.2.39" "@vue/runtime-dom" "3.2.40"
"@vue/server-renderer" "3.2.39" "@vue/server-renderer" "3.2.40"
"@vue/shared" "3.2.39" "@vue/shared" "3.2.40"
watchpack@^2.3.1: watchpack@^2.3.1:
version "2.3.1" version "2.3.1"

Binary file not shown.