aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-12-06 14:45:01 +0000
committerdiogo464 <[email protected]>2025-12-06 14:45:01 +0000
commit809b1b795ed530d20ceb6f3cb42af70daa7eadf9 (patch)
treef8fca20a59a6fb57fe898b6c43d525450e8c395a /src
parente5416e848c8caeed59e8aca6fb3e191fbc621b1b (diff)
Complete error handling for availability publish
Added proper error logging and return for availability publish failure, following the same pattern as other MQTT operations in the codebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs71
1 files changed, 63 insertions, 8 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 0107116..3353663 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,6 +44,9 @@ pub use transport::Transport;
44mod unit; 44mod unit;
45pub use unit::*; 45pub use unit::*;
46 46
47const AVAILABLE_PAYLOAD: &str = "online";
48const NOT_AVAILABLE_PAYLOAD: &str = "offline";
49
47#[derive(Debug)] 50#[derive(Debug)]
48pub struct Error(&'static str); 51pub struct Error(&'static str);
49 52
@@ -118,6 +121,15 @@ struct EntityDiscovery<'a> {
118 #[serde(skip_serializing_if = "Option::is_none")] 121 #[serde(skip_serializing_if = "Option::is_none")]
119 suggested_display_precision: Option<u8>, 122 suggested_display_precision: Option<u8>,
120 123
124 #[serde(skip_serializing_if = "Option::is_none")]
125 availability_topic: Option<&'a str>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
128 payload_available: Option<&'a str>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
131 payload_not_available: Option<&'a str>,
132
121 device: &'a DeviceDiscovery<'a>, 133 device: &'a DeviceDiscovery<'a>,
122} 134}
123 135
@@ -163,6 +175,16 @@ impl<'a> core::fmt::Display for CommandTopicDisplay<'a> {
163 } 175 }
164} 176}
165 177
178struct DeviceAvailabilityTopic<'a> {
179 device_id: &'a str,
180}
181
182impl<'a> core::fmt::Display for DeviceAvailabilityTopic<'a> {
183 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184 write!(f, "embassy-ha/{}/availability", self.device_id)
185 }
186}
187
166pub struct DeviceConfig { 188pub struct DeviceConfig {
167 pub device_id: &'static str, 189 pub device_id: &'static str,
168 pub device_name: &'static str, 190 pub device_name: &'static str,
@@ -178,6 +200,7 @@ pub struct DeviceResources {
178 publish_buffer: Vec<u8, 2048>, 200 publish_buffer: Vec<u8, 2048>,
179 subscribe_buffer: Vec<u8, 128>, 201 subscribe_buffer: Vec<u8, 128>,
180 discovery_buffer: Vec<u8, 2048>, 202 discovery_buffer: Vec<u8, 2048>,
203 availability_topic_buffer: String<128>,
181 discovery_topic_buffer: String<128>, 204 discovery_topic_buffer: String<128>,
182 state_topic_buffer: String<128>, 205 state_topic_buffer: String<128>,
183 command_topic_buffer: String<128>, 206 command_topic_buffer: String<128>,
@@ -197,6 +220,7 @@ impl Default for DeviceResources {
197 publish_buffer: Default::default(), 220 publish_buffer: Default::default(),
198 subscribe_buffer: Default::default(), 221 subscribe_buffer: Default::default(),
199 discovery_buffer: Default::default(), 222 discovery_buffer: Default::default(),
223 availability_topic_buffer: Default::default(),
200 discovery_topic_buffer: Default::default(), 224 discovery_topic_buffer: Default::default(),
201 state_topic_buffer: Default::default(), 225 state_topic_buffer: Default::default(),
202 command_topic_buffer: Default::default(), 226 command_topic_buffer: Default::default(),
@@ -383,6 +407,7 @@ pub struct Device<'a> {
383 publish_buffer: &'a mut VecView<u8>, 407 publish_buffer: &'a mut VecView<u8>,
384 subscribe_buffer: &'a mut VecView<u8>, 408 subscribe_buffer: &'a mut VecView<u8>,
385 discovery_buffer: &'a mut VecView<u8>, 409 discovery_buffer: &'a mut VecView<u8>,
410 availability_topic_buffer: &'a mut StringView,
386 discovery_topic_buffer: &'a mut StringView, 411 discovery_topic_buffer: &'a mut StringView,
387 state_topic_buffer: &'a mut StringView, 412 state_topic_buffer: &'a mut StringView,
388 command_topic_buffer: &'a mut StringView, 413 command_topic_buffer: &'a mut StringView,
@@ -399,6 +424,7 @@ impl<'a> Device<'a> {
399 publish_buffer: &mut resources.publish_buffer, 424 publish_buffer: &mut resources.publish_buffer,
400 subscribe_buffer: &mut resources.subscribe_buffer, 425 subscribe_buffer: &mut resources.subscribe_buffer,
401 discovery_buffer: &mut resources.discovery_buffer, 426 discovery_buffer: &mut resources.discovery_buffer,
427 availability_topic_buffer: &mut resources.availability_topic_buffer,
402 discovery_topic_buffer: &mut resources.discovery_topic_buffer, 428 discovery_topic_buffer: &mut resources.discovery_topic_buffer,
403 state_topic_buffer: &mut resources.state_topic_buffer, 429 state_topic_buffer: &mut resources.state_topic_buffer,
404 command_topic_buffer: &mut resources.command_topic_buffer, 430 command_topic_buffer: &mut resources.command_topic_buffer,
@@ -430,11 +456,7 @@ impl<'a> Device<'a> {
430 } 456 }
431 } 457 }
432 458
433 pub fn create_sensor( 459 pub fn create_sensor(&self, id: &'static str, config: SensorConfig) -> Sensor<'a> {
434 &self,
435 id: &'static str,
436 config: SensorConfig,
437 ) -> Sensor<'a> {
438 let mut entity_config = EntityConfig::default(); 460 let mut entity_config = EntityConfig::default();
439 entity_config.id = id; 461 entity_config.id = id;
440 config.populate(&mut entity_config); 462 config.populate(&mut entity_config);
@@ -502,8 +524,29 @@ impl<'a> Device<'a> {
502 } 524 }
503 525
504 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> Result<(), Error> { 526 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> Result<(), Error> {
527 use core::fmt::Write;
528
529 self.availability_topic_buffer.clear();
530 write!(
531 self.availability_topic_buffer,
532 "{}",
533 DeviceAvailabilityTopic {
534 device_id: self.config.device_id
535 }
536 )
537 .expect("device availability buffer too small");
538 let availability_topic = self.availability_topic_buffer.as_str();
539
505 let mut client = embedded_mqtt::Client::new(self.mqtt_resources, transport); 540 let mut client = embedded_mqtt::Client::new(self.mqtt_resources, transport);
506 if let Err(err) = client.connect(self.config.device_id).await { 541 let connect_params = embedded_mqtt::ConnectParams {
542 will_topic: Some(availability_topic),
543 will_payload: Some(NOT_AVAILABLE_PAYLOAD.as_bytes()),
544 ..Default::default()
545 };
546 if let Err(err) = client
547 .connect_with(self.config.device_id, connect_params)
548 .await
549 {
507 crate::log::error!( 550 crate::log::error!(
508 "mqtt connect failed with: {:?}", 551 "mqtt connect failed with: {:?}",
509 crate::log::Debug2Format(&err) 552 crate::log::Debug2Format(&err)
@@ -520,8 +563,6 @@ impl<'a> Device<'a> {
520 }; 563 };
521 564
522 for entity in self.entities { 565 for entity in self.entities {
523 use core::fmt::Write;
524
525 self.publish_buffer.clear(); 566 self.publish_buffer.clear();
526 self.subscribe_buffer.clear(); 567 self.subscribe_buffer.clear();
527 self.discovery_buffer.clear(); 568 self.discovery_buffer.clear();
@@ -577,6 +618,9 @@ impl<'a> Device<'a> {
577 step: entity_config.step, 618 step: entity_config.step,
578 mode: entity_config.mode, 619 mode: entity_config.mode,
579 suggested_display_precision: entity_config.suggested_display_precision, 620 suggested_display_precision: entity_config.suggested_display_precision,
621 availability_topic: Some(availability_topic),
622 payload_available: Some(AVAILABLE_PAYLOAD),
623 payload_not_available: Some(NOT_AVAILABLE_PAYLOAD),
580 device: &device_discovery, 624 device: &device_discovery,
581 }; 625 };
582 crate::log::debug!( 626 crate::log::debug!(
@@ -620,6 +664,17 @@ impl<'a> Device<'a> {
620 } 664 }
621 } 665 }
622 666
667 if let Err(err) = client
668 .publish(availability_topic, AVAILABLE_PAYLOAD.as_bytes())
669 .await
670 {
671 crate::log::error!(
672 "mqtt availability publish failed with: {:?}",
673 crate::log::Debug2Format(&err)
674 );
675 return Err(Error::new("mqtt availability publish failed"));
676 }
677
623 'outer_loop: loop { 678 'outer_loop: loop {
624 use core::fmt::Write; 679 use core::fmt::Write;
625 680