|
| 1 | +use std::ops::AddAssign; |
| 2 | + |
| 3 | +use serde::{Deserialize, Deserializer, Serialize}; |
| 4 | +use serde_json::Value; |
| 5 | +use uuid::{Uuid, uuid}; |
| 6 | + |
| 7 | +use super::{DollarRef, ResourceLink}; |
| 8 | + |
| 9 | +#[derive(Debug, Serialize, Deserialize, Clone)] |
| 10 | +pub struct BehaviorScript { |
| 11 | + pub configuration_schema: DollarRef, |
| 12 | + pub description: String, |
| 13 | + #[serde(skip_serializing_if = "Option::is_none")] |
| 14 | + pub max_number_instances: Option<u32>, |
| 15 | + pub metadata: BehaviorScriptMetadata, |
| 16 | + pub state_schema: DollarRef, |
| 17 | + pub supported_features: Vec<String>, |
| 18 | + pub trigger_schema: DollarRef, |
| 19 | + pub version: String, |
| 20 | +} |
| 21 | + |
| 22 | +impl BehaviorScript { |
| 23 | + pub const WAKE_UP_ID: Uuid = uuid!("ff8957e3-2eb9-4699-a0c8-ad2cb3ede704"); |
| 24 | + |
| 25 | + #[must_use] |
| 26 | + pub fn wake_up() -> Self { |
| 27 | + Self { |
| 28 | + configuration_schema: DollarRef { |
| 29 | + dref: Some("basic_wake_up_config.json#".to_string()), |
| 30 | + }, |
| 31 | + description: |
| 32 | + "Get your body in the mood to wake up by fading on the lights in the morning." |
| 33 | + .to_string(), |
| 34 | + max_number_instances: None, |
| 35 | + metadata: BehaviorScriptMetadata { |
| 36 | + name: "Basic wake up routine".to_string(), |
| 37 | + category: "automation".to_string(), |
| 38 | + }, |
| 39 | + state_schema: DollarRef { dref: None }, |
| 40 | + supported_features: vec!["style_sunrise".to_string(), "intensity".to_string()], |
| 41 | + trigger_schema: DollarRef { |
| 42 | + dref: Some("trigger.json#".to_string()), |
| 43 | + }, |
| 44 | + version: "0.0.1".to_string(), |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +#[derive(Debug, Serialize, Deserialize, Clone)] |
| 50 | +pub struct BehaviorScriptMetadata { |
| 51 | + pub name: String, |
| 52 | + pub category: String, |
| 53 | +} |
| 54 | + |
| 55 | +fn deserialize_optional_field<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error> |
| 56 | +where |
| 57 | + D: Deserializer<'de>, |
| 58 | +{ |
| 59 | + Ok(Some(Value::deserialize(deserializer)?)) |
| 60 | +} |
| 61 | + |
| 62 | +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] |
| 63 | +pub struct BehaviorInstance { |
| 64 | + #[serde(default)] |
| 65 | + pub dependees: Vec<BehaviorInstanceDependee>, |
| 66 | + pub enabled: bool, |
| 67 | + pub last_error: Option<String>, |
| 68 | + pub metadata: BehaviorInstanceMetadata, |
| 69 | + pub script_id: Uuid, |
| 70 | + pub status: Option<String>, |
| 71 | + #[serde( |
| 72 | + default, |
| 73 | + deserialize_with = "deserialize_optional_field", |
| 74 | + skip_serializing_if = "Option::is_none" |
| 75 | + )] |
| 76 | + pub state: Option<Value>, |
| 77 | + #[serde(skip_serializing_if = "Option::is_none")] |
| 78 | + pub migrated_from: Option<Value>, |
| 79 | + pub configuration: Value, |
| 80 | +} |
| 81 | + |
| 82 | +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] |
| 83 | +pub enum BehaviorInstanceConfiguration { |
| 84 | + Wakeup(WakeupConfiguration), |
| 85 | +} |
| 86 | + |
| 87 | +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] |
| 88 | +pub struct WakeupConfiguration { |
| 89 | + pub end_brightness: f64, |
| 90 | + pub fade_in_duration: configuration::Duration, |
| 91 | + #[serde(skip_serializing_if = "Option::is_none")] |
| 92 | + pub turn_lights_off_after: Option<configuration::Duration>, |
| 93 | + #[serde(skip_serializing_if = "Option::is_none")] |
| 94 | + pub style: Option<WakeupStyle>, |
| 95 | + pub when: configuration::When, |
| 96 | + #[serde(rename = "where")] |
| 97 | + pub where_field: Vec<configuration::Where>, |
| 98 | +} |
| 99 | + |
| 100 | +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] |
| 101 | +#[serde(rename_all = "lowercase")] |
| 102 | +pub enum WakeupStyle { |
| 103 | + Sunrise, |
| 104 | + Basic, |
| 105 | +} |
| 106 | + |
| 107 | +pub mod configuration { |
| 108 | + use std::time::Duration as StdDuration; |
| 109 | + |
| 110 | + use chrono::Weekday; |
| 111 | + use serde::{Deserialize, Serialize}; |
| 112 | + |
| 113 | + use crate::api::ResourceLink; |
| 114 | + |
| 115 | + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 116 | + pub struct Duration { |
| 117 | + pub seconds: u32, |
| 118 | + } |
| 119 | + |
| 120 | + impl Duration { |
| 121 | + pub fn to_std(&self) -> StdDuration { |
| 122 | + StdDuration::from_secs(self.seconds.into()) |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 127 | + pub struct When { |
| 128 | + pub recurrence_days: Option<Vec<Weekday>>, |
| 129 | + pub time_point: TimePoint, |
| 130 | + } |
| 131 | + |
| 132 | + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 133 | + #[serde(tag = "type", rename_all = "snake_case")] |
| 134 | + pub enum TimePoint { |
| 135 | + Time { time: Time }, |
| 136 | + } |
| 137 | + |
| 138 | + impl TimePoint { |
| 139 | + pub const fn time(&self) -> &Time { |
| 140 | + match self { |
| 141 | + Self::Time { time } => time, |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 147 | + pub struct Time { |
| 148 | + pub hour: u32, |
| 149 | + pub minute: u32, |
| 150 | + } |
| 151 | + |
| 152 | + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
| 153 | + pub struct Where { |
| 154 | + pub group: ResourceLink, |
| 155 | + pub items: Option<Vec<ResourceLink>>, |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] |
| 160 | +pub struct BehaviorInstanceDependee { |
| 161 | + #[serde(rename = "type")] |
| 162 | + pub type_field: Option<String>, |
| 163 | + pub target: ResourceLink, |
| 164 | + pub level: BehaviorInstanceDependeeLevel, |
| 165 | +} |
| 166 | + |
| 167 | +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] |
| 168 | +#[serde(rename_all = "snake_case")] |
| 169 | +pub enum BehaviorInstanceDependeeLevel { |
| 170 | + Critical, |
| 171 | + NonCritical, |
| 172 | +} |
| 173 | + |
| 174 | +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] |
| 175 | +pub struct BehaviorInstanceMetadata { |
| 176 | + pub name: String, |
| 177 | +} |
| 178 | + |
| 179 | +#[derive(Debug, Default, Serialize, Deserialize, Clone)] |
| 180 | +pub struct BehaviorInstanceUpdate { |
| 181 | + pub configuration: Option<Value>, |
| 182 | + pub enabled: Option<bool>, |
| 183 | + pub metadata: Option<BehaviorInstanceMetadata>, |
| 184 | +} |
| 185 | + |
| 186 | +impl BehaviorInstanceUpdate { |
| 187 | + #[must_use] |
| 188 | + pub fn new() -> Self { |
| 189 | + Self::default() |
| 190 | + } |
| 191 | + |
| 192 | + #[must_use] |
| 193 | + pub fn with_metadata(self, metadata: BehaviorInstanceMetadata) -> Self { |
| 194 | + Self { |
| 195 | + metadata: Some(metadata), |
| 196 | + ..self |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + #[must_use] |
| 201 | + pub fn with_enabled(self, enabled: bool) -> Self { |
| 202 | + Self { |
| 203 | + enabled: Some(enabled), |
| 204 | + ..self |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + #[must_use] |
| 209 | + pub fn with_configuration(self, configuration: Value) -> Self { |
| 210 | + Self { |
| 211 | + configuration: Some(configuration), |
| 212 | + ..self |
| 213 | + } |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +impl AddAssign<BehaviorInstanceUpdate> for BehaviorInstance { |
| 218 | + fn add_assign(&mut self, upd: BehaviorInstanceUpdate) { |
| 219 | + if let Some(md) = upd.metadata { |
| 220 | + self.metadata = md; |
| 221 | + } |
| 222 | + |
| 223 | + if let Some(enabled) = upd.enabled { |
| 224 | + self.enabled = enabled; |
| 225 | + } |
| 226 | + |
| 227 | + if let Some(configuration) = upd.configuration { |
| 228 | + self.configuration = configuration; |
| 229 | + } |
| 230 | + } |
| 231 | +} |
0 commit comments