diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 6bf0d1ca1a..46ff0c2d53 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_NAME, + CONF_ON_SPEED_SET, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_TRIGGER_ID, @@ -41,6 +42,7 @@ ToggleAction = fan_ns.class_("ToggleAction", automation.Action) FanTurnOnTrigger = fan_ns.class_("FanTurnOnTrigger", automation.Trigger.template()) FanTurnOffTrigger = fan_ns.class_("FanTurnOffTrigger", automation.Trigger.template()) +FanSpeedSetTrigger = fan_ns.class_("FanSpeedSetTrigger", automation.Trigger.template()) FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template()) FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template()) @@ -71,6 +73,11 @@ FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanTurnOffTrigger), } ), + cv.Optional(CONF_ON_SPEED_SET): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanSpeedSetTrigger), + } + ), } ) @@ -110,6 +117,9 @@ async def setup_fan_core_(var, config): for conf in config.get(CONF_ON_TURN_OFF, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_SPEED_SET, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) async def register_fan(var, config): diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index abcad82569..7ff7c720df 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -103,5 +103,23 @@ class FanTurnOffTrigger : public Trigger<> { bool last_on_; }; +class FanSpeedSetTrigger : public Trigger<> { + public: + FanSpeedSetTrigger(FanState *state) { + state->add_on_state_callback([this, state]() { + auto speed = state->speed; + auto should_trigger = speed != !this->last_speed_; + this->last_speed_ = speed; + if (should_trigger) { + this->trigger(); + } + }); + this->last_speed_ = state->speed; + } + + protected: + int last_speed_; +}; + } // namespace fan } // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 6342e2a848..aff03b244b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -425,6 +425,7 @@ CONF_ON_PRESS = "on_press" CONF_ON_RAW_VALUE = "on_raw_value" CONF_ON_RELEASE = "on_release" CONF_ON_SHUTDOWN = "on_shutdown" +CONF_ON_SPEED_SET = "on_speed_set" CONF_ON_STATE = "on_state" CONF_ON_TAG = "on_tag" CONF_ON_TAG_REMOVED = "on_tag_removed" diff --git a/tests/test1.yaml b/tests/test1.yaml index bfdf9c3bea..da3289843d 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1881,6 +1881,9 @@ fan: oscillation_command_topic: oscillation/command/topic speed_state_topic: speed/state/topic speed_command_topic: speed/command/topic + on_speed_set: + then: + - logger.log: "Fan speed was changed!" interval: - interval: 10s