| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: Apache-2.0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <string_view>
 | 
					
						
							|  |  |  | #ifndef __cpp_exceptions
 | 
					
						
							|  |  |  | #error MQTT class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <optional>
 | 
					
						
							|  |  |  | #include <variant>
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | #include <memory>
 | 
					
						
							|  |  |  | #include <string>
 | 
					
						
							|  |  |  | #include "esp_exception.hpp"
 | 
					
						
							|  |  |  | #include "esp_mqtt_client_config.hpp"
 | 
					
						
							|  |  |  | #include "mqtt_client.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace idf::mqtt { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | constexpr auto *TAG = "mqtt_client_cpp"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct MQTTException : ESPException { | 
					
						
							|  |  |  |     using ESPException::ESPException; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief QoS for publish and subscribe | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Sets the QoS as: | 
					
						
							|  |  |  |  * AtMostOnce  : Best effort delivery of messages. Message loss can occur. | 
					
						
							|  |  |  |  * AtLeastOnce : Guaranteed delivery of messages. Duplicates can occur. | 
					
						
							|  |  |  |  * ExactlyOnce : Guaranteed delivery of messages exactly once. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |  * @note | 
					
						
							|  |  |  |  *  When subscribing to a topic the QoS means the maximum QoS that should be sent to | 
					
						
							|  |  |  |  *  client on this topic | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | enum class QoS { AtMostOnce = 0, AtLeastOnce = 1, ExactlyOnce = 2 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Sets if a message must be retained. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Retained messages are delivered to future subscribers that match the topic name. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | enum class Retain : bool { NotRetained = false, Retained = true }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Message class template to publish. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template <typename T> struct Message { | 
					
						
							|  |  |  |     T data; /*!< Data for publish. Should be a contiguous type*/ | 
					
						
							|  |  |  |     QoS qos = QoS::AtLeastOnce; /*!< QoS for the message*/ | 
					
						
							|  |  |  |     Retain retain = Retain::NotRetained; /*!< Retention mark for the message.*/ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Message type that holds std::string for data | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | using StringMessage = Message<std::string>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] bool filter_is_valid(std::string::const_iterator first, std::string::const_iterator last); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Filter for mqtt topic subscription. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Topic filter. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class Filter { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Constructs the topic filter from the user filter | 
					
						
							|  |  |  |      * @throws std::domain_error if the filter is invalid. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param user_filter Filter to be used. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     explicit Filter(std::string user_filter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Get the filter string used. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @return Reference to the topic filter. | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      */ | 
					
						
							|  |  |  |     const std::string &get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Checks the filter string against a topic name. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @param topic_begin Iterator to the beginning of the sequence. | 
					
						
							|  |  |  |      * @param topic_end Iterator to the end of the sequence. | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return true if the topic name match the filter | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     [[nodiscard]] bool match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept; | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Checks the filter string against a topic name. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param topic topic name | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return true if the topic name match the filter | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     [[nodiscard]] bool match(const std::string &topic) const noexcept; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Checks the filter string against a topic name. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @param begin Char array with topic name. | 
					
						
							|  |  |  |      * @param size  Size of given topic name. | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return true if the topic name match the filter | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     [[nodiscard]] bool match(char *begin, int size) const noexcept; | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Advance the topic to the next level. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * An mqtt topic ends with a /. This function is used to iterate in topic levels. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Iterator to the start of the topic. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     [[nodiscard]] std::string::const_iterator advance(std::string::const_iterator begin, std::string::const_iterator end) const; | 
					
						
							|  |  |  |     std::string filter; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Message identifier to track delivery. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | enum class MessageID : int {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Base class for MQTT client | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Should be inherited to provide event handlers. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class Client { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Constructor of the client | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param broker Configuration for broker connection | 
					
						
							|  |  |  |      * @param credentials client credentials to be presented to the broker | 
					
						
							|  |  |  |      * @param config Mqtt client configuration | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     Client(const BrokerConfiguration &broker, const ClientCredentials &credentials, const Configuration &config); | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Constructs Client using the same configuration used for | 
					
						
							|  |  |  |      * `esp_mqtt_client` | 
					
						
							|  |  |  |      * @param config config struct to `esp_mqtt_client` | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     Client(const esp_mqtt_client_config_t &config); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Subscribe to topic | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @param topic_filter MQTT topic filter | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      * @param qos QoS subscription, defaulted as QoS::AtLeastOnce | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Optional MessageID. In case of failure std::nullopt is returned. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     std::optional<MessageID> subscribe(const std::string &topic_filter, QoS qos = QoS::AtLeastOnce); | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief publish message to topic | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @tparam Container Type for data container. Must be a contiguous memory. | 
					
						
							|  |  |  |      * @param topic Topic name | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @param message Message struct containing data, qos and retain | 
					
						
							|  |  |  |      * configuration. | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return Optional MessageID. In case of failure std::nullopt is returned. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     template <class Container> | 
					
						
							|  |  |  |     std::optional<MessageID> publish(const std::string &topic, const Message<Container> &message) | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         return publish(topic, std::begin(message.data), std::end(message.data), message.qos, message.retain); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief publish message to topic | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @tparam InputIt Input data iterator type. | 
					
						
							|  |  |  |      * @param topic Topic name | 
					
						
							|  |  |  |      * @param first, last Iterator pair of data to publish | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |      * @param qos Set qos message | 
					
						
							|  |  |  |      * @param retain Set if message should be retained | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return Optional MessageID. In case of failure std::nullopt is returned. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     template <class InputIt> | 
					
						
							|  |  |  |     std::optional<MessageID> publish(const std::string &topic, InputIt first, InputIt last, QoS qos = QoS::AtLeastOnce, Retain retain = Retain::NotRetained) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto size = std::distance(first, last); | 
					
						
							|  |  |  |         auto res =  esp_mqtt_client_publish(handler.get(), topic.c_str(), &(*first), size, static_cast<int>(qos), | 
					
						
							|  |  |  |                                             static_cast<int>(retain)); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             return std::nullopt; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return MessageID{res}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual ~Client() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Helper type to be used as custom deleter for std::unique_ptr. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     struct MqttClientDeleter { | 
					
						
							|  |  |  |         void operator()(esp_mqtt_client *client_handler) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             esp_mqtt_client_destroy(client_handler); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief Type of the handler for the underlying mqtt_client handler. | 
					
						
							|  |  |  |      * It uses std::unique_ptr for lifetime management | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     using ClientHandler = std::unique_ptr<esp_mqtt_client, MqttClientDeleter>; | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |      * @brief esp_mqtt_client handler | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     ClientHandler handler; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an error event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_error(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an disconnection event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_disconnected(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an subscribed event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_subscribed(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an unsubscribed event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_unsubscribed(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an published event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_published(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an before connect event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_before_connect(const esp_mqtt_event_handle_t event); | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an connected event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_connected(const esp_mqtt_event_handle_t event) = 0; | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  |     /**
 | 
					
						
							|  |  |  |     * @brief Called if there is an data event | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     * @param event mqtt event data | 
					
						
							|  |  |  |     * | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  |     virtual void on_data(const esp_mqtt_event_handle_t event) = 0; | 
					
						
							| 
									
										
										
										
											2022-10-13 17:17:15 +02:00
										 |  |  | private: | 
					
						
							|  |  |  |     static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, | 
					
						
							|  |  |  |                                    void *event_data) noexcept; | 
					
						
							|  |  |  |     void init(const esp_mqtt_client_config_t &config); | 
					
						
							| 
									
										
										
										
											2020-12-15 13:10:10 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | } // namespace idf::mqtt
 |