ESPHome 2026.2.1
Loading...
Searching...
No Matches
mqtt_component.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef USE_MQTT
6
7#include <memory>
8
14#include "mqtt_client.h"
15
16namespace esphome::mqtt {
17
20 bool state_topic{true};
21 bool command_topic{true};
22};
23
24// Max lengths for stack-based topic building.
25// These limits are enforced at Python config validation time in mqtt/__init__.py
26// using cv.Length() validators for topic_prefix and discovery_prefix.
27// This ensures the stack buffers are always large enough.
28static constexpr size_t MQTT_COMPONENT_TYPE_MAX_LEN = 20;
29static constexpr size_t MQTT_SUFFIX_MAX_LEN = 32;
30static constexpr size_t MQTT_TOPIC_PREFIX_MAX_LEN = 64; // Validated in Python: cv.Length(max=64)
31// Stack buffer size - safe because all inputs are length-validated at config time
32// Format: prefix + "/" + type + "/" + object_id + "/" + suffix + null
33static constexpr size_t MQTT_DEFAULT_TOPIC_MAX_LEN =
34 MQTT_TOPIC_PREFIX_MAX_LEN + 1 + MQTT_COMPONENT_TYPE_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 1 + MQTT_SUFFIX_MAX_LEN + 1;
35static constexpr size_t MQTT_DISCOVERY_PREFIX_MAX_LEN = 64; // Validated in Python: cv.Length(max=64)
36// Format: prefix + "/" + type + "/" + name + "/" + object_id + "/config" + null
37static constexpr size_t MQTT_DISCOVERY_TOPIC_MAX_LEN = MQTT_DISCOVERY_PREFIX_MAX_LEN + 1 + MQTT_COMPONENT_TYPE_MAX_LEN +
38 1 + ESPHOME_DEVICE_NAME_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 7 + 1;
39
40class MQTTComponent; // Forward declaration
41void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic);
42
43#define LOG_MQTT_COMPONENT(state_topic, command_topic) log_mqtt_component(TAG, this, state_topic, command_topic)
44
45// Macro to define component_type() with compile-time length verification
46// Usage: MQTT_COMPONENT_TYPE(MQTTSensorComponent, "sensor")
47#define MQTT_COMPONENT_TYPE(class_name, type_str) \
48 const char *class_name::component_type() const { return type_str; } \
49 static_assert(sizeof(type_str) - 1 <= MQTT_COMPONENT_TYPE_MAX_LEN, \
50 #class_name "::component_type() exceeds MQTT_COMPONENT_TYPE_MAX_LEN");
51
52// Macro to define custom topic getter/setter with compile-time suffix length verification
53#define MQTT_COMPONENT_CUSTOM_TOPIC_(name, type) \
54 static_assert(sizeof(#name "/" #type) - 1 <= MQTT_SUFFIX_MAX_LEN, \
55 "topic suffix " #name "/" #type " exceeds MQTT_SUFFIX_MAX_LEN"); \
56\
57 protected: \
58 std::string custom_##name##_##type##_topic_{}; \
59\
60 public: \
61 void set_custom_##name##_##type##_topic(const std::string &topic) { this->custom_##name##_##type##_topic_ = topic; } \
62 StringRef get_##name##_##type##_topic_to(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const { \
63 if (!this->custom_##name##_##type##_topic_.empty()) \
64 return StringRef(this->custom_##name##_##type##_topic_.data(), this->custom_##name##_##type##_topic_.size()); \
65 return this->get_default_topic_for_to_(buf, #name "/" #type, sizeof(#name "/" #type) - 1); \
66 } \
67 std::string get_##name##_##type##_topic() const { \
68 if (this->custom_##name##_##type##_topic_.empty()) \
69 return this->get_default_topic_for_(#name "/" #type); \
70 return this->custom_##name##_##type##_topic_; \
71 }
72
73#define MQTT_COMPONENT_CUSTOM_TOPIC(name, type) MQTT_COMPONENT_CUSTOM_TOPIC_(name, type)
74
91class MQTTComponent : public Component {
92 friend void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic);
93
94 public:
96 explicit MQTTComponent();
97
99 void call_setup() override;
100
101 void call_dump_config() override;
102
104 virtual void send_discovery(JsonObject root, SendDiscoveryConfig &config) = 0;
105
106 virtual bool send_initial_state() = 0;
107
109 bool is_internal() const { return this->is_internal_; }
110
112 void set_qos(uint8_t qos);
113 uint8_t get_qos() const;
114
116 void set_retain(bool retain);
117 bool get_retain() const;
118
120 void disable_discovery();
121 bool is_discovery_enabled() const;
122
124 void set_subscribe_qos(uint8_t qos);
125
127 virtual const char *component_type() const = 0;
128
130 template<typename T> void set_custom_state_topic(T &&custom_state_topic) {
131 this->custom_state_topic_ = std::forward<T>(custom_state_topic);
132 }
133 template<typename T> void set_custom_command_topic(T &&custom_command_topic) {
134 this->custom_command_topic_ = std::forward<T>(custom_command_topic);
135 }
137 void set_command_retain(bool command_retain);
138
140 float get_setup_priority() const override;
141
146 void set_availability(std::string topic, std::string payload_available, std::string payload_not_available);
148
151
153 void process_resend();
154
160 bool publish(const std::string &topic, const std::string &payload);
161
168 bool publish(const std::string &topic, const char *payload, size_t payload_length);
169
175 bool publish(const std::string &topic, const char *payload) {
176 return this->publish(topic.c_str(), payload, strlen(payload));
177 }
178
185 bool publish(const char *topic, const char *payload, size_t payload_length);
186
193 bool publish(StringRef topic, const char *payload, size_t payload_length) {
194 return this->publish(topic.c_str(), payload, payload_length);
195 }
196
202 bool publish(const char *topic, const char *payload);
203
209 bool publish(StringRef topic, const char *payload) { return this->publish(topic.c_str(), payload); }
210
211#ifdef USE_ESP8266
217 bool publish(const std::string &topic, ProgmemStr payload);
218
224 bool publish(const char *topic, ProgmemStr payload);
225
231 bool publish(StringRef topic, ProgmemStr payload) { return this->publish(topic.c_str(), payload); }
232#endif
233
239 bool publish_json(const std::string &topic, const json::json_build_t &f);
240
246 bool publish_json(const char *topic, const json::json_build_t &f);
247
253 bool publish_json(StringRef topic, const json::json_build_t &f) { return this->publish_json(topic.c_str(), f); }
254
261 void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos = 0);
262
272 void subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos = 0);
273
274 protected:
276 StringRef get_discovery_topic_to_(std::span<char, MQTT_DISCOVERY_TOPIC_MAX_LEN> buf,
277 const MQTTDiscoveryInfo &discovery_info) const;
278
285 StringRef get_default_topic_for_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf, const char *suffix,
286 size_t suffix_len) const;
287
293 std::string get_default_topic_for_(const std::string &suffix) const;
294
298 virtual const EntityBase *get_entity() const = 0;
299
301 const StringRef &friendly_name_() const;
302
304 StringRef get_icon_ref_() const;
305
307 bool is_disabled_by_default_() const;
308
312 StringRef get_state_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const;
313
317 StringRef get_command_topic_to_(std::span<char, MQTT_DEFAULT_TOPIC_MAX_LEN> buf) const;
318
320 std::string get_state_topic_() const;
321
323 std::string get_command_topic_() const;
324
325 bool is_connected_() const;
326
328 bool send_discovery_();
329
330 // ========== INTERNAL METHODS ==========
331 // (In most use cases you won't need these)
333 StringRef get_default_object_id_to_(std::span<char, OBJECT_ID_MAX_LEN> buf) const;
334
337
338 std::unique_ptr<Availability> availability_;
339
340 // Packed bitfields - QoS values are 0-2, bools are flags
341 uint8_t qos_ : 2 {0};
342 uint8_t subscribe_qos_ : 2 {0};
343 bool command_retain_ : 1 {false};
344 bool retain_ : 1 {true};
345 bool discovery_enabled_ : 1 {true};
346 bool resend_state_ : 1 {false};
347 bool is_internal_ : 1 {false};
348
352};
353
354} // namespace esphome::mqtt
355
356#endif // USE_MQTt
ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const std uint32_t uint8_t std::function< RetryResult(uint8_t)> && f
Definition component.h:373
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
constexpr const char * c_str() const
Definition string_ref.h:73
MQTTComponent is the base class for all components that interact with MQTT to expose certain function...
StringRef get_command_topic_to_(std::span< char, MQTT_DEFAULT_TOPIC_MAX_LEN > buf) const
Get the MQTT command topic into a buffer (no heap allocation for non-lambda custom topics).
TemplatableValue< std::string > custom_state_topic_
TemplatableValue< std::string > custom_command_topic_
bool publish_json(StringRef topic, const json::json_build_t &f)
Construct and send a JSON MQTT message (no heap allocation for topic).
std::unique_ptr< Availability > availability_
StringRef get_discovery_topic_to_(std::span< char, MQTT_DISCOVERY_TOPIC_MAX_LEN > buf, const MQTTDiscoveryInfo &discovery_info) const
Helper method to get the discovery topic for this component into a buffer.
bool is_disabled_by_default_() const
Get whether the underlying Entity is disabled by default.
MQTTComponent()
Constructs a MQTTComponent.
void set_custom_command_topic(T &&custom_command_topic)
bool is_internal() const
Returns cached is_internal result (computed once during setup).
bool publish(StringRef topic, ProgmemStr payload)
Send a MQTT message with a PROGMEM string payload (no heap allocation for topic).
void set_qos(uint8_t qos)
Set QOS for state messages.
StringRef get_default_topic_for_to_(std::span< char, MQTT_DEFAULT_TOPIC_MAX_LEN > buf, const char *suffix, size_t suffix_len) const
Get this components state/command/... topic into a buffer.
void schedule_resend_state()
Internal method for the MQTT client base to schedule a resend of the state on reconnect.
bool publish(StringRef topic, const char *payload, size_t payload_length)
Send a MQTT message (no heap allocation for topic).
bool publish(const std::string &topic, const std::string &payload)
Send a MQTT message.
void set_command_retain(bool command_retain)
Set whether command message should be retained.
bool send_discovery_()
Internal method to start sending discovery info, this will call send_discovery().
bool publish(const std::string &topic, const char *payload)
Send a MQTT message.
bool is_internal_
Cached result of compute_is_internal_(), set during setup.
void set_subscribe_qos(uint8_t qos)
Set the QOS for subscribe messages (used in discovery).
void subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos=0)
Subscribe to a MQTT topic and automatically parse JSON payload.
void call_setup() override
Override setup_ so that we can call send_discovery() when needed.
bool publish_json(const std::string &topic, const json::json_build_t &f)
Construct and send a JSON MQTT message.
std::string get_default_topic_for_(const std::string &suffix) const
Get this components state/command/... topic (allocates std::string).
void process_resend()
Process pending resend if needed (called by MQTTClientComponent)
StringRef get_default_object_id_to_(std::span< char, OBJECT_ID_MAX_LEN > buf) const
Get the object ID for this MQTT component, writing to the provided buffer.
void set_retain(bool retain)
Set whether state message should be retained.
virtual const EntityBase * get_entity() const =0
Gets the Entity served by this MQTT component.
std::string get_state_topic_() const
Get the MQTT topic that new states will be shared to (allocates std::string).
virtual void send_discovery(JsonObject root, SendDiscoveryConfig &config)=0
Send discovery info the Home Assistant, override this.
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).
bool compute_is_internal_()
Compute is_internal status based on topics and entity state.
friend void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic)
void set_custom_state_topic(T &&custom_state_topic)
Set a custom state topic. Do not set for default behavior.
virtual bool send_initial_state()=0
const StringRef & friendly_name_() const
Get the friendly name of this MQTT component.
void disable_discovery()
Disable discovery. Sets friendly name to "".
float get_setup_priority() const override
MQTT_COMPONENT setup priority.
void set_availability(std::string topic, std::string payload_available, std::string payload_not_available)
Set the Home Assistant availability data.
std::string get_command_topic_() const
Get the MQTT topic for listening to commands (allocates std::string).
bool publish(StringRef topic, const char *payload)
Send a MQTT message (no heap allocation for topic).
void subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos=0)
Subscribe to a MQTT topic.
virtual const char * component_type() const =0
Override this method to return the component type (e.g. "light", "sensor", ...)
StringRef get_icon_ref_() const
Get the icon field of this component as StringRef.
std::function< void(JsonObject)> json_build_t
Callback function typedef for building JsonObjects.
Definition json_util.h:46
std::function< void(const std::string &, JsonObject)> mqtt_json_callback_t
Definition mqtt_client.h:39
std::function< void(const std::string &, const std::string &)> mqtt_callback_t
Callback for MQTT subscriptions.
Definition mqtt_client.h:38
void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, bool command_topic)
const __FlashStringHelper * ProgmemStr
Definition progmem.h:26
Internal struct for MQTT Home Assistant discovery.
Definition mqtt_client.h:83
Simple Helper struct used for Home Assistant MQTT send_discovery().
bool command_topic
If the command topic should be included. Default to true.
bool state_topic
If the state topic should be included. Defaults to true.