1use crate::device::PoKeysDevice;
4use crate::error::{PoKeysError, Result};
5use log::info;
6use serde::{Deserialize, Serialize};
7
8mod private;
9
10use private::INVERT_PIN_BIT;
11
12#[allow(deprecated)]
17mod pin_function {
18 use serde::{Deserialize, Serialize};
19
20 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32 pub enum PinFunction {
33 PinRestricted = 0,
34 Reserved = 1,
35 DigitalInput = 2,
36 DigitalOutput = 4,
37 AnalogInput = 8,
38 AnalogOutput = 16,
39 TriggeredInput = 32,
40 DigitalCounter = 64,
41 #[deprecated(
42 since = "0.23.0",
43 note = "Use PoKeysDevice::set_pin_function_with_invert to apply the invert flag; the variant is a protocol bit, not a standalone pin function."
44 )]
45 InvertPin = 128,
46 }
47}
48
49pub use pin_function::PinFunction;
50
51pub(crate) fn decode_pin_function_from_cache(byte: u8) -> PinFunction {
57 match byte & 0x7F {
58 0 => PinFunction::PinRestricted,
59 1 => PinFunction::Reserved,
60 2 => PinFunction::DigitalInput,
61 4 => PinFunction::DigitalOutput,
62 8 => PinFunction::AnalogInput,
63 16 => PinFunction::AnalogOutput,
64 32 => PinFunction::TriggeredInput,
65 64 => PinFunction::DigitalCounter,
66 _ => PinFunction::PinRestricted,
67 }
68}
69
70impl PinFunction {
71 pub fn from_u8(value: u8) -> Result<Self> {
74 #[allow(deprecated)]
75 match value {
76 0 => Ok(PinFunction::PinRestricted),
77 1 => Ok(PinFunction::Reserved),
78 2 => Ok(PinFunction::DigitalInput),
79 4 => Ok(PinFunction::DigitalOutput),
80 8 => Ok(PinFunction::AnalogInput),
81 16 => Ok(PinFunction::AnalogOutput),
82 32 => Ok(PinFunction::TriggeredInput),
83 64 => Ok(PinFunction::DigitalCounter),
84 128 => Ok(PinFunction::InvertPin),
85 v if (v & PinFunction::DigitalOutput as u8) != 0 => Ok(PinFunction::DigitalOutput),
87 v if (v & PinFunction::DigitalInput as u8) != 0 => Ok(PinFunction::DigitalInput),
88 v if (v & PinFunction::AnalogInput as u8) != 0 => Ok(PinFunction::AnalogInput),
89 v if (v & PinFunction::AnalogOutput as u8) != 0 => Ok(PinFunction::AnalogOutput),
90 v if (v & PinFunction::DigitalCounter as u8) != 0 => Ok(PinFunction::DigitalCounter),
91 v if (v & PinFunction::TriggeredInput as u8) != 0 => Ok(PinFunction::TriggeredInput),
92 _ => Ok(PinFunction::PinRestricted), }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub enum PinCapability {
100 DigitalInput = 1,
101 DigitalOutput,
102 AnalogInput,
103 MfAnalogInput,
104 AnalogOutput,
105 KeyboardMapping,
106 TriggeredInput,
107 DigitalCounter,
108 PwmOutput,
109 FastEncoder1A,
110 FastEncoder1B,
111 FastEncoder1I,
112 FastEncoder2A,
113 FastEncoder2B,
114 FastEncoder2I,
115 FastEncoder3A,
116 FastEncoder3B,
117 FastEncoder3I,
118 UltraFastEncoderA,
119 UltraFastEncoderB,
120 UltraFastEncoderI,
121 LcdE,
122 LcdRw,
123 LcdRs,
124 LcdD4,
125 LcdD5,
126 LcdD6,
127 LcdD7,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct PinData {
133 pub digital_counter_value: u32,
134 pub analog_value: u32,
135 pub pin_function: u8,
136 pub counter_options: u8,
137 pub digital_value_get: u8,
138 pub digital_value_set: u8,
139 pub digital_counter_available: u8,
140 pub mapping_type: u8,
141 pub key_code_macro_id: u8,
142 pub key_modifier: u8,
143 pub down_key_code_macro_id: u8,
144 pub down_key_modifier: u8,
145 pub up_key_code_macro_id: u8,
146 pub up_key_modifier: u8,
147 pub prevent_update: u8,
148}
149
150impl PinData {
151 pub fn new() -> Self {
152 Self {
153 digital_counter_value: 0,
154 analog_value: 0,
155 pin_function: PinFunction::PinRestricted as u8,
156 counter_options: 0,
157 digital_value_get: 0,
158 digital_value_set: 0,
159 digital_counter_available: 0,
160 mapping_type: 0,
161 key_code_macro_id: 0,
162 key_modifier: 0,
163 down_key_code_macro_id: 0,
164 down_key_modifier: 0,
165 up_key_code_macro_id: 0,
166 up_key_modifier: 0,
167 prevent_update: 0,
168 }
169 }
170
171 pub fn is_digital_input(&self) -> bool {
172 (self.pin_function & PinFunction::DigitalInput as u8) != 0
173 }
174
175 pub fn is_digital_output(&self) -> bool {
176 (self.pin_function & PinFunction::DigitalOutput as u8) != 0
177 }
178
179 pub fn is_analog_input(&self) -> bool {
180 (self.pin_function & PinFunction::AnalogInput as u8) != 0
181 }
182
183 pub fn is_analog_output(&self) -> bool {
184 (self.pin_function & PinFunction::AnalogOutput as u8) != 0
185 }
186
187 pub fn is_digital_counter(&self) -> bool {
188 (self.pin_function & PinFunction::DigitalCounter as u8) != 0
189 }
190
191 pub fn is_inverted(&self) -> bool {
194 (self.pin_function & INVERT_PIN_BIT) != 0
195 }
196
197 pub fn base_function(&self) -> PinFunction {
200 decode_pin_function_from_cache(self.pin_function)
201 }
202}
203
204impl Default for PinData {
205 fn default() -> Self {
206 Self::new()
207 }
208}
209
210impl PoKeysDevice {
211 pub fn set_pin_function(
217 &mut self,
218 pin: u32,
219 pin_function: PinFunction,
220 ) -> Result<(u32, PinFunction)> {
221 self.set_pin_function_with_invert(pin, pin_function, false)
222 }
223
224 pub fn set_pin_function_with_invert(
245 &mut self,
246 pin: u32,
247 pin_function: PinFunction,
248 inverted: bool,
249 ) -> Result<(u32, PinFunction)> {
250 self.write_pin_function(pin, pin_function, inverted)
251 }
252
253 pub fn get_pin_function(&self, pin: u32) -> Result<PinFunction> {
257 let pin_index = self.check_pin_range(pin)?;
258 Ok(decode_pin_function_from_cache(
259 self.pins[pin_index].pin_function,
260 ))
261 }
262
263 pub fn get_pin_invert(&self, pin: u32) -> Result<bool> {
270 let pin_index = self.check_pin_range(pin)?;
271 Ok((self.pins[pin_index].pin_function & INVERT_PIN_BIT) != 0)
272 }
273
274 pub fn get_digital_input(&mut self, pin: u32) -> Result<bool> {
276 if pin == 0 || pin as usize > self.pins.len() {
277 return Err(PoKeysError::Parameter("Invalid pin number".to_string()));
278 }
279
280 let res = self.read_digital_input(pin)?;
282
283 let pin_index = (pin - 1) as usize;
284 self.pins[pin_index].digital_value_get = res;
285 Ok(res != 0)
286 }
287
288 pub fn set_digital_output(&mut self, pin: u32, value: bool) -> Result<bool> {
290 if pin == 0 || pin as usize > self.pins.len() {
291 return Err(PoKeysError::Parameter("Invalid pin number".to_string()));
292 }
293
294 match self.write_digital_output(pin, !value) {
296 Ok(_) => {
297 let pin_index = (pin - 1) as usize;
298 self.pins[pin_index].digital_value_set = if value { 1 } else { 0 };
299 info!(
300 "Pin {} set to {:?}",
301 pin,
302 if value { "High" } else { "Low" }
303 );
304 Ok(true)
305 }
306 Err(e) => Err(e),
307 }
308 }
309
310 pub fn get_analog_input(&mut self, pin: u32) -> Result<u32> {
312 if pin == 0 || pin as usize > self.pins.len() {
313 return Err(PoKeysError::Parameter("Invalid pin number".to_string()));
314 }
315
316 self.read_analog_inputs()?;
318
319 let pin_index = (pin - 1) as usize;
320 Ok(self.pins[pin_index].analog_value)
321 }
322
323 pub fn set_analog_output(&mut self, pin: u32, value: u32) -> Result<()> {
325 if pin == 0 || pin as usize > self.pins.len() {
326 return Err(PoKeysError::Parameter("Invalid pin number".to_string()));
327 }
328
329 let pin_index = (pin - 1) as usize;
330 self.pins[pin_index].analog_value = value;
331
332 self.write_analog_outputs()?;
334 Ok(())
335 }
336
337 pub fn get_digital_counter(&mut self, pin: u32) -> Result<u32> {
339 if pin == 0 || pin as usize > self.pins.len() {
340 return Err(PoKeysError::Parameter("Invalid pin number".to_string()));
341 }
342
343 let pin_index = (pin - 1) as usize;
344 if self.pins[pin_index].digital_counter_available == 0 {
345 return Err(PoKeysError::NotSupported);
346 }
347
348 self.read_digital_counters()?;
350
351 Ok(self.pins[pin_index].digital_counter_value)
352 }
353
354 pub fn reset_all_digital_counters(&mut self) -> Result<()> {
359 self.send_request(0x1D, 0, 0, 0, 0)?;
360 Ok(())
361 }
362
363 pub fn get_digital_inputs(&mut self) -> Result<()> {
369 let resp1 = self.send_request(0x31, 0, 0, 0, 0)?;
370 for i in 0..self.pins.len().min(32) {
371 let byte_index = 2 + (i / 8);
372 let bit_index = i % 8;
373 self.pins[i].digital_value_get = if (resp1[byte_index] & (1 << bit_index)) != 0 {
374 1
375 } else {
376 0
377 };
378 }
379
380 if self.pins.len() > 32 {
381 let resp2 = self.send_request(0x32, 0, 0, 0, 0)?;
382 for i in 32..self.pins.len().min(55) {
383 let rel = i - 32;
384 let byte_index = 2 + (rel / 8);
385 let bit_index = rel % 8;
386 self.pins[i].digital_value_get = if (resp2[byte_index] & (1 << bit_index)) != 0 {
387 1
388 } else {
389 0
390 };
391 }
392 }
393
394 Ok(())
395 }
396
397 pub fn write_digital_outputs(&mut self) -> Result<()> {
399 let mut output_data = [0u8; 8];
401
402 for i in 0..self.pins.len().min(55) {
403 if self.pins[i].is_digital_output() && self.pins[i].digital_value_set != 0 {
404 let byte_index = i / 8;
405 let bit_index = i % 8;
406 output_data[byte_index] |= 1 << bit_index;
407 }
408 }
409
410 self.send_request(
412 0x11,
413 output_data[0],
414 output_data[1],
415 output_data[2],
416 output_data[3],
417 )?;
418
419 if self.pins.len() > 32 {
421 self.send_request(
422 0x12,
423 output_data[4],
424 output_data[5],
425 output_data[6],
426 output_data[7],
427 )?;
428 }
429
430 Ok(())
431 }
432
433 pub fn read_analog_inputs(&mut self) -> Result<()> {
435 let response = self.send_request(0x20, 0, 0, 0, 0)?;
436
437 let mut data_index = 8;
439 for i in 0..self.pins.len() {
440 if self.pins[i].is_analog_input() && data_index + 3 < response.len() {
441 self.pins[i].analog_value = u32::from_le_bytes([
442 response[data_index],
443 response[data_index + 1],
444 response[data_index + 2],
445 response[data_index + 3],
446 ]);
447 data_index += 4;
448 }
449 }
450
451 Ok(())
452 }
453
454 pub fn write_analog_outputs(&mut self) -> Result<()> {
461 let targets: Vec<(u8, u32)> = self
462 .pins
463 .iter()
464 .enumerate()
465 .filter_map(|(i, p)| {
466 if p.is_analog_output() {
467 Some((i as u8, p.analog_value))
468 } else {
469 None
470 }
471 })
472 .collect();
473
474 for (pin_code, value) in targets {
475 let (msb, lsb) = encode_analog_output_10bit(value);
476 let response = self.send_request(0x41, pin_code, msb, lsb, 0)?;
477 if response[2] != 0 {
479 return Err(PoKeysError::Protocol(format!(
480 "Analog output write failed for pin code {}: error code {}",
481 pin_code, response[2]
482 )));
483 }
484 }
485
486 Ok(())
487 }
488
489 pub fn read_digital_counters(&mut self) -> Result<()> {
495 let mut pin_ids = [0u8; 13];
497 let mut selected: Vec<usize> = Vec::with_capacity(13);
498 for (i, pin) in self.pins.iter().enumerate() {
499 if selected.len() == 13 {
500 break;
501 }
502 if pin.digital_counter_available != 0 {
503 pin_ids[selected.len()] = i as u8;
504 selected.push(i);
505 }
506 }
507
508 let response = self.send_request_with_data(0xD8, 0, 0, 0, 0, &pin_ids)?;
509
510 for (slot, pin_index) in selected.iter().enumerate() {
512 let start = 8 + slot * 4;
513 if start + 4 <= response.len() {
514 self.pins[*pin_index].digital_counter_value = u32::from_le_bytes([
515 response[start],
516 response[start + 1],
517 response[start + 2],
518 response[start + 3],
519 ]);
520 }
521 }
522
523 Ok(())
524 }
525
526 pub fn read_all_pin_functions(&mut self) -> Result<[PinFunction; 55]> {
530 use crate::io::private::Command;
531
532 let response = self.send_request(
535 Command::InputOutputExtended as u8,
536 0, 0, 0, 0, )?;
541
542 let raw = parse_bulk_pin_settings_response(&response)?;
543
544 let mut functions = [PinFunction::PinRestricted; 55];
546 for i in 0..55 {
547 functions[i] = PinFunction::from_u8(raw[i])?;
548 }
549
550 for i in 0..55 {
554 if i < self.pins.len() {
555 self.pins[i].pin_function = raw[i];
556 }
557 }
558
559 Ok(functions)
560 }
561
562 pub fn read_all_pin_settings_raw(&mut self) -> Result<[u8; 55]> {
569 use crate::io::private::Command;
570
571 let response = self.send_request(Command::InputOutputExtended as u8, 0, 0, 0, 0)?;
572 let raw = parse_bulk_pin_settings_response(&response)?;
573
574 for i in 0..55 {
575 if i < self.pins.len() {
576 self.pins[i].pin_function = raw[i];
577 }
578 }
579
580 Ok(raw)
581 }
582
583 pub fn set_all_pin_settings_raw(&mut self, raw: &[u8; 55]) -> Result<()> {
590 use crate::io::private::Command;
591
592 let response = self.send_request_with_data(
593 Command::InputOutputExtended as u8,
594 1, 0, 0,
597 0,
598 raw,
599 )?;
600
601 if response.len() < 64 {
602 return Err(PoKeysError::Protocol(
603 "Response too short for bulk pin write".to_string(),
604 ));
605 }
606
607 if response[1] != Command::InputOutputExtended as u8 {
608 return Err(PoKeysError::Protocol(
609 "Invalid response command".to_string(),
610 ));
611 }
612
613 for i in 0..55 {
614 if i < self.pins.len() {
615 self.pins[i].pin_function = raw[i];
616 }
617 }
618
619 Ok(())
620 }
621
622 pub fn set_all_pin_functions(&mut self, functions: &[PinFunction; 55]) -> Result<()> {
627 use crate::io::private::Command;
628
629 let payload: [u8; 55] = std::array::from_fn(|i| functions[i] as u8);
632
633 let response = self.send_request_with_data(
634 Command::InputOutputExtended as u8,
635 1, 0, 0, 0, &payload,
640 )?;
641
642 if response.len() < 64 {
643 return Err(PoKeysError::Protocol(
644 "Response too short for bulk pin write".to_string(),
645 ));
646 }
647
648 if response[1] != Command::InputOutputExtended as u8 {
649 return Err(PoKeysError::Protocol(
650 "Invalid response command".to_string(),
651 ));
652 }
653
654 for (i, &function) in functions.iter().enumerate() {
656 if i < self.pins.len() {
657 self.pins[i].pin_function = function as u8;
658 }
659 }
660
661 Ok(())
662 }
663
664 pub fn get_device_status(&mut self) -> Result<()> {
674 let response = self.send_request(0xCC, 0, 0, 0, 0)?;
675 apply_device_status_response(&response, &mut self.pins, &mut self.encoders);
676 Ok(())
677 }
678}
679
680fn encode_analog_output_10bit(value: u32) -> (u8, u8) {
685 let v = value & 0x3FF;
686 let msb = ((v >> 2) & 0xFF) as u8;
687 let lsb = ((v & 0x03) << 6) as u8;
688 (msb, lsb)
689}
690
691pub(crate) fn parse_bulk_pin_settings_response(response: &[u8]) -> Result<[u8; 55]> {
696 use crate::io::private::Command;
697
698 if response.len() < 64 {
699 return Err(PoKeysError::Protocol(
700 "Response too short for bulk pin read".to_string(),
701 ));
702 }
703
704 if response[1] != Command::InputOutputExtended as u8 {
705 return Err(PoKeysError::Protocol(
706 "Invalid response command".to_string(),
707 ));
708 }
709
710 let mut raw = [0u8; 55];
711 raw.copy_from_slice(&response[8..8 + 55]);
712 Ok(raw)
713}
714
715fn apply_device_status_response(
719 response: &[u8],
720 pins: &mut [PinData],
721 encoders: &mut [crate::encoders::EncoderData],
722) {
723 for i in 0..pins.len().min(55) {
725 let byte_index = 8 + (i / 8);
726 let bit_index = i % 8;
727 if byte_index < response.len() {
728 pins[i].digital_value_get = if (response[byte_index] & (1 << bit_index)) != 0 {
729 1
730 } else {
731 0
732 };
733 }
734 }
735
736 let mut data_index = 15;
738 let mut channels_consumed = 0;
739 for pin in pins.iter_mut() {
740 if channels_consumed >= 5 {
741 break;
742 }
743 if pin.is_analog_input() && data_index + 1 < response.len() {
744 let msb = response[data_index] as u32;
745 let lsb = response[data_index + 1] as u32;
746 pin.analog_value = (msb << 8) | lsb;
747 data_index += 2;
748 channels_consumed += 1;
749 }
750 }
751
752 for i in 0..encoders.len().min(25) {
754 let idx = 25 + i;
755 if idx < response.len() {
756 encoders[i].encoder_value = response[idx] as i8 as i32;
757 }
758 }
759}
760
761#[cfg(test)]
762mod tests {
763 use super::*;
764
765 #[test]
766 fn test_pin_data_creation() {
767 let pin_data = PinData::new();
768 assert_eq!(pin_data.pin_function, PinFunction::PinRestricted as u8);
769 assert_eq!(pin_data.digital_value_get, 0);
770 assert_eq!(pin_data.digital_value_set, 0);
771 }
772
773 #[test]
774 fn test_pin_function_checks() {
775 let mut pin_data = PinData::new();
776
777 pin_data.pin_function = PinFunction::DigitalInput as u8;
778 assert!(pin_data.is_digital_input());
779 assert!(!pin_data.is_digital_output());
780
781 pin_data.pin_function = PinFunction::DigitalOutput as u8;
782 assert!(!pin_data.is_digital_input());
783 assert!(pin_data.is_digital_output());
784
785 pin_data.pin_function =
786 (PinFunction::DigitalInput as u8) | (PinFunction::DigitalOutput as u8);
787 assert!(pin_data.is_digital_input());
788 assert!(pin_data.is_digital_output());
789 }
790
791 #[test]
792 fn test_encode_analog_output_10bit() {
793 assert_eq!(encode_analog_output_10bit(0), (0x00, 0x00));
794 assert_eq!(encode_analog_output_10bit(1023), (0xFF, 0xC0));
795 assert_eq!(encode_analog_output_10bit(512), (0x80, 0x00));
796 assert_eq!(encode_analog_output_10bit(3), (0x00, 0xC0));
798 assert_eq!(encode_analog_output_10bit(1), (0x00, 0x40));
799 assert_eq!(encode_analog_output_10bit(0xFFFF), (0xFF, 0xC0));
801 }
802
803 #[test]
804 fn test_apply_device_status_response() {
805 use crate::encoders::EncoderData;
806
807 let mut response = [0u8; 64];
808
809 response[8] = 0b0000_0001; response[9] = 0b0000_0001; response[14] = 0b0100_0000; response[15] = 0x12;
816 response[16] = 0x34;
817 response[17] = 0xAB;
819 response[18] = 0xCD;
820
821 response[25] = 0xFF;
823 response[26] = 0x05;
824
825 let mut pins = vec![PinData::new(); 55];
826 pins[0].pin_function = PinFunction::DigitalInput as u8; pins[8].pin_function = PinFunction::DigitalInput as u8; pins[40].pin_function = PinFunction::AnalogInput as u8; pins[41].pin_function = PinFunction::AnalogInput as u8; let mut encoders = vec![EncoderData::new(); 25];
832
833 apply_device_status_response(&response, &mut pins, &mut encoders);
834
835 assert_eq!(pins[0].digital_value_get, 1);
836 assert_eq!(pins[1].digital_value_get, 0);
837 assert_eq!(pins[8].digital_value_get, 1);
838 assert_eq!(pins[54].digital_value_get, 1);
839
840 assert_eq!(pins[40].analog_value, 0x1234);
841 assert_eq!(pins[41].analog_value, 0xABCD);
842
843 assert_eq!(encoders[0].encoder_value, -1);
844 assert_eq!(encoders[1].encoder_value, 5);
845 assert_eq!(encoders[2].encoder_value, 0);
846 }
847
848 #[test]
849 fn test_compose_pin_function_byte() {
850 use crate::io::private::compose_pin_function_byte;
851
852 assert_eq!(
854 compose_pin_function_byte(PinFunction::DigitalInput, false),
855 0x02
856 );
857 assert_eq!(
858 compose_pin_function_byte(PinFunction::DigitalOutput, false),
859 0x04
860 );
861 assert_eq!(
862 compose_pin_function_byte(PinFunction::TriggeredInput, false),
863 0x20
864 );
865
866 assert_eq!(
868 compose_pin_function_byte(PinFunction::DigitalInput, true),
869 0x82
870 );
871 assert_eq!(
872 compose_pin_function_byte(PinFunction::DigitalOutput, true),
873 0x84
874 );
875 assert_eq!(
876 compose_pin_function_byte(PinFunction::TriggeredInput, true),
877 0xA0
878 );
879 }
880
881 #[test]
882 fn test_decode_pin_function_from_cache() {
883 assert_eq!(
887 decode_pin_function_from_cache(0x82),
888 PinFunction::DigitalInput
889 );
890 assert_eq!(
891 decode_pin_function_from_cache(0x84),
892 PinFunction::DigitalOutput
893 );
894 assert_eq!(
895 decode_pin_function_from_cache(0xA0),
896 PinFunction::TriggeredInput
897 );
898
899 assert_eq!(
901 decode_pin_function_from_cache(0x02),
902 PinFunction::DigitalInput
903 );
904 assert_eq!(
905 decode_pin_function_from_cache(0x00),
906 PinFunction::PinRestricted
907 );
908
909 assert_eq!(
912 decode_pin_function_from_cache(0x80),
913 PinFunction::PinRestricted
914 );
915 }
916
917 #[test]
918 fn test_pin_data_invert_accessors() {
919 let mut pin_data = PinData::new();
920
921 pin_data.pin_function = 0x82; assert!(pin_data.is_inverted());
923 assert_eq!(pin_data.base_function(), PinFunction::DigitalInput);
924 assert!(pin_data.is_digital_input());
926 assert!(!pin_data.is_digital_output());
927
928 pin_data.pin_function = 0x02; assert!(!pin_data.is_inverted());
930 assert_eq!(pin_data.base_function(), PinFunction::DigitalInput);
931 }
932
933 #[test]
934 fn test_parse_bulk_pin_settings_response_preserves_invert_bit() {
935 use crate::io::private::Command;
936
937 let mut response = [0u8; 64];
938 response[1] = Command::InputOutputExtended as u8;
939 response[8] = 0x82; response[9] = 0x04; response[10] = 0xA0; let raw = parse_bulk_pin_settings_response(&response).unwrap();
944 assert_eq!(raw[0], 0x82);
945 assert_eq!(raw[1], 0x04);
946 assert_eq!(raw[2], 0xA0);
947
948 assert_eq!(
951 PinFunction::from_u8(raw[0]).unwrap(),
952 PinFunction::DigitalInput
953 );
954 assert_eq!(
955 PinFunction::from_u8(raw[2]).unwrap(),
956 PinFunction::TriggeredInput
957 );
958 }
959
960 #[test]
961 fn test_parse_bulk_pin_settings_response_rejects_short() {
962 let short = [0u8; 32];
963 assert!(parse_bulk_pin_settings_response(&short).is_err());
964 }
965
966 #[test]
967 fn test_parse_bulk_pin_settings_response_rejects_wrong_command() {
968 let mut response = [0u8; 64];
969 response[1] = 0x12; assert!(parse_bulk_pin_settings_response(&response).is_err());
971 }
972}