use crate::states::{self, ACFanSpeed, ACMode, ACPowerState};
use super::{ac_status_message::ACStatus, ReceivableMessage};
pub fn parse(bytes: &[u8]) -> Result<ReceivableMessage, &'static str> {
let data_length: usize = (((bytes[6] as u16) << 2) | (bytes[7] as u16)) as usize;
if data_length % 8 != 0 {
return Err("invalid message data length to be an ac status message.");
}
let data = bytes.get(8..8 + data_length).unwrap();
let mut ac_units: Vec<ACStatus> = Vec::with_capacity(data_length / 8);
for g in data.chunks(8) {
let power_state: Option<ACPowerState> = match g[0] >> 6 {
0 => Some(states::ACPowerState::Off),
1 => Some(states::ACPowerState::On),
_ => None,
};
let number = g[0] & 0b00111111;
let mode: Option<ACMode> = match g[1] >> 4 {
0 => Some(ACMode::Auto),
1 => Some(ACMode::Heat),
0b10 => Some(ACMode::Dry),
0b11 => Some(ACMode::Fan),
0b100 => Some(ACMode::Cool),
0b101 => Some(ACMode::AutoHeat),
0b110 => Some(ACMode::AutoCool),
_ => None,
};
let fan_speed: Option<ACFanSpeed> = match g[1] & 0b00001111 {
0 => Some(ACFanSpeed::Auto),
1 => Some(ACFanSpeed::Quiet),
0b10 => Some(ACFanSpeed::Low),
0b11 => Some(ACFanSpeed::Medium),
0b100 => Some(ACFanSpeed::High),
0b101 => Some(ACFanSpeed::Powerful),
0b110 => Some(ACFanSpeed::Turbo),
_ => None,
};
let spill = (g[2] >> 7) == 1;
let has_timer = ((g[2] >> 6) & 1) == 1;
let target_setpoint = g[2] & 0b00111111;
let temperature: Option<f64> = {
if g[4] == 0xff {
None
} else {
let g4 = g[4] as u16;
let g5 = g[5] as u16;
let num = (g4 << 3) | (g5 >> 5);
Some(((num as f64) - 500.00) / 10.00)
}
};
let error_code = (g[6] as u16) << 8 | (g[7] as u16);
ac_units.push(ACStatus {
fan_speed,
has_timer,
mode,
number,
power_state,
spill,
target_setpoint,
temperature,
error_code,
})
}
Ok(ReceivableMessage::ACStatuses(ac_units))
}
#[cfg(test)]
mod test {
use crate::{
messaging::{ac_status_message::ACStatus, ReceivableMessage},
states::{ACFanSpeed, ACMode, ACPowerState},
};
use super::parse;
#[test]
fn test_parses_valid() {
let units = parse(&[
0x55, 0x55, 0xb0, 0x80, 0x01, 0x2d, 0x00, 0x10, 0x40, 0x42, 0x1a, 0x00, 0x61, 0x80,
0x00, 0x00, 0x01, 0x00, 0x1a, 0x00, 0x61, 0x80, 0xff, 0xfe, 0xca, 0xcb,
])
.unwrap();
match units {
ReceivableMessage::ACStatuses(m) => {
let valid = vec![
ACStatus {
power_state: Some(ACPowerState::On),
number: 0,
mode: Some(ACMode::Cool),
fan_speed: Some(ACFanSpeed::Low),
spill: false,
has_timer: false,
target_setpoint: 26,
temperature: Some(28.0),
error_code: 0,
},
ACStatus {
power_state: Some(ACPowerState::Off),
number: 1,
mode: Some(ACMode::Auto),
fan_speed: Some(ACFanSpeed::Auto),
spill: false,
has_timer: false,
target_setpoint: 26,
temperature: Some(28.0),
error_code: 65534,
},
];
m.iter()
.zip(valid.iter())
.for_each(|(a, b)| assert_eq!(a, b));
}
_ => panic!("incorrect message type returned."),
}
}
}