* fix logic in HomeAssistent handler * also publish voltage thresholds (not just SoC thresholds) * do not publish irrelevant thresholds to MQTT. if the inverter is solar-powered, no thresholds are effectively in use by the DPL and it therefore makes no sense to publish them to the broker. similarly, if no battery interface is enabled or the SoC values are set to be ignored, the SoC thresholds are effectively not in use and will not be published to the broker. * make HA auto-discovery expire. this makes auto-dicovered items disappear from Home Assistent if their value is no longer updated. changes to settings which cause other thresholds to be relevant will then be reflected in Home Assistent even if some thresholds are no longer maintaned in MQTT. * force HA update when related settings change enabling VE.Direct shall trigger an update since solar passthrough thresholds become relevant. similarly, enabling the battery interface makes SoC thresholds become relevant. there are more settings in the power limiter that also influence the auto-discoverable items. * break very long lines
122 lines
3.9 KiB
C++
122 lines
3.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
|
*/
|
|
|
|
#include "ArduinoJson.h"
|
|
#include "AsyncJson.h"
|
|
#include "Battery.h"
|
|
#include "Configuration.h"
|
|
#include "MqttHandleBatteryHass.h"
|
|
#include "MqttHandlePowerLimiterHass.h"
|
|
#include "WebApi.h"
|
|
#include "WebApi_battery.h"
|
|
#include "WebApi_errors.h"
|
|
#include "helper.h"
|
|
|
|
void WebApiBatteryClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|
{
|
|
using std::placeholders::_1;
|
|
|
|
_server = &server;
|
|
|
|
_server->on("/api/battery/status", HTTP_GET, std::bind(&WebApiBatteryClass::onStatus, this, _1));
|
|
_server->on("/api/battery/config", HTTP_GET, std::bind(&WebApiBatteryClass::onAdminGet, this, _1));
|
|
_server->on("/api/battery/config", HTTP_POST, std::bind(&WebApiBatteryClass::onAdminPost, this, _1));
|
|
}
|
|
|
|
void WebApiBatteryClass::onStatus(AsyncWebServerRequest* request)
|
|
{
|
|
if (!WebApi.checkCredentialsReadonly(request)) {
|
|
return;
|
|
}
|
|
|
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
|
auto& root = response->getRoot();
|
|
const CONFIG_T& config = Configuration.get();
|
|
|
|
root["enabled"] = config.Battery.Enabled;
|
|
root["verbose_logging"] = config.Battery.VerboseLogging;
|
|
root["provider"] = config.Battery.Provider;
|
|
root["jkbms_interface"] = config.Battery.JkBmsInterface;
|
|
root["jkbms_polling_interval"] = config.Battery.JkBmsPollingInterval;
|
|
root["mqtt_soc_topic"] = config.Battery.MqttSocTopic;
|
|
root["mqtt_voltage_topic"] = config.Battery.MqttVoltageTopic;
|
|
|
|
response->setLength();
|
|
request->send(response);
|
|
}
|
|
|
|
void WebApiBatteryClass::onAdminGet(AsyncWebServerRequest* request)
|
|
{
|
|
onStatus(request);
|
|
}
|
|
|
|
void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request)
|
|
{
|
|
if (!WebApi.checkCredentials(request)) {
|
|
return;
|
|
}
|
|
|
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
|
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;
|
|
}
|
|
|
|
String json = request->getParam("data", true)->value();
|
|
|
|
if (json.length() > 1024) {
|
|
retMsg["message"] = "Data too large!";
|
|
retMsg["code"] = WebApiError::GenericDataTooLarge;
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
DynamicJsonDocument root(1024);
|
|
DeserializationError error = deserializeJson(root, json);
|
|
|
|
if (error) {
|
|
retMsg["message"] = "Failed to parse data!";
|
|
retMsg["code"] = WebApiError::GenericParseError;
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
if (!root.containsKey("enabled") || !root.containsKey("provider")) {
|
|
retMsg["message"] = "Values are missing!";
|
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
|
response->setLength();
|
|
request->send(response);
|
|
return;
|
|
}
|
|
|
|
CONFIG_T& config = Configuration.get();
|
|
config.Battery.Enabled = root["enabled"].as<bool>();
|
|
config.Battery.VerboseLogging = root["verbose_logging"].as<bool>();
|
|
config.Battery.Provider = root["provider"].as<uint8_t>();
|
|
config.Battery.JkBmsInterface = root["jkbms_interface"].as<uint8_t>();
|
|
config.Battery.JkBmsPollingInterval = root["jkbms_polling_interval"].as<uint8_t>();
|
|
strlcpy(config.Battery.MqttSocTopic, root["mqtt_soc_topic"].as<String>().c_str(), sizeof(config.Battery.MqttSocTopic));
|
|
strlcpy(config.Battery.MqttVoltageTopic, root["mqtt_voltage_topic"].as<String>().c_str(), sizeof(config.Battery.MqttVoltageTopic));
|
|
|
|
WebApi.writeConfig(retMsg);
|
|
|
|
response->setLength();
|
|
request->send(response);
|
|
|
|
Battery.updateSettings();
|
|
MqttHandleBatteryHass.forceUpdate();
|
|
|
|
// potentially make SoC thresholds auto-discoverable
|
|
MqttHandlePowerLimiterHass.forceUpdate();
|
|
}
|