mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 02:34:29 +05:00
mqtt refactoring, change version to 1.4.0-rc.1
* added MqttWriter * added MqttWiFiClient (modified WiFiClient for esp8266) * adaptation HomeAssistantHelper for MqttWriter * adaptation HaHelper for new HomeAssistantHelper
This commit is contained in:
@@ -5,133 +5,130 @@ class HomeAssistantHelper {
|
|||||||
public:
|
public:
|
||||||
HomeAssistantHelper() {}
|
HomeAssistantHelper() {}
|
||||||
|
|
||||||
HomeAssistantHelper(PubSubClient* client) {
|
void setWriter() {
|
||||||
this->setClient(client);
|
this->writer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setClient() {
|
void setWriter(MqttWriter* writer) {
|
||||||
this->client = nullptr;
|
this->writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setClient(PubSubClient* client) {
|
void setEventPublishCallback(std::function<void(const char*, bool)> callback) {
|
||||||
this->client = client;
|
this->eventPublishCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setYieldCallback(void(*yieldCallback)(void*)) {
|
void setEventPublishCallback() {
|
||||||
this->yieldCallback = yieldCallback;
|
this->eventPublishCallback = nullptr;
|
||||||
this->yieldArg = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setYieldCallback(void(*yieldCallback)(void*), void* arg) {
|
void setDevicePrefix(const char* value) {
|
||||||
this->yieldCallback = yieldCallback;
|
this->devicePrefix = value;
|
||||||
this->yieldArg = arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDevicePrefix(String value) {
|
void setDeviceVersion(const char* value) {
|
||||||
devicePrefix = value;
|
this->deviceVersion = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeviceVersion(String value) {
|
void setDeviceManufacturer(const char* value) {
|
||||||
deviceVersion = value;
|
this->deviceManufacturer = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeviceManufacturer(String value) {
|
void setDeviceModel(const char* value) {
|
||||||
deviceManufacturer = value;
|
this->deviceModel = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeviceModel(String value) {
|
void setDeviceName(const char* value) {
|
||||||
deviceModel = value;
|
this->deviceName = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDeviceName(String value) {
|
void setDeviceConfigUrl(const char* value) {
|
||||||
deviceName = value;
|
this->deviceConfigUrl = value;
|
||||||
}
|
|
||||||
|
|
||||||
void setDeviceConfigUrl(String value) {
|
|
||||||
deviceConfigUrl = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publish(const char* topic, JsonDocument& doc) {
|
bool publish(const char* topic, JsonDocument& doc) {
|
||||||
if (this->client == nullptr) {
|
if (this->writer == nullptr) {
|
||||||
|
this->eventPublishCallback(topic, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_IDENTIFIERS)][0] = devicePrefix;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_IDENTIFIERS)][0] = this->devicePrefix;
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_SW_VERSION)] = deviceVersion;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_SW_VERSION)] = this->deviceVersion;
|
||||||
|
|
||||||
if (deviceManufacturer) {
|
if (this->deviceManufacturer != nullptr) {
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MANUFACTURER)] = deviceManufacturer;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MANUFACTURER)] = this->deviceManufacturer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceModel) {
|
if (this->deviceModel != nullptr) {
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MODEL)] = deviceModel;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MODEL)] = this->deviceModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceName) {
|
if (this->deviceName != nullptr) {
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_NAME)] = deviceName;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_NAME)] = this->deviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceConfigUrl) {
|
if (this->deviceConfigUrl != nullptr) {
|
||||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_CONF_URL)] = deviceConfigUrl;
|
doc[FPSTR(HA_DEVICE)][FPSTR(HA_CONF_URL)] = this->deviceConfigUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool result = this->writer->publish(topic, doc, true);
|
||||||
size_t docSize = measureJson(doc);
|
if (this->eventPublishCallback) {
|
||||||
uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer));
|
this->eventPublishCallback(topic, result);
|
||||||
size_t length = serializeJson(doc, buffer, docSize);
|
|
||||||
|
|
||||||
size_t written = 0;
|
|
||||||
if (length != 0) {
|
|
||||||
if (this->client->beginPublish(topic, docSize, true)) {
|
|
||||||
for (size_t offset = 0; offset < docSize; offset += 128) {
|
|
||||||
size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset;
|
|
||||||
written += this->client->write(buffer + offset, packetSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->client->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic);
|
|
||||||
|
|
||||||
if (this->yieldCallback != nullptr) {
|
|
||||||
this->yieldCallback(yieldArg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return docSize == written;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publish(const char* topic) {
|
bool publish(const char* topic) {
|
||||||
if (this->client == nullptr) {
|
if (this->writer == nullptr) {
|
||||||
|
this->eventPublishCallback(topic, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return client->publish(topic, NULL, true);
|
bool result = writer->publish(topic, nullptr, 0, true);
|
||||||
|
if (this->eventPublishCallback) {
|
||||||
|
this->eventPublishCallback(topic, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTopic(const char* category, const char* name, const char* nameSeparator = "/") {
|
String getTopic(const char* category, const char* name, char nameSeparator = '/') {
|
||||||
String topic = "";
|
String topic = "";
|
||||||
topic.concat(prefix);
|
topic.concat(this->prefix);
|
||||||
topic.concat("/");
|
topic.concat('/');
|
||||||
topic.concat(category);
|
topic.concat(category);
|
||||||
topic.concat("/");
|
topic.concat('/');
|
||||||
topic.concat(devicePrefix);
|
topic.concat(this->devicePrefix);
|
||||||
topic.concat(nameSeparator);
|
topic.concat(nameSeparator);
|
||||||
topic.concat(name);
|
topic.concat(name);
|
||||||
topic.concat("/config");
|
topic.concat("/config");
|
||||||
return topic;
|
return topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T> String getDeviceTopic(T value, char separator = '/') {
|
||||||
|
String topic = "";
|
||||||
|
topic.concat(this->devicePrefix);
|
||||||
|
topic.concat(separator);
|
||||||
|
topic.concat(value);
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> String getObjectId(T value, char separator = '_') {
|
||||||
|
String topic = "";
|
||||||
|
topic.concat(this->devicePrefix);
|
||||||
|
topic.concat(separator);
|
||||||
|
topic.concat(value);
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void(*yieldCallback)(void*) = nullptr;
|
std::function<void(const char*, bool)> eventPublishCallback = nullptr;
|
||||||
void* yieldArg = nullptr;
|
MqttWriter* writer = nullptr;
|
||||||
PubSubClient* client = nullptr;
|
const char* prefix = "homeassistant";
|
||||||
String prefix = "homeassistant";
|
const char* devicePrefix = "";
|
||||||
String devicePrefix = "";
|
const char* deviceVersion = "1.0";
|
||||||
String deviceVersion = "1.0";
|
const char* deviceManufacturer = nullptr;
|
||||||
String deviceManufacturer = "Community";
|
const char* deviceModel = nullptr;
|
||||||
String deviceModel = "";
|
const char* deviceName = nullptr;
|
||||||
String deviceName = "";
|
const char* deviceConfigUrl = nullptr;
|
||||||
String deviceConfigUrl = "";
|
|
||||||
};
|
};
|
||||||
|
|||||||
24
lib/MqttWriter/MqttWiFiClient.h
Normal file
24
lib/MqttWriter/MqttWiFiClient.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include <WiFiClient.h>
|
||||||
|
|
||||||
|
class MqttWiFiClient : public WiFiClient {
|
||||||
|
public:
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
void flush() override {
|
||||||
|
if (this->connected()) {
|
||||||
|
WiFiClient::flush(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() override {
|
||||||
|
this->abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
void setSync(bool) {}
|
||||||
|
|
||||||
|
bool getSync() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
221
lib/MqttWriter/MqttWriter.h
Normal file
221
lib/MqttWriter/MqttWriter.h
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class MqttWriter {
|
||||||
|
public:
|
||||||
|
MqttWriter(PubSubClient* client, size_t bufferSize = 64) {
|
||||||
|
this->client = client;
|
||||||
|
this->bufferSize = bufferSize;
|
||||||
|
this->buffer = (uint8_t*) malloc(bufferSize * sizeof(*this->buffer));
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
this->mutex = new std::mutex();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~MqttWriter() {
|
||||||
|
free(this->buffer);
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delete this->mutex;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void setYieldCallback(std::function<void()> callback) {
|
||||||
|
this->yieldCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setYieldCallback() {
|
||||||
|
this->yieldCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEventPublishCallback(std::function<void(const char*, size_t, size_t, bool)> callback) {
|
||||||
|
this->eventPublishCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEventPublishCallback() {
|
||||||
|
this->eventPublishCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEventFlushCallback(std::function<void(size_t, size_t)> callback) {
|
||||||
|
this->eventFlushCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEventFlushCallback() {
|
||||||
|
this->eventFlushCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
if (!this->mutex->try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (this->isLocked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this->locked = true;
|
||||||
|
this->writeAfterLock = 0;
|
||||||
|
this->lockedTime = millis();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLocked() {
|
||||||
|
return this->locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
this->locked = false;
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
this->mutex->unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool publish(const char* topic, JsonDocument& doc, bool retained = false) {
|
||||||
|
if (!this->client->connected()) {
|
||||||
|
this->bufferPos = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!this->lock()) {
|
||||||
|
if (this->yieldCallback) {
|
||||||
|
this->yieldCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bufferPos = 0;
|
||||||
|
size_t docSize = measureJson(doc);
|
||||||
|
size_t written = 0;
|
||||||
|
if (this->client->beginPublish(topic, docSize, retained)) {
|
||||||
|
serializeJson(doc, *this);
|
||||||
|
doc.clear();
|
||||||
|
this->flush();
|
||||||
|
|
||||||
|
written = this->writeAfterLock;
|
||||||
|
}
|
||||||
|
this->unlock();
|
||||||
|
|
||||||
|
if (this->eventPublishCallback) {
|
||||||
|
this->eventPublishCallback(topic, written, docSize, written == docSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return written == docSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool publish(const char* topic, const char* buffer, bool retained = false) {
|
||||||
|
return this->publish(topic, (uint8_t*) buffer, strlen(buffer), retained);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool publish(const char* topic, const uint8_t* buffer, size_t length, bool retained = false) {
|
||||||
|
if (!this->client->connected()) {
|
||||||
|
this->bufferPos = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!this->lock()) {
|
||||||
|
if (this->yieldCallback) {
|
||||||
|
this->yieldCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->bufferPos = 0;
|
||||||
|
size_t written = 0;
|
||||||
|
bool result = false;
|
||||||
|
if (length == 0) {
|
||||||
|
result = this->client->publish(topic, nullptr, 0, retained);
|
||||||
|
|
||||||
|
} else if (this->client->beginPublish(topic, length, retained)) {
|
||||||
|
this->write(buffer, length);
|
||||||
|
this->flush();
|
||||||
|
|
||||||
|
written = this->writeAfterLock;
|
||||||
|
result = written == length;
|
||||||
|
}
|
||||||
|
this->unlock();
|
||||||
|
|
||||||
|
if (this->eventPublishCallback) {
|
||||||
|
this->eventPublishCallback(topic, written, length, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write(uint8_t c) {
|
||||||
|
this->buffer[this->bufferPos++] = c;
|
||||||
|
|
||||||
|
if (this->bufferPos >= this->bufferSize) {
|
||||||
|
this->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write(const uint8_t* buffer, size_t length) {
|
||||||
|
size_t written = 0;
|
||||||
|
while (written < length) {
|
||||||
|
size_t copySize = this->bufferSize - this->bufferPos;
|
||||||
|
if (written + copySize > length) {
|
||||||
|
copySize = length - written;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(this->buffer + this->bufferPos, buffer + written, copySize);
|
||||||
|
this->bufferPos += copySize;
|
||||||
|
|
||||||
|
if (this->bufferPos >= this->bufferSize) {
|
||||||
|
this->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
written += copySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flush() {
|
||||||
|
if (this->bufferPos == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->client->connected()) {
|
||||||
|
this->bufferPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length = this->bufferPos;
|
||||||
|
size_t written = this->client->write(this->buffer, length);
|
||||||
|
this->client->flush();
|
||||||
|
this->bufferPos = 0;
|
||||||
|
|
||||||
|
if (this->isLocked()) {
|
||||||
|
this->writeAfterLock += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->eventFlushCallback) {
|
||||||
|
this->eventFlushCallback(written, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return written == length;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PubSubClient* client;
|
||||||
|
uint8_t* buffer;
|
||||||
|
size_t bufferSize = 64;
|
||||||
|
size_t bufferPos = 0;
|
||||||
|
bool locked = false;
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
mutable std::mutex* mutex;
|
||||||
|
#endif
|
||||||
|
unsigned long lockedTime = 0;
|
||||||
|
size_t writeAfterLock = 0;
|
||||||
|
std::function<void()> yieldCallback = nullptr;
|
||||||
|
std::function<void(const char*, size_t, size_t, bool)> eventPublishCallback = nullptr;
|
||||||
|
std::function<void(size_t, size_t)> eventFlushCallback = nullptr;
|
||||||
|
};
|
||||||
@@ -32,7 +32,7 @@ build_flags =
|
|||||||
-D USE_TELNET=1
|
-D USE_TELNET=1
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
version = 1.4.0
|
version = 1.4.0-rc.1
|
||||||
|
|
||||||
; Defaults
|
; Defaults
|
||||||
[esp8266_defaults]
|
[esp8266_defaults]
|
||||||
|
|||||||
667
src/HaHelper.h
667
src/HaHelper.h
File diff suppressed because it is too large
Load Diff
182
src/MqttTask.h
182
src/MqttTask.h
@@ -1,5 +1,7 @@
|
|||||||
#include <WiFiClient.h>
|
#include <WiFiClient.h>
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
|
#include <MqttWiFiClient.h>
|
||||||
|
#include <MqttWriter.h>
|
||||||
#include "HaHelper.h"
|
#include "HaHelper.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -20,15 +22,20 @@ public:
|
|||||||
delete this->client;
|
delete this->client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->writer != nullptr) {
|
||||||
|
delete this->writer;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->wifiClient != nullptr) {
|
if (this->wifiClient != nullptr) {
|
||||||
delete this->wifiClient;
|
delete this->wifiClient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WiFiClient* wifiClient = nullptr;
|
MqttWiFiClient* wifiClient = nullptr;
|
||||||
PubSubClient* client = nullptr;
|
PubSubClient* client = nullptr;
|
||||||
HaHelper* haHelper = nullptr;
|
HaHelper* haHelper = nullptr;
|
||||||
|
MqttWriter* writer = nullptr;
|
||||||
unsigned short readyForSendTime = 15000;
|
unsigned short readyForSendTime = 15000;
|
||||||
unsigned long lastReconnectTime = 0;
|
unsigned long lastReconnectTime = 0;
|
||||||
unsigned long connectedTime = 0;
|
unsigned long connectedTime = 0;
|
||||||
@@ -57,12 +64,39 @@ protected:
|
|||||||
void setup() {
|
void setup() {
|
||||||
Log.sinfoln("MQTT", F("Started"));
|
Log.sinfoln("MQTT", F("Started"));
|
||||||
|
|
||||||
|
this->wifiClient = new MqttWiFiClient();
|
||||||
|
this->wifiClient->setSync(true);
|
||||||
|
|
||||||
// client settings
|
// client settings
|
||||||
this->client = new PubSubClient();
|
this->client = new PubSubClient();
|
||||||
this->client->setSocketTimeout(2);
|
this->client->setClient(*this->wifiClient);
|
||||||
this->client->setKeepAlive(5);
|
this->client->setSocketTimeout(3);
|
||||||
|
this->client->setKeepAlive(15);
|
||||||
this->client->setBufferSize(768);
|
this->client->setBufferSize(768);
|
||||||
this->client->setCallback(std::bind(&MqttTask::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
this->client->setCallback([this] (char* topic, uint8_t* payload, unsigned int length) {
|
||||||
|
this->onMessage(topic, payload, length);
|
||||||
|
});
|
||||||
|
|
||||||
|
// writer
|
||||||
|
this->writer = new MqttWriter(this->client, 256);
|
||||||
|
this->writer->setYieldCallback([this] {
|
||||||
|
this->delay(10);
|
||||||
|
});
|
||||||
|
this->writer->setEventPublishCallback([this] (const char* topic, size_t written, size_t length, bool result) {
|
||||||
|
Log.straceln("MQTT", F("%s publish %u of %u bytes to topic: %s"), result ? F("Successfully") : F("Failed"), written, length, topic);
|
||||||
|
|
||||||
|
this->client->loop();
|
||||||
|
this->delay(100);
|
||||||
|
});
|
||||||
|
this->writer->setEventFlushCallback([this] (size_t, size_t) {
|
||||||
|
if (!this->wifiClient->getSync() && this->wifiClient->connected()) {
|
||||||
|
this->wifiClient->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
::yield();
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
// ha helper settings
|
// ha helper settings
|
||||||
this->haHelper = new HaHelper();
|
this->haHelper = new HaHelper();
|
||||||
@@ -70,44 +104,29 @@ protected:
|
|||||||
this->haHelper->setDeviceVersion(PROJECT_VERSION);
|
this->haHelper->setDeviceVersion(PROJECT_VERSION);
|
||||||
this->haHelper->setDeviceModel(PROJECT_NAME);
|
this->haHelper->setDeviceModel(PROJECT_NAME);
|
||||||
this->haHelper->setDeviceName(PROJECT_NAME);
|
this->haHelper->setDeviceName(PROJECT_NAME);
|
||||||
this->haHelper->setClient(this->client);
|
this->haHelper->setWriter(this->writer);
|
||||||
this->haHelper->setYieldCallback([](void* self) {
|
|
||||||
MqttTask* task = static_cast<MqttTask*>(self);
|
|
||||||
task->client->loop();
|
|
||||||
task->delay(100);
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
sprintf(buffer, CONFIG_URL, WiFi.localIP().toString().c_str());
|
sprintf(buffer, CONFIG_URL, WiFi.localIP().toString().c_str());
|
||||||
this->haHelper->setDeviceConfigUrl(buffer);
|
this->haHelper->setDeviceConfigUrl(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (this->wifiClient == nullptr || (!this->client->connected() && millis() - this->lastReconnectTime >= MQTT_RECONNECT_INTERVAL)) {
|
|
||||||
Log.sinfoln("MQTT", F("Not connected, state: %d"), this->client->state());
|
|
||||||
|
|
||||||
// bug?
|
|
||||||
// memory leak at random times if this is not done
|
|
||||||
if (this->wifiClient != nullptr) {
|
|
||||||
delete this->wifiClient;
|
|
||||||
this->wifiClient = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->wifiClient = new WiFiClient();
|
|
||||||
this->client->setClient(*this->wifiClient);
|
|
||||||
this->client->setServer(settings.mqtt.server, settings.mqtt.port);
|
|
||||||
|
|
||||||
Log.sinfoln("MQTT", F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
|
||||||
this->client->connect(settings.hostname, settings.mqtt.user, settings.mqtt.password);
|
|
||||||
|
|
||||||
this->lastReconnectTime = millis();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->client->connected() && this->connected) {
|
if (!this->client->connected() && this->connected) {
|
||||||
this->connected = false;
|
this->connected = false;
|
||||||
this->onDisconnect();
|
this->onDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
} else if (this->client->connected() && !this->connected) {
|
if (this->wifiClient == nullptr || (!this->client->connected() && millis() - this->lastReconnectTime >= MQTT_RECONNECT_INTERVAL)) {
|
||||||
|
Log.sinfoln("MQTT", F("Not connected, state: %d"), this->client->state());
|
||||||
|
Log.sinfoln("MQTT", F("Connecting to %s:%u..."), settings.mqtt.server, settings.mqtt.port);
|
||||||
|
|
||||||
|
this->client->setServer(settings.mqtt.server, settings.mqtt.port);
|
||||||
|
this->client->connect(settings.hostname, settings.mqtt.user, settings.mqtt.password);
|
||||||
|
|
||||||
|
this->lastReconnectTime = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->client->connected() && !this->connected) {
|
||||||
this->connected = true;
|
this->connected = true;
|
||||||
this->onConnect();
|
this->onConnect();
|
||||||
}
|
}
|
||||||
@@ -132,21 +151,19 @@ protected:
|
|||||||
|
|
||||||
// publish variables and status
|
// publish variables and status
|
||||||
if (this->newConnection || millis() - this->prevPubVarsTime > settings.mqtt.interval) {
|
if (this->newConnection || millis() - this->prevPubVarsTime > settings.mqtt.interval) {
|
||||||
this->client->publish(
|
this->writer->publish(
|
||||||
this->getTopicPath("status").c_str(),
|
this->haHelper->getDeviceTopic("status").c_str(),
|
||||||
!vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online"
|
!vars.states.otStatus ? "offline" : vars.states.fault ? "fault" : "online",
|
||||||
|
true
|
||||||
);
|
);
|
||||||
this->client->loop();
|
|
||||||
|
|
||||||
this->publishVariables(this->getTopicPath("state").c_str());
|
this->publishVariables(this->haHelper->getDeviceTopic("state").c_str());
|
||||||
this->client->loop();
|
|
||||||
this->prevPubVarsTime = millis();
|
this->prevPubVarsTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish settings
|
// publish settings
|
||||||
if (this->newConnection || millis() - this->prevPubSettingsTime > settings.mqtt.interval * 10) {
|
if (this->newConnection || millis() - this->prevPubSettingsTime > settings.mqtt.interval * 10) {
|
||||||
this->publishSettings(this->getTopicPath("settings").c_str());
|
this->publishSettings(this->haHelper->getDeviceTopic("settings").c_str());
|
||||||
this->client->loop();
|
|
||||||
this->prevPubSettingsTime = millis();
|
this->prevPubSettingsTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +190,8 @@ protected:
|
|||||||
Log.sinfoln("MQTT", F("Emergency mode disabled"));
|
Log.sinfoln("MQTT", F("Emergency mode disabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
this->client->subscribe(this->getTopicPath("settings/set").c_str());
|
this->client->subscribe(this->haHelper->getDeviceTopic("settings/set").c_str());
|
||||||
this->client->subscribe(this->getTopicPath("state/set").c_str());
|
this->client->subscribe(this->haHelper->getDeviceTopic("state/set").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnect() {
|
void onDisconnect() {
|
||||||
@@ -184,7 +201,7 @@ protected:
|
|||||||
Log.swarningln("MQTT", F("Disconnected (reason: %d uptime: %u s.)"), this->client->state(), uptime);
|
Log.swarningln("MQTT", F("Disconnected (reason: %d uptime: %u s.)"), this->client->state(), uptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMessage(char* topic, byte* payload, unsigned int length) {
|
void onMessage(char* topic, uint8_t* payload, unsigned int length) {
|
||||||
if (!length) {
|
if (!length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -212,37 +229,16 @@ protected:
|
|||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
DeserializationError dErr = deserializeJson(doc, payload, length);
|
DeserializationError dErr = deserializeJson(doc, payload, length);
|
||||||
if (dErr != DeserializationError::Ok || doc.isNull()) {
|
if (dErr != DeserializationError::Ok || doc.isNull()) {
|
||||||
const char* errMsg;
|
Log.swarningln("MQTT.MSG", F("Error on deserialization: %s"), dErr.f_str());
|
||||||
switch (dErr.code()) {
|
|
||||||
case DeserializationError::EmptyInput:
|
|
||||||
case DeserializationError::IncompleteInput:
|
|
||||||
case DeserializationError::InvalidInput:
|
|
||||||
errMsg = "invalid input";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeserializationError::NoMemory:
|
|
||||||
errMsg = "no memory";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeserializationError::TooDeep:
|
|
||||||
errMsg = "too deep";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
errMsg = "failed";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Log.swarningln("MQTT.MSG", F("No deserialization: %s"), errMsg);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->getTopicPath("state/set").compare(topic) == 0) {
|
if (this->haHelper->getDeviceTopic("state/set").equals(topic)) {
|
||||||
this->client->publish(this->getTopicPath("state/set").c_str(), NULL, true);
|
this->writer->publish(this->haHelper->getDeviceTopic("state/set").c_str(), nullptr, 0, true);
|
||||||
this->updateVariables(doc);
|
this->updateVariables(doc);
|
||||||
|
|
||||||
} else if (this->getTopicPath("settings/set").compare(topic) == 0) {
|
} else if (this->haHelper->getDeviceTopic("settings/set").equals(topic)) {
|
||||||
this->client->publish(this->getTopicPath("settings/set").c_str(), NULL, true);
|
this->writer->publish(this->haHelper->getDeviceTopic("settings/set").c_str(), nullptr, 0, true);
|
||||||
this->updateSettings(doc);
|
this->updateSettings(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,27 +723,7 @@ protected:
|
|||||||
doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type;
|
doc["sensors"]["indoor"]["type"] = settings.sensors.indoor.type;
|
||||||
doc["sensors"]["indoor"]["offset"] = settings.sensors.indoor.offset;
|
doc["sensors"]["indoor"]["offset"] = settings.sensors.indoor.offset;
|
||||||
|
|
||||||
|
return this->writer->publish(topic, doc, true);
|
||||||
size_t docSize = measureJson(doc);
|
|
||||||
uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer));
|
|
||||||
size_t length = serializeJson(doc, buffer, docSize);
|
|
||||||
|
|
||||||
size_t written = 0;
|
|
||||||
if (length != 0) {
|
|
||||||
if (this->client->beginPublish(topic, docSize, true)) {
|
|
||||||
for (size_t offset = 0; offset < docSize; offset += 128) {
|
|
||||||
size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset;
|
|
||||||
written += this->client->write(buffer + offset, packetSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->client->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic);
|
|
||||||
|
|
||||||
return docSize == written;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool publishVariables(const char* topic) {
|
bool publishVariables(const char* topic) {
|
||||||
@@ -782,30 +758,6 @@ protected:
|
|||||||
doc["parameters"]["dhwMinTemp"] = vars.parameters.dhwMinTemp;
|
doc["parameters"]["dhwMinTemp"] = vars.parameters.dhwMinTemp;
|
||||||
doc["parameters"]["dhwMaxTemp"] = vars.parameters.dhwMaxTemp;
|
doc["parameters"]["dhwMaxTemp"] = vars.parameters.dhwMaxTemp;
|
||||||
|
|
||||||
|
return this->writer->publish(topic, doc, true);
|
||||||
size_t docSize = measureJson(doc);
|
|
||||||
uint8_t* buffer = (uint8_t*) malloc(docSize * sizeof(*buffer));
|
|
||||||
size_t length = serializeJson(doc, buffer, docSize);
|
|
||||||
|
|
||||||
size_t written = 0;
|
|
||||||
if (length != 0) {
|
|
||||||
if (this->client->beginPublish(topic, docSize, true)) {
|
|
||||||
for (size_t offset = 0; offset < docSize; offset += 128) {
|
|
||||||
size_t packetSize = offset + 128 <= docSize ? 128 : docSize - offset;
|
|
||||||
written += this->client->write(buffer + offset, packetSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->client->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
Log.straceln("MQTT", "Publish %u of %u bytes to topic: %s", written, docSize, topic);
|
|
||||||
|
|
||||||
return docSize == written;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string getTopicPath(const char* topic) {
|
|
||||||
return std::string(settings.mqtt.prefix) + "/" + std::string(topic);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#define PROJECT_NAME "OpenTherm Gateway"
|
#define PROJECT_NAME "OpenTherm Gateway"
|
||||||
#define PROJECT_VERSION "1.4.0"
|
#define PROJECT_VERSION "1.4.0-rc.1"
|
||||||
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
|
||||||
#define AP_SSID "OpenTherm Gateway"
|
#define AP_SSID "OpenTherm Gateway"
|
||||||
#define AP_PASSWORD "otgateway123456"
|
#define AP_PASSWORD "otgateway123456"
|
||||||
|
|||||||
Reference in New Issue
Block a user