aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/switch.rs42
-rw-r--r--src/constants.rs3
-rw-r--r--src/lib.rs96
3 files changed, 140 insertions, 1 deletions
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 @@
1mod common;
2
3use common::AsyncTcp;
4use embassy_executor::{Executor, Spawner};
5use embassy_time::Timer;
6use static_cell::StaticCell;
7
8static RESOURCES: StaticCell<embassy_ha::DeviceResources> = StaticCell::new();
9
10#[embassy_executor::task]
11async fn main_task(spawner: Spawner) {
12 let mut stream = AsyncTcp::connect(std::env!("MQTT_ADDRESS"));
13
14 let mut device = embassy_ha::Device::new(
15 RESOURCES.init(Default::default()),
16 embassy_ha::DeviceConfig {
17 device_id: "example-device-id",
18 device_name: "Example Device Name",
19 manufacturer: "Example Device Manufacturer",
20 model: "Example Device Model",
21 },
22 );
23
24 let switch = device.create_switch("switch-id", "Example Switch");
25
26 spawner.must_spawn(switch_task(switch));
27
28 device.run(&mut stream).await;
29}
30
31#[embassy_executor::task]
32async fn switch_task(mut switch: embassy_ha::Switch<'static>) {
33 loop {
34 let state = switch.wait().await;
35 switch.set(state);
36
37 println!("state = {}", state);
38 Timer::after_secs(1).await;
39 }
40}
41
42example_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";
126 126
127pub const HA_ENTITY_CATEGORY_CONFIG: &str = "config"; 127pub const HA_ENTITY_CATEGORY_CONFIG: &str = "config";
128pub const HA_ENTITY_CATEGORY_DIAGNOSTIC: &str = "diagnostic"; 128pub const HA_ENTITY_CATEGORY_DIAGNOSTIC: &str = "diagnostic";
129
130pub const HA_SWITCH_STATE_ON: &str = "ON";
131pub 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 @@
1#![no_std] 1#![no_std]
2 2
3use core::{cell::RefCell, task::Waker}; 3use core::{cell::RefCell, str::FromStr, task::Waker};
4 4
5use defmt::Format; 5use defmt::Format;
6use embassy_sync::waitqueue::AtomicWaker; 6use embassy_sync::waitqueue::AtomicWaker;
@@ -198,6 +198,90 @@ impl<'a> Number<'a> {
198 } 198 }
199} 199}
200 200
201pub struct InvalidSwitchState;
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum SwitchState {
205 On,
206 Off,
207}
208
209impl SwitchState {
210 pub fn as_str(&self) -> &'static str {
211 match self {
212 Self::On => constants::HA_SWITCH_STATE_ON,
213 Self::Off => constants::HA_SWITCH_STATE_OFF,
214 }
215 }
216}
217
218impl core::fmt::Display for SwitchState {
219 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 f.write_str(self.as_str())
221 }
222}
223
224impl FromStr for SwitchState {
225 type Err = InvalidSwitchState;
226
227 fn from_str(s: &str) -> Result<Self, Self::Err> {
228 if s.eq_ignore_ascii_case(constants::HA_SWITCH_STATE_ON) {
229 return Ok(Self::On);
230 }
231 if s.eq_ignore_ascii_case(constants::HA_SWITCH_STATE_OFF) {
232 return Ok(Self::Off);
233 }
234 Err(InvalidSwitchState)
235 }
236}
237
238impl TryFrom<&[u8]> for SwitchState {
239 type Error = InvalidSwitchState;
240
241 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
242 let string = str::from_utf8(value).map_err(|_| InvalidSwitchState)?;
243 string.parse()
244 }
245}
246
247pub struct Switch<'a>(Entity<'a>);
248
249impl<'a> Switch<'a> {
250 pub fn state(&self) -> Option<SwitchState> {
251 self.0
252 .with_data(|data| SwitchState::try_from(data.command_value.as_slice()).ok())
253 }
254
255 pub fn toggle(&mut self) -> SwitchState {
256 let curr_state = self.state().unwrap_or(SwitchState::Off);
257 let new_state = match curr_state {
258 SwitchState::On => SwitchState::Off,
259 SwitchState::Off => SwitchState::On,
260 };
261 self.set(new_state);
262 new_state
263 }
264
265 pub fn set(&mut self, state: SwitchState) {
266 self.0.publish(
267 match state {
268 SwitchState::On => constants::HA_SWITCH_STATE_ON,
269 SwitchState::Off => constants::HA_SWITCH_STATE_OFF,
270 }
271 .as_bytes(),
272 );
273 }
274
275 pub async fn wait(&mut self) -> SwitchState {
276 loop {
277 self.0.wait_command().await;
278 if let Some(state) = self.state() {
279 return state;
280 }
281 }
282 }
283}
284
201#[derive(Default)] 285#[derive(Default)]
202pub struct EntityConfig { 286pub struct EntityConfig {
203 pub id: &'static str, 287 pub id: &'static str,
@@ -393,6 +477,16 @@ impl<'a> Device<'a> {
393 Number(entity) 477 Number(entity)
394 } 478 }
395 479
480 pub fn create_switch(&self, id: &'static str, name: &'static str) -> Switch<'a> {
481 let entity = self.create_entity(EntityConfig {
482 id,
483 name,
484 domain: constants::HA_DOMAIN_SWITCH,
485 ..Default::default()
486 });
487 Switch(entity)
488 }
489
396 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> ! { 490 pub async fn run<T: Transport>(&mut self, transport: &mut T) -> ! {
397 loop { 491 loop {
398 self.run_iteration(&mut *transport).await; 492 self.run_iteration(&mut *transport).await;