diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index f8b6446006..3a33e5e004 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -1,3 +1,5 @@ +from collections.abc import Callable + from esphome import automation import esphome.codegen as cg from esphome.components import mqtt, time, web_server @@ -21,13 +23,13 @@ from esphome.const import ( CONF_WEB_SERVER, CONF_YEAR, ) -from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority +from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import ( entity_duplicate_validator, queue_entity_register, setup_entity, ) -from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType +from esphome.cpp_generator import MockObj, MockObjClass from esphome.types import ConfigType, SafeExpType CODEOWNERS = ["@rfdarter", "@jesserockz"] @@ -40,11 +42,6 @@ DateEntity = datetime_ns.class_("DateEntity", DateTimeBase) TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase) DateTimeEntity = datetime_ns.class_("DateTimeEntity", DateTimeBase) -# Actions -DateSetAction = datetime_ns.class_("DateSetAction", automation.Action) -TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action) -DateTimeSetAction = datetime_ns.class_("DateTimeSetAction", automation.Action) - DateTimeStateTrigger = datetime_ns.class_( "DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime) ) @@ -181,118 +178,64 @@ async def to_code(config: ConfigType) -> None: cg.add_global(datetime_ns.using) -@automation.register_action( - "datetime.date.set", - DateSetAction, - cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(DateEntity), - cv.Required(CONF_DATE): cv.Any( - cv.returning_lambda, cv.date_time(date=True, time=False) - ), - } - ), - synchronous=True, -) -async def datetime_date_set_to_code( - config: ConfigType, - action_id: ID, - template_arg: cg.TemplateArguments, - args: TemplateArgsType, -) -> MockObj: - action_var = cg.new_Pvariable(action_id, template_arg) - await cg.register_parented(action_var, config[CONF_ID]) - - date_config = config[CONF_DATE] - if cg.is_template(date_config): - template_ = await cg.templatable(date_config, args, cg.ESPTime) - cg.add(action_var.set_date(template_)) - else: - date_struct = cg.StructInitializer( - cg.ESPTime, - ("day_of_month", date_config[CONF_DAY]), - ("month", date_config[CONF_MONTH]), - ("year", date_config[CONF_YEAR]), +def _esptime_initializer( + members: tuple[tuple[str, str], ...], +) -> Callable[[ConfigType, ConfigType], str]: + def const_fn(config: ConfigType, value: ConfigType) -> str: + return str( + cg.StructInitializer( + cg.ESPTime, *((member, value[key]) for member, key in members) + ) ) - template_ = await cg.templatable(date_struct, args, cg.ESPTime) - cg.add(action_var.set_date(template_)) - return action_var + + return const_fn -@automation.register_action( - "datetime.time.set", - TimeSetAction, - cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(TimeEntity), - cv.Required(CONF_TIME): cv.Any( - cv.returning_lambda, cv.date_time(date=False, time=True) - ), - } +# ESPTime member order, so the designated initializer compiles. +_TIME_MEMBERS = (("second", CONF_SECOND), ("minute", CONF_MINUTE), ("hour", CONF_HOUR)) +_DATE_MEMBERS = (("day_of_month", CONF_DAY), ("month", CONF_MONTH), ("year", CONF_YEAR)) + +for _name, _entity, _key, _target, _date, _time, _members in ( + ( + "datetime.date.set", + DateEntity, + CONF_DATE, + "set_date", + True, + False, + _DATE_MEMBERS, ), - synchronous=True, -) -async def datetime_time_set_to_code( - config: ConfigType, - action_id: ID, - template_arg: cg.TemplateArguments, - args: TemplateArgsType, -) -> MockObj: - action_var = cg.new_Pvariable(action_id, template_arg) - await cg.register_parented(action_var, config[CONF_ID]) - - time_config = config[CONF_TIME] - if cg.is_template(time_config): - template_ = await cg.templatable(time_config, args, cg.ESPTime) - cg.add(action_var.set_time(template_)) - else: - time_struct = cg.StructInitializer( - cg.ESPTime, - ("second", time_config[CONF_SECOND]), - ("minute", time_config[CONF_MINUTE]), - ("hour", time_config[CONF_HOUR]), - ) - template_ = await cg.templatable(time_struct, args, cg.ESPTime) - cg.add(action_var.set_time(template_)) - return action_var - - -@automation.register_action( - "datetime.datetime.set", - DateTimeSetAction, - cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(DateTimeEntity), - cv.Required(CONF_DATETIME): cv.Any( - cv.returning_lambda, cv.date_time(date=True, time=True) - ), - }, + ( + "datetime.time.set", + TimeEntity, + CONF_TIME, + "set_time", + False, + True, + _TIME_MEMBERS, ), - synchronous=True, -) -async def datetime_datetime_set_to_code( - config: ConfigType, - action_id: ID, - template_arg: cg.TemplateArguments, - args: TemplateArgsType, -) -> MockObj: - action_var = cg.new_Pvariable(action_id, template_arg) - await cg.register_parented(action_var, config[CONF_ID]) - - datetime_config = config[CONF_DATETIME] - if cg.is_template(datetime_config): - template_ = await cg.templatable(datetime_config, args, cg.ESPTime) - cg.add(action_var.set_datetime(template_)) - else: - datetime_struct = cg.StructInitializer( - cg.ESPTime, - ("second", datetime_config[CONF_SECOND]), - ("minute", datetime_config[CONF_MINUTE]), - ("hour", datetime_config[CONF_HOUR]), - ("day_of_month", datetime_config[CONF_DAY]), - ("month", datetime_config[CONF_MONTH]), - ("year", datetime_config[CONF_YEAR]), - ) - template_ = await cg.templatable(datetime_struct, args, cg.ESPTime) - cg.add(action_var.set_datetime(template_)) - return action_var + ( + "datetime.datetime.set", + DateTimeEntity, + CONF_DATETIME, + "set_datetime", + True, + True, + _TIME_MEMBERS + _DATE_MEMBERS, + ), +): + automation.register_apply_action( + _name, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(_entity), + cv.Required(_key): cv.Any( + cv.returning_lambda, cv.date_time(date=_date, time=_time) + ), + } + ), + automation.ApplyField( + _key, _target, cg.ESPTime, const_fn=_esptime_initializer(_members) + ), + call="make_call", + ) diff --git a/esphome/components/datetime/date_entity.h b/esphome/components/datetime/date_entity.h index 93ce1411f8..9404d21512 100644 --- a/esphome/components/datetime/date_entity.h +++ b/esphome/components/datetime/date_entity.h @@ -98,20 +98,6 @@ class DateCall { inline DateCall DateEntity::make_call() { return DateCall(this); } -template class DateSetAction final : public Action, public Parented { - public: - TEMPLATABLE_VALUE(ESPTime, date) - - void play(const Ts &...x) override { - auto call = this->parent_->make_call(); - - if (this->date_.has_value()) { - call.set_date(this->date_.value(x...)); - } - call.perform(); - } -}; - } // namespace esphome::datetime #endif // USE_DATETIME_DATE diff --git a/esphome/components/datetime/datetime_entity.h b/esphome/components/datetime/datetime_entity.h index fec620b5ba..57d305cb52 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -123,20 +123,6 @@ class DateTimeCall { inline DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); } -template class DateTimeSetAction final : public Action, public Parented { - public: - TEMPLATABLE_VALUE(ESPTime, datetime) - - void play(const Ts &...x) override { - auto call = this->parent_->make_call(); - - if (this->datetime_.has_value()) { - call.set_datetime(this->datetime_.value(x...)); - } - call.perform(); - } -}; - #ifdef USE_TIME class OnDateTimeTrigger final : public Trigger<>, public Component, public Parented { public: diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h index 736e26f4a7..fd31318ce5 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -100,20 +100,6 @@ class TimeCall { inline TimeCall TimeEntity::make_call() { return TimeCall(this); } -template class TimeSetAction final : public Action, public Parented { - public: - TEMPLATABLE_VALUE(ESPTime, time) - - void play(const Ts &...x) override { - auto call = this->parent_->make_call(); - - if (this->time_.has_value()) { - call.set_time(this->time_.value(x...)); - } - call.perform(); - } -}; - #ifdef USE_TIME class OnTimeTrigger final : public Trigger<>, public Component, public Parented { public: diff --git a/tests/components/datetime/common.yaml b/tests/components/datetime/common.yaml index 4e26b68121..add7564529 100644 --- a/tests/components/datetime/common.yaml +++ b/tests/components/datetime/common.yaml @@ -1,3 +1,43 @@ +esphome: + on_boot: + then: + - datetime.date.set: + id: datetime_test_date + date: + year: 2021 + month: 1 + day: 1 + - datetime.date.set: + id: datetime_test_date + date: !lambda "return {.day_of_month = 1, .month = 1, .year = 2021};" + - datetime.time.set: + id: datetime_test_time + time: "12:34:56" + - datetime.time.set: + id: datetime_test_time + time: !lambda "return {.second = 56, .minute = 34, .hour = 12};" + - datetime.datetime.set: + id: datetime_test_datetime + datetime: "2021-01-01 12:34:56" + - datetime.datetime.set: + id: datetime_test_datetime + datetime: !lambda "return {.second = 56, .minute = 34, .hour = 12, .day_of_month = 1, .month = 1, .year = 2021};" + datetime: + - platform: template + name: Datetime Test Date + id: datetime_test_date + type: date + optimistic: true + - platform: template + name: Datetime Test Time + id: datetime_test_time + type: time + optimistic: true + - platform: template + name: Datetime Test DateTime + id: datetime_test_datetime + type: datetime + optimistic: true time: