Merge branch 'tbnobody:master' into master
This commit is contained in:
commit
ab7a30eaf0
54
.github/workflows/repo-maintenance.yml
vendored
Normal file
54
.github/workflows/repo-maintenance.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
name: 'Repository Maintenance'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
name: 'Stale'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 60
|
||||
any-of-labels: 'cant-reproduce,not a bug'
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
lock-threads:
|
||||
name: 'Lock Old Threads'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
issue-inactive-days: '30'
|
||||
pr-inactive-days: '30'
|
||||
discussion-inactive-days: '30'
|
||||
log-output: true
|
||||
issue-comment: >
|
||||
This issue has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
Please open a new discussion or issue for related concerns.
|
||||
pr-comment: >
|
||||
This pull request has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
Please open a new discussion or issue for related concerns.
|
||||
discussion-comment: >
|
||||
This discussion has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
Please open a new discussion for related concerns.
|
||||
@ -5,7 +5,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#define CONFIG_FILENAME "/config.json"
|
||||
#define CONFIG_VERSION 0x00011b00 // 0.1.27 // make sure to clean all after change
|
||||
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
|
||||
|
||||
#define WIFI_MAX_SSID_STRLEN 32
|
||||
#define WIFI_MAX_PASSWORD_STRLEN 64
|
||||
@ -30,8 +30,6 @@
|
||||
|
||||
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
||||
|
||||
#define JSON_BUFFER_SIZE 12288
|
||||
|
||||
struct CHANNEL_CONFIG_T {
|
||||
uint16_t MaxChannelPower;
|
||||
char Name[CHAN_MAX_NAME_STRLEN];
|
||||
|
||||
@ -66,10 +66,10 @@ private:
|
||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100);
|
||||
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
||||
|
||||
static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||
static void createDtuInfo(DynamicJsonDocument& doc);
|
||||
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||
static void createDtuInfo(JsonDocument& doc);
|
||||
|
||||
static void createDeviceInfo(DynamicJsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
||||
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
||||
|
||||
static String getDtuUniqueId();
|
||||
static String getDtuUrl();
|
||||
|
||||
@ -10,6 +10,6 @@ public:
|
||||
static uint64_t generateDtuSerial();
|
||||
static int getTimezoneOffset();
|
||||
static void restartDtu();
|
||||
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
|
||||
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||
static void removeAllFiles();
|
||||
};
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "WebApi_webapp.h"
|
||||
#include "WebApi_ws_console.h"
|
||||
#include "WebApi_ws_live.h"
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
|
||||
@ -37,6 +38,10 @@ public:
|
||||
|
||||
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
|
||||
|
||||
static bool parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document);
|
||||
static uint64_t parseSerialFromRequest(AsyncWebServerRequest* request, String param_name = "inv");
|
||||
static bool sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line);
|
||||
|
||||
private:
|
||||
AsyncWebServer _server;
|
||||
|
||||
|
||||
@ -5,10 +5,11 @@ enum WebApiError {
|
||||
GenericBase = 1000,
|
||||
GenericSuccess,
|
||||
GenericNoValueFound,
|
||||
GenericDataTooLarge,
|
||||
GenericDataTooLarge, // not used anymore
|
||||
GenericParseError,
|
||||
GenericValueMissing,
|
||||
GenericWriteFailed,
|
||||
GenericInternalServerError,
|
||||
|
||||
DtuBase = 2000,
|
||||
DtuSerialZero,
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
|
||||
#define MQTT_JSON_DOC_SIZE 10240
|
||||
|
||||
class WebApiMqttClass {
|
||||
public:
|
||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
|
||||
#define MDNS_ENABLED false
|
||||
|
||||
#define NTP_SERVER "pool.ntp.org"
|
||||
#define NTP_SERVER_OLD "pool.ntp.org"
|
||||
#define NTP_SERVER "opendtu.pool.ntp.org"
|
||||
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
#define NTP_TIMEZONEDESCR "Europe/Berlin"
|
||||
#define NTP_LONGITUDE 10.4515f
|
||||
|
||||
@ -114,7 +114,7 @@ void HoymilesClass::loop()
|
||||
}
|
||||
|
||||
// Fetch grid profile
|
||||
if (iv->Statistics()->getLastUpdate() > 0 && iv->GridProfile()->getLastUpdate() == 0) {
|
||||
if (iv->Statistics()->getLastUpdate() > 0 && (iv->GridProfile()->getLastUpdate() == 0 || !iv->GridProfile()->containsValidData())) {
|
||||
iv->sendGridOnProFileParaRequest();
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include "TimeoutHelper.h"
|
||||
#include "commands/CommandAbstract.h"
|
||||
#include "types.h"
|
||||
#include <memory>
|
||||
#include <ThreadSafeQueue.h>
|
||||
#include <TimeoutHelper.h>
|
||||
#include <memory>
|
||||
|
||||
class HoymilesRadio {
|
||||
public:
|
||||
@ -43,4 +43,4 @@ protected:
|
||||
bool _busyFlag = false;
|
||||
|
||||
TimeoutHelper _rxTimeout;
|
||||
};
|
||||
};
|
||||
|
||||
@ -70,7 +70,7 @@ bool HMT_4CH::isValidSerial(const uint64_t serial)
|
||||
|
||||
String HMT_4CH::typeName() const
|
||||
{
|
||||
return F("HMT-1600/1800/2000-4T");
|
||||
return "HMT-1600/1800/2000-4T";
|
||||
}
|
||||
|
||||
const byteAssign_t* HMT_4CH::getByteAssignment() const
|
||||
|
||||
@ -84,7 +84,7 @@ bool HMT_6CH::isValidSerial(const uint64_t serial)
|
||||
|
||||
String HMT_6CH::typeName() const
|
||||
{
|
||||
return F("HMT-1800/2250-6T");
|
||||
return "HMT-1800/2250-6T";
|
||||
}
|
||||
|
||||
const byteAssign_t* HMT_6CH::getByteAssignment() const
|
||||
|
||||
@ -204,7 +204,7 @@ bool DevInfoParser::containsValidData() const
|
||||
struct tm info;
|
||||
localtime_r(&t, &info);
|
||||
|
||||
return info.tm_year > (2016 - 1900) || getHwPartNumber() == 124097;
|
||||
return info.tm_year > (2016 - 1900) && getHwPartNumber() != 124097;
|
||||
}
|
||||
|
||||
uint8_t DevInfoParser::getDevIdx() const
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2023 Thomas Basler and others
|
||||
* Copyright (C) 2023 - 2024 Thomas Basler and others
|
||||
*/
|
||||
#include "GridProfileParser.h"
|
||||
#include "../Hoymiles.h"
|
||||
@ -446,6 +446,11 @@ std::list<GridProfileSection_t> GridProfileParser::getProfile() const
|
||||
return l;
|
||||
}
|
||||
|
||||
bool GridProfileParser::containsValidData() const
|
||||
{
|
||||
return _gridProfileLength > 6;
|
||||
}
|
||||
|
||||
uint8_t GridProfileParser::getSectionSize(const uint8_t section_id, const uint8_t section_version)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
|
||||
@ -43,6 +43,8 @@ public:
|
||||
|
||||
std::list<GridProfileSection_t> getProfile() const;
|
||||
|
||||
bool containsValidData() const;
|
||||
|
||||
private:
|
||||
static uint8_t getSectionSize(const uint8_t section_id, const uint8_t section_version);
|
||||
static int16_t getSectionStart(const uint8_t section_id, const uint8_t section_version);
|
||||
@ -52,4 +54,4 @@ private:
|
||||
|
||||
static const std::array<const ProfileType_t, PROFILE_TYPE_COUNT> _profileTypes;
|
||||
static const std::array<const GridProfileValue_t, SECTION_VALUE_COUNT> _profileValues;
|
||||
};
|
||||
};
|
||||
|
||||
0
lib/ThreadSafeQueue/README.md
Normal file
0
lib/ThreadSafeQueue/README.md
Normal file
13
lib/ThreadSafeQueue/library.json
Normal file
13
lib/ThreadSafeQueue/library.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "ThreadSafeQueue",
|
||||
"keywords": "queue, threadsafe",
|
||||
"description": "An Arduino for ESP32 thread safe queue implementation",
|
||||
"authors": {
|
||||
"name": "Thomas Basler"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"frameworks": "arduino",
|
||||
"platforms": [
|
||||
"espressif32"
|
||||
]
|
||||
}
|
||||
0
lib/TimeoutHelper/README.md
Normal file
0
lib/TimeoutHelper/README.md
Normal file
13
lib/TimeoutHelper/library.json
Normal file
13
lib/TimeoutHelper/library.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "TimeoutHelper",
|
||||
"keywords": "timeout",
|
||||
"description": "An Arduino for ESP32 timeout helper",
|
||||
"authors": {
|
||||
"name": "Thomas Basler"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"frameworks": "arduino",
|
||||
"platforms": [
|
||||
"espressif32"
|
||||
]
|
||||
}
|
||||
26
patches/async_tcp/event_queue_size.patch
Normal file
26
patches/async_tcp/event_queue_size.patch
Normal file
@ -0,0 +1,26 @@
|
||||
diff --color -ruN a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp
|
||||
--- a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp
|
||||
+++ b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.cpp
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
static inline bool _init_async_event_queue(){
|
||||
if(!_async_queue){
|
||||
- _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *));
|
||||
+ _async_queue = xQueueCreate(CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE, sizeof(lwip_event_packet_t *));
|
||||
if(!_async_queue){
|
||||
return false;
|
||||
}
|
||||
diff --color -ruN a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h
|
||||
--- a/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h
|
||||
+++ b/.pio/libdeps/$$$env$$$/AsyncTCP-esphome/src/AsyncTCP.h
|
||||
@@ -53,6 +53,10 @@
|
||||
#define CONFIG_ASYNC_TCP_STACK_SIZE 8192 * 2
|
||||
#endif
|
||||
|
||||
+#ifndef CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE
|
||||
+#define CONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE 32
|
||||
+#endif
|
||||
+
|
||||
class AsyncClient;
|
||||
|
||||
#define ASYNC_MAX_ACK_TIME 5000
|
||||
@ -1,13 +0,0 @@
|
||||
diff --git a/.pio/libdeps/$$$env$$$/ESP Async WebServer/src/AsyncWebSocket.cpp b/.pio/libdeps/$$$env$$$/ESP Async WebServer/src/AsyncWebSocket.cpp
|
||||
index 12be5f8..8505f73 100644
|
||||
--- a/.pio/libdeps/$$$env$$$/ESP Async WebServer/src/AsyncWebSocket.cpp
|
||||
+++ b/.pio/libdeps/$$$env$$$/ESP Async WebServer/src/AsyncWebSocket.cpp
|
||||
@@ -737,7 +737,7 @@ void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len)
|
||||
IPAddress AsyncWebSocketClient::remoteIP() const
|
||||
{
|
||||
if (!_client)
|
||||
- return IPAddress(0U);
|
||||
+ return IPAddress((uint32_t)0);
|
||||
|
||||
return _client->remoteIP();
|
||||
}
|
||||
@ -19,12 +19,13 @@ extra_configs =
|
||||
custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb
|
||||
|
||||
framework = arduino
|
||||
platform = espressif32@6.5.0
|
||||
platform = espressif32@6.6.0
|
||||
|
||||
build_flags =
|
||||
-DPIOENV=\"$PIOENV\"
|
||||
-D_TASK_STD_FUNCTION=1
|
||||
-D_TASK_THREAD_SAFE=1
|
||||
-DCONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE=128
|
||||
-Wall -Wextra -Wunused -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference
|
||||
; Have to remove -Werror because of
|
||||
; https://github.com/espressif/arduino-esp32/issues/9044 and
|
||||
@ -36,11 +37,11 @@ build_unflags =
|
||||
-std=gnu++11
|
||||
|
||||
lib_deps =
|
||||
mathieucarbou/ESP Async WebServer @ 2.8.1
|
||||
bblanchon/ArduinoJson @ ^6.21.5
|
||||
mathieucarbou/ESP Async WebServer @ 2.9.3
|
||||
bblanchon/ArduinoJson @ ^7.0.4
|
||||
https://github.com/bertmelis/espMqttClient.git#v1.6.0
|
||||
nrf24/RF24 @ ^1.4.8
|
||||
olikraus/U8g2 @ ^2.35.14
|
||||
olikraus/U8g2 @ ^2.35.17
|
||||
buelowp/sunset @ ^1.1.7
|
||||
https://github.com/arkhipenko/TaskScheduler#testing
|
||||
|
||||
@ -59,7 +60,7 @@ board_build.embed_files =
|
||||
webapp_dist/js/app.js.gz
|
||||
webapp_dist/site.webmanifest
|
||||
|
||||
custom_patches =
|
||||
custom_patches = async_tcp
|
||||
|
||||
monitor_filters = esp32_exception_decoder, time, log2file, colorize
|
||||
monitor_speed = 115200
|
||||
@ -87,13 +88,13 @@ build_flags = ${env.build_flags}
|
||||
|
||||
[env:generic_esp32c3]
|
||||
board = esp32-c3-devkitc-02
|
||||
custom_patches = ${env.custom_patches},esp32c3
|
||||
custom_patches = ${env.custom_patches}
|
||||
build_flags = ${env.build_flags}
|
||||
|
||||
|
||||
[env:generic_esp32c3_usb]
|
||||
board = esp32-c3-devkitc-02
|
||||
custom_patches = ${env.custom_patches},esp32c3
|
||||
custom_patches = ${env.custom_patches}
|
||||
build_flags = ${env.build_flags}
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
|
||||
@ -25,17 +25,13 @@ bool ConfigurationClass::write()
|
||||
}
|
||||
config.Cfg.SaveCount++;
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
JsonDocument doc;
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject cfg = doc.createNestedObject("cfg");
|
||||
JsonObject cfg = doc["cfg"].to<JsonObject>();
|
||||
cfg["version"] = config.Cfg.Version;
|
||||
cfg["save_count"] = config.Cfg.SaveCount;
|
||||
|
||||
JsonObject wifi = doc.createNestedObject("wifi");
|
||||
JsonObject wifi = doc["wifi"].to<JsonObject>();
|
||||
wifi["ssid"] = config.WiFi.Ssid;
|
||||
wifi["password"] = config.WiFi.Password;
|
||||
wifi["ip"] = IPAddress(config.WiFi.Ip).toString();
|
||||
@ -47,10 +43,10 @@ bool ConfigurationClass::write()
|
||||
wifi["hostname"] = config.WiFi.Hostname;
|
||||
wifi["aptimeout"] = config.WiFi.ApTimeout;
|
||||
|
||||
JsonObject mdns = doc.createNestedObject("mdns");
|
||||
JsonObject mdns = doc["mdns"].to<JsonObject>();
|
||||
mdns["enabled"] = config.Mdns.Enabled;
|
||||
|
||||
JsonObject ntp = doc.createNestedObject("ntp");
|
||||
JsonObject ntp = doc["ntp"].to<JsonObject>();
|
||||
ntp["server"] = config.Ntp.Server;
|
||||
ntp["timezone"] = config.Ntp.Timezone;
|
||||
ntp["timezone_descr"] = config.Ntp.TimezoneDescr;
|
||||
@ -58,7 +54,7 @@ bool ConfigurationClass::write()
|
||||
ntp["longitude"] = config.Ntp.Longitude;
|
||||
ntp["sunsettype"] = config.Ntp.SunsetType;
|
||||
|
||||
JsonObject mqtt = doc.createNestedObject("mqtt");
|
||||
JsonObject mqtt = doc["mqtt"].to<JsonObject>();
|
||||
mqtt["enabled"] = config.Mqtt.Enabled;
|
||||
mqtt["hostname"] = config.Mqtt.Hostname;
|
||||
mqtt["port"] = config.Mqtt.Port;
|
||||
@ -69,27 +65,27 @@ bool ConfigurationClass::write()
|
||||
mqtt["publish_interval"] = config.Mqtt.PublishInterval;
|
||||
mqtt["clean_session"] = config.Mqtt.CleanSession;
|
||||
|
||||
JsonObject mqtt_lwt = mqtt.createNestedObject("lwt");
|
||||
JsonObject mqtt_lwt = mqtt["lwt"].to<JsonObject>();
|
||||
mqtt_lwt["topic"] = config.Mqtt.Lwt.Topic;
|
||||
mqtt_lwt["value_online"] = config.Mqtt.Lwt.Value_Online;
|
||||
mqtt_lwt["value_offline"] = config.Mqtt.Lwt.Value_Offline;
|
||||
mqtt_lwt["qos"] = config.Mqtt.Lwt.Qos;
|
||||
|
||||
JsonObject mqtt_tls = mqtt.createNestedObject("tls");
|
||||
JsonObject mqtt_tls = mqtt["tls"].to<JsonObject>();
|
||||
mqtt_tls["enabled"] = config.Mqtt.Tls.Enabled;
|
||||
mqtt_tls["root_ca_cert"] = config.Mqtt.Tls.RootCaCert;
|
||||
mqtt_tls["certlogin"] = config.Mqtt.Tls.CertLogin;
|
||||
mqtt_tls["client_cert"] = config.Mqtt.Tls.ClientCert;
|
||||
mqtt_tls["client_key"] = config.Mqtt.Tls.ClientKey;
|
||||
|
||||
JsonObject mqtt_hass = mqtt.createNestedObject("hass");
|
||||
JsonObject mqtt_hass = mqtt["hass"].to<JsonObject>();
|
||||
mqtt_hass["enabled"] = config.Mqtt.Hass.Enabled;
|
||||
mqtt_hass["retain"] = config.Mqtt.Hass.Retain;
|
||||
mqtt_hass["topic"] = config.Mqtt.Hass.Topic;
|
||||
mqtt_hass["individual_panels"] = config.Mqtt.Hass.IndividualPanels;
|
||||
mqtt_hass["expire"] = config.Mqtt.Hass.Expire;
|
||||
|
||||
JsonObject dtu = doc.createNestedObject("dtu");
|
||||
JsonObject dtu = doc["dtu"].to<JsonObject>();
|
||||
dtu["serial"] = config.Dtu.Serial;
|
||||
dtu["poll_interval"] = config.Dtu.PollInterval;
|
||||
dtu["nrf_pa_level"] = config.Dtu.Nrf.PaLevel;
|
||||
@ -97,14 +93,14 @@ bool ConfigurationClass::write()
|
||||
dtu["cmt_frequency"] = config.Dtu.Cmt.Frequency;
|
||||
dtu["cmt_country_mode"] = config.Dtu.Cmt.CountryMode;
|
||||
|
||||
JsonObject security = doc.createNestedObject("security");
|
||||
JsonObject security = doc["security"].to<JsonObject>();
|
||||
security["password"] = config.Security.Password;
|
||||
security["allow_readonly"] = config.Security.AllowReadonly;
|
||||
|
||||
JsonObject device = doc.createNestedObject("device");
|
||||
JsonObject device = doc["device"].to<JsonObject>();
|
||||
device["pinmapping"] = config.Dev_PinMapping;
|
||||
|
||||
JsonObject display = device.createNestedObject("display");
|
||||
JsonObject display = device["display"].to<JsonObject>();
|
||||
display["powersafe"] = config.Display.PowerSafe;
|
||||
display["screensaver"] = config.Display.ScreenSaver;
|
||||
display["rotation"] = config.Display.Rotation;
|
||||
@ -113,15 +109,15 @@ bool ConfigurationClass::write()
|
||||
display["diagram_duration"] = config.Display.Diagram.Duration;
|
||||
display["diagram_mode"] = config.Display.Diagram.Mode;
|
||||
|
||||
JsonArray leds = device.createNestedArray("led");
|
||||
JsonArray leds = device["led"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
||||
JsonObject led = leds.createNestedObject();
|
||||
JsonObject led = leds.add<JsonObject>();
|
||||
led["brightness"] = config.Led_Single[i].Brightness;
|
||||
}
|
||||
|
||||
JsonArray inverters = doc.createNestedArray("inverters");
|
||||
JsonArray inverters = doc["inverters"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||
JsonObject inv = inverters.createNestedObject();
|
||||
JsonObject inv = inverters.add<JsonObject>();
|
||||
inv["serial"] = config.Inverter[i].Serial;
|
||||
inv["name"] = config.Inverter[i].Name;
|
||||
inv["order"] = config.Inverter[i].Order;
|
||||
@ -134,15 +130,19 @@ bool ConfigurationClass::write()
|
||||
inv["zero_day"] = config.Inverter[i].ZeroYieldDayOnMidnight;
|
||||
inv["yieldday_correction"] = config.Inverter[i].YieldDayCorrection;
|
||||
|
||||
JsonArray channel = inv.createNestedArray("channel");
|
||||
JsonArray channel = inv["channel"].to<JsonArray>();
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
JsonObject chanData = channel.createNestedObject();
|
||||
JsonObject chanData = channel.add<JsonObject>();
|
||||
chanData["name"] = config.Inverter[i].channel[c].Name;
|
||||
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
|
||||
chanData["yield_total_offset"] = config.Inverter[i].channel[c].YieldTotalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serialize JSON to file
|
||||
if (serializeJson(doc, f) == 0) {
|
||||
MessageOutput.println("Failed to write file");
|
||||
@ -157,11 +157,7 @@ bool ConfigurationClass::read()
|
||||
{
|
||||
File f = LittleFS.open(CONFIG_FILENAME, "r", false);
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return false;
|
||||
}
|
||||
JsonDocument doc;
|
||||
|
||||
// Deserialize the JSON document
|
||||
const DeserializationError error = deserializeJson(doc, f);
|
||||
@ -169,6 +165,10 @@ bool ConfigurationClass::read()
|
||||
MessageOutput.println("Failed to read file, using default configuration");
|
||||
}
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject cfg = doc["cfg"];
|
||||
config.Cfg.Version = cfg["version"] | CONFIG_VERSION;
|
||||
config.Cfg.SaveCount = cfg["save_count"] | 0;
|
||||
@ -324,11 +324,7 @@ void ConfigurationClass::migrate()
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument doc;
|
||||
|
||||
// Deserialize the JSON document
|
||||
const DeserializationError error = deserializeJson(doc, f);
|
||||
@ -337,6 +333,10 @@ void ConfigurationClass::migrate()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.Cfg.Version < 0x00011700) {
|
||||
JsonArray inverters = doc["inverters"];
|
||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||
@ -372,6 +372,12 @@ void ConfigurationClass::migrate()
|
||||
config.Dtu.Cmt.Frequency *= 1000;
|
||||
}
|
||||
|
||||
if (config.Cfg.Version < 0x00011c00) {
|
||||
if (!strcmp(config.Ntp.Server, NTP_SERVER_OLD)) {
|
||||
strlcpy(config.Ntp.Server, NTP_SERVER, sizeof(config.Ntp.Server));
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
config.Cfg.Version = CONFIG_VERSION;
|
||||
|
||||
@ -51,9 +51,9 @@ void InverterSettingsClass::init(Scheduler& scheduler)
|
||||
|
||||
if (PinMapping.isValidCmt2300Config()) {
|
||||
Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3);
|
||||
MessageOutput.println(F(" Setting country mode... "));
|
||||
MessageOutput.println(" Setting country mode... ");
|
||||
Hoymiles.getRadioCmt()->setCountryMode(static_cast<CountryModeId_t>(config.Dtu.Cmt.CountryMode));
|
||||
MessageOutput.println(F(" Setting CMT target frequency... "));
|
||||
MessageOutput.println(" Setting CMT target frequency... ");
|
||||
Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency);
|
||||
}
|
||||
|
||||
|
||||
@ -137,10 +137,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
||||
name = "CH" + chanNum + " " + fieldName;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = name;
|
||||
root["stat_t"] = stateTopic;
|
||||
@ -163,6 +160,10 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
||||
root["stat_cla"] = stateCls;
|
||||
}
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -185,10 +186,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
||||
|
||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = caption;
|
||||
root["uniq_id"] = serial + "_" + buttonId;
|
||||
@ -204,6 +202,10 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
||||
|
||||
createInverterInfo(root, inv);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -227,10 +229,7 @@ void MqttHandleHassClass::publishInverterNumber(
|
||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + commandTopic;
|
||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = caption;
|
||||
root["uniq_id"] = serial + "_" + buttonId;
|
||||
@ -246,6 +245,10 @@ void MqttHandleHassClass::publishInverterNumber(
|
||||
|
||||
createInverterInfo(root, inv);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -265,10 +268,7 @@ void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAb
|
||||
|
||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = caption;
|
||||
root["uniq_id"] = serial + "_" + sensorId;
|
||||
@ -278,6 +278,10 @@ void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAb
|
||||
|
||||
createInverterInfo(root, inv);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -293,10 +297,7 @@ void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_
|
||||
topic = id;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = name;
|
||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
||||
@ -322,6 +323,10 @@ void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_
|
||||
|
||||
createDtuInfo(root);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
const String configTopic = "sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
||||
serializeJson(root, buffer);
|
||||
@ -339,10 +344,7 @@ void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* d
|
||||
topic = String("dtu/") + "/" + id;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
JsonDocument root;
|
||||
|
||||
root["name"] = name;
|
||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
||||
@ -359,13 +361,17 @@ void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* d
|
||||
|
||||
createDtuInfo(root);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
const String configTopic = "binary_sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::createInverterInfo(DynamicJsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
||||
void MqttHandleHassClass::createInverterInfo(JsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
||||
{
|
||||
createDeviceInfo(
|
||||
root,
|
||||
@ -378,7 +384,7 @@ void MqttHandleHassClass::createInverterInfo(DynamicJsonDocument& root, std::sha
|
||||
getDtuUniqueId());
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::createDtuInfo(DynamicJsonDocument& root)
|
||||
void MqttHandleHassClass::createDtuInfo(JsonDocument& root)
|
||||
{
|
||||
createDeviceInfo(
|
||||
root,
|
||||
@ -391,12 +397,12 @@ void MqttHandleHassClass::createDtuInfo(DynamicJsonDocument& root)
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::createDeviceInfo(
|
||||
DynamicJsonDocument& root,
|
||||
JsonDocument& root,
|
||||
const String& name, const String& identifiers, const String& configuration_url,
|
||||
const String& manufacturer, const String& model, const String& sw_version,
|
||||
const String& via_device)
|
||||
{
|
||||
auto object = root.createNestedObject("dev");
|
||||
auto object = root["dev"].to<JsonObject>();
|
||||
|
||||
object["name"] = name;
|
||||
object["ids"] = identifiers;
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
#include <LittleFS.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JSON_BUFFER_SIZE 6144
|
||||
|
||||
#ifndef DISPLAY_TYPE
|
||||
#define DISPLAY_TYPE 0U
|
||||
#endif
|
||||
@ -141,7 +139,7 @@ bool PinMappingClass::init(const String& deviceMapping)
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
JsonDocument doc;
|
||||
// Deserialize the JSON document
|
||||
DeserializationError error = deserializeJson(doc, f);
|
||||
if (error) {
|
||||
@ -216,4 +214,4 @@ bool PinMappingClass::isValidCmt2300Config() const
|
||||
bool PinMappingClass::isValidEthConfig() const
|
||||
{
|
||||
return _pinMapping.eth_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,9 +69,9 @@ void Utils::restartDtu()
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line)
|
||||
bool Utils::checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line)
|
||||
{
|
||||
if (doc.capacity() == 0) {
|
||||
if (doc.overflowed()) {
|
||||
MessageOutput.printf("Alloc failed: %s, %d\r\n", function, line);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
*/
|
||||
#include "WebApi.h"
|
||||
#include "Configuration.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "defaults.h"
|
||||
#include <AsyncJson.h>
|
||||
|
||||
@ -85,4 +86,58 @@ void WebApiClass::writeConfig(JsonVariant& retMsg, const WebApiError code, const
|
||||
}
|
||||
}
|
||||
|
||||
bool WebApiClass::parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document)
|
||||
{
|
||||
auto& retMsg = response->getRoot();
|
||||
retMsg["type"] = "warning";
|
||||
|
||||
if (!request->hasParam("data", true)) {
|
||||
retMsg["message"] = "No values found!";
|
||||
retMsg["code"] = WebApiError::GenericNoValueFound;
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
const String json = request->getParam("data", true)->value();
|
||||
const DeserializationError error = deserializeJson(json_document, json);
|
||||
if (error) {
|
||||
retMsg["message"] = "Failed to parse data!";
|
||||
retMsg["code"] = WebApiError::GenericParseError;
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t WebApiClass::parseSerialFromRequest(AsyncWebServerRequest* request, String param_name)
|
||||
{
|
||||
if (request->hasParam(param_name)) {
|
||||
String s = request->getParam(param_name)->value();
|
||||
return strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WebApiClass::sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line)
|
||||
{
|
||||
bool ret_val = true;
|
||||
if (response->overflowed()) {
|
||||
auto& root = response->getRoot();
|
||||
|
||||
root.clear();
|
||||
root["message"] = String("500 Internal Server Error: ") + function + ", " + line;
|
||||
root["code"] = WebApiError::GenericInternalServerError;
|
||||
root["type"] = "danger";
|
||||
response->setCode(500);
|
||||
MessageOutput.printf("WebResponse failed: %s, %d\r\n", function, line);
|
||||
ret_val = false;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
WebApiClass WebApi;
|
||||
|
||||
@ -40,6 +40,7 @@ void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request)
|
||||
requestFile = name;
|
||||
} else {
|
||||
request->send(404);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,51 +54,24 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const DeserializationError error = deserializeJson(root, json);
|
||||
|
||||
if (error) {
|
||||
retMsg["message"] = "Failed to parse data!";
|
||||
retMsg["code"] = WebApiError::GenericDataTooLarge;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(root.containsKey("delete"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["delete"].as<bool>() == false) {
|
||||
retMsg["message"] = "Not deleted anything!";
|
||||
retMsg["code"] = WebApiError::ConfigNotDeleted;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -105,8 +79,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Configuration resettet. Rebooting now...";
|
||||
retMsg["code"] = WebApiError::ConfigSuccess;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
Utils::removeAllFiles();
|
||||
Utils::restartDtu();
|
||||
@ -120,7 +93,7 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
auto data = root.createNestedArray("configs");
|
||||
auto data = root["configs"].to<JsonArray>();
|
||||
|
||||
File rootfs = LittleFS.open("/");
|
||||
File file = rootfs.openNextFile();
|
||||
@ -128,15 +101,14 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
|
||||
if (file.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
JsonObject obj = data.createNestedObject();
|
||||
JsonObject obj = data.add<JsonObject>();
|
||||
obj["name"] = String(file.name());
|
||||
|
||||
file = rootfs.openNextFile();
|
||||
}
|
||||
file.close();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request)
|
||||
|
||||
@ -26,15 +26,15 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
const PinMapping_t& pin = PinMapping.get();
|
||||
|
||||
auto curPin = root.createNestedObject("curPin");
|
||||
auto curPin = root["curPin"].to<JsonObject>();
|
||||
curPin["name"] = config.Dev_PinMapping;
|
||||
|
||||
auto nrfPinObj = curPin.createNestedObject("nrf24");
|
||||
auto nrfPinObj = curPin["nrf24"].to<JsonObject>();
|
||||
nrfPinObj["clk"] = pin.nrf24_clk;
|
||||
nrfPinObj["cs"] = pin.nrf24_cs;
|
||||
nrfPinObj["en"] = pin.nrf24_en;
|
||||
@ -42,7 +42,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
nrfPinObj["miso"] = pin.nrf24_miso;
|
||||
nrfPinObj["mosi"] = pin.nrf24_mosi;
|
||||
|
||||
auto cmtPinObj = curPin.createNestedObject("cmt");
|
||||
auto cmtPinObj = curPin["cmt"].to<JsonObject>();
|
||||
cmtPinObj["clk"] = pin.cmt_clk;
|
||||
cmtPinObj["cs"] = pin.cmt_cs;
|
||||
cmtPinObj["fcs"] = pin.cmt_fcs;
|
||||
@ -50,7 +50,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
cmtPinObj["gpio2"] = pin.cmt_gpio2;
|
||||
cmtPinObj["gpio3"] = pin.cmt_gpio3;
|
||||
|
||||
auto ethPinObj = curPin.createNestedObject("eth");
|
||||
auto ethPinObj = curPin["eth"].to<JsonObject>();
|
||||
ethPinObj["enabled"] = pin.eth_enabled;
|
||||
ethPinObj["phy_addr"] = pin.eth_phy_addr;
|
||||
ethPinObj["power"] = pin.eth_power;
|
||||
@ -59,19 +59,19 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
ethPinObj["type"] = pin.eth_type;
|
||||
ethPinObj["clk_mode"] = pin.eth_clk_mode;
|
||||
|
||||
auto displayPinObj = curPin.createNestedObject("display");
|
||||
auto displayPinObj = curPin["display"].to<JsonObject>();
|
||||
displayPinObj["type"] = pin.display_type;
|
||||
displayPinObj["data"] = pin.display_data;
|
||||
displayPinObj["clk"] = pin.display_clk;
|
||||
displayPinObj["cs"] = pin.display_cs;
|
||||
displayPinObj["reset"] = pin.display_reset;
|
||||
|
||||
auto ledPinObj = curPin.createNestedObject("led");
|
||||
auto ledPinObj = curPin["led"].to<JsonObject>();
|
||||
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
||||
ledPinObj["led" + String(i)] = pin.led[i];
|
||||
}
|
||||
|
||||
auto display = root.createNestedObject("display");
|
||||
auto display = root["display"].to<JsonObject>();
|
||||
display["rotation"] = config.Display.Rotation;
|
||||
display["power_safe"] = config.Display.PowerSafe;
|
||||
display["screensaver"] = config.Display.ScreenSaver;
|
||||
@ -80,14 +80,13 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
display["diagramduration"] = config.Display.Diagram.Duration;
|
||||
display["diagrammode"] = config.Display.Diagram.Mode;
|
||||
|
||||
auto leds = root.createNestedArray("led");
|
||||
auto leds = root["led"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
|
||||
auto led = leds.createNestedObject();
|
||||
auto led = leds.add<JsonObject>();
|
||||
led["brightness"] = config.Led_Single[i].Brightness;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
@ -96,45 +95,19 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const String json = request->getParam("data", true)->value();
|
||||
|
||||
if (json.length() > MQTT_JSON_DOC_SIZE) {
|
||||
retMsg["message"] = "Data too large!";
|
||||
retMsg["code"] = WebApiError::GenericDataTooLarge;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(MQTT_JSON_DOC_SIZE);
|
||||
const 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("curPin")
|
||||
|| root.containsKey("display"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -142,8 +115,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Pin mapping must between 1 and " STR(DEV_MAX_MAPPING_NAME_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::HardwarePinMappingLength;
|
||||
retMsg["param"]["max"] = DEV_MAX_MAPPING_NAME_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -174,8 +146,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
if (performRestart) {
|
||||
Utils::restartDtu();
|
||||
|
||||
@ -23,13 +23,7 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
|
||||
uint64_t serial = 0;
|
||||
if (request->hasParam("inv")) {
|
||||
String s = request->getParam("inv")->value();
|
||||
serial = strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
|
||||
auto serial = WebApi.parseSerialFromRequest(request);
|
||||
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||
|
||||
if (inv != nullptr) {
|
||||
@ -43,6 +37,5 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
|
||||
root["fw_build_datetime"] = inv->DevInfo()->getFwBuildDateTimeStr();
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -62,10 +62,10 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
|
||||
root["cmt_country"] = config.Dtu.Cmt.CountryMode;
|
||||
root["cmt_chan_width"] = Hoymiles.getRadioCmt()->getChannelWidth();
|
||||
|
||||
auto data = root.createNestedArray("country_def");
|
||||
auto data = root["country_def"].to<JsonArray>();
|
||||
auto countryDefs = Hoymiles.getRadioCmt()->getCountryFrequencyList();
|
||||
for (const auto& definition : countryDefs) {
|
||||
auto obj = data.createNestedObject();
|
||||
auto obj = data.add<JsonObject>();
|
||||
obj["freq_default"] = definition.definition.Freq_Default;
|
||||
obj["freq_min"] = definition.definition.Freq_Min;
|
||||
obj["freq_max"] = definition.definition.Freq_Max;
|
||||
@ -73,8 +73,7 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
|
||||
obj["freq_legal_max"] = definition.definition.Freq_Legal_Max;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
@ -84,37 +83,12 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("serial")
|
||||
&& root.containsKey("pollinterval")
|
||||
@ -124,8 +98,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
&& root.containsKey("cmt_country"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,40 +108,35 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
if (serial == 0) {
|
||||
retMsg["message"] = "Serial cannot be zero!";
|
||||
retMsg["code"] = WebApiError::DtuSerialZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["pollinterval"].as<uint32_t>() == 0) {
|
||||
retMsg["message"] = "Poll interval must be greater zero!";
|
||||
retMsg["code"] = WebApiError::DtuPollZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["nrf_palevel"].as<uint8_t>() > 3) {
|
||||
retMsg["message"] = "Invalid power level setting!";
|
||||
retMsg["code"] = WebApiError::DtuInvalidPowerLevel;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["cmt_palevel"].as<int8_t>() < -10 || root["cmt_palevel"].as<int8_t>() > 20) {
|
||||
retMsg["message"] = "Invalid power level setting!";
|
||||
retMsg["code"] = WebApiError::DtuInvalidPowerLevel;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["cmt_country"].as<uint8_t>() >= CountryModeId_t::CountryModeId_Max) {
|
||||
retMsg["message"] = "Invalid country setting!";
|
||||
retMsg["code"] = WebApiError::DtuInvalidCmtCountry;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -181,8 +149,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::DtuInvalidCmtFrequency;
|
||||
retMsg["param"]["min"] = FrequencyDefinition.Freq_Min;
|
||||
retMsg["param"]["max"] = FrequencyDefinition.Freq_Max;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -197,8 +164,8 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
_applyDataTask.enable();
|
||||
_applyDataTask.restart();
|
||||
}
|
||||
|
||||
@ -20,14 +20,9 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 2048);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
|
||||
uint64_t serial = 0;
|
||||
if (request->hasParam("inv")) {
|
||||
String s = request->getParam("inv")->value();
|
||||
serial = strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
auto serial = WebApi.parseSerialFromRequest(request);
|
||||
|
||||
AlarmMessageLocale_t locale = AlarmMessageLocale_t::EN;
|
||||
if (request->hasParam("locale")) {
|
||||
@ -47,10 +42,10 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
|
||||
uint8_t logEntryCount = inv->EventLog()->getEntryCount();
|
||||
|
||||
root["count"] = logEntryCount;
|
||||
JsonArray eventsArray = root.createNestedArray("events");
|
||||
JsonArray eventsArray = root["events"].to<JsonArray>();
|
||||
|
||||
for (uint8_t logEntry = 0; logEntry < logEntryCount; logEntry++) {
|
||||
JsonObject eventsObject = eventsArray.createNestedObject();
|
||||
JsonObject eventsObject = eventsArray.add<JsonObject>();
|
||||
|
||||
AlarmLogEntry_t entry;
|
||||
inv->EventLog()->getLogEntry(logEntry, entry, locale);
|
||||
@ -62,6 +57,5 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -21,32 +21,26 @@ void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
|
||||
uint64_t serial = 0;
|
||||
if (request->hasParam("inv")) {
|
||||
String s = request->getParam("inv")->value();
|
||||
serial = strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
|
||||
auto serial = WebApi.parseSerialFromRequest(request);
|
||||
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||
|
||||
if (inv != nullptr) {
|
||||
root["name"] = inv->GridProfile()->getProfileName();
|
||||
root["version"] = inv->GridProfile()->getProfileVersion();
|
||||
|
||||
auto jsonSections = root.createNestedArray("sections");
|
||||
auto jsonSections = root["sections"].to<JsonArray>();
|
||||
auto profSections = inv->GridProfile()->getProfile();
|
||||
|
||||
for (auto &profSection : profSections) {
|
||||
auto jsonSection = jsonSections.createNestedObject();
|
||||
auto jsonSection = jsonSections.add<JsonObject>();
|
||||
jsonSection["name"] = profSection.SectionName;
|
||||
|
||||
auto jsonItems = jsonSection.createNestedArray("items");
|
||||
auto jsonItems = jsonSection["items"].to<JsonArray>();
|
||||
|
||||
for (auto &profItem : profSection.items) {
|
||||
auto jsonItem = jsonItems.createNestedObject();
|
||||
auto jsonItem = jsonItems.add<JsonObject>();
|
||||
|
||||
jsonItem["n"] = profItem.Name;
|
||||
jsonItem["u"] = profItem.Unit;
|
||||
@ -55,8 +49,7 @@ void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request)
|
||||
@ -65,24 +58,17 @@ void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
|
||||
uint64_t serial = 0;
|
||||
if (request->hasParam("inv")) {
|
||||
String s = request->getParam("inv")->value();
|
||||
serial = strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
|
||||
auto serial = WebApi.parseSerialFromRequest(request);
|
||||
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||
|
||||
if (inv != nullptr) {
|
||||
auto raw = root.createNestedArray("raw");
|
||||
auto raw = root["raw"].to<JsonArray>();
|
||||
auto data = inv->GridProfile()->getRawData();
|
||||
|
||||
copyArray(&data[0], data.size(), raw);
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -29,15 +29,15 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 768 * INV_MAX_COUNT);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
JsonArray data = root.createNestedArray("inverter");
|
||||
JsonArray data = root["inverter"].to<JsonArray>();
|
||||
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||
if (config.Inverter[i].Serial > 0) {
|
||||
JsonObject obj = data.createNestedObject();
|
||||
JsonObject obj = data.add<JsonObject>();
|
||||
obj["id"] = i;
|
||||
obj["name"] = String(config.Inverter[i].Name);
|
||||
obj["order"] = config.Inverter[i].Order;
|
||||
@ -67,9 +67,9 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
max_channels = inv->Statistics()->getChannelsByType(TYPE_DC).size();
|
||||
}
|
||||
|
||||
JsonArray channel = obj.createNestedArray("channel");
|
||||
JsonArray channel = obj["channel"].to<JsonArray>();
|
||||
for (uint8_t c = 0; c < max_channels; c++) {
|
||||
JsonObject chanData = channel.createNestedObject();
|
||||
JsonObject chanData = channel.add<JsonObject>();
|
||||
chanData["name"] = config.Inverter[i].channel[c].Name;
|
||||
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
|
||||
chanData["yield_total_offset"] = config.Inverter[i].channel[c].YieldTotalOffset;
|
||||
@ -77,8 +77,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
@ -88,44 +87,18 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("serial")
|
||||
&& root.containsKey("name"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,8 +108,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
if (serial == 0) {
|
||||
retMsg["message"] = "Serial must be a number > 0!";
|
||||
retMsg["code"] = WebApiError::InverterSerialZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -144,8 +116,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Name must between 1 and " STR(INV_MAX_NAME_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::InverterNameLength;
|
||||
retMsg["param"]["max"] = INV_MAX_NAME_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -155,8 +126,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Only " STR(INV_MAX_COUNT) " inverters are supported!";
|
||||
retMsg["code"] = WebApiError::InverterCount;
|
||||
retMsg["param"]["max"] = INV_MAX_COUNT;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -167,8 +137,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg, WebApiError::InverterAdded, "Inverter created!");
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
auto inv = Hoymiles.addInverter(inverter->Name, inverter->Serial);
|
||||
|
||||
@ -188,51 +157,24 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("channel"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["id"].as<uint8_t>() > INV_MAX_COUNT - 1) {
|
||||
retMsg["message"] = "Invalid ID specified!";
|
||||
retMsg["code"] = WebApiError::InverterInvalidId;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -242,8 +184,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
if (serial == 0) {
|
||||
retMsg["message"] = "Serial must be a number > 0!";
|
||||
retMsg["code"] = WebApiError::InverterSerialZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -251,8 +192,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Name must between 1 and " STR(INV_MAX_NAME_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::InverterNameLength;
|
||||
retMsg["param"]["max"] = INV_MAX_NAME_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -260,8 +200,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
if (channelArray.size() == 0 || channelArray.size() > INV_MAX_CHAN_COUNT) {
|
||||
retMsg["message"] = "Invalid amount of max channel setting given!";
|
||||
retMsg["code"] = WebApiError::InverterInvalidMaxChannel;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -293,8 +232,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!");
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
std::shared_ptr<InverterAbstract> inv = Hoymiles.getInverterBySerial(old_serial);
|
||||
|
||||
@ -333,51 +271,24 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("id"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["id"].as<uint8_t>() > INV_MAX_COUNT - 1) {
|
||||
retMsg["message"] = "Invalid ID specified!";
|
||||
retMsg["code"] = WebApiError::InverterInvalidId;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -390,8 +301,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg, WebApiError::InverterDeleted, "Inverter deleted!");
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
MqttHandleHass.forceUpdate();
|
||||
}
|
||||
@ -403,43 +313,17 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -457,6 +341,5 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!");
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -47,8 +47,7 @@ void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
|
||||
root[serial]["limit_set_status"] = limitStatus;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
@ -58,45 +57,19 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("serial")
|
||||
&& root.containsKey("limit_value")
|
||||
&& root.containsKey("limit_type"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -106,8 +79,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
if (serial == 0) {
|
||||
retMsg["message"] = "Serial must be a number > 0!";
|
||||
retMsg["code"] = WebApiError::LimitSerialZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,8 +87,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Limit must between 0 and " STR(MAX_INVERTER_LIMIT) "!";
|
||||
retMsg["code"] = WebApiError::LimitInvalidLimit;
|
||||
retMsg["param"]["max"] = MAX_INVERTER_LIMIT;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -127,8 +98,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
|
||||
retMsg["message"] = "Invalid type specified!";
|
||||
retMsg["code"] = WebApiError::LimitInvalidType;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -139,8 +109,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
if (inv == nullptr) {
|
||||
retMsg["message"] = "Invalid inverter specified!";
|
||||
retMsg["code"] = WebApiError::LimitInvalidInverter;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -150,6 +119,5 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Settings saved!";
|
||||
retMsg["code"] = WebApiError::GenericSuccess;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -22,44 +22,18 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const String json = request->getParam("data", true)->value();
|
||||
|
||||
if (json.length() > MQTT_JSON_DOC_SIZE) {
|
||||
retMsg["message"] = "Data too large!";
|
||||
retMsg["code"] = WebApiError::GenericDataTooLarge;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(MQTT_JSON_DOC_SIZE);
|
||||
const 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("reboot"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -68,14 +42,12 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Reboot triggered!";
|
||||
retMsg["code"] = WebApiError::MaintenanceRebootTriggered;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
Utils::restartDtu();
|
||||
} else {
|
||||
retMsg["message"] = "Reboot cancled!";
|
||||
retMsg["code"] = WebApiError::MaintenanceRebootCancled;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
@ -50,8 +50,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
|
||||
root["mqtt_hass_topic"] = config.Mqtt.Hass.Topic;
|
||||
root["mqtt_hass_individualpanels"] = config.Mqtt.Hass.IndividualPanels;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
|
||||
@ -60,7 +59,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
@ -88,8 +87,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
|
||||
root["mqtt_hass_topic"] = config.Mqtt.Hass.Topic;
|
||||
root["mqtt_hass_individualpanels"] = config.Mqtt.Hass.IndividualPanels;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
@ -98,38 +96,13 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const String json = request->getParam("data", true)->value();
|
||||
|
||||
if (json.length() > MQTT_JSON_DOC_SIZE) {
|
||||
retMsg["message"] = "Data too large!";
|
||||
retMsg["code"] = WebApiError::GenericDataTooLarge;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(MQTT_JSON_DOC_SIZE);
|
||||
const 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("mqtt_enabled")
|
||||
&& root.containsKey("mqtt_hostname")
|
||||
@ -155,8 +128,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
&& root.containsKey("mqtt_hass_individualpanels"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -165,8 +137,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "MqTT Server must between 1 and " STR(MQTT_MAX_HOSTNAME_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::MqttHostnameLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_HOSTNAME_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -174,48 +145,42 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Username must not be longer than " STR(MQTT_MAX_USERNAME_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttUsernameLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_USERNAME_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (root["mqtt_password"].as<String>().length() > MQTT_MAX_PASSWORD_STRLEN) {
|
||||
retMsg["message"] = "Password must not be longer than " STR(MQTT_MAX_PASSWORD_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttPasswordLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_PASSWORD_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (root["mqtt_topic"].as<String>().length() > MQTT_MAX_TOPIC_STRLEN) {
|
||||
retMsg["message"] = "Topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttTopicLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["mqtt_topic"].as<String>().indexOf(' ') != -1) {
|
||||
retMsg["message"] = "Topic must not contain space characters!";
|
||||
retMsg["code"] = WebApiError::MqttTopicCharacter;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!root["mqtt_topic"].as<String>().endsWith("/")) {
|
||||
retMsg["message"] = "Topic must end with a slash (/)!";
|
||||
retMsg["code"] = WebApiError::MqttTopicTrailingSlash;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["mqtt_port"].as<uint>() == 0 || root["mqtt_port"].as<uint>() > 65535) {
|
||||
retMsg["message"] = "Port must be a number between 1 and 65535!";
|
||||
retMsg["code"] = WebApiError::MqttPort;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -225,8 +190,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Certificates must not be longer than " STR(MQTT_MAX_CERT_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttCertificateLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_CERT_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -234,16 +198,14 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "LWT topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttLwtTopicLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["mqtt_lwt_topic"].as<String>().indexOf(' ') != -1) {
|
||||
retMsg["message"] = "LWT topic must not contain space characters!";
|
||||
retMsg["code"] = WebApiError::MqttLwtTopicCharacter;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -251,8 +213,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "LWT online value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttLwtOnlineLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -260,8 +221,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "LWT offline value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttLwtOfflineLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -269,8 +229,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "LWT QoS must not be greater than " STR(2) "!";
|
||||
retMsg["code"] = WebApiError::MqttLwtQos;
|
||||
retMsg["param"]["max"] = 2;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -279,8 +238,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::MqttPublishInterval;
|
||||
retMsg["param"]["min"] = 5;
|
||||
retMsg["param"]["max"] = 65535;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -289,16 +247,14 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Hass topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttHassTopicLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["mqtt_hass_topic"].as<String>().indexOf(' ') != -1) {
|
||||
retMsg["message"] = "Hass topic must not contain space characters!";
|
||||
retMsg["code"] = WebApiError::MqttHassTopicCharacter;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -331,8 +287,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
MqttSettings.performReconnect();
|
||||
MqttHandleHass.forceUpdate();
|
||||
|
||||
@ -46,8 +46,7 @@ void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
|
||||
root["ap_mac"] = WiFi.softAPmacAddress();
|
||||
root["ap_stationnum"] = WiFi.softAPgetStationNum();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
|
||||
@ -72,8 +71,7 @@ void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
|
||||
root["aptimeout"] = config.WiFi.ApTimeout;
|
||||
root["mdnsenabled"] = config.Mdns.Enabled;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
@ -83,37 +81,12 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("ssid")
|
||||
&& root.containsKey("password")
|
||||
@ -127,8 +100,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
&& root.containsKey("aptimeout"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -136,68 +108,59 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
if (!ipaddress.fromString(root["ipaddress"].as<String>())) {
|
||||
retMsg["message"] = "IP address is invalid!";
|
||||
retMsg["code"] = WebApiError::NetworkIpInvalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
IPAddress netmask;
|
||||
if (!netmask.fromString(root["netmask"].as<String>())) {
|
||||
retMsg["message"] = "Netmask is invalid!";
|
||||
retMsg["code"] = WebApiError::NetworkNetmaskInvalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
IPAddress gateway;
|
||||
if (!gateway.fromString(root["gateway"].as<String>())) {
|
||||
retMsg["message"] = "Gateway is invalid!";
|
||||
retMsg["code"] = WebApiError::NetworkGatewayInvalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
IPAddress dns1;
|
||||
if (!dns1.fromString(root["dns1"].as<String>())) {
|
||||
retMsg["message"] = "DNS Server IP 1 is invalid!";
|
||||
retMsg["code"] = WebApiError::NetworkDns1Invalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
IPAddress dns2;
|
||||
if (!dns2.fromString(root["dns2"].as<String>())) {
|
||||
retMsg["message"] = "DNS Server IP 2 is invalid!";
|
||||
retMsg["code"] = WebApiError::NetworkDns2Invalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root["hostname"].as<String>().length() == 0 || root["hostname"].as<String>().length() > WIFI_MAX_HOSTNAME_STRLEN) {
|
||||
retMsg["message"] = "Hostname must between 1 and " STR(WIFI_MAX_HOSTNAME_STRLEN) " characters long!";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (NetworkSettings.NetworkMode() == network_mode::WiFi) {
|
||||
if (root["ssid"].as<String>().length() == 0 || root["ssid"].as<String>().length() > WIFI_MAX_SSID_STRLEN) {
|
||||
retMsg["message"] = "SSID must between 1 and " STR(WIFI_MAX_SSID_STRLEN) " characters long!";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (root["password"].as<String>().length() > WIFI_MAX_PASSWORD_STRLEN - 1) {
|
||||
retMsg["message"] = "Password must not be longer than " STR(WIFI_MAX_PASSWORD_STRLEN) " characters long!";
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (root["aptimeout"].as<uint>() > 99999) {
|
||||
retMsg["message"] = "ApTimeout must be a number between 0 and 99999!";
|
||||
retMsg["code"] = WebApiError::NetworkApTimeoutInvalid;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -235,8 +198,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
NetworkSettings.enableAdminMode();
|
||||
NetworkSettings.applyConfig();
|
||||
|
||||
@ -63,8 +63,7 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
|
||||
root["sun_isSunsetAvailable"] = SunPosition.isSunsetAvailable();
|
||||
root["sun_isDayPeriod"] = SunPosition.isDayPeriod();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
|
||||
@ -84,8 +83,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
|
||||
root["latitude"] = config.Ntp.Latitude;
|
||||
root["sunsettype"] = config.Ntp.SunsetType;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
@ -95,37 +93,12 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("ntp_server")
|
||||
&& root.containsKey("ntp_timezone")
|
||||
@ -134,8 +107,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
&& root.containsKey("sunsettype"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,8 +115,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "NTP Server must between 1 and " STR(NTP_MAX_SERVER_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::NtpServerLength;
|
||||
retMsg["param"]["max"] = NTP_MAX_SERVER_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,8 +123,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Timezone must between 1 and " STR(NTP_MAX_TIMEZONE_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::NtpTimezoneLength;
|
||||
retMsg["param"]["max"] = NTP_MAX_TIMEZONE_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -161,8 +131,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Timezone description must between 1 and " STR(NTP_MAX_TIMEZONEDESCR_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::NtpTimezoneDescriptionLength;
|
||||
retMsg["param"]["max"] = NTP_MAX_TIMEZONEDESCR_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -176,8 +145,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
NtpSettings.setServer();
|
||||
NtpSettings.setTimezone();
|
||||
@ -208,8 +176,7 @@ void WebApiNtpClass::onNtpTimeGet(AsyncWebServerRequest* request)
|
||||
root["minute"] = timeinfo.tm_min;
|
||||
root["second"] = timeinfo.tm_sec;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
@ -219,37 +186,12 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("year")
|
||||
&& root.containsKey("month")
|
||||
@ -259,8 +201,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
&& root.containsKey("second"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -269,8 +210,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpYearInvalid;
|
||||
retMsg["param"]["min"] = 2022;
|
||||
retMsg["param"]["max"] = 2100;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -279,8 +219,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpMonthInvalid;
|
||||
retMsg["param"]["min"] = 1;
|
||||
retMsg["param"]["max"] = 12;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -289,8 +228,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpDayInvalid;
|
||||
retMsg["param"]["min"] = 1;
|
||||
retMsg["param"]["max"] = 31;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -299,8 +237,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpHourInvalid;
|
||||
retMsg["param"]["min"] = 0;
|
||||
retMsg["param"]["max"] = 23;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -309,8 +246,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpMinuteInvalid;
|
||||
retMsg["param"]["min"] = 0;
|
||||
retMsg["param"]["max"] = 59;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -319,8 +255,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["code"] = WebApiError::NtpSecondInvalid;
|
||||
retMsg["param"]["min"] = 0;
|
||||
retMsg["param"]["max"] = 59;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -341,6 +276,5 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Time updated!";
|
||||
retMsg["code"] = WebApiError::NtpTimeUpdated;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -40,8 +40,7 @@ void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
|
||||
root[inv->serialString()]["power_set_status"] = limitStatus;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
||||
@ -51,45 +50,19 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("serial")
|
||||
&& (root.containsKey("power")
|
||||
|| root.containsKey("restart")))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -99,8 +72,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
||||
if (serial == 0) {
|
||||
retMsg["message"] = "Serial must be a number > 0!";
|
||||
retMsg["code"] = WebApiError::PowerSerialZero;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -108,8 +80,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
||||
if (inv == nullptr) {
|
||||
retMsg["message"] = "Invalid inverter specified!";
|
||||
retMsg["code"] = WebApiError::PowerInvalidInverter;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,6 +97,5 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Settings saved!";
|
||||
retMsg["code"] = WebApiError::GenericSuccess;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -31,8 +31,7 @@ void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
|
||||
root["password"] = config.Security.Password;
|
||||
root["allow_readonly"] = config.Security.AllowReadonly;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
||||
@ -42,44 +41,18 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
JsonDocument root;
|
||||
if (!WebApi.parseRequestData(request, response, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
const 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("password")
|
||||
&& root.containsKey("allow_readonly")) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -87,8 +60,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Password must between 8 and " STR(WIFI_MAX_PASSWORD_STRLEN) " characters long!";
|
||||
retMsg["code"] = WebApiError::SecurityPasswordLength;
|
||||
retMsg["param"]["max"] = WIFI_MAX_PASSWORD_STRLEN;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,8 +70,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
||||
|
||||
WebApi.writeConfig(retMsg);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
|
||||
@ -114,6 +85,5 @@ void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
|
||||
retMsg["message"] = "Authentication successful!";
|
||||
retMsg["code"] = WebApiError::SecurityAuthSuccess;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -76,6 +76,5 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||
root["cmt_configured"] = PinMapping.isValidCmt2300Config();
|
||||
root["cmt_connected"] = Hoymiles.getRadioCmt()->isConnected();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
@ -73,19 +73,20 @@ void WebApiWsLiveClass::sendDataTaskCb()
|
||||
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
DynamicJsonDocument root(4096);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
continue;
|
||||
}
|
||||
JsonDocument root;
|
||||
JsonVariant var = root;
|
||||
|
||||
auto invArray = var.createNestedArray("inverters");
|
||||
auto invObject = invArray.createNestedObject();
|
||||
auto invArray = var["inverters"].to<JsonArray>();
|
||||
auto invObject = invArray.add<JsonObject>();
|
||||
|
||||
generateCommonJsonResponse(var);
|
||||
generateInverterCommonJsonResponse(invObject, inv);
|
||||
generateInverterChannelJsonResponse(invObject, inv);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
|
||||
@ -101,12 +102,12 @@ void WebApiWsLiveClass::sendDataTaskCb()
|
||||
|
||||
void WebApiWsLiveClass::generateCommonJsonResponse(JsonVariant& root)
|
||||
{
|
||||
JsonObject totalObj = root.createNestedObject("total");
|
||||
auto totalObj = root["total"].to<JsonObject>();
|
||||
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");
|
||||
JsonObject hintObj = root["hints"].to<JsonObject>();
|
||||
struct tm timeinfo;
|
||||
hintObj["time_sync"] = !getLocalTime(&timeinfo, 5);
|
||||
hintObj["radio_problem"] = (Hoymiles.getRadioNrf()->isInitialized() && (!Hoymiles.getRadioNrf()->isConnected() || !Hoymiles.getRadioNrf()->isPVariant())) || (Hoymiles.getRadioCmt()->isInitialized() && (!Hoymiles.getRadioCmt()->isConnected()));
|
||||
@ -144,7 +145,7 @@ void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, st
|
||||
|
||||
// Loop all channels
|
||||
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
||||
JsonObject chanTypeObj = root.createNestedObject(inv->Statistics()->getChannelTypeName(t));
|
||||
auto chanTypeObj = root[inv->Statistics()->getChannelTypeName(t)].to<JsonObject>();
|
||||
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
|
||||
if (t == TYPE_DC) {
|
||||
chanTypeObj[String(static_cast<uint8_t>(c))]["name"]["u"] = inv_cfg->channel[c].Name;
|
||||
@ -221,21 +222,15 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
|
||||
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||
auto& root = response->getRoot();
|
||||
|
||||
JsonArray invArray = root.createNestedArray("inverters");
|
||||
|
||||
uint64_t serial = 0;
|
||||
if (request->hasParam("inv")) {
|
||||
String s = request->getParam("inv")->value();
|
||||
serial = strtoll(s.c_str(), NULL, 16);
|
||||
}
|
||||
auto invArray = root["inverters"].to<JsonArray>();
|
||||
auto serial = WebApi.parseSerialFromRequest(request);
|
||||
|
||||
if (serial > 0) {
|
||||
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||
if (inv != nullptr) {
|
||||
JsonObject invObject = invArray.createNestedObject();
|
||||
JsonObject invObject = invArray.add<JsonObject>();
|
||||
generateInverterCommonJsonResponse(invObject, inv);
|
||||
generateInverterChannelJsonResponse(invObject, inv);
|
||||
}
|
||||
@ -247,15 +242,14 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject invObject = invArray.createNestedObject();
|
||||
JsonObject invObject = invArray.add<JsonObject>();
|
||||
generateInverterCommonJsonResponse(invObject, inv);
|
||||
}
|
||||
}
|
||||
|
||||
generateCommonJsonResponse(root);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
|
||||
} catch (const std::bad_alloc& bad_alloc) {
|
||||
MessageOutput.printf("Call to /api/livedata/status temporarely out of resources. Reason: \"%s\".\r\n", bad_alloc.what());
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
36
webapp/eslint.config.js
Normal file
36
webapp/eslint.config.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* eslint-env node */
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import js from "@eslint/js";
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...pluginVue.configs['flat/essential'],
|
||||
...compat.extends("@vue/eslint-config-typescript/recommended"),
|
||||
{
|
||||
files: [
|
||||
"**/*.vue",
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.cts",
|
||||
"**/*.mts",
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
},
|
||||
}
|
||||
]
|
||||
@ -9,7 +9,7 @@
|
||||
"preview": "vite preview --port 4173",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
@ -18,32 +18,31 @@
|
||||
"mitt": "^3.0.1",
|
||||
"sortablejs": "^1.15.2",
|
||||
"spark-md5": "^3.0.2",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.10.2",
|
||||
"vue-router": "^4.3.0"
|
||||
"vue": "^3.4.25",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^3.0.1",
|
||||
"@rushstack/eslint-patch": "^1.7.2",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@tsconfig/node18": "^18.2.4",
|
||||
"@types/bootstrap": "^5.2.10",
|
||||
"@types/node": "^20.11.28",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/pulltorefreshjs": "^0.1.7",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@types/spark-md5": "^3.0.4",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"eslint": "^9.1.1",
|
||||
"eslint-plugin-vue": "^9.25.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pulltorefreshjs": "^0.1.22",
|
||||
"sass": "^1.72.0",
|
||||
"terser": "^5.29.2",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^5.1.6",
|
||||
"sass": "^1.75.0",
|
||||
"terser": "^5.30.4",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.10",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-css-injected-by-js": "^3.4.0",
|
||||
"vue-tsc": "^2.0.6"
|
||||
"vite-plugin-css-injected-by-js": "^3.5.0",
|
||||
"vue-tsc": "^2.0.14"
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@
|
||||
"America/Santiago":"<-04>4<-03>,M9.1.6/24,M4.1.6/24",
|
||||
"America/Santo_Domingo":"AST4",
|
||||
"America/Sao_Paulo":"<-03>3",
|
||||
"America/Scoresbysund":"<-01>1<+00>,M3.5.0/0,M10.5.0/1",
|
||||
"America/Scoresbysund":"<-02>2<-01>,M3.5.0/-1,M10.5.0/0",
|
||||
"America/Sitka":"AKST9AKDT,M3.2.0,M11.1.0",
|
||||
"America/St_Barthelemy":"AST4",
|
||||
"America/St_Johns":"NST3:30NDT,M3.2.0,M11.1.0",
|
||||
@ -200,7 +200,7 @@
|
||||
"America/Winnipeg":"CST6CDT,M3.2.0,M11.1.0",
|
||||
"America/Yakutat":"AKST9AKDT,M3.2.0,M11.1.0",
|
||||
"America/Yellowknife":"MST7MDT,M3.2.0,M11.1.0",
|
||||
"Antarctica/Casey":"<+11>-11",
|
||||
"Antarctica/Casey":"<+08>-8",
|
||||
"Antarctica/Davis":"<+07>-7",
|
||||
"Antarctica/DumontDUrville":"<+10>-10",
|
||||
"Antarctica/Macquarie":"AEST-10AEDT,M10.1.0,M4.1.0/3",
|
||||
@ -210,10 +210,10 @@
|
||||
"Antarctica/Rothera":"<-03>3",
|
||||
"Antarctica/Syowa":"<+03>-3",
|
||||
"Antarctica/Troll":"<+00>0<+02>-2,M3.5.0/1,M10.5.0/3",
|
||||
"Antarctica/Vostok":"<+06>-6",
|
||||
"Antarctica/Vostok":"<+05>-5",
|
||||
"Arctic/Longyearbyen":"CET-1CEST,M3.5.0,M10.5.0/3",
|
||||
"Asia/Aden":"<+03>-3",
|
||||
"Asia/Almaty":"<+06>-6",
|
||||
"Asia/Almaty":"<+05>-5",
|
||||
"Asia/Amman":"<+03>-3",
|
||||
"Asia/Anadyr":"<+12>-12",
|
||||
"Asia/Aqtau":"<+05>-5",
|
||||
|
||||
@ -48,15 +48,14 @@ export default defineComponent({
|
||||
showReload: { type: Boolean, required: false, default: false },
|
||||
},
|
||||
mounted() {
|
||||
var self = this;
|
||||
console.log("init");
|
||||
PullToRefresh.init({
|
||||
mainElement: 'body', // above which element?
|
||||
instructionsPullToRefresh: this.$t('base.Pull'),
|
||||
instructionsReleaseToRefresh: this.$t('base.Release'),
|
||||
instructionsRefreshing: this.$t('base.Refreshing'),
|
||||
onRefresh: function() {
|
||||
self.$emit('reload');
|
||||
onRefresh: () => {
|
||||
this.$emit('reload');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -52,7 +52,7 @@ export default defineComponent({
|
||||
_countDownTimeout = undefined;
|
||||
};
|
||||
|
||||
var countDown = ref();
|
||||
const countDown = ref();
|
||||
watch(() => props.modelValue, () => {
|
||||
countDown.value = parseCountDown(props.modelValue);
|
||||
});
|
||||
@ -116,4 +116,4 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -83,10 +83,10 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
modelAllowVersionInfo: {
|
||||
get(): any {
|
||||
get(): boolean {
|
||||
return !!this.allowVersionInfo;
|
||||
},
|
||||
set(value: any) {
|
||||
set(value: boolean) {
|
||||
this.$emit('update:allowVersionInfo', value);
|
||||
},
|
||||
},
|
||||
|
||||
@ -83,10 +83,12 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
model: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
get(): any {
|
||||
if (this.type === 'checkbox') return !!this.modelValue;
|
||||
return this.modelValue;
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
set(value: any) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
@ -112,4 +114,4 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -28,9 +28,11 @@ export default defineComponent({
|
||||
},
|
||||
computed: {
|
||||
model: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
get(): any {
|
||||
return this.modelValue;
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
set(value: any) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
|
||||
@ -146,8 +146,8 @@ export default defineComponent({
|
||||
},
|
||||
isEaster() {
|
||||
const easter = this.getEasterSunday(this.now.getFullYear());
|
||||
var easterStart = new Date(easter);
|
||||
var easterEnd = new Date(easter);
|
||||
const easterStart = new Date(easter);
|
||||
const easterEnd = new Date(easter);
|
||||
easterStart.setDate(easterStart.getDate() - 2);
|
||||
easterEnd.setDate(easterEnd.getDate() + 1);
|
||||
return this.now >= easterStart && this.now < easterEnd;
|
||||
@ -170,18 +170,18 @@ export default defineComponent({
|
||||
this.$refs.navbarCollapse && (this.$refs.navbarCollapse as HTMLElement).classList.remove("show");
|
||||
},
|
||||
getEasterSunday(year: number): Date {
|
||||
var f = Math.floor;
|
||||
var G = year % 19;
|
||||
var C = f(year / 100);
|
||||
var H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30;
|
||||
var I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11));
|
||||
var J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7;
|
||||
var L = I - J;
|
||||
var month = 3 + f((L + 40) / 44);
|
||||
var day = L + 28 - 31 * f(month / 4);
|
||||
const f = Math.floor;
|
||||
const G = year % 19;
|
||||
const C = f(year / 100);
|
||||
const H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30;
|
||||
const I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11));
|
||||
const J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7;
|
||||
const L = I - J;
|
||||
const month = 3 + f((L + 40) / 44);
|
||||
const day = L + 28 - 31 * f(month / 4);
|
||||
|
||||
return new Date(year, month - 1, day);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -84,9 +84,11 @@ export default defineComponent({
|
||||
let comCur = 999999;
|
||||
|
||||
if (this.selectedPinAssignment && category in this.selectedPinAssignment) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
comSel = (this.selectedPinAssignment as any)[category][prop];
|
||||
}
|
||||
if (this.currentPinAssignment && category in this.currentPinAssignment) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
comCur = (this.currentPinAssignment as any)[category][prop];
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
"Release": "Loslassen zum Aktualisieren",
|
||||
"Close": "Schließen"
|
||||
},
|
||||
"Error": {
|
||||
"Oops": "Oops!"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Dunkel",
|
||||
"Light": "Hell",
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
"Release": "Release to refresh",
|
||||
"Close": "Close"
|
||||
},
|
||||
"Error": {
|
||||
"Oops": "Oops!"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Dark",
|
||||
"Light": "Light",
|
||||
|
||||
@ -32,6 +32,9 @@
|
||||
"Release": "Release to refresh",
|
||||
"Close": "Fermer"
|
||||
},
|
||||
"Error": {
|
||||
"Oops": "Oops!"
|
||||
},
|
||||
"localeswitcher": {
|
||||
"Dark": "Sombre",
|
||||
"Light": "Clair",
|
||||
|
||||
@ -3,6 +3,7 @@ import ConfigAdminView from '@/views/ConfigAdminView.vue';
|
||||
import ConsoleInfoView from '@/views/ConsoleInfoView.vue';
|
||||
import DeviceAdminView from '@/views/DeviceAdminView.vue'
|
||||
import DtuAdminView from '@/views/DtuAdminView.vue';
|
||||
import ErrorView from '@/views/ErrorView.vue';
|
||||
import FirmwareUpgradeView from '@/views/FirmwareUpgradeView.vue';
|
||||
import HomeView from '@/views/HomeView.vue';
|
||||
import InverterAdminView from '@/views/InverterAdminView.vue';
|
||||
@ -32,6 +33,11 @@ const router = createRouter({
|
||||
name: 'Login',
|
||||
component: LoginView
|
||||
},
|
||||
{
|
||||
path: '/error?status=:status&message=:message',
|
||||
name: 'Error',
|
||||
component: ErrorView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
@ -115,4 +121,4 @@ const router = createRouter({
|
||||
]
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
||||
@ -41,7 +41,7 @@ export function isLoggedIn(): boolean {
|
||||
return (localStorage.getItem('user') != null);
|
||||
}
|
||||
|
||||
export function login(username: String, password: String) {
|
||||
export function login(username: string, password: string) {
|
||||
const requestOptions = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@ -65,7 +65,7 @@ export function login(username: String, password: String) {
|
||||
});
|
||||
}
|
||||
|
||||
export function handleResponse(response: Response, emitter: Emitter<Record<EventType, unknown>>, router: Router) {
|
||||
export function handleResponse(response: Response, emitter: Emitter<Record<EventType, unknown>>, router: Router, ignore_error: boolean = false) {
|
||||
return response.text().then(text => {
|
||||
const data = text && JSON.parse(text);
|
||||
if (!response.ok) {
|
||||
@ -74,9 +74,13 @@ export function handleResponse(response: Response, emitter: Emitter<Record<Event
|
||||
logout();
|
||||
emitter.emit("logged-out");
|
||||
router.push({ path: "/login", query: { returnUrl: router.currentRoute.value.fullPath } });
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
const error = { message: (data && data.message) || response.statusText, status: response.status || 0 };
|
||||
if (!ignore_error) {
|
||||
router.push({ name: "Error", params: error });
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@ -99,4 +103,4 @@ function handleAuthResponse(response: Response) {
|
||||
|
||||
return data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,8 +188,8 @@ export default defineComponent({
|
||||
fetch("/api/config/get?file=" + this.backupFileSelect, { headers: authHeader() })
|
||||
.then(res => res.blob())
|
||||
.then(blob => {
|
||||
var file = window.URL.createObjectURL(blob);
|
||||
var a = document.createElement('a');
|
||||
const file = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = file;
|
||||
a.download = this.backupFileSelect;
|
||||
document.body.appendChild(a);
|
||||
|
||||
@ -1,154 +1,154 @@
|
||||
<template>
|
||||
<BasePage :title="$t('console.Console')" :isLoading="dataLoading">
|
||||
<CardElement :text="$t('console.VirtualDebugConsole')" textVariant="text-bg-primary">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="autoScroll"
|
||||
v-model="isAutoScroll">
|
||||
<label class="form-check-label" for="autoScroll">
|
||||
{{ $t('console.EnableAutoScroll') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-primary" :onClick="clearConsole">
|
||||
{{ $t('console.ClearConsole') }}</button>
|
||||
<button type="button" class="btn btn-secondary" :onClick="copyConsole">
|
||||
{{ $t('console.CopyToClipboard') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="console" class="form-control" rows="24" v-model="consoleBuffer" readonly></textarea>
|
||||
</CardElement>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BasePage from '@/components/BasePage.vue';
|
||||
import CardElement from '@/components/CardElement.vue';
|
||||
import { authUrl } from '@/utils/authentication';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BasePage,
|
||||
CardElement,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
socket: {} as WebSocket,
|
||||
heartInterval: 0,
|
||||
dataLoading: true,
|
||||
consoleBuffer: "",
|
||||
isAutoScroll: true,
|
||||
endWithNewline: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initSocket();
|
||||
this.dataLoading = false;
|
||||
},
|
||||
unmounted() {
|
||||
this.closeSocket();
|
||||
},
|
||||
watch: {
|
||||
consoleBuffer() {
|
||||
if (this.isAutoScroll) {
|
||||
let textarea = this.$el.querySelector("#console");
|
||||
setTimeout(() => {
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initSocket() {
|
||||
console.log("Starting connection to WebSocket Server");
|
||||
|
||||
const { protocol, host } = location;
|
||||
const authString = authUrl();
|
||||
const webSocketUrl = `${protocol === "https:" ? "wss" : "ws"
|
||||
}://${authString}${host}/console`;
|
||||
|
||||
this.closeSocket();
|
||||
this.socket = new WebSocket(webSocketUrl);
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
console.log(event);
|
||||
|
||||
let outstr = new String(event.data);
|
||||
let removedNewline = false;
|
||||
if (outstr.endsWith('\n')) {
|
||||
outstr = outstr.substring(0, outstr.length - 1);
|
||||
removedNewline = true;
|
||||
}
|
||||
this.consoleBuffer += (this.endWithNewline ? this.getOutDate() : '') + outstr.replaceAll("\n", "\n" + this.getOutDate());
|
||||
this.endWithNewline = removedNewline;
|
||||
this.heartCheck(); // Reset heartbeat detection
|
||||
};
|
||||
|
||||
this.socket.onopen = function (event) {
|
||||
console.log(event);
|
||||
console.log("Successfully connected to the echo websocket server...");
|
||||
};
|
||||
|
||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||
window.onbeforeunload = () => {
|
||||
this.closeSocket();
|
||||
};
|
||||
},
|
||||
// Send heartbeat packets regularly * 59s Send a heartbeat
|
||||
heartCheck() {
|
||||
this.heartInterval && clearTimeout(this.heartInterval);
|
||||
this.heartInterval = setInterval(() => {
|
||||
if (this.socket.readyState === 1) {
|
||||
// Connection status
|
||||
this.socket.send("ping");
|
||||
} else {
|
||||
this.initSocket(); // Breakpoint reconnection 5 Time
|
||||
}
|
||||
}, 5 * 1000);
|
||||
},
|
||||
/** To break off websocket Connect */
|
||||
closeSocket() {
|
||||
try {
|
||||
this.socket.close();
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
|
||||
this.heartInterval && clearTimeout(this.heartInterval);
|
||||
},
|
||||
getOutDate(): String {
|
||||
const u = new Date();
|
||||
return ('0' + u.getHours()).slice(-2) + ':' +
|
||||
('0' + u.getMinutes()).slice(-2) + ':' +
|
||||
('0' + u.getSeconds()).slice(-2) + '.' +
|
||||
(u.getMilliseconds() / 1000).toFixed(3).slice(2, 5) + ' > ';
|
||||
},
|
||||
clearConsole() {
|
||||
this.consoleBuffer = "";
|
||||
},
|
||||
copyConsole() {
|
||||
var input = document.createElement('textarea');
|
||||
input.innerHTML = this.consoleBuffer;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#console {
|
||||
background-color: #0C0C0C;
|
||||
color: #CCCCCC;
|
||||
padding: 8px;
|
||||
font-family: courier new;
|
||||
font-size: .875em;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<BasePage :title="$t('console.Console')" :isLoading="dataLoading">
|
||||
<CardElement :text="$t('console.VirtualDebugConsole')" textVariant="text-bg-primary">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="autoScroll"
|
||||
v-model="isAutoScroll">
|
||||
<label class="form-check-label" for="autoScroll">
|
||||
{{ $t('console.EnableAutoScroll') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-primary" :onClick="clearConsole">
|
||||
{{ $t('console.ClearConsole') }}</button>
|
||||
<button type="button" class="btn btn-secondary" :onClick="copyConsole">
|
||||
{{ $t('console.CopyToClipboard') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="console" class="form-control" rows="24" v-model="consoleBuffer" readonly></textarea>
|
||||
</CardElement>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BasePage from '@/components/BasePage.vue';
|
||||
import CardElement from '@/components/CardElement.vue';
|
||||
import { authUrl } from '@/utils/authentication';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BasePage,
|
||||
CardElement,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
socket: {} as WebSocket,
|
||||
heartInterval: 0,
|
||||
dataLoading: true,
|
||||
consoleBuffer: "",
|
||||
isAutoScroll: true,
|
||||
endWithNewline: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initSocket();
|
||||
this.dataLoading = false;
|
||||
},
|
||||
unmounted() {
|
||||
this.closeSocket();
|
||||
},
|
||||
watch: {
|
||||
consoleBuffer() {
|
||||
if (this.isAutoScroll) {
|
||||
const textarea = this.$el.querySelector("#console");
|
||||
setTimeout(() => {
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initSocket() {
|
||||
console.log("Starting connection to WebSocket Server");
|
||||
|
||||
const { protocol, host } = location;
|
||||
const authString = authUrl();
|
||||
const webSocketUrl = `${protocol === "https:" ? "wss" : "ws"
|
||||
}://${authString}${host}/console`;
|
||||
|
||||
this.closeSocket();
|
||||
this.socket = new WebSocket(webSocketUrl);
|
||||
|
||||
this.socket.onmessage = (event) => {
|
||||
console.log(event);
|
||||
|
||||
let outstr = new String(event.data);
|
||||
let removedNewline = false;
|
||||
if (outstr.endsWith('\n')) {
|
||||
outstr = outstr.substring(0, outstr.length - 1);
|
||||
removedNewline = true;
|
||||
}
|
||||
this.consoleBuffer += (this.endWithNewline ? this.getOutDate() : '') + outstr.replaceAll("\n", "\n" + this.getOutDate());
|
||||
this.endWithNewline = removedNewline;
|
||||
this.heartCheck(); // Reset heartbeat detection
|
||||
};
|
||||
|
||||
this.socket.onopen = function (event) {
|
||||
console.log(event);
|
||||
console.log("Successfully connected to the echo websocket server...");
|
||||
};
|
||||
|
||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||
window.onbeforeunload = () => {
|
||||
this.closeSocket();
|
||||
};
|
||||
},
|
||||
// Send heartbeat packets regularly * 59s Send a heartbeat
|
||||
heartCheck() {
|
||||
this.heartInterval && clearTimeout(this.heartInterval);
|
||||
this.heartInterval = setInterval(() => {
|
||||
if (this.socket.readyState === 1) {
|
||||
// Connection status
|
||||
this.socket.send("ping");
|
||||
} else {
|
||||
this.initSocket(); // Breakpoint reconnection 5 Time
|
||||
}
|
||||
}, 5 * 1000);
|
||||
},
|
||||
/** To break off websocket Connect */
|
||||
closeSocket() {
|
||||
try {
|
||||
this.socket.close();
|
||||
} catch {
|
||||
// continue regardless of error
|
||||
}
|
||||
|
||||
this.heartInterval && clearTimeout(this.heartInterval);
|
||||
},
|
||||
getOutDate(): string {
|
||||
const u = new Date();
|
||||
return ('0' + u.getHours()).slice(-2) + ':' +
|
||||
('0' + u.getMinutes()).slice(-2) + ':' +
|
||||
('0' + u.getSeconds()).slice(-2) + '.' +
|
||||
(u.getMilliseconds() / 1000).toFixed(3).slice(2, 5) + ' > ';
|
||||
},
|
||||
clearConsole() {
|
||||
this.consoleBuffer = "";
|
||||
},
|
||||
copyConsole() {
|
||||
const input = document.createElement('textarea');
|
||||
input.innerHTML = this.consoleBuffer;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#console {
|
||||
background-color: #0C0C0C;
|
||||
color: #CCCCCC;
|
||||
padding: 8px;
|
||||
font-family: courier new;
|
||||
font-size: .875em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -219,7 +219,7 @@ export default defineComponent({
|
||||
getPinMappingList() {
|
||||
this.pinMappingLoading = true;
|
||||
fetch("/api/config/get?file=pin_mapping.json", { headers: authHeader() })
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||
.then((response) => handleResponse(response, this.$emitter, this.$router, true))
|
||||
.then(
|
||||
(data) => {
|
||||
this.pinMappingList = data;
|
||||
@ -246,6 +246,9 @@ export default defineComponent({
|
||||
.then(
|
||||
(data) => {
|
||||
this.deviceConfigList = data;
|
||||
if (this.deviceConfigList.curPin.name === "") {
|
||||
this.deviceConfigList.curPin.name = "Default";
|
||||
}
|
||||
this.dataLoading = false;
|
||||
}
|
||||
)
|
||||
|
||||
18
webapp/src/views/ErrorView.vue
Normal file
18
webapp/src/views/ErrorView.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<BasePage :title="$t('Error.Oops')">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h2>{{ $route.params.message }}</h2>
|
||||
</div>
|
||||
</BasePage>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import BasePage from '@/components/BasePage.vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BasePage,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -191,7 +191,7 @@ export default defineComponent({
|
||||
const remoteHostUrl = "/api/system/status";
|
||||
|
||||
// Use a simple fetch request to check if the remote host is reachable
|
||||
fetch(remoteHostUrl, { method: 'HEAD' })
|
||||
fetch(remoteHostUrl, { method: 'GET' })
|
||||
.then(response => {
|
||||
// Check if the response status is OK (200-299 range)
|
||||
if (response.ok) {
|
||||
|
||||
@ -5,14 +5,20 @@
|
||||
<div class="row gy-3">
|
||||
<div class="col-sm-3 col-md-2" :style="[inverterData.length == 1 ? { 'display': 'none' } : {}]">
|
||||
<div class="nav nav-pills row-cols-sm-1" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
||||
<button v-for="inverter in inverterData" :key="inverter.serial" class="nav-link"
|
||||
<button v-for="inverter in inverterData" :key="inverter.serial" class="nav-link border border-primary text-break"
|
||||
:id="'v-pills-' + inverter.serial + '-tab'" data-bs-toggle="pill"
|
||||
:data-bs-target="'#v-pills-' + inverter.serial" type="button" role="tab"
|
||||
aria-controls="'v-pills-' + inverter.serial" aria-selected="true">
|
||||
<BIconXCircleFill class="fs-4" v-if="!inverter.reachable" />
|
||||
<BIconExclamationCircleFill class="fs-4" v-if="inverter.reachable && !inverter.producing" />
|
||||
<BIconCheckCircleFill class="fs-4" v-if="inverter.reachable && inverter.producing" />
|
||||
{{ inverter.name }}
|
||||
<div class="row">
|
||||
<div class="col-auto col-sm-2">
|
||||
<BIconXCircleFill class="fs-4" v-if="!inverter.reachable" />
|
||||
<BIconExclamationCircleFill class="fs-4" v-if="inverter.reachable && !inverter.producing" />
|
||||
<BIconCheckCircleFill class="fs-4" v-if="inverter.reachable && inverter.producing" />
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
{{ inverter.name }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -470,17 +476,15 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
var self = this;
|
||||
|
||||
this.socket.onopen = function (event) {
|
||||
this.socket.onopen = (event) => {
|
||||
console.log(event);
|
||||
console.log("Successfully connected to the echo websocket server...");
|
||||
self.isWebsocketConnected = true;
|
||||
this.isWebsocketConnected = true;
|
||||
};
|
||||
|
||||
this.socket.onclose = function () {
|
||||
this.socket.onclose = () => {
|
||||
console.log("Connection to websocket closed...")
|
||||
self.isWebsocketConnected = false;
|
||||
this.isWebsocketConnected = false;
|
||||
}
|
||||
|
||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||
|
||||
@ -99,7 +99,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
allowVersionInfo(allow: Boolean) {
|
||||
allowVersionInfo(allow: boolean) {
|
||||
localStorage.setItem("allowVersionInfo", allow ? "1" : "0");
|
||||
if (allow) {
|
||||
this.getUpdateInfo();
|
||||
|
||||
@ -12,6 +12,7 @@ import path from 'path'
|
||||
// example 'vite.user.ts': export const proxy_target = '192.168.16.107'
|
||||
let proxy_target;
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
proxy_target = require('./vite.user.ts').proxy_target;
|
||||
} catch (error) {
|
||||
proxy_target = '192.168.20.110';
|
||||
|
||||
1015
webapp/yarn.lock
1015
webapp/yarn.lock
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user