Skip to main content

obniz_rust/
switch.rs

1use serde::{Deserialize, Serialize};
2use serde_json::json;
3use tokio_tungstenite::tungstenite::protocol::Message;
4
5use crate::error::{ObnizError, ObnizResult};
6use crate::obniz::Obniz;
7
8/// Switch states for obniz board switch
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum SwitchState {
12    None,
13    Push,
14    Left,
15    Right,
16}
17
18impl std::fmt::Display for SwitchState {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            SwitchState::None => write!(f, "none"),
22            SwitchState::Push => write!(f, "push"),
23            SwitchState::Left => write!(f, "left"),
24            SwitchState::Right => write!(f, "right"),
25        }
26    }
27}
28
29/// Switch action types
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31#[serde(rename_all = "lowercase")]
32pub enum SwitchAction {
33    Get,
34    Push,
35    Release,
36    Left,
37    Right,
38}
39
40/// Switch response structure
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct SwitchResponse {
43    pub state: SwitchState,
44    pub action: SwitchAction,
45}
46
47/// Switch manager for obniz board switch
48#[derive(Debug, Clone)]
49pub struct SwitchManager {
50    obniz: Obniz,
51}
52
53impl SwitchManager {
54    pub fn new(obniz: Obniz) -> Self {
55        Self { obniz }
56    }
57
58    /// Get current switch state
59    pub async fn get_state(&self) -> ObnizResult<SwitchState> {
60        let request = json!([{"switch": "get"}]);
61        let message = Message::from(request.to_string());
62
63        let response = self
64            .obniz
65            .send_await_response(message, "switch".to_string())
66            .await
67            .map_err(|e| ObnizError::Connection(e.to_string()))?;
68
69        // Parse the response to extract the switch state
70        // Response format is typically [{"switch": {"state": "none", "action": "get"}}]
71        if let Some(array) = response.as_array() {
72            if let Some(first_item) = array.first() {
73                if let Some(switch_data) = first_item.get("switch") {
74                    if let Some(state_value) = switch_data.get("state") {
75                        return serde_json::from_value(state_value.clone())
76                            .map_err(|e| ObnizError::JsonParse(e.to_string()));
77                    }
78                }
79            }
80        }
81
82        // Fallback: try direct object access
83        if let Some(switch_data) = response.get("switch") {
84            if let Some(state_value) = switch_data.get("state") {
85                serde_json::from_value(state_value.clone())
86                    .map_err(|e| ObnizError::JsonParse(e.to_string()))
87            } else {
88                Err(ObnizError::Generic(
89                    "No state field in switch response".to_string(),
90                ))
91            }
92        } else {
93            Err(ObnizError::Generic(
94                "No switch data in response".to_string(),
95            ))
96        }
97    }
98
99    /// Check if switch is currently pressed (any direction)
100    pub async fn is_pressed(&self) -> ObnizResult<bool> {
101        let state = self.get_state().await?;
102        Ok(state != SwitchState::None)
103    }
104
105    /// Check if switch is pressed in specific direction
106    pub async fn is_pressed_direction(&self, direction: SwitchState) -> ObnizResult<bool> {
107        let state = self.get_state().await?;
108        Ok(state == direction)
109    }
110
111    /// Register callback for switch state changes
112    pub async fn on_change<F>(&self, callback: F) -> ObnizResult<()>
113    where
114        F: Fn(SwitchState, SwitchAction) + Send + Sync + 'static,
115    {
116        self.obniz
117            .register_callback("switch".to_string(), move |response| {
118                if let Some(switch_data) = response.get("switch") {
119                    if let Ok(switch_response) =
120                        serde_json::from_value::<SwitchResponse>(switch_data.clone())
121                    {
122                        callback(switch_response.state, switch_response.action);
123                    }
124                }
125            })
126            .map_err(|e| ObnizError::CallbackError(e.to_string()))?;
127
128        Ok(())
129    }
130
131    /// Register callback for push events only
132    pub async fn on_push<F>(&self, callback: F) -> ObnizResult<()>
133    where
134        F: Fn() + Send + Sync + 'static,
135    {
136        self.on_change(move |state, action| {
137            if state == SwitchState::Push && action == SwitchAction::Push {
138                callback();
139            }
140        })
141        .await
142    }
143
144    /// Register callback for release events
145    pub async fn on_release<F>(&self, callback: F) -> ObnizResult<()>
146    where
147        F: Fn() + Send + Sync + 'static,
148    {
149        self.on_change(move |state, action| {
150            if state == SwitchState::None && action == SwitchAction::Release {
151                callback();
152            }
153        })
154        .await
155    }
156
157    /// Register callback for left direction events
158    pub async fn on_left<F>(&self, callback: F) -> ObnizResult<()>
159    where
160        F: Fn() + Send + Sync + 'static,
161    {
162        self.on_change(move |state, action| {
163            if state == SwitchState::Left && action == SwitchAction::Left {
164                callback();
165            }
166        })
167        .await
168    }
169
170    /// Register callback for right direction events
171    pub async fn on_right<F>(&self, callback: F) -> ObnizResult<()>
172    where
173        F: Fn() + Send + Sync + 'static,
174    {
175        self.on_change(move |state, action| {
176            if state == SwitchState::Right && action == SwitchAction::Right {
177                callback();
178            }
179        })
180        .await
181    }
182
183    /// Register callback for any press event (push, left, or right)
184    pub async fn on_any_press<F>(&self, callback: F) -> ObnizResult<()>
185    where
186        F: Fn(SwitchState) + Send + Sync + 'static,
187    {
188        self.on_change(move |state, _action| {
189            if state != SwitchState::None {
190                callback(state.clone());
191            }
192        })
193        .await
194    }
195
196    /// Remove switch callback
197    pub fn remove_callback(&self) -> ObnizResult<()> {
198        self.obniz
199            .unregister_callback("switch".to_string())
200            .map_err(|e| ObnizError::CallbackError(e.to_string()))
201    }
202
203    /// Wait for specific switch state (blocking until state is reached)
204    pub async fn wait_for_state(
205        &self,
206        target_state: SwitchState,
207        timeout_ms: Option<u64>,
208    ) -> ObnizResult<()> {
209        use tokio::time::{sleep, timeout, Duration};
210
211        let check_interval = Duration::from_millis(50);
212        let max_duration = timeout_ms.map(Duration::from_millis);
213
214        let wait_future = async {
215            loop {
216                let current_state = self.get_state().await?;
217                if current_state == target_state {
218                    return Ok(());
219                }
220                sleep(check_interval).await;
221            }
222        };
223
224        match max_duration {
225            Some(duration) => timeout(duration, wait_future)
226                .await
227                .map_err(|_| ObnizError::Timeout)?,
228            None => wait_future.await,
229        }
230    }
231
232    /// Wait for any press event
233    pub async fn wait_for_press(&self, timeout_ms: Option<u64>) -> ObnizResult<SwitchState> {
234        use tokio::time::{sleep, timeout, Duration};
235
236        let check_interval = Duration::from_millis(50);
237        let max_duration = timeout_ms.map(Duration::from_millis);
238
239        let wait_future = async {
240            loop {
241                let current_state = self.get_state().await?;
242                if current_state != SwitchState::None {
243                    return Ok(current_state);
244                }
245                sleep(check_interval).await;
246            }
247        };
248
249        match max_duration {
250            Some(duration) => timeout(duration, wait_future)
251                .await
252                .map_err(|_| ObnizError::Timeout)?,
253            None => wait_future.await,
254        }
255    }
256
257    /// Wait for release event
258    pub async fn wait_for_release(&self, timeout_ms: Option<u64>) -> ObnizResult<()> {
259        self.wait_for_state(SwitchState::None, timeout_ms).await
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266
267    #[test]
268    fn test_switch_state_serialization() {
269        use serde_json;
270
271        assert_eq!(
272            serde_json::to_string(&SwitchState::None).unwrap(),
273            "\"none\""
274        );
275        assert_eq!(
276            serde_json::to_string(&SwitchState::Push).unwrap(),
277            "\"push\""
278        );
279        assert_eq!(
280            serde_json::to_string(&SwitchState::Left).unwrap(),
281            "\"left\""
282        );
283        assert_eq!(
284            serde_json::to_string(&SwitchState::Right).unwrap(),
285            "\"right\""
286        );
287    }
288
289    #[test]
290    fn test_switch_state_deserialization() {
291        use serde_json;
292
293        assert_eq!(
294            serde_json::from_str::<SwitchState>("\"none\"").unwrap(),
295            SwitchState::None
296        );
297        assert_eq!(
298            serde_json::from_str::<SwitchState>("\"push\"").unwrap(),
299            SwitchState::Push
300        );
301        assert_eq!(
302            serde_json::from_str::<SwitchState>("\"left\"").unwrap(),
303            SwitchState::Left
304        );
305        assert_eq!(
306            serde_json::from_str::<SwitchState>("\"right\"").unwrap(),
307            SwitchState::Right
308        );
309    }
310
311    #[test]
312    fn test_switch_action_serialization() {
313        use serde_json;
314
315        assert_eq!(
316            serde_json::to_string(&SwitchAction::Get).unwrap(),
317            "\"get\""
318        );
319        assert_eq!(
320            serde_json::to_string(&SwitchAction::Push).unwrap(),
321            "\"push\""
322        );
323        assert_eq!(
324            serde_json::to_string(&SwitchAction::Release).unwrap(),
325            "\"release\""
326        );
327        assert_eq!(
328            serde_json::to_string(&SwitchAction::Left).unwrap(),
329            "\"left\""
330        );
331        assert_eq!(
332            serde_json::to_string(&SwitchAction::Right).unwrap(),
333            "\"right\""
334        );
335    }
336
337    #[test]
338    fn test_switch_response_serialization() {
339        use serde_json;
340
341        let response = SwitchResponse {
342            state: SwitchState::Push,
343            action: SwitchAction::Push,
344        };
345
346        let serialized = serde_json::to_string(&response).unwrap();
347        assert!(serialized.contains("\"state\":\"push\""));
348        assert!(serialized.contains("\"action\":\"push\""));
349    }
350
351    #[test]
352    fn test_switch_response_deserialization() {
353        use serde_json;
354
355        let json_str = r#"{"state": "left", "action": "left"}"#;
356        let response: SwitchResponse = serde_json::from_str(json_str).unwrap();
357
358        assert_eq!(response.state, SwitchState::Left);
359        assert_eq!(response.action, SwitchAction::Left);
360    }
361
362    #[test]
363    fn test_switch_state_display() {
364        assert_eq!(format!("{}", SwitchState::None), "none");
365        assert_eq!(format!("{}", SwitchState::Push), "push");
366        assert_eq!(format!("{}", SwitchState::Left), "left");
367        assert_eq!(format!("{}", SwitchState::Right), "right");
368    }
369
370    #[test]
371    fn test_switch_state_equality() {
372        assert_eq!(SwitchState::None, SwitchState::None);
373        assert_eq!(SwitchState::Push, SwitchState::Push);
374        assert_ne!(SwitchState::None, SwitchState::Push);
375        assert_ne!(SwitchState::Left, SwitchState::Right);
376    }
377}