ESPHome  2022.5.1
mqtt_fan.cpp
Go to the documentation of this file.
1 #include "mqtt_fan.h"
2 #include "esphome/core/log.h"
3 
4 #include "mqtt_const.h"
5 
6 #ifdef USE_MQTT
7 #ifdef USE_FAN
9 
10 namespace esphome {
11 namespace mqtt {
12 
13 static const char *const TAG = "mqtt.fan";
14 
15 using namespace esphome::fan;
16 
18 
19 Fan *MQTTFanComponent::get_state() const { return this->state_; }
20 std::string MQTTFanComponent::component_type() const { return "fan"; }
21 const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
22 
24  this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
25  auto val = parse_on_off(payload.c_str());
26  switch (val) {
27  case PARSE_ON:
28  ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name().c_str());
29  this->state_->turn_on().perform();
30  break;
31  case PARSE_OFF:
32  ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name().c_str());
33  this->state_->turn_off().perform();
34  break;
35  case PARSE_TOGGLE:
36  ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name().c_str());
37  this->state_->toggle().perform();
38  break;
39  case PARSE_NONE:
40  default:
41  ESP_LOGW(TAG, "Unknown state payload %s", payload.c_str());
42  this->status_momentary_warning("state", 5000);
43  break;
44  }
45  });
46 
47  if (this->state_->get_traits().supports_oscillation()) {
48  this->subscribe(this->get_oscillation_command_topic(),
49  [this](const std::string &topic, const std::string &payload) {
50  auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off");
51  switch (val) {
52  case PARSE_ON:
53  ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name().c_str());
54  this->state_->make_call().set_oscillating(true).perform();
55  break;
56  case PARSE_OFF:
57  ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name().c_str());
58  this->state_->make_call().set_oscillating(false).perform();
59  break;
60  case PARSE_TOGGLE:
62  break;
63  case PARSE_NONE:
64  ESP_LOGW(TAG, "Unknown Oscillation Payload %s", payload.c_str());
65  this->status_momentary_warning("oscillation", 5000);
66  break;
67  }
68  });
69  }
70 
71  if (this->state_->get_traits().supports_speed()) {
72  this->subscribe(this->get_speed_level_command_topic(),
73  [this](const std::string &topic, const std::string &payload) {
74  optional<int> speed_level_opt = parse_number<int>(payload);
75  if (speed_level_opt.has_value()) {
76  const int speed_level = speed_level_opt.value();
77  if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
78  ESP_LOGD(TAG, "New speed level %d", speed_level);
79  this->state_->make_call().set_speed(speed_level).perform();
80  } else {
81  ESP_LOGW(TAG, "Invalid speed level %d", speed_level);
82  this->status_momentary_warning("speed", 5000);
83  }
84  } else {
85  ESP_LOGW(TAG, "Invalid speed level %s (int expected)", payload.c_str());
86  this->status_momentary_warning("speed", 5000);
87  }
88  });
89  }
90 
91  if (this->state_->get_traits().supports_speed()) {
92  this->subscribe(this->get_speed_command_topic(), [this](const std::string &topic, const std::string &payload) {
93 #pragma GCC diagnostic push
94 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
95  this->state_->make_call()
96  .set_speed(payload.c_str()) // NOLINT(clang-diagnostic-deprecated-declarations)
97  .perform();
98 #pragma GCC diagnostic pop
99  });
100  }
101 
102  auto f = std::bind(&MQTTFanComponent::publish_state, this);
103  this->state_->add_on_state_callback([this, f]() { this->defer("send", f); });
104 }
105 
107  ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str());
108  LOG_MQTT_COMPONENT(true, true);
109  if (this->state_->get_traits().supports_oscillation()) {
110  ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str());
111  ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str());
112  }
113  if (this->state_->get_traits().supports_speed()) {
114  ESP_LOGCONFIG(TAG, " Speed Level State Topic: '%s'", this->get_speed_level_state_topic().c_str());
115  ESP_LOGCONFIG(TAG, " Speed Level Command Topic: '%s'", this->get_speed_level_command_topic().c_str());
116  ESP_LOGCONFIG(TAG, " Speed State Topic: '%s'", this->get_speed_state_topic().c_str());
117  ESP_LOGCONFIG(TAG, " Speed Command Topic: '%s'", this->get_speed_command_topic().c_str());
118  }
119 }
120 
122 
124  if (this->state_->get_traits().supports_oscillation()) {
125  root[MQTT_OSCILLATION_COMMAND_TOPIC] = this->get_oscillation_command_topic();
126  root[MQTT_OSCILLATION_STATE_TOPIC] = this->get_oscillation_state_topic();
127  }
128  if (this->state_->get_traits().supports_speed()) {
129  root["speed_level_command_topic"] = this->get_speed_level_command_topic();
130  root["speed_level_state_topic"] = this->get_speed_level_state_topic();
131  root[MQTT_SPEED_COMMAND_TOPIC] = this->get_speed_command_topic();
132  root[MQTT_SPEED_STATE_TOPIC] = this->get_speed_state_topic();
133  }
134 }
136  const char *state_s = this->state_->state ? "ON" : "OFF";
137  ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
138  this->publish(this->get_state_topic_(), state_s);
139  bool failed = false;
140  if (this->state_->get_traits().supports_oscillation()) {
141  bool success = this->publish(this->get_oscillation_state_topic(),
142  this->state_->oscillating ? "oscillate_on" : "oscillate_off");
143  failed = failed || !success;
144  }
145  auto traits = this->state_->get_traits();
146  if (traits.supports_speed()) {
147  std::string payload = to_string(this->state_->speed);
148  bool success = this->publish(this->get_speed_level_state_topic(), payload);
149  failed = failed || !success;
150  }
151  if (traits.supports_speed()) {
152  const char *payload;
153 #pragma GCC diagnostic push
154 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
155  // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations)
156  switch (fan::speed_level_to_enum(this->state_->speed, traits.supported_speed_count())) {
157  case FAN_SPEED_LOW: { // NOLINT(clang-diagnostic-deprecated-declarations)
158  payload = "low";
159  break;
160  }
161  case FAN_SPEED_MEDIUM: { // NOLINT(clang-diagnostic-deprecated-declarations)
162  payload = "medium";
163  break;
164  }
165  default:
166  case FAN_SPEED_HIGH: { // NOLINT(clang-diagnostic-deprecated-declarations)
167  payload = "high";
168  break;
169  }
170  }
171 #pragma GCC diagnostic pop
172  bool success = this->publish(this->get_speed_state_topic(), payload);
173  failed = failed || !success;
174  }
175 
176  return !failed;
177 }
178 
179 } // namespace mqtt
180 } // namespace esphome
181 
182 #endif
183 #endif // USE_MQTT
value_type const & value() const
Definition: optional.h:89
bool state
The current on/off state of the fan.
Definition: fan.h:114
constexpr const char *const MQTT_SPEED_COMMAND_TOPIC
Definition: mqtt_const.h:191
bool oscillating
The current oscillation state of the fan.
Definition: fan.h:116
MQTTFanComponent(fan::Fan *state)
Definition: mqtt_fan.cpp:17
void setup() override
Setup the fan subscriptions and discovery.
Definition: mqtt_fan.cpp:23
const EntityBase * get_entity() const override
Definition: mqtt_fan.cpp:21
void status_momentary_warning(const std::string &name, uint32_t length=5000)
Definition: component.cpp:150
virtual FanTraits get_traits()=0
constexpr const char *const MQTT_SPEED_STATE_TOPIC
Definition: mqtt_const.h:192
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition: component.cpp:123
fan::Fan * get_state() const
Definition: mqtt_fan.cpp:19
FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels)
Definition: fan_helpers.cpp:10
const std::string & get_name() const
Definition: entity_base.cpp:11
constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC
Definition: mqtt_const.h:117
bool has_value() const
Definition: optional.h:87
bool supports_oscillation() const
Return if this fan supports oscillation.
Definition: fan_traits.h:13
bool publish(const std::string &topic, const std::string &payload)
Send a MQTT message.
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
Definition: helpers.cpp:235
int supported_speed_count() const
Return how many speed levels the fan has.
Definition: fan_traits.h:21
void add_on_state_callback(std::function< void()> &&callback)
Register a callback that will be called each time the state changes.
Definition: fan.cpp:104
std::string component_type() const override
&#39;fan&#39; component type for discovery.
Definition: mqtt_fan.cpp:20
state state state void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override
Definition: mqtt_fan.cpp:123
FanCall turn_off()
Definition: fan.cpp:100
void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos=0)
Subscribe to a MQTT topic.
FanCall toggle()
Definition: fan.cpp:101
int speed
The current fan speed level.
Definition: fan.h:118
Simple Helper struct used for Home Assistant MQTT send_discovery().
constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC
Definition: mqtt_const.h:115
FanCall & set_oscillating(bool oscillating)
Definition: fan.h:57
bool send_initial_state() override
Send the full current state to MQTT.
Definition: mqtt_fan.cpp:121
std::string to_string(int value)
Definition: helpers.cpp:36
FanCall make_call()
Definition: fan.cpp:102
virtual std::string friendly_name() const
Get the friendly name of this MQTT component.
Definition: a4988.cpp:4
uint32_t val
Definition: datatypes.h:79
FanCall & set_speed(int speed)
Definition: fan.h:66
std::string get_state_topic_() const
Get the MQTT topic that new states will be shared to.
std::string get_command_topic_() const
Get the MQTT topic for listening to commands.
bool state
Definition: fan.h:41
bool supports_speed() const
Return if this fan supports speed modes.
Definition: fan_traits.h:17
FanCall turn_on()
Definition: fan.cpp:99