1
0
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:
Jim Ekman
2021-03-17 14:40:02 +01:00
committed by GitHub
parent 08998caabc
commit 7708b81ef5
22 changed files with 182 additions and 99 deletions

View File

@@ -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

View File

@@ -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();

View 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

View 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

View File

@@ -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;
}

View File

@@ -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};

View File

@@ -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