Skip to main content

osdp/command/
buzzer.rs

1//! `osdp_BUZ` (`0x6A`) — reader buzzer control.
2//!
3//! # Spec: §6.10–§6.11, Table 19
4//!
5//! Body is a 5-byte record per buzzer.
6
7use crate::error::Error;
8use crate::payload_util::require_exact_len;
9use alloc::vec::Vec;
10
11/// Buzzer tone code (Table 19).
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[repr(u8)]
14#[allow(missing_docs)]
15pub enum BuzzerTone {
16    /// Deprecated; treated as Off by modern firmware.
17    None = 0x00,
18    Off = 0x01,
19    Default = 0x02,
20}
21
22impl BuzzerTone {
23    /// Parse from byte (returns Off for unrecognized).
24    pub const fn from_byte(b: u8) -> Self {
25        match b {
26            0x00 => Self::None,
27            0x01 => Self::Off,
28            _ => Self::Default,
29        }
30    }
31
32    /// Raw byte.
33    pub const fn as_byte(self) -> u8 {
34        self as u8
35    }
36}
37
38/// `osdp_BUZ` body.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct BuzzerControl {
41    /// Reader number.
42    pub reader: u8,
43    /// Tone code.
44    pub tone: BuzzerTone,
45    /// On time, in 100 ms units.
46    pub on_time: u8,
47    /// Off time, in 100 ms units.
48    pub off_time: u8,
49    /// Number of repetitions.
50    pub count: u8,
51}
52
53impl BuzzerControl {
54    /// Encode.
55    pub fn encode(&self) -> Result<Vec<u8>, Error> {
56        Ok(alloc::vec![
57            self.reader,
58            self.tone.as_byte(),
59            self.on_time,
60            self.off_time,
61            self.count,
62        ])
63    }
64
65    /// Decode.
66    pub fn decode(data: &[u8]) -> Result<Self, Error> {
67        require_exact_len(data, 5, 0x6A)?;
68        Ok(Self {
69            reader: data[0],
70            tone: BuzzerTone::from_byte(data[1]),
71            on_time: data[2],
72            off_time: data[3],
73            count: data[4],
74        })
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn roundtrip() {
84        let body = BuzzerControl {
85            reader: 0x00,
86            tone: BuzzerTone::Default,
87            on_time: 5,
88            off_time: 5,
89            count: 3,
90        };
91        let bytes = body.encode().unwrap();
92        assert_eq!(bytes, [0x00, 0x02, 5, 5, 3]);
93        assert_eq!(BuzzerControl::decode(&bytes).unwrap(), body);
94    }
95
96    #[test]
97    fn decode_rejects_wrong_length() {
98        assert!(matches!(
99            BuzzerControl::decode(&[0; 4]),
100            Err(Error::PayloadLength { code: 0x6A, .. })
101        ));
102    }
103
104    #[test]
105    fn unknown_tone_decodes_to_default() {
106        assert_eq!(BuzzerTone::from_byte(0x99), BuzzerTone::Default);
107    }
108}