ESPHome 2025.6.3
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 // https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
48 // "Manually setting a speed must disable any set preset mode"
49 this->preset_mode_.clear();
50 }
51
52 if (!this->preset_mode_.empty()) {
53 const auto &preset_modes = traits.supported_preset_modes();
54 if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
55 ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
56 this->preset_mode_.clear();
57 }
58 }
59
60 // when turning on...
61 if (!this->parent_.state && this->binary_state_.has_value() &&
62 *this->binary_state_
63 // ..,and no preset mode will be active...
64 && this->preset_mode_.empty() &&
65 this->parent_.preset_mode.empty()
66 // ...and neither current nor new speed is available...
67 && traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) {
68 // ...set speed to 100%
69 this->speed_ = traits.supported_speed_count();
70 }
71
72 if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
73 ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str());
74 this->oscillating_.reset();
75 }
76
77 if (this->speed_.has_value() && !traits.supports_speed()) {
78 ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str());
79 this->speed_.reset();
80 }
81
82 if (this->direction_.has_value() && !traits.supports_direction()) {
83 ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str());
84 this->direction_.reset();
85 }
86}
87
89 auto call = fan.make_call();
90 call.set_state(this->state);
91 call.set_oscillating(this->oscillating);
92 call.set_speed(this->speed);
93 call.set_direction(this->direction);
94
96 // Use stored preset index to get preset name
97 const auto &preset_modes = fan.get_traits().supported_preset_modes();
98 if (this->preset_mode < preset_modes.size()) {
99 call.set_preset_mode(*std::next(preset_modes.begin(), this->preset_mode));
100 }
101 }
102 return call;
103}
105 fan.state = this->state;
106 fan.oscillating = this->oscillating;
107 fan.speed = this->speed;
108 fan.direction = this->direction;
109
110 if (fan.get_traits().supports_preset_modes()) {
111 // Use stored preset index to get preset name
112 const auto &preset_modes = fan.get_traits().supported_preset_modes();
113 if (this->preset_mode < preset_modes.size()) {
114 fan.preset_mode = *std::next(preset_modes.begin(), this->preset_mode);
115 }
116 }
117 fan.publish_state();
118}
119
120FanCall Fan::turn_on() { return this->make_call().set_state(true); }
121FanCall Fan::turn_off() { return this->make_call().set_state(false); }
122FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
123FanCall Fan::make_call() { return FanCall(*this); }
124
125void Fan::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
127 auto traits = this->get_traits();
128
129 ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
130 ESP_LOGD(TAG, " State: %s", ONOFF(this->state));
131 if (traits.supports_speed()) {
132 ESP_LOGD(TAG, " Speed: %d", this->speed);
133 }
134 if (traits.supports_oscillation()) {
135 ESP_LOGD(TAG, " Oscillating: %s", YESNO(this->oscillating));
136 }
137 if (traits.supports_direction()) {
138 ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction)));
139 }
140 if (traits.supports_preset_modes() && !this->preset_mode.empty()) {
141 ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode.c_str());
142 }
143 this->state_callback_.call();
144 this->save_state_();
145}
146
147// Random 32-bit value, change this every time the layout of the FanRestoreState struct changes.
148constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
150 FanRestoreState recovered{};
152 bool restored = this->rtc_.load(&recovered);
153
154 switch (this->restore_mode_) {
156 return {};
158 recovered.state = false;
159 return recovered;
161 recovered.state = true;
162 return recovered;
164 recovered.state = restored ? recovered.state : false;
165 return recovered;
167 recovered.state = restored ? recovered.state : true;
168 return recovered;
170 recovered.state = restored ? !recovered.state : false;
171 return recovered;
173 recovered.state = restored ? !recovered.state : true;
174 return recovered;
175 }
176
177 return {};
178}
181 state.state = this->state;
182 state.oscillating = this->oscillating;
183 state.speed = this->speed;
184 state.direction = this->direction;
185
186 if (this->get_traits().supports_preset_modes() && !this->preset_mode.empty()) {
187 const auto &preset_modes = this->get_traits().supported_preset_modes();
188 // Store index of current preset mode
189 auto preset_iterator = preset_modes.find(this->preset_mode);
190 if (preset_iterator != preset_modes.end())
191 state.preset_mode = std::distance(preset_modes.begin(), preset_iterator);
192 }
193
194 this->rtc_.save(&state);
195}
196
197void Fan::dump_traits_(const char *tag, const char *prefix) {
198 auto traits = this->get_traits();
199
200 if (traits.supports_speed()) {
201 ESP_LOGCONFIG(tag,
202 "%s Speed: YES\n"
203 "%s Speed count: %d",
204 prefix, prefix, traits.supported_speed_count());
205 }
206 if (traits.supports_oscillation()) {
207 ESP_LOGCONFIG(tag, "%s Oscillation: YES", prefix);
208 }
209 if (traits.supports_direction()) {
210 ESP_LOGCONFIG(tag, "%s Direction: YES", prefix);
211 }
212 if (traits.supports_preset_modes()) {
213 ESP_LOGCONFIG(tag, "%s Supported presets:", prefix);
214 for (const std::string &s : traits.supported_preset_modes())
215 ESP_LOGCONFIG(tag, "%s - %s", prefix, s.c_str());
216 }
217}
218
219} // namespace fan
220} // 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:69
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:126
FanCall turn_on()
Definition fan.cpp:120
FanCall turn_off()
Definition fan.cpp:121
FanCall make_call()
Definition fan.cpp:123
virtual FanTraits get_traits()=0
FanCall toggle()
Definition fan.cpp:122
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:125
FanDirection direction
The current direction of the fan.
Definition fan.h:116
void save_state_()
Definition fan.cpp:179
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:197
optional< FanRestoreState > restore_state_()
Definition fan.cpp:149
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:148
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:102
void apply(Fan &fan)
Apply these settings to the fan.
Definition fan.cpp:104
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:88