mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 17:54:13 +01:00
Refactor fan platform to resemble climate/cover platforms (#2848)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> Co-authored-by: rob-deutsch <robzyb+altgithub@gmail.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
8187a4bce9
commit
2a84db7f85
41 changed files with 593 additions and 506 deletions
|
@ -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.
|
// 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 push
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
#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_)
|
if (!this->state_subscription_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
|
||||||
resp.direction = static_cast<enums::FanDirection>(fan->direction);
|
resp.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||||
return this->send_fan_state_response(resp);
|
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();
|
auto traits = fan->get_traits();
|
||||||
ListEntitiesFanResponse msg;
|
ListEntitiesFanResponse msg;
|
||||||
msg.key = fan->get_object_id_hash();
|
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);
|
return this->send_list_entities_fan_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::fan_command(const FanCommandRequest &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)
|
if (fan == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection {
|
||||||
void cover_command(const CoverCommandRequest &msg) override;
|
void cover_command(const CoverCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool send_fan_state(fan::FanState *fan);
|
bool send_fan_state(fan::Fan *fan);
|
||||||
bool send_fan_info(fan::FanState *fan);
|
bool send_fan_info(fan::Fan *fan);
|
||||||
void fan_command(const FanCommandRequest &msg) override;
|
void fan_command(const FanCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
|
|
|
@ -188,7 +188,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void APIServer::on_fan_update(fan::FanState *obj) {
|
void APIServer::on_fan_update(fan::Fan *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
|
|
|
@ -44,7 +44,7 @@ class APIServer : public Component, public Controller {
|
||||||
void on_cover_update(cover::Cover *obj) override;
|
void on_cover_update(cover::Cover *obj) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void on_fan_update(fan::FanState *obj) override;
|
void on_fan_update(fan::Fan *obj) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
void on_light_update(light::LightState *obj) override;
|
void on_light_update(light::LightState *obj) override;
|
||||||
|
|
|
@ -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); }
|
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#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
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
|
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
|
||||||
|
|
|
@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
bool on_cover(cover::Cover *cover) override;
|
bool on_cover(cover::Cover *cover) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool on_fan(fan::FanState *fan) override;
|
bool on_fan(fan::Fan *fan) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool on_light(light::LightState *light) override;
|
bool on_light(light::LightState *light) override;
|
||||||
|
|
|
@ -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); }
|
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#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
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
|
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
|
||||||
|
|
|
@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator {
|
||||||
bool on_cover(cover::Cover *cover) override;
|
bool on_cover(cover::Cover *cover) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool on_fan(fan::FanState *fan) override;
|
bool on_fan(fan::Fan *fan) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool on_light(light::LightState *light) override;
|
bool on_light(light::LightState *light) override;
|
||||||
|
|
|
@ -27,7 +27,7 @@ class ComponentIterator {
|
||||||
virtual bool on_cover(cover::Cover *cover) = 0;
|
virtual bool on_cover(cover::Cover *cover) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
virtual bool on_fan(fan::FanState *fan) = 0;
|
virtual bool on_fan(fan::Fan *fan) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
virtual bool on_light(light::LightState *light) = 0;
|
virtual bool on_light(light::LightState *light) = 0;
|
||||||
|
|
|
@ -9,7 +9,7 @@ from esphome.const import (
|
||||||
)
|
)
|
||||||
from .. import binary_ns
|
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(
|
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
|
@ -24,9 +24,8 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||||
await cg.register_component(var, config)
|
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])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
|
||||||
|
|
|
@ -6,59 +6,35 @@ namespace binary {
|
||||||
|
|
||||||
static const char *const TAG = "binary.fan";
|
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() {
|
void BinaryFan::setup() {
|
||||||
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
|
auto restore = this->restore_state_();
|
||||||
this->fan_->set_traits(traits);
|
if (restore.has_value()) {
|
||||||
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
|
restore->apply(*this);
|
||||||
}
|
this->write_state_();
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
this->write_state_();
|
||||||
// when that component sets itself up.
|
this->publish_state();
|
||||||
float BinaryFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; }
|
}
|
||||||
|
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 binary
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,28 +2,29 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/output/binary_output.h"
|
#include "esphome/components/output/binary_output.h"
|
||||||
#include "esphome/components/fan/fan_state.h"
|
#include "esphome/components/fan/fan.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace binary {
|
namespace binary {
|
||||||
|
|
||||||
class BinaryFan : public Component {
|
class BinaryFan : public Component, public fan::Fan {
|
||||||
public:
|
public:
|
||||||
void set_fan(fan::FanState *fan) { fan_ = fan; }
|
|
||||||
void set_output(output::BinaryOutput *output) { output_ = output; }
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
|
||||||
void dump_config() 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_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
||||||
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
||||||
|
|
||||||
|
fan::FanTraits get_traits() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
fan::FanState *fan_;
|
void control(const fan::FanCall &call) override;
|
||||||
|
void write_state_();
|
||||||
|
|
||||||
output::BinaryOutput *output_;
|
output::BinaryOutput *output_;
|
||||||
output::BinaryOutput *oscillating_{nullptr};
|
output::BinaryOutput *oscillating_{nullptr};
|
||||||
output::BinaryOutput *direction_{nullptr};
|
output::BinaryOutput *direction_{nullptr};
|
||||||
bool next_update_{true};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace binary
|
} // namespace binary
|
||||||
|
|
|
@ -67,7 +67,7 @@ DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component)
|
||||||
DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True)
|
DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True)
|
||||||
DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component)
|
DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component)
|
||||||
DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True)
|
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)
|
DemoFanType = demo_ns.enum("DemoFanType", is_class=True)
|
||||||
DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component)
|
DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component)
|
||||||
DemoLightType = demo_ns.enum("DemoLightType", is_class=True)
|
DemoLightType = demo_ns.enum("DemoLightType", is_class=True)
|
||||||
|
@ -411,8 +411,7 @@ async def to_code(config):
|
||||||
for conf in config[CONF_FANS]:
|
for conf in config[CONF_FANS]:
|
||||||
var = cg.new_Pvariable(conf[CONF_OUTPUT_ID])
|
var = cg.new_Pvariable(conf[CONF_OUTPUT_ID])
|
||||||
await cg.register_component(var, conf)
|
await cg.register_component(var, conf)
|
||||||
fan_ = await fan.create_fan_state(conf)
|
await fan.register_fan(var, conf)
|
||||||
cg.add(var.set_fan(fan_))
|
|
||||||
cg.add(var.set_type(conf[CONF_TYPE]))
|
cg.add(var.set_type(conf[CONF_TYPE]))
|
||||||
|
|
||||||
for conf in config[CONF_LIGHTS]:
|
for conf in config[CONF_LIGHTS]:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/fan/fan_state.h"
|
#include "esphome/components/fan/fan.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace demo {
|
namespace demo {
|
||||||
|
@ -13,11 +13,10 @@ enum class DemoFanType {
|
||||||
TYPE_4,
|
TYPE_4,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DemoFan : public Component {
|
class DemoFan : public fan::Fan, public Component {
|
||||||
public:
|
public:
|
||||||
void set_type(DemoFanType type) { type_ = type; }
|
void set_type(DemoFanType type) { type_ = type; }
|
||||||
void set_fan(fan::FanState *fan) { fan_ = fan; }
|
fan::FanTraits get_traits() override {
|
||||||
void setup() override {
|
|
||||||
fan::FanTraits traits{};
|
fan::FanTraits traits{};
|
||||||
|
|
||||||
// oscillation
|
// oscillation
|
||||||
|
@ -43,10 +42,23 @@ class DemoFan : public Component {
|
||||||
break;
|
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_;
|
DemoFanType type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,23 +27,24 @@ from esphome.cpp_helpers import setup_entity
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
fan_ns = cg.esphome_ns.namespace("fan")
|
fan_ns = cg.esphome_ns.namespace("fan")
|
||||||
FanState = fan_ns.class_("FanState", cg.EntityBase, cg.Component)
|
Fan = fan_ns.class_("Fan", cg.EntityBase)
|
||||||
MakeFan = cg.Application.struct("MakeFan")
|
FanState = fan_ns.class_("Fan", Fan, cg.Component)
|
||||||
|
|
||||||
FanDirection = fan_ns.enum("FanDirection")
|
FanDirection = fan_ns.enum("FanDirection", is_class=True)
|
||||||
FAN_DIRECTION_ENUM = {
|
FAN_DIRECTION_ENUM = {
|
||||||
"FORWARD": FanDirection.FAN_DIRECTION_FORWARD,
|
"FORWARD": FanDirection.FORWARD,
|
||||||
"REVERSE": FanDirection.FAN_DIRECTION_REVERSE,
|
"REVERSE": FanDirection.REVERSE,
|
||||||
}
|
}
|
||||||
|
|
||||||
FanRestoreMode = fan_ns.enum("FanRestoreMode")
|
FanRestoreMode = fan_ns.enum("FanRestoreMode", is_class=True)
|
||||||
RESTORE_MODES = {
|
RESTORE_MODES = {
|
||||||
"RESTORE_DEFAULT_OFF": FanRestoreMode.FAN_RESTORE_DEFAULT_OFF,
|
"NO_RESTORE": FanRestoreMode.NO_RESTORE,
|
||||||
"RESTORE_DEFAULT_ON": FanRestoreMode.FAN_RESTORE_DEFAULT_ON,
|
"ALWAYS_OFF": FanRestoreMode.ALWAYS_OFF,
|
||||||
"ALWAYS_OFF": FanRestoreMode.FAN_ALWAYS_OFF,
|
"ALWAYS_ON": FanRestoreMode.ALWAYS_ON,
|
||||||
"ALWAYS_ON": FanRestoreMode.FAN_ALWAYS_ON,
|
"RESTORE_DEFAULT_OFF": FanRestoreMode.RESTORE_DEFAULT_OFF,
|
||||||
"RESTORE_INVERTED_DEFAULT_OFF": FanRestoreMode.FAN_RESTORE_INVERTED_DEFAULT_OFF,
|
"RESTORE_DEFAULT_ON": FanRestoreMode.RESTORE_DEFAULT_ON,
|
||||||
"RESTORE_INVERTED_DEFAULT_ON": FanRestoreMode.FAN_RESTORE_INVERTED_DEFAULT_ON,
|
"RESTORE_INVERTED_DEFAULT_OFF": FanRestoreMode.RESTORE_INVERTED_DEFAULT_OFF,
|
||||||
|
"RESTORE_INVERTED_DEFAULT_ON": FanRestoreMode.RESTORE_INVERTED_DEFAULT_ON,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Actions
|
# 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(
|
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(FanState),
|
cv.GenerateID(): cv.declare_id(Fan),
|
||||||
cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum(
|
cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum(
|
||||||
RESTORE_MODES, upper=True, space="_"
|
RESTORE_MODES, upper=True, space="_"
|
||||||
),
|
),
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
|
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]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
var = cg.Pvariable(config[CONF_ID], var)
|
var = cg.Pvariable(config[CONF_ID], var)
|
||||||
cg.add(cg.App.register_fan(var))
|
cg.add(cg.App.register_fan(var))
|
||||||
await cg.register_component(var, config)
|
|
||||||
await setup_fan_core_(var, config)
|
await setup_fan_core_(var, config)
|
||||||
|
|
||||||
|
|
||||||
async def create_fan_state(config):
|
async def create_fan_state(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await register_fan(var, config)
|
await register_fan(var, config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
FAN_ACTION_SCHEMA = maybe_simple_id(
|
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,
|
TurnOnAction,
|
||||||
maybe_simple_id(
|
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_OSCILLATING): cv.templatable(cv.boolean),
|
||||||
cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)),
|
cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)),
|
||||||
cv.Optional(CONF_DIRECTION): cv.templatable(
|
cv.Optional(CONF_DIRECTION): cv.templatable(
|
||||||
|
@ -227,7 +228,7 @@ async def fan_cycle_speed_to_code(config, action_id, template_arg, args):
|
||||||
FanIsOnCondition,
|
FanIsOnCondition,
|
||||||
automation.maybe_simple_id(
|
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,
|
FanIsOffCondition,
|
||||||
automation.maybe_simple_id(
|
automation.maybe_simple_id(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.use_id(FanState),
|
cv.Required(CONF_ID): cv.use_id(Fan),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
|
|
@ -9,7 +9,7 @@ namespace fan {
|
||||||
|
|
||||||
template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit TurnOnAction(FanState *state) : state_(state) {}
|
explicit TurnOnAction(Fan *state) : state_(state) {}
|
||||||
|
|
||||||
TEMPLATABLE_VALUE(bool, oscillating)
|
TEMPLATABLE_VALUE(bool, oscillating)
|
||||||
TEMPLATABLE_VALUE(int, speed)
|
TEMPLATABLE_VALUE(int, speed)
|
||||||
|
@ -29,30 +29,30 @@ template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class TurnOffAction : public Action<Ts...> {
|
template<typename... Ts> class TurnOffAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit TurnOffAction(FanState *state) : state_(state) {}
|
explicit TurnOffAction(Fan *state) : state_(state) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->state_->turn_off().perform(); }
|
void play(Ts... x) override { this->state_->turn_off().perform(); }
|
||||||
|
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit ToggleAction(FanState *state) : state_(state) {}
|
explicit ToggleAction(Fan *state) : state_(state) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->state_->toggle().perform(); }
|
void play(Ts... x) override { this->state_->toggle().perform(); }
|
||||||
|
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class CycleSpeedAction : public Action<Ts...> {
|
template<typename... Ts> class CycleSpeedAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit CycleSpeedAction(FanState *state) : state_(state) {}
|
explicit CycleSpeedAction(Fan *state) : state_(state) {}
|
||||||
|
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
// check to see if fan supports speeds and is on
|
// check to see if fan supports speeds and is on
|
||||||
|
@ -83,29 +83,29 @@ template<typename... Ts> class CycleSpeedAction : public Action<Ts...> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class FanIsOnCondition : public Condition<Ts...> {
|
template<typename... Ts> class FanIsOnCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit FanIsOnCondition(FanState *state) : state_(state) {}
|
explicit FanIsOnCondition(Fan *state) : state_(state) {}
|
||||||
bool check(Ts... x) override { return this->state_->state; }
|
bool check(Ts... x) override { return this->state_->state; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
template<typename... Ts> class FanIsOffCondition : public Condition<Ts...> {
|
template<typename... Ts> class FanIsOffCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit FanIsOffCondition(FanState *state) : state_(state) {}
|
explicit FanIsOffCondition(Fan *state) : state_(state) {}
|
||||||
bool check(Ts... x) override { return !this->state_->state; }
|
bool check(Ts... x) override { return !this->state_->state; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FanState *state_;
|
Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FanTurnOnTrigger : public Trigger<> {
|
class FanTurnOnTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
FanTurnOnTrigger(FanState *state) {
|
FanTurnOnTrigger(Fan *state) {
|
||||||
state->add_on_state_callback([this, state]() {
|
state->add_on_state_callback([this, state]() {
|
||||||
auto is_on = state->state;
|
auto is_on = state->state;
|
||||||
auto should_trigger = is_on && !this->last_on_;
|
auto should_trigger = is_on && !this->last_on_;
|
||||||
|
@ -123,7 +123,7 @@ class FanTurnOnTrigger : public Trigger<> {
|
||||||
|
|
||||||
class FanTurnOffTrigger : public Trigger<> {
|
class FanTurnOffTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
FanTurnOffTrigger(FanState *state) {
|
FanTurnOffTrigger(Fan *state) {
|
||||||
state->add_on_state_callback([this, state]() {
|
state->add_on_state_callback([this, state]() {
|
||||||
auto is_on = state->state;
|
auto is_on = state->state;
|
||||||
auto should_trigger = !is_on && this->last_on_;
|
auto should_trigger = !is_on && this->last_on_;
|
||||||
|
@ -141,7 +141,7 @@ class FanTurnOffTrigger : public Trigger<> {
|
||||||
|
|
||||||
class FanSpeedSetTrigger : public Trigger<> {
|
class FanSpeedSetTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
FanSpeedSetTrigger(FanState *state) {
|
FanSpeedSetTrigger(Fan *state) {
|
||||||
state->add_on_state_callback([this, state]() {
|
state->add_on_state_callback([this, state]() {
|
||||||
auto speed = state->speed;
|
auto speed = state->speed;
|
||||||
auto should_trigger = speed != !this->last_speed_;
|
auto should_trigger = speed != !this->last_speed_;
|
||||||
|
|
175
esphome/components/fan/fan.cpp
Normal file
175
esphome/components/fan/fan.cpp
Normal file
|
@ -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<void()> &&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<FanRestoreState> Fan::restore_state_() {
|
||||||
|
FanRestoreState recovered{};
|
||||||
|
this->rtc_ = global_preferences->make_preference<FanRestoreState>(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
|
154
esphome/components/fan/fan.h
Normal file
154
esphome/components/fan/fan.h
Normal file
|
@ -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<bool> binary_state) {
|
||||||
|
this->binary_state_ = binary_state;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
optional<bool> get_state() const { return this->binary_state_; }
|
||||||
|
FanCall &set_oscillating(bool oscillating) {
|
||||||
|
this->oscillating_ = oscillating;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FanCall &set_oscillating(optional<bool> oscillating) {
|
||||||
|
this->oscillating_ = oscillating;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
optional<bool> 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<int> get_speed() const { return this->speed_; }
|
||||||
|
FanCall &set_direction(FanDirection direction) {
|
||||||
|
this->direction_ = direction;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FanCall &set_direction(optional<FanDirection> direction) {
|
||||||
|
this->direction_ = direction;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
optional<FanDirection> get_direction() const { return this->direction_; }
|
||||||
|
|
||||||
|
void perform();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void validate_();
|
||||||
|
|
||||||
|
Fan &parent_;
|
||||||
|
optional<bool> binary_state_;
|
||||||
|
optional<bool> oscillating_;
|
||||||
|
optional<int> speed_;
|
||||||
|
optional<FanDirection> 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<void()> &&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<FanRestoreState> restore_state_();
|
||||||
|
void save_state_();
|
||||||
|
|
||||||
|
void dump_traits_(const char *tag, const char *prefix);
|
||||||
|
uint32_t hash_base() override;
|
||||||
|
|
||||||
|
CallbackManager<void()> state_callback_{};
|
||||||
|
ESPPreferenceObject rtc_;
|
||||||
|
FanRestoreMode restore_mode_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fan
|
||||||
|
} // namespace esphome
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "fan_state.h"
|
|
||||||
|
#include "fan.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace fan {
|
namespace fan {
|
||||||
|
|
|
@ -1,121 +1,16 @@
|
||||||
#include "fan_state.h"
|
#include "fan_state.h"
|
||||||
#include "fan_helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace fan {
|
namespace fan {
|
||||||
|
|
||||||
static const char *const TAG = "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<void()> &&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() {
|
void FanState::setup() {
|
||||||
auto call = this->make_call();
|
auto restore = this->restore_state_();
|
||||||
FanStateRTCState recovered{};
|
if (restore)
|
||||||
|
restore->to_call(*this).perform();
|
||||||
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<FanStateRTCState>(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<FanStateRTCState>(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();
|
|
||||||
}
|
}
|
||||||
float FanState::get_setup_priority() const { return setup_priority::DATA - 1.0f; }
|
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 fan
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,126 +1,34 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "fan.h"
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/preferences.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "fan_traits.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace fan {
|
namespace fan {
|
||||||
|
|
||||||
/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon
|
enum ESPDEPRECATED("LegacyFanDirection members are deprecated, use FanDirection instead.",
|
||||||
enum ESPDEPRECATED("FanSpeed is deprecated.", "2021.9") FanSpeed {
|
"2022.2") LegacyFanDirection {
|
||||||
FAN_SPEED_LOW = 0, ///< The fan is running on low speed.
|
FAN_DIRECTION_FORWARD = 0,
|
||||||
FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed.
|
FAN_DIRECTION_REVERSE = 1
|
||||||
FAN_SPEED_HIGH = 2 ///< The fan is running on high/full speed.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Simple enum to represent the direction of a fan
|
class ESPDEPRECATED("FanState is deprecated, use Fan instead.", "2022.2") FanState : public Fan, public Component {
|
||||||
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<bool> binary_state) {
|
|
||||||
this->binary_state_ = binary_state;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
FanStateCall &set_oscillating(bool oscillating) {
|
|
||||||
this->oscillating_ = oscillating;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
FanStateCall &set_oscillating(optional<bool> 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<FanDirection> direction) {
|
|
||||||
this->direction_ = direction;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
FanState *const state_;
|
|
||||||
optional<bool> binary_state_;
|
|
||||||
optional<bool> oscillating_;
|
|
||||||
optional<int> speed_;
|
|
||||||
optional<FanDirection> direction_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class FanState : public EntityBase, public Component {
|
|
||||||
public:
|
public:
|
||||||
FanState() = default;
|
FanState() = default;
|
||||||
/// Construct the fan state with name.
|
explicit FanState(const std::string &name) : Fan(name) {}
|
||||||
explicit FanState(const std::string &name);
|
|
||||||
|
|
||||||
/// Register a callback that will be called each time the state changes.
|
/// Get the traits of this fan.
|
||||||
void add_on_state_callback(std::function<void()> &&callback);
|
FanTraits get_traits() override { return this->traits_; }
|
||||||
|
|
||||||
/// Get the traits of this fan (i.e. what features it supports).
|
|
||||||
const FanTraits &get_traits() const;
|
|
||||||
/// Set the traits of this fan (i.e. what features it supports).
|
/// Set the traits of this fan (i.e. what features it supports).
|
||||||
void set_traits(const FanTraits &traits);
|
void set_traits(const FanTraits &traits) { this->traits_ = 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 setup() override;
|
void setup() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend FanStateCall;
|
void control(const FanCall &call) override { this->publish_state(); }
|
||||||
|
|
||||||
uint32_t hash_base() override;
|
|
||||||
|
|
||||||
FanTraits traits_{};
|
FanTraits traits_{};
|
||||||
CallbackManager<void()> state_callback_{};
|
|
||||||
ESPPreferenceObject rtc_;
|
|
||||||
|
|
||||||
/// Restore mode of the fan.
|
|
||||||
FanRestoreMode restore_mode_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fan
|
} // namespace fan
|
||||||
|
|
|
@ -17,7 +17,7 @@ from .. import hbridge_ns
|
||||||
CODEOWNERS = ["@WeekendWarrior"]
|
CODEOWNERS = ["@WeekendWarrior"]
|
||||||
|
|
||||||
|
|
||||||
HBridgeFan = hbridge_ns.class_("HBridgeFan", fan.FanState)
|
HBridgeFan = hbridge_ns.class_("HBridgeFan", cg.Component, fan.Fan)
|
||||||
|
|
||||||
DecayMode = hbridge_ns.enum("DecayMode")
|
DecayMode = hbridge_ns.enum("DecayMode")
|
||||||
DECAY_MODE_OPTIONS = {
|
DECAY_MODE_OPTIONS = {
|
||||||
|
@ -59,6 +59,7 @@ async def to_code(config):
|
||||||
config[CONF_SPEED_COUNT],
|
config[CONF_SPEED_COUNT],
|
||||||
config[CONF_DECAY_MODE],
|
config[CONF_DECAY_MODE],
|
||||||
)
|
)
|
||||||
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
await fan.register_fan(var, config)
|
||||||
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
||||||
cg.add(var.set_pin_a(pin_a_))
|
cg.add(var.set_pin_a(pin_a_))
|
||||||
|
|
|
@ -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);
|
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");
|
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);
|
(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);
|
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() {
|
void HBridgeFan::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Fan '%s':", this->get_name().c_str());
|
LOG_FAN("", "H-Bridge Fan", this);
|
||||||
if (this->get_traits().supports_oscillation()) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Oscillation: YES");
|
|
||||||
}
|
|
||||||
if (this->get_traits().supports_direction()) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Direction: YES");
|
|
||||||
}
|
|
||||||
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
||||||
ESP_LOGCONFIG(TAG, " Decay Mode: Slow");
|
ESP_LOGCONFIG(TAG, " Decay Mode: Slow");
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGCONFIG(TAG, " Decay Mode: Fast");
|
ESP_LOGCONFIG(TAG, " Decay Mode: Fast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void HBridgeFan::setup() {
|
fan::FanTraits HBridgeFan::get_traits() {
|
||||||
auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_);
|
return fan::FanTraits(this->oscillating_ != nullptr, true, true, this->speed_count_);
|
||||||
this->set_traits(traits);
|
|
||||||
this->add_on_state_callback([this]() { this->next_update_ = true; });
|
|
||||||
}
|
}
|
||||||
void HBridgeFan::loop() {
|
void HBridgeFan::control(const fan::FanCall &call) {
|
||||||
if (!this->next_update_) {
|
if (call.get_state().has_value())
|
||||||
return;
|
this->state = *call.get_state();
|
||||||
}
|
if (call.get_speed().has_value())
|
||||||
this->next_update_ = false;
|
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;
|
this->write_state_();
|
||||||
if (this->state) {
|
this->publish_state();
|
||||||
speed = static_cast<float>(this->speed) / static_cast<float>(this->speed_count_);
|
}
|
||||||
}
|
void HBridgeFan::write_state_() {
|
||||||
|
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(this->speed_count_) : 0.0f;
|
||||||
if (speed == 0.0f) { // off means idle
|
if (speed == 0.0f) { // off means idle
|
||||||
(this->enable_ == nullptr) ? this->set_hbridge_levels_(speed, speed)
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(speed, speed)
|
||||||
: this->set_hbridge_levels_(speed, speed, speed);
|
: this->set_hbridge_levels_(speed, speed, speed);
|
||||||
return;
|
} else if (this->direction == fan::FanDirection::FORWARD) {
|
||||||
}
|
|
||||||
if (this->direction == fan::FAN_DIRECTION_FORWARD) {
|
|
||||||
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
if (this->decay_mode_ == DECAY_MODE_SLOW) {
|
||||||
(this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f - speed, 1.0f)
|
(this->enable_ == nullptr) ? this->set_hbridge_levels_(1.0f - speed, 1.0f)
|
||||||
: this->set_hbridge_levels_(1.0f - speed, 1.0f, 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);
|
: this->set_hbridge_levels_(1.0f, 0.0f, speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->oscillating_ != nullptr)
|
||||||
|
this->oscillating_->set_state(this->oscillating);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace hbridge
|
} // namespace hbridge
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/components/output/binary_output.h"
|
#include "esphome/components/output/binary_output.h"
|
||||||
#include "esphome/components/output/float_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 esphome {
|
||||||
namespace hbridge {
|
namespace hbridge {
|
||||||
|
@ -13,7 +13,7 @@ enum DecayMode {
|
||||||
DECAY_MODE_FAST = 1,
|
DECAY_MODE_FAST = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
class HBridgeFan : public fan::FanState {
|
class HBridgeFan : public Component, public fan::Fan {
|
||||||
public:
|
public:
|
||||||
HBridgeFan(int speed_count, DecayMode decay_mode) : speed_count_(speed_count), decay_mode_(decay_mode) {}
|
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 set_enable_pin(output::FloatOutput *enable) { enable_ = enable; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
fan::FanTraits get_traits() override;
|
||||||
|
|
||||||
fan::FanStateCall brake();
|
fan::FanCall 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; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
output::FloatOutput *pin_a_;
|
output::FloatOutput *pin_a_;
|
||||||
output::FloatOutput *pin_b_;
|
output::FloatOutput *pin_b_;
|
||||||
output::FloatOutput *enable_{nullptr};
|
output::FloatOutput *enable_{nullptr};
|
||||||
output::BinaryOutput *oscillating_{nullptr};
|
output::BinaryOutput *oscillating_{nullptr};
|
||||||
bool next_update_{true};
|
|
||||||
int speed_count_{};
|
int speed_count_{};
|
||||||
DecayMode decay_mode_{DECAY_MODE_SLOW};
|
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);
|
||||||
void set_hbridge_levels_(float a_level, float b_level, float enable);
|
void set_hbridge_levels_(float a_level, float b_level, float enable);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,9 +14,9 @@ static const char *const TAG = "mqtt.fan";
|
||||||
|
|
||||||
using namespace esphome::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"; }
|
std::string MQTTFanComponent::component_type() const { return "fan"; }
|
||||||
const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
|
const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace mqtt {
|
||||||
|
|
||||||
class MQTTFanComponent : public mqtt::MQTTComponent {
|
class MQTTFanComponent : public mqtt::MQTTComponent {
|
||||||
public:
|
public:
|
||||||
explicit MQTTFanComponent(fan::FanState *state);
|
explicit MQTTFanComponent(fan::Fan *state);
|
||||||
|
|
||||||
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command)
|
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command)
|
||||||
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state)
|
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state)
|
||||||
|
@ -37,12 +37,12 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
|
||||||
/// 'fan' component type for discovery.
|
/// 'fan' component type for discovery.
|
||||||
std::string component_type() const override;
|
std::string component_type() const override;
|
||||||
|
|
||||||
fan::FanState *get_state() const;
|
fan::Fan *get_state() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const EntityBase *get_entity() const override;
|
const EntityBase *get_entity() const override;
|
||||||
|
|
||||||
fan::FanState *state_;
|
fan::Fan *state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mqtt
|
} // namespace mqtt
|
||||||
|
|
|
@ -30,6 +30,14 @@ class BinaryOutput {
|
||||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); }
|
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); }
|
||||||
#endif
|
#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.
|
/// Enable this binary output.
|
||||||
virtual void turn_on() {
|
virtual void turn_on() {
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
|
|
@ -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_speed GAUGE\n"));
|
||||||
stream->print(F("#TYPE esphome_fan_oscillation 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())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
stream->print(F("esphome_fan_failed{id=\""));
|
stream->print(F("esphome_fan_failed{id=\""));
|
||||||
|
|
|
@ -52,7 +52,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
|
||||||
/// Return the type for prometheus
|
/// Return the type for prometheus
|
||||||
void fan_type_(AsyncResponseStream *stream);
|
void fan_type_(AsyncResponseStream *stream);
|
||||||
/// Return the sensor state as prometheus data point
|
/// 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
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
|
|
|
@ -11,7 +11,7 @@ from esphome.const import (
|
||||||
)
|
)
|
||||||
from .. import speed_ns
|
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(
|
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
|
@ -29,11 +29,9 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
state = await fan.create_fan_state(config)
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], output_, config[CONF_SPEED_COUNT])
|
||||||
var = cg.new_Pvariable(
|
|
||||||
config[CONF_OUTPUT_ID], state, output_, config[CONF_SPEED_COUNT]
|
|
||||||
)
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
await fan.register_fan(var, config)
|
||||||
|
|
||||||
if CONF_OSCILLATION_OUTPUT in config:
|
if CONF_OSCILLATION_OUTPUT in config:
|
||||||
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||||
|
|
|
@ -7,59 +7,39 @@ namespace speed {
|
||||||
|
|
||||||
static const char *const TAG = "speed.fan";
|
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() {
|
void SpeedFan::setup() {
|
||||||
auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_);
|
auto restore = this->restore_state_();
|
||||||
this->fan_->set_traits(traits);
|
if (restore.has_value()) {
|
||||||
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
|
restore->apply(*this);
|
||||||
}
|
this->write_state_();
|
||||||
void SpeedFan::loop() {
|
|
||||||
if (!this->next_update_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->next_update_ = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
float speed = 0.0f;
|
|
||||||
if (this->fan_->state) {
|
|
||||||
speed = static_cast<float>(this->fan_->speed) / static_cast<float>(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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
this->write_state_();
|
||||||
// when that component sets itself up.
|
this->publish_state();
|
||||||
float SpeedFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; }
|
}
|
||||||
|
void SpeedFan::write_state_() {
|
||||||
|
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(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 speed
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -3,28 +3,27 @@
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/output/binary_output.h"
|
#include "esphome/components/output/binary_output.h"
|
||||||
#include "esphome/components/output/float_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 esphome {
|
||||||
namespace speed {
|
namespace speed {
|
||||||
|
|
||||||
class SpeedFan : public Component {
|
class SpeedFan : public Component, public fan::Fan {
|
||||||
public:
|
public:
|
||||||
SpeedFan(fan::FanState *fan, output::FloatOutput *output, int speed_count)
|
SpeedFan(output::FloatOutput *output, int speed_count) : output_(output), speed_count_(speed_count) {}
|
||||||
: fan_(fan), output_(output), speed_count_(speed_count) {}
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
|
||||||
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
||||||
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
||||||
|
fan::FanTraits get_traits() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
fan::FanState *fan_;
|
void control(const fan::FanCall &call) override;
|
||||||
|
void write_state_();
|
||||||
|
|
||||||
output::FloatOutput *output_;
|
output::FloatOutput *output_;
|
||||||
output::BinaryOutput *oscillating_{nullptr};
|
output::BinaryOutput *oscillating_{nullptr};
|
||||||
output::BinaryOutput *direction_{nullptr};
|
output::BinaryOutput *direction_{nullptr};
|
||||||
bool next_update_{true};
|
|
||||||
int speed_count_{};
|
int speed_count_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ CONF_SPEED_DATAPOINT = "speed_datapoint"
|
||||||
CONF_OSCILLATION_DATAPOINT = "oscillation_datapoint"
|
CONF_OSCILLATION_DATAPOINT = "oscillation_datapoint"
|
||||||
CONF_DIRECTION_DATAPOINT = "direction_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(
|
CONFIG_SCHEMA = cv.All(
|
||||||
fan.FAN_SCHEMA.extend(
|
fan.FAN_SCHEMA.extend(
|
||||||
|
@ -30,12 +30,10 @@ CONFIG_SCHEMA = cv.All(
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
parent = await cg.get_variable(config[CONF_TUYA_ID])
|
parent = await cg.get_variable(config[CONF_TUYA_ID])
|
||||||
state = await fan.create_fan_state(config)
|
|
||||||
|
|
||||||
var = cg.new_Pvariable(
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], parent, config[CONF_SPEED_COUNT])
|
||||||
config[CONF_OUTPUT_ID], parent, state, config[CONF_SPEED_COUNT]
|
|
||||||
)
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
await fan.register_fan(var, config)
|
||||||
|
|
||||||
if CONF_SPEED_DATAPOINT in config:
|
if CONF_SPEED_DATAPOINT in config:
|
||||||
cg.add(var.set_speed_id(config[CONF_SPEED_DATAPOINT]))
|
cg.add(var.set_speed_id(config[CONF_SPEED_DATAPOINT]))
|
||||||
|
|
|
@ -8,52 +8,48 @@ namespace tuya {
|
||||||
static const char *const TAG = "tuya.fan";
|
static const char *const TAG = "tuya.fan";
|
||||||
|
|
||||||
void TuyaFan::setup() {
|
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()) {
|
if (this->speed_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) {
|
this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) {
|
||||||
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum);
|
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum);
|
||||||
auto call = this->fan_->make_call();
|
if (datapoint.value_enum >= this->speed_count_) {
|
||||||
if (datapoint.value_enum < this->speed_count_)
|
ESP_LOGE(TAG, "Speed has invalid value %d", datapoint.value_enum);
|
||||||
call.set_speed(datapoint.value_enum + 1);
|
} else {
|
||||||
else
|
this->speed = datapoint.value_enum + 1;
|
||||||
ESP_LOGCONFIG(TAG, "Speed has invalid value %d", datapoint.value_enum);
|
this->publish_state();
|
||||||
call.perform();
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this->switch_id_.has_value()) {
|
if (this->switch_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
|
this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
|
||||||
ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool));
|
ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool));
|
||||||
auto call = this->fan_->make_call();
|
this->state = datapoint.value_bool;
|
||||||
call.set_state(datapoint.value_bool);
|
this->publish_state();
|
||||||
call.perform();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this->oscillation_id_.has_value()) {
|
if (this->oscillation_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->oscillation_id_, [this](const TuyaDatapoint &datapoint) {
|
this->parent_->register_listener(*this->oscillation_id_, [this](const TuyaDatapoint &datapoint) {
|
||||||
ESP_LOGV(TAG, "MCU reported oscillation is: %s", ONOFF(datapoint.value_bool));
|
ESP_LOGV(TAG, "MCU reported oscillation is: %s", ONOFF(datapoint.value_bool));
|
||||||
auto call = this->fan_->make_call();
|
this->oscillating = datapoint.value_bool;
|
||||||
call.set_oscillating(datapoint.value_bool);
|
this->publish_state();
|
||||||
call.perform();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this->direction_id_.has_value()) {
|
if (this->direction_id_.has_value()) {
|
||||||
this->parent_->register_listener(*this->direction_id_, [this](const TuyaDatapoint &datapoint) {
|
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));
|
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() {
|
void TuyaFan::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Tuya Fan:");
|
LOG_FAN("", "Tuya Fan", this);
|
||||||
ESP_LOGCONFIG(TAG, " Speed count %d", this->speed_count_);
|
|
||||||
if (this->speed_id_.has_value())
|
if (this->speed_id_.has_value())
|
||||||
ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *this->speed_id_);
|
ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *this->speed_id_);
|
||||||
if (this->switch_id_.has_value())
|
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_);
|
ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *this->direction_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TuyaFan::write_state() {
|
fan::FanTraits TuyaFan::get_traits() {
|
||||||
if (this->switch_id_.has_value()) {
|
return fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), this->direction_id_.has_value(),
|
||||||
ESP_LOGV(TAG, "Setting switch: %s", ONOFF(this->fan_->state));
|
this->speed_count_);
|
||||||
this->parent_->set_boolean_datapoint_value(*this->switch_id_, this->fan_->state);
|
}
|
||||||
|
|
||||||
|
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()) {
|
if (this->oscillation_id_.has_value() && call.get_oscillating().has_value()) {
|
||||||
ESP_LOGV(TAG, "Setting oscillating: %s", ONOFF(this->fan_->oscillating));
|
this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, *call.get_oscillating());
|
||||||
this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, this->fan_->oscillating);
|
|
||||||
}
|
}
|
||||||
if (this->direction_id_.has_value()) {
|
if (this->direction_id_.has_value() && call.get_direction().has_value()) {
|
||||||
bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE;
|
bool enable = *call.get_direction() == fan::FanDirection::REVERSE;
|
||||||
ESP_LOGV(TAG, "Setting reverse direction: %s", ONOFF(enable));
|
|
||||||
this->parent_->set_enum_datapoint_value(*this->direction_id_, enable);
|
this->parent_->set_enum_datapoint_value(*this->direction_id_, enable);
|
||||||
}
|
}
|
||||||
if (this->speed_id_.has_value()) {
|
if (this->speed_id_.has_value() && call.get_speed().has_value()) {
|
||||||
ESP_LOGV(TAG, "Setting speed: %d", this->fan_->speed);
|
this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1);
|
||||||
this->parent_->set_enum_datapoint_value(*this->speed_id_, this->fan_->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 tuya
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,35 +2,31 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/tuya/tuya.h"
|
#include "esphome/components/tuya/tuya.h"
|
||||||
#include "esphome/components/fan/fan_state.h"
|
#include "esphome/components/fan/fan.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tuya {
|
namespace tuya {
|
||||||
|
|
||||||
class TuyaFan : public Component {
|
class TuyaFan : public Component, public fan::Fan {
|
||||||
public:
|
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;
|
void setup() override;
|
||||||
float get_setup_priority() const override;
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_speed_id(uint8_t speed_id) { this->speed_id_ = speed_id; }
|
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_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_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 set_direction_id(uint8_t direction_id) { this->direction_id_ = direction_id; }
|
||||||
void write_state();
|
|
||||||
|
fan::FanTraits get_traits() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void update_speed_(uint32_t value);
|
void control(const fan::FanCall &call) override;
|
||||||
void update_switch_(uint32_t value);
|
|
||||||
void update_oscillation_(uint32_t value);
|
|
||||||
void update_direction_(uint32_t value);
|
|
||||||
|
|
||||||
Tuya *parent_;
|
Tuya *parent_;
|
||||||
optional<uint8_t> speed_id_{};
|
optional<uint8_t> speed_id_{};
|
||||||
optional<uint8_t> switch_id_{};
|
optional<uint8_t> switch_id_{};
|
||||||
optional<uint8_t> oscillation_id_{};
|
optional<uint8_t> oscillation_id_{};
|
||||||
optional<uint8_t> direction_id_{};
|
optional<uint8_t> direction_id_{};
|
||||||
fan::FanState *fan_;
|
|
||||||
int speed_count_{};
|
int speed_count_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -440,8 +440,8 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void WebServer::on_fan_update(fan::FanState *obj) { this->events_.send(this->fan_json(obj).c_str(), "state"); }
|
void WebServer::on_fan_update(fan::Fan *obj) { this->events_.send(this->fan_json(obj).c_str(), "state"); }
|
||||||
std::string WebServer::fan_json(fan::FanState *obj) {
|
std::string WebServer::fan_json(fan::Fan *obj) {
|
||||||
return json::build_json([obj](JsonObject root) {
|
return json::build_json([obj](JsonObject root) {
|
||||||
root["id"] = "fan-" + obj->get_object_id();
|
root["id"] = "fan-" + obj->get_object_id();
|
||||||
root["state"] = obj->state ? "ON" : "OFF";
|
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) {
|
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)
|
if (obj->get_object_id() != match.id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -516,7 +516,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->defer([call]() { call.perform(); });
|
this->defer([call]() mutable { call.perform(); });
|
||||||
request->send(200);
|
request->send(200);
|
||||||
} else if (match.method == "turn_off") {
|
} else if (match.method == "turn_off") {
|
||||||
this->defer([obj]() { obj->turn_off().perform(); });
|
this->defer([obj]() { obj->turn_off().perform(); });
|
||||||
|
|
|
@ -128,13 +128,13 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#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/<id>/</turn_on/turn_off/toggle>'.
|
/// Handle a fan request under '/fan/<id>/</turn_on/turn_off/toggle>'.
|
||||||
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||||
|
|
||||||
/// Dump the fan state as a JSON string.
|
/// Dump the fan state as a JSON string.
|
||||||
std::string fan_json(fan::FanState *obj);
|
std::string fan_json(fan::Fan *obj);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Application {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
|
@ -204,8 +204,8 @@ class Application {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
const std::vector<fan::FanState *> &get_fans() { return this->fans_; }
|
const std::vector<fan::Fan *> &get_fans() { return this->fans_; }
|
||||||
fan::FanState *get_fan_by_key(uint32_t key, bool include_internal = false) {
|
fan::Fan *get_fan_by_key(uint32_t key, bool include_internal = false) {
|
||||||
for (auto *obj : this->fans_)
|
for (auto *obj : this->fans_)
|
||||||
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -288,7 +288,7 @@ class Application {
|
||||||
std::vector<text_sensor::TextSensor *> text_sensors_{};
|
std::vector<text_sensor::TextSensor *> text_sensors_{};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
std::vector<fan::FanState *> fans_{};
|
std::vector<fan::Fan *> fans_{};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
std::vector<cover::Cover *> covers_{};
|
std::vector<cover::Cover *> covers_{};
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Controller {
|
||||||
virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){};
|
virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state){};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
virtual void on_fan_update(fan::FanState *obj){};
|
virtual void on_fan_update(fan::Fan *obj){};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
virtual void on_light_update(light::LightState *obj){};
|
virtual void on_light_update(light::LightState *obj){};
|
||||||
|
|
|
@ -594,6 +594,7 @@ def lint_inclusive_language(fname, match):
|
||||||
"esphome/components/binary_sensor/binary_sensor.h",
|
"esphome/components/binary_sensor/binary_sensor.h",
|
||||||
"esphome/components/cover/cover.h",
|
"esphome/components/cover/cover.h",
|
||||||
"esphome/components/display/display_buffer.h",
|
"esphome/components/display/display_buffer.h",
|
||||||
|
"esphome/components/fan/fan.h",
|
||||||
"esphome/components/i2c/i2c.h",
|
"esphome/components/i2c/i2c.h",
|
||||||
"esphome/components/mqtt/mqtt_component.h",
|
"esphome/components/mqtt/mqtt_component.h",
|
||||||
"esphome/components/number/number.h",
|
"esphome/components/number/number.h",
|
||||||
|
|
Loading…
Reference in a new issue