use std::ops::{AddAssign, Sub}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::api::{ ColorTemperatureUpdate, ColorUpdate, DimmingUpdate, LightGradientUpdate, On, ResourceLink, }; use crate::date_format; #[derive(Copy, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum SceneActive { Inactive, Static, DynamicPalette, } #[derive(Copy, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct SceneStatus { pub active: SceneActive, #[serde( with = "date_format::utc_ms_opt", default, skip_serializing_if = "Option::is_none" )] pub last_recall: Option>, } #[derive(Copy, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum SceneStatusEnum { Active, Static, DynamicPalette, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Scene { pub actions: Vec, #[serde(default)] pub auto_dynamic: bool, pub group: ResourceLink, pub metadata: SceneMetadata, /* palette: { */ /* color: [], */ /* color_temperature: [ */ /* { */ /* color_temperature: { */ /* mirek: u32 */ /* }, */ /* dimming: { */ /* brightness: f64, */ /* } */ /* } */ /* ], */ /* dimming: [], */ /* effects: [] */ /* }, */ #[serde(default, skip_serializing_if = "Value::is_null")] pub palette: Value, #[serde(default)] pub speed: f64, pub status: Option, #[serde(default)] pub recall: SceneRecall, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SceneAction { #[serde(skip_serializing_if = "Option::is_none")] pub color: Option, #[serde(skip_serializing_if = "Option::is_none")] pub color_temperature: Option, #[serde(skip_serializing_if = "Option::is_none")] pub dimming: Option, #[serde(skip_serializing_if = "Option::is_none")] pub on: Option, #[serde(skip_serializing_if = "Option::is_none")] pub gradient: Option, #[serde(default, skip_serializing_if = "Value::is_null")] pub effects: Value, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SceneActionElement { pub action: SceneAction, pub target: ResourceLink, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct SceneMetadata { #[serde(skip_serializing_if = "Option::is_none")] pub appdata: Option, #[serde(skip_serializing_if = "Option::is_none")] pub image: Option, pub name: String, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] pub struct SceneMetadataUpdate { #[serde(skip_serializing_if = "Option::is_none")] pub appdata: Option, #[serde(skip_serializing_if = "Option::is_none")] pub image: Option, pub name: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct SceneUpdate { #[serde(skip_serializing_if = "Option::is_none")] pub actions: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub recall: Option, #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, #[serde(skip_serializing_if = "Option::is_none")] pub palette: Option, #[serde(skip_serializing_if = "Option::is_none")] pub speed: Option, #[serde(skip_serializing_if = "Option::is_none")] pub auto_dynamic: Option, } impl SceneUpdate { #[must_use] pub fn new() -> Self { Self::default() } #[must_use] pub fn with_actions(self, actions: Option>) -> Self { Self { actions, ..self } } #[must_use] pub fn with_recall_action(self, action: Option) -> Self { Self { recall: Some(SceneRecall { action: match action.map(|a| a.active) { Some(SceneActive::DynamicPalette) => Some(SceneStatusEnum::DynamicPalette), Some(SceneActive::Static) => Some(SceneStatusEnum::Active), Some(SceneActive::Inactive) | None => None, }, duration: None, dimming: None, }), ..self } } } impl AddAssign<&SceneUpdate> for Scene { fn add_assign(&mut self, upd: &SceneUpdate) { if let Some(actions) = &upd.actions { self.actions.clone_from(actions); } if let Some(md) = &upd.metadata { self.metadata += md; } if let Some(palette) = &upd.palette { self.palette.clone_from(palette); } if let Some(speed) = upd.speed { self.speed = speed; } if let Some(auto_dynamic) = upd.auto_dynamic { self.auto_dynamic = auto_dynamic; } } } impl AddAssign<&SceneMetadataUpdate> for SceneMetadata { fn add_assign(&mut self, upd: &SceneMetadataUpdate) { if let Some(appdata) = &upd.appdata { self.appdata = Some(appdata.to_string()); } if let Some(image) = &upd.image { self.image = Some(*image); } if let Some(name) = &upd.name { self.name.clone_from(name); } } } impl Sub<&SceneMetadata> for &SceneMetadata { type Output = SceneMetadataUpdate; fn sub(self, rhs: &SceneMetadata) -> Self::Output { let mut upd = Self::Output::default(); if self != rhs { if self.appdata != rhs.appdata { upd.appdata.clone_from(&rhs.appdata); } if self.image != rhs.image { upd.image.clone_from(&rhs.image); } if self.name != rhs.name { upd.name = Some(rhs.name.clone()); } } upd } } #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct SceneRecall { #[serde(skip_serializing_if = "Option::is_none")] pub action: Option, #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, #[serde(skip_serializing_if = "Option::is_none")] pub dimming: Option, }