1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crate::decode::{
    check_length, decode_temperature, encode_temperature, DecodeError, EncodeError,
};
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};

/// Configuration which determines when the sensor displays a happy face.
#[derive(Clone, Debug, PartialEq)]
pub struct ComfortLevel {
    /// Minimum comfortable temperature in ºC, with 2 decimal places of precision
    pub temperature_min: f32,
    /// Maximum comfortable temperature in ºC, with 2 decimal places of precision
    pub temperature_max: f32,
    /// Minimum comfortable percent humidity.
    pub humidity_min: u8,
    /// Maximum comfortable percent humidity.
    pub humidity_max: u8,
}

impl ComfortLevel {
    pub(crate) fn decode(value: &[u8]) -> Result<ComfortLevel, DecodeError> {
        check_length(value.len(), 6)?;

        let temperature_max = decode_temperature(value[0..2].try_into().unwrap());
        let temperature_min = decode_temperature(value[2..4].try_into().unwrap());
        let humidity_max = value[4];
        let humidity_min = value[5];

        Ok(ComfortLevel {
            temperature_min,
            temperature_max,
            humidity_min,
            humidity_max,
        })
    }

    pub(crate) fn encode(&self) -> Result<[u8; 6], EncodeError> {
        let mut bytes = [0; 6];
        bytes[0..2].copy_from_slice(&encode_temperature(self.temperature_max)?);
        bytes[2..4].copy_from_slice(&encode_temperature(self.temperature_min)?);
        bytes[4] = self.humidity_max;
        bytes[5] = self.humidity_min;
        Ok(bytes)
    }
}

impl Display for ComfortLevel {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(
            f,
            "Temperature: {:.2}–{:.2}ºC Humidity: {:?}–{:?}%",
            self.temperature_min, self.temperature_max, self.humidity_min, self.humidity_max
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn decode_too_short() {
        assert_eq!(
            ComfortLevel::decode(&[0x04, 0x02, 0x03, 0x01, 0x06]),
            Err(DecodeError::WrongLength {
                length: 5,
                expected_length: 6
            })
        );
    }

    #[test]
    fn decode_too_long() {
        assert_eq!(
            ComfortLevel::decode(&[0x04, 0x02, 0x03, 0x01, 0x06, 0x05, 0x07]),
            Err(DecodeError::WrongLength {
                length: 7,
                expected_length: 6
            })
        );
    }

    #[test]
    fn decode_valid() {
        assert_eq!(
            ComfortLevel::decode(&[0x04, 0x02, 0x03, 0x01, 0x06, 0x05]).unwrap(),
            ComfortLevel {
                temperature_min: 2.59,
                temperature_max: 5.16,
                humidity_min: 5,
                humidity_max: 6,
            }
        );
    }

    #[test]
    fn encode_decode() {
        let comfort_level = ComfortLevel {
            temperature_min: -5.1,
            temperature_max: 99.5,
            humidity_min: 3,
            humidity_max: 42,
        };
        assert_eq!(
            ComfortLevel::decode(&comfort_level.encode().unwrap()).unwrap(),
            comfort_level
        );
    }
}