aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-12-05 19:29:52 +0000
committerdiogo464 <[email protected]>2025-12-05 19:29:52 +0000
commit3a3d635adddf5cb16a93827e688e061613a083d7 (patch)
treee6be0a075f80d5cfb11b1882e0086727bfe699e9 /src
parentb609a315e7921dcc712da6955890f4dc7c2c4b9f (diff)
reworked logging
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs62
-rw-r--r--src/log.rs115
2 files changed, 152 insertions, 25 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 036ae12..3ebb190 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,6 @@
2 2
3use core::{cell::RefCell, task::Waker}; 3use core::{cell::RefCell, task::Waker};
4 4
5use defmt::Format;
6use embassy_sync::waitqueue::AtomicWaker; 5use embassy_sync::waitqueue::AtomicWaker;
7use heapless::{ 6use heapless::{
8 Vec, VecView, 7 Vec, VecView,
@@ -10,6 +9,9 @@ use heapless::{
10}; 9};
11use serde::Serialize; 10use serde::Serialize;
12 11
12pub mod log;
13pub use log::Format;
14
13pub mod constants; 15pub mod constants;
14 16
15mod binary_state; 17mod binary_state;
@@ -59,7 +61,8 @@ impl Error {
59 } 61 }
60} 62}
61 63
62#[derive(Debug, Format, Clone, Copy, Serialize)] 64#[derive(Debug, Clone, Copy, Serialize)]
65#[cfg_attr(feature = "defmt", derive(Format))]
63struct DeviceDiscovery<'a> { 66struct DeviceDiscovery<'a> {
64 identifiers: &'a [&'a str], 67 identifiers: &'a [&'a str],
65 name: &'a str, 68 name: &'a str,
@@ -67,7 +70,8 @@ struct DeviceDiscovery<'a> {
67 model: &'a str, 70 model: &'a str,
68} 71}
69 72
70#[derive(Debug, Format, Serialize)] 73#[derive(Debug, Serialize)]
74#[cfg_attr(feature = "defmt", derive(Format))]
71struct EntityDiscovery<'a> { 75struct EntityDiscovery<'a> {
72 #[serde(rename = "unique_id")] 76 #[serde(rename = "unique_id")]
73 id: &'a str, 77 id: &'a str,
@@ -490,11 +494,11 @@ impl<'a> Device<'a> {
490 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> Result<(), Error> { 494 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> Result<(), Error> {
491 let mut client = embedded_mqtt::Client::new(self.mqtt_resources, transport); 495 let mut client = embedded_mqtt::Client::new(self.mqtt_resources, transport);
492 if let Err(err) = client.connect(self.config.device_id).await { 496 if let Err(err) = client.connect(self.config.device_id).await {
493 defmt::error!("mqtt connect failed with: {:?}", defmt::Debug2Format(&err)); 497 crate::log::error!("mqtt connect failed with: {:?}", crate::log::Debug2Format(&err));
494 return Err(Error::new("mqtt connection failed")); 498 return Err(Error::new("mqtt connection failed"));
495 } 499 }
496 500
497 defmt::debug!("sending discover messages"); 501 crate::log::debug!("sending discover messages");
498 let device_discovery = DeviceDiscovery { 502 let device_discovery = DeviceDiscovery {
499 identifiers: &[self.config.device_id], 503 identifiers: &[self.config.device_id],
500 name: self.config.device_name, 504 name: self.config.device_name,
@@ -561,7 +565,7 @@ impl<'a> Device<'a> {
561 mode: entity_config.mode, 565 mode: entity_config.mode,
562 device: &device_discovery, 566 device: &device_discovery,
563 }; 567 };
564 defmt::debug!("discovery for entity '{}': {}", entity_config.id, discovery); 568 crate::log::debug!("discovery for entity '{}': {:?}", entity_config.id, discovery);
565 569
566 self.discovery_buffer 570 self.discovery_buffer
567 .resize(self.discovery_buffer.capacity(), 0) 571 .resize(self.discovery_buffer.capacity(), 0)
@@ -572,25 +576,25 @@ impl<'a> Device<'a> {
572 } 576 }
573 577
574 let discovery_topic = self.discovery_topic_buffer.as_str(); 578 let discovery_topic = self.discovery_topic_buffer.as_str();
575 defmt::debug!("sending discovery to topic '{}'", discovery_topic); 579 crate::log::debug!("sending discovery to topic '{}'", discovery_topic);
576 if let Err(err) = client 580 if let Err(err) = client
577 .publish(discovery_topic, &self.discovery_buffer) 581 .publish(discovery_topic, &self.discovery_buffer)
578 .await 582 .await
579 { 583 {
580 defmt::error!( 584 crate::log::error!(
581 "mqtt discovery publish failed with: {:?}", 585 "mqtt discovery publish failed with: {:?}",
582 defmt::Debug2Format(&err) 586 crate::log::Debug2Format(&err)
583 ); 587 );
584 return Err(Error::new("mqtt discovery publish failed")); 588 return Err(Error::new("mqtt discovery publish failed"));
585 } 589 }
586 590
587 let command_topic = self.command_topic_buffer.as_str(); 591 let command_topic = self.command_topic_buffer.as_str();
588 defmt::debug!("subscribing to command topic '{}'", command_topic); 592 crate::log::debug!("subscribing to command topic '{}'", command_topic);
589 if let Err(err) = client.subscribe(command_topic).await { 593 if let Err(err) = client.subscribe(command_topic).await {
590 defmt::error!( 594 crate::log::error!(
591 "mqtt subscribe to '{}' failed with: {:?}", 595 "mqtt subscribe to '{}' failed with: {:?}",
592 command_topic, 596 command_topic,
593 defmt::Debug2Format(&err) 597 crate::log::Debug2Format(&err)
594 ); 598 );
595 return Err(Error::new( 599 return Err(Error::new(
596 "mqtt subscription to entity command topic failed", 600 "mqtt subscription to entity command topic failed",
@@ -641,7 +645,7 @@ impl<'a> Device<'a> {
641 }) => write!(self.publish_buffer, "{}", value) 645 }) => write!(self.publish_buffer, "{}", value)
642 .expect("publish buffer too small for number state payload"), 646 .expect("publish buffer too small for number state payload"),
643 _ => { 647 _ => {
644 defmt::warn!( 648 crate::log::warn!(
645 "entity '{}' requested state publish but its storage does not support it", 649 "entity '{}' requested state publish but its storage does not support it",
646 entity.config.id 650 entity.config.id
647 ); 651 );
@@ -660,10 +664,10 @@ impl<'a> Device<'a> {
660 664
661 let state_topic = self.state_topic_buffer.as_str(); 665 let state_topic = self.state_topic_buffer.as_str();
662 if let Err(err) = client.publish(state_topic, self.publish_buffer).await { 666 if let Err(err) = client.publish(state_topic, self.publish_buffer).await {
663 defmt::error!( 667 crate::log::error!(
664 "mqtt state publish on topic '{}' failed with: {:?}", 668 "mqtt state publish on topic '{}' failed with: {:?}",
665 state_topic, 669 state_topic,
666 defmt::Debug2Format(&err) 670 crate::log::Debug2Format(&err)
667 ); 671 );
668 return Err(Error::new("mqtt publish failed")); 672 return Err(Error::new("mqtt publish failed"));
669 } 673 }
@@ -675,7 +679,7 @@ impl<'a> Device<'a> {
675 embassy_futures::select::Either::First(packet) => match packet { 679 embassy_futures::select::Either::First(packet) => match packet {
676 Ok(embedded_mqtt::Packet::Publish(publish)) => publish, 680 Ok(embedded_mqtt::Packet::Publish(publish)) => publish,
677 Err(err) => { 681 Err(err) => {
678 defmt::error!("mqtt receive failed with: {:?}", defmt::Debug2Format(&err)); 682 crate::log::error!("mqtt receive failed with: {:?}", crate::log::Debug2Format(&err));
679 return Err(Error::new("mqtt receive failed")); 683 return Err(Error::new("mqtt receive failed"));
680 } 684 }
681 _ => continue, 685 _ => continue,
@@ -708,7 +712,7 @@ impl<'a> Device<'a> {
708 712
709 let mut read_buffer = [0u8; 128]; 713 let mut read_buffer = [0u8; 128];
710 if publish.data_len > read_buffer.len() { 714 if publish.data_len > read_buffer.len() {
711 defmt::warn!( 715 crate::log::warn!(
712 "mqtt publish payload on topic {} is too large ({} bytes), ignoring it", 716 "mqtt publish payload on topic {} is too large ({} bytes), ignoring it",
713 publish.topic, 717 publish.topic,
714 publish.data_len 718 publish.data_len
@@ -716,7 +720,7 @@ impl<'a> Device<'a> {
716 continue; 720 continue;
717 } 721 }
718 722
719 defmt::debug!( 723 crate::log::debug!(
720 "mqtt receiving {} bytes of data on topic {}", 724 "mqtt receiving {} bytes of data on topic {}",
721 publish.data_len, 725 publish.data_len,
722 publish.topic 726 publish.topic
@@ -724,9 +728,9 @@ impl<'a> Device<'a> {
724 728
725 let data_len = publish.data_len; 729 let data_len = publish.data_len;
726 if let Err(err) = client.receive_data(&mut read_buffer[..data_len]).await { 730 if let Err(err) = client.receive_data(&mut read_buffer[..data_len]).await {
727 defmt::error!( 731 crate::log::error!(
728 "mqtt receive data failed with: {:?}", 732 "mqtt receive data failed with: {:?}",
729 defmt::Debug2Format(&err) 733 crate::log::Debug2Format(&err)
730 ); 734 );
731 return Err(Error::new("mqtt receive data failed")); 735 return Err(Error::new("mqtt receive data failed"));
732 } 736 }
@@ -734,7 +738,7 @@ impl<'a> Device<'a> {
734 let command = match str::from_utf8(&read_buffer[..data_len]) { 738 let command = match str::from_utf8(&read_buffer[..data_len]) {
735 Ok(command) => command, 739 Ok(command) => command,
736 Err(_) => { 740 Err(_) => {
737 defmt::warn!("mqtt message contained invalid utf-8, ignoring it"); 741 crate::log::warn!("mqtt message contained invalid utf-8, ignoring it");
738 continue; 742 continue;
739 } 743 }
740 }; 744 };
@@ -745,7 +749,7 @@ impl<'a> Device<'a> {
745 match &mut data.storage { 749 match &mut data.storage {
746 EntityStorage::Button(button_storage) => { 750 EntityStorage::Button(button_storage) => {
747 if command != constants::HA_BUTTON_PAYLOAD_PRESS { 751 if command != constants::HA_BUTTON_PAYLOAD_PRESS {
748 defmt::warn!( 752 crate::log::warn!(
749 "button '{}' received unexpected command '{}', expected '{}', ignoring it", 753 "button '{}' received unexpected command '{}', expected '{}', ignoring it",
750 data.config.id, 754 data.config.id,
751 command, 755 command,
@@ -760,7 +764,7 @@ impl<'a> Device<'a> {
760 let command = match command.parse::<BinaryState>() { 764 let command = match command.parse::<BinaryState>() {
761 Ok(command) => command, 765 Ok(command) => command,
762 Err(_) => { 766 Err(_) => {
763 defmt::warn!( 767 crate::log::warn!(
764 "switch '{}' received invalid command '{}', expected 'ON' or 'OFF', ignoring it", 768 "switch '{}' received invalid command '{}', expected 'ON' or 'OFF', ignoring it",
765 data.config.id, 769 data.config.id,
766 command 770 command
@@ -768,16 +772,24 @@ impl<'a> Device<'a> {
768 continue; 772 continue;
769 } 773 }
770 }; 774 };
775 let timestamp = embassy_time::Instant::now();
776 if switch_storage.publish_on_command {
777 data.publish = true;
778 switch_storage.state = Some(SwitchState {
779 value: command,
780 timestamp,
781 });
782 }
771 switch_storage.command = Some(SwitchCommand { 783 switch_storage.command = Some(SwitchCommand {
772 value: command, 784 value: command,
773 timestamp: embassy_time::Instant::now(), 785 timestamp,
774 }); 786 });
775 } 787 }
776 EntityStorage::Number(number_storage) => { 788 EntityStorage::Number(number_storage) => {
777 let command = match command.parse::<f32>() { 789 let command = match command.parse::<f32>() {
778 Ok(command) => command, 790 Ok(command) => command,
779 Err(_) => { 791 Err(_) => {
780 defmt::warn!( 792 crate::log::warn!(
781 "number '{}' received invalid command '{}', expected a valid number, ignoring it", 793 "number '{}' received invalid command '{}', expected a valid number, ignoring it",
782 data.config.id, 794 data.config.id,
783 command 795 command
diff --git a/src/log.rs b/src/log.rs
new file mode 100644
index 0000000..1f7bebd
--- /dev/null
+++ b/src/log.rs
@@ -0,0 +1,115 @@
1//! Logging abstraction that works with both defmt and tracing.
2//!
3//! This module provides logging macros that can use either `defmt` (for embedded targets)
4//! or `tracing` (for desktop/testing) depending on the enabled cargo features.
5//!
6//! ## Features
7//!
8//! - `defmt`: Use defmt for logging
9//! - `tracing`: Use tracing for logging
10//! - Neither: Logging is compiled out (no-op)
11//!
12//! ## Usage
13//!
14//! ```rust,ignore
15//! use crate::log::{trace, debug, info, warn, error};
16//!
17//! info!("Application started");
18//! debug!("Value: {}", 42);
19//! warn!("Something unexpected: {:?}", some_value);
20//! ```
21
22// Re-export Format trait when using defmt
23#[cfg(feature = "defmt")]
24pub use defmt::Format;
25
26// For tracing or no logging, we provide a stub Format trait
27#[cfg(not(feature = "defmt"))]
28pub trait Format {}
29
30// When using defmt, also provide Debug2Format for std types
31#[cfg(feature = "defmt")]
32pub use defmt::Debug2Format;
33
34// For tracing or no logging, Debug2Format is a passthrough
35#[cfg(not(feature = "defmt"))]
36#[inline]
37pub fn Debug2Format<T>(value: &T) -> &T {
38 value
39}
40
41// Logging macros that dispatch to the appropriate backend or no-op
42// If both features are enabled, defmt takes precedence
43
44#[macro_export]
45macro_rules! trace {
46 ($($arg:tt)*) => {
47 #[cfg(feature = "defmt")]
48 defmt::trace!($($arg)*);
49
50 #[cfg(all(feature = "tracing", not(feature = "defmt")))]
51 tracing::trace!($($arg)*);
52
53 #[cfg(not(any(feature = "defmt", feature = "tracing")))]
54 { let _ = (); } // no-op
55 };
56}
57
58#[macro_export]
59macro_rules! debug {
60 ($($arg:tt)*) => {
61 #[cfg(feature = "defmt")]
62 defmt::debug!($($arg)*);
63
64 #[cfg(all(feature = "tracing", not(feature = "defmt")))]
65 tracing::debug!($($arg)*);
66
67 #[cfg(not(any(feature = "defmt", feature = "tracing")))]
68 { let _ = (); } // no-op
69 };
70}
71
72#[macro_export]
73macro_rules! info {
74 ($($arg:tt)*) => {
75 #[cfg(feature = "defmt")]
76 defmt::info!($($arg)*);
77
78 #[cfg(all(feature = "tracing", not(feature = "defmt")))]
79 tracing::info!($($arg)*);
80
81 #[cfg(not(any(feature = "defmt", feature = "tracing")))]
82 { let _ = (); } // no-op
83 };
84}
85
86#[macro_export]
87macro_rules! warn {
88 ($($arg:tt)*) => {
89 #[cfg(feature = "defmt")]
90 defmt::warn!($($arg)*);
91
92 #[cfg(all(feature = "tracing", not(feature = "defmt")))]
93 tracing::warn!($($arg)*);
94
95 #[cfg(not(any(feature = "defmt", feature = "tracing")))]
96 { let _ = (); } // no-op
97 };
98}
99
100#[macro_export]
101macro_rules! error {
102 ($($arg:tt)*) => {
103 #[cfg(feature = "defmt")]
104 defmt::error!($($arg)*);
105
106 #[cfg(all(feature = "tracing", not(feature = "defmt")))]
107 tracing::error!($($arg)*);
108
109 #[cfg(not(any(feature = "defmt", feature = "tracing")))]
110 { let _ = (); } // no-op
111 };
112}
113
114// Re-export the macros at the module level for easier use
115pub use crate::{trace, debug, info, warn, error};