5 Commits

Author SHA1 Message Date
01a7576d7f 0.2.0 2022-10-27 23:39:00 +02:00
1e55385645 Use absolute time instead of incrementing by 1w 2022-10-27 23:36:25 +02:00
1ac0dbe210 Fix alarm time calculation 2022-10-27 23:26:11 +02:00
939e4d9785 Fix wait_for_bulb_command logic 2022-10-27 22:54:52 +02:00
08bdeb693b cargo fmt 2022-10-27 22:50:54 +02:00
5 changed files with 61 additions and 32 deletions

6
Cargo.lock generated
View File

@ -172,7 +172,7 @@ dependencies = [
[[package]] [[package]]
name = "common" name = "common"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"lighter_lib", "lighter_lib",
@ -525,7 +525,7 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]] [[package]]
name = "hemma" name = "hemma"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -549,7 +549,7 @@ dependencies = [
[[package]] [[package]]
name = "hemma_web" name = "hemma_web"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "hemma" name = "hemma"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use chrono::{Datelike, Local, NaiveTime, Weekday}; use chrono::{DateTime, Datelike, Local, NaiveTime, Weekday};
use common::{ClientMessage, ServerMessage}; use common::{ClientMessage, ServerMessage};
use lighter_lib::{BulbColor, BulbId}; use lighter_lib::{BulbColor, BulbId};
use lighter_manager::manager::{BulbCommand, BulbManager, BulbSelector}; use lighter_manager::manager::{BulbCommand, BulbManager, BulbSelector};
@ -40,7 +40,7 @@ pub async fn lights_task(state: &State) {
.flat_map(|(bulb, schedule)| schedule.iter().map(move |(day, time)| (bulb, day, time))) .flat_map(|(bulb, schedule)| schedule.iter().map(move |(day, time)| (bulb, day, time)))
.map(|(bulb, day, time)| { .map(|(bulb, day, time)| {
let handle = spawn(wake_task( let handle = spawn(wake_task(
state.client_message.subscribe(), state.client_message.clone(),
cmd.clone(), cmd.clone(),
bulb.clone(), bulb.clone(),
*day, *day,
@ -105,21 +105,21 @@ pub async fn lights_task(state: &State) {
schedule.insert(day, time); schedule.insert(day, time);
} }
else { else {
schedule.remove(&day); schedule.remove(&day);
} }
}).await { }).await {
error!("Failed to save wake schedule: {e}"); error!("Failed to save wake schedule: {e}");
}; };
if let Some(time) = time { if let Some(time) = time {
let handle = spawn(wake_task( let handle = spawn(wake_task(
state.client_message.subscribe(), state.client_message.clone(),
cmd.clone(), cmd.clone(),
id.clone(), id.clone(),
day, day,
time, time,
)); ));
if let Some(old_handle) = wake_tasks.insert((id, day), handle) { if let Some(old_handle) = wake_tasks.insert((id, day), handle) {
old_handle.abort(); old_handle.abort();
}} else { }} else {
@ -136,39 +136,23 @@ pub async fn lights_task(state: &State) {
} }
async fn wake_task( async fn wake_task(
mut client_messages: broadcast::Receiver<ClientRequest>, client_messages: broadcast::Sender<ClientRequest>,
cmd: mpsc::Sender<BulbCommand>, cmd: mpsc::Sender<BulbCommand>,
id: BulbId, id: BulbId,
day: Weekday, day: Weekday,
time: NaiveTime, time: NaiveTime,
) { ) {
let now = Local::now(); let mut alarm = next_alarm(Local::now(), day, time);
let day_num = day.num_days_from_monday();
let now_day = now.weekday();
let now_day_num = now_day.num_days_from_monday();
let mut alarm = now;
if day_num >= now_day_num {
// next alarm is this week
alarm += chrono::Duration::days((day_num - now_day_num).into());
alarm = alarm.date().and_time(time).unwrap();
} else {
// next alarm is next week
alarm += chrono::Duration::weeks(1);
alarm -= chrono::Duration::days((now_day_num - day_num).into());
alarm = alarm.date().and_time(time).unwrap();
}
loop { loop {
info!("sleeping until {alarm}"); info!("sleeping until {alarm}");
sleep((alarm - Local::now()).to_std().unwrap()).await; sleep((alarm - Local::now()).to_std().unwrap()).await;
alarm += chrono::Duration::weeks(1);
// slowly turn up brightness of bulb // slowly turn up brightness of bulb
for brightness in (1..=75).map(|i| (i as f32) * 0.01) { for brightness in (1..=75).map(|i| (i as f32) * 0.01) {
select! { select! {
// abort if the client pokes the bulb // abort if the client pokes the bulb
_ = wait_for_bulb_command(&id, &mut client_messages) => break, _ = wait_for_bulb_command(&id, client_messages.subscribe()) => break,
_ = sleep(Duration::from_secs(12)) => {} _ = sleep(Duration::from_secs(12)) => {}
}; };
@ -186,13 +170,30 @@ async fn wake_task(
return; return;
}; };
} }
alarm = next_alarm(Local::now(), day, time);
} }
} }
/// Get the next alarm, from a weekday+time schedule.
fn next_alarm(now: DateTime<Local>, day: Weekday, time: NaiveTime) -> DateTime<Local> {
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().and_time(time).unwrap();
if alarm <= now {
alarm += chrono::Duration::weeks(1);
}
alarm
}
/// Wait until we receive a client request that mutates the given bulb /// Wait until we receive a client request that mutates the given bulb
async fn wait_for_bulb_command( async fn wait_for_bulb_command(
bulb_id: &BulbId, bulb_id: &BulbId,
client_messages: &mut broadcast::Receiver<ClientRequest>, mut client_messages: broadcast::Receiver<ClientRequest>,
) { ) {
loop { loop {
match client_messages.recv().await { match client_messages.recv().await {
@ -210,3 +211,31 @@ async fn wait_for_bulb_command(
} }
} }
} }
#[cfg(test)]
mod test {
use chrono::{offset::TimeZone, Local, NaiveTime, Weekday};
use super::next_alarm;
#[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
);
}
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "common" name = "common"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "hemma_web" name = "hemma_web"
version = "0.1.0" version = "0.2.0"
authors = ["Joakim Hulthe <joakim@hulthe.net"] authors = ["Joakim Hulthe <joakim@hulthe.net"]
edition = "2021" edition = "2021"