MQTT settings in the UI

This commit is contained in:
Jaakko Haakana 2025-07-05 23:59:32 +03:00
parent 693689e5af
commit 6cdcb08abb
8 changed files with 140 additions and 27 deletions

View file

@ -128,7 +128,9 @@ void setup() {
// Start tasks
if (mqtt_enabled) {
init_mqtt();
if (!init_mqtt()) {
return;
}
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
esp32hal->WIFICORE());

View file

@ -35,9 +35,14 @@ IPAddress gateway(192, 168, 10, 1);
IPAddress subnet(255, 255, 255, 0);
// MQTT
const char* mqtt_user = MQTT_USER; // Set in USER_SECRETS.h
const char* mqtt_password = MQTT_PASSWORD; // Set in USER_SECRETS.h
#ifdef MQTT_MANUAL_TOPIC_OBJECT_NAME
#ifdef COMMON_IMAGE
std::string mqtt_user;
std::string mqtt_password;
#else
std::string mqtt_user = MQTT_USER; // Set in USER_SECRETS.h
std::string mqtt_password = MQTT_PASSWORD; // Set in USER_SECRETS.h
#endif
const char* mqtt_topic_name =
"BE"; // Custom MQTT topic name. Previously, the name was automatically set to "battery-emulator_esp32-XXXXXX"
const char* mqtt_object_id_prefix =
@ -46,7 +51,6 @@ const char* mqtt_device_name =
"Battery Emulator"; // Custom device name in Home Assistant. Previously, the name was automatically set to "BatteryEmulator_esp32-XXXXXX"
const char* ha_device_id =
"battery-emulator"; // Custom device ID in Home Assistant. Previously, the ID was always "battery-emulator"
#endif // MQTT_MANUAL_TOPIC_OBJECT_NAME
/* Charger settings (Optional, when using generator charging) */
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack

View file

@ -115,6 +115,11 @@ void init_stored_settings() {
mqtt_enabled = settings.getBool("MQTTENABLED", false);
ha_autodiscovery_enabled = settings.getBool("HADISC", false);
mqtt_server = settings.getString("MQTTSERVER").c_str();
mqtt_port = settings.getUInt("MQTTPORT", 0);
mqtt_user = settings.getString("MQTTUSER").c_str();
mqtt_password = settings.getString("MQTTPASSWORD").c_str();
#endif
settings.end();

View file

@ -63,6 +63,18 @@ class BatteryEmulatorSettingsStore {
settingsUpdated = settingsUpdated || value != oldValue;
}
String getString(const char* name) { return settings.getString(name, String()); }
String getString(const char* name, const char* defaultValue) {
return settings.getString(name, String(defaultValue));
}
void saveString(const char* name, const char* value) {
auto oldValue = settings.getString(name);
settings.putString(name, value);
settingsUpdated = settingsUpdated || String(value) != oldValue;
}
bool were_settings_updated() const { return settingsUpdated; }
private:

View file

@ -2,6 +2,7 @@
#include <Arduino.h>
#include <WiFi.h>
#include <freertos/FreeRTOS.h>
#include <src/communication/nvm/comm_nvm.h>
#include <list>
#include "../../../USER_SECRETS.h"
#include "../../../USER_SETTINGS.h"
@ -29,6 +30,25 @@ const bool ha_autodiscovery_enabled_default = false;
bool ha_autodiscovery_enabled = ha_autodiscovery_enabled_default;
#ifdef COMMON_IMAGE
const int mqtt_port_default = 0;
const char* mqtt_server_default = "";
#else
const int mqtt_port_default = MQTT_PORT;
const char* mqtt_server_default = MQTT_SERVER;
#endif
int mqtt_port = mqtt_port_default;
std::string mqtt_server = mqtt_server_default;
#ifdef MQTT_MANUAL_TOPIC_OBJECT_NAME
const bool mqtt_manual_topic_object_name_default = true;
#else
const bool mqtt_manual_topic_object_name_default = false;
#endif
bool mqtt_manual_topic_object_name = mqtt_manual_topic_object_name_default;
esp_mqtt_client_config_t mqtt_cfg;
esp_mqtt_client_handle_t client;
char mqtt_msg[MQTT_MSG_BUFFER_SIZE];
@ -550,34 +570,42 @@ static void mqtt_event_handler(void* handler_args, esp_event_base_t base, int32_
}
}
void init_mqtt(void) {
bool init_mqtt(void) {
if (ha_autodiscovery_enabled) {
create_battery_sensor_configs();
create_global_sensor_configs();
}
#ifdef MQTT_MANUAL_TOPIC_OBJECT_NAME
// Use custom topic name, object ID prefix, and device name from user settings
topic_name = mqtt_topic_name;
object_id_prefix = mqtt_object_id_prefix;
device_name = mqtt_device_name;
device_id = ha_device_id;
if (mqtt_manual_topic_object_name) {
#ifdef COMMON_IMAGE
BatteryEmulatorSettingsStore settings;
topic_name = settings.getString("MQTTTOPIC", mqtt_topic_name);
object_id_prefix = settings.getString("MQTTOBJIDPREFIX", mqtt_object_id_prefix);
device_name = settings.getString("MQTTDEVICENAME", mqtt_device_name);
device_id = settings.getString("HADEVICEID", ha_device_id);
#else
// Use default naming based on WiFi hostname for topic, object ID prefix, and device name
topic_name = "battery-emulator_" + String(WiFi.getHostname());
object_id_prefix = String(WiFi.getHostname()) + String("_");
device_name = "BatteryEmulator_" + String(WiFi.getHostname());
device_id = "battery-emulator";
// Use custom topic name, object ID prefix, and device name from user settings
topic_name = mqtt_topic_name;
object_id_prefix = mqtt_object_id_prefix;
device_name = mqtt_device_name;
device_id = ha_device_id;
#endif
} else {
// Use default naming based on WiFi hostname for topic, object ID prefix, and device name
topic_name = "battery-emulator_" + String(WiFi.getHostname());
object_id_prefix = String(WiFi.getHostname()) + String("_");
device_name = "BatteryEmulator_" + String(WiFi.getHostname());
device_id = "battery-emulator";
}
String clientId = String("BatteryEmulatorClient-") + WiFi.getHostname();
mqtt_cfg.broker.address.transport = MQTT_TRANSPORT_OVER_TCP;
mqtt_cfg.broker.address.hostname = MQTT_SERVER;
mqtt_cfg.broker.address.port = MQTT_PORT;
mqtt_cfg.broker.address.hostname = mqtt_server.c_str();
mqtt_cfg.broker.address.port = mqtt_port;
mqtt_cfg.credentials.client_id = clientId.c_str();
mqtt_cfg.credentials.username = MQTT_USER;
mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD;
mqtt_cfg.credentials.username = mqtt_user.c_str();
mqtt_cfg.credentials.authentication.password = mqtt_password.c_str();
lwt_topic = topic_name + "/status";
mqtt_cfg.session.last_will.topic = lwt_topic.c_str();
mqtt_cfg.session.last_will.qos = 1;
@ -586,7 +614,16 @@ void init_mqtt(void) {
mqtt_cfg.session.last_will.msg_len = strlen(mqtt_cfg.session.last_will.msg);
mqtt_cfg.network.timeout_ms = MQTT_TIMEOUT;
client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, client);
if (client == nullptr) {
return false;
}
if (esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, client) != ESP_OK) {
return false;
}
return true;
}
void mqtt_loop(void) {

View file

@ -42,8 +42,10 @@
extern const char* version_number; // The current software version, used for mqtt
extern const char* mqtt_user;
extern const char* mqtt_password;
extern std::string mqtt_server;
extern std::string mqtt_user;
extern std::string mqtt_password;
extern int mqtt_port;
extern const char* mqtt_topic_name;
extern const char* mqtt_object_id_prefix;
extern const char* mqtt_device_name;
@ -51,7 +53,7 @@ extern const char* ha_device_id;
extern char mqtt_msg[MQTT_MSG_BUFFER_SIZE];
void init_mqtt(void);
bool init_mqtt(void);
void mqtt_loop(void);
bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain);

View file

@ -98,6 +98,26 @@ const char* name_for_button_type(STOP_BUTTON_BEHAVIOR behavior) {
}
}
void render_textbox(String& content, const char* label, const char* name, BatteryEmulatorSettingsStore& settings,
bool password = false, bool number = false) {
content += "<label>";
content += label;
content += ": </label><input style='max-width: 250px;' ";
if (password) {
content += "type='password'";
} else {
content += "type='text'";
}
content += " name='";
content += name;
content += "' value=\"";
auto value = number ? String(settings.getUInt(name, 0)) : settings.getString(name);
value.replace("\"", "&quot;"); // Escape quotes for HTML
content += value;
content += "\"/>";
}
String settings_processor(const String& var) {
if (var == "X") {
String content = "";
@ -194,6 +214,18 @@ String settings_processor(const String& var) {
render_checkbox(content, "Enable WiFi AP", settings.getBool("WIFIAPENABLED"), "WIFIAPENABLED");
render_checkbox(content, "Enable MQTT", settings.getBool("MQTTENABLED"), "MQTTENABLED");
render_textbox(content, "MQTT server", "MQTTSERVER", settings);
render_textbox(content, "MQTT port", "MQTTPORT", settings, false, true);
render_textbox(content, "MQTT user", "MQTTUSER", settings);
render_textbox(content, "MQTT password", "MQTTPASSWORD", settings, true);
render_checkbox(content, "Customized MQTT topics", settings.getBool("MQTTTOPICS"), "MQTTTOPICS");
render_textbox(content, "MQTT topic name", "MQTTTOPIC", settings);
render_textbox(content, "Prefix for MQTT object ID", "MQTTOBJIDPREFIX", settings);
render_textbox(content, "HA device name", "MQTTDEVICENAME", settings);
render_textbox(content, "HA device ID", "HADEVICEID", settings);
render_checkbox(content, "Enable Home Assistant auto discovery", settings.getBool("HADISC"), "HADISC");
content +=

View file

@ -403,8 +403,10 @@ void init_webserver() {
bool newValue;
};
const char* boolSettingNames[] = {"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET",
"REMBMSRESET", "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC"};
const char* boolSettingNames[] = {
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET",
"CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS",
};
// Handles the form POST from UI to save settings of the common image
server.on("/saveSettings", HTTP_POST, [boolSettingNames](AsyncWebServerRequest* request) {
@ -449,6 +451,23 @@ void init_webserver() {
} else if (p->name() == "SHUNTCOMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("SHUNTCOMM", (int)type);
} else if (p->name() == "MQTTSERVER") {
settings.saveString("MQTTSERVER", p->value().c_str());
} else if (p->name() == "MQTTPORT") {
auto port = atoi(p->value().c_str());
settings.saveUInt("MQTTPORT", port);
} else if (p->name() == "MQTTUSER") {
settings.saveString("MQTTUSER", p->value().c_str());
} else if (p->name() == "MQTTPASSWORD") {
settings.saveString("MQTTPASSWORD", p->value().c_str());
} else if (p->name() == "MQTTTOPIC") {
settings.saveString("MQTTTOPIC", p->value().c_str());
} else if (p->name() == "MQTTOBJIDPREFIX") {
settings.saveString("MQTTOBJIDPREFIX", p->value().c_str());
} else if (p->name() == "MQTTDEVICENAME") {
settings.saveString("MQTTDEVICENAME", p->value().c_str());
} else if (p->name() == "HADEVICEID") {
settings.saveString("HADEVICEID", p->value().c_str());
}
for (auto& boolSetting : boolSettings) {