diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index 7d2206ee4..992cce5f5 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -121,7 +121,7 @@ const de: Translation = { READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)', UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten', REMOTE_TIMEOUT: 'Timeout', - REMOTE_TIMEOUT_EN: 'Deaktitiere Remote bei fehlender Temperatur', + REMOTE_TIMEOUT_EN: 'Deaktiviere Remote bei fehlender Temperatur', HEATINGOFF: 'Heizen ausschalten beim EMS-ESP Start', MIN_DURATION: 'Dauer bis die Dusche erkannt wrid', ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren', diff --git a/src/roomcontrol.cpp b/src/roomcontrol.cpp index f789b72c0..1e958d206 100644 --- a/src/roomcontrol.cpp +++ b/src/roomcontrol.cpp @@ -34,7 +34,7 @@ uint32_t Roomctrl::timeout_ = 0; * set the temperature, */ void Roomctrl::set_timeout(uint8_t t) { - timeout_ = t * 3600; + timeout_ = t * 3600000; // ms } void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) { if (!type_[hc] && !type) { diff --git a/src/roomcontrol.h b/src/roomcontrol.h index 5d546e9ec..4dfe948ec 100644 --- a/src/roomcontrol.h +++ b/src/roomcontrol.h @@ -38,7 +38,6 @@ class Roomctrl { private: static constexpr uint32_t SEND_INTERVAL = 15000; // 15 sec - static constexpr uint32_t TIMEOUT = 86400000; // 24 hour static constexpr uint8_t HCS = 4; // max 4 heating circuits enum SendType : uint8_t { TEMP, HUMI }; diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 53b802c10..f39c4f09e 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -37,7 +37,9 @@ void WebCustomEntityService::begin() { // save a local pointer to the item list EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems_ = &webEntity.customEntityItems; }); EMSESP::logger().info("Starting Custom Entity service"); - Mqtt::subscribe(EMSdevice::DeviceType::CUSTOM, "custom/#", nullptr); // use empty function callback + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + snprintf(topic, sizeof(topic), "%s/#", F_(custom)); + Mqtt::subscribe(EMSdevice::DeviceType::CUSTOM, topic, nullptr); // use empty function callback } // this creates the entity file, saving it to the FS @@ -175,7 +177,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i publish(); } char cmd[COMMAND_MAX_LENGTH]; - snprintf(cmd, sizeof(cmd_function_p), "custom/%s", entityItem.name.c_str()); + snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entityItem.name.c_str()); EMSESP::webSchedulerService.onChange(cmd); return true; } @@ -260,7 +262,9 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) output[F_(info)] = Helpers::translated_word(FL_(info_cmd)); output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd)); for (const auto & entity : *customEntityItems_) { - output[entity.name] = "custom entity"; + if (entity.writeable) { + output[entity.name] = "custom entity"; + } } return true; } @@ -346,9 +350,9 @@ void WebCustomEntityService::publish_single(const CustomEntityItem & entity) { char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; if (Mqtt::publish_single2cmd()) { - snprintf(topic, sizeof(topic), "%s/%s", "custom", entity.name.c_str()); + snprintf(topic, sizeof(topic), "%s/%s", F_(custom), entity.name.c_str()); } else { - snprintf(topic, sizeof(topic), "%s/%s", "custom_data", entity.name.c_str()); + snprintf(topic, sizeof(topic), "%s_data/%s", F_(custom), entity.name.c_str()); } JsonDocument doc; @@ -386,7 +390,7 @@ void WebCustomEntityService::publish(const bool force) { if (Mqtt::ha_enabled() && !ha_registered_) { JsonDocument config; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/custom_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::base().c_str(), F_(custom)); config["stat_t"] = stat_t; char val_obj[50]; @@ -396,7 +400,7 @@ void WebCustomEntityService::publish(const bool force) { config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}"; char uniq_s[70]; - snprintf(uniq_s, sizeof(uniq_s), "custom_%s", entityItem.name.c_str()); + snprintf(uniq_s, sizeof(uniq_s), "%s_%s", F_(custom), entityItem.name.c_str()); config["obj_id"] = uniq_s; config["uniq_id"] = uniq_s; // same as object_id @@ -406,22 +410,22 @@ void WebCustomEntityService::publish(const bool force) { if (entityItem.writeable) { if (entityItem.value_type == DeviceValueType::BOOL) { - snprintf(topic, sizeof(topic), "switch/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "switch/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } else if (entityItem.value_type == DeviceValueType::STRING) { - snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } else if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT || Mqtt::discovery_type() == Mqtt::discoveryType::DOMOTICZ_LATEST) { - snprintf(topic, sizeof(topic), "number/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "number/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } else { - snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - snprintf(command_topic, sizeof(command_topic), "%s/custom/%s", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); config["cmd_t"] = command_topic; } else { if (entityItem.value_type == DeviceValueType::BOOL) { - snprintf(topic, sizeof(topic), "binary_sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } else { - snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str()); + snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name.c_str()); } } @@ -442,7 +446,7 @@ void WebCustomEntityService::publish(const bool force) { Mqtt::add_ha_uom(config.as(), entityItem.value_type, entityItem.uom); // add uom - Mqtt::add_ha_sections_to_doc("custom", stat_t, config, !ha_created, val_cond); + Mqtt::add_ha_sections_to_doc(F_(custom), stat_t, config, !ha_created, val_cond); ha_created |= Mqtt::queue_ha(topic, config.as()); } @@ -450,7 +454,9 @@ void WebCustomEntityService::publish(const bool force) { ha_registered_ = ha_created; if (output.size() > 0) { - Mqtt::queue_publish("custom_data", output); + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + sprintf(topic, "%s_data", F_(custom)); + Mqtt::queue_publish(topic, output); } // EMSESP::logger().debug("publish %d custom entities", output.size()); } @@ -598,7 +604,7 @@ bool WebCustomEntityService::get_value(std::shared_ptr telegram) has_change = true; } char cmd[COMMAND_MAX_LENGTH]; - snprintf(cmd, sizeof(cmd_function_p), "custom/%s", entity.name.c_str()); + snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entity.name.c_str()); EMSESP::webSchedulerService.onChange(cmd); } } else if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id @@ -615,7 +621,7 @@ bool WebCustomEntityService::get_value(std::shared_ptr telegram) has_change = true; } char cmd[COMMAND_MAX_LENGTH]; - snprintf(cmd, sizeof(cmd_function_p), "%s/%s", "custom", entity.name.c_str()); + snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entity.name.c_str()); EMSESP::webSchedulerService.onChange(cmd); } // EMSESP::logger().debug("custom entity %s received with value %d", entity.name.c_str(), (int)entity.val); diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 6f4515539..1a0a86c6e 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -34,7 +34,9 @@ void WebSchedulerService::begin() { EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems_ = &webScheduler.scheduleItems; }); EMSESP::logger().info("Starting Scheduler service"); - Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, "scheduler/#", nullptr); // use empty function callback + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + snprintf(topic, sizeof(topic), "%s/#", F_(scheduler)); + Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, topic, nullptr); // use empty function callback } // this creates the scheduler file, saving it to the FS @@ -261,7 +263,7 @@ void WebSchedulerService::publish(const bool force) { if (Mqtt::ha_enabled() && !ha_registered_) { JsonDocument config; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/scheduler_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::base().c_str(), F_(scheduler)); config["stat_t"] = stat_t; char val_obj[50]; @@ -271,7 +273,7 @@ void WebSchedulerService::publish(const bool force) { config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}"; char uniq_s[70]; - snprintf(uniq_s, sizeof(uniq_s), "scheduler_%s", scheduleItem.name.c_str()); + snprintf(uniq_s, sizeof(uniq_s), "%s_%s", F_(scheduler), scheduleItem.name.c_str()); config["obj_id"] = uniq_s; config["uniq_id"] = uniq_s; // same as object_id @@ -280,8 +282,8 @@ void WebSchedulerService::publish(const bool force) { char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.name.c_str()); - snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::base().c_str(), scheduleItem.name.c_str()); + snprintf(topic, sizeof(topic), "switch/%s/%s_%s/config", Mqtt::basename().c_str(), F_(scheduler), scheduleItem.name.c_str()); + snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(scheduler), scheduleItem.name.c_str()); config["cmd_t"] = command_topic; if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { @@ -296,7 +298,7 @@ void WebSchedulerService::publish(const bool force) { config["pl_off"] = Helpers::render_boolean(result, false); } - Mqtt::add_ha_sections_to_doc("scheduler", stat_t, config, !ha_created, val_cond); + Mqtt::add_ha_sections_to_doc(F_(scheduler), stat_t, config, !ha_created, val_cond); ha_created |= Mqtt::queue_ha(topic, config.as()); } @@ -304,7 +306,9 @@ void WebSchedulerService::publish(const bool force) { } ha_registered_ = ha_created; if (doc.size() > 0) { - Mqtt::queue_publish("scheduler_data", doc.as()); + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + snprintf(topic, sizeof(topic), "%s_data", F_(scheduler)); + Mqtt::queue_publish(topic, doc.as()); } } @@ -334,7 +338,7 @@ bool WebSchedulerService::command(const char * cmd, const char * data) { JsonObject output = doc_output.to(); // prefix "api/" to command string - char command_str[100]; + char command_str[COMMAND_MAX_LENGTH]; snprintf(command_str, sizeof(command_str), "/api/%s", cmd); uint8_t return_code = Command::process(command_str, true, input, output); // admin set @@ -368,7 +372,8 @@ bool WebSchedulerService::command(const char * cmd, const char * data) { bool WebSchedulerService::onChange(const char * cmd) { for (const ScheduleItem & scheduleItem : *scheduleItems_) { - if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE && Helpers::toLower(scheduleItem.time) == Helpers::toLower(cmd)) { + if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE + && Helpers::toLower(scheduleItem.time).find(Helpers::toLower(cmd)) != std::string::npos) { #ifdef EMESESP_DEBUG // emsesp::EMSESP::logger().debug(scheduleItem.cmd.c_str()); #endif diff --git a/src/web/shuntingYard.hpp b/src/web/shuntingYard.hpp index 62bf3ca60..208297878 100644 --- a/src/web/shuntingYard.hpp +++ b/src/web/shuntingYard.hpp @@ -371,16 +371,20 @@ int to_logic(const std::string & s) { return -1; } -// number to string +// number to string, remove trailing zeros std::string to_string(double d) { - if (d == static_cast(d)) { - return std::to_string(static_cast(d)); + std::string s = std::to_string(d); + while (!s.empty() && s.back() == '0') { + s.pop_back(); } - return std::to_string(d); + if (!s.empty() && s.back() == '.') { + s.pop_back(); + } + return s; } // RPN calculator -std::string compute(const std::string & expr) { +std::string calculate(const std::string & expr) { auto expr_new = emsesp::Helpers::toLower(expr); // emsesp::EMSESP::logger().info("calculate: %s", expr_new.c_str()); commands(expr_new); @@ -554,3 +558,16 @@ std::string compute(const std::string & expr) { } return stack.back(); } +// check for ? : +std::string compute(const std::string & expr) { + auto q = expr.find_first_of("?"); + auto p = expr.find_first_of(":", q); + if (p != std::string::npos && q != std::string::npos) { + if (calculate(expr.substr(0, q))[0] == '1') { + return calculate(expr.substr(q + 1, p - q - 1)); + } else { + return calculate(expr.substr(p + 1)); + } + } + return calculate(expr); +}