diff options
| -rw-r--r-- | Cargo.lock | 125 | ||||
| -rw-r--r-- | Cargo.toml | 13 | ||||
| -rw-r--r-- | examples/binary_sensor.rs | 2 | ||||
| -rw-r--r-- | examples/button.rs | 2 | ||||
| -rw-r--r-- | examples/common/mod.rs | 8 | ||||
| -rw-r--r-- | examples/common/std_async_tcp.rs | 10 | ||||
| -rw-r--r-- | examples/switch.rs | 3 | ||||
| -rw-r--r-- | src/lib.rs | 62 | ||||
| -rw-r--r-- | src/log.rs | 115 |
9 files changed, 307 insertions, 33 deletions
| @@ -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" | |||
| 381 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" | 383 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" |
| 382 | 384 | ||
| 383 | [[package]] | 385 | [[package]] |
| 386 | name = "lazy_static" | ||
| 387 | version = "1.5.0" | ||
| 388 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 389 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | ||
| 390 | |||
| 391 | [[package]] | ||
| 384 | name = "libc" | 392 | name = "libc" |
| 385 | version = "0.2.178" | 393 | version = "0.2.178" |
| 386 | source = "registry+https://github.com/rust-lang/crates.io-index" | 394 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -393,6 +401,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 393 | checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" | 401 | checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" |
| 394 | 402 | ||
| 395 | [[package]] | 403 | [[package]] |
| 404 | name = "log" | ||
| 405 | version = "0.4.29" | ||
| 406 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 407 | checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" | ||
| 408 | |||
| 409 | [[package]] | ||
| 396 | name = "managed" | 410 | name = "managed" |
| 397 | version = "0.8.0" | 411 | version = "0.8.0" |
| 398 | source = "registry+https://github.com/rust-lang/crates.io-index" | 412 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -414,6 +428,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 414 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" | 428 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" |
| 415 | 429 | ||
| 416 | [[package]] | 430 | [[package]] |
| 431 | name = "nu-ansi-term" | ||
| 432 | version = "0.50.3" | ||
| 433 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 434 | checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" | ||
| 435 | dependencies = [ | ||
| 436 | "windows-sys", | ||
| 437 | ] | ||
| 438 | |||
| 439 | [[package]] | ||
| 440 | name = "once_cell" | ||
| 441 | version = "1.21.3" | ||
| 442 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 443 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" | ||
| 444 | |||
| 445 | [[package]] | ||
| 446 | name = "pin-project-lite" | ||
| 447 | version = "0.2.16" | ||
| 448 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 449 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" | ||
| 450 | |||
| 451 | [[package]] | ||
| 417 | name = "portable-atomic" | 452 | name = "portable-atomic" |
| 418 | version = "1.11.1" | 453 | version = "1.11.1" |
| 419 | source = "registry+https://github.com/rust-lang/crates.io-index" | 454 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -551,6 +586,21 @@ dependencies = [ | |||
| 551 | ] | 586 | ] |
| 552 | 587 | ||
| 553 | [[package]] | 588 | [[package]] |
| 589 | name = "sharded-slab" | ||
| 590 | version = "0.1.7" | ||
| 591 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 592 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" | ||
| 593 | dependencies = [ | ||
| 594 | "lazy_static", | ||
| 595 | ] | ||
| 596 | |||
| 597 | [[package]] | ||
| 598 | name = "smallvec" | ||
| 599 | version = "1.15.1" | ||
| 600 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 601 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" | ||
| 602 | |||
| 603 | [[package]] | ||
| 554 | name = "smoltcp" | 604 | name = "smoltcp" |
| 555 | version = "0.12.0" | 605 | version = "0.12.0" |
| 556 | source = "registry+https://github.com/rust-lang/crates.io-index" | 606 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -617,12 +667,72 @@ dependencies = [ | |||
| 617 | ] | 667 | ] |
| 618 | 668 | ||
| 619 | [[package]] | 669 | [[package]] |
| 670 | name = "thread_local" | ||
| 671 | version = "1.1.9" | ||
| 672 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 673 | checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" | ||
| 674 | dependencies = [ | ||
| 675 | "cfg-if", | ||
| 676 | ] | ||
| 677 | |||
| 678 | [[package]] | ||
| 679 | name = "tracing" | ||
| 680 | version = "0.1.43" | ||
| 681 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 682 | checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" | ||
| 683 | dependencies = [ | ||
| 684 | "pin-project-lite", | ||
| 685 | "tracing-core", | ||
| 686 | ] | ||
| 687 | |||
| 688 | [[package]] | ||
| 689 | name = "tracing-core" | ||
| 690 | version = "0.1.35" | ||
| 691 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 692 | checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" | ||
| 693 | dependencies = [ | ||
| 694 | "once_cell", | ||
| 695 | "valuable", | ||
| 696 | ] | ||
| 697 | |||
| 698 | [[package]] | ||
| 699 | name = "tracing-log" | ||
| 700 | version = "0.2.0" | ||
| 701 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 702 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" | ||
| 703 | dependencies = [ | ||
| 704 | "log", | ||
| 705 | "once_cell", | ||
| 706 | "tracing-core", | ||
| 707 | ] | ||
| 708 | |||
| 709 | [[package]] | ||
| 710 | name = "tracing-subscriber" | ||
| 711 | version = "0.3.22" | ||
| 712 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 713 | checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" | ||
| 714 | dependencies = [ | ||
| 715 | "nu-ansi-term", | ||
| 716 | "sharded-slab", | ||
| 717 | "smallvec", | ||
| 718 | "thread_local", | ||
| 719 | "tracing-core", | ||
| 720 | "tracing-log", | ||
| 721 | ] | ||
| 722 | |||
| 723 | [[package]] | ||
| 620 | name = "unicode-ident" | 724 | name = "unicode-ident" |
| 621 | version = "1.0.22" | 725 | version = "1.0.22" |
| 622 | source = "registry+https://github.com/rust-lang/crates.io-index" | 726 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 623 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" | 727 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" |
| 624 | 728 | ||
| 625 | [[package]] | 729 | [[package]] |
| 730 | name = "valuable" | ||
| 731 | version = "0.1.1" | ||
| 732 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 733 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" | ||
| 734 | |||
| 735 | [[package]] | ||
| 626 | name = "void" | 736 | name = "void" |
| 627 | version = "1.0.2" | 737 | version = "1.0.2" |
| 628 | source = "registry+https://github.com/rust-lang/crates.io-index" | 738 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -638,6 +748,21 @@ dependencies = [ | |||
| 638 | ] | 748 | ] |
| 639 | 749 | ||
| 640 | [[package]] | 750 | [[package]] |
| 751 | name = "windows-link" | ||
| 752 | version = "0.2.1" | ||
| 753 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 754 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" | ||
| 755 | |||
| 756 | [[package]] | ||
| 757 | name = "windows-sys" | ||
| 758 | version = "0.61.2" | ||
| 759 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 760 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" | ||
| 761 | dependencies = [ | ||
| 762 | "windows-link", | ||
| 763 | ] | ||
| 764 | |||
| 765 | [[package]] | ||
| 641 | name = "wit-bindgen" | 766 | name = "wit-bindgen" |
| 642 | version = "0.46.0" | 767 | version = "0.46.0" |
| 643 | source = "registry+https://github.com/rust-lang/crates.io-index" | 768 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| @@ -3,15 +3,21 @@ name = "embassy-ha" | |||
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | edition = "2024" | 4 | edition = "2024" |
| 5 | 5 | ||
| 6 | [features] | ||
| 7 | default = [] | ||
| 8 | defmt = ["dep:defmt", "embassy-net/defmt", "embassy-sync/defmt"] | ||
| 9 | tracing = ["dep:tracing"] | ||
| 10 | |||
| 6 | [dependencies] | 11 | [dependencies] |
| 7 | embedded-mqtt = { path = "./embedded-mqtt" , features = ["embassy-net"] } | 12 | embedded-mqtt = { path = "./embedded-mqtt" , features = ["embassy-net"] } |
| 8 | embassy-net = { version = "0.7.1", features = ["defmt", "medium-ip", "proto-ipv4", "tcp"] } | 13 | embassy-net = { version = "0.7.1", features = ["medium-ip", "proto-ipv4", "tcp"] } |
| 9 | heapless = "0.9.2" | 14 | heapless = "0.9.2" |
| 10 | embassy-time = { version = "0.5.0" } | 15 | embassy-time = { version = "0.5.0" } |
| 11 | serde-json-core = "0.6.0" | 16 | serde-json-core = "0.6.0" |
| 12 | serde = { version = "1.0.228", default-features = false, features = ["derive"] } | 17 | serde = { version = "1.0.228", default-features = false, features = ["derive"] } |
| 13 | defmt = "1.0.1" | 18 | defmt = { version = "1.0.1", optional = true } |
| 14 | embassy-sync = { version = "0.7.2", features = ["defmt"] } | 19 | tracing = { version = "0.1", optional = true, default-features = false } |
| 20 | embassy-sync = { version = "0.7.2" } | ||
| 15 | embassy-futures = "0.1.2" | 21 | embassy-futures = "0.1.2" |
| 16 | embedded-io-async = "0.6" | 22 | embedded-io-async = "0.6" |
| 17 | 23 | ||
| @@ -23,3 +29,4 @@ static_cell = "2.1.1" | |||
| 23 | embedded-io-async = { version = "0.6", features = ["std"] } | 29 | embedded-io-async = { version = "0.6", features = ["std"] } |
| 24 | critical-section = { version = "1", features = ["std"] } | 30 | critical-section = { version = "1", features = ["std"] } |
| 25 | rand = "0.9.2" | 31 | rand = "0.9.2" |
| 32 | tracing-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) { | |||
| 41 | async fn binary_sensor_class(mut switch: embassy_ha::BinarySensor<'static>) { | 41 | async 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) { | |||
| 31 | async fn button_task(mut button: embassy_ha::Button<'static>) { | 31 | async 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(); | |||
| 9 | macro_rules! example_main { | 9 | macro_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 @@ | |||
| 1 | use std::{ | 1 | use 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 | ||
| 17 | impl AsyncTcp { | 18 | impl 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 | ||
| 80 | impl embedded_io_async::Write for AsyncTcp { | 86 | impl 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) { | |||
| 41 | async fn switch_task(mut switch: embassy_ha::Switch<'static>) { | 41 | async 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 | } |
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | use core::{cell::RefCell, task::Waker}; | 3 | use core::{cell::RefCell, task::Waker}; |
| 4 | 4 | ||
| 5 | use defmt::Format; | ||
| 6 | use embassy_sync::waitqueue::AtomicWaker; | 5 | use embassy_sync::waitqueue::AtomicWaker; |
| 7 | use heapless::{ | 6 | use heapless::{ |
| 8 | Vec, VecView, | 7 | Vec, VecView, |
| @@ -10,6 +9,9 @@ use heapless::{ | |||
| 10 | }; | 9 | }; |
| 11 | use serde::Serialize; | 10 | use serde::Serialize; |
| 12 | 11 | ||
| 12 | pub mod log; | ||
| 13 | pub use log::Format; | ||
| 14 | |||
| 13 | pub mod constants; | 15 | pub mod constants; |
| 14 | 16 | ||
| 15 | mod binary_state; | 17 | mod 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))] | ||
| 63 | struct DeviceDiscovery<'a> { | 66 | struct 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))] | ||
| 71 | struct EntityDiscovery<'a> { | 75 | struct 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")] | ||
| 24 | pub use defmt::Format; | ||
| 25 | |||
| 26 | // For tracing or no logging, we provide a stub Format trait | ||
| 27 | #[cfg(not(feature = "defmt"))] | ||
| 28 | pub trait Format {} | ||
| 29 | |||
| 30 | // When using defmt, also provide Debug2Format for std types | ||
| 31 | #[cfg(feature = "defmt")] | ||
| 32 | pub use defmt::Debug2Format; | ||
| 33 | |||
| 34 | // For tracing or no logging, Debug2Format is a passthrough | ||
| 35 | #[cfg(not(feature = "defmt"))] | ||
| 36 | #[inline] | ||
| 37 | pub 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] | ||
| 45 | macro_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] | ||
| 59 | macro_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] | ||
| 73 | macro_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] | ||
| 87 | macro_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] | ||
| 101 | macro_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 | ||
| 115 | pub use crate::{trace, debug, info, warn, error}; | ||
