Skip to main content

obniz_rust/
system.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/// System information structure
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SystemInfo {
11    pub version: String,
12    pub hardware: String,
13    pub device_type: String,
14    pub region: Option<String>,
15}
16
17/// Reset configuration
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ResetConfig {
20    pub reset_obniz_on_ws_disconnection: bool,
21}
22
23/// System manager for obniz device control
24#[derive(Debug, Clone)]
25pub struct SystemManager {
26    obniz: Obniz,
27}
28
29impl SystemManager {
30    pub fn new(obniz: Obniz) -> Self {
31        Self { obniz }
32    }
33
34    /// Reset the obniz device
35    pub async fn reset(&self) -> ObnizResult<()> {
36        let request = json!([{"system": {"reset": true}}]);
37        let message = Message::from(request.to_string());
38
39        self.obniz
40            .send_message(message)
41            .map_err(|e| ObnizError::Connection(e.to_string()))
42    }
43
44    /// Get system information
45    pub async fn info(&self) -> ObnizResult<SystemInfo> {
46        let request = json!([{"system": {"info": "get"}}]);
47        let message = Message::from(request.to_string());
48
49        let response = self
50            .obniz
51            .send_await_response(message, "system".to_string())
52            .await
53            .map_err(|e| ObnizError::Connection(e.to_string()))?;
54
55        // Parse the response to extract the system information
56        // Response format is typically [{"system": {...}}]
57        if let Some(array) = response.as_array() {
58            if let Some(first_item) = array.first() {
59                if let Some(system_info) = first_item.get("system") {
60                    return serde_json::from_value(system_info.clone())
61                        .map_err(|e| ObnizError::JsonParse(e.to_string()));
62                }
63            }
64        }
65
66        // Fallback: try direct object access
67        if let Some(system_info) = response.get("system") {
68            serde_json::from_value(system_info.clone())
69                .map_err(|e| ObnizError::JsonParse(e.to_string()))
70        } else {
71            Err(ObnizError::Generic(
72                "No system information in response".to_string(),
73            ))
74        }
75    }
76
77    /// Configure reset behavior on WebSocket disconnection
78    pub async fn reset_on_disconnect(&self, enable: bool) -> ObnizResult<()> {
79        let request = json!([{"ws": {"reset_obniz_on_ws_disconnection": enable}}]);
80        let message = Message::from(request.to_string());
81
82        self.obniz
83            .send_message(message)
84            .map_err(|e| ObnizError::Connection(e.to_string()))
85    }
86
87    /// Keep alive - prevent auto sleep
88    pub async fn keep_alive(&self) -> ObnizResult<()> {
89        let request = json!([{"system": {"keep_working_at_offline": true}}]);
90        let message = Message::from(request.to_string());
91
92        self.obniz
93            .send_message(message)
94            .map_err(|e| ObnizError::Connection(e.to_string()))
95    }
96
97    /// Set ping interval for connection monitoring
98    pub async fn ping_interval(&self, interval_ms: u32) -> ObnizResult<()> {
99        if interval_ms == 0 {
100            return Err(ObnizError::Generic(
101                "Ping interval must be greater than 0".to_string(),
102            ));
103        }
104
105        let request = json!([{"ws": {"ping": {"interval": interval_ms}}}]);
106        let message = Message::from(request.to_string());
107
108        self.obniz
109            .send_message(message)
110            .map_err(|e| ObnizError::Connection(e.to_string()))
111    }
112
113    /// Get device status
114    pub async fn status(&self) -> ObnizResult<serde_json::Value> {
115        let request = json!([{"system": {"status": "get"}}]);
116        let message = Message::from(request.to_string());
117
118        let response = self
119            .obniz
120            .send_await_response(message, "system".to_string())
121            .await
122            .map_err(|e| ObnizError::Connection(e.to_string()))?;
123
124        Ok(response)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_reset_config_serialization() {
134        use serde_json;
135
136        let config = ResetConfig {
137            reset_obniz_on_ws_disconnection: true,
138        };
139
140        let serialized = serde_json::to_string(&config).unwrap();
141        assert!(serialized.contains("reset_obniz_on_ws_disconnection"));
142        assert!(serialized.contains("true"));
143    }
144
145    #[test]
146    fn test_system_info_deserialization() {
147        use serde_json;
148
149        let json_str = r#"{
150            "version": "3.5.0",
151            "hardware": "obnizb1",
152            "device_type": "obnizb1",
153            "region": "jp"
154        }"#;
155
156        let info: SystemInfo = serde_json::from_str(json_str).unwrap();
157        assert_eq!(info.version, "3.5.0");
158        assert_eq!(info.hardware, "obnizb1");
159        assert_eq!(info.device_type, "obnizb1");
160        assert_eq!(info.region, Some("jp".to_string()));
161    }
162}