diff --git a/CHANGELOG.md b/CHANGELOG.md index 98691971b..db5451e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - add boiler wWType - support for uploading compressed firmware binaries - add wWType to MQTT publish +- option to set the MQTT retain flag ### Fixed - fix wwontime readback +- fixed support for RC300 via MQTT commands (#505) ### Changed - renamed wWCircPumpType to wWChargeType +- Installation doc moved to wiki ### Removed - diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index 0c10aabf2..a3189ea54 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -149,6 +149,16 @@ class MqttSettingsForm extends React.Component { 1 2 + + } + label="Retain Flag" + /> Publish Intervals diff --git a/interface/src/mqtt/types.ts b/interface/src/mqtt/types.ts index 7db88dbfd..1130bfa73 100644 --- a/interface/src/mqtt/types.ts +++ b/interface/src/mqtt/types.ts @@ -35,5 +35,6 @@ export interface MqttSettings { publish_time_sensor: number; mqtt_format: number; mqtt_qos: number; + mqtt_retain: boolean; system_heartbeat: boolean; } diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 15b10340f..e5a4928a1 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -193,6 +193,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) { root["publish_time_sensor"] = settings.publish_time_sensor; root["mqtt_format"] = settings.mqtt_format; root["mqtt_qos"] = settings.mqtt_qos; + root["mqtt_retain"] = settings.mqtt_retain; } StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & settings) { @@ -217,6 +218,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.mqtt_format = root["mqtt_format"] | EMSESP_DEFAULT_MQTT_FORMAT; newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS; + newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN; if (newSettings.system_heartbeat != settings.system_heartbeat) { emsesp::EMSESP::system_.set_heartbeat(newSettings.system_heartbeat); @@ -226,6 +228,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos); } + if (newSettings.mqtt_retain != settings.mqtt_retain) { + emsesp::EMSESP::mqtt_.set_retain(newSettings.mqtt_retain); + } + if (newSettings.publish_time_boiler != settings.publish_time_boiler) { emsesp::EMSESP::mqtt_.set_publish_time_boiler(newSettings.publish_time_boiler); } diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index 573a13b6c..f138989bb 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -63,9 +63,9 @@ static String generateClientId() { #define EMSESP_DEFAULT_SYSTEM_HEARTBEAT true #define EMSESP_DEFAULT_MQTT_FORMAT 2 // nested #define EMSESP_DEFAULT_MQTT_QOS 0 +#define EMSESP_DEFAULT_MQTT_RETAIN false #define EMSESP_DEFAULT_PUBLISH_TIME 10 - class MqttSettings { public: // host and port - if enabled @@ -92,9 +92,10 @@ class MqttSettings { uint16_t publish_time_mixing; uint16_t publish_time_other; uint16_t publish_time_sensor; - uint8_t mqtt_format; // 1=single, 2=nested, 3=ha, 4=custom + uint8_t mqtt_format; // 1=single, 2=nested, 3=ha, 4=custom uint8_t mqtt_qos; bool system_heartbeat; + bool mqtt_retain; static void read(MqttSettings & settings, JsonObject & root); static StateUpdateResult update(JsonObject & root, MqttSettings & settings); diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index d19b29424..f6007fd40 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -26,6 +26,7 @@ class DummySettings { uint16_t publish_time = 10; // seconds uint8_t mqtt_format = 3; // 1=single, 2=nested, 3=ha, 4=custom uint8_t mqtt_qos = 0; + bool mqtt_retain = false; String hostname = "ems-esp"; String jwtSecret = "ems-esp"; String ssid = "ems-esp"; diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 88e75cfed..834c824d7 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -600,7 +600,7 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) { std::string topic(100, '\0'); // e.g homeassistant/climate/hc1/thermostat/config snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/hc%d/config"), hc_num); // Mqtt::publish(topic); // empty payload, this remove any previous config sent to HA - Mqtt::publish(topic, doc, true); // publish the config payload with retain flag + Mqtt::publish_retain(topic, doc, true); // publish the config payload with retain flag // subscribe to the temp and mode commands snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/hc%d/cmd_temp"), hc_num); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index b33b5b980..2bfea6436 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -27,6 +27,7 @@ AsyncMqttClient * Mqtt::mqttClient_; // static parameters we make global std::string Mqtt::hostname_; uint8_t Mqtt::mqtt_qos_; +bool Mqtt::mqtt_retain_; uint8_t Mqtt::bus_id_; uint32_t Mqtt::publish_time_boiler_; uint32_t Mqtt::publish_time_thermostat_; @@ -373,6 +374,7 @@ void Mqtt::start() { publish_time_other_ = mqttSettings.publish_time_other * 1000; publish_time_sensor_ = mqttSettings.publish_time_sensor * 1000; mqtt_qos_ = mqttSettings.mqtt_qos; + mqtt_retain_ = mqttSettings.mqtt_retain; }); EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { bus_id_ = settings.ems_bus_id; }); @@ -470,6 +472,10 @@ void Mqtt::set_qos(uint8_t mqtt_qos) { mqtt_qos_ = mqtt_qos; } +void Mqtt::set_retain(bool mqtt_retain) { + mqtt_retain_ = mqtt_retain; +} + // MQTT onConnect - when a connect is established void Mqtt::on_connect() { // send info topic appended with the version information as JSON @@ -479,9 +485,9 @@ void Mqtt::on_connect() { #ifndef EMSESP_STANDALONE doc["ip"] = WiFi.localIP().toString(); #endif - publish(F("info"), doc, false); // send with retain off + publish(F("info"), doc); - publish(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on + publish_retain(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on reset_publish_fails(); // reset fail count to 0 @@ -533,30 +539,46 @@ std::shared_ptr Mqtt::queue_subscribe_message(const std::stri return queue_message(Operation::SUBSCRIBE, topic, "", false); // no payload } -// MQTT Publish, using a specific retain flag -void Mqtt::publish(const std::string & topic, const std::string & payload, bool retain) { - queue_publish_message(topic, payload, retain); +// MQTT Publish, using a user's retain flag +void Mqtt::publish(const std::string & topic, const std::string & payload) { + queue_publish_message(topic, payload, mqtt_retain_); } // MQTT Publish, using a specific retain flag, topic is a flash string -void Mqtt::publish(const __FlashStringHelper * topic, const std::string & payload, bool retain) { +void Mqtt::publish(const __FlashStringHelper * topic, const std::string & payload) { + queue_publish_message(uuid::read_flash_string(topic), payload, mqtt_retain_); +} + +void Mqtt::publish(const __FlashStringHelper * topic, const JsonDocument & payload) { + publish(uuid::read_flash_string(topic), payload); +} + +// MQTT Publish, using a specific retain flag, topic is a flash string, forcing retain flag +void Mqtt::publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain) { queue_publish_message(uuid::read_flash_string(topic), payload, retain); } -void Mqtt::publish(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain) { - publish(uuid::read_flash_string(topic), payload, retain); -} - -void Mqtt::publish(const std::string & topic, const JsonDocument & payload, bool retain) { +void Mqtt::publish_retain(const std::string & topic, const JsonDocument & payload, bool retain) { std::string payload_text; serializeJson(payload, payload_text); // convert json to string queue_publish_message(topic, payload_text, retain); } +void Mqtt::publish_retain(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain) { + publish_retain(uuid::read_flash_string(topic), payload, retain); +} + +void Mqtt::publish(const std::string & topic, const JsonDocument & payload) { + std::string payload_text; + serializeJson(payload, payload_text); // convert json to string + queue_publish_message(topic, payload_text, mqtt_retain_); +} + // for booleans, which get converted to string values 1 and 0 void Mqtt::publish(const std::string & topic, const bool value) { queue_publish_message(topic, value ? "1" : "0", false); } + void Mqtt::publish(const __FlashStringHelper * topic, const bool value) { queue_publish_message(uuid::read_flash_string(topic), value ? "1" : "0", false); } diff --git a/src/mqtt.h b/src/mqtt.h index e37b6502a..9a6b4f17c 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -74,6 +74,7 @@ class Mqtt { void set_publish_time_other(uint16_t publish_time); void set_publish_time_sensor(uint16_t publish_time); void set_qos(uint8_t mqtt_qos); + void set_retain(bool mqtt_retain); bool get_publish_onchange(uint8_t device_type); enum Operation { PUBLISH, SUBSCRIBE }; @@ -86,14 +87,18 @@ class Mqtt { static void add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb); - static void publish(const std::string & topic, const std::string & payload, bool retain = false); - static void publish(const std::string & topic, const JsonDocument & payload, bool retain = false); - static void publish(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain = false); - static void publish(const __FlashStringHelper * topic, const std::string & payload, bool retain = false); + static void publish(const std::string & topic, const std::string & payload); + static void publish(const std::string & topic, const JsonDocument & payload); + static void publish(const __FlashStringHelper * topic, const JsonDocument & payload); + static void publish(const __FlashStringHelper * topic, const std::string & payload); static void publish(const std::string & topic, const bool value); static void publish(const __FlashStringHelper * topi, const bool value); static void publish(const std::string & topic); + static void publish_retain(const std::string & topic, const JsonDocument & payload, bool retain); + static void publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain); + static void publish_retain(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain); + static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); @@ -163,7 +168,6 @@ class Mqtt { static size_t maximum_mqtt_messages_; static uint16_t mqtt_message_id_; - static bool mqtt_retain_; static constexpr size_t MAX_MQTT_MESSAGES = 20; // size of queue static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads @@ -208,6 +212,7 @@ class Mqtt { // settings, copied over static std::string hostname_; static uint8_t mqtt_qos_; + static bool mqtt_retain_; static uint32_t publish_time_; static uint8_t bus_id_; static uint32_t publish_time_boiler_; diff --git a/src/sensors.cpp b/src/sensors.cpp index c03af10a2..d98152bfb 100644 --- a/src/sensors.cpp +++ b/src/sensors.cpp @@ -342,7 +342,7 @@ void Sensors::publish_values() { config["uniq_id"] = str; snprintf_P(&topic[0], 50, PSTR("homeassistant/sensor/ems-esp/sensor%d/config"), i); - Mqtt::publish(topic, config, false); // publish the config payload with no retain flag + Mqtt::publish_retain(topic, config, false); // publish the config payload with no retain flag registered_ha_[i] = true; } diff --git a/src/system.cpp b/src/system.cpp index 3400df0d9..4fe892103 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -237,7 +237,7 @@ void System::send_heartbeat() { doc["rxfails"] = EMSESP::rxservice_.telegram_error_count(); doc["adc"] = analog_; //analogRead(A0); - Mqtt::publish(F("heartbeat"), doc, false); // send to MQTT with retain off. This will add to MQTT queue. + Mqtt::publish_retain(F("heartbeat"), doc, false); // send to MQTT with retain off. This will add to MQTT queue. } // measure and moving average adc @@ -726,6 +726,7 @@ bool System::check_upgrade() { mqttSettings.host = mqtt["ip"] | FACTORY_MQTT_HOST; mqttSettings.mqtt_format = (mqtt["nestedjson"] ? MQTT_format::NESTED : MQTT_format::SINGLE); mqttSettings.mqtt_qos = mqtt["qos"] | 0; + mqttSettings.mqtt_retain = mqtt["retain"]; mqttSettings.username = mqtt["user"] | ""; mqttSettings.password = mqtt["password"] | ""; mqttSettings.port = mqtt["port"] | FACTORY_MQTT_PORT;