diff --git a/backend/src/tasks/lights.rs b/backend/src/tasks/lights.rs index b35ab5e..51459a1 100644 --- a/backend/src/tasks/lights.rs +++ b/backend/src/tasks/lights.rs @@ -10,7 +10,7 @@ use tokio::select; use crate::persistence::PersistenceFile; use crate::State; -use self::scripts::{LightScript, Party, Waker}; +use self::scripts::{LightScript, Party}; pub mod scripts; @@ -35,10 +35,6 @@ pub async fn lights_task(state: &State) { .expect("Failed to launch bulb manager"); let mut scripts: HashMap> = Default::default(); - scripts.insert( - "waker".to_string(), - Box::new(Waker::create(manager.clone())), - ); scripts.insert( "party".to_string(), Box::new(Party::create(manager.clone())), diff --git a/backend/src/tasks/lights/effect.rs b/backend/src/tasks/lights/effect.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/src/tasks/lights/effect.rs @@ -0,0 +1 @@ + diff --git a/backend/src/tasks/lights/scripts/mod.rs b/backend/src/tasks/lights/scripts/mod.rs index e7c730d..0ccdc51 100644 --- a/backend/src/tasks/lights/scripts/mod.rs +++ b/backend/src/tasks/lights/scripts/mod.rs @@ -2,9 +2,7 @@ use common::{BulbPrefs, Param}; use lighter_lib::BulbId; mod party; -mod waker; pub use party::Party; -pub use waker::Waker; pub trait LightScript { fn get_params(&mut self, bulb: &BulbId) -> BulbPrefs; diff --git a/backend/src/tasks/lights/scripts/waker.rs b/backend/src/tasks/lights/scripts/waker.rs deleted file mode 100644 index 34419d1..0000000 --- a/backend/src/tasks/lights/scripts/waker.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::{collections::HashMap, time::Duration}; - -use chrono::{DateTime, Datelike, Local, NaiveTime, Weekday}; -use common::{BulbPrefs, Param}; -use lighter_lib::{BulbColor, BulbId}; -use lighter_manager::manager::{BulbCommand, BulbManager, BulbSelector}; -use tokio::{spawn, time::sleep}; - -use crate::util::DeadMansHandle; - -use super::LightScript; - -pub struct Waker { - manager: BulbManager, - wake_times: HashMap>, - wake_tasks: HashMap<(BulbId, Weekday), DeadMansHandle>, -} - -impl Waker { - pub fn create(manager: BulbManager) -> Self { - Waker { - manager, - wake_times: Default::default(), - wake_tasks: Default::default(), - } - } -} - -const TIME_FMT: &str = "%H:%M"; -const WAKE_TARGET_BRIGHTNESS: u8 = 75; -const WAKE_TARGET_TEMPERATURE: u8 = 60; - -impl LightScript for Waker { - fn get_params(&mut self, bulb: &BulbId) -> BulbPrefs { - let settings = self.wake_times.entry(bulb.clone()).or_default(); - - let kvs = DAYS_OF_WEEK - .iter() - .map(|day| { - let time = match settings.get(day) { - Some(time) => time.format(TIME_FMT).to_string(), - None => String::new(), - }; - (format!("{day:?}"), Param::String(time)) - }) - .collect(); - - BulbPrefs { kvs } - } - - fn set_param(&mut self, bulb: &BulbId, name: &str, time: super::Param) { - let settings = self.wake_times.entry(bulb.clone()).or_default(); - - let Param::String(time) = time else { - error!("invalit param kind"); - return; - }; - - let time = NaiveTime::parse_from_str(&time, TIME_FMT) - .map(Some) - .unwrap_or(None); - - let weekday = match name { - "Mon" => Weekday::Mon, - "Tue" => Weekday::Tue, - "Wed" => Weekday::Wed, - "Thu" => Weekday::Thu, - "Fri" => Weekday::Fri, - "Sat" => Weekday::Sat, - "Sun" => Weekday::Sun, - _ => { - error!("invalit param name"); - return; - } - }; - - let Some(time) = time else { - settings.remove(&weekday); - self.wake_tasks.remove(&(bulb.clone(), weekday)); - return; - }; - - settings.insert(weekday, time); - let task = spawn(wake_task(self.manager.clone(), bulb.clone(), weekday, time)); - self.wake_tasks - .insert((bulb.clone(), weekday), DeadMansHandle(task.abort_handle())); - } -} - -const DAYS_OF_WEEK: &[Weekday] = &[ - Weekday::Mon, - Weekday::Tue, - Weekday::Wed, - Weekday::Thu, - Weekday::Fri, - Weekday::Sat, - Weekday::Sun, -]; - -async fn wake_task(manager: BulbManager, id: BulbId, day: Weekday, time: NaiveTime) { - let mut alarm = next_alarm(Local::now(), day, time); - - loop { - info!("waking lamp {id:?} at {alarm}"); - sleep((alarm - Local::now()).to_std().unwrap()).await; - - if let Some(bulb) = manager.bulbs().await.get(&id) { - // don't wake the bulb if it's already turned on - if bulb.power { - continue; - } - } else { - warn!("bulb {id:?} does not exist"); - return; - }; - - info!("waking lamp {id:?}"); - let r = manager - .until_interrupted(id.clone(), async { - // slowly turn up brightness of bulb - for brightness in (1..=WAKE_TARGET_BRIGHTNESS).map(|i| (i as f32) * 0.01) { - sleep(Duration::from_secs(12)).await; - - manager - .send_command(BulbCommand::SetColor( - BulbSelector::Id(id.clone()), - BulbColor::Kelvin { - t: 0.0, - b: brightness, - }, - )) - .await - } - - // slowly turn up temperature of bulb - for temperature in (1..=WAKE_TARGET_TEMPERATURE).map(|i| (i as f32) * 0.01) { - sleep(Duration::from_secs(12)).await; - - manager - .send_command(BulbCommand::SetColor( - BulbSelector::Id(id.clone()), - BulbColor::Kelvin { - t: temperature, - b: WAKE_TARGET_BRIGHTNESS as f32 * 0.01, - }, - )) - .await - } - }) - .await; - - if r.is_none() { - info!("interrupted waking lamp {id:?}"); - } - - alarm = next_alarm(Local::now(), day, time); - } -} - -/// Get the next alarm, from a weekday+time schedule. -fn next_alarm(now: DateTime, day: Weekday, time: NaiveTime) -> DateTime { - let day_of_alarm = day.num_days_from_monday() as i64; - let day_now = now.weekday().num_days_from_monday() as i64; - - let alarm = now + chrono::Duration::days(day_of_alarm - day_now); - let mut alarm = alarm - .date_naive() - .and_time(time) - .and_local_timezone(Local) - .unwrap(); - - if alarm <= now { - alarm += chrono::Duration::weeks(1); - } - - alarm -} - -#[cfg(test)] -mod test { - use super::next_alarm; - use chrono::{offset::TimeZone, Local, NaiveTime, Weekday}; - - #[test] - fn test_alarm_date() { - const FMT: &str = "%Y-%m-%d %H:%M"; - let now = Local.datetime_from_str("2022-10-18 15:30", FMT).unwrap(); - let test_values = [ - (Weekday::Tue, (16, 30), "2022-10-18 16:30"), - (Weekday::Tue, (14, 30), "2022-10-25 14:30"), - (Weekday::Wed, (15, 30), "2022-10-19 15:30"), - (Weekday::Mon, (15, 30), "2022-10-24 15:30"), - ]; - - for (day, (hour, min), expected) in test_values { - let expected = Local.datetime_from_str(expected, FMT).unwrap(); - - assert_eq!( - next_alarm(now, day, NaiveTime::from_hms(hour, min, 0)), - expected - ); - } - } -} diff --git a/frontend/src/page/lights.rs b/frontend/src/page/lights.rs index d20c642..1c80892 100644 --- a/frontend/src/page/lights.rs +++ b/frontend/src/page/lights.rs @@ -2,7 +2,7 @@ use crate::components::color_picker::{ColorPicker, ColorPickerMsg}; use crate::css::C; use common::{BulbGroup, BulbGroupShape, BulbMap, BulbPrefs, ClientMessage, Param, ServerMessage}; use lighter_lib::{BulbId, BulbMode}; -use seed::{attrs, button, div, empty, h1, h2, input, label, span, table, td, tr, C}; +use seed::{attrs, button, div, empty, h1, input, label, span, table, td, tr, C}; use seed::{prelude::*, IF}; use seed_router::Page; use std::collections::{BTreeMap, HashSet}; @@ -366,7 +366,6 @@ impl Page for Model { div![ C![C.prefs_box], IF!(selected_bulb.is_none() => C![C.cross_out]), - h2!["Settings"], if let Some(selected_bulb) = selected_bulb { table![selected_bulb .prefs