diff options
| author | diogo464 <[email protected]> | 2025-12-12 12:05:58 +0000 |
|---|---|---|
| committer | diogo464 <[email protected]> | 2025-12-12 12:05:58 +0000 |
| commit | e041614b0607a0950be1446f595d85c93b501418 (patch) | |
| tree | 54a69e34ad53bf7c8a844dba0f6585d52f31d2f4 | |
| parent | feb5ae59654bb11365b939ee871adb9760229355 (diff) | |
added device tracker entity
| -rw-r--r-- | examples/device_tracker.rs | 68 | ||||
| -rw-r--r-- | src/constants.rs | 1 | ||||
| -rw-r--r-- | src/entity.rs | 1 | ||||
| -rw-r--r-- | src/entity_device_tracker.rs | 41 | ||||
| -rw-r--r-- | src/lib.rs | 128 |
5 files changed, 226 insertions, 13 deletions
diff --git a/examples/device_tracker.rs b/examples/device_tracker.rs new file mode 100644 index 0000000..7f02333 --- /dev/null +++ b/examples/device_tracker.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | mod common; | ||
| 2 | |||
| 3 | use common::AsyncTcp; | ||
| 4 | use embassy_executor::{Executor, Spawner}; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use static_cell::StaticCell; | ||
| 7 | |||
| 8 | static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new(); | ||
| 9 | |||
| 10 | #[embassy_executor::task] | ||
| 11 | async fn main_task(spawner: Spawner) { | ||
| 12 | let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS")); | ||
| 13 | |||
| 14 | let mut device = embassy_ha::new( | ||
| 15 | RESOURCES.init(Default::default()), | ||
| 16 | embassy_ha::DeviceConfig { | ||
| 17 | device_id: "example-device-id", | ||
| 18 | device_name: "Example Device Name", | ||
| 19 | manufacturer: "Example Device Manufacturer", | ||
| 20 | model: "Example Device Model", | ||
| 21 | }, | ||
| 22 | ); | ||
| 23 | |||
| 24 | let tracker = embassy_ha::create_device_tracker( | ||
| 25 | &device, | ||
| 26 | "device-tracker-id", | ||
| 27 | embassy_ha::DeviceTrackerConfig { | ||
| 28 | common: embassy_ha::EntityCommonConfig { | ||
| 29 | name: Some("Device Tracker Name"), | ||
| 30 | ..Default::default() | ||
| 31 | }, | ||
| 32 | }, | ||
| 33 | ); | ||
| 34 | |||
| 35 | spawner.must_spawn(tracker_task(tracker)); | ||
| 36 | |||
| 37 | embassy_ha::run(&mut device, &mut stream).await.unwrap(); | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn tracker_task(mut tracker: embassy_ha::DeviceTracker<'static>) { | ||
| 42 | let locations = [ | ||
| 43 | embassy_ha::DeviceTrackerLocation { | ||
| 44 | latitude: 38.72197768549349, | ||
| 45 | longitude: -9.195954862428767, | ||
| 46 | accuracy: None, | ||
| 47 | }, | ||
| 48 | embassy_ha::DeviceTrackerLocation { | ||
| 49 | latitude: 38.72253035645279, | ||
| 50 | longitude: -9.179484976517816, | ||
| 51 | accuracy: None, | ||
| 52 | }, | ||
| 53 | embassy_ha::DeviceTrackerLocation { | ||
| 54 | latitude: 38.72962258768138, | ||
| 55 | longitude: -9.195895830579625, | ||
| 56 | accuracy: None, | ||
| 57 | }, | ||
| 58 | ]; | ||
| 59 | |||
| 60 | let mut idx = 0; | ||
| 61 | loop { | ||
| 62 | tracker.publish(locations[idx]); | ||
| 63 | idx = (idx + 1) % locations.len(); | ||
| 64 | Timer::after_secs(1).await; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | example_main!(); | ||
diff --git a/src/constants.rs b/src/constants.rs index 09636ea..73266f7 100644 --- a/src/constants.rs +++ b/src/constants.rs | |||
| @@ -7,6 +7,7 @@ pub const HA_DOMAIN_LIGHT: &str = "light"; | |||
| 7 | pub const HA_DOMAIN_BUTTON: &str = "button"; | 7 | pub const HA_DOMAIN_BUTTON: &str = "button"; |
| 8 | pub const HA_DOMAIN_SELECT: &str = "select"; | 8 | pub const HA_DOMAIN_SELECT: &str = "select"; |
| 9 | pub const HA_DOMAIN_NUMBER: &str = "number"; | 9 | pub const HA_DOMAIN_NUMBER: &str = "number"; |
| 10 | pub const HA_DOMAIN_DEVICE_TRACKER: &str = "device_tracker"; | ||
| 10 | 11 | ||
| 11 | pub const HA_NUMBER_MODE_AUTO: &str = "auto"; | 12 | pub const HA_NUMBER_MODE_AUTO: &str = "auto"; |
| 12 | pub const HA_NUMBER_MODE_BOX: &str = "box"; | 13 | pub const HA_NUMBER_MODE_BOX: &str = "box"; |
diff --git a/src/entity.rs b/src/entity.rs index e7aa895..1af5f93 100644 --- a/src/entity.rs +++ b/src/entity.rs | |||
| @@ -29,6 +29,7 @@ pub(crate) struct EntityConfig { | |||
| 29 | pub category: Option<&'static str>, | 29 | pub category: Option<&'static str>, |
| 30 | pub state_class: Option<&'static str>, | 30 | pub state_class: Option<&'static str>, |
| 31 | pub schema: Option<&'static str>, | 31 | pub schema: Option<&'static str>, |
| 32 | pub platform: Option<&'static str>, | ||
| 32 | pub min: Option<f32>, | 33 | pub min: Option<f32>, |
| 33 | pub max: Option<f32>, | 34 | pub max: Option<f32>, |
| 34 | pub step: Option<f32>, | 35 | pub step: Option<f32>, |
diff --git a/src/entity_device_tracker.rs b/src/entity_device_tracker.rs new file mode 100644 index 0000000..6c82d6a --- /dev/null +++ b/src/entity_device_tracker.rs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | use crate::{DeviceTrackerState, Entity, EntityCommonConfig, EntityConfig, constants}; | ||
| 2 | |||
| 3 | #[derive(Debug, Default)] | ||
| 4 | pub struct DeviceTrackerConfig { | ||
| 5 | pub common: EntityCommonConfig, | ||
| 6 | } | ||
| 7 | |||
| 8 | impl DeviceTrackerConfig { | ||
| 9 | pub(crate) fn populate(&self, config: &mut EntityConfig) { | ||
| 10 | self.common.populate(config); | ||
| 11 | config.domain = constants::HA_DOMAIN_DEVICE_TRACKER; | ||
| 12 | config.platform = Some(constants::HA_DOMAIN_DEVICE_TRACKER); | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | #[derive(Debug, Clone, Copy)] | ||
| 17 | pub struct DeviceTrackerLocation { | ||
| 18 | pub latitude: f32, | ||
| 19 | pub longitude: f32, | ||
| 20 | pub accuracy: Option<f32>, | ||
| 21 | } | ||
| 22 | |||
| 23 | pub struct DeviceTracker<'a>(Entity<'a>); | ||
| 24 | |||
| 25 | impl<'a> DeviceTracker<'a> { | ||
| 26 | pub(crate) fn new(entity: Entity<'a>) -> Self { | ||
| 27 | Self(entity) | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn publish(&mut self, location: DeviceTrackerLocation) { | ||
| 31 | self.0.with_data(|data| { | ||
| 32 | let storage = data.storage.as_device_tracker_mut(); | ||
| 33 | storage.state = Some(DeviceTrackerState { | ||
| 34 | latitude: location.latitude, | ||
| 35 | longitude: location.longitude, | ||
| 36 | gps_accuracy: location.accuracy, | ||
| 37 | }); | ||
| 38 | }); | ||
| 39 | self.0.queue_publish(); | ||
| 40 | } | ||
| 41 | } | ||
| @@ -52,6 +52,9 @@ pub use entity_button::*; | |||
| 52 | mod entity_category; | 52 | mod entity_category; |
| 53 | pub use entity_category::*; | 53 | pub use entity_category::*; |
| 54 | 54 | ||
| 55 | mod entity_device_tracker; | ||
| 56 | pub use entity_device_tracker::*; | ||
| 57 | |||
| 55 | mod entity_number; | 58 | mod entity_number; |
| 56 | pub use entity_number::*; | 59 | pub use entity_number::*; |
| 57 | 60 | ||
| @@ -115,12 +118,18 @@ struct EntityDiscovery<'a> { | |||
| 115 | command_topic: Option<&'a str>, | 118 | command_topic: Option<&'a str>, |
| 116 | 119 | ||
| 117 | #[serde(skip_serializing_if = "Option::is_none")] | 120 | #[serde(skip_serializing_if = "Option::is_none")] |
| 121 | json_attributes_topic: Option<&'a str>, | ||
| 122 | |||
| 123 | #[serde(skip_serializing_if = "Option::is_none")] | ||
| 118 | unit_of_measurement: Option<&'a str>, | 124 | unit_of_measurement: Option<&'a str>, |
| 119 | 125 | ||
| 120 | #[serde(skip_serializing_if = "Option::is_none")] | 126 | #[serde(skip_serializing_if = "Option::is_none")] |
| 121 | schema: Option<&'a str>, | 127 | schema: Option<&'a str>, |
| 122 | 128 | ||
| 123 | #[serde(skip_serializing_if = "Option::is_none")] | 129 | #[serde(skip_serializing_if = "Option::is_none")] |
| 130 | platform: Option<&'a str>, | ||
| 131 | |||
| 132 | #[serde(skip_serializing_if = "Option::is_none")] | ||
| 124 | state_class: Option<&'a str>, | 133 | state_class: Option<&'a str>, |
| 125 | 134 | ||
| 126 | #[serde(skip_serializing_if = "Option::is_none")] | 135 | #[serde(skip_serializing_if = "Option::is_none")] |
| @@ -198,6 +207,21 @@ impl<'a> core::fmt::Display for CommandTopicDisplay<'a> { | |||
| 198 | } | 207 | } |
| 199 | } | 208 | } |
| 200 | 209 | ||
| 210 | struct AttributesTopicDisplay<'a> { | ||
| 211 | device_id: &'a str, | ||
| 212 | entity_id: &'a str, | ||
| 213 | } | ||
| 214 | |||
| 215 | impl<'a> core::fmt::Display for AttributesTopicDisplay<'a> { | ||
| 216 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 217 | write!( | ||
| 218 | f, | ||
| 219 | "embassy-ha/{}/{}/attributes", | ||
| 220 | self.device_id, self.entity_id | ||
| 221 | ) | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 201 | struct DeviceAvailabilityTopic<'a> { | 225 | struct DeviceAvailabilityTopic<'a> { |
| 202 | device_id: &'a str, | 226 | device_id: &'a str, |
| 203 | } | 227 | } |
| @@ -227,6 +251,7 @@ pub struct DeviceResources { | |||
| 227 | discovery_topic_buffer: String<128>, | 251 | discovery_topic_buffer: String<128>, |
| 228 | state_topic_buffer: String<128>, | 252 | state_topic_buffer: String<128>, |
| 229 | command_topic_buffer: String<128>, | 253 | command_topic_buffer: String<128>, |
| 254 | attributes_topic_buffer: String<128>, | ||
| 230 | } | 255 | } |
| 231 | 256 | ||
| 232 | impl DeviceResources { | 257 | impl DeviceResources { |
| @@ -247,6 +272,7 @@ impl Default for DeviceResources { | |||
| 247 | discovery_topic_buffer: Default::default(), | 272 | discovery_topic_buffer: Default::default(), |
| 248 | state_topic_buffer: Default::default(), | 273 | state_topic_buffer: Default::default(), |
| 249 | command_topic_buffer: Default::default(), | 274 | command_topic_buffer: Default::default(), |
| 275 | attributes_topic_buffer: Default::default(), | ||
| 250 | } | 276 | } |
| 251 | } | 277 | } |
| 252 | } | 278 | } |
| @@ -323,6 +349,19 @@ pub(crate) struct NumberStorage { | |||
| 323 | pub publish_on_command: bool, | 349 | pub publish_on_command: bool, |
| 324 | } | 350 | } |
| 325 | 351 | ||
| 352 | #[derive(Debug, Serialize)] | ||
| 353 | pub(crate) struct DeviceTrackerState { | ||
| 354 | pub latitude: f32, | ||
| 355 | pub longitude: f32, | ||
| 356 | #[serde(skip_serializing_if = "Option::is_none")] | ||
| 357 | pub gps_accuracy: Option<f32>, | ||
| 358 | } | ||
| 359 | |||
| 360 | #[derive(Debug, Default)] | ||
| 361 | pub(crate) struct DeviceTrackerStorage { | ||
| 362 | pub state: Option<DeviceTrackerState>, | ||
| 363 | } | ||
| 364 | |||
| 326 | #[derive(Debug)] | 365 | #[derive(Debug)] |
| 327 | pub(crate) enum EntityStorage { | 366 | pub(crate) enum EntityStorage { |
| 328 | Button(ButtonStorage), | 367 | Button(ButtonStorage), |
| @@ -330,6 +369,7 @@ pub(crate) enum EntityStorage { | |||
| 330 | BinarySensor(BinarySensorStorage), | 369 | BinarySensor(BinarySensorStorage), |
| 331 | NumericSensor(NumericSensorStorage), | 370 | NumericSensor(NumericSensorStorage), |
| 332 | Number(NumberStorage), | 371 | Number(NumberStorage), |
| 372 | DeviceTracker(DeviceTrackerStorage), | ||
| 333 | } | 373 | } |
| 334 | 374 | ||
| 335 | impl EntityStorage { | 375 | impl EntityStorage { |
| @@ -367,6 +407,13 @@ impl EntityStorage { | |||
| 367 | _ => panic!("expected storage type to be number"), | 407 | _ => panic!("expected storage type to be number"), |
| 368 | } | 408 | } |
| 369 | } | 409 | } |
| 410 | |||
| 411 | pub fn as_device_tracker_mut(&mut self) -> &mut DeviceTrackerStorage { | ||
| 412 | match self { | ||
| 413 | EntityStorage::DeviceTracker(storage) => storage, | ||
| 414 | _ => panic!("expected storage type to be device tracker"), | ||
| 415 | } | ||
| 416 | } | ||
| 370 | } | 417 | } |
| 371 | 418 | ||
| 372 | struct EntityData { | 419 | struct EntityData { |
| @@ -440,6 +487,7 @@ pub struct Device<'a> { | |||
| 440 | discovery_topic_buffer: &'a mut StringView, | 487 | discovery_topic_buffer: &'a mut StringView, |
| 441 | state_topic_buffer: &'a mut StringView, | 488 | state_topic_buffer: &'a mut StringView, |
| 442 | command_topic_buffer: &'a mut StringView, | 489 | command_topic_buffer: &'a mut StringView, |
| 490 | attributes_topic_buffer: &'a mut StringView, | ||
| 443 | } | 491 | } |
| 444 | 492 | ||
| 445 | pub fn new<'a>(resources: &'a mut DeviceResources, config: DeviceConfig) -> Device<'a> { | 493 | pub fn new<'a>(resources: &'a mut DeviceResources, config: DeviceConfig) -> Device<'a> { |
| @@ -456,6 +504,7 @@ pub fn new<'a>(resources: &'a mut DeviceResources, config: DeviceConfig) -> Devi | |||
| 456 | discovery_topic_buffer: &mut resources.discovery_topic_buffer, | 504 | discovery_topic_buffer: &mut resources.discovery_topic_buffer, |
| 457 | state_topic_buffer: &mut resources.state_topic_buffer, | 505 | state_topic_buffer: &mut resources.state_topic_buffer, |
| 458 | command_topic_buffer: &mut resources.command_topic_buffer, | 506 | command_topic_buffer: &mut resources.command_topic_buffer, |
| 507 | attributes_topic_buffer: &mut resources.attributes_topic_buffer, | ||
| 459 | } | 508 | } |
| 460 | } | 509 | } |
| 461 | 510 | ||
| @@ -589,6 +638,25 @@ pub fn create_binary_sensor<'a>( | |||
| 589 | BinarySensor::new(entity) | 638 | BinarySensor::new(entity) |
| 590 | } | 639 | } |
| 591 | 640 | ||
| 641 | pub fn create_device_tracker<'a>( | ||
| 642 | device: &Device<'a>, | ||
| 643 | id: &'static str, | ||
| 644 | config: DeviceTrackerConfig, | ||
| 645 | ) -> DeviceTracker<'a> { | ||
| 646 | let mut entity_config = EntityConfig { | ||
| 647 | id, | ||
| 648 | ..Default::default() | ||
| 649 | }; | ||
| 650 | config.populate(&mut entity_config); | ||
| 651 | |||
| 652 | let entity = create_entity( | ||
| 653 | device, | ||
| 654 | entity_config, | ||
| 655 | EntityStorage::DeviceTracker(Default::default()), | ||
| 656 | ); | ||
| 657 | DeviceTracker::new(entity) | ||
| 658 | } | ||
| 659 | |||
| 592 | pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Result<(), Error> { | 660 | pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Result<(), Error> { |
| 593 | use core::fmt::Write; | 661 | use core::fmt::Write; |
| 594 | 662 | ||
| @@ -647,6 +715,7 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 647 | device.discovery_topic_buffer.clear(); | 715 | device.discovery_topic_buffer.clear(); |
| 648 | device.state_topic_buffer.clear(); | 716 | device.state_topic_buffer.clear(); |
| 649 | device.command_topic_buffer.clear(); | 717 | device.command_topic_buffer.clear(); |
| 718 | device.attributes_topic_buffer.clear(); | ||
| 650 | 719 | ||
| 651 | // borrow the entity and fill out the buffers to be sent | 720 | // borrow the entity and fill out the buffers to be sent |
| 652 | // this should be done inside a block so that we do not hold the RefMut across an | 721 | // this should be done inside a block so that we do not hold the RefMut across an |
| @@ -672,6 +741,10 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 672 | device_id: device.config.device_id, | 741 | device_id: device.config.device_id, |
| 673 | entity_id: entity_config.id, | 742 | entity_id: entity_config.id, |
| 674 | }; | 743 | }; |
| 744 | let attributes_topic_display = AttributesTopicDisplay { | ||
| 745 | device_id: device.config.device_id, | ||
| 746 | entity_id: entity_config.id, | ||
| 747 | }; | ||
| 675 | 748 | ||
| 676 | write!(device.discovery_topic_buffer, "{discovery_topic_display}") | 749 | write!(device.discovery_topic_buffer, "{discovery_topic_display}") |
| 677 | .expect("discovery topic buffer too small"); | 750 | .expect("discovery topic buffer too small"); |
| @@ -679,6 +752,8 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 679 | .expect("state topic buffer too small"); | 752 | .expect("state topic buffer too small"); |
| 680 | write!(device.command_topic_buffer, "{command_topic_display}") | 753 | write!(device.command_topic_buffer, "{command_topic_display}") |
| 681 | .expect("command topic buffer too small"); | 754 | .expect("command topic buffer too small"); |
| 755 | write!(device.attributes_topic_buffer, "{attributes_topic_display}") | ||
| 756 | .expect("attributes topic buffer too small"); | ||
| 682 | 757 | ||
| 683 | let discovery = EntityDiscovery { | 758 | let discovery = EntityDiscovery { |
| 684 | id: entity_config.id, | 759 | id: entity_config.id, |
| @@ -686,8 +761,10 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 686 | device_class: entity_config.device_class, | 761 | device_class: entity_config.device_class, |
| 687 | state_topic: Some(device.state_topic_buffer.as_str()), | 762 | state_topic: Some(device.state_topic_buffer.as_str()), |
| 688 | command_topic: Some(device.command_topic_buffer.as_str()), | 763 | command_topic: Some(device.command_topic_buffer.as_str()), |
| 764 | json_attributes_topic: Some(device.attributes_topic_buffer.as_str()), | ||
| 689 | unit_of_measurement: entity_config.measurement_unit, | 765 | unit_of_measurement: entity_config.measurement_unit, |
| 690 | schema: entity_config.schema, | 766 | schema: entity_config.schema, |
| 767 | platform: entity_config.platform, | ||
| 691 | state_class: entity_config.state_class, | 768 | state_class: entity_config.state_class, |
| 692 | icon: entity_config.icon, | 769 | icon: entity_config.icon, |
| 693 | entity_picture: entity_config.picture, | 770 | entity_picture: entity_config.picture, |
| @@ -790,7 +867,7 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 790 | use core::fmt::Write; | 867 | use core::fmt::Write; |
| 791 | 868 | ||
| 792 | for entity in device.entities { | 869 | for entity in device.entities { |
| 793 | { | 870 | let publish_topic = { |
| 794 | let mut entity = entity.borrow_mut(); | 871 | let mut entity = entity.borrow_mut(); |
| 795 | let entity = match entity.as_mut() { | 872 | let entity = match entity.as_mut() { |
| 796 | Some(entity) => entity, | 873 | Some(entity) => entity, |
| @@ -804,6 +881,7 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 804 | entity.publish = false; | 881 | entity.publish = false; |
| 805 | device.publish_buffer.clear(); | 882 | device.publish_buffer.clear(); |
| 806 | 883 | ||
| 884 | let mut publish_to_attributes = false; | ||
| 807 | match &entity.storage { | 885 | match &entity.storage { |
| 808 | EntityStorage::Switch(SwitchStorage { | 886 | EntityStorage::Switch(SwitchStorage { |
| 809 | state: Some(SwitchState { value, .. }), | 887 | state: Some(SwitchState { value, .. }), |
| @@ -828,6 +906,19 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 828 | .. | 906 | .. |
| 829 | }) => write!(device.publish_buffer, "{}", value) | 907 | }) => write!(device.publish_buffer, "{}", value) |
| 830 | .expect("publish buffer too small for number state payload"), | 908 | .expect("publish buffer too small for number state payload"), |
| 909 | EntityStorage::DeviceTracker(DeviceTrackerStorage { | ||
| 910 | state: Some(tracker_state), | ||
| 911 | }) => { | ||
| 912 | publish_to_attributes = true; | ||
| 913 | device | ||
| 914 | .publish_buffer | ||
| 915 | .resize(device.publish_buffer.capacity(), 0) | ||
| 916 | .expect("resize to capacity should never fail"); | ||
| 917 | let n = | ||
| 918 | serde_json_core::to_slice(&tracker_state, &mut device.publish_buffer) | ||
| 919 | .expect("publish buffer too small for tracker state payload"); | ||
| 920 | device.publish_buffer.truncate(n); | ||
| 921 | } | ||
| 831 | _ => { | 922 | _ => { |
| 832 | crate::log::warn!( | 923 | crate::log::warn!( |
| 833 | "entity '{}' requested state publish but its storage does not support it", | 924 | "entity '{}' requested state publish but its storage does not support it", |
| @@ -837,19 +928,30 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 837 | } | 928 | } |
| 838 | } | 929 | } |
| 839 | 930 | ||
| 840 | let state_topic_display = StateTopicDisplay { | 931 | if publish_to_attributes { |
| 841 | device_id: device.config.device_id, | 932 | let attributes_topic_display = AttributesTopicDisplay { |
| 842 | entity_id: entity.config.id, | 933 | device_id: device.config.device_id, |
| 843 | }; | 934 | entity_id: entity.config.id, |
| 844 | device.state_topic_buffer.clear(); | 935 | }; |
| 845 | write!(device.state_topic_buffer, "{state_topic_display}") | 936 | device.attributes_topic_buffer.clear(); |
| 846 | .expect("state topic buffer too small"); | 937 | write!(device.attributes_topic_buffer, "{attributes_topic_display}") |
| 847 | } | 938 | .expect("attributes topic buffer too small"); |
| 939 | device.attributes_topic_buffer.as_str() | ||
| 940 | } else { | ||
| 941 | let state_topic_display = StateTopicDisplay { | ||
| 942 | device_id: device.config.device_id, | ||
| 943 | entity_id: entity.config.id, | ||
| 944 | }; | ||
| 945 | device.state_topic_buffer.clear(); | ||
| 946 | write!(device.state_topic_buffer, "{state_topic_display}") | ||
| 947 | .expect("state topic buffer too small"); | ||
| 948 | device.state_topic_buffer.as_str() | ||
| 949 | } | ||
| 950 | }; | ||
| 848 | 951 | ||
| 849 | let state_topic = device.state_topic_buffer.as_str(); | ||
| 850 | match embassy_time::with_timeout( | 952 | match embassy_time::with_timeout( |
| 851 | MQTT_TIMEOUT, | 953 | MQTT_TIMEOUT, |
| 852 | client.publish(state_topic, device.publish_buffer), | 954 | client.publish(publish_topic, device.publish_buffer), |
| 853 | ) | 955 | ) |
| 854 | .await | 956 | .await |
| 855 | { | 957 | { |
| @@ -857,13 +959,13 @@ pub async fn run<T: Transport>(device: &mut Device<'_>, transport: &mut T) -> Re | |||
| 857 | Ok(Err(err)) => { | 959 | Ok(Err(err)) => { |
| 858 | crate::log::error!( | 960 | crate::log::error!( |
| 859 | "mqtt state publish on topic '{}' failed with: {:?}", | 961 | "mqtt state publish on topic '{}' failed with: {:?}", |
| 860 | state_topic, | 962 | publish_topic, |
| 861 | crate::log::Debug2Format(&err) | 963 | crate::log::Debug2Format(&err) |
| 862 | ); | 964 | ); |
| 863 | return Err(Error::new("mqtt publish failed")); | 965 | return Err(Error::new("mqtt publish failed")); |
| 864 | } | 966 | } |
| 865 | Err(_) => { | 967 | Err(_) => { |
| 866 | crate::log::error!("mqtt state publish on topic '{}' timed out", state_topic); | 968 | crate::log::error!("mqtt state publish on topic '{}' timed out", publish_topic); |
| 867 | return Err(Error::new("mqtt publish timed out")); | 969 | return Err(Error::new("mqtt publish timed out")); |
| 868 | } | 970 | } |
| 869 | } | 971 | } |
