mirror of
https://github.com/esphome/esphome.git
synced 2025-10-29 22:24:26 +00:00
Support fan speed levels (#1541)
* Add fan speed percentage support to the API * Add float fan speed percentage * Add percentage support to automation and configuration * Update Tuya fan * Fix pylint warning * Update API to use speed levels instead of percentage * Use speed levels * Fix type warnings * MQTT component now converts between speed levels and enums * Webserver now supports speed_level * Update prometheus * Remove low/medium/high settings from speed fan * Remove unused enum * Configurable speed levels for speed fan * Remove unused import * Rename speed_level->speed and speed_levels->speed_count * Rename supported_speed_levels -> supported_speed_count in API and FanTraits Field id stays the same in the protocol, so the change is not breaking for aioesphome.
This commit is contained in:
@@ -28,14 +28,6 @@ TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action)
|
||||
TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action)
|
||||
ToggleAction = fan_ns.class_("ToggleAction", automation.Action)
|
||||
|
||||
FanSpeed = fan_ns.enum("FanSpeed")
|
||||
FAN_SPEEDS = {
|
||||
"OFF": FanSpeed.FAN_SPEED_OFF,
|
||||
"LOW": FanSpeed.FAN_SPEED_LOW,
|
||||
"MEDIUM": FanSpeed.FAN_SPEED_MEDIUM,
|
||||
"HIGH": FanSpeed.FAN_SPEED_HIGH,
|
||||
}
|
||||
|
||||
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(FanState),
|
||||
@@ -128,7 +120,7 @@ def fan_turn_off_to_code(config, action_id, template_arg, args):
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(FanState),
|
||||
cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
|
||||
cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)),
|
||||
cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)),
|
||||
}
|
||||
),
|
||||
)
|
||||
@@ -139,7 +131,7 @@ def fan_turn_on_to_code(config, action_id, template_arg, args):
|
||||
template_ = yield cg.templatable(config[CONF_OSCILLATING], args, bool)
|
||||
cg.add(var.set_oscillating(template_))
|
||||
if CONF_SPEED in config:
|
||||
template_ = yield cg.templatable(config[CONF_SPEED], args, FanSpeed)
|
||||
template_ = yield cg.templatable(config[CONF_SPEED], args, int)
|
||||
cg.add(var.set_speed(template_))
|
||||
yield var
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||
explicit TurnOnAction(FanState *state) : state_(state) {}
|
||||
|
||||
TEMPLATABLE_VALUE(bool, oscillating)
|
||||
TEMPLATABLE_VALUE(FanSpeed, speed)
|
||||
TEMPLATABLE_VALUE(int, speed)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto call = this->state_->turn_on();
|
||||
|
||||
20
esphome/components/fan/fan_helpers.cpp
Normal file
20
esphome/components/fan/fan_helpers.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <cassert>
|
||||
#include "fan_helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fan {
|
||||
|
||||
FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels) {
|
||||
const auto speed_ratio = static_cast<float>(speed_level) / (supported_speed_levels + 1);
|
||||
const auto legacy_level = static_cast<int>(clamp(ceilf(speed_ratio * 3), 1, 3));
|
||||
return static_cast<FanSpeed>(legacy_level - 1);
|
||||
}
|
||||
|
||||
int speed_enum_to_level(FanSpeed speed, int supported_speed_levels) {
|
||||
const auto enum_level = static_cast<int>(speed) + 1;
|
||||
const auto speed_level = roundf(enum_level / 3.0f * supported_speed_levels);
|
||||
return static_cast<int>(speed_level);
|
||||
}
|
||||
|
||||
} // namespace fan
|
||||
} // namespace esphome
|
||||
11
esphome/components/fan/fan_helpers.h
Normal file
11
esphome/components/fan/fan_helpers.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "fan_state.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fan {
|
||||
|
||||
FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels);
|
||||
int speed_enum_to_level(FanSpeed speed, int supported_speed_levels);
|
||||
|
||||
} // namespace fan
|
||||
} // namespace esphome
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "fan_state.h"
|
||||
#include "fan_helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -20,7 +21,7 @@ FanStateCall FanState::make_call() { return FanStateCall(this); }
|
||||
|
||||
struct FanStateRTCState {
|
||||
bool state;
|
||||
FanSpeed speed;
|
||||
int speed;
|
||||
bool oscillating;
|
||||
FanDirection direction;
|
||||
};
|
||||
@@ -52,16 +53,8 @@ void FanStateCall::perform() const {
|
||||
this->state_->direction = *this->direction_;
|
||||
}
|
||||
if (this->speed_.has_value()) {
|
||||
switch (*this->speed_) {
|
||||
case FAN_SPEED_LOW:
|
||||
case FAN_SPEED_MEDIUM:
|
||||
case FAN_SPEED_HIGH:
|
||||
this->state_->speed = *this->speed_;
|
||||
break;
|
||||
default:
|
||||
// protect from invalid input
|
||||
break;
|
||||
}
|
||||
const int speed_count = this->state_->get_traits().supported_speed_count();
|
||||
this->state_->speed = static_cast<int>(clamp(*this->speed_, 1, speed_count));
|
||||
}
|
||||
|
||||
FanStateRTCState saved{};
|
||||
@@ -73,13 +66,15 @@ void FanStateCall::perform() const {
|
||||
|
||||
this->state_->state_callback_.call();
|
||||
}
|
||||
FanStateCall &FanStateCall::set_speed(const char *speed) {
|
||||
if (strcasecmp(speed, "low") == 0) {
|
||||
this->set_speed(FAN_SPEED_LOW);
|
||||
} else if (strcasecmp(speed, "medium") == 0) {
|
||||
this->set_speed(FAN_SPEED_MEDIUM);
|
||||
} else if (strcasecmp(speed, "high") == 0) {
|
||||
this->set_speed(FAN_SPEED_HIGH);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "fan_traits.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace fan {
|
||||
|
||||
/// Simple enum to represent the speed of a fan.
|
||||
/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon
|
||||
enum FanSpeed {
|
||||
FAN_SPEED_LOW = 0, ///< The fan is running on low speed.
|
||||
FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed.
|
||||
@@ -40,15 +41,11 @@ class FanStateCall {
|
||||
this->oscillating_ = oscillating;
|
||||
return *this;
|
||||
}
|
||||
FanStateCall &set_speed(FanSpeed speed) {
|
||||
FanStateCall &set_speed(int speed) {
|
||||
this->speed_ = speed;
|
||||
return *this;
|
||||
}
|
||||
FanStateCall &set_speed(optional<FanSpeed> speed) {
|
||||
this->speed_ = speed;
|
||||
return *this;
|
||||
}
|
||||
FanStateCall &set_speed(const char *speed);
|
||||
FanStateCall &set_speed(const char *legacy_speed);
|
||||
FanStateCall &set_direction(FanDirection direction) {
|
||||
this->direction_ = direction;
|
||||
return *this;
|
||||
@@ -63,8 +60,8 @@ class FanStateCall {
|
||||
protected:
|
||||
FanState *const state_;
|
||||
optional<bool> binary_state_;
|
||||
optional<bool> oscillating_{};
|
||||
optional<FanSpeed> speed_{};
|
||||
optional<bool> oscillating_;
|
||||
optional<int> speed_;
|
||||
optional<FanDirection> direction_{};
|
||||
};
|
||||
|
||||
@@ -86,8 +83,8 @@ class FanState : public Nameable, public Component {
|
||||
bool state{false};
|
||||
/// The current oscillation state of the fan.
|
||||
bool oscillating{false};
|
||||
/// The current fan speed.
|
||||
FanSpeed speed{FAN_SPEED_HIGH};
|
||||
/// The current fan speed level
|
||||
int speed{};
|
||||
/// The current direction of the fan
|
||||
FanDirection direction{FAN_DIRECTION_FORWARD};
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace fan {
|
||||
class FanTraits {
|
||||
public:
|
||||
FanTraits() = default;
|
||||
FanTraits(bool oscillation, bool speed, bool direction)
|
||||
: oscillation_(oscillation), speed_(speed), direction_(direction) {}
|
||||
FanTraits(bool oscillation, bool speed, bool direction, int speed_count)
|
||||
: oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {}
|
||||
|
||||
/// Return if this fan supports oscillation.
|
||||
bool supports_oscillation() const { return this->oscillation_; }
|
||||
@@ -15,8 +15,12 @@ class FanTraits {
|
||||
void set_oscillation(bool oscillation) { this->oscillation_ = oscillation; }
|
||||
/// Return if this fan supports speed modes.
|
||||
bool supports_speed() const { return this->speed_; }
|
||||
/// Set whether this fan supports speed modes.
|
||||
/// Set whether this fan supports speed levels.
|
||||
void set_speed(bool speed) { this->speed_ = speed; }
|
||||
/// Return how many speed levels the fan has
|
||||
int supported_speed_count() const { return this->speed_count_; }
|
||||
/// Set how many speed levels this fan has.
|
||||
void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; }
|
||||
/// Return if this fan supports changing direction
|
||||
bool supports_direction() const { return this->direction_; }
|
||||
/// Set whether this fan supports changing direction
|
||||
@@ -26,6 +30,7 @@ class FanTraits {
|
||||
bool oscillation_{false};
|
||||
bool speed_{false};
|
||||
bool direction_{false};
|
||||
int speed_count_{};
|
||||
};
|
||||
|
||||
} // namespace fan
|
||||
|
||||
Reference in New Issue
Block a user