From aa47d88882d9bd6c7753315c8fe1b9b2e9b21fa7 Mon Sep 17 00:00:00 2001 From: diogo464 Date: Mon, 19 Jan 2026 20:10:35 +0000 Subject: fix: prefix discovery unique_id with device id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit right now we use the entity id as the unique_id in the entity discovery payload but we should have been prefixing it with the device_id like with did for the topics to avoid generating collisions. for example if we have devices 'relay_0' and 'relay_1' and they both have a button entity with id 'trigger' then the topics will be correctly generated using the id 'relay_0_trigger' and 'relay_1_trigger' but the unique id field in the discovery payload is being set as 'trigger' for both entities which causes this type of error to show up on the home assistant logs: 2026-01-19 19:52:07.539 ERROR (MainThread) [homeassistant.components.button] Platform mqtt does not generate unique IDs. ID open already exists - ignoring button.porta_frente_abri │ │ 2026-01-19 19:52:07.611 ERROR (MainThread) [homeassistant.components.sensor] Platform mqtt does not generate unique IDs. ID wifi-rssi already exists - ignoring sensor.porta_frente │ │ 2026-01-19 19:56:08.013 ERROR (MainThread) [homeassistant.components.button] Platform mqtt does not generate unique IDs. ID open already exists - ignoring button.porta_frente_abri │ │ 2026-01-19 19:56:08.067 ERROR (MainThread) [homeassistant.components.sensor] Platform mqtt does not generate unique IDs. ID wifi-rssi already exists - ignoring sensor.porta_frente | --- src/lib.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index b09bda2..2d1ecfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,11 +181,33 @@ struct DeviceDiscovery<'a> { model: &'a str, } +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(Format))] +struct EntityIdDiscovery<'a> { + device_id: &'a str, + entity_id: &'a str, +} + +impl<'a> core::fmt::Display for EntityIdDiscovery<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}_{}", self.device_id, self.entity_id) + } +} + +impl<'a> Serialize for EntityIdDiscovery<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(self) + } +} + #[derive(Debug, Serialize)] #[cfg_attr(feature = "defmt", derive(Format))] struct EntityDiscovery<'a> { #[serde(rename = "unique_id")] - id: &'a str, + id: EntityIdDiscovery<'a>, #[serde(skip_serializing_if = "Option::is_none")] name: Option<&'a str>, @@ -1006,7 +1028,10 @@ fn generate_entity_discovery( }; let discovery = EntityDiscovery { - id: entity_config.id, + id: EntityIdDiscovery { + device_id: device_config.device_id, + entity_id: entity_config.id, + }, name: entity_config.name, device_class: entity_config.device_class, state_topic: Some(buffers.state_topic.as_str()), @@ -1332,11 +1357,10 @@ pub async fn run(device: &mut Device<'_>, transport: &mut T) -> Re let mut read_buffer = [0u8; 128]; let data_len = publish.data_len; - let receive_data = - match mqtt_receive_data(&mut client, data_len, &mut read_buffer).await { - Ok(data) => data, - Err(_) => continue 'outer_loop, - }; + let receive_data = match mqtt_receive_data(&mut client, data_len, &mut read_buffer).await { + Ok(data) => data, + Err(_) => continue 'outer_loop, + }; let command = match str::from_utf8(receive_data) { Ok(command) => command, -- cgit