ESPHome 2025.5.0
Loading...
Searching...
No Matches
fan.cpp
Go to the documentation of this file.
1#include "fan.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace fan {
6
7static const char *const TAG = "fan";
8
10 switch (direction) {
12 return LOG_STR("FORWARD");
14 return LOG_STR("REVERSE");
15 default:
16 return LOG_STR("UNKNOWN");
17 }
18}
19
21 ESP_LOGD(TAG, "'%s' - Setting:", this->parent_.get_name().c_str());
22 this->validate_();
23 if (this->binary_state_.has_value()) {
24 ESP_LOGD(TAG, " State: %s", ONOFF(*this->binary_state_));
25 }
26 if (this->oscillating_.has_value()) {
27 ESP_LOGD(TAG, " Oscillating: %s", YESNO(*this->oscillating_));
28 }
29 if (this->speed_.has_value()) {
30 ESP_LOGD(TAG, " Speed: %d", *this->speed_);
31 }
32 if (this->direction_.has_value()) {
33 ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(*this->direction_)));
34 }
35 if (!this->preset_mode_.empty()) {
36 ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode_.c_str());
37 }
38 this->parent_.control(*this);
39}
40
42 auto traits = this->parent_.get_traits();
43
44 if (this->speed_.has_value())
45 this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
46
47 if (this->binary_state_.has_value() && *this->binary_state_) {
48 // when turning on, if neither current nor new speed available, set speed to 100%
49 if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) {
50 this->speed_ = traits.supported_speed_count();
51 }
52 }
53
54 if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
55 ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str());
56 this->oscillating_.reset();
57 }
58
59 if (this->speed_.has_value() && !traits.supports_speed()) {
60 ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str());
61 this->speed_.reset();
62 }
63
64 if (this->direction_.has_value() && !traits.supports_direction()) {
65 ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str());
66 this->direction_.reset();
67 }
68
69 if (!this->preset_mode_.empty()) {
70 const auto &preset_modes = traits.supported_preset_modes();
71 if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
72 ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(),
73 this->preset_mode_.c_str());
74 this->preset_mode_.clear();
75 }
76 }
77}
78
80 auto call = fan.make_call();
81 call.set_state(this->state);
82 call.set_oscillating(this->oscillating);
83 call.set_speed(this->speed);
84 call.set_direction(this->direction);
85
87 // Use stored preset index to get preset name
88 const auto &preset_modes = fan.get_traits().supported_preset_modes();
89 if (this->preset_mode < preset_modes.size()) {
90 call.set_preset_mode(*std::next(preset_modes.begin(), this->preset_mode));
91 }
92 }
93 return call;
94}
96 fan.state = this->state;
97 fan.oscillating = this->oscillating;
98 fan.speed = this->speed;
99 fan.direction = this->direction;
100
101 if (fan.get_traits().supports_preset_modes()) {
102 // Use stored preset index to get preset name
103 const auto &preset_modes = fan.get_traits().supported_preset_modes();
104 if (this->preset_mode < preset_modes.size()) {
105 fan.preset_mode = *std::next(preset_modes.begin(), this->preset_mode);
106 }
107 }
108 fan.publish_state();
109}
110
111FanCall Fan::turn_on() { return this->make_call().set_state(true); }
112FanCall Fan::turn_off() { return this->make_call().set_state(false); }
113FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
114FanCall Fan::make_call() { return FanCall(*this); }
115
116void Fan::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
118 auto traits = this->get_traits();
119
120 ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
121 ESP_LOGD(TAG, " State: %s", ONOFF(this->state));
122 if (traits.supports_speed()) {
123 ESP_LOGD(TAG, " Speed: %d", this->speed);
124 }
125 if (traits.supports_oscillation()) {
126 ESP_LOGD(TAG, " Oscillating: %s", YESNO(this->oscillating));
127 }
128 if (traits.supports_direction()) {
129 ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction)));
130 }
131 if (traits.supports_preset_modes() && !this->preset_mode.empty()) {
132 ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode.c_str());
133 }
134 this->state_callback_.call();
135 this->save_state_();
136}
137
138// Random 32-bit value, change this every time the layout of the FanRestoreState struct changes.
139constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
141 FanRestoreState recovered{};
143 bool restored = this->rtc_.load(&recovered);
144
145 switch (this->restore_mode_) {
147 return {};
149 recovered.state = false;
150 return recovered;
152 recovered.state = true;
153 return recovered;
155 recovered.state = restored ? recovered.state : false;
156 return recovered;
158 recovered.state = restored ? recovered.state : true;
159 return recovered;
161 recovered.state = restored ? !recovered.state : false;
162 return recovered;
164 recovered.state = restored ? !recovered.state : true;
165 return recovered;
166 }
167
168 return {};
169}
172 state.state = this->state;
173 state.oscillating = this->oscillating;
174 state.speed = this->speed;
175 state.direction = this->direction;
176
177 if (this->get_traits().supports_preset_modes() && !this->preset_mode.empty()) {
178 const auto &preset_modes = this->get_traits().supported_preset_modes();
179 // Store index of current preset mode
180 auto preset_iterator = preset_modes.find(this->preset_mode);
181 if (preset_iterator != preset_modes.end())
182 state.preset_mode = std::distance(preset_modes.begin(), preset_iterator);
183 }
184
185 this->rtc_.save(&state);
186}
187
188void Fan::dump_traits_(const char *tag, const char *prefix) {
189 auto traits = this->get_traits();
190
191 if (traits.supports_speed()) {
192 ESP_LOGCONFIG(tag, "%s Speed: YES", prefix);
193 ESP_LOGCONFIG(tag, "%s Speed count: %d", prefix, traits.supported_speed_count());
194 }
195 if (traits.supports_oscillation()) {
196 ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix);
197 }
198 if (traits.supports_direction()) {
199 ESP_LOGCONFIG(tag, "%s Direction: YES", prefix);
200 }
201 if (traits.supports_preset_modes()) {
202 ESP_LOGCONFIG(tag, "%s Supported presets:", prefix);
203 for (const std::string &s : traits.supported_preset_modes())
204 ESP_LOGCONFIG(tag, "%s - %s", prefix, s.c_str());
205 }
206}
207
208} // namespace fan
209} // namespace esphome
bool save(const T *src)
Definition preferences.h:21
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
uint32_t get_object_id_hash()
const StringRef & get_name() const
constexpr const char * c_str() const
Definition string_ref.h:68
FanCall & set_oscillating(bool oscillating)
Definition fan.h:50
FanCall & set_direction(FanDirection direction)
Definition fan.h:66
optional< bool > binary_state_
Definition fan.h:87
optional< FanDirection > direction_
Definition fan.h:90
optional< int > speed_
Definition fan.h:89
std::string preset_mode_
Definition fan.h:91
FanCall & set_speed(int speed)
Definition fan.h:59
FanCall & set_state(bool binary_state)
Definition fan.h:41
optional< bool > oscillating_
Definition fan.h:88
friend FanCall
Definition fan.h:136
void publish_state()
Definition fan.cpp:117
FanCall turn_on()
Definition fan.cpp:111
FanCall turn_off()
Definition fan.cpp:112
FanCall make_call()
Definition fan.cpp:114
virtual FanTraits get_traits()=0
FanCall toggle()
Definition fan.cpp:113
ESPPreferenceObject rtc_
Definition fan.h:146
void add_on_state_callback(std::function< void()> &&callback)
Register a callback that will be called each time the state changes.
Definition fan.cpp:116
FanDirection direction
The current direction of the fan.
Definition fan.h:116
void save_state_()
Definition fan.cpp:170
FanRestoreMode restore_mode_
Definition fan.h:147
std::string preset_mode
Definition fan.h:118
CallbackManager< void()> state_callback_
Definition fan.h:145
bool oscillating
The current oscillation state of the fan.
Definition fan.h:112
virtual void control(const FanCall &call)=0
bool state
The current on/off state of the fan.
Definition fan.h:110
int speed
The current fan speed level.
Definition fan.h:114
void dump_traits_(const char *tag, const char *prefix)
Definition fan.cpp:188
optional< FanRestoreState > restore_state_()
Definition fan.cpp:140
bool supports_preset_modes() const
Return if preset modes are supported.
Definition fan_traits.h:36
std::set< std::string > supported_preset_modes() const
Return the preset modes supported by the fan.
Definition fan_traits.h:32
bool has_value() const
Definition optional.h:87
FanDirection direction
Definition fan.h:3
const LogString * fan_direction_to_string(FanDirection direction)
Definition fan.cpp:9
constexpr uint32_t RESTORE_STATE_VERSION
Definition fan.cpp:139
FanDirection
Simple enum to represent the direction of a fan.
Definition fan.h:20
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
ESPPreferences * global_preferences
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition helpers.h:101
void apply(Fan &fan)
Apply these settings to the fan.
Definition fan.cpp:95
FanDirection direction
Definition fan.h:98
FanCall to_call(Fan &fan)
Convert this struct to a fan call that can be performed.
Definition fan.cpp:79