mirror of
https://github.com/esphome/esphome.git
synced 2024-12-12 08:24:55 +01:00
Add support for time based Text Panel and Text Run Panels
Easy way to print times and dates in different formats
This commit is contained in:
parent
ff4c918ceb
commit
17659e9783
6 changed files with 105 additions and 6 deletions
|
@ -16,7 +16,7 @@ ContainerLayoutItem = graphical_layout_ns.class_("ContainerLayoutItem", LayoutIt
|
||||||
|
|
||||||
CODEOWNERS = ["@MrMDavidson"]
|
CODEOWNERS = ["@MrMDavidson"]
|
||||||
|
|
||||||
AUTO_LOAD = ["display", "sensor", "text_sensor"]
|
AUTO_LOAD = ["display", "sensor", "text_sensor", "time"]
|
||||||
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ void TextPanel::dump_config(int indent_depth, int additional_level_depth) {
|
||||||
if (this->text_sensor_ != nullptr) {
|
if (this->text_sensor_ != nullptr) {
|
||||||
ESP_LOGCONFIG(TAG, "%*sText Sensor: %s", indent_depth, "", this->text_sensor_->get_name().c_str());
|
ESP_LOGCONFIG(TAG, "%*sText Sensor: %s", indent_depth, "", this->text_sensor_->get_name().c_str());
|
||||||
}
|
}
|
||||||
|
if (this->time_ != nullptr) {
|
||||||
|
ESP_LOGCONFIG(TAG, "%*sTime: YES", indent_depth, "");
|
||||||
|
ESP_LOGCONFIG(TAG, "%*sUse UTC Time: %s", indent_depth, "", YESNO(this->use_utc_time_));
|
||||||
|
ESP_LOGCONFIG(TAG, "%*sTime Format: %s", indent_depth, "", this->time_format_.value().c_str());
|
||||||
|
}
|
||||||
ESP_LOGCONFIG(TAG, "%*sHas Text Formatter: %s", indent_depth, "", YESNO(!this->text_formatter_.has_value()));
|
ESP_LOGCONFIG(TAG, "%*sHas Text Formatter: %s", indent_depth, "", YESNO(!this->text_formatter_.has_value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +53,19 @@ void TextPanel::setup_complete() {
|
||||||
this->text_ = [this]() { return this->text_formatter_.value(this->text_sensor_->get_state()); };
|
this->text_ = [this]() { return this->text_formatter_.value(this->text_sensor_->get_state()); };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->time_ != nullptr) {
|
||||||
|
this->text_ = [this]() {
|
||||||
|
ESPTime time;
|
||||||
|
if (this->use_utc_time_) {
|
||||||
|
time = this->time_->utcnow();
|
||||||
|
} else {
|
||||||
|
time = this->time_->now();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->text_formatter_.value(time.strftime(this->time_format_.value()));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (this->text_input_.has_value()) {
|
if (this->text_input_.has_value()) {
|
||||||
this->text_ = [this]() { return this->text_formatter_.value(this->text_input_.value()); };
|
this->text_ = [this]() { return this->text_formatter_.value(this->text_input_.value()); };
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/text_sensor/text_sensor.h"
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace graphical_layout {
|
namespace graphical_layout {
|
||||||
|
@ -22,6 +23,9 @@ class TextPanel : public LayoutItem {
|
||||||
template<typename V> void set_text(V text) { this->text_input_ = text; };
|
template<typename V> void set_text(V text) { this->text_input_ = text; };
|
||||||
void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; };
|
void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; };
|
||||||
void set_text_sensor(text_sensor::TextSensor *text_sensor) { this->text_sensor_ = text_sensor; };
|
void set_text_sensor(text_sensor::TextSensor *text_sensor) { this->text_sensor_ = text_sensor; };
|
||||||
|
void set_time(time::RealTimeClock *time) { this->time_ = time; };
|
||||||
|
void set_use_utc_time(bool use_utc_time) { this->use_utc_time_ = use_utc_time; };
|
||||||
|
template<typename V> void set_time_format(V time_format) { this->time_format_ = time_format; };
|
||||||
template<typename V> void set_text_formatter(V text_formatter) { this->text_formatter_ = text_formatter; };
|
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; };
|
||||||
|
@ -32,6 +36,9 @@ class TextPanel : public LayoutItem {
|
||||||
TemplatableValue<std::string> text_{};
|
TemplatableValue<std::string> text_{};
|
||||||
sensor::Sensor *sensor_{nullptr};
|
sensor::Sensor *sensor_{nullptr};
|
||||||
text_sensor::TextSensor *text_sensor_{nullptr};
|
text_sensor::TextSensor *text_sensor_{nullptr};
|
||||||
|
time::RealTimeClock *time_{nullptr};
|
||||||
|
bool use_utc_time_{false};
|
||||||
|
TemplatableValue<std::string> time_format_{};
|
||||||
TemplatableValue<std::string, const std::string> text_formatter_{};
|
TemplatableValue<std::string, const std::string> text_formatter_{};
|
||||||
TemplatableValue<std::string> text_input_{};
|
TemplatableValue<std::string> text_input_{};
|
||||||
display::BaseFont *font_{nullptr};
|
display::BaseFont *font_{nullptr};
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
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, sensor, text_sensor
|
from esphome.components import font, color, sensor, text_sensor, time
|
||||||
from esphome.components.display import display_ns
|
from esphome.components.display import display_ns
|
||||||
from esphome.const import CONF_FOREGROUND_COLOR, CONF_BACKGROUND_COLOR, CONF_SENSOR
|
from esphome.const import (
|
||||||
|
CONF_FOREGROUND_COLOR,
|
||||||
|
CONF_BACKGROUND_COLOR,
|
||||||
|
CONF_SENSOR,
|
||||||
|
CONF_TIME_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
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")
|
||||||
|
@ -14,6 +20,8 @@ CONF_TEXT = "text"
|
||||||
CONF_TEXT_ALIGN = "text_align"
|
CONF_TEXT_ALIGN = "text_align"
|
||||||
CONF_TEXT_SENSOR = "text_sensor"
|
CONF_TEXT_SENSOR = "text_sensor"
|
||||||
CONF_TEXT_FORMATTER = "text_formatter"
|
CONF_TEXT_FORMATTER = "text_formatter"
|
||||||
|
CONF_TIME_FORMAT = "time_format"
|
||||||
|
CONF_USE_UTC_TIME = "use_utc_time"
|
||||||
|
|
||||||
TEXT_ALIGN = {
|
TEXT_ALIGN = {
|
||||||
"TOP_LEFT": TextAlign.TOP_LEFT,
|
"TOP_LEFT": TextAlign.TOP_LEFT,
|
||||||
|
@ -43,10 +51,15 @@ def get_config_schema(base_item_schema, item_type_schema):
|
||||||
cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True),
|
cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True),
|
||||||
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
|
||||||
cv.Optional(CONF_TEXT_SENSOR): cv.use_id(text_sensor.TextSensor),
|
cv.Optional(CONF_TEXT_SENSOR): cv.use_id(text_sensor.TextSensor),
|
||||||
|
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
|
cv.Optional(CONF_TIME_FORMAT, default="%H:%M"): cv.templatable(
|
||||||
|
cv.string
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_USE_UTC_TIME, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_TEXT_FORMATTER): cv.returning_lambda,
|
cv.Optional(CONF_TEXT_FORMATTER): cv.returning_lambda,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.has_exactly_one_key(CONF_TEXT, CONF_SENSOR, CONF_TEXT_SENSOR),
|
cv.has_exactly_one_key(CONF_TEXT, CONF_SENSOR, CONF_TEXT_SENSOR, CONF_TIME_ID),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +83,15 @@ async def config_to_layout_item(pvariable_builder, item_config, child_item_build
|
||||||
elif text_sensor_config := item_config.get(CONF_TEXT_SENSOR):
|
elif text_sensor_config := item_config.get(CONF_TEXT_SENSOR):
|
||||||
text_sens = await cg.get_variable(text_sensor_config)
|
text_sens = await cg.get_variable(text_sensor_config)
|
||||||
cg.add(var.set_text_sensor(text_sens))
|
cg.add(var.set_text_sensor(text_sens))
|
||||||
|
elif time_id_config := item_config.get(CONF_TIME_ID):
|
||||||
|
time_sens = await cg.get_variable(time_id_config)
|
||||||
|
time_format = await cg.templatable(
|
||||||
|
item_config[CONF_TIME_FORMAT], args=[], output_type=cg.std_string
|
||||||
|
)
|
||||||
|
use_utc_time = item_config[CONF_USE_UTC_TIME]
|
||||||
|
cg.add(var.set_time(time_sens))
|
||||||
|
cg.add(var.set_time_format(time_format))
|
||||||
|
cg.add(var.set_use_utc_time(use_utc_time))
|
||||||
else:
|
else:
|
||||||
text = await cg.templatable(
|
text = await cg.templatable(
|
||||||
item_config[CONF_TEXT], args=[], output_type=cg.std_string
|
item_config[CONF_TEXT], args=[], output_type=cg.std_string
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/text_sensor/text_sensor.h"
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace graphical_layout {
|
namespace graphical_layout {
|
||||||
|
@ -64,7 +65,7 @@ class TextRun : public TextRunBase, public FormattableTextRun {
|
||||||
this->text_ = std::move(text);
|
this->text_ = std::move(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_text() override { return this->format_text(text_.value()); }
|
std::string get_text() override { return this->format_text(this->text_.value()); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TemplatableValue<std::string> text_{};
|
TemplatableValue<std::string> text_{};
|
||||||
|
@ -96,6 +97,33 @@ class TextSensorTextRun : public TextRunBase, public FormattableTextRun {
|
||||||
text_sensor::TextSensor *text_sensor_{nullptr};
|
text_sensor::TextSensor *text_sensor_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TimeTextRun : public TextRunBase, public FormattableTextRun {
|
||||||
|
public:
|
||||||
|
TimeTextRun(time::RealTimeClock *time, TemplatableValue<std::string> time_format, bool use_utc_time,
|
||||||
|
display::BaseFont *font)
|
||||||
|
: TextRunBase(font) {
|
||||||
|
this->time_ = time;
|
||||||
|
this->time_format_ = std::move(time_format);
|
||||||
|
this->use_utc_time_ = use_utc_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_text() override {
|
||||||
|
ESPTime time;
|
||||||
|
if (this->use_utc_time_) {
|
||||||
|
time = this->time_->utcnow();
|
||||||
|
} else {
|
||||||
|
time = this->time_->now();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->format_text(time.strftime(this->time_format_.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
time::RealTimeClock *time_{nullptr};
|
||||||
|
TemplatableValue<std::string> time_format_{""};
|
||||||
|
bool use_utc_time_{false};
|
||||||
|
};
|
||||||
|
|
||||||
class CalculatedTextRun {
|
class CalculatedTextRun {
|
||||||
public:
|
public:
|
||||||
CalculatedTextRun(TextRunBase *run, std::string text) {
|
CalculatedTextRun(TextRunBase *run, std::string text) {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
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, sensor, text_sensor
|
from esphome.components import font, color, sensor, text_sensor, time
|
||||||
from esphome.components.display import display_ns
|
from esphome.components.display import display_ns
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_FOREGROUND_COLOR,
|
CONF_FOREGROUND_COLOR,
|
||||||
CONF_BACKGROUND_COLOR,
|
CONF_BACKGROUND_COLOR,
|
||||||
CONF_SENSOR,
|
CONF_SENSOR,
|
||||||
|
CONF_TIME_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
|
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
|
||||||
|
@ -16,6 +17,7 @@ TextRunBase = graphical_layout_ns.class_("TextRunBase")
|
||||||
TextRun = graphical_layout_ns.class_("TextRun", TextRunBase)
|
TextRun = graphical_layout_ns.class_("TextRun", TextRunBase)
|
||||||
SensorTextRun = graphical_layout_ns.class_("SensorTextRun", TextRunBase)
|
SensorTextRun = graphical_layout_ns.class_("SensorTextRun", TextRunBase)
|
||||||
TextSensorTextRun = graphical_layout_ns.class_("TextSensorTextRun", TextRunBase)
|
TextSensorTextRun = graphical_layout_ns.class_("TextSensorTextRun", TextRunBase)
|
||||||
|
TimeTextRun = graphical_layout_ns.class_("TimeTextRun", TextRunBase)
|
||||||
CanWrapAtCharacterArguments = graphical_layout_ns.struct("CanWrapAtCharacterArguments")
|
CanWrapAtCharacterArguments = graphical_layout_ns.struct("CanWrapAtCharacterArguments")
|
||||||
CanWrapAtCharacterArgumentsConstRef = CanWrapAtCharacterArguments.operator(
|
CanWrapAtCharacterArgumentsConstRef = CanWrapAtCharacterArguments.operator(
|
||||||
"const"
|
"const"
|
||||||
|
@ -32,6 +34,8 @@ CONF_CAN_WRAP_AT_CHARACTER = "can_wrap_at_character"
|
||||||
CONF_DEBUG_OUTLINE_RUNS = "debug_outline_runs"
|
CONF_DEBUG_OUTLINE_RUNS = "debug_outline_runs"
|
||||||
CONF_TEXT_SENSOR = "text_sensor"
|
CONF_TEXT_SENSOR = "text_sensor"
|
||||||
CONF_TEXT_FORMATTER = "text_formatter"
|
CONF_TEXT_FORMATTER = "text_formatter"
|
||||||
|
CONF_TIME_FORMAT = "time_format"
|
||||||
|
CONF_USE_UTC_TIME = "use_utc_time"
|
||||||
|
|
||||||
TEXT_ALIGN = {
|
TEXT_ALIGN = {
|
||||||
"TOP_LEFT": TextAlign.TOP_LEFT,
|
"TOP_LEFT": TextAlign.TOP_LEFT,
|
||||||
|
@ -80,10 +84,21 @@ TEXT_SENSOR_TEXT_RUN_SCHEMA = BASE_RUN_SCHEMA.extend(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TIME_TEXT_RUN_SCHEMA = BASE_RUN_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(TimeTextRun),
|
||||||
|
cv.Required(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
|
cv.Optional(CONF_TIME_FORMAT, default="%H:%M"): cv.templatable(cv.string),
|
||||||
|
cv.Optional(CONF_USE_UTC_TIME, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_TEXT_FORMATTER): cv.returning_lambda,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
RUN_SCHEMA = cv.Any(
|
RUN_SCHEMA = cv.Any(
|
||||||
SENSOR_TEXT_RUN_SCHEMA,
|
SENSOR_TEXT_RUN_SCHEMA,
|
||||||
TEXT_RUN_SCHEMA,
|
TEXT_RUN_SCHEMA,
|
||||||
TEXT_SENSOR_TEXT_RUN_SCHEMA,
|
TEXT_SENSOR_TEXT_RUN_SCHEMA,
|
||||||
|
TIME_TEXT_RUN_SCHEMA,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +151,15 @@ async def config_to_layout_item(pvariable_builder, item_config, child_item_build
|
||||||
elif run_text_sensor_config := run_config.get(CONF_TEXT_SENSOR):
|
elif run_text_sensor_config := run_config.get(CONF_TEXT_SENSOR):
|
||||||
text_sens = await cg.get_variable(run_text_sensor_config)
|
text_sens = await cg.get_variable(run_text_sensor_config)
|
||||||
run = cg.new_Pvariable(run_config[CONF_ID], text_sens, run_font)
|
run = cg.new_Pvariable(run_config[CONF_ID], text_sens, run_font)
|
||||||
|
elif run_time_id_config := run_config.get(CONF_TIME_ID):
|
||||||
|
time_sens = await cg.get_variable(run_time_id_config)
|
||||||
|
time_format = await cg.templatable(
|
||||||
|
run_config[CONF_TIME_FORMAT], args=[], output_type=cg.std_string
|
||||||
|
)
|
||||||
|
use_utc_time = run_config[CONF_USE_UTC_TIME]
|
||||||
|
run = cg.new_Pvariable(
|
||||||
|
run_config[CONF_ID], time_sens, time_format, use_utc_time, run_font
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
run_text = await cg.templatable(
|
run_text = await cg.templatable(
|
||||||
run_config[CONF_TEXT], args=[], output_type=cg.std_string
|
run_config[CONF_TEXT], args=[], output_type=cg.std_string
|
||||||
|
|
Loading…
Reference in a new issue