Add support for TextPanels grabbing their content from Sensors and TextSensors

This commit is contained in:
Michael Davidson 2024-01-02 17:32:41 +11:00
parent c018ba2ac3
commit 4e37a09310
No known key found for this signature in database
GPG Key ID: B8D1A99712B8B0EB
3 changed files with 89 additions and 17 deletions

View File

@ -3,6 +3,8 @@
#include "esphome/components/display/display.h" #include "esphome/components/display/display.h"
#include "esphome/components/display/rect.h" #include "esphome/components/display/rect.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <iomanip>
#include <sstream>
namespace esphome { namespace esphome {
namespace graphical_layout { namespace graphical_layout {
@ -14,10 +16,47 @@ static const int TEXT_ALIGN_Y_MASK =
void TextPanel::dump_config(int indent_depth, int additional_level_depth) { void TextPanel::dump_config(int indent_depth, int additional_level_depth) {
this->dump_config_base_properties(TAG, indent_depth); this->dump_config_base_properties(TAG, indent_depth);
std::string text = this->text_.value(); std::string text = this->text_input_.value();
ESP_LOGCONFIG(TAG, "%*sText Align: %s", indent_depth, "", ESP_LOGCONFIG(TAG, "%*sText Align: %s", indent_depth, "",
LOG_STR_ARG(display::text_align_to_string(this->text_align_))); LOG_STR_ARG(display::text_align_to_string(this->text_align_)));
ESP_LOGCONFIG(TAG, "%*sText: %s", indent_depth, "", text.c_str()); ESP_LOGCONFIG(TAG, "%*sText: %s", indent_depth, "", text.c_str());
if (this->sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, "%*sSensor: %s", indent_depth, "", this->sensor_->get_name());
}
if (this->text_sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, "%*sText Sensor: %s", indent_depth, "", this->text_sensor_->get_name());
}
ESP_LOGCONFIG(TAG, "%*sHas Text Formatter: %s", indent_depth, "", YESNO(!this->text_formatter_.has_value()));
}
void TextPanel::setup_complete() {
if (!this->text_formatter_.has_value()) {
this->text_formatter_ = [this](const std::string string) {
return string;
};
}
if (this->sensor_ != nullptr) {
// Need to setup the text callback for the sensor
this->text_ = [this]() {
std::stringstream stream;
stream << std::fixed << std::setprecision(this->sensor_->get_accuracy_decimals()) << this->sensor_->get_state();
return this->text_formatter_.value(stream.str());
};
}
if (this->text_sensor_ != nullptr) {
// Need to setup the text callback to the TextSensor
this->text_ = [this]() {
return this->text_formatter_.value(this->text_sensor_->get_state());
};
}
if (this->text_input_.has_value()) {
this->text_ = [this]() {
return this->text_formatter_.value(this->text_input_.value());
};
}
} }
display::Rect TextPanel::measure_item_internal(display::Display *display) { display::Rect TextPanel::measure_item_internal(display::Display *display) {

View File

@ -5,6 +5,8 @@
#include "esphome/components/graphical_layout/graphical_layout.h" #include "esphome/components/graphical_layout/graphical_layout.h"
#include "esphome/components/font/font.h" #include "esphome/components/font/font.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
namespace esphome { namespace esphome {
namespace graphical_layout { namespace graphical_layout {
@ -15,8 +17,12 @@ class TextPanel : public LayoutItem {
display::Rect measure_item_internal(display::Display *display) override; display::Rect measure_item_internal(display::Display *display) override;
void render_internal(display::Display *display, display::Rect bounds) override; void render_internal(display::Display *display, display::Rect bounds) override;
void dump_config(int indent_depth, int additional_level_depth) override; void dump_config(int indent_depth, int additional_level_depth) override;
void setup_complete() override;
template<typename V> void set_text(V text) { this->text_ = text; }; template<typename V> void set_text(V text) { this->text_input_ = text; };
void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; };
void set_text_sensor(text_sensor::TextSensor *text_sensor) { this->text_sensor_ = text_sensor; };
template<typename V> void set_text_formatter(V text_formatter) { this->text_formatter_ = text_formatter; };
void set_font(display::BaseFont *font) { this->font_ = font; }; void set_font(display::BaseFont *font) { this->font_ = font; };
void set_foreground_color(Color foreground_color) { this->foreground_color_ = foreground_color; }; void set_foreground_color(Color foreground_color) { this->foreground_color_ = foreground_color; };
void set_background_color(Color background_color) { this->background_color_ = background_color; }; void set_background_color(Color background_color) { this->background_color_ = background_color; };
@ -24,6 +30,10 @@ class TextPanel : public LayoutItem {
protected: protected:
TemplatableValue<std::string> text_{}; TemplatableValue<std::string> text_{};
sensor::Sensor *sensor_{nullptr};
text_sensor::TextSensor *text_sensor_{nullptr};
TemplatableValue<std::string, const std::string> text_formatter_{};
TemplatableValue<std::string> text_input_{};
display::BaseFont *font_{nullptr}; display::BaseFont *font_{nullptr};
display::TextAlign text_align_{display::TextAlign::TOP_LEFT}; display::TextAlign text_align_{display::TextAlign::TOP_LEFT};
Color foreground_color_{COLOR_ON}; Color foreground_color_{COLOR_ON};

View File

@ -1,8 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import font, color from esphome.components import font, color, sensor, text_sensor
from esphome.components.display import display_ns from esphome.components.display import display_ns
from esphome.const import CONF_FOREGROUND_COLOR, CONF_BACKGROUND_COLOR from esphome.const import CONF_FOREGROUND_COLOR, CONF_BACKGROUND_COLOR, CONST_SENSOR
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout") graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
TextPanel = graphical_layout_ns.class_("TextPanel") TextPanel = graphical_layout_ns.class_("TextPanel")
@ -12,6 +12,8 @@ CONF_TEXT_PANEL = "text_panel"
CONF_FONT = "font" CONF_FONT = "font"
CONF_TEXT = "text" CONF_TEXT = "text"
CONF_TEXT_ALIGN = "text_align" CONF_TEXT_ALIGN = "text_align"
CONF_TEXT_SENSOR = "text_sensor"
CONF_TEXT_FORMATTER = "text_formatter"
TEXT_ALIGN = { TEXT_ALIGN = {
"TOP_LEFT": TextAlign.TOP_LEFT, "TOP_LEFT": TextAlign.TOP_LEFT,
@ -30,15 +32,21 @@ TEXT_ALIGN = {
def get_config_schema(base_item_schema, item_type_schema): def get_config_schema(base_item_schema, item_type_schema):
return base_item_schema.extend( return cv.All(
{ base_item_schema.extend(
cv.GenerateID(): cv.declare_id(TextPanel), {
cv.Required(CONF_FONT): cv.use_id(font.Font), cv.GenerateID(): cv.declare_id(TextPanel),
cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color.ColorStruct), cv.Required(CONF_FONT): cv.use_id(font.Font),
cv.Optional(CONF_BACKGROUND_COLOR): cv.use_id(color.ColorStruct), cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color.ColorStruct),
cv.Required(CONF_TEXT): cv.templatable(cv.string), cv.Optional(CONF_BACKGROUND_COLOR): cv.use_id(color.ColorStruct),
cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True), cv.Optional(CONF_TEXT): cv.templatable(cv.string),
} cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True),
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Optional(CONF_TEXT_SENSOR): cv.use_id(text_sensor.TextSensor),
cv.Optional(CONF_TEXT_FORMATTER): cv.returning_lambda,
}
),
cv.has_exactly_one_key(CONF_TEXT, CONF_SENSOR, CONF_TEXT_SENSOR),
) )
@ -56,10 +64,25 @@ async def config_to_layout_item(pvariable_builder, item_config, child_item_build
background_color = await cg.get_variable(background_color_config) background_color = await cg.get_variable(background_color_config)
cg.add(var.set_background_color(background_color)) cg.add(var.set_background_color(background_color))
text = await cg.templatable( if sensor_config := item_config.get(CONF_SENSOR):
item_config[CONF_TEXT], args=[], output_type=cg.std_string sens = await cg.get_variable(sensor_config)
) cg.add(var.set_sensor(sens))
cg.add(var.set_text(text)) elif text_sensor_config := item_config.get(CONF_TEXT_SENSOR):
text_sens = await cg.get_variable(text_sensor_config)
cg.add(var.set_text_sensor(text_sens))
else:
text = await cg.templatable(
item_config[CONF_TEXT], args=[], output_type=cg.std_string
)
cg.add(var.set_text(text))
if text_formatter_config := item_config.get(CONF_TEXT_FORMATTER):
text_formatter = await cg.process_lambda(
text_formatter_config,
[(cg.std_string, "it")],
return_type=cg.std_string,
)
cg.add(var.set_text_formatter(text_formatter))
if text_align := item_config.get(CONF_TEXT_ALIGN): if text_align := item_config.get(CONF_TEXT_ALIGN):
cg.add(var.set_text_align(text_align)) cg.add(var.set_text_align(text_align))