From 9f033bce3b0c2c985c4328295c5e1d111168d157 Mon Sep 17 00:00:00 2001 From: "Jordan W. Cobb" Date: Thu, 19 Oct 2023 03:02:27 -0400 Subject: [PATCH] Fan no off cycle action (#5564) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/fan/__init__.py | 17 +++++++++++++++-- esphome/components/fan/automation.h | 12 ++++++++++-- esphome/const.py | 1 + tests/test1.yaml | 13 ++++++++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 9a05bff3a0..23df3c2214 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -14,6 +14,7 @@ from esphome.const import ( CONF_SPEED_LEVEL_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, + CONF_OFF_SPEED_CYCLE, CONF_ON_SPEED_SET, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, @@ -217,10 +218,22 @@ async def fan_turn_on_to_code(config, action_id, template_arg, args): return var -@automation.register_action("fan.cycle_speed", CycleSpeedAction, FAN_ACTION_SCHEMA) +@automation.register_action( + "fan.cycle_speed", + CycleSpeedAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(Fan), + cv.Optional(CONF_OFF_SPEED_CYCLE, default=True): cv.boolean, + } + ), +) async def fan_cycle_speed_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(action_id, template_arg, paren) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_OFF_SPEED_CYCLE], args, bool) + cg.add(var.set_no_off_cycle(template_)) + return var @automation.register_condition( diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index 23fb70a95b..511acf5682 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -54,18 +54,26 @@ template class CycleSpeedAction : public Action { public: explicit CycleSpeedAction(Fan *state) : state_(state) {} + TEMPLATABLE_VALUE(bool, no_off_cycle) + void play(Ts... x) override { // check to see if fan supports speeds and is on if (this->state_->get_traits().supported_speed_count()) { if (this->state_->state) { int speed = this->state_->speed + 1; int supported_speed_count = this->state_->get_traits().supported_speed_count(); - if (speed > supported_speed_count) { - // was running at max speed, so turn off + bool off_speed_cycle = no_off_cycle_.value(x...); + if (speed > supported_speed_count && off_speed_cycle) { + // was running at max speed, off speed cycle enabled, so turn off speed = 1; auto call = this->state_->turn_off(); call.set_speed(speed); call.perform(); + } else if (speed > supported_speed_count && !off_speed_cycle) { + // was running at max speed, off speed cycle disabled, so set to lowest speed + auto call = this->state_->turn_on(); + call.set_speed(1); + call.perform(); } else { auto call = this->state_->turn_on(); call.set_speed(speed); diff --git a/esphome/const.py b/esphome/const.py index be561c2880..a06c75ae44 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -475,6 +475,7 @@ CONF_NUM_SCANS = "num_scans" CONF_NUMBER = "number" CONF_NUMBER_DATAPOINT = "number_datapoint" CONF_OFF_MODE = "off_mode" +CONF_OFF_SPEED_CYCLE = "off_speed_cycle" CONF_OFFSET = "offset" CONF_ON = "on" CONF_ON_BLE_ADVERTISE = "on_ble_advertise" diff --git a/tests/test1.yaml b/tests/test1.yaml index 9bf9a32e17..acd119d8a5 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1693,7 +1693,18 @@ binary_sensor: number: 7 mode: INPUT inverted: false - + - platform: gpio + name: Speed Fan Cycle binary sensor" + pin: + number: 18 + mode: + input: true + pulldown: true + on_press: + - fan.cycle_speed: + id: fan_speed + off_speed_cycle: False + - logger.log: "Cycle speed clicked" - platform: remote_receiver name: Raw Remote Receiver Test raw: