ESPHome  2024.12.2
fan.cpp
Go to the documentation of this file.
1 #include "fan.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace fan {
6 
7 static 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 
86  if (fan.get_traits().supports_preset_modes()) {
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 
111 FanCall Fan::turn_on() { return this->make_call().set_state(true); }
112 FanCall Fan::turn_off() { return this->make_call().set_state(false); }
113 FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
114 FanCall Fan::make_call() { return FanCall(*this); }
115 
116 void 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.
139 constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
141  FanRestoreState recovered{};
142  this->rtc_ = global_preferences->make_preference<FanRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
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 
188 void 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 state
The current on/off state of the fan.
Definition: fan.h:110
bool oscillating
The current oscillation state of the fan.
Definition: fan.h:112
optional< bool > oscillating_
Definition: fan.h:88
void apply(Fan &fan)
Apply these settings to the fan.
Definition: fan.cpp:95
FanDirection direction
The current direction of the fan.
Definition: fan.h:116
void publish_state()
Definition: fan.cpp:117
void save_state_()
Definition: fan.cpp:170
virtual FanTraits get_traits()=0
int speed
Definition: fan.h:35
optional< FanRestoreState > restore_state_()
Definition: fan.cpp:140
bool has_value() const
Definition: optional.h:87
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:93
FanCall(Fan &parent)
Definition: fan.h:39
constexpr uint32_t RESTORE_STATE_VERSION
Definition: fan.cpp:139
bool supports_preset_modes() const
Return if preset modes are supported.
Definition: fan_traits.h:36
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
optional< int > speed_
Definition: fan.h:89
FanDirection direction
Definition: fan.h:37
FanCall turn_off()
Definition: fan.cpp:112
FanCall & set_speed(int speed)
Definition: fan.h:59
virtual void control(const FanCall &call)=0
std::string preset_mode
Definition: fan.h:118
FanCall toggle()
Definition: fan.cpp:113
optional< bool > binary_state_
Definition: fan.h:87
FanDirection
Simple enum to represent the direction of a fan.
Definition: fan.h:20
ESPPreferences * global_preferences
std::string preset_mode_
Definition: fan.h:91
int speed
The current fan speed level.
Definition: fan.h:114
FanCall & set_oscillating(bool oscillating)
Definition: fan.h:50
void validate_()
Definition: fan.cpp:41
FanCall to_call(Fan &fan)
Convert this struct to a fan call that can be performed.
Definition: fan.cpp:79
constexpr const char * c_str() const
Definition: string_ref.h:68
FanCall & set_state(bool binary_state)
Definition: fan.h:41
std::set< std::string > supported_preset_modes() const
Return the preset modes supported by the fan.
Definition: fan_traits.h:32
optional< FanDirection > direction_
Definition: fan.h:90
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
FanCall make_call()
Definition: fan.cpp:114
bool oscillating
Definition: fan.h:36
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t preset_mode
Definition: fan.h:38
const LogString * fan_direction_to_string(FanDirection direction)
Definition: fan.cpp:9
const StringRef & get_name() const
Definition: entity_base.cpp:10
FanCall & set_direction(FanDirection direction)
Definition: fan.h:66
bool state
Definition: fan.h:34
void dump_traits_(const char *tag, const char *prefix)
Definition: fan.cpp:188
FanCall turn_on()
Definition: fan.cpp:111