196 lines
6.4 KiB
C++
196 lines
6.4 KiB
C++
/**
|
|
* ESP32 SvelteKit
|
|
*
|
|
* A simple, secure and extensible framework for IoT projects for ESP32 platforms
|
|
* with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
|
|
* https://github.com/theelims/ESP32-sveltekit
|
|
*
|
|
* Copyright (C) 2018 - 2023 rjwats
|
|
* Copyright (C) 2023 theelims
|
|
*
|
|
* All Rights Reserved. This software may be modified and distributed under
|
|
* the terms of the LGPL v3 license. See the LICENSE file for details.
|
|
**/
|
|
|
|
#include <MqttSettingsService.h>
|
|
|
|
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_bundle_bin_start");
|
|
|
|
/**
|
|
* Retains a copy of the cstr provided in the pointer provided using dynamic allocation.
|
|
*
|
|
* Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr.
|
|
*/
|
|
static char *retainCstr(const char *cstr, char **ptr)
|
|
{
|
|
// free up previously retained value if exists
|
|
free(*ptr);
|
|
*ptr = nullptr;
|
|
|
|
// dynamically allocate and copy cstr (if non null)
|
|
if (cstr != nullptr)
|
|
{
|
|
*ptr = (char *)malloc(strlen(cstr) + 1);
|
|
strcpy(*ptr, cstr);
|
|
}
|
|
|
|
// return reference to pointer for convenience
|
|
return *ptr;
|
|
}
|
|
|
|
MqttSettingsService::MqttSettingsService(PsychicHttpServer *server,
|
|
FS *fs,
|
|
SecurityManager *securityManager) : _server(server),
|
|
_securityManager(securityManager),
|
|
_httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager),
|
|
_fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE),
|
|
_retainedHost(nullptr),
|
|
_retainedClientId(nullptr),
|
|
_retainedUsername(nullptr),
|
|
_retainedPassword(nullptr),
|
|
_reconfigureMqtt(false),
|
|
_mqttClient(),
|
|
_lastError("None")
|
|
{
|
|
addUpdateHandler([&](const String &originId)
|
|
{ onConfigUpdated(); },
|
|
false);
|
|
|
|
_mqttClient.setCACertBundle(rootca_crt_bundle_start);
|
|
}
|
|
|
|
MqttSettingsService::~MqttSettingsService()
|
|
{
|
|
}
|
|
|
|
void MqttSettingsService::begin()
|
|
{
|
|
WiFi.onEvent(
|
|
std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2),
|
|
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
|
|
WiFi.onEvent(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2),
|
|
WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP);
|
|
_mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, std::placeholders::_1));
|
|
_mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, std::placeholders::_1));
|
|
_mqttClient.onError(std::bind(&MqttSettingsService::onMqttError, this, std::placeholders::_1));
|
|
|
|
_httpEndpoint.begin();
|
|
_fsPersistence.readFromFS();
|
|
}
|
|
|
|
void MqttSettingsService::loop()
|
|
{
|
|
if (_reconfigureMqtt)
|
|
{
|
|
// reconfigure MQTT client
|
|
configureMqtt();
|
|
|
|
// clear the reconnection flags
|
|
_reconfigureMqtt = false;
|
|
}
|
|
}
|
|
|
|
bool MqttSettingsService::isEnabled()
|
|
{
|
|
return _state.enabled;
|
|
}
|
|
|
|
bool MqttSettingsService::isConnected()
|
|
{
|
|
return _mqttClient.connected();
|
|
}
|
|
|
|
const char *MqttSettingsService::getClientId()
|
|
{
|
|
// return _mqttClient.getClientId();
|
|
return _state.clientId.c_str();
|
|
}
|
|
|
|
PsychicMqttClient *MqttSettingsService::getMqttClient()
|
|
{
|
|
return &_mqttClient;
|
|
}
|
|
|
|
String MqttSettingsService::getLastError()
|
|
{
|
|
return _lastError;
|
|
}
|
|
|
|
void MqttSettingsService::onMqttConnect(bool sessionPresent)
|
|
{
|
|
ESP_LOGI("MQTT", "Connected to MQTT: %s", _mqttClient.getMqttConfig()->uri);
|
|
#ifdef SERIAL_INFO
|
|
Serial.printf("Connected to MQTT: %s\n", _mqttClient.getMqttConfig()->uri);
|
|
#endif
|
|
_lastError = "None";
|
|
}
|
|
|
|
void MqttSettingsService::onMqttDisconnect(bool sessionPresent)
|
|
{
|
|
ESP_LOGI("MQTT", "Disconnected from MQTT.");
|
|
#ifdef SERIAL_INFO
|
|
Serial.println("Disconnected from MQTT.");
|
|
#endif
|
|
}
|
|
|
|
void MqttSettingsService::onMqttError(esp_mqtt_error_codes_t error)
|
|
{
|
|
if (error.error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT)
|
|
{
|
|
_lastError = strerror(error.esp_transport_sock_errno);
|
|
ESP_LOGE("MQTT", "MQTT TCP error: %s", _lastError.c_str());
|
|
}
|
|
}
|
|
|
|
void MqttSettingsService::onConfigUpdated()
|
|
{
|
|
_reconfigureMqtt = true;
|
|
}
|
|
|
|
void MqttSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
|
|
{
|
|
if (_state.enabled)
|
|
{
|
|
ESP_LOGI("MQTT", "WiFi connection established, starting MQTT client.");
|
|
onConfigUpdated();
|
|
}
|
|
}
|
|
|
|
void MqttSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info)
|
|
{
|
|
if (_state.enabled)
|
|
{
|
|
ESP_LOGI("MQTT", "WiFi connection dropped, stopping MQTT client.");
|
|
onConfigUpdated();
|
|
}
|
|
}
|
|
|
|
void MqttSettingsService::configureMqtt()
|
|
{
|
|
// disconnect if currently connected
|
|
_mqttClient.disconnect();
|
|
|
|
// only connect if WiFi is connected and MQTT is enabled
|
|
if (_state.enabled && WiFi.isConnected())
|
|
{
|
|
#ifdef SERIAL_INFO
|
|
Serial.println("Connecting to MQTT...");
|
|
#endif
|
|
_mqttClient.setServer(retainCstr(_state.uri.c_str(), &_retainedHost));
|
|
if (_state.username.length() > 0)
|
|
{
|
|
_mqttClient.setCredentials(
|
|
retainCstr(_state.username.c_str(), &_retainedUsername),
|
|
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
|
|
}
|
|
else
|
|
{
|
|
_mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword));
|
|
}
|
|
_mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId));
|
|
_mqttClient.setKeepAlive(_state.keepAlive);
|
|
_mqttClient.setCleanSession(_state.cleanSession);
|
|
_mqttClient.connect();
|
|
}
|
|
}
|