aboutsummaryrefslogtreecommitdiff
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
parentb609a315e7921dcc712da6955890f4dc7c2c4b9f (diff)
reworked logging
-rw-r--r--Cargo.lock125
-rw-r--r--Cargo.toml13
-rw-r--r--examples/binary_sensor.rs2
-rw-r--r--examples/button.rs2
-rw-r--r--examples/common/mod.rs8
-rw-r--r--examples/common/std_async_tcp.rs10
-rw-r--r--examples/switch.rs3
-rw-r--r--src/lib.rs62
-rw-r--r--src/log.rs115
9 files changed, 307 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5bc99f3..d38b2c7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -165,6 +165,8 @@ dependencies = [
165 "serde", 165 "serde",
166 "serde-json-core", 166 "serde-json-core",
167 "static_cell", 167 "static_cell",
168 "tracing",
169 "tracing-subscriber",
168] 170]
169 171
170[[package]] 172[[package]]
@@ -381,6 +383,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 383checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
382 384
383[[package]] 385[[package]]
386name = "lazy_static"
387version = "1.5.0"
388source = "registry+https://github.com/rust-lang/crates.io-index"
389checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
390
391[[package]]
384name = "libc" 392name = "libc"
385version = "0.2.178" 393version = "0.2.178"
386source = "registry+https://github.com/rust-lang/crates.io-index" 394source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -393,6 +401,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
393checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" 401checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
394 402
395[[package]] 403[[package]]
404name = "log"
405version = "0.4.29"
406source = "registry+https://github.com/rust-lang/crates.io-index"
407checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
408
409[[package]]
396name = "managed" 410name = "managed"
397version = "0.8.0" 411version = "0.8.0"
398source = "registry+https://github.com/rust-lang/crates.io-index" 412source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -414,6 +428,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
414checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 428checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
415 429
416[[package]] 430[[package]]
431name = "nu-ansi-term"
432version = "0.50.3"
433source = "registry+https://github.com/rust-lang/crates.io-index"
434checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
435dependencies = [
436 "windows-sys",
437]
438
439[[package]]
440name = "once_cell"
441version = "1.21.3"
442source = "registry+https://github.com/rust-lang/crates.io-index"
443checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
444
445[[package]]
446name = "pin-project-lite"
447version = "0.2.16"
448source = "registry+https://github.com/rust-lang/crates.io-index"
449checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
450
451[[package]]
417name = "portable-atomic" 452name = "portable-atomic"
418version = "1.11.1" 453version = "1.11.1"
419source = "registry+https://github.com/rust-lang/crates.io-index" 454source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -551,6 +586,21 @@ dependencies = [
551] 586]
552 587
553[[package]] 588[[package]]
589name = "sharded-slab"
590version = "0.1.7"
591source = "registry+https://github.com/rust-lang/crates.io-index"
592checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
593dependencies = [
594 "lazy_static",
595]
596
597[[package]]
598name = "smallvec"
599version = "1.15.1"
600source = "registry+https://github.com/rust-lang/crates.io-index"
601checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
602
603[[package]]
554name = "smoltcp" 604name = "smoltcp"
555version = "0.12.0" 605version = "0.12.0"
556source = "registry+https://github.com/rust-lang/crates.io-index" 606source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -617,12 +667,72 @@ dependencies = [
617] 667]
618 668
619[[package]] 669[[package]]
670name = "thread_local"
671version = "1.1.9"
672source = "registry+https://github.com/rust-lang/crates.io-index"
673checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
674dependencies = [
675 "cfg-if",
676]
677
678[[package]]
679name = "tracing"
680version = "0.1.43"
681source = "registry+https://github.com/rust-lang/crates.io-index"
682checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
683dependencies = [
684 "pin-project-lite",
685 "tracing-core",
686]
687
688[[package]]
689name = "tracing-core"
690version = "0.1.35"
691source = "registry+https://github.com/rust-lang/crates.io-index"
692checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
693dependencies = [
694 "once_cell",
695 "valuable",
696]
697
698[[package]]
699name = "tracing-log"
700version = "0.2.0"
701source = "registry+https://github.com/rust-lang/crates.io-index"
702checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
703dependencies = [
704 "log",
705 "once_cell",
706 "tracing-core",
707]
708
709[[package]]
710name = "tracing-subscriber"
711version = "0.3.22"
712source = "registry+https://github.com/rust-lang/crates.io-index"
713checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
714dependencies = [
715 "nu-ansi-term",
716 "sharded-slab",
717 "smallvec",
718 "thread_local",
719 "tracing-core",
720 "tracing-log",
721]
722
723[[package]]
620name = "unicode-ident" 724name = "unicode-ident"
621version = "1.0.22" 725version = "1.0.22"
622source = "registry+https://github.com/rust-lang/crates.io-index" 726source = "registry+https://github.com/rust-lang/crates.io-index"
623checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 727checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
624 728
625[[package]] 729[[package]]
730name = "valuable"
731version = "0.1.1"
732source = "registry+https://github.com/rust-lang/crates.io-index"
733checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
734
735[[package]]
626name = "void" 736name = "void"
627version = "1.0.2" 737version = "1.0.2"
628source = "registry+https://github.com/rust-lang/crates.io-index" 738source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -638,6 +748,21 @@ dependencies = [
638] 748]
639 749
640[[package]] 750[[package]]
751name = "windows-link"
752version = "0.2.1"
753source = "registry+https://github.com/rust-lang/crates.io-index"
754checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
755
756[[package]]
757name = "windows-sys"
758version = "0.61.2"
759source = "registry+https://github.com/rust-lang/crates.io-index"
760checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
761dependencies = [
762 "windows-link",
763]
764
765[[package]]
641name = "wit-bindgen" 766name = "wit-bindgen"
642version = "0.46.0" 767version = "0.46.0"
643source = "registry+https://github.com/rust-lang/crates.io-index" 768source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 6a9ea4f..38ada69 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,15 +3,21 @@ name = "embassy-ha"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2024" 4edition = "2024"
5 5
6[features]
7default = []
8defmt = ["dep:defmt", "embassy-net/defmt", "embassy-sync/defmt"]
9tracing = ["dep:tracing"]
10
6[dependencies] 11[dependencies]
7embedded-mqtt = { path = "./embedded-mqtt" , features = ["embassy-net"] } 12embedded-mqtt = { path = "./embedded-mqtt" , features = ["embassy-net"] }
8embassy-net = { version = "0.7.1", features = ["defmt", "medium-ip", "proto-ipv4", "tcp"] } 13embassy-net = { version = "0.7.1", features = ["medium-ip", "proto-ipv4", "tcp"] }
9heapless = "0.9.2" 14heapless = "0.9.2"
10embassy-time = { version = "0.5.0" } 15embassy-time = { version = "0.5.0" }
11serde-json-core = "0.6.0" 16serde-json-core = "0.6.0"
12serde = { version = "1.0.228", default-features = false, features = ["derive"] } 17serde = { version = "1.0.228", default-features = false, features = ["derive"] }
13defmt = "1.0.1" 18defmt = { version = "1.0.1", optional = true }
14embassy-sync = { version = "0.7.2", features = ["defmt"] } 19tracing = { version = "0.1", optional = true, default-features = false }
20embassy-sync = { version = "0.7.2" }
15embassy-futures = "0.1.2" 21embassy-futures = "0.1.2"
16embedded-io-async = "0.6" 22embedded-io-async = "0.6"
17 23
@@ -23,3 +29,4 @@ static_cell = "2.1.1"
23embedded-io-async = { version = "0.6", features = ["std"] } 29embedded-io-async = { version = "0.6", features = ["std"] }
24critical-section = { version = "1", features = ["std"] } 30critical-section = { version = "1", features = ["std"] }
25rand = "0.9.2" 31rand = "0.9.2"
32tracing-subscriber = "0.3"
diff --git a/examples/binary_sensor.rs b/examples/binary_sensor.rs
index 7363a6c..27cfdb5 100644
--- a/examples/binary_sensor.rs
+++ b/examples/binary_sensor.rs
@@ -41,7 +41,7 @@ async fn main_task(spawner: Spawner) {
41async fn binary_sensor_class(mut switch: embassy_ha::BinarySensor<'static>) { 41async fn binary_sensor_class(mut switch: embassy_ha::BinarySensor<'static>) {
42 loop { 42 loop {
43 let state = switch.toggle(); 43 let state = switch.toggle();
44 println!("state = {}", state); 44 tracing::info!("state = {}", state);
45 Timer::after_secs(2).await; 45 Timer::after_secs(2).await;
46 } 46 }
47} 47}
diff --git a/examples/button.rs b/examples/button.rs
index d1d2159..e3d7086 100644
--- a/examples/button.rs
+++ b/examples/button.rs
@@ -31,7 +31,7 @@ async fn main_task(spawner: Spawner) {
31async fn button_task(mut button: embassy_ha::Button<'static>) { 31async fn button_task(mut button: embassy_ha::Button<'static>) {
32 loop { 32 loop {
33 button.pressed().await; 33 button.pressed().await;
34 println!("The button has been pressed"); 34 tracing::info!("The button has been pressed");
35 } 35 }
36} 36}
37 37
diff --git a/examples/common/mod.rs b/examples/common/mod.rs
index fd61e9d..db97463 100644
--- a/examples/common/mod.rs
+++ b/examples/common/mod.rs
@@ -9,6 +9,14 @@ pub static EXECUTOR: StaticCell<Executor> = StaticCell::new();
9macro_rules! example_main { 9macro_rules! example_main {
10 () => { 10 () => {
11 fn main() { 11 fn main() {
12 // Initialize tracing if tracing feature is enabled
13 #[cfg(feature = "tracing")]
14 {
15 tracing_subscriber::fmt()
16 .with_max_level(tracing::Level::DEBUG)
17 .init();
18 }
19
12 let executor = common::EXECUTOR.init(Executor::new()); 20 let executor = common::EXECUTOR.init(Executor::new());
13 executor.run(|spawner| { 21 executor.run(|spawner| {
14 spawner.must_spawn(main_task(spawner)); 22 spawner.must_spawn(main_task(spawner));
diff --git a/examples/common/std_async_tcp.rs b/examples/common/std_async_tcp.rs
index bd97fa9..10a9fd8 100644
--- a/examples/common/std_async_tcp.rs
+++ b/examples/common/std_async_tcp.rs
@@ -1,4 +1,5 @@
1use std::{ 1use std::{
2 future::Future,
2 io::{Read, Write}, 3 io::{Read, Write},
3 net::{TcpStream, ToSocketAddrs}, 4 net::{TcpStream, ToSocketAddrs},
4 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
@@ -16,7 +17,9 @@ pub struct AsyncTcp {
16 17
17impl AsyncTcp { 18impl AsyncTcp {
18 pub fn connect(addr: impl ToSocketAddrs) -> Self { 19 pub fn connect(addr: impl ToSocketAddrs) -> Self {
20 tracing::info!("Connecting to TCP server");
19 let stream = TcpStream::connect(addr).expect("failed to connect to remote"); 21 let stream = TcpStream::connect(addr).expect("failed to connect to remote");
22 tracing::info!("TCP connection established");
20 let mut read_stream = stream.try_clone().unwrap(); 23 let mut read_stream = stream.try_clone().unwrap();
21 let mut write_stream = stream; 24 let mut write_stream = stream;
22 25
@@ -34,9 +37,10 @@ impl AsyncTcp {
34 std::mem::take(&mut *buffer) 37 std::mem::take(&mut *buffer)
35 }; 38 };
36 if !buffer.is_empty() { 39 if !buffer.is_empty() {
37 println!("writing {} bytes", buffer.len()); 40 let len = buffer.len();
38 write_stream.write_all(&buffer).unwrap(); 41 write_stream.write_all(&buffer).unwrap();
39 write_stream.flush().unwrap(); 42 write_stream.flush().unwrap();
43 tracing::debug!("Wrote {} bytes to TCP stream", len);
40 } else { 44 } else {
41 std::thread::park(); 45 std::thread::park();
42 } 46 }
@@ -52,9 +56,11 @@ impl AsyncTcp {
52 loop { 56 loop {
53 let n = read_stream.read(&mut scratch).unwrap(); 57 let n = read_stream.read(&mut scratch).unwrap();
54 if n == 0 { 58 if n == 0 {
59 tracing::warn!("TCP stream closed (EOF)");
55 panic!("EOF"); 60 panic!("EOF");
56 } 61 }
57 62
63 tracing::debug!("Read {} bytes from TCP stream", n);
58 { 64 {
59 let mut buffer = read_buffer.lock().unwrap(); 65 let mut buffer = read_buffer.lock().unwrap();
60 buffer.extend_from_slice(&scratch[..n]); 66 buffer.extend_from_slice(&scratch[..n]);
@@ -79,6 +85,7 @@ impl embedded_io_async::ErrorType for AsyncTcp {
79 85
80impl embedded_io_async::Write for AsyncTcp { 86impl embedded_io_async::Write for AsyncTcp {
81 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 87 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
88 tracing::trace!("Queueing {} bytes for write", buf.len());
82 { 89 {
83 let mut buffer = self.write_buffer.lock().unwrap(); 90 let mut buffer = self.write_buffer.lock().unwrap();
84 buffer.extend_from_slice(buf); 91 buffer.extend_from_slice(buf);
@@ -116,6 +123,7 @@ impl embedded_io_async::Read for AsyncTcp {
116 let copy_n = buf.len().min(buffer.len()); 123 let copy_n = buf.len().min(buffer.len());
117 buf[..copy_n].copy_from_slice(&buffer[..copy_n]); 124 buf[..copy_n].copy_from_slice(&buffer[..copy_n]);
118 buffer.drain(..copy_n); 125 buffer.drain(..copy_n);
126 tracing::trace!("Async read returned {} bytes", copy_n);
119 return Ok(copy_n); 127 return Ok(copy_n);
120 } 128 }
121 } 129 }
diff --git a/examples/switch.rs b/examples/switch.rs
index 0571758..67767d7 100644
--- a/examples/switch.rs
+++ b/examples/switch.rs
@@ -41,8 +41,7 @@ async fn main_task(spawner: Spawner) {
41async fn switch_task(mut switch: embassy_ha::Switch<'static>) { 41async fn switch_task(mut switch: embassy_ha::Switch<'static>) {
42 loop { 42 loop {
43 let state = switch.wait().await; 43 let state = switch.wait().await;
44 44 tracing::info!("state = {}", state);
45 println!("state = {}", state);
46 Timer::after_secs(1).await; 45 Timer::after_secs(1).await;
47 } 46 }
48} 47}
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};