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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ResetConfig {
20 pub reset_obniz_on_ws_disconnection: bool,
21}
22
23#[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 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 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 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 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 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 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 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 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}