Merge branch 'master' of https://github.com/tbnobody/OpenDTU into Database

This commit is contained in:
Ralf Bauer 2024-01-07 19:54:57 +01:00
commit 06f4e64f2c
32 changed files with 213 additions and 165 deletions

View File

@ -5,7 +5,7 @@
#include <cstdint>
#define CONFIG_FILENAME "/config.json"
#define CONFIG_VERSION 0x00011900 // 0.1.24 // make sure to clean all after change
#define CONFIG_VERSION 0x00011a00 // 0.1.26 // make sure to clean all after change
#define WIFI_MAX_SSID_STRLEN 32
#define WIFI_MAX_PASSWORD_STRLEN 64

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ArduinoJson.h>
#include <cstdint>
class Utils {
@ -9,4 +10,5 @@ public:
static uint64_t generateDtuSerial();
static int getTimezoneOffset();
static void restartDtu();
static bool checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line);
};

View File

@ -5,6 +5,7 @@
#include "WebApi_device.h"
#include "WebApi_devinfo.h"
#include "WebApi_dtu.h"
#include "WebApi_errors.h"
#include "WebApi_eventlog.h"
#include "WebApi_firmware.h"
#include "WebApi_gridprofile.h"
@ -35,6 +36,8 @@ public:
static void sendTooManyRequests(AsyncWebServerRequest* request);
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
private:
void loop();

View File

@ -8,6 +8,7 @@ enum WebApiError {
GenericDataTooLarge,
GenericParseError,
GenericValueMissing,
GenericWriteFailed,
DtuBase = 2000,
DtuSerialZero,

View File

@ -13,7 +13,7 @@
#define AUTH_USERNAME "admin"
#define SECURITY_ALLOW_READONLY true
#define WIFI_RECONNECT_TIMEOUT 15
#define WIFI_RECONNECT_TIMEOUT 30
#define WIFI_RECONNECT_REDO_TIMEOUT 600
#define WIFI_SSID ""

View File

@ -24,6 +24,7 @@ platform = espressif32@6.5.0
build_flags =
-DPIOENV=\"$PIOENV\"
-D_TASK_STD_FUNCTION=1
-D_TASK_THREAD_SAFE=1
-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

View File

@ -4,9 +4,11 @@
*/
#include "Configuration.h"
#include "MessageOutput.h"
#include "Utils.h"
#include "defaults.h"
#include <ArduinoJson.h>
#include <LittleFS.h>
#include <nvs_flash.h>
CONFIG_T config;
@ -25,6 +27,10 @@ bool ConfigurationClass::write()
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
return false;
}
JsonObject cfg = doc.createNestedObject("cfg");
cfg["version"] = config.Cfg.Version;
cfg["save_count"] = config.Cfg.SaveCount;
@ -150,6 +156,11 @@ bool ConfigurationClass::read()
File f = LittleFS.open(CONFIG_FILENAME, "r", false);
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
return false;
}
// Deserialize the JSON document
const DeserializationError error = deserializeJson(doc, f);
if (error) {
@ -310,6 +321,11 @@ void ConfigurationClass::migrate()
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
return;
}
// Deserialize the JSON document
const DeserializationError error = deserializeJson(doc, f);
if (error) {
@ -339,6 +355,14 @@ void ConfigurationClass::migrate()
config.Dtu.Nrf.PaLevel = dtu["pa_level"];
}
if (config.Cfg.Version < 0x00011a00) {
// This migration fixes this issue: https://github.com/espressif/arduino-esp32/issues/8828
// It occours when migrating from Core 2.0.9 to 2.0.14
// which was done by updating ESP32 PlatformIO from 6.3.2 to 6.5.0
nvs_flash_erase();
nvs_flash_init();
}
f.close();
config.Cfg.Version = CONFIG_VERSION;

View File

@ -135,6 +135,10 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
}
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = name;
root["stat_t"] = stateTopic;
root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName;
@ -179,6 +183,10 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = caption;
root["uniq_id"] = serial + "_" + buttonId;
if (strcmp(icon, "")) {
@ -217,6 +225,10 @@ void MqttHandleHassClass::publishInverterNumber(
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = caption;
root["uniq_id"] = serial + "_" + buttonId;
if (strcmp(icon, "")) {
@ -251,6 +263,10 @@ void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAb
const String statTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = caption;
root["uniq_id"] = serial + "_" + sensorId;
root["stat_t"] = statTopic;
@ -275,6 +291,10 @@ void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_
}
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = name;
root["uniq_id"] = getDtuUniqueId() + "_" + id;
if (strcmp(device_class, "")) {
@ -317,6 +337,10 @@ void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* d
}
DynamicJsonDocument root(1024);
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
}
root["name"] = name;
root["uniq_id"] = getDtuUniqueId() + "_" + id;
root["stat_t"] = MqttSettings.getPrefix() + topic;

View File

@ -268,7 +268,8 @@ void NetworkSettingsClass::applyConfig()
MessageOutput.print("new credentials... ");
WiFi.begin(
Configuration.get().WiFi.Ssid,
Configuration.get().WiFi.Password);
Configuration.get().WiFi.Password,
WIFI_ALL_CHANNEL_SCAN);
} else {
MessageOutput.print("existing credentials... ");
WiFi.begin();

View File

@ -5,6 +5,7 @@
#include "Utils.h"
#include "Display_Graphic.h"
#include "Led_Single.h"
#include "MessageOutput.h"
#include <Esp.h>
uint32_t Utils::getChipId()
@ -65,3 +66,13 @@ void Utils::restartDtu()
yield();
ESP.restart();
}
bool Utils::checkJsonAlloc(const DynamicJsonDocument& doc, const char* function, const uint16_t line)
{
if (doc.capacity() == 0) {
MessageOutput.printf("Alloc failed: %s, %d\r\n", function, line);
return false;
}
return true;
}

View File

@ -103,4 +103,16 @@ void WebApiClass::sendTooManyRequests(AsyncWebServerRequest* request)
request->send(response);
}
void WebApiClass::writeConfig(JsonVariant& retMsg, const WebApiError code, const String& message)
{
if (!Configuration.write()) {
retMsg["message"] = "Write failed!";
retMsg["code"] = WebApiError::GenericWriteFailed;
} else {
retMsg["type"] = "success";
retMsg["message"] = message;
retMsg["code"] = code;
}
}
WebApiClass WebApi;

View File

@ -59,7 +59,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -125,8 +125,8 @@ void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
JsonArray data = root.createNestedArray("configs");
auto& root = response->getRoot();
auto data = root.createNestedArray("configs");
File rootfs = LittleFS.open("/");
File file = rootfs.openNextFile();

View File

@ -33,14 +33,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
const PinMapping_t& pin = PinMapping.get();
JsonObject curPin = root.createNestedObject("curPin");
auto curPin = root.createNestedObject("curPin");
curPin["name"] = config.Dev_PinMapping;
JsonObject nrfPinObj = curPin.createNestedObject("nrf24");
auto nrfPinObj = curPin.createNestedObject("nrf24");
nrfPinObj["clk"] = pin.nrf24_clk;
nrfPinObj["cs"] = pin.nrf24_cs;
nrfPinObj["en"] = pin.nrf24_en;
@ -48,7 +48,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
nrfPinObj["miso"] = pin.nrf24_miso;
nrfPinObj["mosi"] = pin.nrf24_mosi;
JsonObject cmtPinObj = curPin.createNestedObject("cmt");
auto cmtPinObj = curPin.createNestedObject("cmt");
cmtPinObj["clk"] = pin.cmt_clk;
cmtPinObj["cs"] = pin.cmt_cs;
cmtPinObj["fcs"] = pin.cmt_fcs;
@ -56,7 +56,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
cmtPinObj["gpio2"] = pin.cmt_gpio2;
cmtPinObj["gpio3"] = pin.cmt_gpio3;
JsonObject ethPinObj = curPin.createNestedObject("eth");
auto ethPinObj = curPin.createNestedObject("eth");
ethPinObj["enabled"] = pin.eth_enabled;
ethPinObj["phy_addr"] = pin.eth_phy_addr;
ethPinObj["power"] = pin.eth_power;
@ -65,19 +65,19 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
ethPinObj["type"] = pin.eth_type;
ethPinObj["clk_mode"] = pin.eth_clk_mode;
JsonObject displayPinObj = curPin.createNestedObject("display");
auto displayPinObj = curPin.createNestedObject("display");
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;
JsonObject ledPinObj = curPin.createNestedObject("led");
auto ledPinObj = curPin.createNestedObject("led");
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
ledPinObj["led" + String(i)] = pin.led[i];
}
JsonObject display = root.createNestedObject("display");
auto display = root.createNestedObject("display");
display["rotation"] = config.Display.Rotation;
display["power_safe"] = config.Display.PowerSafe;
display["screensaver"] = config.Display.ScreenSaver;
@ -85,9 +85,9 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
display["language"] = config.Display.Language;
display["diagramduration"] = config.Display.DiagramDuration;
JsonArray leds = root.createNestedArray("led");
auto leds = root.createNestedArray("led");
for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
JsonObject led = leds.createNestedObject();
auto led = leds.createNestedObject();
led["brightness"] = config.Led_Single[i].Brightness;
}
@ -102,7 +102,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -175,11 +175,7 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
Display.setLanguage(config.Display.Language);
Display.Diagram().updatePeriod();
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);

View File

@ -28,7 +28,7 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
uint64_t serial = 0;
if (request->hasParam("inv")) {

View File

@ -30,7 +30,7 @@ void WebApiDtuClass::onDtuAdminGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
// DTU Serial is read as HEX
@ -57,7 +57,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -154,11 +154,8 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
config.Dtu.Nrf.PaLevel = root["nrf_palevel"].as<uint8_t>();
config.Dtu.Cmt.PaLevel = root["cmt_palevel"].as<int8_t>();
config.Dtu.Cmt.Frequency = root["cmt_frequency"].as<uint32_t>();
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);

View File

@ -27,7 +27,7 @@ void WebApiEventlogClass::onEventlogStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, 2048);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
uint64_t serial = 0;
if (request->hasParam("inv")) {

View File

@ -28,7 +28,7 @@ void WebApiGridProfileClass::onGridProfileStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
uint64_t serial = 0;
if (request->hasParam("inv")) {
@ -72,7 +72,7 @@ void WebApiGridProfileClass::onGridProfileRawdata(AsyncWebServerRequest* request
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
uint64_t serial = 0;
if (request->hasParam("inv")) {

View File

@ -36,7 +36,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, 768 * INV_MAX_COUNT);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
JsonArray data = root.createNestedArray("inverter");
const CONFIG_T& config = Configuration.get();
@ -94,7 +94,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -167,11 +167,8 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
inverter->Serial = strtoll(root["serial"].as<String>().c_str(), NULL, 16);
strncpy(inverter->Name, root["name"].as<String>().c_str(), INV_MAX_NAME_STRLEN);
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Inverter created!";
retMsg["code"] = WebApiError::InverterAdded;
WebApi.writeConfig(retMsg, WebApiError::InverterAdded, "Inverter created!");
response->setLength();
request->send(response);
@ -194,7 +191,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -294,11 +291,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
arrayCount++;
}
Configuration.write();
retMsg["type"] = "success";
retMsg["code"] = WebApiError::InverterChanged;
retMsg["message"] = "Inverter changed!";
WebApi.writeConfig(retMsg, WebApiError::InverterChanged, "Inverter changed!");
response->setLength();
request->send(response);
@ -340,7 +333,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -395,11 +388,8 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
inverter.Serial = 0;
strncpy(inverter.Name, "", sizeof(inverter.Name));
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Inverter deleted!";
retMsg["code"] = WebApiError::InverterDeleted;
WebApi.writeConfig(retMsg, WebApiError::InverterDeleted, "Inverter deleted!");
response->setLength();
request->send(response);
@ -414,7 +404,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -466,11 +456,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
order++;
}
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Inverter order saved!";
retMsg["code"] = WebApiError::InverterOrdered;
WebApi.writeConfig(retMsg, WebApiError::InverterOrdered, "Inverter order saved!");
response->setLength();
request->send(response);

View File

@ -31,7 +31,7 @@ void WebApiLimitClass::onLimitStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
@ -64,7 +64,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {

View File

@ -29,7 +29,7 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {

View File

@ -33,7 +33,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["mqtt_enabled"] = config.Mqtt.Enabled;
@ -67,7 +67,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["mqtt_enabled"] = config.Mqtt.Enabled;
@ -105,7 +105,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse(false, MQTT_JSON_DOC_SIZE);
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -334,11 +334,8 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
config.Mqtt.Hass.Retain = root["mqtt_hass_retain"].as<bool>();
config.Mqtt.Hass.IndividualPanels = root["mqtt_hass_individualpanels"].as<bool>();
strlcpy(config.Mqtt.Hass.Topic, root["mqtt_hass_topic"].as<String>().c_str(), sizeof(config.Mqtt.Hass.Topic));
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);

View File

@ -32,7 +32,7 @@ void WebApiNetworkClass::onNetworkStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
root["sta_status"] = ((WiFi.getMode() & WIFI_STA) != 0);
root["sta_ssid"] = WiFi.SSID();
@ -63,7 +63,7 @@ void WebApiNetworkClass::onNetworkAdminGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["hostname"] = config.WiFi.Hostname;
@ -89,7 +89,7 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -238,11 +238,8 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
}
config.WiFi.ApTimeout = root["aptimeout"].as<uint>();
config.Mdns.Enabled = root["mdnsenabled"].as<bool>();
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);

View File

@ -35,7 +35,7 @@ void WebApiNtpClass::onNtpStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["ntp_server"] = config.Ntp.Server;
@ -80,7 +80,7 @@ void WebApiNtpClass::onNtpAdminGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["ntp_server"] = config.Ntp.Server;
@ -101,7 +101,7 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -179,11 +179,8 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
config.Ntp.Latitude = root["latitude"].as<double>();
config.Ntp.Longitude = root["longitude"].as<double>();
config.Ntp.SunsetType = root["sunsettype"].as<uint8_t>();
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
@ -201,7 +198,7 @@ void WebApiNtpClass::onNtpTimeGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
struct tm timeinfo;
if (!getLocalTime(&timeinfo, 5)) {
@ -228,7 +225,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {

View File

@ -29,7 +29,7 @@ void WebApiPowerClass::onPowerStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
@ -57,7 +57,7 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {

View File

@ -31,7 +31,7 @@ void WebApiSecurityClass::onSecurityGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root["password"] = config.Security.Password;
@ -48,7 +48,7 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "warning";
if (!request->hasParam("data", true)) {
@ -101,11 +101,8 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
CONFIG_T& config = Configuration.get();
strlcpy(config.Security.Password, root["password"].as<String>().c_str(), sizeof(config.Security.Password));
config.Security.AllowReadonly = root["allow_readonly"].as<bool>();
Configuration.write();
retMsg["type"] = "success";
retMsg["message"] = "Settings saved!";
retMsg["code"] = WebApiError::GenericSuccess;
WebApi.writeConfig(retMsg);
response->setLength();
request->send(response);
@ -118,7 +115,7 @@ void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
auto& retMsg = response->getRoot();
retMsg["type"] = "success";
retMsg["message"] = "Authentication successful!";
retMsg["code"] = WebApiError::SecurityAuthSuccess;

View File

@ -36,7 +36,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
auto& root = response->getRoot();
root["hostname"] = NetworkSettings.getHostname();

View File

@ -6,6 +6,7 @@
#include "Configuration.h"
#include "Datastore.h"
#include "MessageOutput.h"
#include "Utils.h"
#include "WebApi.h"
#include "defaults.h"
#include <AsyncJson.h>
@ -64,11 +65,11 @@ void WebApiWsLiveClass::loop()
try {
std::lock_guard<std::mutex> lock(_mutex);
DynamicJsonDocument root(4096 * INV_MAX_COUNT);
if (Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
JsonVariant var = root;
generateJsonResponse(var);
String buffer;
if (buffer) {
serializeJson(root, buffer);
if (Configuration.get().Security.AllowReadonly) {
@ -221,7 +222,7 @@ void WebApiWsLiveClass::onLivedataStatus(AsyncWebServerRequest* request)
try {
std::lock_guard<std::mutex> lock(_mutex);
AsyncJsonResponse* response = new AsyncJsonResponse(false, 4096 * INV_MAX_COUNT);
JsonVariant root = response->getRoot();
auto& root = response->getRoot();
generateJsonResponse(root);

View File

@ -18,9 +18,7 @@
"mitt": "^3.0.1",
"sortablejs": "^1.15.1",
"spark-md5": "^3.0.2",
"tippy.js": "^6.3.7",
"vue": "^3.4.3",
"vue-google-charts": "^1.1.0",
"vue": "^3.4.5",
"vue-i18n": "^9.8.0",
"vue-router": "^4.2.5",
"vue3-calendar-heatmap": "^2.0.5"
@ -39,7 +37,7 @@
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"npm-run-all": "^4.1.5",
"sass": "^1.69.6",
"sass": "^1.69.7",
"terser": "^5.26.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",

View File

@ -39,6 +39,7 @@
"1003": "Daten zu groß!",
"1004": "Fehler beim interpretieren der Daten!",
"1005": "Benötigte Werte fehlen!",
"1006": "Schreiben fehlgeschlagen!",
"2001": "Die Seriennummer darf nicht 0 sein!",
"2002": "Das Abfraginterval muss größer als 0 sein!",
"2003": "Ungültige Sendeleistung angegeben!",

View File

@ -39,6 +39,7 @@
"1003": "Data too large!",
"1004": "Failed to parse data!",
"1005": "Values are missing!",
"1006": "Write failed!",
"2001": "Serial cannot be zero!",
"2002": "Poll interval must be greater zero!",
"2003": "Invalid power level setting!",

View File

@ -39,6 +39,7 @@
"1003": "Données trop importantes !",
"1004": "Échec de l'analyse des données !",
"1005": "Certaines valeurs sont manquantes !",
"1006": "Write failed!",
"2001": "Le numéro de série ne peut pas être nul !",
"2002": "L'intervalle de sondage doit être supérieur à zéro !",
"2003": "Réglage du niveau de puissance invalide !",

View File

@ -569,13 +569,13 @@
estree-walker "^2.0.2"
source-map-js "^1.0.2"
"@vue/compiler-core@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.3.tgz#8e8f88273f061cf0a49bf958255f5f0621f12d8b"
integrity sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==
"@vue/compiler-core@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.5.tgz#9565aebaadef8649eb7c8e150a5f4f4e2542667d"
integrity sha512-Daka7P1z2AgKjzuueWXhwzIsKu0NkLB6vGbNVEV2iJ8GJTrzraZo/Sk4GWCMRtd/qVi3zwnk+Owbd/xSZbwHtQ==
dependencies:
"@babel/parser" "^7.23.6"
"@vue/shared" "3.4.3"
"@vue/shared" "3.4.5"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.0.2"
@ -588,13 +588,13 @@
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-dom@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.3.tgz#bea8acde9585d5ce92a3f11c062c863fb33e44d7"
integrity sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==
"@vue/compiler-dom@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.5.tgz#c53c9d7715b777b1d6d2adcbc491bfd4f9510edd"
integrity sha512-J8YlxknJVd90SXFJ4HwGANSAXsx5I0lK30sO/zvYV7s5gXf7gZR7r/1BmZ2ju7RGH1lnc6bpBc6nL61yW+PsAQ==
dependencies:
"@vue/compiler-core" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/compiler-core" "3.4.5"
"@vue/shared" "3.4.5"
"@vue/compiler-dom@^3.3.0":
version "3.3.2"
@ -604,16 +604,16 @@
"@vue/compiler-core" "3.3.2"
"@vue/shared" "3.3.2"
"@vue/compiler-sfc@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.3.tgz#a9d35b2deef38576dedd9938851c032fb2ca8617"
integrity sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==
"@vue/compiler-sfc@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.5.tgz#f93f986dfc5c7f72b9a5e00b48be75d9116cc948"
integrity sha512-jauvkDuSSUbP0ebhfNqljhShA90YEfX/0wZ+w40oZF43IjGyWYjqYaJbvMJwGOd+9+vODW6eSvnk28f0SGV7OQ==
dependencies:
"@babel/parser" "^7.23.6"
"@vue/compiler-core" "3.4.3"
"@vue/compiler-dom" "3.4.3"
"@vue/compiler-ssr" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/compiler-core" "3.4.5"
"@vue/compiler-dom" "3.4.5"
"@vue/compiler-ssr" "3.4.5"
"@vue/shared" "3.4.5"
estree-walker "^2.0.2"
magic-string "^0.30.5"
postcss "^8.4.32"
@ -643,13 +643,13 @@
"@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-ssr@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.3.tgz#c3f641a15a04893b5bc3278f3dac65bed44dce1d"
integrity sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==
"@vue/compiler-ssr@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.5.tgz#d412a4c9b10d69172a5ce0ec78de98dad441a58d"
integrity sha512-DDdEcDzj2lWTMfUMMtEpLDhURai9LhM0zSZ219jCt7b2Vyl0/jy3keFgCPMitG0V1S1YG4Cmws3lWHWdxHQOpg==
dependencies:
"@vue/compiler-dom" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/compiler-dom" "3.4.5"
"@vue/shared" "3.4.5"
"@vue/devtools-api@^6.5.0":
version "6.5.1"
@ -691,37 +691,37 @@
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.3.tgz#95287b5950b328df4a942a7cf14a0e13487f1eac"
integrity sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==
"@vue/reactivity@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.5.tgz#68bc91cd356eed95dc5e9e0570e3f7becaee578b"
integrity sha512-BcWkKvjdvqJwb7BhhFkXPLDCecX4d4a6GATvCduJQDLv21PkPowAE5GKuIE5p6RC07/Lp9FMkkq4AYCTVF5KlQ==
dependencies:
"@vue/shared" "3.4.3"
"@vue/shared" "3.4.5"
"@vue/runtime-core@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.3.tgz#fe7649a93d9b20b9b351cd699f69f0e34a26e3ab"
integrity sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==
"@vue/runtime-core@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.5.tgz#2bf253a6f6b0430af1aacf0fdfd8f5782feefce9"
integrity sha512-wh9ELIOQKeWT9SaUPdLrsxRkZv14jp+SJm9aiQGWio+/MWNM3Lib0wE6CoKEqQ9+SCYyGjDBhTOTtO47kCgbkg==
dependencies:
"@vue/reactivity" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/reactivity" "3.4.5"
"@vue/shared" "3.4.5"
"@vue/runtime-dom@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.3.tgz#54a6115cfba364f20cdf5a44c2ff87337a57def8"
integrity sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==
"@vue/runtime-dom@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.5.tgz#b43736d66c32f6038778024587592cb9d68495de"
integrity sha512-n5ewvOjyG3IEpqGBahdPXODFSpVlSz3H4LF76Sx0XAqpIOqyJ5bIb2PrdYuH2ogBMAQPh+o5tnoH4nJpBr8U0Q==
dependencies:
"@vue/runtime-core" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/runtime-core" "3.4.5"
"@vue/shared" "3.4.5"
csstype "^3.1.3"
"@vue/server-renderer@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.3.tgz#c508f58b9f83f0959085d5aa6854eac9141b4bc6"
integrity sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==
"@vue/server-renderer@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.5.tgz#4bfa7aa763217d8b2d4767d2c8d968a9d40352c1"
integrity sha512-jOFc/VE87yvifQpNju12VcqimH8pBLxdcT+t3xMeiED1K6DfH9SORyhFEoZlW5TG2Vwfn3Ul5KE+1aC99xnSBg==
dependencies:
"@vue/compiler-ssr" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/compiler-ssr" "3.4.5"
"@vue/shared" "3.4.5"
"@vue/shared@3.2.47":
version "3.2.47"
@ -733,10 +733,10 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.2.tgz#774cd9b4635ce801b70a3fc3713779a5ef5d77c3"
integrity sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==
"@vue/shared@3.4.3":
version "3.4.3"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.3.tgz#01d54b32b9796c85c853c670d9395a813f23a8c2"
integrity sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==
"@vue/shared@3.4.5":
version "3.4.5"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.5.tgz#c8b4eb6399a7fc986565ea736d938b3a1579256d"
integrity sha512-6XptuzlMvN4l4cDnDw36pdGEV+9njYkQ1ZE0Q6iZLwrKefKaOJyiFmcP3/KBDHbt72cJZGtllAc1GaHe6XGAyg==
"@vue/tsconfig@^0.5.1":
version "0.5.1"
@ -2226,10 +2226,10 @@ safe-regex-test@^1.0.0:
get-intrinsic "^1.1.3"
is-regex "^1.1.4"
sass@^1.69.6:
version "1.69.6"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.6.tgz#88ae1f93facc46d2da9b0bdd652d65068bcfa397"
integrity sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==
sass@^1.69.7:
version "1.69.7"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.7.tgz#6e7e1c8f51e8162faec3e9619babc7da780af3b7"
integrity sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
@ -2666,16 +2666,16 @@ vue3-calendar-heatmap@^2.0.5:
resolved "https://registry.yarnpkg.com/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.5.tgz#775a8d4e9d7bc62f242d7d6f28a899a3fae2bb82"
integrity sha512-qvveNQlTS5Aw7AvRLs0zOyu3uP5iGJlXJAnkrkG2ElDdyQ8H1TJhQ8rL702CROjAg16ezIveUY10nCO7lqZ25w==
vue@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.3.tgz#e1ba36a64134dcedc12cfb2c28e7cd15ba121f04"
integrity sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==
vue@^3.4.5:
version "3.4.5"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.5.tgz#c08b9d903a20faaf4df7270bf2fa7487741b2294"
integrity sha512-VH6nHFhLPjgu2oh5vEBXoNZxsGHuZNr3qf4PHClwJWw6IDqw6B3x+4J+ABdoZ0aJuT8Zi0zf3GpGlLQCrGWHrw==
dependencies:
"@vue/compiler-dom" "3.4.3"
"@vue/compiler-sfc" "3.4.3"
"@vue/runtime-dom" "3.4.3"
"@vue/server-renderer" "3.4.3"
"@vue/shared" "3.4.3"
"@vue/compiler-dom" "3.4.5"
"@vue/compiler-sfc" "3.4.5"
"@vue/runtime-dom" "3.4.5"
"@vue/server-renderer" "3.4.5"
"@vue/shared" "3.4.5"
webpack-sources@^3.2.3:
version "3.2.3"