use std::sync::Mutex;
use crate::device::Device;
use crate::observing_conditions::ObservingConditions;
use crate::types::{AlpacaError, AlpacaResult, DeviceType};
const SENSORS: &[&str] = &[
"CloudCover",
"DewPoint",
"Humidity",
"Pressure",
"RainRate",
"SkyBrightness",
"SkyQuality",
"SkyTemperature",
"StarFWHM",
"Temperature",
"WindDirection",
"WindGust",
"WindSpeed",
];
pub struct MockObservingConditions {
connected: Mutex<bool>,
average_period: Mutex<f64>,
}
impl Default for MockObservingConditions {
fn default() -> Self {
Self::new()
}
}
impl MockObservingConditions {
pub fn new() -> Self {
Self {
connected: Mutex::new(false),
average_period: Mutex::new(0.0),
}
}
fn validate_sensor(name: &str) -> AlpacaResult<()> {
if name.is_empty() {
return Err(AlpacaError::InvalidValue(
"Sensor name must not be empty".into(),
));
}
if SENSORS.iter().any(|s| s.eq_ignore_ascii_case(name)) {
Ok(())
} else {
Err(AlpacaError::InvalidValue(format!("Unknown sensor: {name}")))
}
}
}
impl Device for MockObservingConditions {
fn static_name(&self) -> &str {
"Mock ObservingConditions"
}
fn unique_id(&self) -> &str {
"mock-oc-001"
}
fn device_type(&self) -> DeviceType {
DeviceType::ObservingConditions
}
fn connected(&self) -> AlpacaResult<bool> {
Ok(*self.connected.lock().unwrap())
}
fn set_connected(&self, v: bool) -> AlpacaResult<()> {
*self.connected.lock().unwrap() = v;
Ok(())
}
fn connecting(&self) -> AlpacaResult<bool> {
Ok(false)
}
fn connect(&self) -> AlpacaResult<()> {
*self.connected.lock().unwrap() = true;
Ok(())
}
fn disconnect(&self) -> AlpacaResult<()> {
*self.connected.lock().unwrap() = false;
Ok(())
}
fn description(&self) -> AlpacaResult<String> {
Ok("Mock ObservingConditions with all 13 weather sensors".into())
}
fn driver_info(&self) -> AlpacaResult<String> {
Ok("ascom-alpaca-core mock".into())
}
fn driver_version(&self) -> AlpacaResult<String> {
Ok(env!("CARGO_PKG_VERSION").into())
}
fn interface_version(&self) -> AlpacaResult<i32> {
Ok(2)
}
fn name(&self) -> AlpacaResult<String> {
Ok("Mock ObservingConditions".into())
}
fn supported_actions(&self) -> AlpacaResult<Vec<String>> {
Ok(vec![])
}
fn device_state(&self) -> AlpacaResult<Vec<crate::device::common::DeviceStateItem>> {
use crate::device::common::DeviceStateItem;
Ok(vec![
DeviceStateItem {
name: "CloudCover".into(),
value: serde_json::json!(20.0),
},
DeviceStateItem {
name: "DewPoint".into(),
value: serde_json::json!(7.0),
},
DeviceStateItem {
name: "Humidity".into(),
value: serde_json::json!(60.0),
},
DeviceStateItem {
name: "Pressure".into(),
value: serde_json::json!(1013.25),
},
DeviceStateItem {
name: "RainRate".into(),
value: serde_json::json!(0.0),
},
DeviceStateItem {
name: "SkyBrightness".into(),
value: serde_json::json!(21.5),
},
DeviceStateItem {
name: "SkyQuality".into(),
value: serde_json::json!(21.5),
},
DeviceStateItem {
name: "SkyTemperature".into(),
value: serde_json::json!(-20.0),
},
DeviceStateItem {
name: "StarFWHM".into(),
value: serde_json::json!(2.5),
},
DeviceStateItem {
name: "Temperature".into(),
value: serde_json::json!(15.0),
},
DeviceStateItem {
name: "WindDirection".into(),
value: serde_json::json!(180.0),
},
DeviceStateItem {
name: "WindGust".into(),
value: serde_json::json!(8.0),
},
DeviceStateItem {
name: "WindSpeed".into(),
value: serde_json::json!(5.0),
},
])
}
}
impl ObservingConditions for MockObservingConditions {
fn cloud_cover(&self) -> AlpacaResult<f64> {
Ok(20.0)
} fn dew_point(&self) -> AlpacaResult<f64> {
Ok(7.0)
} fn humidity(&self) -> AlpacaResult<f64> {
Ok(60.0)
} fn pressure(&self) -> AlpacaResult<f64> {
Ok(1013.25)
} fn rain_rate(&self) -> AlpacaResult<f64> {
Ok(0.0)
} fn sky_brightness(&self) -> AlpacaResult<f64> {
Ok(21.5)
} fn sky_quality(&self) -> AlpacaResult<f64> {
Ok(21.5)
} fn sky_temperature(&self) -> AlpacaResult<f64> {
Ok(-20.0)
} fn star_fwhm(&self) -> AlpacaResult<f64> {
Ok(2.5)
} fn temperature(&self) -> AlpacaResult<f64> {
Ok(15.0)
} fn wind_direction(&self) -> AlpacaResult<f64> {
Ok(180.0)
} fn wind_gust(&self) -> AlpacaResult<f64> {
Ok(8.0)
} fn wind_speed(&self) -> AlpacaResult<f64> {
Ok(5.0)
}
fn average_period(&self) -> AlpacaResult<f64> {
Ok(*self.average_period.lock().unwrap())
}
fn set_average_period(&self, hours: f64) -> AlpacaResult<()> {
if hours < 0.0 {
return Err(AlpacaError::InvalidValue(format!(
"AveragePeriod must be >= 0, got {hours}"
)));
}
*self.average_period.lock().unwrap() = hours;
Ok(())
}
fn sensor_description(&self, property_name: &str) -> AlpacaResult<String> {
Self::validate_sensor(property_name)?;
Ok(format!("Mock {property_name} sensor"))
}
fn time_of_latest_update(&self, property_name: &str) -> AlpacaResult<f64> {
if !property_name.is_empty() {
Self::validate_sensor(property_name)?;
}
Ok(0.0)
}
fn refresh(&self) -> AlpacaResult<()> {
Ok(())
}
}