From d54afeb2f73a0244834833281744c98b5e623f76 Mon Sep 17 00:00:00 2001 From: andreibodrov Date: Tue, 2 Jul 2024 15:33:10 +0300 Subject: [PATCH] text dynamic lambas --- esphome/components/template/text/__init__.py | 13 +++- .../template/text/template_text.cpp | 62 +++++++++++++++++++ .../components/template/text/template_text.h | 11 ++++ esphome/const.py | 1 + 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/esphome/components/template/text/__init__.py b/esphome/components/template/text/__init__.py index f73b240197..a185cf396e 100644 --- a/esphome/components/template/text/__init__.py +++ b/esphome/components/template/text/__init__.py @@ -1,7 +1,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import text +from esphome.components import text, text_sensor from esphome.const import ( CONF_INITIAL_VALUE, CONF_LAMBDA, @@ -11,9 +11,12 @@ from esphome.const import ( CONF_MIN_LENGTH, CONF_PATTERN, CONF_SET_ACTION, + CONF_DYNAMIC_LAMBDA, ) from .. import template_ns +AUTO_LOAD = ["text_sensor"] + TemplateText = template_ns.class_("TemplateText", text.Text, cg.PollingComponent) TextSaverBase = template_ns.class_("TemplateTextSaverBase") @@ -49,13 +52,14 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(TemplateText), cv.Optional(CONF_MIN_LENGTH, default=0): cv.int_range(min=0, max=255), - cv.Optional(CONF_MAX_LENGTH, default=255): cv.int_range(min=0, max=255), + cv.Optional(CONF_MAX_LENGTH, default=255): cv.int_range(min=0, max=1024), cv.Optional(CONF_PATTERN): cv.string, cv.Optional(CONF_LAMBDA): cv.returning_lambda, cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_INITIAL_VALUE): cv.string_strict, cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, + cv.Optional(CONF_DYNAMIC_LAMBDA): text_sensor.text_sensor_schema(), } ).extend(cv.polling_component_schema("60s")), validate, @@ -85,6 +89,11 @@ async def to_code(config): args = cg.TemplateArguments(config[CONF_MAX_LENGTH]) saver = TextSaverTemplate.template(args).new() cg.add(var.set_value_saver(saver)) + if CONF_DYNAMIC_LAMBDA in config: + cg.add(var.set_dynamic(True)) + sens = await text_sensor.new_text_sensor(config[CONF_DYNAMIC_LAMBDA]) + cg.add(var.set_lambda_result(sens)) + cg.add_library("jingoro2112/wrench", "6.0.3") if CONF_SET_ACTION in config: await automation.build_automation( diff --git a/esphome/components/template/text/template_text.cpp b/esphome/components/template/text/template_text.cpp index fb0208e198..ed662a5d6d 100644 --- a/esphome/components/template/text/template_text.cpp +++ b/esphome/components/template/text/template_text.cpp @@ -1,11 +1,38 @@ #include "template_text.h" +#include "esphome/core/application.h" #include "esphome/core/log.h" +#include + namespace esphome { namespace template_ { static const char *const TAG = "template.text"; +void print(WRContext *c, const WRValue *argv, const int argn, WRValue &retVal, void *usr) { + char buf[255]; + for (int i = 0; i < argn; ++i) { + printf("%s", argv[i].asString(buf, 255)); + } +} + +void switches(WRContext *c, const WRValue *argv, const int argn, WRValue &retVal, void *usr) { + std::vector sw = App.get_switches(); + char buf[255]; + + for (int i = 0; i < argn; ++i) { + printf("%s", argv[i].asString(buf, 255)); + } + + for (unsigned int i = 0; i < sw.size(); i++) { + ESP_LOGD(TAG, "switch: %s", sw[i]->get_name().c_str()); + ESP_LOGD(TAG, "switch: %i", sw[i]->state); + if (strcmp(sw[i]->get_name().c_str(), buf) == 0) { + wr_makeInt(&retVal, sw[i]->state); + } + }; +} + void TemplateText::setup() { if (!(this->f_ == nullptr)) { if (this->f_.has_value()) @@ -26,9 +53,21 @@ void TemplateText::setup() { } if (!value.empty()) this->publish_state(value); + + if (this->dynamic_) { + ESP_LOGD(TAG, "Initiate wrench"); + this->w = wr_newState(); + wr_registerFunction(this->w, "print", print); + wr_registerFunction(this->w, "switches", switches); + } } void TemplateText::update() { + ESP_LOGD(TAG, "update"); + + if (this->dynamic_) { + this->execute_wrench(this->state); + } if (this->f_ == nullptr) return; @@ -42,9 +81,31 @@ void TemplateText::update() { this->publish_state(*val); } +void TemplateText::execute_wrench(const std::string &value) { + int err = wr_compile(value.c_str(), strlen(value.c_str()), &this->outBytes, &this->outLen); // compile it + + ESP_LOGD(TAG, "wrench err: %i", err); + if (err == 0) { + WRContext *gc = wr_run(this->w, this->outBytes, this->outLen); // load and run the code! + + WRValue *g = wr_getGlobalRef(gc, "result"); + + if (g) { + char buf[1024]; + this->lambda_result_->publish_state(g->asString(buf, 1024)); + } + + delete[] outBytes; // clean up + } +} + void TemplateText::control(const std::string &value) { this->set_trigger_->trigger(value); + if (this->dynamic_) { + this->execute_wrench(value); + } + if (this->optimistic_) this->publish_state(value); @@ -57,6 +118,7 @@ void TemplateText::control(const std::string &value) { void TemplateText::dump_config() { LOG_TEXT("", "Template Text Input", this); ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); + ESP_LOGCONFIG(TAG, " Dynamic: %s", YESNO(this->dynamic_)); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/template/text/template_text.h b/esphome/components/template/text/template_text.h index bcfc54a2ba..31ad3fdccf 100644 --- a/esphome/components/template/text/template_text.h +++ b/esphome/components/template/text/template_text.h @@ -1,9 +1,11 @@ #pragma once +#include #include "esphome/components/text/text.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "wrench.h" namespace esphome { namespace template_ { @@ -72,15 +74,24 @@ class TemplateText : public text::Text, public PollingComponent { void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } void set_initial_value(const std::string &initial_value) { this->initial_value_ = initial_value; } void set_value_saver(TemplateTextSaverBase *restore_value_saver) { this->pref_ = restore_value_saver; } + void set_dynamic(bool dynamic) { this->dynamic_ = dynamic; } + void set_lambda_result(text_sensor::TextSensor *lambda_result) { this->lambda_result_ = lambda_result; } protected: void control(const std::string &value) override; bool optimistic_ = false; + bool dynamic_ = true; std::string initial_value_; Trigger *set_trigger_ = new Trigger(); optional()>> f_{nullptr}; + void execute_wrench(const std::string &value); TemplateTextSaverBase *pref_ = nullptr; + text_sensor::TextSensor *lambda_result_{nullptr}; + // text_sensor::TextSensor *lambda_result_nullptr}; + unsigned char *outBytes; // compiled code is alloc'ed + int outLen; + WRState *w; }; } // namespace template_ diff --git a/esphome/const.py b/esphome/const.py index a13a0af8eb..df7c007ec1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -236,6 +236,7 @@ CONF_DUMMY_RECEIVER = "dummy_receiver" CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id" CONF_DUMP = "dump" CONF_DURATION = "duration" +CONF_DYNAMIC_LAMBDA = "dynamic_lambda" CONF_EAP = "eap" CONF_EC = "ec" CONF_ECHO_PIN = "echo_pin"