diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 20011c0954..c4cc9d18f8 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -255,7 +255,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { // Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -bool APIConnection::send_fan_state(fan::FanState *fan) { +bool APIConnection::send_fan_state(fan::Fan *fan) { if (!this->state_subscription_) return false; @@ -273,7 +273,7 @@ bool APIConnection::send_fan_state(fan::FanState *fan) { resp.direction = static_cast(fan->direction); return this->send_fan_state_response(resp); } -bool APIConnection::send_fan_info(fan::FanState *fan) { +bool APIConnection::send_fan_info(fan::Fan *fan) { auto traits = fan->get_traits(); ListEntitiesFanResponse msg; msg.key = fan->get_object_id_hash(); @@ -290,7 +290,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) { return this->send_list_entities_fan_response(msg); } void APIConnection::fan_command(const FanCommandRequest &msg) { - fan::FanState *fan = App.get_fan_by_key(msg.key); + fan::Fan *fan = App.get_fan_by_key(msg.key); if (fan == nullptr) return; diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 72697b5911..c1f520c83b 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection { void cover_command(const CoverCommandRequest &msg) override; #endif #ifdef USE_FAN - bool send_fan_state(fan::FanState *fan); - bool send_fan_info(fan::FanState *fan); + bool send_fan_state(fan::Fan *fan); + bool send_fan_info(fan::Fan *fan); void fan_command(const FanCommandRequest &msg) override; #endif #ifdef USE_LIGHT diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 25081a809a..c857763f95 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -188,7 +188,7 @@ void APIServer::on_cover_update(cover::Cover *obj) { #endif #ifdef USE_FAN -void APIServer::on_fan_update(fan::FanState *obj) { +void APIServer::on_fan_update(fan::Fan *obj) { if (obj->is_internal()) return; for (auto &c : this->clients_) diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 056d9f54f2..23b01df375 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -44,7 +44,7 @@ class APIServer : public Component, public Controller { void on_cover_update(cover::Cover *obj) override; #endif #ifdef USE_FAN - void on_fan_update(fan::FanState *obj) override; + void on_fan_update(fan::Fan *obj) override; #endif #ifdef USE_LIGHT void on_light_update(light::LightState *obj) override; diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index cb97df8ca1..35a590a828 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -16,7 +16,7 @@ bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_ bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); } #endif #ifdef USE_FAN -bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); } +bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); } #endif #ifdef USE_LIGHT bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); } diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 714edaa91f..81b814676a 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator { bool on_cover(cover::Cover *cover) override; #endif #ifdef USE_FAN - bool on_fan(fan::FanState *fan) override; + bool on_fan(fan::Fan *fan) override; #endif #ifdef USE_LIGHT bool on_light(light::LightState *light) override; diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 1b8453f233..07b3913ff7 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -14,7 +14,7 @@ bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_ bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); } #endif #ifdef USE_FAN -bool InitialStateIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_state(fan); } +bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); } #endif #ifdef USE_LIGHT bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index d3f2d3aa45..4b83b5e793 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator { bool on_cover(cover::Cover *cover) override; #endif #ifdef USE_FAN - bool on_fan(fan::FanState *fan) override; + bool on_fan(fan::Fan *fan) override; #endif #ifdef USE_LIGHT bool on_light(light::LightState *light) override; diff --git a/esphome/components/api/util.h b/esphome/components/api/util.h index 7849b3e028..f329867a4e 100644 --- a/esphome/components/api/util.h +++ b/esphome/components/api/util.h @@ -27,7 +27,7 @@ class ComponentIterator { virtual bool on_cover(cover::Cover *cover) = 0; #endif #ifdef USE_FAN - virtual bool on_fan(fan::FanState *fan) = 0; + virtual bool on_fan(fan::Fan *fan) = 0; #endif #ifdef USE_LIGHT virtual bool on_light(light::LightState *light) = 0; diff --git a/esphome/components/binary/fan/__init__.py b/esphome/components/binary/fan/__init__.py index e6c8d9bfe9..6edfa885c9 100644 --- a/esphome/components/binary/fan/__init__.py +++ b/esphome/components/binary/fan/__init__.py @@ -9,7 +9,7 @@ from esphome.const import ( ) from .. import binary_ns -BinaryFan = binary_ns.class_("BinaryFan", cg.Component) +BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component) CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( { @@ -24,9 +24,8 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( async def to_code(config): var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) await cg.register_component(var, config) + await fan.register_fan(var, config) - fan_ = await fan.create_fan_state(config) - cg.add(var.set_fan(fan_)) output_ = await cg.get_variable(config[CONF_OUTPUT]) cg.add(var.set_output(output_)) diff --git a/esphome/components/binary/fan/binary_fan.cpp b/esphome/components/binary/fan/binary_fan.cpp index 2201fe576e..a2f75242de 100644 --- a/esphome/components/binary/fan/binary_fan.cpp +++ b/esphome/components/binary/fan/binary_fan.cpp @@ -6,59 +6,35 @@ namespace binary { static const char *const TAG = "binary.fan"; -void binary::BinaryFan::dump_config() { - ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str()); - if (this->fan_->get_traits().supports_oscillation()) { - ESP_LOGCONFIG(TAG, " Oscillation: YES"); - } - if (this->fan_->get_traits().supports_direction()) { - ESP_LOGCONFIG(TAG, " Direction: YES"); - } -} void BinaryFan::setup() { - auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0); - this->fan_->set_traits(traits); - this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); -} -void BinaryFan::loop() { - if (!this->next_update_) { - return; - } - this->next_update_ = false; - - { - bool enable = this->fan_->state; - if (enable) - this->output_->turn_on(); - else - this->output_->turn_off(); - ESP_LOGD(TAG, "Setting binary state: %s", ONOFF(enable)); - } - - if (this->oscillating_ != nullptr) { - bool enable = this->fan_->oscillating; - if (enable) { - this->oscillating_->turn_on(); - } else { - this->oscillating_->turn_off(); - } - ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable)); - } - - if (this->direction_ != nullptr) { - bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; - if (enable) { - this->direction_->turn_on(); - } else { - this->direction_->turn_off(); - } - ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable)); + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(*this); + this->write_state_(); } } +void BinaryFan::dump_config() { LOG_FAN("", "Binary Fan", this); } +fan::FanTraits BinaryFan::get_traits() { + return fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0); +} +void BinaryFan::control(const fan::FanCall &call) { + if (call.get_state().has_value()) + this->state = *call.get_state(); + if (call.get_oscillating().has_value()) + this->oscillating = *call.get_oscillating(); + if (call.get_direction().has_value()) + this->direction = *call.get_direction(); -// We need a higher priority than the FanState component to make sure that the traits are set -// when that component sets itself up. -float BinaryFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; } + this->write_state_(); + this->publish_state(); +} +void BinaryFan::write_state_() { + this->output_->set_state(this->state); + if (this->oscillating_ != nullptr) + this->oscillating_->set_state(this->oscillating); + if (this->direction_ != nullptr) + this->direction_->set_state(this->direction == fan::FanDirection::REVERSE); +} } // namespace binary } // namespace esphome diff --git a/esphome/components/binary/fan/binary_fan.h b/esphome/components/binary/fan/binary_fan.h index 93294b8dee..16bce2e6af 100644 --- a/esphome/components/binary/fan/binary_fan.h +++ b/esphome/components/binary/fan/binary_fan.h @@ -2,28 +2,29 @@ #include "esphome/core/component.h" #include "esphome/components/output/binary_output.h" -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" namespace esphome { namespace binary { -class BinaryFan : public Component { +class BinaryFan : public Component, public fan::Fan { public: - void set_fan(fan::FanState *fan) { fan_ = fan; } - void set_output(output::BinaryOutput *output) { output_ = output; } void setup() override; - void loop() override; void dump_config() override; - float get_setup_priority() const override; + + void set_output(output::BinaryOutput *output) { this->output_ = output; } void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } + fan::FanTraits get_traits() override; + protected: - fan::FanState *fan_; + void control(const fan::FanCall &call) override; + void write_state_(); + output::BinaryOutput *output_; output::BinaryOutput *oscillating_{nullptr}; output::BinaryOutput *direction_{nullptr}; - bool next_update_{true}; }; } // namespace binary diff --git a/esphome/components/demo/__init__.py b/esphome/components/demo/__init__.py index fae8a2b07d..6b4a55aac9 100644 --- a/esphome/components/demo/__init__.py +++ b/esphome/components/demo/__init__.py @@ -67,7 +67,7 @@ DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component) DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True) DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component) DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True) -DemoFan = demo_ns.class_("DemoFan", cg.Component) +DemoFan = demo_ns.class_("DemoFan", fan.Fan, cg.Component) DemoFanType = demo_ns.enum("DemoFanType", is_class=True) DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component) DemoLightType = demo_ns.enum("DemoLightType", is_class=True) @@ -411,8 +411,7 @@ async def to_code(config): for conf in config[CONF_FANS]: var = cg.new_Pvariable(conf[CONF_OUTPUT_ID]) await cg.register_component(var, conf) - fan_ = await fan.create_fan_state(conf) - cg.add(var.set_fan(fan_)) + await fan.register_fan(var, conf) cg.add(var.set_type(conf[CONF_TYPE])) for conf in config[CONF_LIGHTS]: diff --git a/esphome/components/demo/demo_fan.h b/esphome/components/demo/demo_fan.h index e926f68edb..09edc4e0b7 100644 --- a/esphome/components/demo/demo_fan.h +++ b/esphome/components/demo/demo_fan.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" namespace esphome { namespace demo { @@ -13,11 +13,10 @@ enum class DemoFanType { TYPE_4, }; -class DemoFan : public Component { +class DemoFan : public fan::Fan, public Component { public: void set_type(DemoFanType type) { type_ = type; } - void set_fan(fan::FanState *fan) { fan_ = fan; } - void setup() override { + fan::FanTraits get_traits() override { fan::FanTraits traits{}; // oscillation @@ -43,10 +42,23 @@ class DemoFan : public Component { break; } - this->fan_->set_traits(traits); + return traits; + } + + protected: + void control(const fan::FanCall &call) override { + if (call.get_state().has_value()) + this->state = *call.get_state(); + if (call.get_oscillating().has_value()) + this->oscillating = *call.get_oscillating(); + if (call.get_speed().has_value()) + this->speed = *call.get_speed(); + if (call.get_direction().has_value()) + this->direction = *call.get_direction(); + + this->publish_state(); } - fan::FanState *fan_; DemoFanType type_; }; diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 8dec2dee51..eb67bbcbd7 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -27,23 +27,24 @@ from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True fan_ns = cg.esphome_ns.namespace("fan") -FanState = fan_ns.class_("FanState", cg.EntityBase, cg.Component) -MakeFan = cg.Application.struct("MakeFan") +Fan = fan_ns.class_("Fan", cg.EntityBase) +FanState = fan_ns.class_("Fan", Fan, cg.Component) -FanDirection = fan_ns.enum("FanDirection") +FanDirection = fan_ns.enum("FanDirection", is_class=True) FAN_DIRECTION_ENUM = { - "FORWARD": FanDirection.FAN_DIRECTION_FORWARD, - "REVERSE": FanDirection.FAN_DIRECTION_REVERSE, + "FORWARD": FanDirection.FORWARD, + "REVERSE": FanDirection.REVERSE, } -FanRestoreMode = fan_ns.enum("FanRestoreMode") +FanRestoreMode = fan_ns.enum("FanRestoreMode", is_class=True) RESTORE_MODES = { - "RESTORE_DEFAULT_OFF": FanRestoreMode.FAN_RESTORE_DEFAULT_OFF, - "RESTORE_DEFAULT_ON": FanRestoreMode.FAN_RESTORE_DEFAULT_ON, - "ALWAYS_OFF": FanRestoreMode.FAN_ALWAYS_OFF, - "ALWAYS_ON": FanRestoreMode.FAN_ALWAYS_ON, - "RESTORE_INVERTED_DEFAULT_OFF": FanRestoreMode.FAN_RESTORE_INVERTED_DEFAULT_OFF, - "RESTORE_INVERTED_DEFAULT_ON": FanRestoreMode.FAN_RESTORE_INVERTED_DEFAULT_ON, + "NO_RESTORE": FanRestoreMode.NO_RESTORE, + "ALWAYS_OFF": FanRestoreMode.ALWAYS_OFF, + "ALWAYS_ON": FanRestoreMode.ALWAYS_ON, + "RESTORE_DEFAULT_OFF": FanRestoreMode.RESTORE_DEFAULT_OFF, + "RESTORE_DEFAULT_ON": FanRestoreMode.RESTORE_DEFAULT_ON, + "RESTORE_INVERTED_DEFAULT_OFF": FanRestoreMode.RESTORE_INVERTED_DEFAULT_OFF, + "RESTORE_INVERTED_DEFAULT_ON": FanRestoreMode.RESTORE_INVERTED_DEFAULT_ON, } # Actions @@ -61,8 +62,8 @@ FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.temp FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { - cv.GenerateID(): cv.declare_id(FanState), - cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum( + cv.GenerateID(): cv.declare_id(Fan), + cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( RESTORE_MODES, upper=True, space="_" ), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent), @@ -158,19 +159,19 @@ async def register_fan(var, config): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) cg.add(cg.App.register_fan(var)) - await cg.register_component(var, config) await setup_fan_core_(var, config) async def create_fan_state(config): var = cg.new_Pvariable(config[CONF_ID]) await register_fan(var, config) + await cg.register_component(var, config) return var FAN_ACTION_SCHEMA = maybe_simple_id( { - cv.Required(CONF_ID): cv.use_id(FanState), + cv.Required(CONF_ID): cv.use_id(Fan), } ) @@ -192,7 +193,7 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args): TurnOnAction, maybe_simple_id( { - cv.Required(CONF_ID): cv.use_id(FanState), + cv.Required(CONF_ID): cv.use_id(Fan), cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)), cv.Optional(CONF_DIRECTION): cv.templatable( @@ -227,7 +228,7 @@ async def fan_cycle_speed_to_code(config, action_id, template_arg, args): FanIsOnCondition, automation.maybe_simple_id( { - cv.Required(CONF_ID): cv.use_id(FanState), + cv.Required(CONF_ID): cv.use_id(Fan), } ), ) @@ -236,7 +237,7 @@ async def fan_cycle_speed_to_code(config, action_id, template_arg, args): FanIsOffCondition, automation.maybe_simple_id( { - cv.Required(CONF_ID): cv.use_id(FanState), + cv.Required(CONF_ID): cv.use_id(Fan), } ), ) diff --git a/esphome/components/fan/automation.cpp b/esphome/components/fan/automation.cpp deleted file mode 100644 index 79e583fc57..0000000000 --- a/esphome/components/fan/automation.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "automation.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace fan { - -static const char *const TAG = "fan.automation"; - -} // namespace fan -} // namespace esphome diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index 608f772b75..23fb70a95b 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -9,7 +9,7 @@ namespace fan { template class TurnOnAction : public Action { public: - explicit TurnOnAction(FanState *state) : state_(state) {} + explicit TurnOnAction(Fan *state) : state_(state) {} TEMPLATABLE_VALUE(bool, oscillating) TEMPLATABLE_VALUE(int, speed) @@ -29,30 +29,30 @@ template class TurnOnAction : public Action { call.perform(); } - FanState *state_; + Fan *state_; }; template class TurnOffAction : public Action { public: - explicit TurnOffAction(FanState *state) : state_(state) {} + explicit TurnOffAction(Fan *state) : state_(state) {} void play(Ts... x) override { this->state_->turn_off().perform(); } - FanState *state_; + Fan *state_; }; template class ToggleAction : public Action { public: - explicit ToggleAction(FanState *state) : state_(state) {} + explicit ToggleAction(Fan *state) : state_(state) {} void play(Ts... x) override { this->state_->toggle().perform(); } - FanState *state_; + Fan *state_; }; template class CycleSpeedAction : public Action { public: - explicit CycleSpeedAction(FanState *state) : state_(state) {} + explicit CycleSpeedAction(Fan *state) : state_(state) {} void play(Ts... x) override { // check to see if fan supports speeds and is on @@ -83,29 +83,29 @@ template class CycleSpeedAction : public Action { } } - FanState *state_; + Fan *state_; }; template class FanIsOnCondition : public Condition { public: - explicit FanIsOnCondition(FanState *state) : state_(state) {} + explicit FanIsOnCondition(Fan *state) : state_(state) {} bool check(Ts... x) override { return this->state_->state; } protected: - FanState *state_; + Fan *state_; }; template class FanIsOffCondition : public Condition { public: - explicit FanIsOffCondition(FanState *state) : state_(state) {} + explicit FanIsOffCondition(Fan *state) : state_(state) {} bool check(Ts... x) override { return !this->state_->state; } protected: - FanState *state_; + Fan *state_; }; class FanTurnOnTrigger : public Trigger<> { public: - FanTurnOnTrigger(FanState *state) { + FanTurnOnTrigger(Fan *state) { state->add_on_state_callback([this, state]() { auto is_on = state->state; auto should_trigger = is_on && !this->last_on_; @@ -123,7 +123,7 @@ class FanTurnOnTrigger : public Trigger<> { class FanTurnOffTrigger : public Trigger<> { public: - FanTurnOffTrigger(FanState *state) { + FanTurnOffTrigger(Fan *state) { state->add_on_state_callback([this, state]() { auto is_on = state->state; auto should_trigger = !is_on && this->last_on_; @@ -141,7 +141,7 @@ class FanTurnOffTrigger : public Trigger<> { class FanSpeedSetTrigger : public Trigger<> { public: - FanSpeedSetTrigger(FanState *state) { + FanSpeedSetTrigger(Fan *state) { state->add_on_state_callback([this, state]() { auto speed = state->speed; auto should_trigger = speed != !this->last_speed_; diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp new file mode 100644 index 0000000000..5f9660f6d6 --- /dev/null +++ b/esphome/components/fan/fan.cpp @@ -0,0 +1,175 @@ +#include "fan.h" +#include "fan_helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace fan { + +static const char *const TAG = "fan"; + +const LogString *fan_direction_to_string(FanDirection direction) { + switch (direction) { + case FanDirection::FORWARD: + return LOG_STR("FORWARD"); + case FanDirection::REVERSE: + return LOG_STR("REVERSE"); + default: + return LOG_STR("UNKNOWN"); + } +} + +void FanCall::perform() { + ESP_LOGD(TAG, "'%s' - Setting:", this->parent_.get_name().c_str()); + this->validate_(); + if (this->binary_state_.has_value()) + ESP_LOGD(TAG, " State: %s", ONOFF(*this->binary_state_)); + if (this->oscillating_.has_value()) + ESP_LOGD(TAG, " Oscillating: %s", YESNO(*this->oscillating_)); + if (this->speed_.has_value()) + ESP_LOGD(TAG, " Speed: %d", *this->speed_); + if (this->direction_.has_value()) + ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(*this->direction_))); + + this->parent_.control(*this); +} +void FanCall::validate_() { + auto traits = this->parent_.get_traits(); + + if (this->speed_.has_value()) + this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count()); + + if (this->binary_state_.has_value() && *this->binary_state_) { + // when turning on, if current speed is zero, set speed to 100% + if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0) { + this->speed_ = traits.supported_speed_count(); + } + } + + if (this->oscillating_.has_value() && !traits.supports_oscillation()) { + ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str()); + this->oscillating_.reset(); + } + + if (this->speed_.has_value() && !traits.supports_speed()) { + ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str()); + this->speed_.reset(); + } + + if (this->direction_.has_value() && !traits.supports_direction()) { + ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str()); + this->direction_.reset(); + } +} + +// This whole method is deprecated, don't warn about usage of deprecated methods inside of it. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +FanCall &FanCall::set_speed(const char *legacy_speed) { + const auto supported_speed_count = this->parent_.get_traits().supported_speed_count(); + if (strcasecmp(legacy_speed, "low") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_LOW, supported_speed_count)); + } else if (strcasecmp(legacy_speed, "medium") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_MEDIUM, supported_speed_count)); + } else if (strcasecmp(legacy_speed, "high") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_HIGH, supported_speed_count)); + } + return *this; +} +#pragma GCC diagnostic pop + +FanCall FanRestoreState::to_call(Fan &fan) { + auto call = fan.make_call(); + call.set_state(this->state); + call.set_oscillating(this->oscillating); + call.set_speed(this->speed); + call.set_direction(this->direction); + return call; +} +void FanRestoreState::apply(Fan &fan) { + fan.state = this->state; + fan.oscillating = this->oscillating; + fan.speed = this->speed; + fan.direction = this->direction; + fan.publish_state(); +} + +Fan::Fan() : EntityBase("") {} +Fan::Fan(const std::string &name) : EntityBase(name) {} + +FanCall Fan::turn_on() { return this->make_call().set_state(true); } +FanCall Fan::turn_off() { return this->make_call().set_state(false); } +FanCall Fan::toggle() { return this->make_call().set_state(!this->state); } +FanCall Fan::make_call() { return FanCall(*this); } + +void Fan::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } +void Fan::publish_state() { + auto traits = this->get_traits(); + + ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str()); + ESP_LOGD(TAG, " State: %s", ONOFF(this->state)); + if (traits.supports_speed()) + ESP_LOGD(TAG, " Speed: %d", this->speed); + if (traits.supports_oscillation()) + ESP_LOGD(TAG, " Oscillating: %s", YESNO(this->oscillating)); + if (traits.supports_direction()) + ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction))); + + this->state_callback_.call(); + this->save_state_(); +} + +// Random 32-bit value, change this every time the layout of the FanRestoreState struct changes. +constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA; +optional Fan::restore_state_() { + FanRestoreState recovered{}; + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash() ^ RESTORE_STATE_VERSION); + bool restored = this->rtc_.load(&recovered); + + switch (this->restore_mode_) { + case FanRestoreMode::NO_RESTORE: + return {}; + case FanRestoreMode::ALWAYS_OFF: + recovered.state = false; + return recovered; + case FanRestoreMode::ALWAYS_ON: + recovered.state = true; + return recovered; + case FanRestoreMode::RESTORE_DEFAULT_OFF: + recovered.state = restored ? recovered.state : false; + return recovered; + case FanRestoreMode::RESTORE_DEFAULT_ON: + recovered.state = restored ? recovered.state : true; + return recovered; + case FanRestoreMode::RESTORE_INVERTED_DEFAULT_OFF: + recovered.state = restored ? !recovered.state : false; + return recovered; + case FanRestoreMode::RESTORE_INVERTED_DEFAULT_ON: + recovered.state = restored ? !recovered.state : true; + return recovered; + } + + return {}; +} +void Fan::save_state_() { + FanRestoreState state{}; + state.state = this->state; + state.oscillating = this->oscillating; + state.speed = this->speed; + state.direction = this->direction; + this->rtc_.save(&state); +} + +void Fan::dump_traits_(const char *tag, const char *prefix) { + if (this->get_traits().supports_speed()) { + ESP_LOGCONFIG(tag, "%s Speed: YES", prefix); + ESP_LOGCONFIG(tag, "%s Speed count: %d", prefix, this->get_traits().supported_speed_count()); + } + if (this->get_traits().supports_oscillation()) + ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix); + if (this->get_traits().supports_direction()) + ESP_LOGCONFIG(tag, "%s Direction: YES", prefix); +} +uint32_t Fan::hash_base() { return 418001110UL; } + +} // namespace fan +} // namespace esphome diff --git a/esphome/components/fan/fan.h b/esphome/components/fan/fan.h new file mode 100644 index 0000000000..cafb5843d1 --- /dev/null +++ b/esphome/components/fan/fan.h @@ -0,0 +1,154 @@ +#pragma once + +#include "esphome/core/entity_base.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/optional.h" +#include "esphome/core/preferences.h" +#include "fan_traits.h" + +namespace esphome { +namespace fan { + +#define LOG_FAN(prefix, type, obj) \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ + (obj)->dump_traits_(TAG, prefix); \ + } + +/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon +enum ESPDEPRECATED("FanSpeed is deprecated.", "2021.9") FanSpeed { + FAN_SPEED_LOW = 0, ///< The fan is running on low speed. + FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed. + FAN_SPEED_HIGH = 2 ///< The fan is running on high/full speed. +}; + +/// Simple enum to represent the direction of a fan. +enum class FanDirection { FORWARD = 0, REVERSE = 1 }; + +/// Restore mode of a fan. +enum class FanRestoreMode { + NO_RESTORE, + ALWAYS_OFF, + ALWAYS_ON, + RESTORE_DEFAULT_OFF, + RESTORE_DEFAULT_ON, + RESTORE_INVERTED_DEFAULT_OFF, + RESTORE_INVERTED_DEFAULT_ON, +}; + +const LogString *fan_direction_to_string(FanDirection direction); + +class Fan; + +class FanCall { + public: + explicit FanCall(Fan &parent) : parent_(parent) {} + + FanCall &set_state(bool binary_state) { + this->binary_state_ = binary_state; + return *this; + } + FanCall &set_state(optional binary_state) { + this->binary_state_ = binary_state; + return *this; + } + optional get_state() const { return this->binary_state_; } + FanCall &set_oscillating(bool oscillating) { + this->oscillating_ = oscillating; + return *this; + } + FanCall &set_oscillating(optional oscillating) { + this->oscillating_ = oscillating; + return *this; + } + optional get_oscillating() const { return this->oscillating_; } + FanCall &set_speed(int speed) { + this->speed_ = speed; + return *this; + } + ESPDEPRECATED("set_speed() with string argument is deprecated, use integer argument instead.", "2021.9") + FanCall &set_speed(const char *legacy_speed); + optional get_speed() const { return this->speed_; } + FanCall &set_direction(FanDirection direction) { + this->direction_ = direction; + return *this; + } + FanCall &set_direction(optional direction) { + this->direction_ = direction; + return *this; + } + optional get_direction() const { return this->direction_; } + + void perform(); + + protected: + void validate_(); + + Fan &parent_; + optional binary_state_; + optional oscillating_; + optional speed_; + optional direction_{}; +}; + +struct FanRestoreState { + bool state; + int speed; + bool oscillating; + FanDirection direction; + + /// Convert this struct to a fan call that can be performed. + FanCall to_call(Fan &fan); + /// Apply these settings to the fan. + void apply(Fan &fan); +} __attribute__((packed)); + +class Fan : public EntityBase { + public: + Fan(); + /// Construct the fan with name. + explicit Fan(const std::string &name); + + /// The current on/off state of the fan. + bool state{false}; + /// The current oscillation state of the fan. + bool oscillating{false}; + /// The current fan speed level + int speed{0}; + /// The current direction of the fan + FanDirection direction{FanDirection::FORWARD}; + + FanCall turn_on(); + FanCall turn_off(); + FanCall toggle(); + FanCall make_call(); + + /// Register a callback that will be called each time the state changes. + void add_on_state_callback(std::function &&callback); + + void publish_state(); + + virtual FanTraits get_traits() = 0; + + /// Set the restore mode of this fan. + void set_restore_mode(FanRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } + + protected: + friend FanCall; + + virtual void control(const FanCall &call) = 0; + + optional restore_state_(); + void save_state_(); + + void dump_traits_(const char *tag, const char *prefix); + uint32_t hash_base() override; + + CallbackManager state_callback_{}; + ESPPreferenceObject rtc_; + FanRestoreMode restore_mode_; +}; + +} // namespace fan +} // namespace esphome diff --git a/esphome/components/fan/fan_helpers.h b/esphome/components/fan/fan_helpers.h index 009505601e..8e8e3859bd 100644 --- a/esphome/components/fan/fan_helpers.h +++ b/esphome/components/fan/fan_helpers.h @@ -1,5 +1,6 @@ #pragma once -#include "fan_state.h" + +#include "fan.h" namespace esphome { namespace fan { diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index 7f9023f881..7c1658fb2e 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -1,121 +1,16 @@ #include "fan_state.h" -#include "fan_helpers.h" -#include "esphome/core/log.h" namespace esphome { namespace fan { static const char *const TAG = "fan"; -const FanTraits &FanState::get_traits() const { return this->traits_; } -void FanState::set_traits(const FanTraits &traits) { this->traits_ = traits; } -void FanState::add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); -} -FanState::FanState(const std::string &name) : EntityBase(name) {} - -FanStateCall FanState::turn_on() { return this->make_call().set_state(true); } -FanStateCall FanState::turn_off() { return this->make_call().set_state(false); } -FanStateCall FanState::toggle() { return this->make_call().set_state(!this->state); } -FanStateCall FanState::make_call() { return FanStateCall(this); } - -struct FanStateRTCState { - bool state; - int speed; - bool oscillating; - FanDirection direction; -}; - void FanState::setup() { - auto call = this->make_call(); - FanStateRTCState recovered{}; - - switch (this->restore_mode_) { - case FAN_RESTORE_DEFAULT_OFF: - case FAN_RESTORE_DEFAULT_ON: - case FAN_RESTORE_INVERTED_DEFAULT_OFF: - case FAN_RESTORE_INVERTED_DEFAULT_ON: - this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); - if (!this->rtc_.load(&recovered)) { - if (this->restore_mode_ == FAN_RESTORE_DEFAULT_ON || this->restore_mode_ == FAN_RESTORE_INVERTED_DEFAULT_ON) { - call.set_state(true); - } else { - call.set_state(false); - } - } else { - if (this->restore_mode_ == FAN_RESTORE_INVERTED_DEFAULT_OFF || - this->restore_mode_ == FAN_RESTORE_INVERTED_DEFAULT_ON) { - call.set_state(!recovered.state); - } else { - call.set_state(recovered.state); - } - - call.set_speed(recovered.speed); - call.set_oscillating(recovered.oscillating); - call.set_direction(recovered.direction); - } - break; - case FAN_ALWAYS_OFF: - case FAN_ALWAYS_ON: - if (this->restore_mode_ == FAN_ALWAYS_OFF) { - call.set_state(false); - } else if (this->restore_mode_ == FAN_ALWAYS_ON) { - call.set_state(true); - } - - this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); - if (this->rtc_.load(&recovered)) { - call.set_speed(recovered.speed); - call.set_oscillating(recovered.oscillating); - call.set_direction(recovered.direction); - } - - break; - } - - call.perform(); + auto restore = this->restore_state_(); + if (restore) + restore->to_call(*this).perform(); } float FanState::get_setup_priority() const { return setup_priority::DATA - 1.0f; } -uint32_t FanState::hash_base() { return 418001110UL; } - -void FanStateCall::perform() const { - if (this->binary_state_.has_value()) { - this->state_->state = *this->binary_state_; - } - if (this->oscillating_.has_value()) { - this->state_->oscillating = *this->oscillating_; - } - if (this->direction_.has_value()) { - this->state_->direction = *this->direction_; - } - if (this->speed_.has_value()) { - const int speed_count = this->state_->get_traits().supported_speed_count(); - this->state_->speed = clamp(*this->speed_, 1, speed_count); - } - - FanStateRTCState saved{}; - saved.state = this->state_->state; - saved.speed = this->state_->speed; - saved.oscillating = this->state_->oscillating; - saved.direction = this->state_->direction; - this->state_->rtc_.save(&saved); - - this->state_->state_callback_.call(); -} - -// This whole method is deprecated, don't warn about usage of deprecated methods inside of it. -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -FanStateCall &FanStateCall::set_speed(const char *legacy_speed) { - const auto supported_speed_count = this->state_->get_traits().supported_speed_count(); - if (strcasecmp(legacy_speed, "low") == 0) { - this->set_speed(fan::speed_enum_to_level(FAN_SPEED_LOW, supported_speed_count)); - } else if (strcasecmp(legacy_speed, "medium") == 0) { - this->set_speed(fan::speed_enum_to_level(FAN_SPEED_MEDIUM, supported_speed_count)); - } else if (strcasecmp(legacy_speed, "high") == 0) { - this->set_speed(fan::speed_enum_to_level(FAN_SPEED_HIGH, supported_speed_count)); - } - return *this; -} } // namespace fan } // namespace esphome diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h index c0d9d64d2c..044ee59736 100644 --- a/esphome/components/fan/fan_state.h +++ b/esphome/components/fan/fan_state.h @@ -1,126 +1,34 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/entity_base.h" -#include "esphome/core/helpers.h" -#include "esphome/core/preferences.h" -#include "esphome/core/log.h" -#include "fan_traits.h" +#include "fan.h" namespace esphome { namespace fan { -/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon -enum ESPDEPRECATED("FanSpeed is deprecated.", "2021.9") FanSpeed { - FAN_SPEED_LOW = 0, ///< The fan is running on low speed. - FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed. - FAN_SPEED_HIGH = 2 ///< The fan is running on high/full speed. +enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection instead.", + "2022.2") LegacyFanDirection { + FAN_DIRECTION_FORWARD = 0, + FAN_DIRECTION_REVERSE = 1 }; -/// Simple enum to represent the direction of a fan -enum FanDirection { FAN_DIRECTION_FORWARD = 0, FAN_DIRECTION_REVERSE = 1 }; - -enum FanRestoreMode { - FAN_RESTORE_DEFAULT_OFF, - FAN_RESTORE_DEFAULT_ON, - FAN_ALWAYS_OFF, - FAN_ALWAYS_ON, - FAN_RESTORE_INVERTED_DEFAULT_OFF, - FAN_RESTORE_INVERTED_DEFAULT_ON, -}; - -class FanState; - -class FanStateCall { - public: - explicit FanStateCall(FanState *state) : state_(state) {} - - FanStateCall &set_state(bool binary_state) { - this->binary_state_ = binary_state; - return *this; - } - FanStateCall &set_state(optional binary_state) { - this->binary_state_ = binary_state; - return *this; - } - FanStateCall &set_oscillating(bool oscillating) { - this->oscillating_ = oscillating; - return *this; - } - FanStateCall &set_oscillating(optional oscillating) { - this->oscillating_ = oscillating; - return *this; - } - FanStateCall &set_speed(int speed) { - this->speed_ = speed; - return *this; - } - ESPDEPRECATED("set_speed() with string argument is deprecated, use integer argument instead.", "2021.9") - FanStateCall &set_speed(const char *legacy_speed); - FanStateCall &set_direction(FanDirection direction) { - this->direction_ = direction; - return *this; - } - FanStateCall &set_direction(optional direction) { - this->direction_ = direction; - return *this; - } - - void perform() const; - - protected: - FanState *const state_; - optional binary_state_; - optional oscillating_; - optional speed_; - optional direction_{}; -}; - -class FanState : public EntityBase, public Component { +class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component { public: FanState() = default; - /// Construct the fan state with name. - explicit FanState(const std::string &name); + explicit FanState(const std::string &name) : Fan(name) {} - /// Register a callback that will be called each time the state changes. - void add_on_state_callback(std::function &&callback); - - /// Get the traits of this fan (i.e. what features it supports). - const FanTraits &get_traits() const; + /// Get the traits of this fan. + FanTraits get_traits() override { return this->traits_; } /// Set the traits of this fan (i.e. what features it supports). - void set_traits(const FanTraits &traits); - - /// Set the restore mode of this fan - void set_restore_mode(FanRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } - - /// The current ON/OFF state of the fan. - bool state{false}; - /// The current oscillation state of the fan. - bool oscillating{false}; - /// The current fan speed level - int speed{}; - /// The current direction of the fan - FanDirection direction{FAN_DIRECTION_FORWARD}; - - FanStateCall turn_on(); - FanStateCall turn_off(); - FanStateCall toggle(); - FanStateCall make_call(); + void set_traits(const FanTraits &traits) { this->traits_ = traits; } void setup() override; float get_setup_priority() const override; protected: - friend FanStateCall; - - uint32_t hash_base() override; + void control(const FanCall &call) override { this->publish_state(); } FanTraits traits_{}; - CallbackManager state_callback_{}; - ESPPreferenceObject rtc_; - - /// Restore mode of the fan. - FanRestoreMode restore_mode_; }; } // namespace fan diff --git a/esphome/components/hbridge/fan/__init__.py b/esphome/components/hbridge/fan/__init__.py index b169978acd..421883a1ff 100644 --- a/esphome/components/hbridge/fan/__init__.py +++ b/esphome/components/hbridge/fan/__init__.py @@ -17,7 +17,7 @@ from .. import hbridge_ns CODEOWNERS = ["@WeekendWarrior"] -HBridgeFan = hbridge_ns.class_("HBridgeFan", fan.FanState) +HBridgeFan = hbridge_ns.class_("HBridgeFan", cg.Component, fan.Fan) DecayMode = hbridge_ns.enum("DecayMode") DECAY_MODE_OPTIONS = { @@ -59,6 +59,7 @@ async def to_code(config): config[CONF_SPEED_COUNT], config[CONF_DECAY_MODE], ) + await cg.register_component(var, config) await fan.register_fan(var, config) pin_a_ = await cg.get_variable(config[CONF_PIN_A]) cg.add(var.set_pin_a(pin_a_)) diff --git a/esphome/components/hbridge/fan/hbridge_fan.cpp b/esphome/components/hbridge/fan/hbridge_fan.cpp index a4e5429ff4..52d2b3d8b7 100644 --- a/esphome/components/hbridge/fan/hbridge_fan.cpp +++ b/esphome/components/hbridge/fan/hbridge_fan.cpp @@ -22,47 +22,49 @@ void HBridgeFan::set_hbridge_levels_(float a_level, float b_level, float enable) ESP_LOGD(TAG, "Setting speed: a: %.2f, b: %.2f, enable: %.2f", a_level, b_level, enable); } -fan::FanStateCall HBridgeFan::brake() { +fan::FanCall HBridgeFan::brake() { ESP_LOGD(TAG, "Braking"); (this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f, 1.0f) : this->set_hbridge_levels_(1.0f, 1.0f, 1.0f); return this->make_call().set_state(false); } +void HBridgeFan::setup() { + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(*this); + this->write_state_(); + } +} void HBridgeFan::dump_config() { - ESP_LOGCONFIG(TAG, "Fan '%s':", this->get_name().c_str()); - if (this->get_traits().supports_oscillation()) { - ESP_LOGCONFIG(TAG, " Oscillation: YES"); - } - if (this->get_traits().supports_direction()) { - ESP_LOGCONFIG(TAG, " Direction: YES"); - } + LOG_FAN("", "H-Bridge Fan", this); if (this->decay_mode_ == DECAY_MODE_SLOW) { ESP_LOGCONFIG(TAG, " Decay Mode: Slow"); } else { ESP_LOGCONFIG(TAG, " Decay Mode: Fast"); } } -void HBridgeFan::setup() { - auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_); - this->set_traits(traits); - this->add_on_state_callback([this]() { this->next_update_ = true; }); +fan::FanTraits HBridgeFan::get_traits() { + return fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_); } -void HBridgeFan::loop() { - if (!this->next_update_) { - return; - } - this->next_update_ = false; +void HBridgeFan::control(const fan::FanCall &call) { + if (call.get_state().has_value()) + this->state = *call.get_state(); + if (call.get_speed().has_value()) + this->speed = *call.get_speed(); + if (call.get_oscillating().has_value()) + this->oscillating = *call.get_oscillating(); + if (call.get_direction().has_value()) + this->direction = *call.get_direction(); - float speed = 0.0f; - if (this->state) { - speed = static_cast(this->speed) / static_cast(this->speed_count_); - } + this->write_state_(); + this->publish_state(); +} +void HBridgeFan::write_state_() { + float speed = this->state ? static_cast(this->speed) / static_cast(this->speed_count_) : 0.0f; if (speed == 0.0f) { // off means idle (this->enable_ == nullptr) ? this->set_hbridge_levels_(speed, speed) : this->set_hbridge_levels_(speed, speed, speed); - return; - } - if (this->direction == fan::FAN_DIRECTION_FORWARD) { + } else if (this->direction == fan::FanDirection::FORWARD) { if (this->decay_mode_ == DECAY_MODE_SLOW) { (this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f - speed, 1.0f) : this->set_hbridge_levels_(1.0f - speed, 1.0f, 1.0f); @@ -79,6 +81,9 @@ void HBridgeFan::loop() { : this->set_hbridge_levels_(1.0f, 0.0f, speed); } } + + if (this->oscillating_ != nullptr) + this->oscillating_->set_state(this->oscillating); } } // namespace hbridge diff --git a/esphome/components/hbridge/fan/hbridge_fan.h b/esphome/components/hbridge/fan/hbridge_fan.h index 984318c8d6..4389b97ccb 100644 --- a/esphome/components/hbridge/fan/hbridge_fan.h +++ b/esphome/components/hbridge/fan/hbridge_fan.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/output/binary_output.h" #include "esphome/components/output/float_output.h" -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" namespace esphome { namespace hbridge { @@ -13,7 +13,7 @@ enum DecayMode { DECAY_MODE_FAST = 1, }; -class HBridgeFan : public fan::FanState { +class HBridgeFan : public Component, public fan::Fan { public: HBridgeFan(int speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {} @@ -22,25 +22,22 @@ class HBridgeFan : public fan::FanState { void set_enable_pin(output::FloatOutput *enable) { enable_ = enable; } void setup() override; - void loop() override; void dump_config() override; - float get_setup_priority() const override { return setup_priority::HARDWARE; } + fan::FanTraits get_traits() override; - fan::FanStateCall brake(); - - int get_speed_count() { return this->speed_count_; } - // update Hbridge without a triggered FanState change, eg. for acceleration/deceleration ramping - void internal_update() { this->next_update_ = true; } + fan::FanCall brake(); protected: output::FloatOutput *pin_a_; output::FloatOutput *pin_b_; output::FloatOutput *enable_{nullptr}; output::BinaryOutput *oscillating_{nullptr}; - bool next_update_{true}; int speed_count_{}; DecayMode decay_mode_{DECAY_MODE_SLOW}; + void control(const fan::FanCall &call) override; + void write_state_(); + void set_hbridge_levels_(float a_level, float b_level); void set_hbridge_levels_(float a_level, float b_level, float enable); }; diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 755f99d777..e4d867843c 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -14,9 +14,9 @@ static const char *const TAG = "mqtt.fan"; using namespace esphome::fan; -MQTTFanComponent::MQTTFanComponent(FanState *state) : state_(state) {} +MQTTFanComponent::MQTTFanComponent(Fan *state) : state_(state) {} -FanState *MQTTFanComponent::get_state() const { return this->state_; } +Fan *MQTTFanComponent::get_state() const { return this->state_; } std::string MQTTFanComponent::component_type() const { return "fan"; } const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; } diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 9d15a6cd0e..12286b9f01 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -13,7 +13,7 @@ namespace mqtt { class MQTTFanComponent : public mqtt::MQTTComponent { public: - explicit MQTTFanComponent(fan::FanState *state); + explicit MQTTFanComponent(fan::Fan *state); MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command) MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state) @@ -37,12 +37,12 @@ class MQTTFanComponent : public mqtt::MQTTComponent { /// 'fan' component type for discovery. std::string component_type() const override; - fan::FanState *get_state() const; + fan::Fan *get_state() const; protected: const EntityBase *get_entity() const override; - fan::FanState *state_; + fan::Fan *state_; }; } // namespace mqtt diff --git a/esphome/components/output/binary_output.h b/esphome/components/output/binary_output.h index 2697b23616..993ed9e8ac 100644 --- a/esphome/components/output/binary_output.h +++ b/esphome/components/output/binary_output.h @@ -30,6 +30,14 @@ class BinaryOutput { void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); } #endif + /// Enable or disable this binary output. + virtual void set_state(bool state) { + if (state) + this->turn_on(); + else + this->turn_off(); + } + /// Enable this binary output. virtual void turn_on() { #ifdef USE_POWER_SUPPLY diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 618c866d5b..e65729b184 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -127,7 +127,7 @@ void PrometheusHandler::fan_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_fan_speed GAUGE\n")); stream->print(F("#TYPE esphome_fan_oscillation GAUGE\n")); } -void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::FanState *obj) { +void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { if (obj->is_internal()) return; stream->print(F("esphome_fan_failed{id=\"")); diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 5076883ba6..82e3fe28e0 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -52,7 +52,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { /// Return the type for prometheus void fan_type_(AsyncResponseStream *stream); /// Return the sensor state as prometheus data point - void fan_row_(AsyncResponseStream *stream, fan::FanState *obj); + void fan_row_(AsyncResponseStream *stream, fan::Fan *obj); #endif #ifdef USE_LIGHT diff --git a/esphome/components/speed/fan/__init__.py b/esphome/components/speed/fan/__init__.py index 0ee31c76a0..978e68d1e9 100644 --- a/esphome/components/speed/fan/__init__.py +++ b/esphome/components/speed/fan/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( ) from .. import speed_ns -SpeedFan = speed_ns.class_("SpeedFan", cg.Component) +SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan) CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( { @@ -29,11 +29,9 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( async def to_code(config): output_ = await cg.get_variable(config[CONF_OUTPUT]) - state = await fan.create_fan_state(config) - var = cg.new_Pvariable( - config[CONF_OUTPUT_ID], state, output_, config[CONF_SPEED_COUNT] - ) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID], output_, config[CONF_SPEED_COUNT]) await cg.register_component(var, config) + await fan.register_fan(var, config) if CONF_OSCILLATION_OUTPUT in config: oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT]) diff --git a/esphome/components/speed/fan/speed_fan.cpp b/esphome/components/speed/fan/speed_fan.cpp index cb10db4ed4..9ed201982a 100644 --- a/esphome/components/speed/fan/speed_fan.cpp +++ b/esphome/components/speed/fan/speed_fan.cpp @@ -7,59 +7,39 @@ namespace speed { static const char *const TAG = "speed.fan"; -void SpeedFan::dump_config() { - ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str()); - if (this->fan_->get_traits().supports_oscillation()) { - ESP_LOGCONFIG(TAG, " Oscillation: YES"); - } - if (this->fan_->get_traits().supports_direction()) { - ESP_LOGCONFIG(TAG, " Direction: YES"); - } -} void SpeedFan::setup() { - auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_); - this->fan_->set_traits(traits); - this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); -} -void SpeedFan::loop() { - if (!this->next_update_) { - return; - } - this->next_update_ = false; - - { - float speed = 0.0f; - if (this->fan_->state) { - speed = static_cast(this->fan_->speed) / static_cast(this->speed_count_); - } - ESP_LOGD(TAG, "Setting speed: %.2f", speed); - this->output_->set_level(speed); - } - - if (this->oscillating_ != nullptr) { - bool enable = this->fan_->oscillating; - if (enable) { - this->oscillating_->turn_on(); - } else { - this->oscillating_->turn_off(); - } - ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable)); - } - - if (this->direction_ != nullptr) { - bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; - if (enable) { - this->direction_->turn_on(); - } else { - this->direction_->turn_off(); - } - ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable)); + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(*this); + this->write_state_(); } } +void SpeedFan::dump_config() { LOG_FAN("", "Speed Fan", this); } +fan::FanTraits SpeedFan::get_traits() { + return fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_); +} +void SpeedFan::control(const fan::FanCall &call) { + if (call.get_state().has_value()) + this->state = *call.get_state(); + if (call.get_speed().has_value()) + this->speed = *call.get_speed(); + if (call.get_oscillating().has_value()) + this->oscillating = *call.get_oscillating(); + if (call.get_direction().has_value()) + this->direction = *call.get_direction(); -// We need a higher priority than the FanState component to make sure that the traits are set -// when that component sets itself up. -float SpeedFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; } + this->write_state_(); + this->publish_state(); +} +void SpeedFan::write_state_() { + float speed = this->state ? static_cast(this->speed) / static_cast(this->speed_count_) : 0.0f; + this->output_->set_level(speed); + + if (this->oscillating_ != nullptr) + this->oscillating_->set_state(this->oscillating); + if (this->direction_ != nullptr) + this->direction_->set_state(this->direction == fan::FanDirection::REVERSE); +} } // namespace speed } // namespace esphome diff --git a/esphome/components/speed/fan/speed_fan.h b/esphome/components/speed/fan/speed_fan.h index 6b7fa0b0f2..1fad53813a 100644 --- a/esphome/components/speed/fan/speed_fan.h +++ b/esphome/components/speed/fan/speed_fan.h @@ -3,28 +3,27 @@ #include "esphome/core/component.h" #include "esphome/components/output/binary_output.h" #include "esphome/components/output/float_output.h" -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" namespace esphome { namespace speed { -class SpeedFan : public Component { +class SpeedFan : public Component, public fan::Fan { public: - SpeedFan(fan::FanState *fan, output::FloatOutput *output, int speed_count) - : fan_(fan), output_(output), speed_count_(speed_count) {} + SpeedFan(output::FloatOutput *output, int speed_count) : output_(output), speed_count_(speed_count) {} void setup() override; - void loop() override; void dump_config() override; - float get_setup_priority() const override; void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } + fan::FanTraits get_traits() override; protected: - fan::FanState *fan_; + void control(const fan::FanCall &call) override; + void write_state_(); + output::FloatOutput *output_; output::BinaryOutput *oscillating_{nullptr}; output::BinaryOutput *direction_{nullptr}; - bool next_update_{true}; int speed_count_{}; }; diff --git a/esphome/components/tuya/fan/__init__.py b/esphome/components/tuya/fan/__init__.py index 6d660e6d29..4832fd8638 100644 --- a/esphome/components/tuya/fan/__init__.py +++ b/esphome/components/tuya/fan/__init__.py @@ -10,7 +10,7 @@ CONF_SPEED_DATAPOINT = "speed_datapoint" CONF_OSCILLATION_DATAPOINT = "oscillation_datapoint" CONF_DIRECTION_DATAPOINT = "direction_datapoint" -TuyaFan = tuya_ns.class_("TuyaFan", cg.Component) +TuyaFan = tuya_ns.class_("TuyaFan", cg.Component, fan.Fan) CONFIG_SCHEMA = cv.All( fan.FAN_SCHEMA.extend( @@ -30,12 +30,10 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): parent = await cg.get_variable(config[CONF_TUYA_ID]) - state = await fan.create_fan_state(config) - var = cg.new_Pvariable( - config[CONF_OUTPUT_ID], parent, state, config[CONF_SPEED_COUNT] - ) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID], parent, config[CONF_SPEED_COUNT]) await cg.register_component(var, config) + await fan.register_fan(var, config) if CONF_SPEED_DATAPOINT in config: cg.add(var.set_speed_id(config[CONF_SPEED_DATAPOINT])) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index d0c8809564..019b504deb 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -8,52 +8,48 @@ namespace tuya { static const char *const TAG = "tuya.fan"; void TuyaFan::setup() { - auto traits = fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), - this->direction_id_.has_value(), this->speed_count_); - this->fan_->set_traits(traits); - if (this->speed_id_.has_value()) { this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); - auto call = this->fan_->make_call(); - if (datapoint.value_enum < this->speed_count_) - call.set_speed(datapoint.value_enum + 1); - else - ESP_LOGCONFIG(TAG, "Speed has invalid value %d", datapoint.value_enum); - call.perform(); + if (datapoint.value_enum >= this->speed_count_) { + ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum); + } else { + this->speed = datapoint.value_enum + 1; + this->publish_state(); + } }); } if (this->switch_id_.has_value()) { this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool)); - auto call = this->fan_->make_call(); - call.set_state(datapoint.value_bool); - call.perform(); + this->state = datapoint.value_bool; + this->publish_state(); }); } if (this->oscillation_id_.has_value()) { this->parent_->register_listener(*this->oscillation_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported oscillation is: %s", ONOFF(datapoint.value_bool)); - auto call = this->fan_->make_call(); - call.set_oscillating(datapoint.value_bool); - call.perform(); + this->oscillating = datapoint.value_bool; + this->publish_state(); }); } if (this->direction_id_.has_value()) { this->parent_->register_listener(*this->direction_id_, [this](const TuyaDatapoint &datapoint) { - auto call = this->fan_->make_call(); - call.set_direction(datapoint.value_bool ? fan::FAN_DIRECTION_REVERSE : fan::FAN_DIRECTION_FORWARD); - call.perform(); ESP_LOGD(TAG, "MCU reported reverse direction is: %s", ONOFF(datapoint.value_bool)); + this->direction = datapoint.value_bool ? fan::FanDirection::REVERSE : fan::FanDirection::FORWARD; + this->publish_state(); }); } - this->fan_->add_on_state_callback([this]() { this->write_state(); }); + this->parent_->add_on_initialized_callback([this]() { + auto restored = this->restore_state_(); + if (restored) + restored->to_call(*this).perform(); + }); } void TuyaFan::dump_config() { - ESP_LOGCONFIG(TAG, "Tuya Fan:"); - ESP_LOGCONFIG(TAG, " Speed count %d", this->speed_count_); + LOG_FAN("", "Tuya Fan", this); if (this->speed_id_.has_value()) ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *this->speed_id_); if (this->switch_id_.has_value()) @@ -64,29 +60,26 @@ void TuyaFan::dump_config() { ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *this->direction_id_); } -void TuyaFan::write_state() { - if (this->switch_id_.has_value()) { - ESP_LOGV(TAG, "Setting switch: %s", ONOFF(this->fan_->state)); - this->parent_->set_boolean_datapoint_value(*this->switch_id_, this->fan_->state); +fan::FanTraits TuyaFan::get_traits() { + return fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), this->direction_id_.has_value(), + this->speed_count_); +} + +void TuyaFan::control(const fan::FanCall &call) { + if (this->switch_id_.has_value() && call.get_state().has_value()) { + this->parent_->set_boolean_datapoint_value(*this->switch_id_, *call.get_state()); } - if (this->oscillation_id_.has_value()) { - ESP_LOGV(TAG, "Setting oscillating: %s", ONOFF(this->fan_->oscillating)); - this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, this->fan_->oscillating); + if (this->oscillation_id_.has_value() && call.get_oscillating().has_value()) { + this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, *call.get_oscillating()); } - if (this->direction_id_.has_value()) { - bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; - ESP_LOGV(TAG, "Setting reverse direction: %s", ONOFF(enable)); + if (this->direction_id_.has_value() && call.get_direction().has_value()) { + bool enable = *call.get_direction() == fan::FanDirection::REVERSE; this->parent_->set_enum_datapoint_value(*this->direction_id_, enable); } - if (this->speed_id_.has_value()) { - ESP_LOGV(TAG, "Setting speed: %d", this->fan_->speed); - this->parent_->set_enum_datapoint_value(*this->speed_id_, this->fan_->speed - 1); + if (this->speed_id_.has_value() && call.get_speed().has_value()) { + this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1); } } -// We need a higher priority than the FanState component to make sure that the traits are set -// when that component sets itself up. -float TuyaFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; } - } // namespace tuya } // namespace esphome diff --git a/esphome/components/tuya/fan/tuya_fan.h b/esphome/components/tuya/fan/tuya_fan.h index e96770d8c3..4aba1e1c07 100644 --- a/esphome/components/tuya/fan/tuya_fan.h +++ b/esphome/components/tuya/fan/tuya_fan.h @@ -2,35 +2,31 @@ #include "esphome/core/component.h" #include "esphome/components/tuya/tuya.h" -#include "esphome/components/fan/fan_state.h" +#include "esphome/components/fan/fan.h" namespace esphome { namespace tuya { -class TuyaFan : public Component { +class TuyaFan : public Component, public fan::Fan { public: - TuyaFan(Tuya *parent, fan::FanState *fan, int speed_count) : parent_(parent), fan_(fan), speed_count_(speed_count) {} + TuyaFan(Tuya *parent, int speed_count) : parent_(parent), speed_count_(speed_count) {} void setup() override; - float get_setup_priority() const override; void dump_config() override; void set_speed_id(uint8_t speed_id) { this->speed_id_ = speed_id; } void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; } void set_oscillation_id(uint8_t oscillation_id) { this->oscillation_id_ = oscillation_id; } void set_direction_id(uint8_t direction_id) { this->direction_id_ = direction_id; } - void write_state(); + + fan::FanTraits get_traits() override; protected: - void update_speed_(uint32_t value); - void update_switch_(uint32_t value); - void update_oscillation_(uint32_t value); - void update_direction_(uint32_t value); + void control(const fan::FanCall &call) override; Tuya *parent_; optional speed_id_{}; optional switch_id_{}; optional oscillation_id_{}; optional direction_id_{}; - fan::FanState *fan_; int speed_count_{}; }; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 7413af67c4..a7dbcd4b85 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -440,8 +440,8 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con #endif #ifdef USE_FAN -void WebServer::on_fan_update(fan::FanState *obj) { this->events_.send(this->fan_json(obj).c_str(), "state"); } -std::string WebServer::fan_json(fan::FanState *obj) { +void WebServer::on_fan_update(fan::Fan *obj) { this->events_.send(this->fan_json(obj).c_str(), "state"); } +std::string WebServer::fan_json(fan::Fan *obj) { return json::build_json([obj](JsonObject root) { root["id"] = "fan-" + obj->get_object_id(); root["state"] = obj->state ? "ON" : "OFF"; @@ -470,7 +470,7 @@ std::string WebServer::fan_json(fan::FanState *obj) { }); } void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { - for (fan::FanState *obj : App.get_fans()) { + for (fan::Fan *obj : App.get_fans()) { if (obj->get_object_id() != match.id) continue; @@ -516,7 +516,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc return; } } - this->defer([call]() { call.perform(); }); + this->defer([call]() mutable { call.perform(); }); request->send(200); } else if (match.method == "turn_off") { this->defer([obj]() { obj->turn_off().perform(); }); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 8edb4237a2..afd4f1d4b5 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -128,13 +128,13 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_FAN - void on_fan_update(fan::FanState *obj) override; + void on_fan_update(fan::Fan *obj) override; /// Handle a fan request under '/fan//'. void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the fan state as a JSON string. - std::string fan_json(fan::FanState *obj); + std::string fan_json(fan::Fan *obj); #endif #ifdef USE_LIGHT diff --git a/esphome/core/application.h b/esphome/core/application.h index 2a20793c19..2598a2f4a4 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -81,7 +81,7 @@ class Application { #endif #ifdef USE_FAN - void register_fan(fan::FanState *state) { this->fans_.push_back(state); } + void register_fan(fan::Fan *state) { this->fans_.push_back(state); } #endif #ifdef USE_COVER @@ -204,8 +204,8 @@ class Application { } #endif #ifdef USE_FAN - const std::vector &get_fans() { return this->fans_; } - fan::FanState *get_fan_by_key(uint32_t key, bool include_internal = false) { + const std::vector &get_fans() { return this->fans_; } + fan::Fan *get_fan_by_key(uint32_t key, bool include_internal = false) { for (auto *obj : this->fans_) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; @@ -288,7 +288,7 @@ class Application { std::vector text_sensors_{}; #endif #ifdef USE_FAN - std::vector fans_{}; + std::vector fans_{}; #endif #ifdef USE_COVER std::vector covers_{}; diff --git a/esphome/core/controller.h b/esphome/core/controller.h index 0c3722855c..49750d1cc4 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -44,7 +44,7 @@ class Controller { virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){}; #endif #ifdef USE_FAN - virtual void on_fan_update(fan::FanState *obj){}; + virtual void on_fan_update(fan::Fan *obj){}; #endif #ifdef USE_LIGHT virtual void on_light_update(light::LightState *obj){}; diff --git a/script/ci-custom.py b/script/ci-custom.py index 52ac4025ca..2beba3483d 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -594,6 +594,7 @@ def lint_inclusive_language(fname, match): "esphome/components/binary_sensor/binary_sensor.h", "esphome/components/cover/cover.h", "esphome/components/display/display_buffer.h", + "esphome/components/fan/fan.h", "esphome/components/i2c/i2c.h", "esphome/components/mqtt/mqtt_component.h", "esphome/components/number/number.h",