micropelt 1.1.8

Micropelt device communication definitions
Documentation
use std::fmt;
use std::io::{Error, ErrorKind, Result};

use micropelt_derive::PartialClose;

use crate::utils::{
    bin_to_float_point_five, bin_to_float_point_two_five, float_point_five_to_bin, percent_to_bin,
};

pub const DEFAULT_AMBIENT_TEMPERATURE: f32 = 19.0;
pub const DEFAULT_FLOW_TEMPERATURE: f32 = 55.0;

#[derive(Clone, Debug, PartialClose)]
pub enum DeviceValue {
    User(SetValue),
    FreezeProtect(u8),
    ForcedHeating(u8),
    #[partial_close(resolution = 0.25)]
    DetectingOpeningPoint(f32),
    #[partial_close(resolution = 0.25)]
    SlowHarvesting(f32),
    TemperatureDropDetected,
    ValvePositionInverse(u8),
}

#[derive(Clone, Debug, PartialClose)]
pub enum SetValue {
    ValvePosition(u8),
    #[partial_close(resolution = 0.5)]
    FlowTemperature(f32),
    #[partial_close(resolution = 0.5)]
    AmbientTemperature(f32),
    InverseValvePositionMode(u8),
}

impl fmt::Display for DeviceValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::User(x) => x.fmt(f),
            Self::FreezeProtect(value) => {
                write!(f, "Freeze Protecting (Device opened to {value}%)")
            }
            Self::ForcedHeating(value) => write!(f, "Forced Heating (Device opened to {value}%)"),
            Self::DetectingOpeningPoint(value) => write!(
                f,
                "Detecting Opening Point (maximum flow temperature {value}°C)",
            ),
            Self::SlowHarvesting(value) => {
                write!(f, "Slow Harvesting (maximum flow temperature {value}°C)")
            }
            Self::TemperatureDropDetected => write!(f, "Temperature Drop Detected"),
            Self::ValvePositionInverse(value) => write!(f, "Inverse Valve Position {value}%")
        }
    }
}

impl fmt::Display for SetValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::ValvePosition(value) => write!(f, "Valve Position {value}%"),
            Self::InverseValvePositionMode(value) => write!(f, "Inverse Valve Position {value}%"),
            Self::FlowTemperature(value) => {
                write!(f, "Flow Temperature {value}°C")
            }
            Self::AmbientTemperature(value) => {
                write!(f, "Ambient Temperature {value}°C")
            }
        }
    }
}

impl PartialEq for DeviceValue {
    fn eq(&self, other: &Self) -> bool {
        self.partial_close(other)
    }
}

impl PartialEq for SetValue {
    fn eq(&self, other: &Self) -> bool {
        self.partial_close(other)
    }
}

impl DeviceValue {
    pub(super) fn from_bin(mode: u8, value: u8) -> Result<Self> {
        match mode {
            0 => Ok(Self::User(SetValue::ValvePosition(value))),
            1 => Ok(Self::User(SetValue::FlowTemperature(
                bin_to_float_point_five(value),
            ))),
            2 => Ok(Self::User(SetValue::AmbientTemperature(
                bin_to_float_point_five(value),
            ))),
            3 => Ok(Self::DetectingOpeningPoint(bin_to_float_point_two_five(
                value,
            ))),
            4 => Ok(Self::SlowHarvesting(bin_to_float_point_two_five(value))),
            5 => Ok(Self::TemperatureDropDetected),
            6 => Ok(Self::FreezeProtect(value)),
            7 => Ok(Self::ForcedHeating(value)),
            8 => Ok(Self::ValvePositionInverse(value)),
            _ => Err(Error::new(
                ErrorKind::InvalidInput,
                format!("Unexpected set mode: {mode} (set value {value})"),
            )),
        }
    }
}

impl SetValue {
    pub fn default_radiator() -> Self {
        Self::AmbientTemperature(DEFAULT_AMBIENT_TEMPERATURE)
    }

    pub fn default_domestic_hot_water() -> Self {
        Self::FlowTemperature(DEFAULT_FLOW_TEMPERATURE)
    }

    pub(super) fn value_to_bin(&self) -> Result<u8> {
        match self {
            SetValue::ValvePosition(value) => Ok(percent_to_bin(*value)?),
            SetValue::InverseValvePositionMode(value) => Ok(percent_to_bin(*value)?),
            SetValue::FlowTemperature(value) => Ok(float_point_five_to_bin(*value, 80.0)?),
            SetValue::AmbientTemperature(value) => Ok(float_point_five_to_bin(*value, 40.0)?),
        }
    }

    pub(super) fn user_mode_to_bin(&self) -> u8 {
        match self {
            SetValue::ValvePosition(_) => 0,
            SetValue::FlowTemperature(_) => 1,
            SetValue::AmbientTemperature(_) => 2,
            SetValue::InverseValvePositionMode(_) => 3,
        }
    }

    pub(super) fn safety_mode_to_bin(&self) -> u8 {
        match self {
            SetValue::AmbientTemperature(_) => 0,
            SetValue::FlowTemperature(_) => 1,
            SetValue::ValvePosition(_) => 2,
            SetValue::InverseValvePositionMode(_) => 3,
        }
    }
}

#[cfg(test)]
#[path = "./test_device_value.rs"]
mod test_device_value;