ESPHome 2026.1.4
Loading...
Searching...
No Matches
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
8
9namespace esphome::mqtt {
10
11static const char *const TAG = "mqtt.fan";
12
13using namespace esphome::fan;
14
16
17Fan *MQTTFanComponent::get_state() const { return this->state_; }
19const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
20
22 this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
23 auto val = parse_on_off(payload.c_str());
24 switch (val) {
25 case PARSE_ON:
26 ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name_().c_str());
27 this->state_->turn_on().perform();
28 break;
29 case PARSE_OFF:
30 ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name_().c_str());
31 this->state_->turn_off().perform();
32 break;
33 case PARSE_TOGGLE:
34 ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name_().c_str());
35 this->state_->toggle().perform();
36 break;
37 case PARSE_NONE:
38 default:
39 ESP_LOGW(TAG, "Unknown state payload %s", payload.c_str());
40 this->status_momentary_warning("state", 5000);
41 break;
42 }
43 });
44
45 if (this->state_->get_traits().supports_direction()) {
46 this->subscribe(this->get_direction_command_topic(), [this](const std::string &topic, const std::string &payload) {
47 auto val = parse_on_off(payload.c_str(), "forward", "reverse");
48 switch (val) {
49 case PARSE_ON:
50 ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name_().c_str());
52 break;
53 case PARSE_OFF:
54 ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name_().c_str());
56 break;
57 case PARSE_TOGGLE:
58 this->state_->make_call()
61 .perform();
62 break;
63 case PARSE_NONE:
64 ESP_LOGW(TAG, "Unknown direction Payload %s", payload.c_str());
65 this->status_momentary_warning("direction", 5000);
66 break;
67 }
68 });
69 }
70
71 if (this->state_->get_traits().supports_oscillation()) {
72 this->subscribe(this->get_oscillation_command_topic(),
73 [this](const std::string &topic, const std::string &payload) {
74 auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off");
75 switch (val) {
76 case PARSE_ON:
77 ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name_().c_str());
78 this->state_->make_call().set_oscillating(true).perform();
79 break;
80 case PARSE_OFF:
81 ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name_().c_str());
82 this->state_->make_call().set_oscillating(false).perform();
83 break;
84 case PARSE_TOGGLE:
86 break;
87 case PARSE_NONE:
88 ESP_LOGW(TAG, "Unknown Oscillation Payload %s", payload.c_str());
89 this->status_momentary_warning("oscillation", 5000);
90 break;
91 }
92 });
93 }
94
95 if (this->state_->get_traits().supports_speed()) {
96 this->subscribe(this->get_speed_level_command_topic(),
97 [this](const std::string &topic, const std::string &payload) {
98 optional<int> speed_level_opt = parse_number<int>(payload);
99 if (speed_level_opt.has_value()) {
100 const int speed_level = speed_level_opt.value();
101 if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
102 ESP_LOGD(TAG, "New speed level %d", speed_level);
103 this->state_->make_call().set_speed(speed_level).perform();
104 } else {
105 ESP_LOGW(TAG, "Invalid speed level %d", speed_level);
106 this->status_momentary_warning("speed", 5000);
107 }
108 } else {
109 ESP_LOGW(TAG, "Invalid speed level %s (int expected)", payload.c_str());
110 this->status_momentary_warning("speed", 5000);
111 }
112 });
113 }
114
115 auto f = std::bind(&MQTTFanComponent::publish_state, this);
116 this->state_->add_on_state_callback([this, f]() { this->defer("send", f); });
117}
118
120 ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str());
121 LOG_MQTT_COMPONENT(true, true);
122 if (this->state_->get_traits().supports_direction()) {
123 ESP_LOGCONFIG(TAG,
124 " Direction State Topic: '%s'\n"
125 " Direction Command Topic: '%s'",
126 this->get_direction_state_topic().c_str(), this->get_direction_command_topic().c_str());
127 }
128 if (this->state_->get_traits().supports_oscillation()) {
129 ESP_LOGCONFIG(TAG,
130 " Oscillation State Topic: '%s'\n"
131 " Oscillation Command Topic: '%s'",
132 this->get_oscillation_state_topic().c_str(), this->get_oscillation_command_topic().c_str());
133 }
134 if (this->state_->get_traits().supports_speed()) {
135 ESP_LOGCONFIG(TAG,
136 " Speed Level State Topic: '%s'\n"
137 " Speed Level Command Topic: '%s'",
138 this->get_speed_level_state_topic().c_str(), this->get_speed_level_command_topic().c_str());
139 }
140}
141
143
145 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
146 if (this->state_->get_traits().supports_direction()) {
147 root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
148 root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
149 }
150 if (this->state_->get_traits().supports_oscillation()) {
151 root[MQTT_OSCILLATION_COMMAND_TOPIC] = this->get_oscillation_command_topic();
152 root[MQTT_OSCILLATION_STATE_TOPIC] = this->get_oscillation_state_topic();
153 }
154 if (this->state_->get_traits().supports_speed()) {
155 root[MQTT_PERCENTAGE_COMMAND_TOPIC] = this->get_speed_level_command_topic();
156 root[MQTT_PERCENTAGE_STATE_TOPIC] = this->get_speed_level_state_topic();
157 root[MQTT_SPEED_RANGE_MAX] = this->state_->get_traits().supported_speed_count();
158 }
159}
161 const char *state_s = this->state_->state ? "ON" : "OFF";
162 ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
163 this->publish(this->get_state_topic_(), state_s);
164 bool failed = false;
165 if (this->state_->get_traits().supports_direction()) {
166 bool success = this->publish(this->get_direction_state_topic(),
167 this->state_->direction == fan::FanDirection::FORWARD ? "forward" : "reverse");
168 failed = failed || !success;
169 }
170 if (this->state_->get_traits().supports_oscillation()) {
171 bool success = this->publish(this->get_oscillation_state_topic(),
172 this->state_->oscillating ? "oscillate_on" : "oscillate_off");
173 failed = failed || !success;
174 }
175 auto traits = this->state_->get_traits();
176 if (traits.supports_speed()) {
177 char buf[12];
178 int len = snprintf(buf, sizeof(buf), "%d", this->state_->speed);
179 bool success = this->publish(this->get_speed_level_state_topic(), buf, len);
180 failed = failed || !success;
181 }
182 return !failed;
183}
184
185} // namespace esphome::mqtt
186
187#endif
188#endif // USE_MQTT
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition component.h:492
void status_momentary_warning(const char *name, uint32_t length=5000)
Set warning status flag and automatically clear it after a timeout.
const StringRef & get_name() const
constexpr const char * c_str() const
Definition string_ref.h:73
FanCall & set_oscillating(bool oscillating)
Definition fan.h:51
FanCall & set_direction(FanDirection direction)
Definition fan.h:65
FanCall & set_speed(int speed)
Definition fan.h:60
FanCall turn_on()
Definition fan.cpp:144
FanCall turn_off()
Definition fan.cpp:145
FanCall make_call()
Definition fan.cpp:147
virtual FanTraits get_traits()=0
FanCall toggle()
Definition fan.cpp: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:199
FanDirection direction
The current direction of the fan.
Definition fan.h:115
bool oscillating
The current oscillation state of the fan.
Definition fan.h:111
bool state
The current on/off state of the fan.
Definition fan.h:109
int speed
The current fan speed level.
Definition fan.h:113
int supported_speed_count() const
Return how many speed levels the fan has.
Definition fan_traits.h:26
bool supports_direction() const
Return if this fan supports changing direction.
Definition fan_traits.h:30
bool supports_speed() const
Return if this fan supports speed modes.
Definition fan_traits.h:22
bool supports_oscillation() const
Return if this fan supports oscillation.
Definition fan_traits.h:18
bool publish(const std::string &topic, const std::string &payload)
Send a MQTT message.
std::string get_state_topic_() const
Get the MQTT topic that new states will be shared to.
std::string friendly_name_() const
Get the friendly name of this MQTT component.
std::string get_command_topic_() const
Get the MQTT topic for listening to commands.
void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos=0)
Subscribe to a MQTT topic.
fan::Fan * get_state() const
Definition mqtt_fan.cpp:17
state state state state void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override
Definition mqtt_fan.cpp:144
bool send_initial_state() override
Send the full current state to MQTT.
Definition mqtt_fan.cpp:142
void setup() override
Setup the fan subscriptions and discovery.
MQTTFanComponent(fan::Fan *state)
Definition mqtt_fan.cpp:15
bool has_value() const
Definition optional.h:92
value_type const & value() const
Definition optional.h:94
bool state
Definition fan.h:0
mopeka_std_values val[4]
MQTT_COMPONENT_TYPE(MQTTAlarmControlPanelComponent, "alarm_control_panel") const EntityBase *MQTTAlarmControlPanelComponent
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:419
std::string size_t len
Definition helpers.h:595
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:640
@ PARSE_ON
Definition helpers.h:1088
@ PARSE_TOGGLE
Definition helpers.h:1090
@ PARSE_OFF
Definition helpers.h:1089
@ PARSE_NONE
Definition helpers.h:1087
Simple Helper struct used for Home Assistant MQTT send_discovery().