diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py
index 0f7d246473..73af0bad90 100644
--- a/esphome/components/mqtt/__init__.py
+++ b/esphome/components/mqtt/__init__.py
@@ -14,6 +14,7 @@ from esphome.const import (
CONF_DISCOVERY,
CONF_DISCOVERY_PREFIX,
CONF_DISCOVERY_RETAIN,
+ CONF_DISCOVERY_UNIQUE_ID_GENERATOR,
CONF_ID,
CONF_KEEPALIVE,
CONF_LEVEL,
@@ -95,6 +96,12 @@ MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
+MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator")
+MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS = {
+ "legacy": MQTTDiscoveryUniqueIdGenerator.MQTT_LEGACY_UNIQUE_ID_GENERATOR,
+ "mac": MQTTDiscoveryUniqueIdGenerator.MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
+}
+
def validate_config(value):
# Populate default fields
@@ -153,6 +160,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(
CONF_DISCOVERY_PREFIX, default="homeassistant"
): cv.publish_topic,
+ cv.Optional(CONF_DISCOVERY_UNIQUE_ID_GENERATOR, default="legacy"): cv.enum(
+ MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS
+ ),
cv.Optional(CONF_USE_ABBREVIATIONS, default=True): cv.boolean,
cv.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
cv.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
@@ -231,13 +241,22 @@ async def to_code(config):
discovery = config[CONF_DISCOVERY]
discovery_retain = config[CONF_DISCOVERY_RETAIN]
discovery_prefix = config[CONF_DISCOVERY_PREFIX]
+ discovery_unique_id_generator = config[CONF_DISCOVERY_UNIQUE_ID_GENERATOR]
if not discovery:
cg.add(var.disable_discovery())
elif discovery == "CLEAN":
- cg.add(var.set_discovery_info(discovery_prefix, discovery_retain, True))
+ cg.add(
+ var.set_discovery_info(
+ discovery_prefix, discovery_unique_id_generator, discovery_retain, True
+ )
+ )
elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
- cg.add(var.set_discovery_info(discovery_prefix, discovery_retain))
+ cg.add(
+ var.set_discovery_info(
+ discovery_prefix, discovery_unique_id_generator, discovery_retain
+ )
+ )
cg.add(var.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp
index 040b0001fe..43c49e9f7f 100644
--- a/esphome/components/mqtt/mqtt_client.cpp
+++ b/esphome/components/mqtt/mqtt_client.cpp
@@ -535,8 +535,10 @@ void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
-void MQTTClientComponent::set_discovery_info(std::string &&prefix, bool retain, bool clean) {
+void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
+ bool retain, bool clean) {
this->discovery_info_.prefix = std::move(prefix);
+ this->discovery_info_.unique_id_generator = unique_id_generator;
this->discovery_info_.retain = retain;
this->discovery_info_.clean = clean;
}
diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h
index fa689eaa04..d6194da794 100644
--- a/esphome/components/mqtt/mqtt_client.h
+++ b/esphome/components/mqtt/mqtt_client.h
@@ -55,6 +55,12 @@ struct Availability {
std::string payload_not_available;
};
+/// available discovery unique_id generators
+enum MQTTDiscoveryUniqueIdGenerator {
+ MQTT_LEGACY_UNIQUE_ID_GENERATOR = 0,
+ MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
+};
+
/** Internal struct for MQTT Home Assistant discovery
*
* See MQTT Discovery.
@@ -63,6 +69,7 @@ struct MQTTDiscoveryInfo {
std::string prefix; ///< The Home Assistant discovery prefix. Empty means disabled.
bool retain; ///< Whether to retain discovery messages.
bool clean;
+ MQTTDiscoveryUniqueIdGenerator unique_id_generator;
};
enum MQTTClientState {
@@ -98,9 +105,11 @@ class MQTTClientComponent : public Component {
*
* See MQTT Discovery.
* @param prefix The Home Assistant discovery prefix.
+ * @param unique_id_generator Controls how UniqueId is generated.
* @param retain Whether to retain discovery messages.
*/
- void set_discovery_info(std::string &&prefix, bool retain, bool clean = false);
+ void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, bool retain,
+ bool clean = false);
/// Get Home Assistant discovery info.
const MQTTDiscoveryInfo &get_discovery_info() const;
/// Globally disable Home Assistant discovery.
diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp
index e3ae4dea50..bf9f5e34b8 100644
--- a/esphome/components/mqtt/mqtt_component.cpp
+++ b/esphome/components/mqtt/mqtt_component.cpp
@@ -114,9 +114,17 @@ bool MQTTComponent::send_discovery_() {
if (!unique_id.empty()) {
root[MQTT_UNIQUE_ID] = unique_id;
} else {
- // default to almost-unique ID. It's a hack but the only way to get that
- // gorgeous device registry view.
- root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
+ const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
+ if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
+ char friendly_name_hash[9];
+ sprintf(friendly_name_hash, "%08x", fnv1_hash(this->friendly_name()));
+ friendly_name_hash[8] = 0; // ensure the hash-string ends with null
+ root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash;
+ } else {
+ // default to almost-unique ID. It's a hack but the only way to get that
+ // gorgeous device registry view.
+ root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
+ }
}
JsonObject &device_info = root.createNestedObject(MQTT_DEVICE);
diff --git a/esphome/const.py b/esphome/const.py
index fb241f04b5..9a778edfeb 100644
--- a/esphome/const.py
+++ b/esphome/const.py
@@ -167,6 +167,7 @@ CONF_DISABLED_BY_DEFAULT = "disabled_by_default"
CONF_DISCOVERY = "discovery"
CONF_DISCOVERY_PREFIX = "discovery_prefix"
CONF_DISCOVERY_RETAIN = "discovery_retain"
+CONF_DISCOVERY_UNIQUE_ID_GENERATOR = "discovery_unique_id_generator"
CONF_DISTANCE = "distance"
CONF_DITHER = "dither"
CONF_DIV_RATIO = "div_ratio"
diff --git a/tests/test1.yaml b/tests/test1.yaml
index 263754dc4f..18c6610b08 100644
--- a/tests/test1.yaml
+++ b/tests/test1.yaml
@@ -98,6 +98,7 @@ mqtt:
discovery: True
discovery_retain: False
discovery_prefix: discovery
+ discovery_unique_id_generator: legacy
topic_prefix: helloworld
log_topic:
topic: helloworld/hi