# MQTT usage ## Callbacks and tuning The following snippets extend a sketch that already owns long-lived `HADevice`, `HAMqtt`, and entity objects. Configure callbacks, discovery metadata, and availability before `mqtt.begin(...)`; call the publishing snippets later from normal application logic. `HAMqtt` supports optional callbacks and PubSubClient tuning: ```cpp // These callbacks are registered before mqtt.begin(...). void onMessage(const char* topic, const uint8_t* payload, uint16_t length) { /* ... */ } void onConnected() { /* ... */ } void onDisconnected() { /* ... */ } void onStateChanged(HAMqtt::ConnectionState state) { /* ... */ } void setup() { // A successful reconnect creates a new MQTT session, so onConnected runs again. mqtt.onMessage(onMessage); mqtt.onConnected(onConnected); mqtt.onDisconnected(onDisconnected); mqtt.onStateChanged(onStateChanged); mqtt.setBufferSize(512); // Increase only when a larger MQTT packet is required. mqtt.setKeepAlive(60); // Seconds; the default is 15. mqtt.begin("192.168.1.50", "user", "pass"); } ``` ## Custom subscriptions Subscribe after each successful connection (for example in `onConnected`), because subscriptions are tied to the MQTT session: ```cpp void onConnected() { // MQTT subscriptions belong to this session and must be restored after reconnecting. mqtt.subscribe("my/custom/topic"); } ``` Handle payloads in `onMessage`. ## Publishing arbitrary payloads ```cpp // Call after mqtt.begin(...); retain controls whether the broker keeps this value. void publishApplicationState() { mqtt.publish("customTopic", "payload", false); // Not retained. mqtt.publish("customTopic", "payload", true); // Retained. } ``` ## Availability **Shared availability (recommended):** one availability topic for the whole device — works well with **Last Will** (LWT): ```cpp // Configure device-wide availability before mqtt.begin(...). void configureSharedAvailability() { device.enableSharedAvailability(); device.setPayloadAvailable("up"); device.setPayloadNotAvailable("down"); device.enableLastWill(); // Broker publishes offline when TCP drops. // device.setAvailability(false); // Optional: start as offline. } ``` **Per-entity availability:** call `someEntity.setAvailability(true/false)` on each type. Does not use LWT the same way as shared mode; see examples under `examples/availability/`. Custom per-entity payloads are supported: ```cpp // Configure the relevant entity before mqtt.begin(...). void configureSensorAvailability() { sensor.setPayloadAvailable("ready"); sensor.setPayloadNotAvailable("lost"); } ``` For multi-topic availability discovery, add full MQTT topics and a mode: ```cpp // `sensor` is the entity whose availability depends on these external topics. void configureExternalAvailability() { sensor.setAvailabilityMode("all"); sensor.addAvailabilityEntry("bridge/status"); sensor.addAvailabilityEntry("sensor/status", "{{ value_json.state }}"); } ``` ## Discovery helpers by entity Common entity discovery metadata is available on most entity classes: ```cpp // `entity` is an entity type that supports these common discovery fields. void configureCommonDiscovery(HABaseDeviceType& entity) { entity.setEnabledByDefault(false); entity.setEntityPicture("https://example.com/entity.png"); entity.setQos(1); entity.setEncoding("utf-8"); entity.setEntityCategory("diagnostic"); } ``` Read-only sensor presentation/template helpers: ```cpp // `sensor` is a read-only HASensor instance. void configureSensorPresentation(HASensor& sensor) { sensor.setSuggestedDisplayPrecision(2); sensor.setValueTemplate("{{ value_json.temperature }}"); sensor.setJsonAttributesTemplate("{{ value_json.attrs | tojson }}"); sensor.setLastResetValueTemplate("{{ value_json.last_reset }}"); sensor.setDeviceClass("enum"); sensor.setOptions("idle;charging;discharging;fault"); } ``` Writable entity template/payload helpers: ```cpp // These names refer to the matching writable entity instances in the application. void configureWritableEntities(HASwitch& mySwitch, HANumber& myNumber, HASelect& mySelect, HAText& myText, HAButton& myButton) { mySwitch.setPayloadOn("ENABLE"); mySwitch.setPayloadOff("DISABLE"); mySwitch.setStateOn("running"); mySwitch.setStateOff("stopped"); mySwitch.setValueTemplate("{{ value_json.state }}"); mySwitch.setCommandTemplate("{{ value_json.command }}"); myNumber.setPayloadReset("RESET"); myNumber.setCommandTemplate("{{ value | float | round(1) }}"); mySelect.setCommandTemplate("{{ value_json.choice }}"); myText.setCommandTemplate("{{ value_json.text }}"); myButton.setPayloadPress("PRESS"); } ``` ## Compiler macros Defined in `ArduinoHADefines.h` or via build flags. - **`ARDUINOHA_DISABLE_STDFUNCTION`** — disables the default capturing-lambda and `std::bind` overloads on entity command APIs. Use it on targets without a complete `std::function` implementation or where code size/RAM matters (constrained AVR builds are the usual case). ESP8266/ESP32 support the overloads. `HAMqtt` lifecycle callbacks remain ordinary function pointers. - **`ARDUINOHA_DEBUG`** — enables ArduinoHA logging by default and sets the initial maximum verbosity to `Debug`. Without this flag, structured logs are compiled in but remain disabled until you call `arduinoHASetLogEnabled(true)`. Structured logging is available through a long-lived sink: ```cpp class MyLogSink : public ArduinoHALogSink { public: void log(const ArduinoHALogMessage& msg) override { Serial.print("["); Serial.print(arduinoHALogLevelTag(msg.level)); Serial.print("][aha."); Serial.print(msg.subsystem); Serial.print("] "); Serial.println(msg.text); } }; MyLogSink logSink; // The installed sink must outlive ArduinoHA logging. void setup() { Serial.begin(115200); arduinoHASetLogSink(&logSink); arduinoHASetLogEnabled(true); arduinoHASetLogLevel(ArduinoHALogLevel::Trace); } ``` `arduinoHALog(...)` and `arduinoHALogf(...)` support subsystem-tagged messages such as `mqtt`, `discovery`, `availability`, `serializer`, `entity`, and `device`. Install a sink when you want those messages to join your application’s normal serial or structured log stream. Applications that use Wi-Fi can register **`arduinoHASetNetworkStatusFn`** with a callback returning `WiFi.status()`. Publish failure diagnostics can then include the current Wi-Fi status without coupling ArduinoHA to a particular network library. **Exclude unused device types** (saves flash from vtables), e.g.: `EX_ARDUINOHA_BINARY_SENSOR`, `EX_ARDUINOHA_BUTTON`, `EX_ARDUINOHA_CAMERA`, `EX_ARDUINOHA_COVER`, `EX_ARDUINOHA_DEVICE_TRACKER`, `EX_ARDUINOHA_DEVICE_TRIGGER`, `EX_ARDUINOHA_FAN`, `EX_ARDUINOHA_HVAC`, `EX_ARDUINOHA_LIGHT`, `EX_ARDUINOHA_LOCK`, `EX_ARDUINOHA_NUMBER`, `EX_ARDUINOHA_SCENE`, `EX_ARDUINOHA_SELECT`, `EX_ARDUINOHA_SENSOR`, `EX_ARDUINOHA_SWITCH`, `EX_ARDUINOHA_TAG_SCANNER`