1use crate::states::{self, ACFanSpeed, ACMode, ACPowerState};
2
3use super::{ac_status_message::ACStatus, ReceivableMessage};
4
5pub fn parse(bytes: &[u8]) -> Result<ReceivableMessage, &'static str> {
7 let data_length: usize = (((bytes[6] as u16) << 2) | (bytes[7] as u16)) as usize;
8
9 if data_length % 8 != 0 {
11 return Err("invalid message data length to be an ac status message.");
12 }
13
14 let data = bytes.get(8..8 + data_length).unwrap();
16
17 let mut ac_units: Vec<ACStatus> = Vec::with_capacity(data_length / 8);
18
19 for g in data.chunks(8) {
20 let power_state: Option<ACPowerState> = match g[0] >> 6 {
21 0 => Some(states::ACPowerState::Off),
22 1 => Some(states::ACPowerState::On),
23 _ => None,
24 };
25
26 let number = g[0] & 0b00111111;
28
29 let mode: Option<ACMode> = match g[1] >> 4 {
31 0 => Some(ACMode::Auto),
32 1 => Some(ACMode::Heat),
33 0b10 => Some(ACMode::Dry),
34 0b11 => Some(ACMode::Fan),
35 0b100 => Some(ACMode::Cool),
36 0b101 => Some(ACMode::AutoHeat),
37 0b110 => Some(ACMode::AutoCool),
38 _ => None,
39 };
40
41 let fan_speed: Option<ACFanSpeed> = match g[1] & 0b00001111 {
42 0 => Some(ACFanSpeed::Auto),
43 1 => Some(ACFanSpeed::Quiet),
44 0b10 => Some(ACFanSpeed::Low),
45 0b11 => Some(ACFanSpeed::Medium),
46 0b100 => Some(ACFanSpeed::High),
47 0b101 => Some(ACFanSpeed::Powerful),
48 0b110 => Some(ACFanSpeed::Turbo),
49 _ => None,
50 };
51
52 let spill = (g[2] >> 7) == 1;
53 let has_timer = ((g[2] >> 6) & 1) == 1;
54 let target_setpoint = g[2] & 0b00111111;
55
56 let temperature: Option<f64> = {
57 if g[4] == 0xff {
58 None
59 } else {
60 let g4 = g[4] as u16;
61 let g5 = g[5] as u16;
62
63 let num = (g4 << 3) | (g5 >> 5);
64
65 Some(((num as f64) - 500.00) / 10.00)
66 }
67 };
68
69 let error_code = (g[6] as u16) << 8 | (g[7] as u16);
70
71 ac_units.push(ACStatus {
72 fan_speed,
73 has_timer,
74 mode,
75 number,
76 power_state,
77 spill,
78 target_setpoint,
79 temperature,
80 error_code,
81 })
82 }
83
84 Ok(ReceivableMessage::ACStatuses(ac_units))
85}
86
87#[cfg(test)]
88mod test {
89
90 use crate::{
91 messaging::{ac_status_message::ACStatus, ReceivableMessage},
92 states::{ACFanSpeed, ACMode, ACPowerState},
93 };
94
95 use super::parse;
96
97 #[test]
98 fn test_parses_valid() {
99 let units = parse(&[
100 0x55, 0x55, 0xb0, 0x80, 0x01, 0x2d, 0x00, 0x10, 0x40, 0x42, 0x1a, 0x00, 0x61, 0x80,
101 0x00, 0x00, 0x01, 0x00, 0x1a, 0x00, 0x61, 0x80, 0xff, 0xfe, 0xca, 0xcb,
102 ])
103 .unwrap();
104
105 match units {
106 ReceivableMessage::ACStatuses(m) => {
107 let valid = vec![
108 ACStatus {
109 power_state: Some(ACPowerState::On),
110 number: 0,
111 mode: Some(ACMode::Cool),
112 fan_speed: Some(ACFanSpeed::Low),
113 spill: false,
114 has_timer: false,
115 target_setpoint: 26,
116 temperature: Some(28.0),
117 error_code: 0,
118 },
119 ACStatus {
120 power_state: Some(ACPowerState::Off),
121 number: 1,
122 mode: Some(ACMode::Auto),
123 fan_speed: Some(ACFanSpeed::Auto),
124 spill: false,
125 has_timer: false,
126 target_setpoint: 26,
127 temperature: Some(28.0),
128 error_code: 65534,
129 },
130 ];
131
132 m.iter()
133 .zip(valid.iter())
134 .for_each(|(a, b)| assert_eq!(a, b));
135 }
136 _ => panic!("incorrect message type returned."),
137 }
138 }
139}