Update 24.8.5
Update 24.8.5
This commit is contained in:
parent
c43aa757af
commit
eb380eddcf
@ -1,10 +1,10 @@
|
||||
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||
*/
|
||||
#include "Configuration.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "NetworkSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "defaults.h"
|
||||
#include <ArduinoJson.h>
|
||||
@ -26,17 +26,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();
|
||||
@ -48,10 +44,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;
|
||||
@ -59,10 +55,11 @@ 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;
|
||||
mqtt["clientid"] = config.Mqtt.ClientId;
|
||||
mqtt["username"] = config.Mqtt.Username;
|
||||
mqtt["password"] = config.Mqtt.Password;
|
||||
mqtt["topic"] = config.Mqtt.Topic;
|
||||
@ -70,27 +67,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;
|
||||
@ -98,14 +95,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;
|
||||
@ -114,19 +111,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;
|
||||
}
|
||||
|
||||
JsonObject relay = device.createNestedObject("relay");
|
||||
relay["relay_01"] = config.Relay.R01;
|
||||
relay["relay_02"] = config.Relay.R02;
|
||||
|
||||
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;
|
||||
@ -137,17 +130,26 @@ bool ConfigurationClass::write()
|
||||
inv["reachable_threshold"] = config.Inverter[i].ReachableThreshold;
|
||||
inv["zero_runtime"] = config.Inverter[i].ZeroRuntimeDataIfUnrechable;
|
||||
inv["zero_day"] = config.Inverter[i].ZeroYieldDayOnMidnight;
|
||||
inv["clear_eventlog"] = config.Inverter[i].ClearEventlogOnMidnight;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject relay = device["relay"].to<JsonObject>();
|
||||
relay["relay_01"] = config.Relay.R01;
|
||||
relay["relay_02"] = config.Relay.R02;
|
||||
|
||||
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serialize JSON to file
|
||||
if (serializeJson(doc, f) == 0) {
|
||||
MessageOutput.println("Failed to write file");
|
||||
@ -162,11 +164,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);
|
||||
@ -174,6 +172,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;
|
||||
@ -236,6 +238,7 @@ bool ConfigurationClass::read()
|
||||
config.Mqtt.Enabled = mqtt["enabled"] | MQTT_ENABLED;
|
||||
strlcpy(config.Mqtt.Hostname, mqtt["hostname"] | MQTT_HOST, sizeof(config.Mqtt.Hostname));
|
||||
config.Mqtt.Port = mqtt["port"] | MQTT_PORT;
|
||||
strlcpy(config.Mqtt.ClientId, mqtt["clientid"] | NetworkSettings.getApName().c_str(), sizeof(config.Mqtt.ClientId));
|
||||
strlcpy(config.Mqtt.Username, mqtt["username"] | MQTT_USER, sizeof(config.Mqtt.Username));
|
||||
strlcpy(config.Mqtt.Password, mqtt["password"] | MQTT_PASSWORD, sizeof(config.Mqtt.Password));
|
||||
strlcpy(config.Mqtt.Topic, mqtt["topic"] | MQTT_TOPIC, sizeof(config.Mqtt.Topic));
|
||||
@ -293,10 +296,6 @@ bool ConfigurationClass::read()
|
||||
config.Led_Single[i].Brightness = led["brightness"] | LED_BRIGHTNESS;
|
||||
}
|
||||
|
||||
JsonObject relay = device["relay"];
|
||||
config.Relay.R01 = relay["relay_01"];
|
||||
config.Relay.R02 = relay["relay_02"];
|
||||
|
||||
JsonArray inverters = doc["inverters"];
|
||||
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
|
||||
JsonObject inv = inverters[i].as<JsonObject>();
|
||||
@ -311,6 +310,7 @@ bool ConfigurationClass::read()
|
||||
config.Inverter[i].ReachableThreshold = inv["reachable_threshold"] | REACHABLE_THRESHOLD;
|
||||
config.Inverter[i].ZeroRuntimeDataIfUnrechable = inv["zero_runtime"] | false;
|
||||
config.Inverter[i].ZeroYieldDayOnMidnight = inv["zero_day"] | false;
|
||||
config.Inverter[i].ClearEventlogOnMidnight = inv["clear_eventlog"] | false;
|
||||
config.Inverter[i].YieldDayCorrection = inv["yieldday_correction"] | false;
|
||||
|
||||
JsonArray channel = inv["channel"];
|
||||
@ -321,6 +321,10 @@ bool ConfigurationClass::read()
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray relay = device["relay"];
|
||||
config.Relay.R01 = relay["relay_01"];
|
||||
config.Relay.R02 = relay["relay_02"];
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
@ -333,11 +337,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);
|
||||
@ -346,6 +346,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++) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ void InverterSettingsClass::init(Scheduler& scheduler)
|
||||
inv->setReachableThreshold(config.Inverter[i].ReachableThreshold);
|
||||
inv->setZeroValuesIfUnreachable(config.Inverter[i].ZeroRuntimeDataIfUnrechable);
|
||||
inv->setZeroYieldDayOnMidnight(config.Inverter[i].ZeroYieldDayOnMidnight);
|
||||
inv->setClearEventlogOnMidnight(config.Inverter[i].ClearEventlogOnMidnight);
|
||||
inv->Statistics()->setYieldDayCorrection(config.Inverter[i].YieldDayCorrection);
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
inv->Statistics()->setStringMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower);
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "NetworkSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "defaults.h"
|
||||
#include "__compiled_constants.h"
|
||||
|
||||
MqttHandleHassClass MqttHandleHass;
|
||||
|
||||
@ -63,11 +64,10 @@ void MqttHandleHassClass::publishConfig()
|
||||
publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi");
|
||||
publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", "");
|
||||
publishDtuBinarySensor("Status", "connectivity", "diagnostic", config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, config.Mqtt.Lwt.Topic);
|
||||
publishBinarySensor("Relay", "status/get_relay", "1", "0");
|
||||
|
||||
yield();
|
||||
|
||||
publishBinarySensor("Relay", "status/get_relay", "1", "0");
|
||||
|
||||
// Loop all inverters
|
||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||
auto inv = Hoymiles.getInverterByPos(i);
|
||||
@ -76,8 +76,8 @@ void MqttHandleHassClass::publishConfig()
|
||||
publishInverterButton(inv, "Turn Inverter On", "mdi:power-plug", "config", "", "cmd/power", "1");
|
||||
publishInverterButton(inv, "Restart Inverter", "", "config", "restart", "cmd/restart", "1");
|
||||
|
||||
publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100);
|
||||
publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100);
|
||||
publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
||||
publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
||||
|
||||
publishInverterNumber(inv, "Limit NonPersistent Absolute", "mdi:speedometer", "config", "cmd/limit_nonpersistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
||||
publishInverterNumber(inv, "Limit Persistent Absolute", "mdi:speedometer", "config", "cmd/limit_persistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
||||
@ -141,10 +141,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;
|
||||
@ -167,6 +164,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);
|
||||
@ -189,10 +190,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;
|
||||
@ -208,39 +206,10 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
||||
|
||||
createInverterInfo(root, inv);
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::publishBinarySensor(const char* caption, const char* subTopic, const char* payload_on, const char* payload_off)
|
||||
{
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
sensorId.toLowerCase();
|
||||
|
||||
const String configTopic = "binary_sensor/dtu_" + sensorId + "/config";
|
||||
|
||||
const String statTopic = MqttSettings.getPrefix() + "/" + subTopic;
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& pin = PinMapping.get();
|
||||
Relay Cmd_Relay_R01 = Relay(pin.relay_r01);
|
||||
Relay Cmd_Relay_R02 = Relay(pin.relay_r02);
|
||||
|
||||
root["name"] = caption;
|
||||
root["stat_t"] = statTopic;
|
||||
root["pl_on"] = payload_on;
|
||||
root["pl_off"] = payload_off;
|
||||
root["status_R01"] = Cmd_Relay_R01.isStat();
|
||||
root["status_R02"] = Cmd_Relay_R02.isStat();
|
||||
|
||||
createDtuInfo(root);
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -249,7 +218,7 @@ void MqttHandleHassClass::publishBinarySensor(const char* caption, const char* s
|
||||
void MqttHandleHassClass::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, const int16_t max)
|
||||
const int16_t min, const int16_t max, float step)
|
||||
{
|
||||
const String serial = inv->serialString();
|
||||
|
||||
@ -264,10 +233,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;
|
||||
@ -280,9 +246,14 @@ void MqttHandleHassClass::publishInverterNumber(
|
||||
root["unit_of_meas"] = unitOfMeasure;
|
||||
root["min"] = min;
|
||||
root["max"] = max;
|
||||
root["step"] = step;
|
||||
|
||||
createInverterInfo(root, inv);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -302,10 +273,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;
|
||||
@ -315,6 +283,44 @@ 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);
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::publishBinarySensor(const char* caption, const char* subTopic, const char* payload_on, const char* payload_off)
|
||||
{
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
sensorId.toLowerCase();
|
||||
|
||||
const String configTopic = "binary_sensor/dtu_" + sensorId + "/config";
|
||||
|
||||
const String statTopic = MqttSettings.getPrefix() + "/" + subTopic;
|
||||
|
||||
JsonDocument root;
|
||||
|
||||
const auto& pin = PinMapping.get();
|
||||
Relay Cmd_Relay_R01 = Relay(pin.relay_r01);
|
||||
Relay Cmd_Relay_R02 = Relay(pin.relay_r02);
|
||||
|
||||
root["name"] = caption;
|
||||
root["stat_t"] = statTopic;
|
||||
root["pl_on"] = payload_on;
|
||||
root["pl_off"] = payload_off;
|
||||
root["status_R01"] = Cmd_Relay_R01.isStat();
|
||||
root["status_R02"] = Cmd_Relay_R02.isStat();
|
||||
|
||||
createDtuInfo(root);
|
||||
|
||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String buffer;
|
||||
serializeJson(root, buffer);
|
||||
publish(configTopic, buffer);
|
||||
@ -330,10 +336,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;
|
||||
@ -359,6 +362,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);
|
||||
@ -376,10 +383,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;
|
||||
@ -396,13 +400,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,
|
||||
@ -411,11 +419,11 @@ void MqttHandleHassClass::createInverterInfo(DynamicJsonDocument& root, std::sha
|
||||
getDtuUrl(),
|
||||
"OpenDTU",
|
||||
inv->typeName(),
|
||||
AUTO_GIT_HASH,
|
||||
__COMPILED_GIT_HASH__,
|
||||
getDtuUniqueId());
|
||||
}
|
||||
|
||||
void MqttHandleHassClass::createDtuInfo(DynamicJsonDocument& root)
|
||||
void MqttHandleHassClass::createDtuInfo(JsonDocument& root)
|
||||
{
|
||||
createDeviceInfo(
|
||||
root,
|
||||
@ -424,16 +432,16 @@ void MqttHandleHassClass::createDtuInfo(DynamicJsonDocument& root)
|
||||
getDtuUrl(),
|
||||
"OpenDTU",
|
||||
"OpenDTU",
|
||||
AUTO_GIT_HASH);
|
||||
__COMPILED_GIT_HASH__);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#define TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE "limit_nonpersistent_absolute"
|
||||
#define TOPIC_SUB_POWER "power"
|
||||
#define TOPIC_SUB_RESTART "restart"
|
||||
|
||||
#define TOPIC_SUB_RELAY_R01 "Relay R01"
|
||||
#define TOPIC_SUB_RELAY_R02 "Relay R02"
|
||||
|
||||
@ -29,22 +30,7 @@ MqttHandleInverterClass::MqttHandleInverterClass()
|
||||
|
||||
void MqttHandleInverterClass::init(Scheduler& scheduler)
|
||||
{
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
using std::placeholders::_3;
|
||||
using std::placeholders::_4;
|
||||
using std::placeholders::_5;
|
||||
using std::placeholders::_6;
|
||||
|
||||
const String topic = MqttSettings.getPrefix();
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R01), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R02), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
subscribeTopics();
|
||||
|
||||
scheduler.addTask(_loopTask);
|
||||
_loopTask.setInterval(Configuration.get().Mqtt.PublishInterval * TASK_SECOND);
|
||||
@ -206,7 +192,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
char* strlimit = new char[len + 1];
|
||||
memcpy(strlimit, payload, len);
|
||||
strlimit[len] = '\0';
|
||||
const int32_t payload_val = strtol(strlimit, NULL, 10);
|
||||
const float payload_val = strtof(strlimit, NULL);
|
||||
delete[] strlimit;
|
||||
|
||||
if (payload_val < 0) {
|
||||
@ -216,17 +202,17 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
|
||||
if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) {
|
||||
// Set inverter limit relative persistent
|
||||
MessageOutput.printf("Limit Persistent: %d %%\r\n", payload_val);
|
||||
MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val);
|
||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent);
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) {
|
||||
// Set inverter limit absolute persistent
|
||||
MessageOutput.printf("Limit Persistent: %d W\r\n", payload_val);
|
||||
MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val);
|
||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent);
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) {
|
||||
// Set inverter limit relative non persistent
|
||||
MessageOutput.printf("Limit Non-Persistent: %d %%\r\n", payload_val);
|
||||
MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val);
|
||||
if (!properties.retain) {
|
||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativNonPersistent);
|
||||
} else {
|
||||
@ -235,7 +221,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) {
|
||||
// Set inverter limit absolute non persistent
|
||||
MessageOutput.printf("Limit Non-Persistent: %d W\r\n", payload_val);
|
||||
MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val);
|
||||
if (!properties.retain) {
|
||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutNonPersistent);
|
||||
} else {
|
||||
@ -244,8 +230,8 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_POWER)) {
|
||||
// Turn inverter on or off
|
||||
MessageOutput.printf("Set inverter power to: %d\r\n", payload_val);
|
||||
inv->sendPowerControlRequest(payload_val > 0);
|
||||
MessageOutput.printf("Set inverter power to: %d\r\n", static_cast<int32_t>(payload_val));
|
||||
inv->sendPowerControlRequest(static_cast<int32_t>(payload_val) > 0);
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_RESTART)) {
|
||||
// Restart inverter
|
||||
@ -253,12 +239,11 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
if (!properties.retain && payload_val == 1) {
|
||||
inv->sendRestartControlRequest();
|
||||
} else {
|
||||
MessageOutput.println("Ignored because retained");
|
||||
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
||||
}
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_RELAY_R01)) {
|
||||
} else if (!strcmp(setting, TOPIC_SUB_RELAY_R01)) {
|
||||
// Turn relay on or off
|
||||
MessageOutput.printf("Set relay R01 to: %d\r\n", payload_val);
|
||||
MessageOutput.printf("Set relay R01 to: %d\r\n", int(payload_val));
|
||||
//Relay Cmd_Relay_R01 = Relay(pin.relay_r01);
|
||||
if (payload_val == 1) {
|
||||
Cmd_Relay_R01.on();
|
||||
@ -269,7 +254,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
|
||||
} else if (!strcmp(setting, TOPIC_SUB_RELAY_R02)) {
|
||||
// Turn relay on or off
|
||||
MessageOutput.printf("Set relay R02 to: %d\r\n", payload_val);
|
||||
MessageOutput.printf("Set relay R02 to: %d\r\n", int(payload_val));
|
||||
//Relay Cmd_Relay_R02 = Relay(pin.relay_r02);
|
||||
if (payload_val == 1) {
|
||||
Cmd_Relay_R02.on();
|
||||
@ -277,6 +262,41 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
||||
else {
|
||||
Cmd_Relay_R02.off();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void MqttHandleInverterClass::subscribeTopics()
|
||||
{
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
using std::placeholders::_3;
|
||||
using std::placeholders::_4;
|
||||
using std::placeholders::_5;
|
||||
using std::placeholders::_6;
|
||||
|
||||
const String topic = MqttSettings.getPrefix();
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R01), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R02), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
}
|
||||
|
||||
void MqttHandleInverterClass::unsubscribeTopics()
|
||||
{
|
||||
const String topic = MqttSettings.getPrefix();
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART));
|
||||
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R01));
|
||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_RELAY_R02));
|
||||
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ void MqttSettingsClass::performConnect()
|
||||
MessageOutput.println("Connecting to MQTT...");
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
const String willTopic = getPrefix() + config.Mqtt.Lwt.Topic;
|
||||
const String clientId = NetworkSettings.getApName();
|
||||
String clientId = getClientId();
|
||||
if (config.Mqtt.Tls.Enabled) {
|
||||
static_cast<espMqttClientSecure*>(_mqttClient)->setCACert(config.Mqtt.Tls.RootCaCert);
|
||||
static_cast<espMqttClientSecure*>(_mqttClient)->setServer(config.Mqtt.Hostname, config.Mqtt.Port);
|
||||
@ -180,6 +180,15 @@ String MqttSettingsClass::getPrefix() const
|
||||
return Configuration.get().Mqtt.Topic;
|
||||
}
|
||||
|
||||
String MqttSettingsClass::getClientId()
|
||||
{
|
||||
String clientId = Configuration.get().Mqtt.ClientId;
|
||||
if (clientId == "") {
|
||||
clientId = NetworkSettings.getApName();
|
||||
}
|
||||
return clientId;
|
||||
}
|
||||
|
||||
void MqttSettingsClass::publish(const String& subtopic, const String& payload)
|
||||
{
|
||||
String topic = getPrefix();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "defaults.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <ETH.h>
|
||||
#include "__compiled_constants.h"
|
||||
|
||||
NetworkSettingsClass::NetworkSettingsClass()
|
||||
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&NetworkSettingsClass::loop, this))
|
||||
@ -26,6 +27,8 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
|
||||
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
||||
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
|
||||
|
||||
WiFi.disconnect(true, true);
|
||||
|
||||
WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1));
|
||||
setupMode();
|
||||
|
||||
@ -76,7 +79,8 @@ void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event)
|
||||
MessageOutput.println("WiFi disconnected");
|
||||
if (_networkMode == network_mode::WiFi) {
|
||||
MessageOutput.println("Try reconnecting");
|
||||
WiFi.reconnect();
|
||||
WiFi.disconnect(true, false);
|
||||
WiFi.begin();
|
||||
raiseEvent(network_event::NETWORK_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
@ -136,7 +140,7 @@ void NetworkSettingsClass::handleMDNS()
|
||||
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService("opendtu", "tcp", 80);
|
||||
MDNS.addServiceTxt("opendtu", "tcp", "git_hash", AUTO_GIT_HASH);
|
||||
MDNS.addServiceTxt("opendtu", "tcp", "git_hash", __COMPILED_GIT_HASH__);
|
||||
|
||||
MessageOutput.println("done");
|
||||
} else {
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
#include <LittleFS.h>
|
||||
#include <string.h>
|
||||
|
||||
#define JSON_BUFFER_SIZE 6144
|
||||
|
||||
#ifndef DISPLAY_TYPE
|
||||
#define DISPLAY_TYPE 0U
|
||||
#endif
|
||||
@ -152,7 +150,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) {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2022 - 2023 Thomas Basler and others
|
||||
* Copyright (C) 2022 - 2024 Thomas Basler and others
|
||||
*/
|
||||
|
||||
#include "Utils.h"
|
||||
#include "Display_Graphic.h"
|
||||
#include "Led_Single.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "PinMapping.h"
|
||||
#include <Esp.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -86,4 +87,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,9 +80,9 @@ 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;
|
||||
}
|
||||
|
||||
@ -90,8 +90,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
RelayPinObj["R01"] = pin.relay_r01;
|
||||
RelayPinObj["R02"] = pin.relay_r02;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
||||
@ -100,45 +99,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;
|
||||
}
|
||||
|
||||
@ -146,8 +119,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;
|
||||
}
|
||||
|
||||
@ -178,8 +150,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;
|
||||
@ -55,6 +55,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
obj["reachable_threshold"] = config.Inverter[i].ReachableThreshold;
|
||||
obj["zero_runtime"] = config.Inverter[i].ZeroRuntimeDataIfUnrechable;
|
||||
obj["zero_day"] = config.Inverter[i].ZeroYieldDayOnMidnight;
|
||||
obj["clear_eventlog"] = config.Inverter[i].ClearEventlogOnMidnight;
|
||||
obj["yieldday_correction"] = config.Inverter[i].YieldDayCorrection;
|
||||
|
||||
auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial);
|
||||
@ -67,9 +68,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 +78,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
||||
@ -88,44 +88,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 +109,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 +117,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 +127,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 +138,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 +158,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 +185,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 +193,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 +201,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;
|
||||
}
|
||||
|
||||
@ -274,27 +214,27 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
inverter.Serial = new_serial;
|
||||
strncpy(inverter.Name, root["name"].as<String>().c_str(), INV_MAX_NAME_STRLEN);
|
||||
|
||||
inverter.Poll_Enable = root["poll_enable"] | true;
|
||||
inverter.Poll_Enable_Night = root["poll_enable_night"] | true;
|
||||
inverter.Command_Enable = root["command_enable"] | true;
|
||||
inverter.Command_Enable_Night = root["command_enable_night"] | true;
|
||||
inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD;
|
||||
inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false;
|
||||
inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false;
|
||||
inverter.ClearEventlogOnMidnight = root["clear_eventlog"] | false;
|
||||
inverter.YieldDayCorrection = root["yieldday_correction"] | false;
|
||||
|
||||
uint8_t arrayCount = 0;
|
||||
for (JsonVariant channel : channelArray) {
|
||||
inverter.channel[arrayCount].MaxChannelPower = channel["max_power"].as<uint16_t>();
|
||||
inverter.channel[arrayCount].YieldTotalOffset = channel["yield_total_offset"].as<float>();
|
||||
strncpy(inverter.channel[arrayCount].Name, channel["name"] | "", sizeof(inverter.channel[arrayCount].Name));
|
||||
inverter.Poll_Enable = root["poll_enable"] | true;
|
||||
inverter.Poll_Enable_Night = root["poll_enable_night"] | true;
|
||||
inverter.Command_Enable = root["command_enable"] | true;
|
||||
inverter.Command_Enable_Night = root["command_enable_night"] | true;
|
||||
inverter.ReachableThreshold = root["reachable_threshold"] | REACHABLE_THRESHOLD;
|
||||
inverter.ZeroRuntimeDataIfUnrechable = root["zero_runtime"] | false;
|
||||
inverter.ZeroYieldDayOnMidnight = root["zero_day"] | false;
|
||||
inverter.YieldDayCorrection = root["yieldday_correction"] | false;
|
||||
|
||||
arrayCount++;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -316,6 +256,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
inv->setReachableThreshold(inverter.ReachableThreshold);
|
||||
inv->setZeroValuesIfUnreachable(inverter.ZeroRuntimeDataIfUnrechable);
|
||||
inv->setZeroYieldDayOnMidnight(inverter.ZeroYieldDayOnMidnight);
|
||||
inv->setClearEventlogOnMidnight(inverter.ClearEventlogOnMidnight);
|
||||
inv->Statistics()->setYieldDayCorrection(inverter.YieldDayCorrection);
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
inv->Statistics()->setStringMaxPower(c, inverter.channel[c].MaxChannelPower);
|
||||
@ -333,51 +274,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 +304,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 +316,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 +344,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,17 +79,15 @@ 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;
|
||||
}
|
||||
|
||||
if (root["limit_value"].as<uint16_t>() > MAX_INVERTER_LIMIT) {
|
||||
if (root["limit_value"].as<float>() > MAX_INVERTER_LIMIT) {
|
||||
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,20 +98,18 @@ 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;
|
||||
}
|
||||
|
||||
uint16_t limit = root["limit_value"].as<uint16_t>();
|
||||
float limit = root["limit_value"].as<float>();
|
||||
PowerLimitControlType type = root["limit_type"].as<PowerLimitControlType>();
|
||||
|
||||
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||
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__);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "WebApi_mqtt.h"
|
||||
#include "Configuration.h"
|
||||
#include "MqttHandleHass.h"
|
||||
#include "MqttHandleInverter.h"
|
||||
#include "MqttSettings.h"
|
||||
#include "WebApi.h"
|
||||
#include "WebApi_errors.h"
|
||||
@ -26,13 +27,14 @@ 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();
|
||||
|
||||
root["mqtt_enabled"] = config.Mqtt.Enabled;
|
||||
root["mqtt_hostname"] = config.Mqtt.Hostname;
|
||||
root["mqtt_port"] = config.Mqtt.Port;
|
||||
root["mqtt_clientid"] = MqttSettings.getClientId();
|
||||
root["mqtt_username"] = config.Mqtt.Username;
|
||||
root["mqtt_topic"] = config.Mqtt.Topic;
|
||||
root["mqtt_connected"] = MqttSettings.getConnected();
|
||||
@ -50,8 +52,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,13 +61,14 @@ 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();
|
||||
|
||||
root["mqtt_enabled"] = config.Mqtt.Enabled;
|
||||
root["mqtt_hostname"] = config.Mqtt.Hostname;
|
||||
root["mqtt_port"] = config.Mqtt.Port;
|
||||
root["mqtt_clientid"] = config.Mqtt.ClientId;
|
||||
root["mqtt_username"] = config.Mqtt.Username;
|
||||
root["mqtt_password"] = config.Mqtt.Password;
|
||||
root["mqtt_topic"] = config.Mqtt.Topic;
|
||||
@ -77,7 +79,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
|
||||
root["mqtt_client_cert"] = config.Mqtt.Tls.ClientCert;
|
||||
root["mqtt_client_key"] = config.Mqtt.Tls.ClientKey;
|
||||
root["mqtt_lwt_topic"] = config.Mqtt.Lwt.Topic;
|
||||
root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online;;
|
||||
root["mqtt_lwt_online"] = config.Mqtt.Lwt.Value_Online;
|
||||
root["mqtt_lwt_offline"] = config.Mqtt.Lwt.Value_Offline;
|
||||
root["mqtt_lwt_qos"] = config.Mqtt.Lwt.Qos;
|
||||
root["mqtt_publish_interval"] = config.Mqtt.PublishInterval;
|
||||
@ -88,8 +90,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,42 +99,18 @@ 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")
|
||||
&& root.containsKey("mqtt_port")
|
||||
&& root.containsKey("mqtt_clientid")
|
||||
&& root.containsKey("mqtt_username")
|
||||
&& root.containsKey("mqtt_password")
|
||||
&& root.containsKey("mqtt_topic")
|
||||
@ -155,8 +132,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,57 +141,57 @@ 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;
|
||||
}
|
||||
|
||||
if (root["mqtt_clientid"].as<String>().length() > MQTT_MAX_CLIENTID_STRLEN) {
|
||||
retMsg["message"] = "Client ID must not be longer than " STR(MQTT_MAX_CLIENTID_STRLEN) " characters!";
|
||||
retMsg["code"] = WebApiError::MqttClientIdLength;
|
||||
retMsg["param"]["max"] = MQTT_MAX_CLIENTID_STRLEN;
|
||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||
return;
|
||||
}
|
||||
if (root["mqtt_username"].as<String>().length() > MQTT_MAX_USERNAME_STRLEN) {
|
||||
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 +201,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 +209,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 +224,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 +232,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 +240,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 +249,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 +258,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;
|
||||
}
|
||||
}
|
||||
@ -314,9 +281,9 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as<String>().c_str(), sizeof(config.Mqtt.Tls.ClientKey));
|
||||
config.Mqtt.Port = root["mqtt_port"].as<uint>();
|
||||
strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as<String>().c_str(), sizeof(config.Mqtt.Hostname));
|
||||
strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as<String>().c_str(), sizeof(config.Mqtt.ClientId));
|
||||
strlcpy(config.Mqtt.Username, root["mqtt_username"].as<String>().c_str(), sizeof(config.Mqtt.Username));
|
||||
strlcpy(config.Mqtt.Password, root["mqtt_password"].as<String>().c_str(), sizeof(config.Mqtt.Password));
|
||||
strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Topic));
|
||||
strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Topic));
|
||||
strlcpy(config.Mqtt.Lwt.Value_Online, root["mqtt_lwt_online"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Online));
|
||||
strlcpy(config.Mqtt.Lwt.Value_Offline, root["mqtt_lwt_offline"].as<String>().c_str(), sizeof(config.Mqtt.Lwt.Value_Offline));
|
||||
@ -329,10 +296,16 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
||||
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));
|
||||
|
||||
// Check if base topic was changed
|
||||
if (strcmp(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str())) {
|
||||
MqttHandleInverter.unsubscribeTopics();
|
||||
strlcpy(config.Mqtt.Topic, root["mqtt_topic"].as<String>().c_str(), sizeof(config.Mqtt.Topic));
|
||||
MqttHandleInverter.subscribeTopics();
|
||||
}
|
||||
|
||||
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__);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "NetworkSettings.h"
|
||||
#include "WebApi.h"
|
||||
#include <Hoymiles.h>
|
||||
#include "__compiled_constants.h"
|
||||
|
||||
void WebApiPrometheusClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
||||
{
|
||||
@ -29,7 +30,7 @@ void WebApiPrometheusClass::onPrometheusMetricsGet(AsyncWebServerRequest* reques
|
||||
stream->print("# HELP opendtu_build Build info\n");
|
||||
stream->print("# TYPE opendtu_build gauge\n");
|
||||
stream->printf("opendtu_build{name=\"%s\",id=\"%s\",version=\"%d.%d.%d\"} 1\n",
|
||||
NetworkSettings.getHostname().c_str(), AUTO_GIT_HASH, CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff);
|
||||
NetworkSettings.getHostname().c_str(), __COMPILED_GIT_HASH__, CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff);
|
||||
|
||||
stream->print("# HELP opendtu_platform Platform info\n");
|
||||
stream->print("# TYPE opendtu_platform gauge\n");
|
||||
@ -142,7 +143,7 @@ void WebApiPrometheusClass::addPanelInfo(AsyncResponseStream* stream, const Stri
|
||||
return;
|
||||
}
|
||||
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
const auto& config = Configuration.getInverterConfig(inv->serial());
|
||||
|
||||
const bool printHelp = (idx == 0 && channel == 0);
|
||||
if (printHelp) {
|
||||
@ -154,7 +155,7 @@ void WebApiPrometheusClass::addPanelInfo(AsyncResponseStream* stream, const Stri
|
||||
idx,
|
||||
inv->name(),
|
||||
channel,
|
||||
config.Inverter[idx].channel[channel].Name);
|
||||
config->channel[channel].Name);
|
||||
|
||||
if (printHelp) {
|
||||
stream->print("# HELP opendtu_MaxPower panel maximum output power\n");
|
||||
@ -165,7 +166,7 @@ void WebApiPrometheusClass::addPanelInfo(AsyncResponseStream* stream, const Stri
|
||||
idx,
|
||||
inv->name(),
|
||||
channel,
|
||||
config.Inverter[idx].channel[channel].MaxChannelPower);
|
||||
config->channel[channel].MaxChannelPower);
|
||||
|
||||
if (printHelp) {
|
||||
stream->print("# HELP opendtu_YieldTotalOffset panel yield offset (for used inverters)\n");
|
||||
@ -176,5 +177,5 @@ void WebApiPrometheusClass::addPanelInfo(AsyncResponseStream* stream, const Stri
|
||||
idx,
|
||||
inv->name(),
|
||||
channel,
|
||||
config.Inverter[idx].channel[channel].YieldTotalOffset);
|
||||
config->channel[channel].YieldTotalOffset);
|
||||
}
|
||||
|
||||
@ -50,37 +50,12 @@ void WebApiRelayClass::onRelayPost(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("R01") || root.containsKey("R02"))) {
|
||||
retMsg["message"] = "Values are missing!";
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
#include "WebApi_errors.h"
|
||||
#include "helper.h"
|
||||
#include <AsyncJson.h>
|
||||
#include "MessageOutput.h"
|
||||
|
||||
void WebApiSecurityClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
||||
{
|
||||
@ -32,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)
|
||||
@ -43,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;
|
||||
}
|
||||
|
||||
@ -88,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;
|
||||
}
|
||||
|
||||
@ -99,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)
|
||||
@ -115,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__);
|
||||
}
|
||||
|
||||
@ -7,15 +7,13 @@
|
||||
#include "NetworkSettings.h"
|
||||
#include "PinMapping.h"
|
||||
#include "WebApi.h"
|
||||
#include "__compiled_constants.h"
|
||||
#include <AsyncJson.h>
|
||||
#include <CpuTemperature.h>
|
||||
#include <Hoymiles.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ResetReason.h>
|
||||
|
||||
#ifndef AUTO_GIT_HASH
|
||||
#define AUTO_GIT_HASH ""
|
||||
#endif
|
||||
|
||||
void WebApiSysstatusClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
||||
{
|
||||
using std::placeholders::_1;
|
||||
@ -36,6 +34,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||
|
||||
root["sdkversion"] = ESP.getSdkVersion();
|
||||
root["cpufreq"] = ESP.getCpuFreqMHz();
|
||||
root["cputemp"] = CpuTemperature.read();
|
||||
|
||||
root["heap_total"] = ESP.getHeapSize();
|
||||
root["heap_used"] = ESP.getHeapSize() - ESP.getFreeHeap();
|
||||
@ -51,6 +50,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||
root["chiprevision"] = ESP.getChipRevision();
|
||||
root["chipmodel"] = ESP.getChipModel();
|
||||
root["chipcores"] = ESP.getChipCores();
|
||||
root["flashsize"] = ESP.getFlashChipSize();
|
||||
|
||||
String reason;
|
||||
reason = ResetReason::get_reset_reason_verbose(0);
|
||||
@ -64,7 +64,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||
char version[16];
|
||||
snprintf(version, sizeof(version), "%d.%d.%d", CONFIG_VERSION >> 24 & 0xff, CONFIG_VERSION >> 16 & 0xff, CONFIG_VERSION >> 8 & 0xff);
|
||||
root["config_version"] = version;
|
||||
root["git_hash"] = AUTO_GIT_HASH;
|
||||
root["git_hash"] = __COMPILED_GIT_HASH__;
|
||||
root["pioenv"] = PIOENV;
|
||||
|
||||
root["uptime"] = esp_timer_get_time() / 1000000;
|
||||
@ -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__);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ void WebApiWebappClass::responseBinaryDataWithETagCache(AsyncWebServerRequest *r
|
||||
if (eTagMatch) {
|
||||
response = request->beginResponse(304);
|
||||
} else {
|
||||
response = request->beginResponse_P(200, contentType, content, len);
|
||||
response = request->beginResponse(200, contentType, content, len);
|
||||
if (contentEncoding.length() > 0) {
|
||||
response->addHeader("Content-Encoding", contentEncoding);
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
17
src/main.cpp
17
src/main.cpp
@ -139,14 +139,6 @@ void setup()
|
||||
LedSingle.init(scheduler);
|
||||
MessageOutput.println("done");
|
||||
|
||||
// Initialize Relay
|
||||
MessageOutput.print("Initialize Relay... ");
|
||||
Relay Cmd_Relay_R01 = Relay(pin.relay_r01);
|
||||
Relay Cmd_Relay_R02 = Relay(pin.relay_r02);
|
||||
Cmd_Relay_R01.off();
|
||||
Cmd_Relay_R02.off();
|
||||
MessageOutput.println("done");
|
||||
|
||||
// Check for default DTU serial
|
||||
MessageOutput.print("Check for default DTU serial... ");
|
||||
if (config.Dtu.Serial == DTU_SERIAL) {
|
||||
@ -158,6 +150,15 @@ void setup()
|
||||
config.Dtu.Serial = dtuId;
|
||||
Configuration.write();
|
||||
}
|
||||
|
||||
// Initialize Relay
|
||||
MessageOutput.print("Initialize Relay... ");
|
||||
Relay Cmd_Relay_R01 = Relay(pin.relay_r01);
|
||||
Relay Cmd_Relay_R02 = Relay(pin.relay_r02);
|
||||
Cmd_Relay_R01.off();
|
||||
Cmd_Relay_R02.off();
|
||||
MessageOutput.println("done");
|
||||
|
||||
MessageOutput.println("done");
|
||||
|
||||
InverterSettings.init(scheduler);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user