at4_protocol/messaging/
ac_status.rs

1use crate::states::{self, ACFanSpeed, ACMode, ACPowerState};
2
3use super::{ac_status_message::ACStatus, ReceivableMessage};
4
5/// Parse an ac status packet.
6pub 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    // Check length
10    if data_length % 8 != 0 {
11        return Err("invalid message data length to be an ac status message.");
12    }
13
14    // Only grab the data
15    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        // AC unit number
27        let number = g[0] & 0b00111111;
28
29        // AC operating mode
30        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}