mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-10 18:24:27 +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:
|
||||
HomeAssistantHelper() {}
|
||||
|
||||
HomeAssistantHelper(PubSubClient* client) {
|
||||
this->setClient(client);
|
||||
void setWriter() {
|
||||
this->writer = nullptr;
|
||||
}
|
||||
|
||||
void setClient() {
|
||||
this->client = nullptr;
|
||||
void setWriter(MqttWriter* writer) {
|
||||
this->writer = writer;
|
||||
}
|
||||
|
||||
void setClient(PubSubClient* client) {
|
||||
this->client = client;
|
||||
void setEventPublishCallback(std::function<void(const char*, bool)> callback) {
|
||||
this->eventPublishCallback = callback;
|
||||
}
|
||||
|
||||
void setYieldCallback(void(*yieldCallback)(void*)) {
|
||||
this->yieldCallback = yieldCallback;
|
||||
this->yieldArg = nullptr;
|
||||
void setEventPublishCallback() {
|
||||
this->eventPublishCallback = nullptr;
|
||||
}
|
||||
|
||||
void setYieldCallback(void(*yieldCallback)(void*), void* arg) {
|
||||
this->yieldCallback = yieldCallback;
|
||||
this->yieldArg = arg;
|
||||
void setDevicePrefix(const char* value) {
|
||||
this->devicePrefix = value;
|
||||
}
|
||||
|
||||
void setDevicePrefix(String value) {
|
||||
devicePrefix = value;
|
||||
void setDeviceVersion(const char* value) {
|
||||
this->deviceVersion = value;
|
||||
}
|
||||
|
||||
void setDeviceVersion(String value) {
|
||||
deviceVersion = value;
|
||||
void setDeviceManufacturer(const char* value) {
|
||||
this->deviceManufacturer = value;
|
||||
}
|
||||
|
||||
void setDeviceManufacturer(String value) {
|
||||
deviceManufacturer = value;
|
||||
void setDeviceModel(const char* value) {
|
||||
this->deviceModel = value;
|
||||
}
|
||||
|
||||
void setDeviceModel(String value) {
|
||||
deviceModel = value;
|
||||
void setDeviceName(const char* value) {
|
||||
this->deviceName = value;
|
||||
}
|
||||
|
||||
void setDeviceName(String value) {
|
||||
deviceName = value;
|
||||
}
|
||||
|
||||
void setDeviceConfigUrl(String value) {
|
||||
deviceConfigUrl = value;
|
||||
void setDeviceConfigUrl(const char* value) {
|
||||
this->deviceConfigUrl = value;
|
||||
}
|
||||
|
||||
bool publish(const char* topic, JsonDocument& doc) {
|
||||
if (this->client == nullptr) {
|
||||
if (this->writer == nullptr) {
|
||||
this->eventPublishCallback(topic, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_IDENTIFIERS)][0] = devicePrefix;
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_SW_VERSION)] = deviceVersion;
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_IDENTIFIERS)][0] = this->devicePrefix;
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_SW_VERSION)] = this->deviceVersion;
|
||||
|
||||
if (deviceManufacturer) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MANUFACTURER)] = deviceManufacturer;
|
||||
if (this->deviceManufacturer != nullptr) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MANUFACTURER)] = this->deviceManufacturer;
|
||||
}
|
||||
|
||||
if (deviceModel) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MODEL)] = deviceModel;
|
||||
if (this->deviceModel != nullptr) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_MODEL)] = this->deviceModel;
|
||||
}
|
||||
|
||||
if (deviceName) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_NAME)] = deviceName;
|
||||
if (this->deviceName != nullptr) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_NAME)] = this->deviceName;
|
||||
}
|
||||
|
||||
if (deviceConfigUrl) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_CONF_URL)] = deviceConfigUrl;
|
||||
if (this->deviceConfigUrl != nullptr) {
|
||||
doc[FPSTR(HA_DEVICE)][FPSTR(HA_CONF_URL)] = this->deviceConfigUrl;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
if (this->yieldCallback != nullptr) {
|
||||
this->yieldCallback(yieldArg);
|
||||
bool result = this->writer->publish(topic, doc, true);
|
||||
if (this->eventPublishCallback) {
|
||||
this->eventPublishCallback(topic, result);
|
||||
}
|
||||
|
||||
return docSize == written;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool publish(const char* topic) {
|
||||
if (this->client == nullptr) {
|
||||
if (this->writer == nullptr) {
|
||||
this->eventPublishCallback(topic, 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 = "";
|
||||
topic.concat(prefix);
|
||||
topic.concat("/");
|
||||
topic.concat(this->prefix);
|
||||
topic.concat('/');
|
||||
topic.concat(category);
|
||||
topic.concat("/");
|
||||
topic.concat(devicePrefix);
|
||||
topic.concat('/');
|
||||
topic.concat(this->devicePrefix);
|
||||
topic.concat(nameSeparator);
|
||||
topic.concat(name);
|
||||
topic.concat("/config");
|
||||
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:
|
||||
void(*yieldCallback)(void*) = nullptr;
|
||||
void* yieldArg = nullptr;
|
||||
PubSubClient* client = nullptr;
|
||||
String prefix = "homeassistant";
|
||||
String devicePrefix = "";
|
||||
String deviceVersion = "1.0";
|
||||
String deviceManufacturer = "Community";
|
||||
String deviceModel = "";
|
||||
String deviceName = "";
|
||||
String deviceConfigUrl = "";
|
||||
std::function<void(const char*, bool)> eventPublishCallback = nullptr;
|
||||
MqttWriter* writer = nullptr;
|
||||
const char* prefix = "homeassistant";
|
||||
const char* devicePrefix = "";
|
||||
const char* deviceVersion = "1.0";
|
||||
const char* deviceManufacturer = nullptr;
|
||||
const char* deviceModel = nullptr;
|
||||
const char* deviceName = nullptr;
|
||||
const char* deviceConfigUrl = nullptr;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user