use crate::types::{
ColorTemperature, Dimmer, FadeDuration, HsbColor, PowerState, Scheme, TasmotaDateTime,
WakeupDuration,
};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum StateChange {
Power {
index: u8,
state: PowerState,
},
Dimmer(Dimmer),
HsbColor(HsbColor),
ColorTemperature(ColorTemperature),
Scheme(Scheme),
WakeupDuration(WakeupDuration),
FadeEnabled(bool),
FadeDuration(FadeDuration),
Energy {
power: Option<f32>,
voltage: Option<f32>,
current: Option<f32>,
apparent_power: Option<f32>,
reactive_power: Option<f32>,
power_factor: Option<f32>,
energy_today: Option<f32>,
energy_yesterday: Option<f32>,
energy_total: Option<f32>,
total_start_time: Option<TasmotaDateTime>,
frequency: Option<f32>,
},
Batch(Vec<StateChange>),
}
impl StateChange {
#[must_use]
pub fn power(index: u8, state: PowerState) -> Self {
Self::Power { index, state }
}
#[must_use]
pub fn power_on() -> Self {
Self::Power {
index: 1,
state: PowerState::On,
}
}
#[must_use]
pub fn power_off() -> Self {
Self::Power {
index: 1,
state: PowerState::Off,
}
}
#[must_use]
pub fn dimmer(value: Dimmer) -> Self {
Self::Dimmer(value)
}
#[must_use]
pub fn hsb_color(color: HsbColor) -> Self {
Self::HsbColor(color)
}
#[must_use]
pub fn color_temperature(ct: ColorTemperature) -> Self {
Self::ColorTemperature(ct)
}
#[must_use]
pub fn scheme(scheme: Scheme) -> Self {
Self::Scheme(scheme)
}
#[must_use]
pub fn wakeup_duration(duration: WakeupDuration) -> Self {
Self::WakeupDuration(duration)
}
#[must_use]
pub fn fade_enabled(enabled: bool) -> Self {
Self::FadeEnabled(enabled)
}
#[must_use]
pub fn fade_duration(duration: FadeDuration) -> Self {
Self::FadeDuration(duration)
}
#[must_use]
pub fn energy(power: f32, voltage: f32, current: f32) -> Self {
Self::Energy {
power: Some(power),
voltage: Some(voltage),
current: Some(current),
apparent_power: None,
reactive_power: None,
power_factor: None,
energy_today: None,
energy_yesterday: None,
energy_total: None,
total_start_time: None,
frequency: None,
}
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn energy_full(
power: Option<f32>,
voltage: Option<f32>,
current: Option<f32>,
apparent_power: Option<f32>,
reactive_power: Option<f32>,
power_factor: Option<f32>,
energy_today: Option<f32>,
energy_yesterday: Option<f32>,
energy_total: Option<f32>,
total_start_time: Option<TasmotaDateTime>,
frequency: Option<f32>,
) -> Self {
Self::Energy {
power,
voltage,
current,
apparent_power,
reactive_power,
power_factor,
energy_today,
energy_yesterday,
energy_total,
total_start_time,
frequency,
}
}
#[must_use]
pub fn batch(changes: Vec<StateChange>) -> Self {
Self::Batch(changes)
}
#[must_use]
pub fn is_power(&self) -> bool {
matches!(self, Self::Power { .. })
}
#[must_use]
pub fn is_light(&self) -> bool {
matches!(
self,
Self::Dimmer(_)
| Self::HsbColor(_)
| Self::ColorTemperature(_)
| Self::Scheme(_)
| Self::WakeupDuration(_)
| Self::FadeEnabled(_)
| Self::FadeDuration(_)
)
}
#[must_use]
pub fn is_scheme(&self) -> bool {
matches!(self, Self::Scheme(_))
}
#[must_use]
pub fn is_energy(&self) -> bool {
matches!(self, Self::Energy { .. })
}
#[must_use]
pub fn is_batch(&self) -> bool {
matches!(self, Self::Batch(_))
}
#[must_use]
pub fn change_count(&self) -> usize {
match self {
Self::Batch(changes) => changes.iter().map(Self::change_count).sum(),
_ => 1,
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
#[test]
fn power_change_constructors() {
let change = StateChange::power(1, PowerState::On);
assert!(matches!(
change,
StateChange::Power {
index: 1,
state: PowerState::On
}
));
let on = StateChange::power_on();
assert!(matches!(
on,
StateChange::Power {
index: 1,
state: PowerState::On
}
));
let off = StateChange::power_off();
assert!(matches!(
off,
StateChange::Power {
index: 1,
state: PowerState::Off
}
));
}
#[test]
fn is_power() {
assert!(StateChange::power_on().is_power());
assert!(!StateChange::Dimmer(Dimmer::MAX).is_power());
}
#[test]
fn is_light() {
assert!(StateChange::Dimmer(Dimmer::MAX).is_light());
assert!(StateChange::fade_enabled(true).is_light());
assert!(
StateChange::fade_duration(FadeDuration::new(Duration::from_secs(10)).unwrap())
.is_light()
);
assert!(!StateChange::power_on().is_light());
}
#[test]
fn fade_constructors() {
let enabled = StateChange::fade_enabled(true);
assert!(matches!(enabled, StateChange::FadeEnabled(true)));
let disabled = StateChange::fade_enabled(false);
assert!(matches!(disabled, StateChange::FadeEnabled(false)));
let duration =
StateChange::fade_duration(FadeDuration::new(Duration::from_millis(500)).unwrap());
assert!(matches!(duration, StateChange::FadeDuration(_)));
}
#[test]
fn is_energy() {
assert!(StateChange::energy(100.0, 230.0, 0.5).is_energy());
assert!(!StateChange::power_on().is_energy());
}
#[test]
fn change_count() {
assert_eq!(StateChange::power_on().change_count(), 1);
let batch = StateChange::batch(vec![
StateChange::power_on(),
StateChange::Dimmer(Dimmer::MAX),
]);
assert_eq!(batch.change_count(), 2);
let nested = StateChange::batch(vec![batch, StateChange::power_off()]);
assert_eq!(nested.change_count(), 3);
}
}