use crate::api::types::Config;
use crate::api::types::Session;
use crate::api::errors;
use reqwest;
use crate::api::json::{Building, Device, DeviceLessDetail};
use std::{thread, time};
use chrono::{Utc, TimeZone};
use std::time::{SystemTime, Duration, SystemTimeError};
use crate::api::errors::ApiError;
use std::collections::VecDeque;
use std::thread::sleep;
use std::fmt;
#[derive(Debug)]
pub struct Devices<'a> {
session: &'a Session
}
pub struct DeviceEventsIter<'a> {
devices: &'a Devices<'a>,
previous_device: Option<Device>,
device_id: u32,
building_id: u32,
event_buffer: VecDeque<Event>,
last_request: Option<SystemTime>,
}
pub struct Event {
name: String,
field: String,
previous_value: EventValue,
value: EventValue,
timestamp: i64
}
impl std::fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let timestamp = Utc.timestamp_millis(self.timestamp).to_string();
write!(f, "Devices::Event {} : {}({}) from: {:?}, to {:?}", timestamp, self.name, self.field, self.previous_value, self.value)
}
}
#[derive(Debug)]
pub enum EventValue {
I32(i32),
U32(u32),
String(String),
F32(f32),
Bool(bool)
}
impl <'a> Iterator for DeviceEventsIter<'a> {
type Item = Event;
fn next(&mut self) -> Option<Event> {
return if self.event_buffer.is_empty() {
match self.last_request {
Some(last_request) => {
match last_request.elapsed() {
Ok(elapsed) => {
if elapsed.as_secs() < 60 {
sleep(Duration::new(60 - elapsed.as_secs(), 0));
}
},
Err(_) => (),
}
},
_ => ()
}
self.last_request = Some(SystemTime::now());
match self.devices.detailed(self.device_id, self.building_id) {
Ok(Some(device)) => {
match &self.previous_device {
Some(previous_device) => {
let now = Utc::now();
Devices::compare_devices(&previous_device, &device, &mut self.event_buffer);
self.previous_device = Some(device);
self.next()
},
None => {
self.previous_device = Some(device);
self.next()
}
}
},
_ => self.next() // @TODO Caution - recursion - unsure if this does tail recursion
}
} else {
self.event_buffer.pop_front()
}
}
}
impl <'a> Devices<'a> {
pub fn new(session: &'a Session) -> Devices<'a> {
Devices{
session
}
}
pub fn set_atw(&self, device_data: DeviceLessDetail) -> Result<DeviceLessDetail, errors::ApiError> {
let request_url = format!("{api_base}/Device/SetAtw", api_base = &self.session.config.api_url);
let result = reqwest::Client::new()
.post(&request_url)
.body(serde_json::to_string(&device_data).unwrap())
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header("X-MitsContextKey", self.session.api_key.clone())
.send();
match result {
Ok(mut resp) => {
match resp.json() {
Ok(device) => Ok(device),
Err(e) => {
println!("{}", e);
Err(errors::ApiError::InvalidJsonResponse)
}
}
},
Err(e) => Err(errors::ApiError::InvalidSetAtwResponse)
}
}
pub fn list(&self) -> Result<Vec<Building>, errors::ApiError> {
let request_url = format!("{api_base}/User/ListDevices", api_base = &self.session.config.api_url);
let result = reqwest::Client::new()
.get(&request_url)
.header("Accept", "application/json")
.header("X-MitsContextKey", self.session.api_key.clone())
.send();
match result {
Ok(mut resp) => {
match resp.json() {
Ok(json) => {
Devices::parse_list_devices_response(&self.session.config, json)
},
Err(err) => {
println!("{}", err);
Err(errors::ApiError::InvalidJsonResponse)
}
}
},
Err(err) => Err(errors::ApiError::LoginFailure)
}
}
pub fn show(&self, device_id: u32, building_id: u32) -> Result<DeviceLessDetail, errors::ApiError> {
let request_url = format!("{api_base}/Device/Get?id={device_id}&buildingID={building_id}", api_base = &self.session.config.api_url, device_id = device_id, building_id = building_id);
let result = reqwest::Client::new()
.get(&request_url)
.header("Accept", "application/json")
.header("X-MitsContextKey", self.session.api_key.clone())
.send();
match result {
Ok(mut resp) => {
match resp.json() {
Ok(json) => {
Devices::parse_show_device_response(&self.session.config, json)
},
Err(err) => {
println!("{}", err);
Err(errors::ApiError::InvalidJsonResponse)
}
}
},
Err(err) => {
println!("{}", err);
Err(errors::ApiError::InvalidListResponse)
}
}
}
pub fn detailed(&self, device_id: u32, building_id: u32) -> Result<Option<Device>, errors::ApiError> {
match self.list() {
Err(e) => return Err(e),
Ok(buildings) => {
let building = buildings.into_iter().find_map(|b| {
return b.structure.floors.into_iter().find_map(|f| {
return f.areas.into_iter().find_map(|a| {
return a.devices.into_iter().find_map(|device_header| {
match device_header.device.device_id {
device_id=> Some(device_header.device),
_ => None
}
});
});
});
});
return Ok(building);
}
}
}
pub fn iterate_changes(&self, device_id: u32, building_id: u32) -> Result<DeviceEventsIter, ApiError> {
// First, get an initial snapshot
match self.detailed(device_id, building_id) {
Err(e) => return Err(e),
Ok(option) => {
match option {
None => Err(ApiError::BuildingNotFound),
Some(device) => {
let mut v: VecDeque<Event> = VecDeque::new();
Ok(DeviceEventsIter{devices: &self, previous_device: None, device_id, building_id, event_buffer: v, last_request: None })
}
}
}
}
}
pub fn add_event_unless_equal(collector: &mut VecDeque<Event>, e: Event) -> () {
let a = &e.previous_value;
match(e) {
Event{ value: EventValue::U32(new_value), previous_value: EventValue::U32(previous_value), .. } => { if new_value != previous_value { collector.push_back(e); } }
Event{ value: EventValue::F32(new_value), previous_value: EventValue::F32(previous_value), .. } => { if new_value != previous_value { collector.push_back(e); } }
Event{ value: EventValue::Bool(new_value), previous_value: EventValue::Bool(previous_value), .. } => { if new_value != previous_value { collector.push_back(e); } }
_ => ()
}
}
fn timestamp() -> i64 {
return Utc::now().timestamp_millis();
}
pub fn compare_devices(old_data: &Device, new_data: &Device, collector: &mut VecDeque<Event>) -> () {
Devices::add_event_unless_equal(collector, Event{
name: "HeatPumpFrequencyChanged".to_string(),
field: "heat_pump_frequency".to_string(),
previous_value: EventValue::U32(old_data.heat_pump_frequency),
value: EventValue::U32(new_data.heat_pump_frequency),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "FlowTemperatureChanged".to_string(),
field: "flow_temperature".to_string(),
previous_value: EventValue::F32(old_data.flow_temperature),
value: EventValue::F32(new_data.flow_temperature),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "ReturnTemperatureChanged".to_string(),
field: "return_temperature".to_string(),
previous_value: EventValue::F32(old_data.return_temperature),
value: EventValue::F32(new_data.return_temperature),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "FlowTemperatureZone1Changed".to_string(),
field: "flow_temperature_zone_1".to_string(),
previous_value: EventValue::F32(old_data.flow_temperature_zone_1),
value: EventValue::F32(new_data.flow_temperature_zone_1),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "ReturnTemperatureZone1Changed".to_string(),
field: "return_temperature_zone_1".to_string(),
previous_value: EventValue::F32(old_data.return_temperature_zone_1),
value: EventValue::F32(new_data.return_temperature_zone_1),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "FlowTemperatureBoilerChanged".to_string(),
field: "flow_temperature_boiler".to_string(),
previous_value: EventValue::F32(old_data.flow_temperature_boiler),
value: EventValue::F32(new_data.flow_temperature_boiler),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "ReturnTemperatureBoilerChanged".to_string(),
field: "return_temperature_boiler".to_string(),
previous_value: EventValue::F32(old_data.return_temperature_boiler),
value: EventValue::F32(new_data.return_temperature_boiler),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "RoomTemperatureZone1Changed".to_string(),
field: "room_temperature_zone_1".to_string(),
previous_value: EventValue::F32(old_data.room_temperature_zone_1),
value: EventValue::F32(new_data.room_temperature_zone_1),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "OutdoorTemperatureChanged".to_string(),
field: "outdoor_temperature".to_string(),
previous_value: EventValue::F32(old_data.outdoor_temperature),
value: EventValue::F32(new_data.outdoor_temperature),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "MinSetTemperatureChanged".to_string(),
field: "min_set_temperature".to_string(),
previous_value: EventValue::F32(old_data.min_set_temperature),
value: EventValue::F32(new_data.min_set_temperature),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "MaxSetTemperatureChanged".to_string(),
field: "max_set_temperature".to_string(),
previous_value: EventValue::F32(old_data.max_set_temperature),
value: EventValue::F32(new_data.max_set_temperature),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "CurrentEnergyConsumedChanged".to_string(),
field: "current_energy_consumed".to_string(),
previous_value: EventValue::U32(old_data.current_energy_consumed),
value: EventValue::U32(new_data.current_energy_consumed),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "CurrentEnergyProducedChanged".to_string(),
field: "current_energy_produced".to_string(),
previous_value: EventValue::U32(old_data.current_energy_produced),
value: EventValue::U32(new_data.current_energy_produced),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "CurrentEnergyModeChanged".to_string(),
field: "current_energy_mode".to_string(),
previous_value: EventValue::U32(old_data.current_energy_mode),
value: EventValue::U32(new_data.current_energy_mode),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "HeatingEnergyConsumedRate1Changed".to_string(),
field: "heating_energy_consumed_rate_1".to_string(),
previous_value: EventValue::U32(old_data.heating_energy_consumed_rate_1),
value: EventValue::U32(new_data.heating_energy_consumed_rate_1),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "BoilerStatusChanged".to_string(),
field: "boiler_status".to_string(),
previous_value: EventValue::Bool(old_data.boiler_status),
value: EventValue::Bool(new_data.boiler_status),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "BoosterHeater1StatusChanged".to_string(),
field: "booster_heater_1_status".to_string(),
previous_value: EventValue::Bool(old_data.booster_heater_1_status),
value: EventValue::Bool(new_data.booster_heater_1_status),
timestamp: Devices::timestamp()
});
Devices::add_event_unless_equal(collector, Event{
name: "ImmersionHeaterStatusChanged".to_string(),
field: "immersion_heater_status".to_string(),
previous_value: EventValue::Bool(old_data.immersion_heater_status),
value: EventValue::Bool(new_data.immersion_heater_status),
timestamp: Devices::timestamp()
});
}
fn parse_list_devices_response(config: &Config, message: Vec<Building>) -> Result<Vec<Building>, errors::ApiError> {
Ok(message)
}
fn parse_show_device_response(config: &Config, message: DeviceLessDetail) -> Result<DeviceLessDetail, errors::ApiError> {
Ok(message)
}
fn calc_events(&self, old_data: &Device, new_data: &Device) {
}
pub fn set_flow_temperature(&self, value: f32, device_id: u32, building_id: u32) -> Result<bool, errors::ApiError> {
return match self.show(device_id, building_id) {
Ok(device) => {
let mut new_data = device.clone();
new_data.set_heat_flow_temperature_zone_1 = value;
new_data.effective_flags = 281474976710688;
new_data.has_pending_command = true;
match self.set_atw(new_data) {
Ok(device) => {
Ok(true)
},
Err(e) => Err(e)
}
},
Err(e) => Err(e)
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use mockito::mock;
fn default_device() -> Device {
return Device {
device_type: 0,
heat_pump_frequency: 10,
flow_temperature: 0.0,
return_temperature: 0.0,
flow_temperature_zone_1: 0.0,
return_temperature_zone_1: 0.0,
flow_temperature_zone_2: 0.0,
return_temperature_zone_2: 0.0,
flow_temperature_boiler: 0.0,
return_temperature_boiler: 0.0,
max_set_temperature: 0.0,
min_set_temperature: 0.0,
room_temperature_zone_1: 0.0,
room_temperature_zone_2: 0.0,
outdoor_temperature: 0.0,
current_energy_consumed: 0,
current_energy_produced: 0,
current_energy_mode: 0,
heating_energy_consumed_rate_1: 0,
heating_energy_consumed_rate_2: 0,
boiler_status: false,
booster_heater_1_status: false,
booster_heater_2_status: false,
booster_heater_2_plus_status: false,
immersion_heater_status: false,
water_pump_1_status: false,
water_pump_2_status: false,
water_pump_3_status: false,
valve_status_3_way: false,
valve_status_2_way: false,
water_pump_4_status: false,
valve_status_2_way_2_a: false,
valve_status_2_way_2_b: false,
tank_water_temperature: 0.0,
unit_status: 0,
heating_function_enabled: false,
server_timer_enabled: false,
thermostat_status_zone_1: false,
thermostat_status_zone_2: false,
thermostat_type_zone_1: 0,
thermostat_type_zone_2: 0,
effective_flags: 0,
last_effective_flags: 0,
power: false,
eco_hot_water: false,
operation_mode: 0,
operation_mode_zone_1: 0,
operation_mode_zone_2: 0,
set_temperature_zone_1: 0.0,
set_temperature_zone_2: 0.0,
set_tank_water_temperature: 0.0,
target_hc_temperature_zone_1: 0.0,
target_hc_temperature_zone_2: 0.0,
forced_hot_water_mode: false,
holiday_mode: false,
prohibit_hot_water: false,
prohibit_heating_zone_1: false,
prohibit_heating_zone_2: false,
prohibit_cooling_zone_1: false,
prohibit_cooling_zone_2: false,
server_timer_desired: false,
secondary_zone_heat_curve: false,
set_heat_flow_temperature_zone_1: 0.0,
set_heat_flow_temperature_zone_2: 0.0,
set_cool_flow_temperature_zone_1: 0.0,
set_cool_flow_temperature_zone_2: 0.0,
thermostat_temperature_zone_1: 0.0,
thermostat_temperature_zone_2: 0.0,
decc_report: false,
csv_report_1_min: false,
zone_2_master: false,
daily_energy_consumed_date: "".to_string(),
daily_energy_produced_date: "".to_string(),
cooling_energy_consumed_rate_1: 0,
cooling_energy_consumed_rate_2: 0,
hot_water_energy_consumed_rate_1: 0,
hot_water_energy_consumed_rate_2: 0,
heating_energy_produced_rate_1: 0,
heating_energy_produced_rate_2: 0,
cooling_energy_produced_rate_1: 0,
cooling_energy_produced_rate_2: 0,
hot_water_energy_produced_rate_1: 0,
hot_water_energy_produced_rate_2: 0,
error_code_2_digit: 0,
send_special_functions: 0,
request_special_functions: 0,
special_functions_state: 0,
pending_send_special_functions: 0,
pending_request_special_functions: 0,
has_zone_2: false,
has_simplified_zone_2: false,
can_heat: false,
can_cool: false,
has_hot_water_tank: false,
can_set_tank_temperature: false,
can_set_eco_hot_water: false,
has_energy_consumed_meter: false,
has_energy_produced_meter: false,
can_measure_energy_produced: false,
can_measure_energy_consumed: false,
zone_1_in_room_mode: false,
zone_2_in_room_mode: false,
zone_1_in_heat_mode: false,
zone_2_in_heat_mode: false,
zone_1_in_cool_mode: false,
zone_2_in_cool_mode: false,
allow_dual_room_temperature: false,
has_eco_cute_settings: false,
has_ftc_45_settings: false,
can_estimate_energy_usage: false,
can_use_room_temperature_cooling: false,
is_ftc_model_supported: false,
max_tank_temperature: 0.0,
idle_zone_1: false,
idle_zone_2: false,
min_pcycle: 0,
max_pcycle: 0,
max_outdoor_units: 0,
max_indoor_units: 0,
max_temperature_control_units: 0,
device_id: 0,
mac_address: "".to_string(),
serial_number: "".to_string(),
time_zone_id: 0,
diagnostic_mode: 0,
diagnostic_end_date: None,
expected_command: 0,
owner: None,
detected_country: None,
adaptor_type: 0,
firmware_deployment: None,
firmware_update_aborted: false,
linked_device: None,
wifi_signal_strength: 0,
wifi_adapter_status: "".to_string(),
position: "".to_string(),
p_cycle: 0,
record_num_max: 0,
last_time_stamp: "".to_string(),
error_code: 0,
has_error: false,
last_reset: "".to_string(),
flash_writes: 0,
scene: None,
temperature_increment_override: 0,
ssl_expiration_date: "".to_string(),
sp_timeout: 0,
passcode: None,
server_communication_disabled: false,
consecutive_upload_errors: 0,
do_not_respond_after: None,
owner_role_access_level: 0,
owner_country: 0,
hide_energy_report: false,
rate_1_start_time: None,
rate_2_start_time: None,
protocol_version: 0,
unit_version: 0,
firmware_app_version: 0,
firmware_web_version: 0,
firmware_wlan_version: 0,
effective_p_cycle: 0,
has_error_messages: false,
offline: false
};
}
#[test]
fn test_set_flow_temperature() {
let raw_get_response = r#"{"EffectiveFlags":0,"LocalIPAddress":null,"SetTemperatureZone1":21.0,"SetTemperatureZone2":20.0,"RoomTemperatureZone1":19.5,"RoomTemperatureZone2":-39.0,"OperationMode":2,"OperationModeZone1":0,"OperationModeZone2":2,"WeatherObservations":[{"Date":"2019-12-03T18:00:00","Sunrise":"2019-12-03T07:58:00","Sunset":"2019-12-03T15:53:00","Condition":116,"ID":247591914,"Humidity":91,"Temperature":3,"Icon":"wsymbol_0008_clear_sky_night","ConditionName":"Partly Cloudy","Day":2,"WeatherType":0},{"Date":"2019-12-04T03:00:00","Sunrise":"2019-12-04T07:59:00","Sunset":"2019-12-04T15:52:00","Condition":116,"ID":247591917,"Humidity":90,"Temperature":2,"Icon":"wsymbol_0008_clear_sky_night","ConditionName":"Partly Cloudy","Day":2,"WeatherType":2},{"Date":"2019-12-04T15:00:00","Sunrise":"2019-12-04T07:59:00","Sunset":"2019-12-04T15:52:00","Condition":116,"ID":247591921,"Humidity":87,"Temperature":6,"Icon":"wsymbol_0002_sunny_intervals","ConditionName":"Partly Cloudy","Day":3,"WeatherType":1},{"Date":"2019-12-05T03:00:00","Sunrise":"2019-12-05T08:01:00","Sunset":"2019-12-05T15:52:00","Condition":143,"ID":247591925,"Humidity":95,"Temperature":2,"Icon":"wsymbol_0006_mist","ConditionName":"Mist","Day":3,"WeatherType":2}],"ErrorMessage":null,"ErrorCode":8000,"SetHeatFlowTemperatureZone1":50.0,"SetHeatFlowTemperatureZone2":20.0,"SetCoolFlowTemperatureZone1":20.0,"SetCoolFlowTemperatureZone2":20.0,"HCControlType":1,"TankWaterTemperature":44.5,"SetTankWaterTemperature":48.0,"ForcedHotWaterMode":false,"UnitStatus":0,"OutdoorTemperature":4.0,"EcoHotWater":false,"Zone1Name":null,"Zone2Name":null,"HolidayMode":false,"ProhibitZone1":false,"ProhibitZone2":true,"ProhibitHotWater":false,"TemperatureIncrementOverride":0,"IdleZone1":false,"IdleZone2":true,"DeviceID":147672,"DeviceType":1,"LastCommunication":"2019-12-03T17:49:37.293","NextCommunication":"2019-12-03T17:50:37.293","Power":true,"HasPendingCommand":false,"Offline":false,"Scene":null,"SceneOwner":null}"#;
let raw_post_response = r#"{"EffectiveFlags":0,"LocalIPAddress":null,"SetTemperatureZone1":21.0,"SetTemperatureZone2":20.0,"RoomTemperatureZone1":19.5,"RoomTemperatureZone2":-39.0,"OperationMode":2,"OperationModeZone1":0,"OperationModeZone2":2,"WeatherObservations":[{"Date":"2019-12-03T18:00:00","Sunrise":"2019-12-03T07:58:00","Sunset":"2019-12-03T15:53:00","Condition":116,"ID":247591914,"Humidity":91,"Temperature":3,"Icon":"wsymbol_0008_clear_sky_night","ConditionName":"Partly Cloudy","Day":2,"WeatherType":0},{"Date":"2019-12-04T03:00:00","Sunrise":"2019-12-04T07:59:00","Sunset":"2019-12-04T15:52:00","Condition":116,"ID":247591917,"Humidity":90,"Temperature":2,"Icon":"wsymbol_0008_clear_sky_night","ConditionName":"Partly Cloudy","Day":2,"WeatherType":2},{"Date":"2019-12-04T15:00:00","Sunrise":"2019-12-04T07:59:00","Sunset":"2019-12-04T15:52:00","Condition":116,"ID":247591921,"Humidity":87,"Temperature":6,"Icon":"wsymbol_0002_sunny_intervals","ConditionName":"Partly Cloudy","Day":3,"WeatherType":1},{"Date":"2019-12-05T03:00:00","Sunrise":"2019-12-05T08:01:00","Sunset":"2019-12-05T15:52:00","Condition":143,"ID":247591925,"Humidity":95,"Temperature":2,"Icon":"wsymbol_0006_mist","ConditionName":"Mist","Day":3,"WeatherType":2}],"ErrorMessage":null,"ErrorCode":8000,"SetHeatFlowTemperatureZone1":55.0,"SetHeatFlowTemperatureZone2":20.0,"SetCoolFlowTemperatureZone1":20.0,"SetCoolFlowTemperatureZone2":20.0,"HCControlType":1,"TankWaterTemperature":44.5,"SetTankWaterTemperature":48.0,"ForcedHotWaterMode":false,"UnitStatus":0,"OutdoorTemperature":4.0,"EcoHotWater":false,"Zone1Name":null,"Zone2Name":null,"HolidayMode":false,"ProhibitZone1":false,"ProhibitZone2":true,"ProhibitHotWater":false,"TemperatureIncrementOverride":0,"IdleZone1":false,"IdleZone2":true,"DeviceID":147672,"DeviceType":1,"LastCommunication":"2019-12-03T17:49:37.293","NextCommunication":"2019-12-03T17:50:37.293","Power":true,"HasPendingCommand":false,"Offline":false,"Scene":null,"SceneOwner":null}"#;
let config = Config::new(&mockito::server_url(), "testuser", "testpassword");
let session = Session { config, api_key: "fakeapikey".to_string() };
let get_mock = mock("GET", "/Device/Get?id=147672&buildingID=88314")
.with_status(200)
.with_body(raw_get_response)
.create();
let post_mock = mock("POST", "/Device/SetAtw")
.with_status(200)
.with_body(raw_post_response)
.create();
match session.devices().set_flow_temperature(55.0, 147672, 88314) {
Ok(v) => assert!(v, "Return value must be true"),
Err(e) => assert!(false, format!("Return value must be true, but an error occured - {:?}", e))
}
}
#[test]
fn test_compare_devices_heat_pump_frequency() {
let device1 = Device {
heat_pump_frequency: 10,
..default_device()
};
let device2 = Device {
heat_pump_frequency: 20,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::U32(p), value: EventValue::U32(v), ..} = x {
*name == "HeatPumpFrequencyChanged".to_string() && *field == "heat_pump_frequency".to_string() && *v == 20 && *p == 10
} else {
false
}
});
assert!(found, "HeatPumpFrequencyChanged event not found");
}
#[test]
fn test_compare_devices_flow_return_temperatures() {
let device1 = Device {
flow_temperature: 25.0,
return_temperature: 22.0,
..default_device()
};
let device2 = Device {
flow_temperature: 30.0,
return_temperature: 29.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "FlowTemperatureChanged".to_string() && *field == "flow_temperature".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "FlowTemperatureChanged event not found");
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "ReturnTemperatureChanged".to_string() && *field == "return_temperature".to_string() && *v == 29.0 && *p == 22.0
} else {
false
}
});
assert!(found, "ReturnTemperatureChanged event Not found");
}
#[test]
fn test_compare_devices_flow_return_temperature_zone_1s() {
let device1 = Device {
flow_temperature_zone_1: 25.0,
return_temperature_zone_1: 22.0,
..default_device()
};
let device2 = Device {
flow_temperature_zone_1: 30.0,
return_temperature_zone_1: 29.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "FlowTemperatureZone1Changed".to_string() && *field == "flow_temperature_zone_1".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "FlowTemperatureZone1Changed event not found");
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "ReturnTemperatureZone1Changed".to_string() && *field == "return_temperature_zone_1".to_string() && *v == 29.0 && *p == 22.0
} else {
false
}
});
assert!(found, "ReturnTemperatureZone1Changed event Not found");
}
#[test]
fn test_compare_devices_flow_return_temperature_boiler() {
let device1 = Device {
flow_temperature_boiler: 25.0,
return_temperature_boiler: 22.0,
..default_device()
};
let device2 = Device {
flow_temperature_boiler: 30.0,
return_temperature_boiler: 29.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "FlowTemperatureBoilerChanged".to_string() && *field == "flow_temperature_boiler".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "FlowTemperatureBoilerChanged event not found");
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "ReturnTemperatureBoilerChanged".to_string() && *field == "return_temperature_boiler".to_string() && *v == 29.0 && *p == 22.0
} else {
false
}
});
assert!(found, "ReturnTemperatureBoilerChanged event Not found");
}
#[test]
fn test_compare_min_max_set_temperature() {
let device1 = Device {
min_set_temperature: 25.0,
max_set_temperature: 22.0,
..default_device()
};
let device2 = Device {
min_set_temperature: 30.0,
max_set_temperature: 29.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "MinSetTemperatureChanged".to_string() && *field == "min_set_temperature".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "MinSetTemperatureChanged event not found");
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "MaxSetTemperatureChanged".to_string() && *field == "max_set_temperature".to_string() && *v == 29.0 && *p == 22.0
} else {
false
}
});
assert!(found, "MaxSetTemperatureChanged event Not found");
}
#[test]
fn test_compare_energy_consumed_produced() {
let device1 = Device {
current_energy_consumed: 250,
current_energy_produced: 220,
..default_device()
};
let device2 = Device {
current_energy_consumed: 300,
current_energy_produced: 290,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::U32(p), value: EventValue::U32(v), ..} = x {
*name == "CurrentEnergyConsumedChanged".to_string() && *field == "current_energy_consumed".to_string() && *v == 300 && *p == 250
} else {
false
}
});
assert!(found, "CurrentEnergyConsumed event not found");
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::U32(p), value: EventValue::U32(v), ..} = x {
*name == "CurrentEnergyProducedChanged".to_string() && *field == "current_energy_produced".to_string() && *v == 290 && *p == 220
} else {
false
}
});
assert!(found, "CurrentEnergyProducedChanged event Not found");
}
#[test]
fn test_compare_devices_room_temperature_zone_1() {
let device1 = Device {
room_temperature_zone_1: 25.0,
..default_device()
};
let device2 = Device {
room_temperature_zone_1: 30.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "RoomTemperatureZone1Changed".to_string() && *field == "room_temperature_zone_1".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "RoomTemperatureZone1Changed event not found");
}
#[test]
fn test_compare_outdoor_temperature() {
let device1 = Device {
outdoor_temperature: 25.0,
..default_device()
};
let device2 = Device {
outdoor_temperature: 30.0,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::F32(p), value: EventValue::F32(v), ..} = x {
*name == "OutdoorTemperatureChanged".to_string() && *field == "outdoor_temperature".to_string() && *v == 30.0 && *p == 25.0
} else {
false
}
});
assert!(found, "OutdoorTemperatureChanged event not found");
}
#[test]
fn test_compare_current_energy_mode() {
let device1 = Device {
current_energy_mode: 250,
..default_device()
};
let device2 = Device {
current_energy_mode: 300,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::U32(p), value: EventValue::U32(v), ..} = x {
*name == "CurrentEnergyModeChanged".to_string() && *field == "current_energy_mode".to_string() && *v == 300 && *p == 250
} else {
false
}
});
assert!(found, "CurrentEnergyModeChanged event not found");
}
#[test]
fn test_compare_heating_energy_consumed_rate_1() {
let device1 = Device {
heating_energy_consumed_rate_1: 250,
..default_device()
};
let device2 = Device {
heating_energy_consumed_rate_1: 300,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::U32(p), value: EventValue::U32(v), ..} = x {
*name == "HeatingEnergyConsumedRate1Changed".to_string() && *field == "heating_energy_consumed_rate_1".to_string() && *v == 300 && *p == 250
} else {
false
}
});
assert!(found, "HeatingEnergyConsumedRate1Changed event not found");
}
#[test]
fn test_compare_boiler_status() {
let device1 = Device {
boiler_status: false,
..default_device()
};
let device2 = Device {
boiler_status: true,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::Bool(p), value: EventValue::Bool(v), ..} = x {
*name == "BoilerStatusChanged".to_string() && *field == "boiler_status".to_string() && *v == true && *p == false
} else {
false
}
});
assert!(found, "BoilerStatusChanged event not found");
}
#[test]
fn test_booster_heater_1_status() {
let device1 = Device {
booster_heater_1_status: false,
..default_device()
};
let device2 = Device {
booster_heater_1_status: true,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::Bool(p), value: EventValue::Bool(v), ..} = x {
*name == "BoosterHeater1StatusChanged".to_string() && *field == "booster_heater_1_status".to_string() && *v == true && *p == false
} else {
false
}
});
assert!(found, "BoosterHeater1StatusChanged event not found");
}
#[test]
fn test_compare_immersion_heater_status() {
let device1 = Device {
immersion_heater_status: false,
..default_device()
};
let device2 = Device {
immersion_heater_status: true,
..default_device()
};
let mut collector: VecDeque<Event> = VecDeque::new();
Devices::compare_devices(&device1, &device2, &mut collector);
let found = collector.iter().any(|x| {
if let Event {name: name, field: field, previous_value: EventValue::Bool(p), value: EventValue::Bool(v), ..} = x {
*name == "ImmersionHeaterStatusChanged".to_string() && *field == "immersion_heater_status".to_string() && *v == true && *p == false
} else {
false
}
});
assert!(found, "ImmertionHeaterStatusChanged event not found");
}
}