1use crate::error::Error;
14use alloc::vec::Vec;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18#[repr(u8)]
19#[allow(missing_docs)]
20pub enum OutputControlCode {
21 Nop = 0x00,
22 PermanentOffAbortTimed = 0x01,
23 PermanentOnAbortTimed = 0x02,
24 PermanentOffAllowTimed = 0x03,
25 PermanentOnAllowTimed = 0x04,
26 TemporaryOnResume = 0x05,
27 TemporaryOffResume = 0x06,
28}
29
30impl OutputControlCode {
31 pub const fn from_byte(b: u8) -> Result<Self, Error> {
33 Ok(match b {
34 0x00 => Self::Nop,
35 0x01 => Self::PermanentOffAbortTimed,
36 0x02 => Self::PermanentOnAbortTimed,
37 0x03 => Self::PermanentOffAllowTimed,
38 0x04 => Self::PermanentOnAllowTimed,
39 0x05 => Self::TemporaryOnResume,
40 0x06 => Self::TemporaryOffResume,
41 _ => {
42 return Err(Error::MalformedPayload {
43 code: 0x68,
44 reason: "unknown output control code",
45 });
46 }
47 })
48 }
49
50 pub const fn as_byte(self) -> u8 {
52 self as u8
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct OutputRecord {
59 pub output: u8,
61 pub code: OutputControlCode,
63 pub timer: u16,
65}
66
67impl OutputRecord {
68 pub const WIRE_LEN: usize = 4;
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct OutputControl {
75 pub records: Vec<OutputRecord>,
77}
78
79impl OutputControl {
80 pub fn new(records: Vec<OutputRecord>) -> Self {
82 Self { records }
83 }
84
85 pub fn encode(&self) -> Result<Vec<u8>, Error> {
87 if self.records.is_empty() {
88 return Err(Error::MalformedPayload {
89 code: 0x68,
90 reason: "OUT requires at least one record",
91 });
92 }
93 let mut out = Vec::with_capacity(self.records.len() * OutputRecord::WIRE_LEN);
94 for r in &self.records {
95 out.push(r.output);
96 out.push(r.code.as_byte());
97 out.extend_from_slice(&r.timer.to_le_bytes());
98 }
99 Ok(out)
100 }
101
102 pub fn decode(data: &[u8]) -> Result<Self, Error> {
104 if data.is_empty() || data.len() % OutputRecord::WIRE_LEN != 0 {
105 return Err(Error::MalformedPayload {
106 code: 0x68,
107 reason: "OUT payload must be a multiple of 4 bytes",
108 });
109 }
110 let mut records = Vec::with_capacity(data.len() / OutputRecord::WIRE_LEN);
111 for chunk in data.chunks_exact(OutputRecord::WIRE_LEN) {
112 records.push(OutputRecord {
113 output: chunk[0],
114 code: OutputControlCode::from_byte(chunk[1])?,
115 timer: u16::from_le_bytes([chunk[2], chunk[3]]),
116 });
117 }
118 Ok(Self { records })
119 }
120}