mirror of
https://github.com/esphome/esphome.git
synced 2026-02-20 08:25:35 -07:00
Merge branch 'template_select_trigger' into integration
This commit is contained in:
@@ -10,7 +10,11 @@ from esphome.const import (
|
||||
CONF_OPTIONS,
|
||||
CONF_RESTORE_VALUE,
|
||||
CONF_SET_ACTION,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
SCHEDULER_DONT_RUN,
|
||||
)
|
||||
from esphome.core import TimePeriodMilliseconds
|
||||
from esphome.cpp_generator import TemplateArguments
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
@@ -23,29 +27,47 @@ TemplateSelectWithSetAction = template_ns.class_(
|
||||
|
||||
|
||||
def validate(config):
|
||||
errors = []
|
||||
if CONF_LAMBDA in config:
|
||||
if config[CONF_OPTIMISTIC]:
|
||||
raise cv.Invalid("optimistic cannot be used with lambda")
|
||||
errors.append(
|
||||
cv.Invalid(
|
||||
"optimistic cannot be used with lambda", path=[CONF_OPTIMISTIC]
|
||||
)
|
||||
)
|
||||
if CONF_INITIAL_OPTION in config:
|
||||
raise cv.Invalid("initial_value cannot be used with lambda")
|
||||
errors.append(
|
||||
cv.Invalid(
|
||||
"initial_value cannot be used with lambda",
|
||||
path=[CONF_INITIAL_OPTION],
|
||||
)
|
||||
)
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
raise cv.Invalid("restore_value cannot be used with lambda")
|
||||
errors.append(
|
||||
cv.Invalid(
|
||||
"restore_value cannot be used with lambda",
|
||||
path=[CONF_RESTORE_VALUE],
|
||||
)
|
||||
)
|
||||
elif CONF_INITIAL_OPTION in config:
|
||||
if config[CONF_INITIAL_OPTION] not in config[CONF_OPTIONS]:
|
||||
raise cv.Invalid(
|
||||
f"initial_option '{config[CONF_INITIAL_OPTION]}' is not a valid option [{', '.join(config[CONF_OPTIONS])}]"
|
||||
errors.append(
|
||||
cv.Invalid(
|
||||
f"initial_option '{config[CONF_INITIAL_OPTION]}' is not a valid option [{', '.join(config[CONF_OPTIONS])}]",
|
||||
path=[CONF_INITIAL_OPTION],
|
||||
)
|
||||
)
|
||||
else:
|
||||
config[CONF_INITIAL_OPTION] = config[CONF_OPTIONS][0]
|
||||
|
||||
if not config[CONF_OPTIMISTIC] and CONF_SET_ACTION not in config:
|
||||
raise cv.Invalid(
|
||||
"Either optimistic mode must be enabled, or set_action must be set, to handle the option being set."
|
||||
errors.append(
|
||||
cv.Invalid(
|
||||
"Either optimistic mode must be enabled, or set_action must be set, to handle the option being set."
|
||||
)
|
||||
)
|
||||
|
||||
# Use subclass with trigger only when set_action is configured
|
||||
if CONF_SET_ACTION in config:
|
||||
config[CONF_ID].type = TemplateSelectWithSetAction
|
||||
if errors:
|
||||
raise cv.MultipleInvalid(errors)
|
||||
|
||||
return config
|
||||
|
||||
@@ -70,29 +92,34 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await select.register_select(var, config, options=config[CONF_OPTIONS])
|
||||
var_id = config[CONF_ID]
|
||||
if CONF_SET_ACTION in config:
|
||||
var_id.type = TemplateSelectWithSetAction
|
||||
has_lambda = CONF_LAMBDA in config
|
||||
optimistic = config.get(CONF_OPTIMISTIC, False)
|
||||
restore_value = config.get(CONF_RESTORE_VALUE, False)
|
||||
options = config[CONF_OPTIONS]
|
||||
initial_option = config.get(CONF_INITIAL_OPTION, 0)
|
||||
initial_option_index = options.index(initial_option) if not has_lambda else 0
|
||||
|
||||
var = cg.new_Pvariable(
|
||||
var_id,
|
||||
TemplateArguments(has_lambda, optimistic, restore_value, initial_option_index),
|
||||
)
|
||||
component_config = config.copy()
|
||||
if not has_lambda:
|
||||
# No point in polling if not using a lambda
|
||||
component_config[CONF_UPDATE_INTERVAL] = TimePeriodMilliseconds(
|
||||
milliseconds=SCHEDULER_DONT_RUN
|
||||
)
|
||||
await cg.register_component(var, component_config)
|
||||
await select.register_select(var, config, options=options)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
else:
|
||||
# Only set if non-default to avoid bloating setup() function
|
||||
if config[CONF_OPTIMISTIC]:
|
||||
cg.add(var.set_optimistic(True))
|
||||
initial_option_index = config[CONF_OPTIONS].index(config[CONF_INITIAL_OPTION])
|
||||
# Only set if non-zero to avoid bloating setup() function
|
||||
# (initial_option_index_ is zero-initialized in the header)
|
||||
if initial_option_index != 0:
|
||||
cg.add(var.set_initial_option_index(initial_option_index))
|
||||
|
||||
# Only set if True (default is False)
|
||||
if config.get(CONF_RESTORE_VALUE):
|
||||
cg.add(var.set_restore_value(True))
|
||||
cg.add(var.set_lambda(lambda_))
|
||||
|
||||
if CONF_SET_ACTION in config:
|
||||
await automation.build_automation(
|
||||
|
||||
@@ -5,64 +5,44 @@ namespace esphome::template_ {
|
||||
|
||||
static const char *const TAG = "template.select";
|
||||
|
||||
void TemplateSelect::setup() {
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
|
||||
size_t index = this->initial_option_index_;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = this->make_entity_preference<size_t>();
|
||||
size_t restored_index;
|
||||
if (this->pref_.load(&restored_index) && this->has_index(restored_index)) {
|
||||
index = restored_index;
|
||||
ESP_LOGD(TAG, "State from restore: %s", this->option_at(index));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "State from initial (could not load or invalid stored index): %s", this->option_at(index));
|
||||
}
|
||||
void dump_config_helper(BaseTemplateSelect *sel_comp, bool optimistic, bool has_lambda,
|
||||
const size_t initial_option_index, bool restore_value) {
|
||||
LOG_SELECT("", "Template Select", sel_comp);
|
||||
if (has_lambda) {
|
||||
LOG_UPDATE_INTERVAL(sel_comp);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "State from initial: %s", this->option_at(index));
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Optimistic: %s\n"
|
||||
" Initial Option: %s\n"
|
||||
" Restore Value: %s",
|
||||
YESNO(optimistic), sel_comp->option_at(initial_option_index), YESNO(restore_value));
|
||||
}
|
||||
|
||||
this->publish_state(index);
|
||||
}
|
||||
|
||||
void TemplateSelect::update() {
|
||||
if (!this->f_.has_value())
|
||||
return;
|
||||
void setup_initial(BaseTemplateSelect *sel_comp, size_t initial_index) {
|
||||
ESP_LOGD(TAG, "State from initial: %s", sel_comp->option_at(initial_index));
|
||||
sel_comp->publish_state(initial_index);
|
||||
}
|
||||
|
||||
auto val = this->f_();
|
||||
void setup_with_restore(BaseTemplateSelect *sel_comp, ESPPreferenceObject &pref, size_t initial_index) {
|
||||
size_t index = initial_index;
|
||||
if (pref.load(&index) && sel_comp->has_index(index)) {
|
||||
ESP_LOGD(TAG, "State from restore: %s", sel_comp->option_at(index));
|
||||
} else {
|
||||
index = initial_index;
|
||||
ESP_LOGD(TAG, "State from initial (no valid stored index): %s", sel_comp->option_at(initial_index));
|
||||
}
|
||||
sel_comp->publish_state(index);
|
||||
}
|
||||
|
||||
void update_lambda(BaseTemplateSelect *sel_comp, const optional<std::string> &val) {
|
||||
if (val.has_value()) {
|
||||
if (!this->has_option(*val)) {
|
||||
if (!sel_comp->has_option(*val)) {
|
||||
ESP_LOGE(TAG, "Lambda returned an invalid option: %s", (*val).c_str());
|
||||
return;
|
||||
}
|
||||
this->publish_state(*val);
|
||||
sel_comp->publish_state(*val);
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateSelect::control(size_t index) {
|
||||
if (this->optimistic_)
|
||||
this->publish_state(index);
|
||||
|
||||
if (this->restore_value_)
|
||||
this->pref_.save(&index);
|
||||
}
|
||||
|
||||
void TemplateSelectWithSetAction::control(size_t index) {
|
||||
this->set_trigger_.trigger(StringRef(this->option_at(index)));
|
||||
TemplateSelect::control(index);
|
||||
}
|
||||
|
||||
void TemplateSelect::dump_config() {
|
||||
LOG_SELECT("", "Template Select", this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
if (this->f_.has_value())
|
||||
return;
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Optimistic: %s\n"
|
||||
" Initial Option: %s\n"
|
||||
" Restore Value: %s",
|
||||
YESNO(this->optimistic_), this->option_at(this->initial_option_index_), YESNO(this->restore_value_));
|
||||
}
|
||||
|
||||
} // namespace esphome::template_
|
||||
|
||||
@@ -9,37 +9,70 @@
|
||||
|
||||
namespace esphome::template_ {
|
||||
|
||||
/// Base template select class - used when no set_action is configured
|
||||
class TemplateSelect : public select::Select, public PollingComponent {
|
||||
public:
|
||||
template<typename F> void set_template(F &&f) { this->f_.set(std::forward<F>(f)); }
|
||||
struct Empty {};
|
||||
class BaseTemplateSelect : public select::Select, public PollingComponent {};
|
||||
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
void dump_config_helper(BaseTemplateSelect *sel_comp, bool optimistic, bool has_lambda, size_t initial_option_index,
|
||||
bool restore_value);
|
||||
void setup_initial(BaseTemplateSelect *sel_comp, size_t initial_index);
|
||||
void setup_with_restore(BaseTemplateSelect *sel_comp, ESPPreferenceObject &pref, size_t initial_index);
|
||||
void update_lambda(BaseTemplateSelect *sel_comp, const optional<std::string> &val);
|
||||
|
||||
/// Base template select class - used when no set_action is configured
|
||||
|
||||
template<bool HAS_LAMBDA, bool OPTIMISTIC, bool RESTORE_VALUE, size_t INITIAL_OPTION_INDEX>
|
||||
class TemplateSelect : public BaseTemplateSelect {
|
||||
public:
|
||||
template<typename F> void set_lambda(F &&f) {
|
||||
if constexpr (HAS_LAMBDA) {
|
||||
this->f_.set(std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
if constexpr (!HAS_LAMBDA) {
|
||||
if constexpr (RESTORE_VALUE) {
|
||||
this->pref_ = this->template make_entity_preference<size_t>();
|
||||
setup_with_restore(this, this->pref_, INITIAL_OPTION_INDEX);
|
||||
} else {
|
||||
setup_initial(this, INITIAL_OPTION_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update() override {
|
||||
if constexpr (HAS_LAMBDA) {
|
||||
update_lambda(this, this->f_());
|
||||
}
|
||||
}
|
||||
void dump_config() override {
|
||||
dump_config_helper(this, OPTIMISTIC, HAS_LAMBDA, INITIAL_OPTION_INDEX, RESTORE_VALUE);
|
||||
};
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_initial_option_index(size_t initial_option_index) { this->initial_option_index_ = initial_option_index; }
|
||||
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
bool optimistic_ = false;
|
||||
size_t initial_option_index_{0};
|
||||
bool restore_value_ = false;
|
||||
TemplateLambda<std::string> f_;
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
void control(size_t index) override {
|
||||
if constexpr (OPTIMISTIC)
|
||||
this->publish_state(index);
|
||||
if constexpr (RESTORE_VALUE)
|
||||
this->pref_.save(&index);
|
||||
}
|
||||
[[no_unique_address]] std::conditional_t<HAS_LAMBDA, TemplateLambda<std::string>, Empty> f_{};
|
||||
[[no_unique_address]] std::conditional_t<RESTORE_VALUE, ESPPreferenceObject, Empty> pref_{};
|
||||
};
|
||||
|
||||
/// Template select with set_action trigger - only instantiated when set_action is configured
|
||||
class TemplateSelectWithSetAction final : public TemplateSelect {
|
||||
template<bool HAS_LAMBDA, bool OPTIMISTIC, bool RESTORE_VALUE, size_t INITIAL_OPTION_INDEX>
|
||||
class TemplateSelectWithSetAction final
|
||||
: public TemplateSelect<HAS_LAMBDA, OPTIMISTIC, RESTORE_VALUE, INITIAL_OPTION_INDEX> {
|
||||
public:
|
||||
Trigger<StringRef> *get_set_trigger() { return &this->set_trigger_; }
|
||||
|
||||
protected:
|
||||
void control(size_t index) override;
|
||||
void control(size_t index) override {
|
||||
this->set_trigger_.trigger(StringRef(this->option_at(index)));
|
||||
TemplateSelect<HAS_LAMBDA, OPTIMISTIC, RESTORE_VALUE, INITIAL_OPTION_INDEX>::control(index);
|
||||
}
|
||||
Trigger<StringRef> set_trigger_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user