diff --git a/examples/src/bin/bridge.rs b/examples/src/bin/bridge.rs index 85d9285d..30ca3959 100644 --- a/examples/src/bin/bridge.rs +++ b/examples/src/bin/bridge.rs @@ -76,12 +76,12 @@ fn main() -> Result<(), Error> { let on_off_handler_ep2 = on_off::OnOffHandler::new_standalone( Dataver::new_rand(matter.rand()), 2, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(false), ); let on_off_handler_ep3 = on_off::OnOffHandler::new_standalone( Dataver::new_rand(matter.rand()), 3, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(false), ); // Create the Data Model instance diff --git a/examples/src/bin/chip_tool_tests.rs b/examples/src/bin/chip_tool_tests.rs index 5cb86597..92fd6e21 100644 --- a/examples/src/bin/chip_tool_tests.rs +++ b/examples/src/bin/chip_tool_tests.rs @@ -18,7 +18,6 @@ //! A dedicated Matter device for ConnectedHomeIP YAML integration tests. //! Implements On/Off and Unit Testing clusters over Ethernet. -use core::cell::Cell; use core::pin::pin; use std::net::UdpSocket; @@ -36,13 +35,11 @@ use log::info; use rs_matter::dm::clusters::basic_info::{ BasicInfoConfig, ColorEnum, ProductAppearance, ProductFinishEnum, }; -use rs_matter::dm::clusters::decl::on_off as on_off_cluster; use rs_matter::dm::clusters::desc::{self, ClusterHandler as _}; use rs_matter::dm::clusters::level_control::LevelControlHooks; use rs_matter::dm::clusters::net_comm::NetworkType; -use rs_matter::dm::clusters::on_off::{ - self, EffectVariantEnum, OnOffHandler, OnOffHooks, StartUpOnOffEnum, -}; +use rs_matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; +use rs_matter::dm::clusters::on_off::{self, OnOffHandler, OnOffHooks}; use rs_matter::dm::clusters::unit_testing::{ ClusterHandler as _, UnitTestingHandler, UnitTestingHandlerData, }; @@ -52,20 +49,18 @@ use rs_matter::dm::endpoints; use rs_matter::dm::networks::unix::UnixNetifs; use rs_matter::dm::subscriptions::DefaultSubscriptions; use rs_matter::dm::{ - Async, AsyncHandler, AsyncMetadata, Cluster, DataModel, Dataver, EmptyHandler, Endpoint, - EpClMatcher, Node, + Async, AsyncHandler, AsyncMetadata, DataModel, Dataver, EmptyHandler, Endpoint, EpClMatcher, + Node, }; use rs_matter::error::Error; use rs_matter::pairing::DiscoveryCapabilities; use rs_matter::persist::{Psm, NO_NETWORKS}; use rs_matter::respond::DefaultResponder; -use rs_matter::tlv::Nullable; use rs_matter::transport::MATTER_SOCKET_BIND_ADDR; use rs_matter::utils::cell::RefCell; use rs_matter::utils::init::InitMaybeUninit; use rs_matter::utils::select::Coalesce; use rs_matter::utils::storage::pooled::PooledBuffers; -use rs_matter::with; use rs_matter::{clusters, devices, Matter, MATTER_PORT}; use static_cell::StaticCell; @@ -136,8 +131,11 @@ fn main() -> Result<(), Error> { .init_with(DefaultSubscriptions::init()); // Our on-off cluster - let on_off_handler = - OnOffHandler::new_standalone(Dataver::new_rand(matter.rand()), 1, OnOffDeviceLogic::new()); + let on_off_handler = OnOffHandler::new_standalone( + Dataver::new_rand(matter.rand()), + 1, + TestOnOffDeviceLogic::new(false), + ); // Our unit testing cluster data let unit_testing_data = UNIT_TESTING_DATA @@ -247,7 +245,7 @@ const NODE: Node<'static> = Node { device_types: devices!(DEV_TYPE_ON_OFF_LIGHT), clusters: clusters!( desc::DescHandler::CLUSTER, - OnOffDeviceLogic::CLUSTER, + TestOnOffDeviceLogic::CLUSTER, UnitTestingHandler::CLUSTER ), }, @@ -276,7 +274,7 @@ fn dm_handler<'a, OH: OnOffHooks, LH: LevelControlHooks>( Async(desc::DescHandler::new(Dataver::new_rand(matter.rand())).adapt()), ) .chain( - EpClMatcher::new(Some(1), Some(OnOffDeviceLogic::CLUSTER.id)), + EpClMatcher::new(Some(1), Some(TestOnOffDeviceLogic::CLUSTER.id)), on_off::HandlerAsyncAdaptor(on_off), ) .chain( @@ -293,66 +291,3 @@ fn dm_handler<'a, OH: OnOffHooks, LH: LevelControlHooks>( ), ) } - -// Implementing the OnOff business logic - -#[derive(Default)] -pub struct OnOffDeviceLogic { - on_off: Cell, - start_up_on_off: Cell>, -} - -impl OnOffDeviceLogic { - pub fn new() -> Self { - Self { - on_off: Cell::new(false), - start_up_on_off: Cell::new(None), - } - } -} - -impl OnOffHooks for OnOffDeviceLogic { - const CLUSTER: Cluster<'static> = on_off_cluster::FULL_CLUSTER - .with_revision(6) - .with_features(on_off_cluster::Feature::LIGHTING.bits()) - .with_attrs(with!( - required; - on_off_cluster::AttributeId::OnOff - | on_off_cluster::AttributeId::GlobalSceneControl - | on_off_cluster::AttributeId::OnTime - | on_off_cluster::AttributeId::OffWaitTime - | on_off_cluster::AttributeId::StartUpOnOff - )) - .with_cmds(with!( - on_off_cluster::CommandId::Off - | on_off_cluster::CommandId::On - | on_off_cluster::CommandId::Toggle - | on_off_cluster::CommandId::OffWithEffect - | on_off_cluster::CommandId::OnWithRecallGlobalScene - | on_off_cluster::CommandId::OnWithTimedOff - )); - - fn on_off(&self) -> bool { - self.on_off.get() - } - - fn set_on_off(&self, on: bool) { - self.on_off.set(on); - } - - fn start_up_on_off(&self) -> Nullable { - match self.start_up_on_off.get() { - Some(value) => Nullable::some(value), - None => Nullable::none(), - } - } - - fn set_start_up_on_off(&self, value: Nullable) -> Result<(), Error> { - self.start_up_on_off.set(value.into_option()); - Ok(()) - } - - async fn handle_off_with_effect(&self, _effect: EffectVariantEnum) { - // no effect - } -} diff --git a/examples/src/bin/dimmable_light.rs b/examples/src/bin/dimmable_light.rs index db367f3c..80b1ba2b 100644 --- a/examples/src/bin/dimmable_light.rs +++ b/examples/src/bin/dimmable_light.rs @@ -28,8 +28,6 @@ use std::path::PathBuf; use embassy_futures::select::{select3, select4}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; -#[cfg(not(feature = "chip-test"))] -use embassy_time::{Duration, Timer}; use async_signal::{Signal, Signals}; use log::{error, info, trace}; @@ -43,8 +41,6 @@ use rs_matter::dm::clusters::decl::on_off as on_off_cluster; use rs_matter::dm::clusters::desc::{self, ClusterHandler as _}; use rs_matter::dm::clusters::level_control::{self, LevelControlHooks}; use rs_matter::dm::clusters::net_comm::NetworkType; -#[cfg(not(feature = "chip-test"))] -use rs_matter::dm::clusters::on_off::OutOfBandMessage; use rs_matter::dm::clusters::on_off::{self, OnOffHooks, StartUpOnOffEnum}; use rs_matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; use rs_matter::dm::devices::DEV_TYPE_DIMMABLE_LIGHT; @@ -536,16 +532,4 @@ impl OnOffHooks for OnOffDeviceLogic { async fn handle_off_with_effect(&self, _effect: on_off::EffectVariantEnum) { // no effect } - - #[cfg(not(feature = "chip-test"))] - async fn run(&self, notify: F) { - loop { - // In a real example we wait for physical interaction. - Timer::after(Duration::from_secs(5)).await; - match self.on_off() { - true => notify(OutOfBandMessage::Off), - false => notify(OutOfBandMessage::On), - } - } - } } diff --git a/examples/src/bin/media_player.rs b/examples/src/bin/media_player.rs index 1ebb4d6a..42f9086e 100644 --- a/examples/src/bin/media_player.rs +++ b/examples/src/bin/media_player.rs @@ -101,7 +101,7 @@ fn main() -> Result<(), Error> { let on_off_handler = on_off::OnOffHandler::new_standalone( Dataver::new_rand(matter.rand()), 1, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(false), ); // Create the Data Model instance diff --git a/examples/src/bin/onoff_light.rs b/examples/src/bin/onoff_light.rs index 7cbe77c8..1f5bc8ce 100644 --- a/examples/src/bin/onoff_light.rs +++ b/examples/src/bin/onoff_light.rs @@ -21,9 +21,8 @@ use core::pin::pin; use std::net::UdpSocket; -use embassy_futures::select::{select3, select4}; +use embassy_futures::select::{select, select4}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_time::{Duration, Timer}; use log::info; @@ -116,7 +115,7 @@ fn run() -> Result<(), Error> { let on_off_handler = on_off::OnOffHandler::new_standalone( Dataver::new_rand(matter.rand()), 1, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(true), ); // Create the Data Model instance @@ -143,19 +142,6 @@ fn run() -> Result<(), Error> { // Run the background job of the data model let mut dm_job = pin!(dm.run()); - // This is a sample code that simulates state changes triggered by the HAL - // Changes will be properly communicated to the Matter controllers and other Matter apps (i.e. Google Home, Alexa), thanks to subscriptions - let mut device = pin!(async { - loop { - Timer::after(Duration::from_secs(5)).await; - - on_off_handler.set_on_off(!on_off_handler.on_off()); - subscriptions.notify_cluster_changed(1, TestOnOffDeviceLogic::CLUSTER.id); - - info!("Lamp toggled"); - } - }); - // Create, load and run the persister let socket = async_io::Async::::bind(MATTER_SOCKET_BIND_ADDR)?; @@ -188,7 +174,7 @@ fn run() -> Result<(), Error> { &mut transport, &mut mdns, &mut persist, - select3(&mut respond, &mut device, &mut dm_job).coalesce(), + select(&mut respond, &mut dm_job).coalesce(), ); // Run with a simple `block_on`. Any local executor would do. diff --git a/examples/src/bin/onoff_light_bt.rs b/examples/src/bin/onoff_light_bt.rs index db1d941e..f0e6f565 100644 --- a/examples/src/bin/onoff_light_bt.rs +++ b/examples/src/bin/onoff_light_bt.rs @@ -34,10 +34,9 @@ use core::pin::pin; use std::net::UdpSocket; -use embassy_futures::select::{select, select3, select4}; +use embassy_futures::select::{select, select4}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_time::{Duration, Timer}; use log::{info, warn}; use rs_matter::dm::clusters::desc::{self, ClusterHandler as _}; @@ -135,7 +134,7 @@ fn run(connection: &Connection, net_ctl: N) -> Result<(), let on_off_handler = on_off::OnOffHandler::new_standalone( Dataver::new_rand(matter.rand()), 1, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(true), ); // A storage for the Wifi networks @@ -165,19 +164,6 @@ fn run(connection: &Connection, net_ctl: N) -> Result<(), // Run the background job of the data model let mut dm_job = pin!(dm.run()); - // This is a sample code that simulates state changes triggered by the HAL - // Changes will be properly communicated to the Matter controllers and other Matter apps (i.e. Google Home, Alexa), thanks to subscriptions - let mut device = pin!(async { - loop { - Timer::after(Duration::from_secs(5)).await; - - on_off_handler.set_on_off(!on_off_handler.on_off()); - subscriptions.notify_cluster_changed(1, TestOnOffDeviceLogic::CLUSTER.id); - - info!("Lamp toggled"); - } - }); - // Create, load and run the persister let mut psm: Psm<4096> = Psm::new(); let path = std::env::temp_dir().join("rs-matter"); @@ -207,7 +193,7 @@ fn run(connection: &Connection, net_ctl: N) -> Result<(), &mut transport, &mut bluetooth, select(&mut wifi_prov_task, &mut persist).coalesce(), - select3(&mut respond, &mut device, &mut dm_job).coalesce(), + select(&mut respond, &mut dm_job).coalesce(), ); // Run with a simple `block_on`. Any local executor would do. @@ -227,7 +213,7 @@ fn run(connection: &Connection, net_ctl: N) -> Result<(), &mut transport, &mut mdns, &mut persist, - select3(&mut respond, &mut device, &mut dm_job).coalesce(), + select(&mut respond, &mut dm_job).coalesce(), ); // Run with a simple `block_on`. Any local executor would do. diff --git a/examples/src/bin/speaker.rs b/examples/src/bin/speaker.rs index ca8739cf..cba8ff2b 100644 --- a/examples/src/bin/speaker.rs +++ b/examples/src/bin/speaker.rs @@ -75,7 +75,7 @@ fn main() -> Result<(), Error> { let on_off_handler = on_off::OnOffHandler::new( Dataver::new_rand(matter.rand()), 1, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(true), ); // LevelControl cluster setup diff --git a/rs-matter/src/dm/clusters/on_off.rs b/rs-matter/src/dm/clusters/on_off.rs index 4e145cf3..a2d2038d 100644 --- a/rs-matter/src/dm/clusters/on_off.rs +++ b/rs-matter/src/dm/clusters/on_off.rs @@ -924,12 +924,16 @@ impl LevelControlHooks for NoLevelControl { } pub mod test { + use embassy_time::{Duration, Timer}; + use crate::error::Error; use crate::tlv::Nullable; use core::cell::Cell; use crate::dm::clusters::decl::on_off as on_off_cluster; - use crate::dm::clusters::on_off::{EffectVariantEnum, OnOffHooks, StartUpOnOffEnum}; + use crate::dm::clusters::on_off::{ + EffectVariantEnum, OnOffHooks, OutOfBandMessage, StartUpOnOffEnum, + }; use crate::dm::Cluster; use crate::with; @@ -939,13 +943,15 @@ pub mod test { pub struct TestOnOffDeviceLogic { on_off: Cell, start_up_on_off: Cell>, + toggle_periodically: bool, } impl TestOnOffDeviceLogic { - pub const fn new() -> Self { + pub const fn new(toggle_periodically: bool) -> Self { Self { on_off: Cell::new(false), start_up_on_off: Cell::new(None), + toggle_periodically, } } } @@ -986,5 +992,26 @@ pub mod test { async fn handle_off_with_effect(&self, _effect: EffectVariantEnum) { // no effect } + + async fn run(&self, notify: F) { + if self.toggle_periodically { + loop { + // In a real example we wait for physical interaction. + Timer::after(Duration::from_secs(5)).await; + match self.on_off() { + true => { + info!("Emulation: out of band switch off"); + notify(OutOfBandMessage::Off); + } + false => { + info!("Emulation: out of band switch on"); + notify(OutOfBandMessage::On); + } + } + } + } else { + core::future::pending::<()>().await + } + } } } diff --git a/rs-matter/tests/common/e2e/im/handler.rs b/rs-matter/tests/common/e2e/im/handler.rs index 42295295..0c685b29 100644 --- a/rs-matter/tests/common/e2e/im/handler.rs +++ b/rs-matter/tests/common/e2e/im/handler.rs @@ -147,7 +147,7 @@ impl<'a> E2eRunner { let on_off_handler = on_off::OnOffHandler::new_standalone( Dataver::new_rand(self.matter.rand()), 1, - TestOnOffDeviceLogic::new(), + TestOnOffDeviceLogic::new(false), ); E2eTestHandler::new(&self.matter, on_off_handler)