ESPHome 2026.11.0-dev
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"
4
5#include "mqtt_const.h"
6
7#ifdef USE_MQTT
8#ifdef USE_FAN
9
10namespace esphome::mqtt {
11
12ESPHOME_LOG_TAG(TAG, "mqtt.fan");
13
14using namespace esphome::fan;
15
16static ProgmemStr fan_direction_to_mqtt_str(FanDirection direction) {
17 return direction == FanDirection::FORWARD ? ESPHOME_F("forward") : ESPHOME_F("reverse");
18}
19
20static ProgmemStr fan_oscillation_to_mqtt_str(bool oscillating) {
21 return oscillating ? ESPHOME_F("oscillate_on") : ESPHOME_F("oscillate_off");
22}
23
25
26Fan *MQTTFanComponent::get_state() const { return this->state_; }
28const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
29
31 this->subscribe(this->command_topic_source_(), [this](const std::string &topic, const std::string &payload) {
32 auto val = parse_on_off(payload.c_str());
33 switch (val) {
34 case PARSE_ON:
35 ESP_LOGD(TAG, "'%s' Turning Fan ON.", LOG_STR_ARG(this->log_name_()));
36 this->state_->turn_on().perform();
37 break;
38 case PARSE_OFF:
39 ESP_LOGD(TAG, "'%s' Turning Fan OFF.", LOG_STR_ARG(this->log_name_()));
40 this->state_->turn_off().perform();
41 break;
42 case PARSE_TOGGLE:
43 ESP_LOGD(TAG, "'%s' Toggling Fan.", LOG_STR_ARG(this->log_name_()));
44 this->state_->toggle().perform();
45 break;
46 case PARSE_NONE:
47 default:
48 ESP_LOGW(TAG, "Unknown state payload %s", payload.c_str());
49 this->status_momentary_warning(5000);
50 break;
51 }
52 });
53
54 if (this->state_->get_traits().supports_direction()) {
55 this->subscribe(
56 this->get_direction_command_topic_source(), [this](const std::string &topic, const std::string &payload) {
57 auto val = parse_on_off(payload.c_str(), "forward", "reverse");
58 switch (val) {
59 case PARSE_ON:
60 ESP_LOGD(TAG, "'%s': Setting direction FORWARD", LOG_STR_ARG(this->log_name_()));
62 break;
63 case PARSE_OFF:
64 ESP_LOGD(TAG, "'%s': Setting direction REVERSE", LOG_STR_ARG(this->log_name_()));
66 break;
67 case PARSE_TOGGLE:
68 this->state_->make_call()
71 .perform();
72 break;
73 case PARSE_NONE:
74 ESP_LOGW(TAG, "Unknown direction Payload %s", payload.c_str());
75 this->status_momentary_warning(5000);
76 break;
77 }
78 });
79 }
80
81 if (this->state_->get_traits().supports_oscillation()) {
82 this->subscribe(this->get_oscillation_command_topic_source(),
83 [this](const std::string &topic, const std::string &payload) {
84 auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off");
85 switch (val) {
86 case PARSE_ON:
87 ESP_LOGD(TAG, "'%s': Setting oscillating ON", LOG_STR_ARG(this->log_name_()));
88 this->state_->make_call().set_oscillating(true).perform();
89 break;
90 case PARSE_OFF:
91 ESP_LOGD(TAG, "'%s': Setting oscillating OFF", LOG_STR_ARG(this->log_name_()));
92 this->state_->make_call().set_oscillating(false).perform();
93 break;
94 case PARSE_TOGGLE:
96 break;
97 case PARSE_NONE:
98 ESP_LOGW(TAG, "Unknown Oscillation Payload %s", payload.c_str());
99 this->status_momentary_warning(5000);
100 break;
101 }
102 });
103 }
104
105 if (this->state_->get_traits().supports_speed()) {
106 this->subscribe(this->get_speed_level_command_topic_source(),
107 [this](const std::string &topic, const std::string &payload) {
108 optional<int> speed_level_opt = parse_number<int>(payload);
109 if (speed_level_opt.has_value()) {
110 const int speed_level = speed_level_opt.value();
111 if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
112 ESP_LOGD(TAG, "New speed level %d", speed_level);
113 this->state_->make_call().set_speed(speed_level).perform();
114 } else {
115 ESP_LOGW(TAG, "Invalid speed level %d", speed_level);
116 this->status_momentary_warning(5000);
117 }
118 } else {
119 ESP_LOGW(TAG, "Invalid speed level %s (int expected)", payload.c_str());
120 this->status_momentary_warning(5000);
121 }
122 });
123 }
124
125 this->state_->add_on_state_callback([this]() { this->defer(SEND_DEFER_ID, [this]() { this->publish_state(); }); });
126}
127
129 LOG_MQTT_COMPONENT(true, true);
130 if (this->state_->get_traits().supports_direction()) {
131 ESP_LOGCONFIG(TAG,
132 " Direction State Topic: '%s'\n"
133 " Direction Command Topic: '%s'",
134 this->get_direction_state_topic().c_str(), this->get_direction_command_topic().c_str());
135 }
136 if (this->state_->get_traits().supports_oscillation()) {
137 ESP_LOGCONFIG(TAG,
138 " Oscillation State Topic: '%s'\n"
139 " Oscillation Command Topic: '%s'",
140 this->get_oscillation_state_topic().c_str(), this->get_oscillation_command_topic().c_str());
141 }
142 if (this->state_->get_traits().supports_speed()) {
143 ESP_LOGCONFIG(TAG,
144 " Speed Level State Topic: '%s'\n"
145 " Speed Level Command Topic: '%s'",
146 this->get_speed_level_state_topic().c_str(), this->get_speed_level_command_topic().c_str());
147 }
148}
149
151
153 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
154 if (this->state_->get_traits().supports_direction()) {
155 root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
156 root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
157 }
158 if (this->state_->get_traits().supports_oscillation()) {
159 root[MQTT_OSCILLATION_COMMAND_TOPIC] = this->get_oscillation_command_topic();
160 root[MQTT_OSCILLATION_STATE_TOPIC] = this->get_oscillation_state_topic();
161 }
162 if (this->state_->get_traits().supports_speed()) {
163 root[MQTT_PERCENTAGE_COMMAND_TOPIC] = this->get_speed_level_command_topic();
164 root[MQTT_PERCENTAGE_STATE_TOPIC] = this->get_speed_level_state_topic();
165 root[MQTT_SPEED_RANGE_MAX] = this->state_->get_traits().supported_speed_count();
166 }
167}
169 char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
170 const char *state_s = this->state_->state ? "ON" : "OFF";
171 ESP_LOGD(TAG, "'%s' Sending state %s.", LOG_STR_ARG(this->state_->get_log_name()), state_s);
172 bool failed = !this->publish(this->get_state_topic_to_(topic_buf), state_s);
173 if (this->state_->get_traits().supports_direction()) {
174 bool success = this->publish(this->get_direction_state_topic_to(topic_buf),
175 fan_direction_to_mqtt_str(this->state_->direction));
176 failed = failed || !success;
177 }
178 if (this->state_->get_traits().supports_oscillation()) {
179 bool success = this->publish(this->get_oscillation_state_topic_to(topic_buf),
180 fan_oscillation_to_mqtt_str(this->state_->oscillating));
181 failed = failed || !success;
182 }
183 auto traits = this->state_->get_traits();
184 if (traits.supports_speed()) {
185 char buf[12];
186 size_t len = buf_append_printf(buf, sizeof(buf), 0, "%d", this->state_->speed);
187 bool success = this->publish(this->get_speed_level_state_topic_to(topic_buf), buf, len);
188 failed = failed || !success;
189 }
190 return !failed;
191}
192
193} // namespace esphome::mqtt
194
195#endif
196#endif // USE_MQTT
void status_momentary_warning(uint32_t length=5000)
Set warning status flag and clear it after length ms.
void defer(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call with a const char* name.
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
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
void add_on_state_callback(F &&callback)
Register a callback that will be called each time the state changes.
Definition fan.h:125
FanCall turn_on()
Definition fan.h:119
FanCall turn_off()
Definition fan.h:120
FanCall make_call()
Definition fan.h:122
virtual FanTraits get_traits()=0
FanCall toggle()
Definition fan.h:121
FanDirection direction
The current direction of the fan.
Definition fan.h:117
bool oscillating
The current oscillation state of the fan.
Definition fan.h:113
bool state
The current on/off state of the fan.
Definition fan.h:111
int speed
The current fan speed level.
Definition fan.h:115
int supported_speed_count() const
Return how many speed levels the fan has.
Definition fan_traits.h:31
bool supports_direction() const
Return if this fan supports changing direction.
Definition fan_traits.h:35
bool supports_speed() const
Return if this fan supports speed modes.
Definition fan_traits.h:27
bool supports_oscillation() const
Return if this fan supports oscillation.
Definition fan_traits.h:23
static constexpr uint32_t SEND_DEFER_ID
const LogString * log_name_() const
Entity name for a "%s" log argument.
void subscribe(T &&topic, F &&callback, uint8_t qos=0)
Subscribe to a MQTT topic.
MQTTTopicSource command_topic_source_() const
The command topic as a subscription source. A lambda topic is read once and copied,...
bool publish(const std::string &topic, const std::string &payload)
Send a MQTT message.
StringRef get_state_topic_to_(std::span< char, MQTT_DEFAULT_TOPIC_MAX_LEN > buf) const
Get the MQTT state topic into a buffer (no heap allocation for non-lambda custom topics).
fan::Fan * get_state() const
Definition mqtt_fan.cpp:26
state state state state void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override
Definition mqtt_fan.cpp:152
bool send_initial_state() override
Send the full current state to MQTT.
Definition mqtt_fan.cpp:150
void setup() override
Setup the fan subscriptions and discovery.
MQTTFanComponent(fan::Fan *state)
Definition mqtt_fan.cpp:24
FanDirection direction
Definition fan.h:5
bool oscillating
Definition fan.h:4
bool state
Definition fan.h:2
mopeka_std_values val[3]
ESPHOME_LOG_TAG(TAG, "alarm_control_panel")
FanDirection
Simple enum to represent the direction of a fan.
Definition fan.h:21
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:554
const void size_t len
Definition hal.h:68
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1352
@ PARSE_ON
Definition helpers.h:1741
@ PARSE_TOGGLE
Definition helpers.h:1743
@ PARSE_OFF
Definition helpers.h:1742
@ PARSE_NONE
Definition helpers.h:1740
const __FlashStringHelper * ProgmemStr
Definition progmem.h:31
Simple Helper struct used for Home Assistant MQTT send_discovery().