From abdcb51d7e9d6ebfacdf17b32e5296464e0aa2d4 Mon Sep 17 00:00:00 2001 From: diogo464 Date: Thu, 4 Dec 2025 21:14:27 +0000 Subject: added switch example --- examples/switch.rs | 42 ++++++++++++++++++++++++ src/constants.rs | 3 ++ src/lib.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 examples/switch.rs diff --git a/examples/switch.rs b/examples/switch.rs new file mode 100644 index 0000000..a12b8d0 --- /dev/null +++ b/examples/switch.rs @@ -0,0 +1,42 @@ +mod common; + +use common::AsyncTcp; +use embassy_executor::{Executor, Spawner}; +use embassy_time::Timer; +use static_cell::StaticCell; + +static RESOURCES: StaticCell = StaticCell::new(); + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS")); + + let mut device = embassy_ha::Device::new( + RESOURCES.init(Default::default()), + embassy_ha::DeviceConfig { + device_id: "example-device-id", + device_name: "Example Device Name", + manufacturer: "Example Device Manufacturer", + model: "Example Device Model", + }, + ); + + let switch = device.create_switch("switch-id", "Example Switch"); + + spawner.must_spawn(switch_task(switch)); + + device.run(&mut stream).await; +} + +#[embassy_executor::task] +async fn switch_task(mut switch: embassy_ha::Switch<'static>) { + loop { + let state = switch.wait().await; + switch.set(state); + + println!("state = {}", state); + Timer::after_secs(1).await; + } +} + +example_main!(); diff --git a/src/constants.rs b/src/constants.rs index 44ba09e..6159e3d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -126,3 +126,6 @@ pub const HA_UNIT_DISTANCE_KILOMETER: &str = "km"; pub const HA_ENTITY_CATEGORY_CONFIG: &str = "config"; pub const HA_ENTITY_CATEGORY_DIAGNOSTIC: &str = "diagnostic"; + +pub const HA_SWITCH_STATE_ON: &str = "ON"; +pub const HA_SWITCH_STATE_OFF: &str = "OFF"; diff --git a/src/lib.rs b/src/lib.rs index 1f9d2d5..d954883 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -use core::{cell::RefCell, task::Waker}; +use core::{cell::RefCell, str::FromStr, task::Waker}; use defmt::Format; use embassy_sync::waitqueue::AtomicWaker; @@ -198,6 +198,90 @@ impl<'a> Number<'a> { } } +pub struct InvalidSwitchState; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SwitchState { + On, + Off, +} + +impl SwitchState { + pub fn as_str(&self) -> &'static str { + match self { + Self::On => constants::HA_SWITCH_STATE_ON, + Self::Off => constants::HA_SWITCH_STATE_OFF, + } + } +} + +impl core::fmt::Display for SwitchState { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for SwitchState { + type Err = InvalidSwitchState; + + fn from_str(s: &str) -> Result { + if s.eq_ignore_ascii_case(constants::HA_SWITCH_STATE_ON) { + return Ok(Self::On); + } + if s.eq_ignore_ascii_case(constants::HA_SWITCH_STATE_OFF) { + return Ok(Self::Off); + } + Err(InvalidSwitchState) + } +} + +impl TryFrom<&[u8]> for SwitchState { + type Error = InvalidSwitchState; + + fn try_from(value: &[u8]) -> Result { + let string = str::from_utf8(value).map_err(|_| InvalidSwitchState)?; + string.parse() + } +} + +pub struct Switch<'a>(Entity<'a>); + +impl<'a> Switch<'a> { + pub fn state(&self) -> Option { + self.0 + .with_data(|data| SwitchState::try_from(data.command_value.as_slice()).ok()) + } + + pub fn toggle(&mut self) -> SwitchState { + let curr_state = self.state().unwrap_or(SwitchState::Off); + let new_state = match curr_state { + SwitchState::On => SwitchState::Off, + SwitchState::Off => SwitchState::On, + }; + self.set(new_state); + new_state + } + + pub fn set(&mut self, state: SwitchState) { + self.0.publish( + match state { + SwitchState::On => constants::HA_SWITCH_STATE_ON, + SwitchState::Off => constants::HA_SWITCH_STATE_OFF, + } + .as_bytes(), + ); + } + + pub async fn wait(&mut self) -> SwitchState { + loop { + self.0.wait_command().await; + if let Some(state) = self.state() { + return state; + } + } + } +} + #[derive(Default)] pub struct EntityConfig { pub id: &'static str, @@ -393,6 +477,16 @@ impl<'a> Device<'a> { Number(entity) } + pub fn create_switch(&self, id: &'static str, name: &'static str) -> Switch<'a> { + let entity = self.create_entity(EntityConfig { + id, + name, + domain: constants::HA_DOMAIN_SWITCH, + ..Default::default() + }); + Switch(entity) + } + pub async fn run(&mut self, transport: &mut T) -> ! { loop { self.run_iteration(&mut *transport).await; -- cgit