diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 6c21ec375..34ce60043 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -46,6 +46,7 @@ - Improved API. Restful HTTP API works in the same way as MQTT calls - Removed settings for MQTT subscribe format [#173](https://github.com/emsesp/EMS-ESP32/issues/173) - Improve Nefit Moduline 200 functionality [#183](https://github.com/emsesp/EMS-ESP32/issues/183) +- `status` in the MQTT heartbeat renamed to `bus_status` ## **BREAKING CHANGES** @@ -55,3 +56,5 @@ - HA: # removed from counts in MQTT Fails, Rx fails, Rx received, Tx fails, Tx reads & Tx writes - `txread` renamed to `txreads` and `txwrite` renamed to `txwrites` in MQTT heartbeat payload - 'dallas sensors' in api/system/info moved to the "System" section. Renamed "uptime (seconds)" and "reset reason" +- `status` in the MQTT heartbeat renamed to `bus_status` + diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index f3ca772b5..2465104ed 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -1101,7 +1101,7 @@ void EMSdevice::publish_mqtt_ha_entity_config() { #if defined(EMSESP_STANDALONE) // debug messages to go with the test called 'dv' if (strcmp(read_flash_string(dv.short_name).c_str(), "wwseltemp") == 0) { - EMSESP::logger().warning(F("! init: wwseltemp state=%d, active=%d config_created=%d"), + EMSESP::logger().warning(F("publish_mqtt_ha_entity_config: wwseltemp state=%d, active=%d config_created=%d"), dv.get_state(), dv.has_state(DV_ACTIVE), dv.has_state(DV_HA_CONFIG_CREATED)); diff --git a/src/emsesp.cpp b/src/emsesp.cpp index a15e76fe1..bd7ce5cca 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -437,7 +437,6 @@ void EMSESP::publish_all_loop() { Mqtt::ha_status(); } system_.send_heartbeat(); - shower_.send_mqtt_stat(false, true); break; default: // all finished diff --git a/src/locale_EN.h b/src/locale_EN.h index f36878d5a..8ccf6da7b 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -370,7 +370,7 @@ MAKE_PSTR_LIST(enum_progMode4, F("prog_a"), F("prog_b"), F("prog_c"), F("prog_d" MAKE_PSTR_LIST(enum_solarmode, F_(constant), F("pwm"), F("analog")) MAKE_PSTR_LIST(enum_collectortype, F("flat"), F("vacuum")) -// MQTT topics and full text for values and commands +// MQTT topic for homeassistant. Must include / MAKE_PSTR(homeassistant, "homeassistant/") // id used to store the device ID. empty full name so only gets displayed in the MQTT payload diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 2018d4264..698424a6c 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -545,8 +545,8 @@ void Mqtt::on_connect() { } // send initial MQTT messages for some of our services - EMSESP::shower_.send_mqtt_stat(false); // Send shower_activated as false - EMSESP::system_.send_heartbeat(); // send heatbeat + EMSESP::shower_.set_shower_state(false, true); // Send shower_activated as false + EMSESP::system_.send_heartbeat(); // send heatbeat // re-subscribe to all custom registered MQTT topics resubscribe(); @@ -749,7 +749,7 @@ void Mqtt::publish_ha(const std::string & topic) { LOG_DEBUG(F("[DEBUG] Publishing empty HA topic=%s"), fulltopic.c_str()); #endif - publish(topic); // call it immediately, don't queue it + publish(fulltopic); // call it immediately, don't queue it } // publish a Home Assistant config topic and payload, with retain flag off. @@ -758,9 +758,6 @@ void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) { return; } - // empty payload will remove the previous config - // publish(topic); - std::string payload_text; payload_text.reserve(measureJson(payload) + 1); serializeJson(payload, payload_text); // convert json to string @@ -908,7 +905,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevi // if we're asking to remove this topic, send an empty payload // https://github.com/emsesp/EMS-ESP32/issues/196 if (remove) { - LOG_WARNING(F("Device value %s gone silent. Removing HA config topic %s"), uniq.c_str(), topic); + LOG_WARNING(F("Lost device value for %s. Removing HA config"), uniq.c_str()); publish_ha(topic); return; } diff --git a/src/shower.cpp b/src/shower.cpp index 228f21d9b..79d2eb6b0 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -28,7 +28,7 @@ void Shower::start() { shower_alert_ = settings.shower_alert; }); - send_mqtt_stat(false); + set_shower_state(false, true); // turns shower to off and creates HA topic if not already done } void Shower::loop() { @@ -49,14 +49,13 @@ void Shower::loop() { timer_pause_ = 0; // remove any last pauses doing_cold_shot_ = false; duration_ = 0; - shower_on_ = false; + shower_state_ = false; } else { // hot water has been on for a while // first check to see if hot water has been on long enough to be recognized as a Shower/Bath - if (!shower_on_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) { - shower_on_ = true; - send_mqtt_stat(true); - publish_values(); + if (!shower_state_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) { + set_shower_state(true); + publish_shower_data(); LOG_DEBUG(F("[Shower] hot water still running, starting shower timer")); } // check if the shower has been on too long @@ -77,18 +76,27 @@ void Shower::loop() { if ((timer_pause_ - timer_start_) > SHOWER_OFFSET_TIME) { duration_ = (timer_pause_ - timer_start_ - SHOWER_OFFSET_TIME); if (duration_ > SHOWER_MIN_DURATION) { - send_mqtt_stat(false); + publish_shower_data(); LOG_DEBUG(F("[Shower] finished with duration %d"), duration_); - publish_values(); } } +#if defined(EMSESP_DEBUG) + else { + if (shower_state_) { + Mqtt::publish("message", "shower state is ON"); + } else { + Mqtt::publish("message", "shower state is OFF"); + } + } +#endif // reset everything timer_start_ = 0; timer_pause_ = 0; - shower_on_ = false; doing_cold_shot_ = false; alert_timer_start_ = 0; + + set_shower_state(false); } } return; @@ -101,37 +109,6 @@ void Shower::loop() { } } -// send status of shower to MQTT -void Shower::send_mqtt_stat(bool state, bool force) { - if (!shower_timer_ && !shower_alert_) { - return; - } - - char s[7]; - Mqtt::publish(F("shower_active"), Helpers::render_boolean(s, state)); // https://github.com/emsesp/EMS-ESP/issues/369 - - // if we're in HA mode make sure we've first sent out the HA MQTT Discovery config topic - if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) { - ha_configdone_ = true; - - StaticJsonDocument doc; - doc["name"] = FJSON("Shower Active"); - doc["uniq_id"] = FJSON("shower_active"); - doc["~"] = Mqtt::base(); // default ems-esp - doc["stat_t"] = FJSON("~/shower_active"); - char result[10]; - doc[F("payload_on")] = Helpers::render_boolean(result, true); - doc[F("payload_off")] = Helpers::render_boolean(result, false); - JsonObject dev = doc.createNestedObject("dev"); - JsonArray ids = dev.createNestedArray("ids"); - ids.add("ems-esp"); - - char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::base().c_str()); - Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag - } -} - // turn back on the hot water for the shower void Shower::shower_alert_stop() { if (doing_cold_shot_) { @@ -150,9 +127,10 @@ void Shower::shower_alert_start() { } } -// Publish shower data -// returns true if added to MQTT queue went ok -void Shower::publish_values() { +// Publish to the shower_data topic +// showing whether the shower timer and alert are enabled or disabled +// and the duration of the last shower +void Shower::publish_shower_data() { StaticJsonDocument doc; char result[10]; @@ -169,4 +147,47 @@ void Shower::publish_values() { Mqtt::publish(F("shower_data"), doc.as()); } +// send status of shower to MQTT topic called shower_active - which is determined by the state parameter +// and creates the HA config topic if HA enabled +// force is used by EMSESP::publish_all_loop() +void Shower::set_shower_state(bool state, bool force) { + if (!shower_timer_ && !shower_alert_) { + return; + } + + // sets the state + shower_state_ = state; + + // only publish if that state has changed + static bool old_shower_state_; + if ((shower_state_ == old_shower_state_) && !force) { + return; + } + old_shower_state_ = shower_state_; // copy current state + + char s[7]; + Mqtt::publish(F("shower_active"), Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369 + + // send out HA MQTT Discovery config topic + if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) { + ha_configdone_ = true; + + StaticJsonDocument doc; + doc["name"] = FJSON("Shower Active"); + doc["uniq_id"] = FJSON("shower_active"); + doc["~"] = Mqtt::base(); // default ems-esp + doc["stat_t"] = FJSON("~/shower_active"); + char result[10]; + doc[F("payload_on")] = Helpers::render_boolean(result, true); + doc[F("payload_off")] = Helpers::render_boolean(result, false); + JsonObject dev = doc.createNestedObject("dev"); + JsonArray ids = dev.createNestedArray("ids"); + ids.add("ems-esp"); + + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::base().c_str()); + Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag + } +} + } // namespace emsesp diff --git a/src/shower.h b/src/shower.h index 7de573ec1..a9705923c 100644 --- a/src/shower.h +++ b/src/shower.h @@ -28,7 +28,7 @@ class Shower { void start(); void loop(); - void send_mqtt_stat(bool state, bool force = false); + void set_shower_state(bool state, bool force = false); bool shower_alert() const { return shower_alert_; @@ -55,14 +55,14 @@ class Shower { static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10000; // 10 seconds for cold water before turning back hot water static constexpr uint32_t SHOWER_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water - void publish_values(); + void publish_shower_data(); void shower_alert_start(); void shower_alert_stop(); bool shower_timer_; // true if we want to report back on shower times bool shower_alert_; // true if we want the alert of cold water bool ha_configdone_ = false; // for HA MQTT Discovery - bool shower_on_; + bool shower_state_; uint32_t timer_start_; // ms uint32_t timer_pause_; // ms uint32_t duration_; // ms diff --git a/src/system.cpp b/src/system.cpp index b27a2ea3e..1dd409e6d 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -497,13 +497,13 @@ void System::show_mem(const char * note) { // create the json for heartbeat bool System::heartbeat_json(JsonObject & output) { - uint8_t ems_status = EMSESP::bus_status(); - if (ems_status == EMSESP::BUS_STATUS_TX_ERRORS) { - output["status"] = FJSON("txerror"); - } else if (ems_status == EMSESP::BUS_STATUS_CONNECTED) { - output["status"] = FJSON("connected"); + uint8_t bus_status = EMSESP::bus_status(); + if (bus_status == EMSESP::BUS_STATUS_TX_ERRORS) { + output["bus_status"] = FJSON("txerror"); + } else if (bus_status == EMSESP::BUS_STATUS_CONNECTED) { + output["bus_status"] = FJSON("connected"); } else { - output["status"] = FJSON("disconnected"); + output["bus_status"] = FJSON("disconnected"); } output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); diff --git a/src/test/test.cpp b/src/test/test.cpp index d0a6fe8d8..e6fed390b 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -30,7 +30,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "general") == 0) { - EMSESP::logger().info(F("Testing general...")); + EMSESP::logger().info(F("Testing general. Adding a Boiler and Thermostat")); add_device(0x08, 123); // Nefit Trendline add_device(0x18, 157); // Bosch CR100 @@ -54,7 +54,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "310") == 0) { - EMSESP::logger().info(F("Testing GB072/RC310...")); + EMSESP::logger().info(F("Adding a GB072/RC310 combo...")); add_device(0x08, 123); // GB072 add_device(0x10, 158); // RC310 @@ -81,7 +81,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "gateway") == 0) { - EMSESP::logger().info(F("Testing gateway...")); + EMSESP::logger().info(F("Adding a Gateway...")); // add 0x48 KM200, via a version command rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00}); @@ -101,7 +101,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "mixer") == 0) { - EMSESP::logger().info(F("Testing mixer...")); + EMSESP::logger().info(F("Adding a mixer...")); // add controller add_device(0x09, 114); @@ -123,7 +123,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "boiler") == 0) { - EMSESP::logger().info(F("Testing boiler...")); + EMSESP::logger().info(F("Adding boiler...")); add_device(0x08, 123); // Nefit Trendline // UBAuptime @@ -140,7 +140,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "thermostat") == 0) { - EMSESP::logger().info(F("Testing thermostat...")); + EMSESP::logger().info(F("Adding thermostat...")); add_device(0x10, 192); // FW120 @@ -153,7 +153,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "solar") == 0) { - EMSESP::logger().info(F("Testing solar...")); + EMSESP::logger().info(F("Adding solar...")); add_device(0x30, 163); // SM100 @@ -172,7 +172,7 @@ bool Test::run_test(const char * command, int8_t id) { } if (strcmp(command, "heatpump") == 0) { - EMSESP::logger().info(F("Testing heatpump...")); + EMSESP::logger().info(F("Adding heatpump...")); add_device(0x38, 200); // Enviline module add_device(0x10, 192); // FW120 thermostat @@ -187,7 +187,8 @@ bool Test::run_test(const char * command, int8_t id) { return false; } -// used with the 'test' command, under su/admin +// These next tests are run from the Console +// using the test command void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { // switch to su shell.add_flags(CommandFlags::ADMIN); @@ -475,29 +476,27 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { shell.printfln(F("Testing device value rendering")); Mqtt::ha_enabled(true); - // Mqtt::ha_enabled(false); - Mqtt::nested_format(1); Mqtt::send_response(false); run_test("boiler"); run_test("thermostat"); - // shell.invoke_command("show"); - // change a value to null/bogus/dormant - // homeassistant/sensor/ems-esp/boiler_wwseltemp/config shell.invoke_command("call boiler wwseltemp"); shell.invoke_command("call system publish"); + } + + if (command == "dv2") { + shell.printfln(F("Testing device value lost")); // Boiler -> Me, UBAParameterWW(0x33) // wwseltemp = goes from 52 degrees (0x34) to void (0xFF) + // it should delete the HA config topic homeassistant/sensor/ems-esp/boiler_wwseltemp/config uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0xFF, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00}); shell.invoke_command("call boiler wwseltemp"); shell.invoke_command("call system publish"); - - // shell.invoke_command("show mqtt"); } if (command == "api") { diff --git a/src/version.h b/src/version.h index 215017a09..213091a59 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.3.0b8" +#define EMSESP_APP_VERSION "3.3.0b9"