1use crate::device::PoKeysDevice;
4use crate::error::{PoKeysError, Result};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct EasySensor {
10 pub sensor_value: i32,
11 pub sensor_type: u8,
12 pub sensor_refresh_period: u8,
13 pub sensor_failsafe_config: u8,
14 pub sensor_reading_id: u8,
15 pub sensor_id: [u8; 8],
16 pub sensor_ok_status: u8,
17}
18
19impl EasySensor {
20 pub fn new() -> Self {
21 Self {
22 sensor_value: 0,
23 sensor_type: 0,
24 sensor_refresh_period: 0,
25 sensor_failsafe_config: 0,
26 sensor_reading_id: 0,
27 sensor_id: [0; 8],
28 sensor_ok_status: 0,
29 }
30 }
31
32 pub fn is_ok(&self) -> bool {
33 self.sensor_ok_status != 0
34 }
35
36 pub fn get_refresh_period_seconds(&self) -> f32 {
37 self.sensor_refresh_period as f32 * 0.1
38 }
39
40 pub fn set_refresh_period_seconds(&mut self, seconds: f32) {
41 self.sensor_refresh_period = (seconds * 10.0) as u8;
42 }
43
44 pub fn get_failsafe_timeout(&self) -> u8 {
45 self.sensor_failsafe_config & 0x3F
46 }
47
48 pub fn set_failsafe_timeout(&mut self, timeout_seconds: u8) {
49 self.sensor_failsafe_config =
50 (self.sensor_failsafe_config & 0xC0) | (timeout_seconds & 0x3F);
51 }
52
53 pub fn is_failsafe_invalid_zero(&self) -> bool {
54 (self.sensor_failsafe_config & 0x40) != 0
55 }
56
57 pub fn set_failsafe_invalid_zero(&mut self, enable: bool) {
58 if enable {
59 self.sensor_failsafe_config |= 0x40;
60 } else {
61 self.sensor_failsafe_config &= !0x40;
62 }
63 }
64
65 pub fn is_failsafe_invalid_max(&self) -> bool {
66 (self.sensor_failsafe_config & 0x80) != 0
67 }
68
69 pub fn set_failsafe_invalid_max(&mut self, enable: bool) {
70 if enable {
71 self.sensor_failsafe_config |= 0x80;
72 } else {
73 self.sensor_failsafe_config &= !0x80;
74 }
75 }
76}
77
78impl Default for EasySensor {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct CustomSensorUnit {
87 pub html_code: [u8; 32],
88 pub simple_text: [u8; 8],
89}
90
91impl CustomSensorUnit {
92 pub fn new() -> Self {
93 Self {
94 html_code: [0; 32],
95 simple_text: [0; 8],
96 }
97 }
98
99 pub fn set_html_code(&mut self, code: &str) -> Result<()> {
100 if code.len() > 32 {
101 return Err(PoKeysError::Parameter("HTML code too long".to_string()));
102 }
103
104 self.html_code.fill(0);
105 let code_bytes = code.as_bytes();
106 self.html_code[..code_bytes.len()].copy_from_slice(code_bytes);
107 Ok(())
108 }
109
110 pub fn get_html_code(&self) -> String {
111 let end = self.html_code.iter().position(|&b| b == 0).unwrap_or(32);
112 String::from_utf8_lossy(&self.html_code[..end]).to_string()
113 }
114
115 pub fn set_simple_text(&mut self, text: &str) -> Result<()> {
116 if text.len() > 8 {
117 return Err(PoKeysError::Parameter("Simple text too long".to_string()));
118 }
119
120 self.simple_text.fill(0);
121 let text_bytes = text.as_bytes();
122 self.simple_text[..text_bytes.len()].copy_from_slice(text_bytes);
123 Ok(())
124 }
125
126 pub fn get_simple_text(&self) -> String {
127 let end = self.simple_text.iter().position(|&b| b == 0).unwrap_or(8);
128 String::from_utf8_lossy(&self.simple_text[..end]).to_string()
129 }
130}
131
132impl Default for CustomSensorUnit {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl PoKeysDevice {
139 pub fn configure_easy_sensor(
141 &mut self,
142 sensor_index: usize,
143 sensor_type: u8,
144 sensor_id: &[u8; 8],
145 refresh_period_seconds: f32,
146 reading_id: u8,
147 ) -> Result<()> {
148 if sensor_index >= self.easy_sensors.len() {
149 return Err(PoKeysError::Parameter("Invalid sensor index".to_string()));
150 }
151
152 let sensor = &mut self.easy_sensors[sensor_index];
153 sensor.sensor_type = sensor_type;
154 sensor.sensor_id = *sensor_id;
155 sensor.set_refresh_period_seconds(refresh_period_seconds);
156 sensor.sensor_reading_id = reading_id;
157
158 self.send_easy_sensor_configuration(sensor_index)?;
160 Ok(())
161 }
162
163 pub fn enable_easy_sensor(&mut self, sensor_index: usize, enable: bool) -> Result<()> {
165 if sensor_index >= self.easy_sensors.len() {
166 return Err(PoKeysError::Parameter("Invalid sensor index".to_string()));
167 }
168
169 self.send_request(0xF0, sensor_index as u8, if enable { 1 } else { 0 }, 0, 0)?;
171 Ok(())
172 }
173
174 pub fn read_easy_sensor(&mut self, sensor_index: usize) -> Result<i32> {
176 if sensor_index >= self.easy_sensors.len() {
177 return Err(PoKeysError::Parameter("Invalid sensor index".to_string()));
178 }
179
180 self.read_all_easy_sensors()?;
182
183 Ok(self.easy_sensors[sensor_index].sensor_value)
184 }
185
186 pub fn read_all_easy_sensors(&mut self) -> Result<()> {
188 let response = self.send_request(0xF1, 0, 0, 0, 0)?;
189
190 let mut data_index = 8;
192 for sensor in &mut self.easy_sensors {
193 if data_index + 3 < response.len() {
194 sensor.sensor_value = i32::from_le_bytes([
195 response[data_index],
196 response[data_index + 1],
197 response[data_index + 2],
198 response[data_index + 3],
199 ]);
200 data_index += 4;
201
202 if data_index < response.len() {
204 sensor.sensor_ok_status = response[data_index];
205 data_index += 1;
206 }
207 }
208 }
209
210 Ok(())
211 }
212
213 pub fn configure_sensor_failsafe(
215 &mut self,
216 sensor_index: usize,
217 timeout_seconds: u8,
218 invalid_value_zero: bool,
219 invalid_value_max: bool,
220 ) -> Result<()> {
221 if sensor_index >= self.easy_sensors.len() {
222 return Err(PoKeysError::Parameter("Invalid sensor index".to_string()));
223 }
224
225 self.easy_sensors[sensor_index].set_failsafe_timeout(timeout_seconds);
226 self.easy_sensors[sensor_index].set_failsafe_invalid_zero(invalid_value_zero);
227 self.easy_sensors[sensor_index].set_failsafe_invalid_max(invalid_value_max);
228
229 let failsafe_config = self.easy_sensors[sensor_index].sensor_failsafe_config;
230
231 self.send_request(0xF2, sensor_index as u8, failsafe_config, 0, 0)?;
233
234 Ok(())
235 }
236
237 pub fn get_sensor_status(&self, sensor_index: usize) -> Result<bool> {
239 if sensor_index >= self.easy_sensors.len() {
240 return Err(PoKeysError::Parameter("Invalid sensor index".to_string()));
241 }
242
243 Ok(self.easy_sensors[sensor_index].is_ok())
244 }
245
246 pub fn set_custom_sensor_unit(
248 &mut self,
249 unit_index: usize,
250 unit: &CustomSensorUnit,
251 ) -> Result<()> {
252 if unit_index >= 16 {
253 return Err(PoKeysError::Parameter("Invalid unit index".to_string()));
254 }
255
256 self.send_request(
258 0xF3,
259 unit_index as u8,
260 unit.html_code[0],
261 unit.html_code[1],
262 unit.html_code[2],
263 )?;
264
265 self.send_request(
270 0xF4,
271 unit_index as u8,
272 unit.simple_text[0],
273 unit.simple_text[1],
274 unit.simple_text[2],
275 )?;
276
277 Ok(())
278 }
279
280 fn send_easy_sensor_configuration(&mut self, sensor_index: usize) -> Result<()> {
282 let sensor_type = self.easy_sensors[sensor_index].sensor_type;
284 let sensor_refresh_period = self.easy_sensors[sensor_index].sensor_refresh_period;
285 let sensor_reading_id = self.easy_sensors[sensor_index].sensor_reading_id;
286 let sensor_id = self.easy_sensors[sensor_index].sensor_id;
287
288 self.send_request(
290 0xF5,
291 sensor_index as u8,
292 sensor_type,
293 sensor_refresh_period,
294 sensor_reading_id,
295 )?;
296
297 self.send_request(
299 0xF6,
300 sensor_index as u8,
301 sensor_id[0],
302 sensor_id[1],
303 sensor_id[2],
304 )?;
305
306 self.send_request(
307 0xF7,
308 sensor_index as u8,
309 sensor_id[3],
310 sensor_id[4],
311 sensor_id[5],
312 )?;
313
314 self.send_request(0xF8, sensor_index as u8, sensor_id[6], sensor_id[7], 0)?;
315
316 Ok(())
317 }
318}
319
320pub mod sensor_types {
322 pub const TEMPERATURE_DS18B20: u8 = 1;
323 pub const HUMIDITY_DHT22: u8 = 2;
324 pub const PRESSURE_BMP180: u8 = 3;
325 pub const LIGHT_BH1750: u8 = 4;
326 pub const DISTANCE_HC_SR04: u8 = 5;
327 pub const ANALOG_VOLTAGE: u8 = 10;
328 pub const ANALOG_CURRENT: u8 = 11;
329 pub const DIGITAL_COUNTER: u8 = 20;
330 pub const ENCODER_POSITION: u8 = 21;
331}
332
333pub fn configure_ds18b20_sensor(
337 device: &mut PoKeysDevice,
338 sensor_index: usize,
339 sensor_id: &[u8; 8],
340 refresh_period: f32,
341) -> Result<()> {
342 device.configure_easy_sensor(
343 sensor_index,
344 sensor_types::TEMPERATURE_DS18B20,
345 sensor_id,
346 refresh_period,
347 0, )
349}
350
351pub fn configure_analog_voltage_sensor(
353 device: &mut PoKeysDevice,
354 sensor_index: usize,
355 pin_id: u8,
356 refresh_period: f32,
357) -> Result<()> {
358 let mut sensor_id = [0u8; 8];
359 sensor_id[0] = pin_id;
360
361 device.configure_easy_sensor(
362 sensor_index,
363 sensor_types::ANALOG_VOLTAGE,
364 &sensor_id,
365 refresh_period,
366 0,
367 )
368}
369
370pub fn read_temperature_celsius(device: &mut PoKeysDevice, sensor_index: usize) -> Result<f32> {
372 let raw_value = device.read_easy_sensor(sensor_index)?;
373 Ok(raw_value as f32 * 0.0625)
375}
376
377pub fn read_voltage(
379 device: &mut PoKeysDevice,
380 sensor_index: usize,
381 reference_voltage: f32,
382) -> Result<f32> {
383 let raw_value = device.read_easy_sensor(sensor_index)?;
384 Ok((raw_value as f32 / 4095.0) * reference_voltage)
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_easy_sensor_creation() {
394 let sensor = EasySensor::new();
395 assert_eq!(sensor.sensor_value, 0);
396 assert!(!sensor.is_ok());
397 assert_eq!(sensor.get_refresh_period_seconds(), 0.0);
398 }
399
400 #[test]
401 fn test_refresh_period_conversion() {
402 let mut sensor = EasySensor::new();
403
404 sensor.set_refresh_period_seconds(1.5);
405 assert_eq!(sensor.sensor_refresh_period, 15);
406 assert_eq!(sensor.get_refresh_period_seconds(), 1.5);
407
408 sensor.set_refresh_period_seconds(0.1);
409 assert_eq!(sensor.sensor_refresh_period, 1);
410 assert_eq!(sensor.get_refresh_period_seconds(), 0.1);
411 }
412
413 #[test]
414 fn test_failsafe_configuration() {
415 let mut sensor = EasySensor::new();
416
417 sensor.set_failsafe_timeout(30);
418 assert_eq!(sensor.get_failsafe_timeout(), 30);
419
420 sensor.set_failsafe_invalid_zero(true);
421 assert!(sensor.is_failsafe_invalid_zero());
422 assert!(!sensor.is_failsafe_invalid_max());
423
424 sensor.set_failsafe_invalid_max(true);
425 assert!(sensor.is_failsafe_invalid_zero());
426 assert!(sensor.is_failsafe_invalid_max());
427
428 sensor.set_failsafe_invalid_zero(false);
429 assert!(!sensor.is_failsafe_invalid_zero());
430 assert!(sensor.is_failsafe_invalid_max());
431 }
432
433 #[test]
434 fn test_custom_sensor_unit() {
435 let mut unit = CustomSensorUnit::new();
436
437 assert!(unit.set_html_code("°C").is_ok());
438 assert_eq!(unit.get_html_code(), "°C");
439
440 assert!(unit.set_simple_text("°C").is_ok());
441 assert_eq!(unit.get_simple_text(), "°C");
442
443 assert!(unit.set_html_code(&"x".repeat(33)).is_err());
445 assert!(unit.set_simple_text(&"x".repeat(9)).is_err());
446 }
447
448 #[test]
449 fn test_temperature_conversion() {
450 let raw_value = 400; let temperature = raw_value as f32 * 0.0625;
453 assert_eq!(temperature, 25.0);
454
455 let raw_value = -160; let temperature = raw_value as f32 * 0.0625;
457 assert_eq!(temperature, -10.0);
458 }
459
460 #[test]
461 fn test_voltage_conversion() {
462 let raw_value = 2048; let reference_voltage = 5.0;
465 let voltage = (raw_value as f32 / 4095.0) * reference_voltage;
466 assert!((voltage - 2.5).abs() < 0.01);
467
468 let raw_value = 4095; let voltage = (raw_value as f32 / 4095.0) * reference_voltage;
470 assert!((voltage - 5.0).abs() < 0.01);
471 }
472}