Skip to content

Instantly share code, notes, and snippets.

@Resinchem
Last active February 29, 2024 23:42
Show Gist options
  • Save Resinchem/ecd86dfb52bd699c79acfa80cd348d7b to your computer and use it in GitHub Desktop.
Save Resinchem/ecd86dfb52bd699c79acfa80cd348d7b to your computer and use it in GitHub Desktop.
Sample Arduino code for publishing devices using Home Assistant MQTT Discovery. See the following for use case: https://youtu.be/VHiCtZqllU8 or https://resinchemtech.blogspot.com/2023/12/mqtt-auto-discovery.html
/* ===================================================================
SAMPLE CODE Segments for Home Assistant MQTT Discovery
This is NOT complete code but shows an example of generating a unique ID
for topic/entity publishing based on the device MAC address.
It also includes sample code for creating a Home Assistant device with four
entities via MQTT Discovery, as shown here:
Video: https://youtu.be/VHiCtZqllU8
Blog: https://resinchemtech.blogspot.com/2023/12/mqtt-auto-discovery.html
It is highly recommended that you include some sort of end-user accessible setting to
enable/disable Home Assistant discovery as some users may not want your device auto-discovered
and Home Assistant currently doesn't provide any prompting... devices/entities are immediately
added as soon as a valid topic/payload are received.
THIS IS AN INCOMPLETE EXAMPLE ONLY!!!!
===================================================================== */
//Partial list of libaries - also need Wifi, ESP8266/ESP32 libraries, etc.
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ESP8266WebServer.h> //Used for HTTP callback to enable/disable discovery
//Auto-discover enable/disable option
bool auto_discovery = false; //default to false and provide end-user interface to allow toggling
//Variables for creating unique entity IDs and topics
byte macAddr[6]; //Device MAC address
char uidPrefix[] = "rctdev"; //Prefix for unique ID generation (limit to 20 chars)
char devUniqueID[30]; //Generated Unique ID for this device (uidPrefix + last 6 MAC characters)
// =====================================
// Create Unique ID for topics/entities
// =====================================
void createDiscoveryUniqueID() {
//Generate UniqueID from uidPrefix + last 6 chars of device MAC address
//This should insure that even multiple devices installed in same HA instance are unique
strcpy(devUniqueID, uidPrefix);
int preSizeBytes = sizeof(uidPrefix);
int preSizeElements = (sizeof(uidPrefix) / sizeof(uidPrefix[0]));
//Now add last 6 chars from MAC address (these are 'reversed' in the array)
int j = 0;
for (int i = 2; i >= 0; i--) {
sprintf(&devUniqueID[(preSizeBytes - 1) + (j)], "%02X", macAddr[i]); //preSizeBytes indicates num of bytes in prefix - null terminator, plus 2 (j) bytes for each hex segment of MAC
j = j + 2;
}
// End result is a unique ID for this device (e.g. rctdevE350CA)
Serial.print("Unique ID: ");
Serial.println(devUniqueID);
}
// ===============================
// Main HA MQTT Discover Function
// This creates a single fictional device with four entities:
// - A dimmer switch with light level, temperature and IP address
// ===============================
void haDiscovery() {
char topic[128];
if (auto_discovery) {
char buffer1[512];
char buffer2[512];
char buffer3[512];
char buffer4[512];
char uid[128];
DynamicJsonDocument doc(512);
doc.clear();
Serial.println("Discovering new devices...");
Serial.println("Adding light switch...");
//Create unique topic based on devUniqueID
strcpy(topic, "homeassistant/light/");
strcat(topic, devUniqueID);
strcat(topic, "S/config");
//Create unique_id based on devUniqueID
strcpy(uid, devUniqueID);
strcat(uid, "S");
//Create JSON payload per HA documentation
doc["name"] = "My MQTT Light";
doc["obj_id"] = "mqtt_light";
doc["uniq_id"] = uid;
doc["stat_t"] = "stat/mydevice/switch";
doc["cmd_t"] = "cmnd/mydevice/switch";
doc["brightness"] = "true";
doc["bri_scl"] = "100";
doc["bri_stat_t"] = "stat/mydevice/brightness";
doc["bri_cmd_t"] = "cmnd/mydevice/brightness";
JsonObject device = doc.createNestedObject("device");
device["ids"] = "mymqttdevice01";
device["name"] = "My MQTT Device";
device["mf"] = "Resinchem Tech";
device["mdl"] = "ESP8266";
device["sw"] = "1.24";
device["hw"] = "0.45";
device["cu"] = "http://192.168.1.226/config"; //web interface for device, with discovery toggle
serializeJson(doc, buffer1);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer1, true);
//Lux Sensor
Serial.println("Adding light sensor...");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "L/config");
//Create unique_id based on decUniqueID
strcpy(uid, devUniqueID);
strcat(uid, "L");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "Light Level";
doc["obj_id"] = "mqtt_light_level";
doc["dev_cla"] = "illuminance";
doc["uniq_id"] = uid;
doc["stat_t"] = "stat/mydevice/lightlevel";
doc["unit_of_meas"] = "lx";
JsonObject deviceS = doc.createNestedObject("device");
deviceS["ids"] = "mymqttdevice01";
deviceS["name"] = "My MQTT Device";
serializeJson(doc, buffer2);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer2, true);
//Temperature Sensor
Serial.println("Adding Temp Sensor...");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "T/config");
//Create unique_id based on decUniqueID
strcpy(uid, devUniqueID);
strcat(uid, "T");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "Temperature";
doc["obj_id"] = "mqtt_temperature";
doc["deve_cla"] = "temperature";
doc["uniq_id"] = uid;
doc["stat_t"] = "stat/mydevice/temperature";
doc["unit_of_meas"] = "°F";
JsonObject deviceT = doc.createNestedObject("device");
deviceT["ids"] = "mymqttdevice01";
deviceT["name"] = "My MQTT Device";
serializeJson(doc, buffer3);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer3, true);
//IP Address Diagnostic
Serial.println("Adding IP Diagnostic Sensor...");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "I/config");
//Create unique_id based on decUniqueID
strcpy(uid, devUniqueID);
strcat(uid, "I");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "IP Address";
doc["uniq_id"] = uid;
doc["ent_cat"] = "diagnostic";
doc["stat_t"] = "stat/mydevice/ipaddress";
JsonObject deviceI = doc.createNestedObject("device");
deviceI = doc.createNestedObject("device");
deviceI["ids"] = "mymqttdevice01";
deviceI["name"] = "My MQTT Device";
serializeJson(doc, buffer4);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer4, true);
Serial.println("All devices added!");
} else {
//Remove all entities by publishing empty payloads
//Must use original topic, so recreate from original Unique ID
//This will immediately remove/delete the device/entities from HA
Serial.println("Removing discovered devices...");
//Lux Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "L/config");
client.publish(topic, "");
//Temperature Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "T/config");
client.publish(topic, "");
//IP Address Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, devUniqueID);
strcat(topic, "I/config");
client.publish(topic, "");
//Light (switch)
strcpy(topic, "homeassistant/light/");
strcat(topic, devUniqueID);
strcat(topic, "S/config");
client.publish(topic, "");
Serial.println("Devices Removed...");
}
}
// =====================
// MAIN SETUP
// =====================
void Setup() {
// The following should be included in (or called from) the main setup routine
//Get MAC address when joining wifi and place into char array
WiFi.macAddress(macAddr);
//Call routing (or embed here) to create initial Unique ID
createDiscoveryUniqueID();
//Handle web callbacks for enabling or disabling discovery (using this method is just one of many ways to do this)
server.on("/discovery_on",[]() {
server.send(200, "text/html", "<h1>Discovery ON...<h1><h3>Home Assistant MQTT Discovery enabled</h3>");
delay(200);
auto_discovery = true;
haDiscovery();
});
server.on("/discovery_off",[]() {
server.send(200, "text/html", "<h1>Discovery OFF...<h1><h3>Home Assistant MQTT Discovery disabled. Previous entities removed.</h3>");
delay(200);
auto_discovery = false;
haDiscovery();
});
server.begin();
}
@ylnest2018
Copy link

You're a wizard...thank you!

I will look at that project, im still stumbling my way through arduino and mqtt so there are some things where I look very silly and then you point it out and it's obvious 😅

Thank you again, please keep up the great content!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment