mirror of
https://github.com/alexhopeoconnor/arduino-home-assistant.git
synced 2026-10-04 02:48:13 +10:00
186 lines
7.1 KiB
Markdown
186 lines
7.1 KiB
Markdown
# 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`
|