Skip to content

Instantly share code, notes, and snippets.

@Resinchem
Last active July 12, 2025 10:54
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["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();
}
@Resinchem
Copy link
Author

Yeah... as stated at the top of the code, what is provided is simply an example and likely not functional 'as-is'. I posted it after multiple requests for sample code. I took code from an existing project and made it more "generic" to serve as an example here. I'm not surprised that during the copy/paste/edit process, that a few things were missed or duplicated (like the deviceI from above). My goal was to try to supplement the Home Assistant MQTT documentation with more of a complete example. And of course, a later Home Assistant update also slightly changed the way that devices are discovered. This old method still works (for now), but I also posted an update video Updates to Home Assistant MQTT Discovery, that shows the newer method... but the gist here is still using the original method.

All this is to say that the above code is just an example that, along with the official documentation, hopefully can help you to build discovery into your own project. But it isn't a simple drop-in example that can be used 'as-is'. If you do find other errors (using the older method), please let me know and I'll try to correct them as time allows.

@ch0gan
Copy link

ch0gan commented May 29, 2025 via email

@Resinchem
Copy link
Author

Right now, the short update video on changes introduced in Home Assistant 2024.11 provides examples of the changes. It is on my list to update this gist with those changes, but I simply haven't had the time to modify and test the new code. As mentioned already, the older method (as shown here) still works. Yes, ideally you should use the latest implementation method and between the video and HA documentation, it should be fairly easy to adapt the example here for the new device structure.

I'll try to get around to updating this gist at some point, but with other priorities I simply can't say when that might happen.

@ch0gan
Copy link

ch0gan commented May 30, 2025 via email

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