embassy_ha/
entity_switch.rs

1use crate::{
2    BinaryState, CommandPolicy, Entity, EntityCommonConfig, EntityConfig, SwitchCommand,
3    SwitchState, constants,
4};
5
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub enum SwitchClass {
8    #[default]
9    Generic,
10    Outlet,
11    Switch,
12}
13
14/// Configuration for a switch entity.
15///
16/// See [`CommandPolicy`] for details on how commands are handled.
17#[derive(Debug, Default)]
18pub struct SwitchConfig {
19    pub common: EntityCommonConfig,
20    pub class: SwitchClass,
21    pub command_policy: CommandPolicy,
22}
23
24impl SwitchConfig {
25    pub(crate) fn populate(&self, config: &mut EntityConfig) {
26        self.common.populate(config);
27        config.domain = constants::HA_DOMAIN_SWITCH;
28        config.device_class = match self.class {
29            SwitchClass::Generic => None,
30            SwitchClass::Outlet => Some(constants::HA_DEVICE_CLASS_SWITCH_OUTLET),
31            SwitchClass::Switch => Some(constants::HA_DEVICE_CLASS_SWITCH_SWITCH),
32        };
33    }
34}
35
36pub struct Switch<'a>(Entity<'a>);
37
38impl<'a> Switch<'a> {
39    pub(crate) fn new(entity: Entity<'a>) -> Self {
40        Self(entity)
41    }
42
43    pub fn state(&self) -> Option<BinaryState> {
44        self.0.with_data(|data| {
45            let storage = data.storage.as_switch_mut();
46            storage.state.as_ref().map(|s| s.value)
47        })
48    }
49
50    pub fn command(&self) -> Option<BinaryState> {
51        self.0.with_data(|data| {
52            let storage = data.storage.as_switch_mut();
53            storage.command.as_ref().map(|s| s.value)
54        })
55    }
56
57    pub fn toggle(&mut self) -> BinaryState {
58        let new_state = self.state().unwrap_or(BinaryState::Off).flip();
59        self.set(new_state);
60        new_state
61    }
62
63    pub fn set(&mut self, state: BinaryState) {
64        let publish = self.0.with_data(|data| {
65            let storage = data.storage.as_switch_mut();
66            let timestamp = embassy_time::Instant::now();
67            let publish = match &storage.command {
68                Some(command) => command.value != state,
69                None => true,
70            };
71            storage.state = Some(SwitchState {
72                value: state,
73                timestamp,
74            });
75            storage.command = Some(SwitchCommand {
76                value: state,
77                timestamp,
78            });
79            publish
80        });
81        if publish {
82            self.0.queue_publish();
83        }
84    }
85
86    pub async fn wait(&mut self) -> BinaryState {
87        loop {
88            self.0.wait_command().await;
89            if let Some(state) = self.command() {
90                return state;
91            }
92        }
93    }
94}