diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index 9c4703636..80958ad0c 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -116,7 +116,6 @@ class MqttSettingsForm extends React.Component { Single Nested Home Assistant - Custom getParam(F_(id))->value(); } + if (id.isEmpty()) { + id = "-1"; + } + DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE); JsonObject output = doc.to(); bool ok = false; // execute the command if (data.isEmpty()) { - ok = Command::call(device_type, cmd.c_str(), nullptr, -1, output); // command only + ok = Command::call(device_type, cmd.c_str(), nullptr, id.toInt(), output); // command only } else { if (api_enabled) { // we only allow commands with parameters if the API is enabled - if (id.isEmpty()) { - ok = Command::call(device_type, cmd.c_str(), data.c_str(), -1, output); // only ID - } else { - ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), output); // has cmd, data and id - } + ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), output); // has cmd, data and id } else { request->send(401, "text/plain", F("Unauthorized")); return; diff --git a/src/command.cpp b/src/command.cpp index 10f07226c..1ca9999aa 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -40,7 +40,7 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is %d in %s"), cmd, value, id, dname.c_str()); } #endif - + bool ok = false; if (!cmdfunctions_.empty()) { for (const auto & cf : cmdfunctions_) { if (cf.device_type_ == device_type) { @@ -50,16 +50,17 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val // check if json object is empty, if so quit if (output.isNull()) { LOG_WARNING(F("Ignore call for command %s in %s because no json"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); + return false; } - return ((cf.cmdfunction_json_)(value, id, output)); + ok |= ((cf.cmdfunction_json_)(value, id, output)); } else { - return ((cf.cmdfunction_)(value, id)); + ok |= ((cf.cmdfunction_)(value, id)); } } } } } - return false; // command not found + return ok; } // add a command to the list, which does not return json diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp index 065b66fad..bef10a197 100644 --- a/src/devices/mixing.cpp +++ b/src/devices/mixing.cpp @@ -69,22 +69,22 @@ void Mixing::device_info_web(JsonArray & root) { // fetch the values into a JSON document StaticJsonDocument doc; JsonObject output = doc.to(); - if (!export_values(output)) { + if (!export_values(Mqtt::Format::SINGLE, output)) { return; // empty } char prefix_str[10]; - if (type() == Type::WWC) { - snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(wwc %d) "), hc_); - print_value_json(root, F("wwTemp"), FPSTR(prefix_str), F_(wwTemp), F_(degrees), output); - print_value_json(root, F("pumpStatus"), FPSTR(prefix_str), F_(pumpStatus), nullptr, output); - print_value_json(root, F("tempStatus"), FPSTR(prefix_str), F_(tempStatus), nullptr, output); - } else { + if (type() == Type::HC) { snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(hc %d) "), hc_); print_value_json(root, F("flowTemp"), FPSTR(prefix_str), F_(flowTemp), F_(degrees), output); print_value_json(root, F("flowSetTemp"), FPSTR(prefix_str), F_(flowSetTemp), F_(degrees), output); print_value_json(root, F("pumpStatus"), FPSTR(prefix_str), F_(pumpStatus), nullptr, output); print_value_json(root, F("valveStatus"), FPSTR(prefix_str), F_(valveStatus), F_(percent), output); + } else { + snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(wwc %d) "), hc_); + print_value_json(root, F("wwTemp"), FPSTR(prefix_str), F_(wwTemp), F_(degrees), output); + print_value_json(root, F("pumpStatus"), FPSTR(prefix_str), F_(pumpStatus), nullptr, output); + print_value_json(root, F("tempStatus"), FPSTR(prefix_str), F_(tempStatus), nullptr, output); } } @@ -108,28 +108,32 @@ void Mixing::show_values(uuid::console::Shell & shell) { // fetch the values into a JSON document StaticJsonDocument doc; JsonObject output = doc.to(); - if (!export_values(output)) { + if (!export_values(Mqtt::Format::SINGLE, output)) { return; // empty } - if (type() == Type::WWC) { - shell.println(F_(ww_hc)); - print_value_json(shell, F("wwTemp"), nullptr, F_(wwTemp), F_(degrees), output); - print_value_json(shell, F("pumpStatus"), nullptr, F_(pumpStatus), nullptr, output); - print_value_json(shell, F("tempStatus"), nullptr, F_(tempStatus), nullptr, output); + if (type() == Type::HC) { + shell.printfln(F_(hc), hc_); + print_value_json(shell, F("flowTemp"), F(" "), F_(flowTemp), F_(degrees), output); + print_value_json(shell, F("flowSetTemp"), F(" "), F_(flowSetTemp), F_(degrees), output); + print_value_json(shell, F("pumpStatus"), F(" "), F_(pumpStatus), nullptr, output); + print_value_json(shell, F("valveStatus"), F(" "), F_(valveStatus), F_(percent), output); } else { - shell.println(F_(hc)); - print_value_json(shell, F("flowTemp"), nullptr, F_(flowTemp), F_(degrees), output); - print_value_json(shell, F("flowSetTemp"), nullptr, F_(flowSetTemp), F_(degrees), output); - print_value_json(shell, F("pumpStatus"), nullptr, F_(pumpStatus), nullptr, output); - print_value_json(shell, F("valveStatus"), nullptr, F_(valveStatus), F_(percent), output); + shell.printfln(F_(ww_hc), hc_); + print_value_json(shell, F("wwTemp"), F(" "), F_(wwTemp), F_(degrees), output); + print_value_json(shell, F("pumpStatus"), F(" "), F_(pumpStatus), nullptr, output); + print_value_json(shell, F("tempStatus"), F(" "), F_(tempStatus), nullptr, output); } shell.println(); } +// export all valuet to info command bool Mixing::command_info(const char * value, const int8_t id, JsonObject & output) { - return (export_values(output)); + if (id != (device_id() - 0x20 + 1) && id > 0) { // defaults to first hc if no id + return false; + } + return (export_values(Mqtt::Format::NESTED, output)); } // publish values via MQTT @@ -137,7 +141,7 @@ bool Mixing::command_info(const char * value, const int8_t id, JsonObject & outp void Mixing::publish_values() { StaticJsonDocument doc; JsonObject output = doc.to(); - if (export_values(output)) { + if (export_values(Mqtt::mqtt_format(), output)) { char topic[30]; char s[5]; strlcpy(topic, "mixing_data", 30); @@ -189,43 +193,49 @@ void Mixing::register_mqtt_ha_config(const char * topic) { // creates JSON doc from values // returns false if empty -bool Mixing::export_values(JsonObject & output) { - switch (this->type()) { - case Type::HC: - output["type"] = "hc"; +bool Mixing::export_values(uint8_t mqtt_format, JsonObject & output) { + JsonObject output_hc; + char hc_name[10]; // hc{1-4} + + if (this->type() == Type::HC) { + snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc_); + if ((mqtt_format == Mqtt::Format::NESTED)) { + output_hc = output.createNestedObject(hc_name); + } else { + output_hc = output; + output["type"] = "hc"; + } if (Helpers::hasValue(flowTemp_)) { - output["flowTemp"] = (float)flowTemp_ / 10; + output_hc["flowTemp"] = (float)flowTemp_ / 10; } if (Helpers::hasValue(flowSetTemp_)) { - output["flowSetTemp"] = flowSetTemp_; + output_hc["flowSetTemp"] = flowSetTemp_; } if (Helpers::hasValue(pumpStatus_)) { char s[5]; // for formatting strings - output["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL); + output_hc["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL); } if (Helpers::hasValue(status_)) { - output["valveStatus"] = status_; + output_hc["valveStatus"] = status_; + } + } else { + snprintf_P(hc_name, sizeof(hc_name), PSTR("wwc%d"), hc_); + if ((mqtt_format == Mqtt::Format::NESTED)) { + output_hc = output.createNestedObject(hc_name); + } else { + output_hc = output; + output["type"] = "wwc"; } - break; - - case Type::WWC: - output["type"] = "wwc"; if (Helpers::hasValue(flowTemp_)) { - output["wwTemp"] = (float)flowTemp_ / 10; + output_hc["wwTemp"] = (float)flowTemp_ / 10; } if (Helpers::hasValue(pumpStatus_)) { char s[5]; // for formatting strings - output["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL); + output_hc["pumpStatus"] = Helpers::render_value(s, pumpStatus_, EMS_VALUE_BOOL); } if (Helpers::hasValue(status_)) { - output["tempStatus"] = status_; + output_hc["tempStatus"] = status_; } - break; - - case Type::NONE: - default: - return false; - break; } return output.size(); @@ -285,8 +295,8 @@ void Mixing::process_MMStatusMessage(std::shared_ptr telegram) { // 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module // see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918 hc_ = device_id() - 0x20 + 1; - changed_ |= telegram->read_value(flowTemp_, 1); // is * 10 - changed_ |= telegram->read_bitvalue(pumpStatus_, 3, 0); + changed_ |= telegram->read_value(flowTemp_, 1); // is * 10 + changed_ |= telegram->read_bitvalue(pumpStatus_, 3, 2); // is 0 or 0x64 (100%), check only bit 2 changed_ |= telegram->read_value(flowSetTemp_, 0); changed_ |= telegram->read_value(status_, 4); // valve status -100 to 100 } diff --git a/src/devices/mixing.h b/src/devices/mixing.h index 27d4a68e9..9d8307d19 100644 --- a/src/devices/mixing.h +++ b/src/devices/mixing.h @@ -44,7 +44,7 @@ class Mixing : public EMSdevice { private: static uuid::log::Logger logger_; - bool export_values(JsonObject & doc); + bool export_values(uint8_t mqtt_format, JsonObject & doc); void register_mqtt_ha_config(const char * topic); bool command_info(const char * value, const int8_t id, JsonObject & output); diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 59ebff1cd..d9c2b8718 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -182,6 +182,8 @@ void Thermostat::device_info_web(JsonArray & root) { print_value_json(root, F("minexttemp"), nullptr, F_(minexttemp), F_(degrees), output_main); print_value_json(root, F("building"), nullptr, F_(building), nullptr, output_main); print_value_json(root, F("wwmode"), nullptr, F_(wwmode), nullptr, output_main); + print_value_json(root, F("wwtemp"), nullptr, F_(wwtemp), nullptr, output_main); + print_value_json(root, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, output_main); print_value_json(root, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, output_main); } @@ -192,13 +194,10 @@ void Thermostat::device_info_web(JsonArray & root) { for (const auto & hc : heating_circuits_) { if (hc->is_active()) { char prefix_str[10]; - snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(hc %d) "), hc->hc_num()); + snprintf_P(prefix_str, sizeof(prefix_str), PSTR("hc%d"), hc->hc_num()); + JsonObject output = output_hc[prefix_str]; - char hc_name[10]; // hc{1-4} - strlcpy(hc_name, "hc", 10); - char s[3]; - strlcat(hc_name, Helpers::itoa(s, hc->hc_num()), 10); - JsonObject output = output_hc[hc_name]; + snprintf_P(prefix_str, sizeof(prefix_str), PSTR("(hc %d) "), hc->hc_num()); print_value_json(root, F("seltemp"), FPSTR(prefix_str), F_(seltemp), F_(degrees), output); print_value_json(root, F("currtemp"), FPSTR(prefix_str), F_(currtemp), F_(degrees), output); @@ -263,6 +262,8 @@ void Thermostat::show_values(uuid::console::Shell & shell) { print_value_json(shell, F("minexttemp"), nullptr, F_(minexttemp), F_(degrees), output_main); print_value_json(shell, F("building"), nullptr, F_(building), nullptr, output_main); print_value_json(shell, F("wwmode"), nullptr, F_(wwmode), nullptr, output_main); + print_value_json(shell, F("wwtemp"), nullptr, F_(wwtemp), nullptr, output_main); + print_value_json(shell, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, output_main); print_value_json(shell, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, output_main); } @@ -274,12 +275,10 @@ void Thermostat::show_values(uuid::console::Shell & shell) { // display for each active heating circuit for (const auto & hc : heating_circuits_) { if (hc->is_active()) { - shell.printfln(" Heating Circuit %d:", hc->hc_num()); + shell.printfln(F_(hc), hc->hc_num()); char hc_name[10]; // hc{1-4} - strlcpy(hc_name, "hc", 10); - char s[3]; - strlcat(hc_name, Helpers::itoa(s, hc->hc_num()), 10); + snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc->hc_num()); JsonObject output = output_hc[hc_name]; print_value_json(shell, F("seltemp"), F(" "), F_(seltemp), F_(degrees), output); @@ -423,6 +422,16 @@ bool Thermostat::export_values_main(JsonObject & rootThermostat) { } } + // Warm water temp + if (Helpers::hasValue(wwTemp_)) { + rootThermostat["wwtemp"] = wwTemp_; + } + + // Warm water low temp + if (Helpers::hasValue(wwTempLow_)) { + rootThermostat["wwtemplow"] = wwTempLow_; + } + // Warm Water circulation mode if (Helpers::hasValue(wwCircMode_)) { char s[7]; @@ -546,7 +555,7 @@ bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermost // Summer mode if (Helpers::hasValue(hc->summer_setmode)) { char s[7]; - dataThermostat["summermode"] = Helpers::render_enum(s, {"off", "auto", "on"}, hc->summer_setmode); + dataThermostat["summermode"] = Helpers::render_enum(s, {"summer", "auto", "winter"}, hc->summer_setmode); } // mode - always force showing this when in HA so not to break HA's climate component @@ -1710,7 +1719,7 @@ bool Thermostat::set_summermode(const char * value, const int8_t id) { return false; } uint8_t set = 0xFF; - if (!Helpers::value2enum(value, set, {"off", "auto", "on"})) { + if (!Helpers::value2enum(value, set, {"summer", "auto", "winter"})) { LOG_WARNING(F("Setting summer mode: Invalid mode")); return false; } diff --git a/src/locale_EN.h b/src/locale_EN.h index 646fe33e2..604794d76 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -214,11 +214,11 @@ MAKE_PSTR(tankHeated, "Tank Heated") MAKE_PSTR(collectorShutdown, "Collector shutdown") // mixing -MAKE_PSTR(ww_hc, "Warm Water Circuit") +MAKE_PSTR(ww_hc, " Warm Water Circuit %d:") MAKE_PSTR(wwTemp, "Current warm water temperature") MAKE_PSTR(pumpStatus, "Current pump status") MAKE_PSTR(tempStatus, "Current temperature status") -MAKE_PSTR(hc, "Heating Circuit") +MAKE_PSTR(hc, " Heating Circuit %d:") MAKE_PSTR(flowTemp, "Current flow temperature") MAKE_PSTR(flowSetTemp, "Setpoint flow temperature") @@ -234,6 +234,8 @@ MAKE_PSTR(intoffset, "Offset int. temperature") MAKE_PSTR(minexttemp, "Min ext. temperature") MAKE_PSTR(building, "Building") MAKE_PSTR(wwmode, "Warm water mode") +MAKE_PSTR(wwtemp, "Warm water high temperature") +MAKE_PSTR(wwtemplow, "Warm water low temperature") MAKE_PSTR(wwcircmode, "Warm Water circulation mode") // thermostat - per heating circuit diff --git a/src/mqtt.h b/src/mqtt.h index 80fd8a94c..d5d180dda 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -79,7 +79,7 @@ class Mqtt { enum Operation { PUBLISH, SUBSCRIBE }; - enum Format : uint8_t { NONE = 0, SINGLE, NESTED, HA, CUSTOM }; + enum Format : uint8_t { NONE = 0, SINGLE, NESTED, HA }; static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 100; diff --git a/src/sensor.cpp b/src/sensor.cpp index bdddcf0de..de3ebd57f 100644 --- a/src/sensor.cpp +++ b/src/sensor.cpp @@ -325,10 +325,7 @@ void Sensor::publish_values() { for (const auto & device : devices_) { char s[7]; - if (mqtt_format_ == Mqtt::Format::CUSTOM) { - // e.g. sensor_data = {28-EA41-9497-0E03-5F":23.30,"28-233D-9497-0C03-8B":24.0} - doc[device.to_string()] = Helpers::render_value(s, device.temperature_c, 1); - } else if ((mqtt_format_ == Mqtt::Format::NESTED) || (mqtt_format_ == Mqtt::Format::HA)) { + if ((mqtt_format_ == Mqtt::Format::NESTED) || (mqtt_format_ == Mqtt::Format::HA)) { // e.g. sensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.30"},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":"24.0"}} char sensorID[20]; // sensor{1-n} strlcpy(sensorID, "sensor", 20); @@ -372,9 +369,7 @@ void Sensor::publish_values() { i++; // increment sensor count } - if (mqtt_format_ != Mqtt::Format::SINGLE) { - Mqtt::publish(F("sensor_data"), doc.as()); - } + Mqtt::publish(F("sensor_data"), doc.as()); } } // namespace emsesp \ No newline at end of file