1use serde::{Deserialize, Serialize};
2use serde_json::json;
3use tokio_tungstenite::tungstenite::protocol::Message;
4
5use crate::error::{validate_pin, ObnizError, ObnizResult};
6use crate::obniz::Obniz;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(rename_all = "kebab-case")]
10pub enum OutputType {
11 PushPull5v,
12 PushPull3v,
13 OpenDrain,
14}
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17#[serde(rename_all = "kebab-case")]
18pub enum PullType {
19 PullUp5v,
20 PullUp3v,
21 PullDown,
22 Float,
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26#[serde(rename_all = "lowercase")]
27pub enum Direction {
28 Input,
29 Output,
30}
31
32#[derive(Debug, Clone)]
33pub struct IoConfig {
34 pub direction: Direction,
35 pub value: Option<bool>,
36 pub output_type: Option<OutputType>,
37 pub pull_type: Option<PullType>,
38 pub stream: Option<bool>,
39}
40
41#[derive(Debug)]
42pub struct IoPin {
43 pin: u8,
44 obniz: Obniz,
45}
46
47impl IoPin {
48 pub fn new(pin: u8, obniz: Obniz) -> Self {
49 Self { pin, obniz }
50 }
51
52 pub fn pin_key(&self) -> String {
53 format!("io{}", self.pin)
54 }
55
56 pub async fn get(&self) -> ObnizResult<bool> {
58 validate_pin(self.pin)?;
59
60 let pin_key = self.pin_key();
61 let request = json!([{&pin_key: "get"}]);
62 let message = Message::from(request.to_string());
63
64 let response = self
65 .obniz
66 .send_await_response(message, pin_key.clone())
67 .await
68 .map_err(|e| ObnizError::Connection(e.to_string()))?;
69
70 if let Some(array) = response.as_array() {
73 if let Some(first_item) = array.first() {
74 if let Some(value) = first_item.get(&pin_key) {
75 return value.as_bool().ok_or_else(|| {
76 ObnizError::IoOperation("Invalid response format".to_string())
77 });
78 }
79 }
80 }
81
82 if let Some(value) = response.get(&pin_key) {
84 value
85 .as_bool()
86 .ok_or_else(|| ObnizError::IoOperation("Invalid response format".to_string()))
87 } else {
88 Err(ObnizError::IoOperation(format!(
89 "No response for pin {}",
90 self.pin
91 )))
92 }
93 }
94
95 pub async fn set(&self, value: bool) -> ObnizResult<()> {
97 validate_pin(self.pin)?;
98
99 let pin_key = self.pin_key();
100 let request = json!([{&pin_key: value}]);
101 let message = Message::from(request.to_string());
102
103 self.obniz
104 .send_message(message)
105 .map_err(|e| ObnizError::Connection(e.to_string()))?;
106 Ok(())
107 }
108
109 pub async fn configure(&self, config: IoConfig) -> ObnizResult<()> {
111 validate_pin(self.pin)?;
112 let pin_key = self.pin_key();
113 let mut pin_config = json!({
114 "direction": config.direction
115 });
116
117 if let Some(value) = config.value {
118 pin_config["value"] = json!(value);
119 }
120
121 if let Some(output_type) = config.output_type {
122 pin_config["output_type"] = json!(output_type);
123 }
124
125 if let Some(pull_type) = config.pull_type {
126 pin_config["pull_type"] = json!(pull_type);
127 }
128
129 if let Some(stream) = config.stream {
130 pin_config["stream"] = json!(stream);
131 }
132
133 let request = json!([{&pin_key: pin_config}]);
134 let message = Message::from(request.to_string());
135
136 self.obniz
137 .send_message(message)
138 .map_err(|e| ObnizError::Connection(e.to_string()))?;
139 Ok(())
140 }
141
142 pub async fn set_as_input(&self, enable_stream: bool) -> ObnizResult<()> {
144 validate_pin(self.pin)?;
145 let config = IoConfig {
146 direction: Direction::Input,
147 value: None,
148 output_type: None,
149 pull_type: None,
150 stream: Some(enable_stream),
151 };
152 self.configure(config).await
153 }
154
155 pub async fn set_as_output(&self, value: bool) -> ObnizResult<()> {
157 validate_pin(self.pin)?;
158 let config = IoConfig {
159 direction: Direction::Output,
160 value: Some(value),
161 output_type: None,
162 pull_type: None,
163 stream: None,
164 };
165 self.configure(config).await
166 }
167
168 pub async fn set_output_type(&self, output_type: OutputType) -> ObnizResult<()> {
170 validate_pin(self.pin)?;
171 let pin_key = self.pin_key();
172 let request = json!([{&pin_key: {"output_type": output_type}}]);
173 let message = Message::from(request.to_string());
174
175 self.obniz
176 .send_message(message)
177 .map_err(|e| ObnizError::Connection(e.to_string()))?;
178 Ok(())
179 }
180
181 pub async fn set_pull_type(&self, pull_type: PullType) -> ObnizResult<()> {
183 validate_pin(self.pin)?;
184 let pin_key = self.pin_key();
185 let request = json!([{&pin_key: {"pull_type": pull_type}}]);
186 let message = Message::from(request.to_string());
187
188 self.obniz
189 .send_message(message)
190 .map_err(|e| ObnizError::Connection(e.to_string()))?;
191 Ok(())
192 }
193
194 pub async fn on_change<F>(&self, callback: F) -> ObnizResult<()>
197 where
198 F: Fn(bool) + Send + Sync + 'static,
199 {
200 validate_pin(self.pin)?;
201 self.set_as_input(true).await?;
203
204 let pin_key = self.pin_key();
205 let pin_key_clone = pin_key.clone();
206
207 self.obniz
208 .register_callback(pin_key, move |response| {
209 if let Some(value) = response.get(&pin_key_clone) {
211 if let Some(bool_value) = value.as_bool() {
212 callback(bool_value);
213 }
214 }
215 })
216 .map_err(|e| ObnizError::CallbackError(e.to_string()))?;
217
218 Ok(())
219 }
220
221 pub async fn enable_stream(&self) -> ObnizResult<()> {
223 self.set_as_input(true).await
224 }
225
226 pub async fn disable_stream(&self) -> ObnizResult<()> {
228 self.set_as_input(false).await
229 }
230
231 pub fn remove_callback(&self) -> ObnizResult<()> {
233 validate_pin(self.pin)?;
234 let pin_key = self.pin_key();
235 self.obniz
236 .unregister_callback(pin_key)
237 .map_err(|e| ObnizError::CallbackError(e.to_string()))
238 }
239
240 pub async fn deinit(&self) -> ObnizResult<()> {
242 validate_pin(self.pin)?;
243 let pin_key = self.pin_key();
244 let request = json!([{&pin_key: null}]);
245 let message = Message::from(request.to_string());
246
247 self.obniz
248 .send_message(message)
249 .map_err(|e| ObnizError::Connection(e.to_string()))?;
250 Ok(())
251 }
252}
253
254#[derive(Debug)]
256pub struct IoManager {
257 obniz: Obniz,
258}
259
260impl IoManager {
261 pub fn new(obniz: Obniz) -> Self {
262 Self { obniz }
263 }
264
265 pub fn pin(&self, pin: u8) -> ObnizResult<IoPin> {
267 validate_pin(pin)?;
268 Ok(IoPin::new(pin, self.obniz.clone()))
269 }
270
271 pub async fn get_pin(&self, pin: u8) -> ObnizResult<bool> {
273 self.pin(pin)?.get().await
274 }
275
276 pub async fn set_pin(&self, pin: u8, value: bool) -> ObnizResult<()> {
278 self.pin(pin)?.set(value).await
279 }
280
281 pub async fn configure_pin(&self, pin: u8, config: IoConfig) -> ObnizResult<()> {
283 self.pin(pin)?.configure(config).await
284 }
285
286 pub async fn set_pin_as_input(&self, pin: u8, enable_stream: bool) -> ObnizResult<()> {
288 self.pin(pin)?.set_as_input(enable_stream).await
289 }
290
291 pub async fn set_pin_as_output(&self, pin: u8, value: bool) -> ObnizResult<()> {
293 self.pin(pin)?.set_as_output(value).await
294 }
295
296 pub async fn set_pin_callback<F>(&self, pin: u8, callback: F) -> ObnizResult<()>
299 where
300 F: Fn(bool) + Send + Sync + 'static,
301 {
302 self.pin(pin)?.on_change(callback).await
303 }
304
305 pub fn remove_pin_callback(&self, pin: u8) -> ObnizResult<()> {
307 self.pin(pin)?.remove_callback()
308 }
309
310 pub async fn enable_pin_stream(&self, pin: u8) -> ObnizResult<()> {
312 self.pin(pin)?.enable_stream().await
313 }
314
315 pub async fn disable_pin_stream(&self, pin: u8) -> ObnizResult<()> {
317 self.pin(pin)?.disable_stream().await
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324 use crate::error::validate_pin;
325
326 #[test]
327 fn test_pin_validation() {
328 assert!(validate_pin(0).is_ok());
330 assert!(validate_pin(5).is_ok());
331 assert!(validate_pin(11).is_ok());
332
333 assert!(validate_pin(12).is_err());
335 assert!(validate_pin(255).is_err());
336 }
337
338 #[test]
349 fn test_io_config_serialization() {
350 let config = IoConfig {
351 direction: Direction::Output,
352 value: Some(true),
353 output_type: Some(OutputType::PushPull5v),
354 pull_type: Some(PullType::PullUp5v),
355 stream: Some(false),
356 };
357
358 assert_eq!(config.direction, Direction::Output);
360 assert_eq!(config.value, Some(true));
361 }
362
363 #[test]
364 fn test_output_type_serialization() {
365 use serde_json;
366
367 let output_type = OutputType::PushPull5v;
368 let serialized = serde_json::to_string(&output_type).unwrap();
369 assert_eq!(serialized, "\"push-pull5v\"");
370
371 let output_type = OutputType::OpenDrain;
372 let serialized = serde_json::to_string(&output_type).unwrap();
373 assert_eq!(serialized, "\"open-drain\"");
374 }
375
376 #[test]
377 fn test_pull_type_serialization() {
378 use serde_json;
379
380 let pull_type = PullType::PullUp5v;
381 let serialized = serde_json::to_string(&pull_type).unwrap();
382 assert_eq!(serialized, "\"pull-up5v\"");
383
384 let pull_type = PullType::Float;
385 let serialized = serde_json::to_string(&pull_type).unwrap();
386 assert_eq!(serialized, "\"float\"");
387 }
388
389 #[test]
390 fn test_direction_serialization() {
391 use serde_json;
392
393 let direction = Direction::Input;
394 let serialized = serde_json::to_string(&direction).unwrap();
395 assert_eq!(serialized, "\"input\"");
396
397 let direction = Direction::Output;
398 let serialized = serde_json::to_string(&direction).unwrap();
399 assert_eq!(serialized, "\"output\"");
400 }
401
402 }