diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 709d0d12ed..4a16c3fb7d 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -20,6 +20,7 @@ from esphome.const import ( CONF_MODE, CONF_MODE_COMMAND_TOPIC, CONF_MODE_STATE_TOPIC, + CONF_ON_CONTROL, CONF_ON_STATE, CONF_PRESET, CONF_PRESET_COMMAND_TOPIC, @@ -127,6 +128,7 @@ def single_visual_temperature(value): # Actions ControlAction = climate_ns.class_("ControlAction", automation.Action) StateTrigger = climate_ns.class_("StateTrigger", automation.Trigger.template()) +ControlTrigger = climate_ns.class_("ControlTrigger", automation.Trigger.template()) VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any( single_visual_temperature, @@ -203,6 +205,11 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA). cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All( cv.requires_component("mqtt"), cv.publish_topic ), + cv.Optional(CONF_ON_CONTROL): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger), + } + ), cv.Optional(CONF_ON_STATE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), diff --git a/esphome/components/climate/automation.h b/esphome/components/climate/automation.h index 3145358dab..9b06563eb4 100644 --- a/esphome/components/climate/automation.h +++ b/esphome/components/climate/automation.h @@ -42,6 +42,13 @@ template class ControlAction : public Action { Climate *climate_; }; +class ControlTrigger : public Trigger<> { + public: + ControlTrigger(Climate *climate) { + climate->add_on_control_callback([this]() { this->trigger(); }); + } +}; + class StateTrigger : public Trigger<> { public: StateTrigger(Climate *climate) { diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index b80fe640c8..37572ae913 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -44,6 +44,7 @@ void ClimateCall::perform() { if (this->target_temperature_high_.has_value()) { ESP_LOGD(TAG, " Target Temperature High: %.2f", *this->target_temperature_high_); } + this->parent_->control_callback_.call(); this->parent_->control(*this); } void ClimateCall::validate_() { @@ -317,6 +318,10 @@ void Climate::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } +void Climate::add_on_control_callback(std::function &&callback) { + this->control_callback_.add(std::move(callback)); +} + // Random 32bit value; If this changes existing restore preferences are invalidated static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL; diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 8cc260abbe..520036f718 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -219,6 +219,14 @@ class Climate : public EntityBase { */ void add_on_state_callback(std::function &&callback); + /** + * Add a callback for the climate device configuration; each time the configuration parameters of a climate device + * is updated (using perform() of a ClimateCall), this callback will be called, before any on_state callback. + * + * @param callback The callback to call. + */ + void add_on_control_callback(std::function &&callback); + /** Make a climate device control call, this is used to control the climate device, see the ClimateCall description * for more info. * @return A new ClimateCall instance targeting this climate device. @@ -285,6 +293,7 @@ class Climate : public EntityBase { void dump_traits_(const char *tag); CallbackManager state_callback_{}; + CallbackManager control_callback_{}; ESPPreferenceObject rtc_; optional visual_min_temperature_override_{}; optional visual_max_temperature_override_{}; diff --git a/esphome/const.py b/esphome/const.py index d821a76b75..0ab9dd54bf 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -447,6 +447,7 @@ CONF_ON_BLE_SERVICE_DATA_ADVERTISE = "on_ble_service_data_advertise" CONF_ON_BOOT = "on_boot" CONF_ON_CLICK = "on_click" CONF_ON_CONNECT = "on_connect" +CONF_ON_CONTROL = "on_control" CONF_ON_DISCONNECT = "on_disconnect" CONF_ON_DOUBLE_CLICK = "on_double_click" CONF_ON_ENROLLMENT_DONE = "on_enrollment_done" diff --git a/tests/test1.yaml b/tests/test1.yaml index d43490c8cc..a554d45771 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2052,6 +2052,8 @@ climate: name: Midea IR use_fahrenheit: true - platform: midea + on_control: + logger.log: Control message received! on_state: logger.log: State changed! id: midea_unit