MqttManager
Overview
The MqttManager class provides a robust bridge between the device’s internal event-driven system and an external MQTT broker. It handles the complexities of establishing and maintaining an MQTT connection (including SSL/TLS), subscribing to command topics, and publishing state and event data from the device.
This class acts as a translator, converting internal application events (like lock state changes or NFC taps) into MQTT messages, and converting incoming MQTT messages into internal events that other managers can act upon. It also supports Home Assistant MQTT Discovery, allowing the lock to be automatically recognized and configured in Home Assistant.
Key Responsibilities
- MQTT Client Management: Initializes, starts, and manages the lifecycle of the ESP-MQTT client.
- Connection Handling: Manages connection and disconnection events, including publishing a Last Will and Testament (LWT) for presence detection.
- SSL/TLS Configuration: Configures secure MQTT connections using certificates managed by the
ConfigManager. - Event Bridging: Subscribes to internal events (
LOCK_EVENT,NFC_EVENT,HW_EVENT) and publishes corresponding data to MQTT topics. - Command Handling: Subscribes to MQTT command topics and publishes internal events (
LOCK_TARGET_STATE_CHANGED,LOCK_OVERRIDE_STATE, etc.) to control the device remotely. - Home Assistant Discovery: Publishes configuration payloads to Home Assistant’s discovery topics, enabling seamless integration.
- JsonGuard Integration: The manager now uses
JsonBuilder(part of theJsonGuardutility) for constructing JSON payloads. This provides RAII-based memory safety for cJSON objects and a cleaner, more readable fluent API for building JSON strings. - Data Formatting: Formats event data (e.g., NFC tap details) into structured JSON payloads for easy consumption by external services.
Public API
MqttManager()
Constructs a new MqttManager instance. The constructor retrieves the necessary MQTT and SSL configurations from the provided ConfigManager. This wiring connects the manager to the application’s event bus.
Signature:
MqttManager(const ConfigManager& configManager);Parameters:
configManager: A reference to theConfigManager, which provides all necessary MQTT broker details, topics, and SSL certificates.
~MqttManager()
Destructor for the MqttManager. It ensures that the MQTT client is properly stopped and its resources are released to prevent memory leaks.
Signature:
~MqttManager();begin()
Initializes and starts the MQTT client. This method configures the client with the broker address, credentials, and SSL settings, then attempts to establish a connection. If the MQTT broker is not configured in the settings, this method will do nothing and return false.
Signature:
bool begin(std::string deviceID);Parameters:
deviceID: A unique string identifier for this device, used in Home Assistant discovery payloads.
Returns:
bool:trueif the MQTT client was started successfully, orfalseif the broker was not configured or if initialization failed.
publish()
Publishes a message to a specified MQTT topic. This is a general-purpose method for sending data to the MQTT broker.
Signature:
void publish(const std::string& topic, const std::string& payload, int qos = 0, bool retain = false);Parameters:
topic: The MQTT topic to publish the message to.payload: The message content to send.qos: The Quality of Service level for the message (0, 1, or 2).retain: A boolean flag indicating if the message should be retained by the broker.
isConnected()
Returns the current connection status of the MQTT client.
Signature:
bool isConnected() const;Returns:
bool:trueif the client is currently connected to the broker,falseotherwise.
end()
Stops the MQTT client and unsubscribes from all event listeners. Performs a clean shutdown of the MQTT client by stopping and destroying it, then removes all event subscriptions registered by this instance.
Signature:
void end();getLastErrorCode()
Gets the last MQTT error code that occurred during connection or operation.
Signature:
MqttErrorCode getLastErrorCode() const;Returns:
MqttErrorCode: The last error code. Possible values:NONE(0): No errorCONNECTION_REFUSED(1): Connection refused by brokerAUTH_FAILED(2): Authentication failedNETWORK_ERROR(3): Network connectivity issueSSL_ERROR(4): TLS/SSL handshake or certificate errorTIMEOUT(5): Connection or operation timed outUNKNOWN(255): Unknown error
getLastErrorMessage()
Gets the last MQTT error message as a human-readable string.
Signature:
const std::string& getLastErrorMessage() const;Returns:
const std::string&: Reference to the last error message string.
Internal Workings
Event Handling
The MqttManager is primarily event-driven. The mqttEventHandler is a static callback registered with the ESP-MQTT library, which forwards all events to the instance’s onMqttEvent method.
onMqttEvent: This method acts as a dispatcher. It handles connection/disconnection logic, logs errors, updates the internal status (error code and message), and passes incoming message data to theonDatamethod.onConnected: Called upon a successful connection, this method publishes the retainedonlinepresence message to the LWT topic, subscribes to all necessary command topics, and triggers the Home Assistant discovery process.onData: This is the core of the command handling logic. It parses the topic and payload of an incoming message and translates it into an appropriate internal event using theAppEventLoopsystem. For example, a message on thelockTStateCmdtopic will be converted into aLOCK_TARGET_STATE_CHANGEDevent.
Event System Integration
The MqttManager uses the AppEventLoop system (ESP-IDF’s native event loop) for internal communication:
- Subscribes to:
LOCK_EVENT(LOCK_STATE_CHANGED): Publishes lock state changes to MQTTHW_EVENT(HW_ALT_ACTION): Publishes alternate action eventsNFC_EVENT(NFC_TAP_EVENT): Publishes NFC/HomeKey tap data
- Publishes:
LOCK_EVENT(LOCK_TARGET_STATE_CHANGED/LOCK_OVERRIDE_STATE/LOCK_UPDATE_STATE): Translates incoming MQTT lock commands into internal lock events
Note
The MQTT_EVENT base (MQTT_STATUS_CHANGED) is declared in app_events.hpp but is currently not published by any component. MQTT connection status does not flow through the event loop: publishMqttStatus() only updates internal state, and consumers (like the WebUI) poll isConnected(), getLastErrorCode(), and getLastErrorMessage() instead.
Data Publishing
The manager subscribes to internal events via the AppEventLoop to publish data out to the MQTT broker.
publishLockState: Listens forLOCK_STATE_CHANGEDevents and publishes the lock’s status to the configured state topic. It correctly represents transitional states like “locking” or “unlocking.”publishHomeKeyTap/publishUidTap: Listen forNFC_TAP_EVENTnotifications and publish detailed, JSON-formatted information about the NFC tap to thehkTopic.publishMqttStatus: Updates the internal MQTT connection status (error code and message) retrievable viagetLastErrorCode()/getLastErrorMessage(). Does not publish to an MQTT topic and does not emit an event-loop event; consumers poll these getters (e.g., the WebUI includes the values in its periodicmetricsWebSocket message).- Home Assistant Discovery: The
publishHassDiscoverymethod constructs detailed JSON configuration payloads that describe the lock and NFC tag sensor entities to Home Assistant, allowing for zero-config integration.
SSL/TLS Configuration
configureSSL: This method populates theesp_mqtt_client_config_tstruct with pointers to the certificate strings (CA, client cert, and private key) obtained from theConfigManager. It also handles theallowInsecureflag.logSSLError: A utility function that provides detailed, human-readable logging for SSL/TLS errors to aid in troubleshooting connection issues.